Sei sulla pagina 1di 7

How to access a Linux server behind NAT via reverse SSH

tunnel
Last updated on May 4, 2015 Authored by Dan Nanni 12 Comments
You are running a Linux server at home, which is behind a NAT router or restrictive firewall. Now you want
to SSH to the home server while you are away from home. How would you set that up? SSH port forwarding
will certainly be an option. However, port forwarding can become tricky if you are dealing with multiple
nested NAT environment. Besides, it can be interfered with under various ISP-specific conditions, such as
restrictive ISP firewalls which block forwarded ports, or carrier-grade NAT which shares IPv4 addresses
among users.

What is Reverse SSH Tunneling?


One alternative to SSH port forwarding is reverse SSH tunneling. The concept of reverse SSH tunneling is
simple. For this, you will need another host (so-called "relay host") outside your restrictive home network,
which you can connect to via SSH from where you are. You could set up a relay host using a VPS
instance with a public IP address. What you do then is to set up a persistent SSH tunnel from the server in
your home network to the public relay host. With that, you can connect "back" to the home server from the
relay host (which is why it's called a "reverse" tunnel). As long as the relay host is reachable to you, you can
connect to your home server wherever you are, or however restrictive your NAT or firewall is in your home
network.

Set up a Reverse SSH Tunnel on Linux


Let's see how we can create and use a reverse SSH tunnel. We assume the following. We will be setting up a
reverse SSH tunnel from homeserver to relayserver, so that we can SSH
to homeserver via relayserver from another computer called clientcomputer. The public IP address
of relayserver is 1.1.1.1.
On homeserver, open an SSH connection to relayserver as follows.
homeserver~$ ssh -fN -R 10022:localhost:22 relayserver_user@1.1.1.1

Here the port 10022 is any arbitrary port number you can choose. Just make sure that this port is not used by
other programs on relayserver.
The "-R 10022:localhost:22" option defines a reverse tunnel. It forwards traffic on port 10022
of relayserver to port 22 of homeserver.
With "-fN" option, SSH will go right into the background once you successfully authenticate with an SSH
server. This option is useful when you do not want to execute any command on a remote SSH server, and just
want to forward ports, like in our case.

After running the above command, you will be right back to the command prompt of homeserver.
Log in to relayserver, and verify that 127.0.0.1:10022 is bound to sshd. If so, that means a reverse tunnel
is set up correctly.
relayserver~$ sudo netstat -nap | grep 10022

tcp 0 0 127.0.0.1:10022 0.0.0.0:* LISTEN


8493/sshd

Now from any other computer (e.g., clientcomputer), log in to relayserver. Then
access homeserver as follows.
relayserver~$ ssh -p 10022 homeserver_user@localhost

One thing to take note is that the SSH login/password you type for localhost should be for homeserver,
not for relayserver, since you are logging in to homeserver via the tunnel's local endpoint. So do not
type login/password for relayserver. After successful login, you will be on homeserver.
Connect Directly to a NATed Server via a Reverse SSH
Tunnel
While the above method allows you to reach homeserver behind NAT, you need to log in twice: first
to relayserver, and then to homeserver. This is because the end point of an SSH tunnel
on relayserver is binding to loopback address (127.0.0.1).
But in fact, there is a way to reach NATed homeserver directly with a single login to relayserver. For
this, you will need to let sshd on relayserver forward a port not only from loopback address, but also
from an external host. This is achieved by specifying GatewayPorts option in sshd running
on relayserver.
Open /etc/ssh/sshd_conf of relayserver and add the following line.
relayserver~$ vi /etc/ssh/sshd_conf

GatewayPorts clientspecified

Restart sshd.
Debian-based system:
relayserver~$ sudo /etc/init.d/ssh restart

Red Hat-based system:


relayserver~$ sudo systemctl restart sshd

Now let's initiate a reverse SSH tunnel from homeserver as follows.


homeserver~$ ssh -fN -R 1.1.1.1:10022:localhost:22 relayserver_user@1.1.1.1

Log in to relayserver and confirm with netstat command that a reverse SSH tunnel is established
successfully.
relayserver~$ sudo netstat -nap | grep 10022

tcp 0 0 1.1.1.1:10022 0.0.0.0:* LISTEN 1538/sshd: dev


Unlike a previous case, the end point of a tunnel is now at 1.1.1.1:10022 (relayserver's public IP address),
not 127.0.0.1:10022. This means that the end point of the tunnel is reachable from an external host.
Now from any other computer (e.g., clientcomputer), type the following command to gain access to
NATed homeserver.
clientcomputer~$ ssh -p 10022 homeserver_user@1.1.1.1

In the above command, while 1.1.1.1 is the public IP address of relayserver, homeserver_user must be
the user account associated with homeserver. This is because the real host you are logging in to
is homeserver, not relayserver. The latter simply relays your SSH traffic to homeserver.
Set up a Persistent Reverse SSH Tunnel on Linux
Now that you understand how to create a reverse SSH tunnel, let's make the tunnel "persistent", so that the
tunnel is up and running all the time (regardless of temporary network congestion, SSH timeout, relay host
rebooting, etc.). After all, if the tunnel is not always up, you won't be able to connect to your home server
reliably.

For a persistent tunnel, I am going to use a tool called autossh. As the name implies, this program allows
you to automatically restart an SSH session should it breaks for any reason. So it is useful to keep a reverse
SSH tunnel active.
As the first step, let's set up passwordless SSH login from homeserver to relayserver. That
way, autossh can restart a broken reverse SSH tunnel without user's involvement.
Next, install autossh on homeserver where a tunnel is initiated.
From homeserver, run autossh with the following arguments to create a persistent SSH tunnel destined
to relayserver.
homeserver~$ autossh -M 10900 -fN -o "PubkeyAuthentication=yes" -o
"StrictHostKeyChecking=false" -o "PasswordAuthentication=no" -o "ServerAliveInterval
60" -o "ServerAliveCountMax 3" -R 1.1.1.1:10022:localhost:22 relayserver_user@1.1.1.1

The "-M 10900" option specifies a monitoring port on relayserver which will be used to exchange test data
to monitor an SSH session. This port should not be used by any program on relayserver.
The "-fN" option is passed to ssh command, which will let the SSH tunnel run in the background.
The "-o XXXX" options tell ssh to:
 Use key authentication, not password authentication.
 Automatically accept (unknown) SSH host keys.
 Exchange keep-alive messages every 60 seconds.
 Send up to 3 keep-alive messages without receiving any response back.
The rest of reverse SSH tunneling related options remain the same as before.

If you want an SSH tunnel to be automatically up upon boot, you can add the above autossh command in
/etc/rc.local.
Conclusion
In this post, I talked about how you can use a reverse SSH tunnel to access a Linux server behind a restrictive
firewall or NAT gateway from outside world. While I demonstrated its use case for a home network via
a public VPS, you must be careful when applying it for corporate networks. Such a tunnel can be considered
as a breach of a corporate policy, as it circumvents corporate firewalls and can expose corporate networks to
outside attacks. There is a great chance it can be misused or abused. So always remember its implication
before setting it up.

Subscribe to Xmodulo
Do you want to receive Linux FAQs, detailed tutorials and tips published at Xmodulo? Enter your email
address below, and we will deliver our Linux posts straight to your email box, for free. Delivery powered by
Google Feedburner.
Your email add Subscribe
Support Xmodulo
Did you find this tutorial helpful? Then please be generous and support Xmodulo!

 Bio

 Latest Posts

Dan Nanni
Dan Nanni is the founder and also a regular contributor of Xmodulo.com. He is a Linux/FOSS enthusiast who
loves to get his hands dirty with his Linux box. He likes to procrastinate when he is supposed to be busy and
productive. When he is otherwise free, he likes to watch movies and shop for the coolest gadgets.





Related FAQs:
 How to create an HTTP tunnel on Linux with httptunnel
 How to diff remote files over SSH
 How to edit a remote file over ssh
 How to block unwanted IP addresses on Linux efficiently
 How to secure SSH login with one-time passwords on Linux
Categories: Networking, Security
Tags: firewall, nat, ssh, tunnel

12 thoughts on “How to access a Linux server behind NAT via reverse SSH tunnel”
1. Reply

Iulian Murgulet on May 4, 2015 at 11:32 am said:


When I need to solve this problem (restricted NAT), I will use this:

- on the vps, I install openvpn with certificates in UDP mode


- on the remote host and on the internal sever I install openvpn-client (auth with client certificate and
password)
- so in the end, you can initiate any ssh connection from remote-host to the internal server (using the IP
address of the openvpn tunnel)
Advantages:
- you have two auth factor (password and certificate) when you want to start openvpn client
- open-vpn will be a second layer for the ssh communication
- open-vpn in udp mode is more stealthy compared with ssh on tcp
- and if you set up the openvpn in bridge mode, you can even use a dedicate VLAN for open-vpn

 Reply

Dan Nanni on May 5, 2015 at 1:43 am said:


Thanks for your comment. VPN can be another option. But I would prefer SSH for ease of setup.

> open-vpn in udp mode is more stealthy compared with ssh on tcp

I thought the opposite. If a firewall is set to block unrecognized encrypted application protocols, it will
block OpenVPN. But SSH is whitelisted in many corporate networks.

 Reply

Iulian Murgulet on May 5, 2015 at 2:12 pm said:


I was telling about the ssh installed on the vps, not related with the any corporate networks. For
example if you scan the vps, it is easy to discover ssh daemon, but to discover open-vpn (running on a
udp port), you need a lot of time.
On a corporate network, any ssh traffic is treated with a lot of care/attention, so it is hard to cover any
ssh link. But if you want very hard to escape, you could use openvpn (server side) in TCP mode,
running on tcp/443 (with ssl encryption). In this case any link to a VPS will be like as any other
browsing activity. You could even use openvpn with any http/https proxy like any other http/https
client.

And you are right. ssh is easy, but think for a moment there was time in the past when all of us
discovered a bug in ssl (Heartbleed). At that time any admin who used ssh could not sleep very well,
until the patch was available! I was running ssh with Heartbleed bug for many years!

But for some others admins (me included), open-vpn (udp/tls-auth, and ssh accessible only from the
open-vpn tunnel) was very happy, because it was not vulnerable (TLS-auth on top of the SSL -
see https://openvpn.net/index.php/access-server/heartbleed.html)
In conclusion, some setups are easy to made, others are more complicated, each of them has
advantages and disadvantages at the same time.

2. Reply

Jan Rafaj on May 4, 2015 at 1:46 pm said:


No need for autossh. There's a simpler way to get the ssh tunnel persistent though - just throw the initiating ssh
process under init control (put a dedicated 'respawn' record in /etc/inittab). The only thing to keep in mind then
is to avoid too frequent respawns, which can be done by controlling reception of 'icmp host-unreachable', that
essentially controls when the initiating ssh gives up another tunnel init attempt...

3. Reply

unnikked on May 4, 2015 at 11:20 pm said:


Nice and clean article thank you. That could be useful to manage home raspberry without messing the home
router.

Can the same trick be used to expose other services? Like a MySQL database for example?

 Reply

Dan Nanni on May 5, 2015 at 1:53 am said:


Sure certainly it can. Simply replace SSH port with MySQL port (3306). Something like will do:

homeserver~$ ssh -fN -R 10022:localhost:3306 relayserver_user@1.1.1.1


relayserver~$ mysql -h 127.0.0.1 -P 10022 -u user -p db

4. Reply

Dashamir Hoxha on May 5, 2015 at 10:06 am said:


These scripts are based on the same idea and can be used to access the terminal of the home server too:
- https://github.com/docker-build/p2p
- http://docker-build.github.io/p2p/
- https://registry.hub.docker.com/u/dashohoxha/p2p/

5. Reply

Mateus Marcuzzo da Rosa on May 6, 2015 at 1:03 pm said:


Excellent.

I've been searching for something close like that so I can access services that come out of my home network,
which is NATed.

At least for SSH now I know how to do it.


I'd like to know if it's possible for an arbitrary service on another port (like VPN port) and how can I do it.

I don't master very well using VPNs and stuff...(I've read the above comments too)

Thanks for the article.

6. Reply

Fernando Vieira on July 15, 2015 at 5:46 pm said:


Thank you so much!

This is what I call flawless instructions!

7. Reply

Jeremy weber on August 27, 2015 at 2:25 pm said:


Hi, thank you a lot for these explanations.
I would like to make sure about one thing. If I have multiple homeservers at different places but all are behind
NAT. Can we access them all from one single vps? Is it sufficient to create each ssh connection to a specific
port on the relay server (10022 for homeserver 1, 10023 for homeserver 2, etc)
Thank you for your response.
Weber Jerem

 Reply

Dan Nanni on August 27, 2015 at 3:03 pm said:


Definitely. Just make sure to use different ports on the relay VPS for different home servers.

 Reply

Jéremy Weber on August 27, 2015 at 3:34 pm said:


Ok thank you. Very nice tutorial BTW :)

Potrebbero piacerti anche