Alternatives to SSH agent forwarding

SSH has a handy feature called agent forwarding that allows you to log in to a remote server and use the keys loaded into your local ssh-agent as if they were on the server. This can be convenient if you want to run some command on the server that needs an ssh-based connection to another server - for example, git over ssh. It’s also sometimes used to simplify network access restrictions: instead of maintaining an up-to-date list of everyone’s dynamic IP addresses, give them SSH access to a single “bastion” server and allow access to the rest of the network from there.

Unfortunately, this useful feature has a downside: it’s not safe to use on servers you don’t trust. To see why, try this simple experiment (you’ll need root access on a server that you do trust):

Connect to the server, forwarding your agent. Then, separately, connect without your agent and switch to root. Have a look in /tmp - you’ll see a directory with a name like ssh-XXXXXXXXXX containing a single file named something like agent.1234. Set the environment variable SSH_AUTH_SOCK to the full path to this file:

export SSH_AUTH_SOCK=/tmp/ssh-XXXXXXXXXX/agent.1234

Now you can use ssh-add -L on the server to view the (public) keys loaded into your local agent, and you can connect to any server that those keys grant access to. If anyone else was logged in with a forwarded agent, you could do the same with their keys.

If you want to avoid this situation, you’ll need to ensure that anything that needs to use your SSH keys is running directly on your local machine. Here are some ways to achieve that.

Bastion server

If you’re using the untrusted server simply as a proxy to access other resources, you can configure SSH to provide a local SOCKS proxy that you can use instead. This is easy to do:

ssh -NfD 1080 username@untrusted.example

This starts a background SSH session with no TTY, providing a local SOCKS proxy on port 1080 that forwards traffic through the SSH connection and onwards from there. This can be used with any application that supports the SOCKS proxy protocol.

If you want to proxy SSH itself, there’s an even simpler solution: the ProxyCommand configuration option allows you to configure a default command to be used to start a proxy for your SSH connections. Add it to your ~/.ssh/config file, like so:

Host target.example
ProxyCommand ssh -W %h:%p username@untrusted.example

If your SSH client doesn’t support the -W option, you can use netcat instead:

Host target.example
ProxyCommand ssh username@untrusted.example nc %h %p

Accessing files on the untrusted server

The above techniques don’t allow you to access files on the untrusted server, so they’re not useful with remote git working copies, for example. Instead, you can use sshfs:

sshfs username@untrusted.example:/remote/path /local/path

Note that /local/path must already exist. This will mount /remote/path on the untrusted server at /local/path on your local machine, so that you can read and write it as if it were a local filesystem. Be careful to avoid mounting it in a location where any locally running scripts might randomly delete things! When you’re done, unmount it:

fusermount -u /local/path

You need to be careful with this approach, however. Recall that the goal here is to ensure that any software that needs access to your keys is running on your local machine. If multiple users are sharing access to the same git repository, they might not all be using the same version of git locally, which may lead to compatibility issues.