Have you ever run into the situation where you want to access a server behind multiple layers of firewall and with no public IP? If so, please read on. In this post, I’ll be sharing how I can successfully access my server with a custom domain.
Pre-requisite
You are gonna need a few things before we move on, they are listed below:
- Your server (of course!)
- A publicly accessible server
- A domain name (optional)
Here let’s assume the public server has an IP of 1.2.3.4
, and your domain name is example.com
.
Personally, the server I have is a Ubuntu 16.04 LTS system. And the server with public IP is a VPS hosted on DigitalOcean running Debian 8. My steps below should work for a similar configuration. You might need some small tweaks to cope with different OS.
SSH tunneling
The first step is to create a SSH tunnel between the public server and your server. This enables you to access a certain port of your server by connecting
to a port of the publicly accessible server. For example, if you create a tunnel between port 8888 of the public server and port 8080 of your server,
whenever you access port 8888 of the public server, your traffic is tunnelled to port 8080 of your server. Hence, port 8080 of your server is exposed to
the Internet. To create a tunnel, first edit the file /etc/ssh/sshd_config
on your public server. Make sure you have the following line in that file:
GatewayPorts yes
This enables remote port forwarding, which is disabled by default. Now restart the ssh daemon on your public server with:
$ sudo service ssh restart
This reloads your new configuration. After that, let’s connect to your public server from your server sitting behind firewalls with SSH to set up a remote port forwarding. Run the following command on your server:
$ ssh -R 8888:localhost:8080 user@1.2.3.4
This creates the actual tunnel between 1.2.3.4:8888
and port 8080 of your server. Remember to change user
to your actual username.
Adding a domain name
After the previous steps, you can already access your server with 1.2.3.4:8888
. If you think this is enough, you can stop here. However, I think adding
a domain name makes the address easier to remember. I’ll skip the steps on how to map a domain name to a server. Instead, I’ll emphasize on how to map
port 80 of your domain to 1.2.3.4:8888
, so that you don’t have to append :8888
everytime you access it. To make it work, we need to install nginx,
which is a lightweight HTTP server and reverse proxy, on your public server. On a debian/ubuntu system, run the following command:
$ sudo apt-get install nginx
Now let’s create a file at /etc/nginx/sites-available
named server
and add the following to it:
server {
server_name “~^example\.com$”;
location / {
proxy_pass http://127.0.0.1:8888;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
}
}
And let’s create a soft link for it:
$ sudo ln -s /etc/nginx/sites-available/server /etc/nginx/sites-enabled/server
Now reload the configuration by restart the nginx service:
$ sudo service nginx restart
You should now be able to access example.com
to use port 8080 of your server. Great! If you want a second level domain such as test.example.com
,
change ”~^example\.com$”
to ”~^test\.example\.com$”
and restart nginx.
Redirecting WebSocket for Jupyter
If you happen to run Jupyter like me, you may notice that you can access its pages, but not the notebook kernels. This is because Jupyter uses the
WebSocket protocol for bi-directional communication, which is different from the normal HTTP protocol. Therefore, we need to change the configuration
a little bit for this. Change /etc/nginx/sites-available/server
to the following:
server {
server_name “~^test\.example\.com$”;
location / {
proxy_pass http://127.0.0.1:8888;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection “upgrade”;
proxy_read_timeout 86400;
}
}
This will upgrade your connection from HTTP protocol to WebSocket protocol. And Jupyter should work now. Don’t forget to restart the nginx server.
Relevant links
Here are some of the references I used when I was doing this:
- Setup for an easy to use, simple reverse http tunnels with nginx and ssh.
- Setup Tensorflow, Ipython/Jupyter, Docker behind NGINX HTTP/2 with SSL!
- WebSocket proxying
Hope this helps!
comments powered by Disqus