SSH Agent Forwarding Without Leaking Your Farm
Use ssh-agent for passphrases, prefer ProxyJump over ForwardAgent, and lock confirmation so a compromised jump host cannot silently sign as you.
Netbay Cloud Team
Netbay Engineering
On this page
ssh-agent exists so a passphrase-protected key can sit decrypted in memory for a working session instead of being typed on every hop. Agent forwarding exists so a remote host can ask that same agent to sign a challenge for a third host. The first is a convenience you should use. The second is a loaded gun that people enable with ssh -A because a blog post from 2014 told them to. A compromised jump host with a forwarded agent can authenticate as you to every machine that trusts that key, for as long as the agent socket is live. That is how a single bastion breach becomes a farm breach.
The next-layer rule is simple. Load keys into an agent. Do not forward the agent unless you can explain, for this hop, why ProxyJump cannot do the job.
What the agent actually holds
Start the agent once per login session. Desktop environments often already did. ssh-add -l lists fingerprints. If it errors with no agent, eval $(ssh-agent) in that shell only, then add keys. Prefer ssh-add -c so every signature pops a confirmation dialog. Prefer ssh-add -t 8h so a forgotten agent does not hold a decrypted key overnight.
ssh-add -D
ssh-add -c -t 8h ~/.ssh/id_ed25519_bastion
ssh-add -c -t 8h ~/.ssh/id_ed25519_prod
ssh-add -lIdentitiesOnly in ~/.ssh/config still matters. The agent can hold ten keys; the client should offer one. Without IdentitiesOnly, MaxAuthTries on a hardened sshd will drop you before the right key is tried. The agent is a cache, not a substitute for per-host IdentityFile.
Never copy a private key onto a jump host so that ssh from that host to an app node "just works". That is worse than forwarding. The key on disk is stealable at leisure. A forwarded agent is stealable only while you are connected, which is still too long.
Why ForwardAgent leaks the farm
When you ssh -A bastion, the client creates a Unix socket on the bastion and sets SSH_AUTH_SOCK in the remote session. Any process that can read that socket — you, root, a compromised binary running as you — can ask your laptop to sign SSH challenges. From the app node's point of view the signature is valid. From your point of view you never typed ssh app. The jump host became a signing oracle.
This is not theoretical. A web app running as the same user, a malicious git hook, a world-readable socket in /tmp leftover from an older OpenSSH, or a rootkit on the bastion all get the same power. Confirmation with ssh-add -c mitigates silent use if you notice dialogs you did not expect. It does not mitigate a dialog you click because you are deploying.
ProxyJump does not create that socket. The laptop opens hop one, then hop two, and both handshakes happen locally. The bastion sees an encrypted channel, not an agent. That is why the previous posts treated ProxyJump as the default path through a Lucknow jump VPS.
Lock sshd so forwarding cannot sneak in
On every host that is not a carefully justified exception, turn it off. The client can still request forwarding; the server will refuse.
sudo tee /etc/ssh/sshd_config.d/55-no-agent-fwd.conf > /dev/null <<'EOF'
AllowAgentForwarding no
AllowStreamLocalForwarding no
EOF
sudo sshd -t && sudo systemctl reload sshIf you truly need forwarding — a vendor tool that SSHs onward from the remote shell and cannot be taught ProxyJump — scope it. Match User deploy and AllowAgentForwarding yes only on that account, and only on the bastion, never on app nodes. Combine it with ssh-add -c and a dedicated key that has no access to production databases. Forwarding a key that can reach everything is the failure.
Disable ForwardAgent in the client too. Host * with ForwardAgent no stops a muscle-memory -A. If a single alias needs it, set ForwardAgent yes only there, with a comment that names the ticket and the expiry.
StreamLocal forwarding is the cousin that exposes Unix sockets. If you do not need it, off. People accidentally forward Docker sockets and then wonder who is building images from the bastion.
CI, git, and the exceptions that still are not -A
Git over SSH from a workstation to GitHub or a self-hosted forge uses your local agent. No forwarding. Git running on a server during deploy should use a deploy key or a short-lived cert, not your forwarded personal key. ansible with ProxyJump and IdentitiesOnly will SSH from your laptop through the bastion; it does not need an agent on the remote.
If a build runner must SSH into a private node, give the runner its own key or cert with a force-command and a source-address of that runner. Do not install your laptop key on the runner "temporarily".
A short takeaway: the agent stays on the laptop, keys stay passphrase-protected, ProxyJump replaces -A, and sshd refuses forwarded agents by default. Treat a forwarded socket as a production incident waiting for a compromised hop. Practice the locked-down path on a Netbay Ubuntu 24.04 VPS — up in under 60 seconds at netbayhosts.in.
Keep reading
Follow along on a real VPS
Deploy Linux in under 60 seconds
These guides are written against Ubuntu, Debian, and RHEL-family images — the same ones on NetBay.
Deploy an instance