SSH Bastion

Christmas is coming and some hackers might like to digest Christmas delicacies while breaking into our servers. How to make it a bit more difficult for them this year?

Let’s assume we manage some servers and we connect to them using SSH (or git, rsync, or other programs that use secure shell internally). Do we really need many of these servers to expose port 22 (default SSH port) to the internet? With SSH Bastion concept, we do not.

SSH Bastion is a nickname for a server in our infrastructure, which sole purpose is to provide us with secure access to other servers. It should have minimal, hardened configuration, with no unnecessary user accounts and no running daemons, except for sshd. Ports other than 22 should be closed. On the other hand, SSH Bastion should be the only server in our infrastructure, that does expose port 22 to the internet.


Let’s see our SSH Bastion server in action.

We have to make sure that public key authentication is set up correctly on the Bastion machine, as well as on all other servers we want to eventually have access to. There are many articles on this topic around the internet. Please note, that we never store our private keys on any of the servers.

Open SSH configuration file ~/.ssh/config on your client computer (we will call it client), and enter the following configuration snippet to it, assuming our Bastion hostname is bastion, and internal is the hostname of a server we want to eventually connect to. In this example we also assume that we use a single SSH key ~/.ssh/id_rsa for both connections.

Host bastion  
  IdentityFile ~/.ssh/id_rsa

Host internal  
  IdentityFile ~/.ssh/id_rsa
  ProxyCommand ssh user@bastion -W %h:%p

With our ProxyCommand configuration in place, we can connect to our internal server using command ssh user@internal.

Behind the scenes, a parent SSH connection is created between client and the internal server, followed by a child connection between client and bastion. The SSH daemon on bastion machine then creates a TCP connection to the internal server.

This situation allows us to connect from our client (or the internet in general) to our internal machine. However, if we use the command who to see who is who inside our internal machine, we can see that the SSH connection actually comes from bastion box. And that’s correct, since nobody should be able to connect to our internal server directly.

There’s more we can do with SSH bastions to make networks more secure. Different SSH keys can be used for parent and child connections (specified as IdentityFile in the code snippet above). Or if the SSH access is required only occasionally, stopping/starting bastion machine on demand can temporarily disable SSH access to our entire network.

SSH bastion improves network security with elegance, transparency and simplicity. Highly recommended.