Problem

I need SSH access to a particulr machine (schoolsvr) which is behind a NAT. I only need to enable access from a single client (homesvr), which has a public IP address of its own. Both machines are running sshd. I can access homesvr from a shell on schoolsvr, but not vise-versa.

If I had admin access on schoolsvr’s gateway, I could alter the NAT to forward some unused port (say, 12345) to schoolsvr:22, which would allow me to SSH to schoolsvr using the gateway’s public IP and port 12345. Unfortunately, I dons’t have admin access to the gateway.

How do I enable SSH access to schoolsvr?

Solution

The solution is to open an SSH tunnel from schoolsvr, which I can access from a shell on homesvr. To achieve this, I use the OpenSSH client program’s -R option to bind an SSH tunnel to a non-standard port on homesvr. Consider the following command:

nick@schoolsvr$ ssh -R 12345:localhost:22 nick@homesvr
nick@homesvr's password:
nick@homesvr$

This command connects to homesvr via the standard SSH port (22) and binds that connection to the specified bind port (12345). This port remains bound until the SSH session is terminated. Now all SSH traffic directed to port 12345 on homesvr will be forwarded to port 22. When I get back to homesvr, I can open a new SSH session with schoolsvr using the following command:

nick@homesvr$ ssh -p 12345 localhost
nick@localhost's password:
nick@schoolsvr$

I’m in! I can terminate this session when I am finished, and the original tunnel remains open until I kill it on schoolsvr.

This command can be set up in /etc/inittab (or an Upstart config file, depending on your system configuration) with the respawn action, which would ensure that the tunnel is open upon boot and will be automatically reopened upon termination. Note that such a setup requires the appropriate SSH keys to be configured on both machines, as an init process can’t enter a password.

Because each half of the connection is done using SSH, this setup is completely secure. Of course, anyone with physical access to schoolsvr would have full control over the open login to homesvr. To prevent this, I can modify the original command as follows:

nick@schoolsvr$ ssh -nNT -R 12345:localhost:22 nick@homesvr &

The -n option redirects standard input from /dev/null. The -N option is specifically designed for port-forwarding applications such as this, and tells SSH not to bother preparing a command stream for this connection. The -T option tells the remote host not to bother allocating a pseudo-tty for this connection. These three options eliminate the possibility of using this open tunnel to execute any other processes on schoolsvr. Additionally, I appended an ampersand (&) to send the process to the background. Now I can close the shell in which I ran the command without killing the process.

Conclusion

While not as elegant as a true NAT-based port forwarding solution, reverse SSH tunnels are a fast, secure way to connect two remote machines for general use. When used with discretion, they can be a real time-saver.

What do you think of this solution? Did I leave anything out? Let me know in the comments.