Using a Bastion Host
A bastion host is a server that acts as a gateway between you and the servers you are logging in to. Many companies set up a group of servers in a private network that blocks all incoming traffic. Then a single bastion server is added to the network and is the only server accessible outside of the private network. The only way to log in to one of the servers is to pass traffic through the bastion host, and ssh
provides multiple ways to accomplish this.
Agent Forwarding is Insecure
A common, but dangerous, practice in using bastion hosts is to first ssh
into the bastion with agent forwarding enabled (the -A
flag), then ssh
into the destination server. In this case, your ssh
session is decrypted on the bastion host, then re-encrypted to your local machine, meaning that anyone with access to the bastion host can potentially read or hijack your ssh
session. Agent forwarding also leaves a socket open on the bastion that connects back to your local ssh-agent
, potentially allowing other users to use your local private keys. Finally, invoking ssh
on the bastion does not use your local ~/.ssh/known_hosts
file or Krypton’s pinned host public keys for authenticating remote hosts.
Use Proxying Instead
ssh
supports proxying encrypted traffic through one (or many) intermediate servers, where each server adds a layer of encryption instead of decrypting and re-encrypting the traffic. We accomplish this using the ProxyCommand
configuration option.
Single Bastion Host
Suppose our bastion host is bastion.krypt.co
and our destination server is dest.krypt.co
. Adding the following to our ~/.ssh/config
before the Krypton block will use our bastion as a proxy:
Host dest.krypt.co
ProxyCommand krssh -p "ssh -v -W %h:%p bastion.krypt.co" -h %h
# Added by Krypton...
Now when we log in to dest.krypt.co
, we first authenticate to bastion.krypt.co
. This first ssh
session sets up a tunnel that forwards traffic from our local machine to bastion.krypt.co
and finally to dest.krypt.co
. Then a new ssh
login is started over this tunnel, starting on our local machine and ending at dest.krypt.co
. The final result is that traffic is locally encrypted to the dest.krypt.co
session, then locally encrypted again to bastion.krypt.co
and sent. Then bastion.krypt.co
decrypts the outer layer and sends the still-encrypted session to dest.krypt.co
, where it is fully decrypted. Each step of the authentication uses Krypton’s pinned host public keys to authenticate each host.
Even if the bastion host is compromised, an adversary cannot read or hijack the session established between our local machine and the destination server.
Many Destination Servers
Usually more than one server is protected by a single bastion host. We can use ssh
config rules to easily proxy traffic for multiple destinations through one proxy:
# Specify each proxied host individually
Host dest1.krypt.co dest2.krypt.co
ProxyCommand krssh -p "ssh -v -W %h:%p bastion.krypt.co" -h %h
# Proxy traffic for a group of servers
Host *.dev.krypt.co
ProxyCommand krssh -p "ssh -v -W %h:%p bastion.krypt.co" -h %h
# Added by Krypton...
Check out man ssh_config
for more config tricks using the Host
and Match
directives.
Multiple Proxy Hops
ssh
traffic can even be proxied through multiple hops, allowing you to navigate multiple layers of private networks. We accomplish this by simply adding a ProxyCommand
directive for the intermediate bastion as well.
For example, to proxy traffic through hop1.krypt.co
to hop2.krypt.co
and finally to dest.krypt.co
, we would add the following to ~/.ssh/config
:
Host hop2.krypt.co
ProxyCommand krssh -p "ssh -v -W %h:%p hop1.krypt.co" -h %h
Host dest.krypt.co
ProxyCommand krssh -p "ssh -v -W %h:%p hop2.krypt.co" -h %h
# Added by Krypton...