Easy ways to setup Reverse Proxy for NAT-Passthrough

It's time to abandon NPS, Frp, or other solutions that are hard to configure or no longer maintained. Thanks to Docker, it's possible to set up a reliable reverse proxy with single command.

Prerequisite

Make sure you have a machine with a public IP address (it could be a VPS), otherwise our method may not be applicable. Let's take exposing the SSH port of a machine behind NAT as an example. There are three roles in total:

  • Local Machine: The machine behind NAT firewall with a local IP address, and you want to expose its SSH port 22 to the public network.
  • Public Server: The machine with a public IP address (a.b.c.d). Typically you rent it from a public cloud service provider like AWS or Azure. Its port 22 is also open and being used by its own SSH service.
  • Your Computer: Another machine you are currently using.

Before getting started: Install Docker

The first step is to install the Docker on both the local machine and the public server. For Ubuntu 18.04+, I personally prefer to install Docker through apt.

1
2
local-machine&public-server$ sudo apt update
local-machine&public-server$ sudo apt install docker.io -y

Gost supports nuermous proxy protocols. For reliablity, it is suggested to use a secured protocol to resist the interference by some secure gateways like GFW. Luckily, with the help of Docker, it is very easy to set up a secured tunnel. Another good news is that Docker Daemon will help to monitor the service and automatically restart Gost Service at boot or on failure. Let's say goodbye to the annoying Systemd.

On Public Server

1
2
3
cd ~ # other any other path you like
mkdir gost
docker run --name gost_server --net host -v $(pwd)/gost:/root -id --restart always --entrypoint "" -w "/root" gogost/gost gost -L "relay+wss://<user>:<passwd>@:<gost_service_port>?bind=true"

On Local Machine

1
docker run --name gost_client --net host -id --restart always --entrypoint ""  gogost/gost gost -L rtcp://:<remote_port>/:<local_port> -F "relay+wss://<user>:<passwd>@<pub_server_ip_or_domain>:<gost_service_port>"

Here is the explanation of parameters:

  • user / passwd: The username and the password for Gost. They are irrelevant to any other account such as Linux accounts.
  • pub_server_ip_or_domain: It could be either the public IP address or the binded domain name. If you would like to use your valid SSL certificate issued by CA, you should only use the domain name here.
  • gost_service_port: Could be arbitrary value. It is also fine to set this port number to 443 to pretend as a HTTPS server.
  • remote_port: The port that the public server's Gost listens to. The data received from this port will be forwarded to local_port on the local machine. It could be arbitrary value.
  • local_port: The port that should associate with some services on the local machine. In our case, it should be 22, the SSH port.

Back to our case, the corresponding commands will be:

1
2
3
4
5
6
7
8
9
10
# On Public Server
cd ~
mkdir gost
docker run --name gost_server --net host -v $(pwd)/gost:/root -id --restart always --entrypoint "" -w "/root" gogost/gost gost -L "relay+wss://user:pass@:1234?bind=true"

# On Local Machine
docker run --name gost_client --net host -id --restart always --entrypoint "" gogost/gost gost -L rtcp://:2022/:22 -F "relay+wss://user:pass@a.b.c.d:1234" # a.b.c.d is the public IP of the public server

# or
docker run --name gost_client --net host -id --restart always --entrypoint "" gogost/gost gost -L rtcp://:2022/:22 -F "relay+wss://user:pass@mydomain.com:1234" # mydomain.com can be parsed to a.b.c.d

After that, the SSH port 22 on the local machine should be mapped to the port 2022 on the public server now.

1
2
3
4
# user-local is the name of the user on local machine
your-computer$ ssh user-local@a.b.c.d -p 2022
# or
your-computer$ ssh user-local@mydomain.com -p 2022

Debugging Tips: If Gost is not working properly, we can read the log using the command below.

1
docker logs -f gost_server # or gost_client

(Optional) Using your SSL certificate

By default, Gost will generate a self-signed SSL certificate if the user doesn't specify one. However, this might be considered unsafe as the communication is no longer able to defense the MITM attack. Moreover, some secure gateways may disrupt the TLS session with a self-signed certificate.

The solution is simple. Do you remember the gost directory we created before? We just need to put our certificate there and restart the service. There should be two files in total, cert.pem and key.pem, and their content should start with -----BEGIN CERTIFICATE---- and -----BEGIN RSA PRIVATE KEY----- respectively.

Lastly, the command to restart the service is:

1
2
# On Public Server
docker restart gost_server

Temporary Method: via OpenSSH Client

If you don't want to install any new software, we can also utilize SSH to build a tunnel. This method is also simple (sometimes), but it is not quite reliable. As you might know, a SSH connection can be easily disrupted due to many reasons. In our case, we just need to type this command on the local machine to set up a tunnel:

1
2
local-machine$ ssh -R "[::]:2022:localhost:22" user-public@a.b.c.d
# a.b.c.d is the public IP, user-public is the name of the user on public server

If everything goes well, now you can connect to the local machine through:

1
2
your-computer$ ssh user-local@a.b.c.d -p 2022
# user-local is the name of the user on local machine

However, if you failed to connect to your local machine. Please check the sshd configuration on the public server.

1
public-server$ sudo nano /etc/ssh/sshd_config

Find the following line (for nano editor, press Ctrl-W to search), uncomment, and replaced no with yes.

1
2
3
4
5
# Before
#GatewayPorts no

# After
GatewayPorts yes

Then restart the SSH server.

1
public-server$ sudo service sshd restart