ssh-agent Explained (and When Not to Forward It)
What ssh-agent holds, how to load a key for a session, and why agent forwarding hands your identity to whoever runs the server you hop through.
Keys · Bartłomiej Żabicki · updated 2026-07-27 · 9 min read
A passphrase on your private key is good security and terrible ergonomics. Type it forty times a day and you will quietly remove it. The SSH agent exists to resolve that tension — and its one optional feature, agent forwarding, is among the easiest ways to hand your identity to someone else.
What the agent is
ssh-agent is a small background process that holds decrypted private keys in memory and
performs signatures on request. Your SSH client never receives the key itself; it asks the agent
"sign this challenge" and gets a signature back.
That gives you two things:
- You unlock the key once per session instead of once per connection.
- Programs that need to authenticate never handle the key material directly.
The agent talks over a Unix socket, and its location lives in an environment variable:
echo $SSH_AUTH_SOCK
# /private/tmp/com.apple.launchd.k3Jd9/Listeners
If that variable is empty, no agent is reachable — which is the cause of most "why is it asking again?" confusion.
Loading a key
# start an agent if your system does not run one (macOS and most desktop Linux do)
eval "$(ssh-agent -s)"
# add a key — prompts for the passphrase once
ssh-add ~/.ssh/id_ed25519
# what is loaded right now?
ssh-add -l
# forget everything
ssh-add -D
# forget after 4 hours
ssh-add -t 4h ~/.ssh/id_ed25519
ssh-add -t is underused. A key that expires at the end of the working day limits the damage if
your laptop is taken while unlocked.
Better than remembering to run ssh-add at all, let the config do it:
Host *
AddKeysToAgent yes
The key is added to the agent the first time it is used, so you are prompted once and never again that session.
On macOS
macOS runs an agent for you and integrates with the Keychain, so the passphrase survives reboots:
ssh-add --apple-use-keychain ~/.ssh/id_ed25519
Host *
AddKeysToAgent yes
UseKeychain yes
UseKeychain yes tells SSH to look up the passphrase from the Keychain rather than prompting.
Note it is macOS-only — putting it in a config you sync to Linux produces a warning on every
connection.
Why "too many keys" breaks logins
This is the agent's most common side effect, and it does not look like an agent problem.
Servers default to MaxAuthTries 6. Your client offers the agent's keys first, in order,
before anything named in your config — and every offer counts as an attempt. Load seven keys and
a server can disconnect you before your correct key is ever tried. The error you get is
Permission denied (publickey), which sends most people looking at authorized_keys.
ssh-add -l | wc -l # how many is it offering?
The fix is per-host, not global:
Host web1
IdentityFile ~/.ssh/prod_ed25519
IdentitiesOnly yes
IdentitiesOnly yes restricts the client to the keys you explicitly named, even when the agent
holds others. Full checklist in
fixing Permission denied (publickey).
Agent forwarding, and why to avoid it
Now the dangerous part. -A, or ForwardAgent yes, makes your agent's socket available on the
server you connect to, so you can hop onward from there using your local keys.
ssh -A deploy@bastion # convenient, and a bad idea on a shared box
Here is what you have actually done. The socket on that server is a live channel back to your agent, and anyone who can read it can ask your agent to sign anything. That includes:
rooton that machine- any process running as your user there
- an administrator you have never met
- anything that has already compromised the host
They cannot copy your key — the agent never sends it — but for as long as your session is open they can authenticate as you to every server that trusts that key. The requests look identical to your own, so nothing in any log distinguishes them.
The blast radius is not the one server you logged into. It is every machine your key opens.
What to do instead
Use ProxyJump. It tunnels an end-to-end connection through the intermediate host, so your key
is only ever used against the machine you actually meant to reach, and the bastion never gets a
socket:
Host bastion
HostName bastion.example.com
User deploy
Host db
HostName db.internal
User deploy
ProxyJump bastion
Details and the multi-hop form in jump hosts and ProxyJump.
If you genuinely cannot avoid it
Some environments still require forwarding. Constrain it:
# require confirmation for every signature
ssh-add -c ~/.ssh/id_ed25519
# forward only to the one host that needs it — never Host *
Host legacy-bastion
ForwardAgent yes
With -c, each signing request pops a confirmation dialog, so an unexpected one is visible rather
than silent. It turns invisible abuse into something you would notice — a mitigation, not a fix.
Two rules worth keeping: never put ForwardAgent yes under Host *, and never forward to a
machine whose administrators you would not hand a key to.
Agent, Keychain, hardware: which is which
They solve overlapping problems and get conflated constantly.
| Holds | Survives reboot | Gated by | |
|---|---|---|---|
| ssh-agent | Decrypted keys in memory | No | Whatever unlocked it |
| Apple Keychain | Encrypted keys/passphrases on disk | Yes | Face ID / Touch ID |
Hardware token (ed25519-sk, YubiKey) | Key that cannot be extracted | Yes | Physical touch |
On a phone or tablet the agent is largely beside the point: there is no long-lived shell session to populate, and the platform already offers something stronger. A key stored in the device Keychain is encrypted at rest, gated behind biometrics, and never handed to the app as raw bytes — which is how Moray does it, and why there is no passphrase to retype on a touchscreen.
Hardware-backed keys are the strongest option where they fit. ssh-keygen -t ed25519-sk creates a
key whose private half physically cannot leave the token, so even a fully compromised laptop
cannot exfiltrate it — the attacker can only use it while the token is plugged in and touched.
The short version
- The agent holds decrypted keys and signs on request; it never sends the key.
AddKeysToAgent yesbeats rememberingssh-add.ssh-add -t 8hlimits exposure.- Too many loaded keys causes
Permission denied (publickey)— fix withIdentitiesOnly yes. - Do not forward the agent. Use
ProxyJump. If forced to, scope it to one host and use-c. - On Apple platforms, the Keychain does the agent's job better and with biometrics.
Next: SSH keys explained for choosing and storing the keys the agent holds.
Written by Bartłomiej Żabicki
iOS developer writing Swift since 2015, with a homelab he administers from an iPhone more often than he would like to admit. Moray is the SSH client he wanted to exist.
Frequently asked
- Why does SSH keep asking for my passphrase?
- Because nothing is holding the decrypted key. Start an agent and add the key with ssh-add, or set AddKeysToAgent yes in your config so it is loaded on first use. On macOS, --apple-use-keychain stores the passphrase so the agent is populated after a reboot.
- Is agent forwarding safe?
- Not on a machine you do not fully trust. Anyone with root on that host can use the forwarded socket to authenticate as you to any server your key opens, for as long as your session lasts. Use ProxyJump instead, and if you genuinely must forward, add -c so every signature needs confirmation.
- What is the difference between the agent and the Keychain?
- The agent is a process holding decrypted keys in memory for the length of a session. Apple's Keychain is encrypted storage on disk, gated behind Face ID or Touch ID. A key kept in the Keychain does not need an agent at all, which is how SSH clients on iPhone and iPad avoid the problem.
try it yourself
Moray does all of this natively
A real terminal, dual-pane SFTP, tunnels and jump hosts on iPhone, iPad and Mac. Keys stay in the Keychain, hosts sync over your own iCloud, and there is no account to make.
Keep reading
- 10 minSSH Keys Explained: Ed25519, RSA, and Where They Should LiveWhich algorithm to pick, how the two halves work, and the one place a private key should ever be stored.
- 11 minSSH from an iPhone or iPad: The Complete GuideWhat iOS actually permits, how to get a usable keyboard, and why your session drops when you switch apps.
- 11 minSSH Port Forwarding Explained: Local, Remote and Dynamic-L, -R and -D, finally in an order that makes sense — with the mental model that makes the flags obvious.