The short answer
SSH agent forwarding lets a server you are logged into use the private key that stays on your phone. You turn it on with ssh -A or ForwardAgent yes, the server gets a socket instead of a key, and it can then authenticate onward — to GitHub, to a second server — as you, without any key material ever being written to its disk.
It works from a phone. Blink Shell, Termius, ConnectBot and Termux all implement it, and there is one thing on mobile it is uniquely good for: a key generated inside the Secure Enclave or the Android keystore cannot be copied to a server, so forwarding is the only way to use it from one.
The price is precise, and it is the whole reason this article exists: for as long as that session is open, anyone who can bypass file permissions on that host — root, or any process running as your user — can sign with your key. They cannot steal it. They do not need to. They ask the socket, and the socket answers.
So the rule that survives contact with reality:
- Off by default. Never
Host *, never a global toggle in the app. - On per host, per session, for a machine whose root you already are.
- Never to a bastion, a CI runner, a shared box, or a server you are logging into because you suspect it is compromised.
- If you only want to reach a second server through a first one, you do not need this at all. That is ProxyJump, and the key never leaves the phone.
What SSH agent forwarding actually does
An agent is a program that holds unlocked private keys and answers exactly one kind of question: here is a challenge, sign it. On a laptop that is ssh-agent. On a phone it is the SSH app itself, backed by the iOS Keychain or the Android Keystore.
When you connect with -A, your client opens an extra channel ([email protected]) inside the encrypted session. sshd on the far side creates a UNIX-domain socket — something like /tmp/ssh-Xf3kQ2/agent.14127 — owned by your user, and sets SSH_AUTH_SOCK in your shell to point at it. Any process that can open that socket can send signature requests back down the tunnel to your phone.
Three consequences follow, and they explain everything else:
- No key material crosses. Only challenges and signatures. A compromised server cannot end up holding your private key.
- Anyone who can open the socket is you. File permissions are the only guard, and root ignores file permissions. Root can enumerate every
/tmp/ssh-*socket on the box, exportSSH_AUTH_SOCK, and authenticate to anything your key opens. So can malware running as your own user. - The window is the session. Close the connection and the socket dies. This is why “per session” is a real mitigation rather than security theatre.
The ssh_config manual says this in its own words: agent forwarding should be enabled with caution, because users able to bypass file permissions on the remote host can access the local agent through the forwarded connection — they cannot obtain key material, but they can perform operations on the keys that let them authenticate using your identities. The same paragraph recommends a jump host as the safer alternative.
When it is safe and when it is not
The question is never “is agent forwarding secure” in the abstract. It is: do I trust root on that box, more than I would mind that box signing as me everywhere my key works?
| Where you are forwarding to | Verdict |
|---|---|
| A personal VPS or dev box where you are the only root | Acceptable, per session |
Your own server, so it can git push as you | Acceptable — but a deploy key is better and permanent |
| A jump host or bastion | No. Use ssh -J; that is what it is for |
| A CI runner, a Jenkins node, a build agent | No. This is the case that has actually burned people |
| A client’s server, a shared box, anything with other admins | No. You are trusting every one of them |
| A box you are logging into because it is behaving strangely | Absolutely not. Forwarding into a possible compromise hands over the key |
That fourth row is not hypothetical. In the Matrix.org breach of April 2019, an attacker exploited a known Jenkins vulnerability, then planted a script on the Jenkins node that hijacked SSH agent forwarding from anyone who logged in. On 4 April an engineer connected to that node with their agent forwarded, for convenience, to reach other machines. The attacker inherited that access and ended up with superuser reach across much of the production network, including the databases. The published remediation was to stop forwarding agents into that tier and route access through a bastion instead.
One more piece of evidence about the shape of this risk: the recent OpenSSH agent vulnerabilities all have “you forwarded your agent” as their precondition. CVE-2023-38408 (July 2023, affecting OpenSSH 5.5 through 9.3p1) let a malicious server execute code on the client through the forwarded agent. And CVE-2026-73281, fixed in OpenSSH 10.5 on 11 August 2026, was a misinteraction between agent locking and the [email protected] extension: locking the agent switched off the check that distinguishes a local request from one arriving down a forwarded connection, so an attacker holding a forwarded socket could perform operations meant to be local-only — including using keys that carried destination restrictions. Ubuntu shipped the fix in USN-8721-1 on 3 September 2026. Do not forward, and none of these reach you.
What changes when the agent is a phone
Everything above is true on a laptop too. Five things are specific to doing it from a handset, and two of them are genuinely in mobile’s favour.
1. The agent is an app, and apps get suspended. Your phone is now a service the server depends on. If iOS suspends the SSH app — Termius documents that recent iOS and iPadOS versions stop an app’s background processes in roughly 20 to 30 seconds — the agent stops answering. A git clone on the server that needs a signature mid-operation will simply hang and then fail. Anything long-running belongs in tmux on the server, and anything that needs your agent should finish while you are looking at the screen.
2. Not every client can do it. Agent forwarding requires the app to implement an agent and the auth-agent channel. Plenty of mobile SSH clients do public-key authentication and nothing more; there is no setting to find, because the machinery is not there.
3. Biometric keys turn the risk into an alarm. This is the good part. If your key is gated by Face ID or a fingerprint per signature, then a hijacked socket cannot be used silently — the attacker’s request surfaces as a prompt on a phone in your pocket that you did not trigger. That is the mobile-native version of ssh-add -c, and it is stronger than anything a laptop offers by default. Treat an unexplained authentication prompt as an incident, not an annoyance: decline it and kill the session.
4. Forwarding is the only way to use a hardware-backed mobile key remotely. A Secure Enclave key is ecdsa-sha2-nistp256 and non-extractable by hardware design (the full explanation). You cannot scp it to a server, which is the point. If you want a server to act with that key — signing commits, for instance — forwarding is the mechanism. That is the strongest legitimate reason to use it from a phone.
5. You get the app’s controls, not OpenSSH’s. ssh-add -c, -t and -h are OpenSSH features. A GUI client gives you whatever toggles it chose to build, which is usually one on/off switch per host. If you need real constraints, you need a client with a command line.
How to forward your agent from a phone
| Client | Platform | Agent forwarding | How |
|---|---|---|---|
| Blink Shell | iOS / iPadOS | Yes | Real OpenSSH-style flags: ssh -A host, or ForwardAgent yes in the host’s SSHConfig field (docs) |
| Termius | iOS / Android / desktop | Yes | Uses its own built-in agent rather than the OS agent; agent forwarding is a per-host setting (docs) |
| ConnectBot | Android | Yes | Long-press host → Edit host → Use SSH auth agent: No / Yes / With confirmation (issue #378) |
| Termux | Android | Yes | It is actual OpenSSH — ssh-agent, ssh-add -c/-t/-h, ssh -A, all exactly as documented |
| TermAI | iOS / Android | Yes | Listed among the supported auth methods on the features page; keys stay in the iOS Keychain / Android Keystore |
ConnectBot’s three-state setting deserves the attention. With confirmation is the correct choice and it is rare in mobile clients — every signature request raises a prompt, which means a compromised server gets one visible attempt rather than unlimited silent ones. If you are choosing an Android client for this specific job, that setting is a reason to pick it; the wider comparison is in ConnectBot alternatives.
In a client with a real config file, the shape you want is per host and nothing wider:
Host devbox
HostName devbox.example.com
User chen
IdentityFile ~/.ssh/id_ed25519_phone
IdentitiesOnly yes
ForwardAgent yes # this host only
Host bastion
HostName bastion.example.com
ForwardAgent no # explicit, so no future edit turns it on
Host prod-*
ProxyJump bastion # jump, do not forward
ForwardAgent no
IdentitiesOnly yes matters more on a phone than on a laptop, because mobile clients tend to accumulate keys and offer all of them — which is how you meet too many authentication failures.
On the server side, forwarding must also be permitted where you land. AllowAgentForwarding in sshd_config defaults to yes, and per-key options in authorized_keys can override it: no-agent-forwarding, or restrict, which disables agent forwarding, port forwarding, X11 and pty allocation in one word since OpenSSH 7.2 (29 February 2016). Note the direction: those settings control whether other people can forward agents into your box. They do nothing to protect your own key on a host someone else runs.
Six settings that make it defensible
If you have decided a particular hop justifies forwarding, these are the controls worth applying, roughly in order of value per unit of effort.
1. Per host, never Host *. A global ForwardAgent yes means every server you touch — including one you connect to once, from a link, in a hurry — gets a live signing oracle. This single line is the most common way people get the Matrix outcome.
2. Require confirmation for every use. On OpenSSH, ssh-add -c ~/.ssh/id_ed25519 makes the agent ask before each signature. In ConnectBot, that is the With confirmation value. On a phone with a biometric-gated key, you get it for free. This converts a silent compromise into a visible one.
3. Give the key a lifetime. ssh-add -t 30m makes the agent forget the key after thirty minutes, so a forgotten session does not stay signable all afternoon.
4. Constrain the destination. OpenSSH 8.9 (23 February 2022) added destination-constrained keys, documented at openssh.com/agent-restrict.html:
ssh-add -h "bastion.example.com>prod-web.internal" ~/.ssh/id_ed25519
The client sends a [email protected] message on every hop, cryptographically tying the session to the host key it authenticated, so the agent can reconstruct the path a request travelled and refuse anything off the declared route. A key stolen through a forwarded socket then only works on the one destination you named. The catch: every ssh in the chain must be new enough to send the binding, which in practice means Termux or a desktop, not a GUI mobile client.
5. Forward a smaller agent, not your whole keyring. Since OpenSSH 8.2 (14 February 2020), ForwardAgent accepts an explicit socket path or the name of an environment variable, not just yes/no. Run a second agent holding only the one key that hop needs, and forward that. What is exposed is then one credential rather than every key you own.
6. Patch the agent. The agent is the component the CVEs land in — CVE-2023-38408 and CVE-2026-73281 both required a forwarded socket to exploit. Worth saying plainly: those are OpenSSH bugs, and a mobile app that implements its own agent is running its own, much less audited code. That is not a reassurance.
The alternatives that are usually better
Most requests for agent forwarding are really requests for something else. Check this list before enabling it.
| What you actually want | Better tool | Why |
|---|---|---|
| Reach server B through bastion A | ssh -J bastion B | Authentication happens on the phone; A relays ciphertext and can do nothing with it |
| Let a dev box pull and push one repo | Deploy key on that repo | Read-only by default, scoped to one repo, survives your phone being asleep |
| Let a shared box use Git | Machine user with its own account | Proper isolation and its own audit trail |
| Reach a private host directly | Tailscale | The second hop stops existing; there is nothing to jump through (setup) |
| Run one command on a second host | ssh -J a b 'systemctl status nginx' | No interactive session on the middle box at all |
| Sign commits made on the server | Forwarded Secure Enclave key — the real use case | The key cannot be copied there; forwarding is the only route |
ProxyJump is the substitution that covers most of the traffic. ssh -J bastion prod-web opens a direct-tcpip channel through the bastion and then performs a fresh key exchange and authentication with the final host, inside that channel — so the bastion sees encrypted bytes, never a signing socket, and never needs to give you a shell. Two host keys get verified rather than one, which is why a first connection from a phone prompts twice (host keys and TOFU).
The Git case is worth spelling out because it is the one people arrive here for: if a box needs to talk to GitHub, a deploy key (read-only by default, scoped to one repository) beats a forwarded agent on every axis except setup time, and it keeps working when your phone is in a drawer. The full decision — deploy key, user key, machine user, or forwarding — is in Git from your phone over SSH.
And the mobile-specific version: with a mesh network on the phone, reaching the target directly usually removes the middle host from the picture entirely. There is no agent to forward when there is no hop. This is why TermAI embeds Tailscale rather than treating the bastion chain as the normal case — the safest forwarded agent is the one you never needed. The rest of the mobile threat model is in is mobile SSH safe for production.
Checking and troubleshooting
First, verify from the remote host that forwarding actually happened:
echo $SSH_AUTH_SOCK # empty means no agent was forwarded
ssh-add -l # lists the keys your phone is offering
ssh-add -l printing your key fingerprint means the chain works end to end. It also means anyone else on that box can run the same command, which is a useful thing to see once.
| Symptom | Cause | Fix |
|---|---|---|
Could not open a connection to your authentication agent | Nothing was forwarded | Client did not send -A; or the host’s sshd has AllowAgentForwarding no; or the key carries restrict / no-agent-forwarding |
SSH_AUTH_SOCK set, but ssh-add -l says The agent has no identities | The socket reached the host; the phone’s agent is empty or locked | Unlock the app, or load the key into it |
Works in ssh, fails in scp or sftp | Deliberate: those tools do not forward by default | Pass -A explicitly (OpenSSH 8.4, 27 September 2020) |
Works as your user, fails under sudo | sudo resets the environment and drops SSH_AUTH_SOCK | sudo -E, or do the SSH part as your own user |
Agent admitted failure to sign | The requested key is not in the agent, or a destination constraint refused it | ssh-add -l to confirm; check the -h path you declared |
| Everything hangs after you switch apps | The phone suspended the agent | Keep the app foregrounded for anything that needs a signature |
| A Face ID prompt you did not trigger | Something on the remote host is using your socket | Decline, disconnect, treat as an incident |
If the connection itself is not establishing, the problem is upstream of any of this — start at Permission denied (publickey) or, in the worse case, getting back into a server you are locked out of.
FAQ
What is SSH agent forwarding?
A client feature, enabled with ssh -A or ForwardAgent yes, that lets a server you are logged into ask your local machine to sign authentication challenges with your private key. The server never receives the key. sshd creates a UNIX-domain socket on the remote host and points SSH_AUTH_SOCK at it; anything that can talk to that socket can request a signature. That is how you authenticate onward from that server to a third host without storing a key on the middle one. On a phone, the agent is the SSH app, and the key stays in the iOS Keychain, the Secure Enclave, or the Android Keystore.
Is SSH agent forwarding safe? It is safe exactly to the degree that you trust root on the host you forward to, for as long as the session is open. The OpenSSH manual states the risk plainly: users who can bypass file permissions on the remote host can reach your forwarded agent, and while they cannot extract the key, they can perform operations with it — so they can authenticate as you anywhere that key is accepted. In the April 2019 Matrix.org breach, an attacker who controlled a Jenkins host ran a script that hijacked the forwarded agent of any engineer who logged in, and used it to reach production.
Can I use ssh -A from an iPhone or Android phone?
Yes, in clients that implement an agent. Blink Shell on iOS supports agent forwarding and lets you set ForwardAgent per host through its SSHConfig field. Termius uses its own built-in agent rather than the OS agent and exposes agent forwarding as a per-host setting. ConnectBot on Android has a Use SSH auth agent option in each host’s settings with three values — No, Yes, and With confirmation. Termux runs real OpenSSH, so ssh-agent, ssh-add and every documented flag behave exactly as they do on a desktop.
Why would I forward an agent from a phone at all?
Because a mobile SSH key is usually non-extractable by design. A key generated in the Secure Enclave or the Android keystore cannot be copied to a server, so forwarding is the only mechanism that makes it usable from one — to git push from a development box, or to sign commits there with a key that Face ID guards. If your goal is only to reach a second server through a first, you do not need forwarding: ProxyJump does that with the key never leaving the phone.
What is the difference between agent forwarding and ProxyJump?
Where the authentication happens. With agent forwarding you log into the jump host and run ssh from there, so that host holds a live socket that can sign with your key. With ssh -J the jump host only relays an encrypted channel; key exchange and authentication with the final host happen on your phone, end to end, and the middle box can read nothing. A compromised jump host can impersonate you in the first case and can do nothing in the second.
How do I make agent forwarding less dangerous?
Enable it per host rather than in a Host * block. Require confirmation for every signature — ssh-add -c, ConnectBot’s With confirmation, or a biometric-gated key, so an unexpected prompt becomes your intrusion alarm. Give keys a lifetime with ssh-add -t. Add destination constraints with ssh-add -h, introduced in OpenSSH 8.9 on 23 February 2022, to bind a key to a specific chain of hosts. And keep the agent patched: OpenSSH 10.5, released 11 August 2026, fixed CVE-2026-73281, where a locked agent could be driven from the far end of a forwarded connection.
Does agent forwarding work with scp and sftp?
Not by default, even when your ssh_config enables it. OpenSSH 8.4, released 27 September 2020, added an explicit -A flag to scp and sftp precisely because those tools deliberately do not forward the agent unless asked on the command line. If a transfer from an intermediate host fails to authenticate while an interactive session from the same host works, this asymmetry is usually why.
Quick facts
ssh -Aforwards the agent;-adisables it;ForwardAgentdefaults tono. Keep it that way and set it per host.- No key material crosses. A socket appears on the remote host and
SSH_AUTH_SOCKpoints at it; requests travel back to the phone for signing. - Root on that host, or any process running as you, can use the socket for the life of the session. They cannot extract the key and do not need to.
- Matrix.org, April 2019: a compromised Jenkins node hijacked forwarded agents from engineers who logged in, leading to superuser access across production.
- Recent agent CVEs need a forwarded agent: CVE-2023-38408 (July 2023, OpenSSH 5.5–9.3p1) and CVE-2026-73281 (fixed in OpenSSH 10.5, 11 August 2026).
- Mobile clients that support it: Blink Shell (iOS), Termius (iOS/Android, own built-in agent), ConnectBot (Android, with a With confirmation mode), Termux (real OpenSSH), TermAI.
- A biometric-gated key is the best confirmation prompt available — an unexpected Face ID request is an intrusion alarm.
- A suspended app is a dead agent. iOS stops background processes in roughly 20–30 seconds; signature requests then hang.
- Destination constraints (
ssh-add -h, OpenSSH 8.9, 23 February 2022) bind a key to a named host chain, but every hop’ssshmust support the binding extension. scpandsftpnever forward by default — pass-Aexplicitly (OpenSSH 8.4+).- If you only need to reach a host behind a bastion, use
ssh -J. If you control both ends, a mesh network removes the hop entirely.
Free on iOS and Android. 5 AI requests/day on the free tier, plus unlimited SSH/SFTP and built-in Tailscale.