# SSH agent forwarding risk

`ssh-agent` is the local daemon that holds decrypted private keys after `ssh-add`. The SSH client talks to it over a Unix domain socket (`$SSH_AUTH_SOCK`) when signing authentication challenges. Agent forwarding (`-A` flag, or `ForwardAgent yes` in `~/.ssh/config`) extends that socket to the SSH server: the remote host can now ask the local agent to sign challenges on its behalf.

This is convenient — `ssh user@hop`, then `ssh user@next` works without copying any private keys around — and dangerous: root on the destination host (or anyone who can reach the forwarded socket file) can use the agent to authenticate to anything the operator's loaded keys can reach. They never see the private key material, but they don't need to. They just ask the agent to sign on demand.

## The attack shape

1. Operator runs `ssh -A user@target`, has `id_rsa_prod` loaded in their agent.
2. Attacker with root on `target` enumerates `/tmp/ssh-*/agent.*` sockets, finds the operator's.
3. Sets `SSH_AUTH_SOCK` to that path, runs `ssh-add -l` to list loaded keys.
4. Picks an inbound host (`ssh-add -l` shows comments, often hostnames), runs `ssh prod-bastion.corp` — agent signs, auth succeeds.

The operator never sees this. The forwarded socket lives only as long as the SSH session, but that's plenty of time. Helton has a full writeup at [Zero Effort Private Key Compromise](https://grahamhelton.com/blog/ssh_agent/), still unread for this wiki.

## Why operators do it anyway

Without `-A`, multi-hop workflows force key copies onto every hop. That's worse — now there's persistent key material on machines the operator doesn't control. Agent forwarding is the lesser of the two evils when the alternative is dropping keys.

## Safer alternatives

- **`-J` / `ProxyJump`** — for the common case of "I want to land on the inner host, not work from the hop." Routes through the hop without ever exposing keys on it. Authentication still happens client-side at each leg, but the agent socket stays local. This is what `-A` is most often misused for.
- **Per-host keypairs** — break the "one key opens everything" assumption. A compromised agent only authorizes the keys currently loaded.
- **Confirmation prompt (`ssh-add -c`)** — forces the agent to ask before signing each request. Slow but breaks unattended replay.
- **Time-limited keys (`ssh-add -t <seconds>`)** — keys expire from the agent automatically; combine with `-c` for layered defense.
- **[[ephemeral-credentials]]** — short-lived SSH certificates from a CA (Vault, smallstep, EC2 Instance Connect) take the long-lived-key problem off the table entirely.

## Detection

The `Match` keyword in `~/.ssh/config` can scope `ForwardAgent yes` to specific hosts where the operator has actually audited the trust boundary, defaulting to `no` everywhere else. Inverting the default — opt-in instead of opt-out — is the cheapest mitigation.

## See also

- [[ssh-port-forwarding]] — the orthogonal `-L/-R/-D/-J` modes that handle traffic, not credentials
- [[ssh-port-forwarding-cheatsheet]] — Helton's red-team walkthrough where this risk shows up
- [[ephemeral-credentials]] — the structural fix for long-lived SSH keys
- [[long-lived-keys]] — Ludwig's general case against key rotation as the answer
- [[credential-compartmentalization]] — the same principle applied across credential types
