What -A does
Without forwarding, your keys are of no use once you are on a server, so a second hop from there cannot use them. ssh -A solves that by connecting the remote host back to your agent:
ssh -A user@server
echo "$SSH_AUTH_SOCK"
/tmp/ssh-G63yAM532X/agent.1441
That path is a Unix socket on the server. Anything sent to it travels back to the agent on your computer, which signs what is asked and returns the result. The server never receives the private key.
The location depends on the server's version. We saw /tmp/ssh-XXXXXXXXXX/agent.<pid> on OpenSSH 10.0. Version 10.1 moved agent sockets to ~/.ssh/agent/.
To load keys into a local agent first, see ssh-agent and the macOS keychain.
What root on the remote host can do
File permissions protect the socket from other ordinary users, and root ignores those permissions. We tested this on a server while a user was connected with -A. As root:
# find /tmp -type s -name 'agent.*'
/tmp/ssh-G63yAM532X/agent.1441
# SSH_AUTH_SOCK=/tmp/ssh-G63yAM532X/agent.1441 ssh-add -l
256 SHA256:IikDwOZxEooOrmm+d3MXJXlobc7rwEyUrQNIBXHzXaU key (ED25519)
# SSH_AUTH_SOCK=/tmp/ssh-G63yAM532X/agent.1441 ssh user@203.0.113.11 id -un
user
So root used the key to log in to another machine, with no prompt on the user's side. Here is the same picture in full:
- It can: use every key in your agent, to log in to any server or service that accepts them, including a code hosting account. It can also list your public keys and remove keys from your agent:
ssh-add -Drun on the server emptied our local agent. - It cannot: copy the private key. The agent does not hand it out. A stolen socket stops working once your session ends, and we confirmed the socket disappears at that point.
- Another ordinary user was refused:
Error connecting to agent: Permission denied.
"Root" here includes anyone who has broken into the server and gained root. A login made while you are connected can plant its own key on the target and keep access long after you leave. That is why the short lifetime of the socket gives less comfort than it seems.
The alternative: ProxyJump
Most people use forwarding to get through a bastion. With -J, your computer opens an encrypted connection through the jump host to the final server, and the keys are used from your computer alone:
ssh -J user@bastion user@server
Or in ~/.ssh/config:
Host server
HostName 10.0.0.5
ProxyJump user@bastion
In our test the connection worked, $SSH_AUTH_SOCK was empty at the target, and no agent socket existed on the jump host. A compromised bastion gets nothing to use. See ProxyJump and jump hosts.
If you do need forwarding
A typical case is running git pull on a server with your personal credentials. First ask whether a deploy key for that one repository, kept on the server, would do. If you still need forwarding, reduce what it exposes.
Ask before each use: ssh-add -c
ssh-add -c ~/.ssh/id_ed25519
Your agent now asks for confirmation on your own screen each time the key is used, so a surprise request is visible. If you refuse, the login fails:
sign_and_send_pubkey: signing failed for ED25519 "key" from agent: agent refused operation
It needs an ssh-askpass program on your computer, which you may have to install. On macOS we found none by default. Without one, test before you rely on it.
Expose fewer keys
- Run a separate agent holding only the key you need, and forward that one with
ForwardAgent /path/to/agent.sockin the host's block. - Limit how long keys stay loaded with
ssh-add -t 1h ~/.ssh/id_ed25519. - Use a key that opens only what this job needs, and not your main key.
- Look at destination constraints,
ssh-add -h, which limit where a key may be used.
Forward only to named hosts
Host build-box
ForwardAgent yes
Never set ForwardAgent yes under Host *. It is off by default, and that is right. A single careless ssh to a server you do not fully trust would hand that server your agent for the session.
When it is acceptable
It is a reasonable tradeoff when all of these hold: you trust everyone with root on that host, and you trust the host is not compromised; the session is short; and you could not do the job with -J or a deploy key. Avoid it on shared servers, on hosts exposed to the internet that others manage, and on anything you would not leave your unlocked laptop with.
Turning it off on a server
AllowAgentForwarding no
With this in sshd_config, ssh -A connected normally in our test but no socket appeared. The manual notes that users with shell access can still set up their own forwarders, so treat this as a default that prevents accidents, and not as a hard barrier.
Questions
Is SSH agent forwarding safe?
Only on servers where you trust everyone with root. While you are connected, root there can use your keys to log in elsewhere as you. It cannot copy the private key.
What is the difference between ssh -A and ssh -J?
-A lets the remote host use your agent. -J passes through a jump host to the target, and your keys are used only from your computer, so the jump host gets nothing to use.
How do I disable agent forwarding on a server?
Put AllowAgentForwarding no in sshd_config and reload sshd. Users with a shell can still build their own forwarders, so treat it as a default against accidents.
Sources
- ssh(1), the -A option
- ssh_config(5), ForwardAgent
- ssh-add(1), OpenBSD manual
- ssh-agent(1), OpenBSD manual
- OpenSSH 10.1 release notes: agent socket location