In support of disabling SSH agent forwarding

The SSH Agent is a little program which typically runs on your desktop which allows the Secure Shell program to use your private secret key for logging into remote servers. The idea with the agent is that it, and it alone, holds onto the secret key. Each time you use ssh(1) to connect to a server, it checks with the agent to see if any of the keys that the agent is holding can be used to login to the server you are trying to log into. If there is, the remote server comes up with a challenge, which can only be solved with the private key. The challenge is passed to the agent, and the agent responds with the solution, proving to the remote server that you have the private key. Neither the remote server, nor the ssh program ever see the private key.

I came across this post about SSH Agent forwarding, which is a great piece on SSH authentication agent forwarding.

I’ve seen on multiple occasions, instructions like this:

  1. Set up access to the bastion host; put this in your ~/.ssh/config:
  ForwardAgent yes
  host bastion
  1. Now, when you want to log in to a server, first login to the bastion, then ssh to the server you want to go to:
  $ ssh bastion
  ...bastion presents challenges...
  bastion:~$ ssh appserver1

The connection from bastion to appserver1 was able to work without entering a password because the SSH agent connection was forwarded to bastion. If you log in to a host with forwarding, you can see the socket in /tmp:

$ ls -l $SSH_AUTH_SOCK
srw-rw-rw-  1 samv  wheel  0 Nov 18 12:39 /private/tmp/

That little s in the very first column indicates that the file is a unix domain socket, which is essentially a little access panel to the program that created it. It can be connected to as if it were an IP address and port number, but only from somewhere that shares a filesystem (ie, on the same system or kubernetes pod).

So what happened was that the ssh running on the bastion host connected to that socket, asked it for the keys, and then the program listening on it - the SSH daemon process (sshd) forwarded the request to your desktop. Once it discovers that the appserver1 you are trying to log into accepts a key it has, it will happily provide solutions to the challenges required to prove that the key is available.

The Gaping Security Hole

This agent forwarding is somewhat pragmatic for single hosts which act as gatekeepers to other hosts, and on which nobody is granted root access.

If people are allowed root access, then you’ve just opened up a little security hole: if somebody can access that file in /tmp, and send messages from wherever they are, then they can login as you to wherever you are allowed access. And to make things worse, you’ll never know your key was used.

If you enable this all the time, then every system you login to is able to use your agent while you are logged in to login anywhere else you are allowed to log in. Let that sink in.

So, at the very least, only put ForwardAgent Yes on those few hosts that you expect this to work with:

  host bastion
  ForwardAgent yes

It’s also possible to make the agent request confirmation each time it is used to login to a remote server (see the post linked earlier); however, the version which ships with MacOS X 10.11 (El Capitan) does not support that. The built-in keyring system seems to silently carry out authentications, as well.

It’s much better and more convenient to follow the instructions in the post and use ProxyCommand. And it works even better with ControlMaster!

SSH ControlMaster and ProxyCommand

First, let’s set up SSH ControlMaster, so that you don’t have to keep re-authenticating to hosts you’re already connected to.

ControlMaster auto
ControlPath ~/.ssh/control-%r@%h:%p.sock

This command opens another one of those magic unix domain sockets, which lets ssh commands you run go right through.

Be warned that if your local machine gets compromised, this does make it a little easier for the attacker to get into the host you’re trying to get into. That said, it’s not really that much harder to replace your ssh command with a malicious version which achieves the same access tunnel even if you disable ControlMaster. If you like, set ControlMaster to ask, so that you at least get a pop up window each time this is used (unless the person owning you is cleverer than that).

Next, set up a wildcard rule. This will be based on your company’s hostname convention: let’s assume it’s always something .prod:

host *.prod
proxyCommand ssh -ax nc %h %p

(nc is an alternative to the ssh -W command in the original post; if that works, then go ahead and use it, because it’s slightly more resource efficient)

Now, you can complete both of these steps with a single command:

  $ ssh
  ...bastion presents challenges...

You skipped the intermediate host!

Not only that, but now you’ll also be able to use commands like scp to send files directly to and from the server behind the bastion you’re interested in. Without entering a password!

  $ scp .
  data_file.json 200kb

Once the original session is closed, the ControlMaster socket closes and all other sessions piggy-backing over it will be shut down.

Personally, I find the ability to use scp, rsync etc through the bastions is indispensible and set up something like this so that I don’t have to use ForwardAgent.

Share Comments
comments powered by Disqus