A bastion host is a server that sits on a public network whose sole purpose is to provide access to an inner private network.

For example, if you use AWS and have instances on a private VPC subnet, then the only way you can gain SSH access to them is to use a bastion host as a kind of proxy.

You > Bastion > Server

For the best security, you typically have SSH configured for key-only authentication. This presents a problem: Your bastion server needs the keys to any internal server. Having a single bastion server with the keys to the castle is a pretty big risk.

There are a few solutions to this problem. The most common or “popular” is to use an SSH agent with ssh forwarding. But SSH forwarding isn’t very secure. If your bastion host were ever compromised, then an attacker could use it to gain access to anything else you connected to.

One other problem is that with ssh forwarding, the agent just dumbly attempts every key, one by one. If you have more than a few keys, then this tends to result in failures because many hosts auto-disconnect you after too many attempts.

So the better option I’ve found is to use SSH proxying. This is where you connect via SSH to the bastion host, and then open another SSH connection from your computer to the target server through the bastion. In other words, the SSH connection is still started on your computer and terminated at the target; the bastion becomes just a proxy.

SSH Proxy

The simplest method is like this:

ssh -o ProxyCommand='ssh -W %h:%p [email protected]' [email protected]

To make this easier (and to make it also work for other tools like scp or rsync), you can edit your ~/.ssh/config file to define the proxy command and other params. For example:

Host bastion
  Hostname my-bastion-host.example.com

Host my_server
  ProxyCommand ssh bastion -W %h:%p

# then you can use:
# $ ssh my_server

There are lots of ways you can combine options to suit nearly any workflow. Combining hosts, using different keys, whatever. Check out the cookbook for really good examples.