Agent forwarding is not the agent. Forwarding asks sshd on the remote host to create its own socket and point SSH_AUTH_SOCK at it, so programs on that host can ask your agent back home to sign. The remote never holds the key, and it never needs to: it holds full use of it.
That is the standard way to reach a machine behind a bastion, and it is also a capability handed to every hop in the path. Most people who reach for -A wanted ProxyJump instead, which relays TCP and leaves the agent at home.
The remote gets the socket, not the key
ssh_config(5) is blunt about the trade. ForwardAgent takes yes, no (the default), an explicit socket path, or the name of an environment variable, and the man page warns that users able to bypass file permissions on the remote host for the agent’s socket can reach the local agent through the forwarded connection. The important half is the next sentence: an attacker cannot obtain key material from the agent, but they can perform operations on the keys that authenticate using the loaded identities.
The run behind this note shows exactly that shape. A third process given nothing but SSH_AUTH_SOCK — no key file, no passphrase — listed the identity and obtained an 88-byte signature. That is the whole capability a forwarded agent confers on a hop.
Forwarding is opt-in per hop
The chain of hosts that can sign is exactly the set of hops that ran ssh with forwarding enabled. ssh -A bastion exposes the agent to bastion; a second ssh -A build-box from there exposes it to build-box as well, while ssh build-box without -A stops at bastion. Nothing cascades by accident, but nothing warns you either.
ProxyJump is the safer shape. ssh_config(5) describes it as connecting to the target by first making an ssh connection to the jump host and then establishing a TCP forwarding to the ultimate target from there. The jump hosts forward packets rather than signatures, so the agent never leaves the workstation, and ssh -G confirms that -o ProxyJump=bastion leaves forwardagent at no.
What a server can and cannot enforce
On the server side, sshd -T reports allowagentforwarding yes when the configuration says nothing, and AllowAgentForwarding no once you set it. The man page adds a warning worth reading twice: disabling agent forwarding does not improve security unless users are also denied shell access, because they can always install their own forwarders. Treat the directive as hygiene, not as a boundary.
What does work is narrowing the identity. ssh-add -c demands confirmation for every use, and a signature request with no tty to answer the prompt was refused. ssh-add -h 'bastion>prod-db' constrains a key to specific source and destination host pairs and tests the whole path, forwarding included, against them. ssh-add -t bounds how long any of this remains possible.
When it breaks
The classic failure is a compromised bastion. Whoever owns that host inherits your identity for as long as the agent holds the key: they can authenticate to every host the key opens, and they can use the forwarded agent to reach further hosts you never intended. Rotating the key on disk does not interrupt it, because the loaded copy in your agent is still the old one and still signs.
The quieter failure is forwarding left on globally. A Host * stanza with ForwardAgent yes turns every connection into a delegation, including the one to a colleague’s test box, and the socket that appears on the remote is owned by your account there and reachable by root on that machine. Forwarding should be a per-host decision, and it should not outlive the task that needed it.
Check it quickly
Find out what you are actually delegating:
ssh -G prod-db | grep -iE '^(forwardagent|proxyjump)'
grep -rin forwardagent ~/.ssh/config
ssh-add -l
ssh -o ForwardAgent=no -J bastion,build-box prod-db
sshd -T | grep -i allowagentforwarding
The first line prints the effective values, so forwardagent yes for a host you do not control is the finding; the last one, run on the server, shows whether the server will accept forwarding at all. ssh-add -l should list only the identities the task needs, because everything loaded is delegated along with them.
Sources
- OpenBSD: ssh_config, ForwardAgent and its warning, and ProxyJump as TCP forwarding from the jump host, checked 2026-09-16.
- OpenBSD: sshd_config, AllowAgentForwarding, its default, and why disabling it is not a boundary, checked 2026-09-16.
- OpenBSD: ssh-add, -c confirmation, -t lifetime, and -h destination constraints for forwarded keys, checked 2026-09-16.