When working with multi-server architectures, we often chain SSH connections like LOCAL → A → B → C
. While single-hop forwarding (LOCAL → A → B
) works perfectly, the authentication breaks when attempting the third hop to server C. This occurs because SSH agent forwarding doesn't automatically propagate through multiple user context changes - especially when switching between regular users and root.
The fundamental issue stems from how SSH agent sockets are handled during user transitions. When you:
ssh -A liquidity@A
ssh -A root@B
ssh -A liquidity@C
The root user on server B can't access the forwarded agent socket from user liquidity's session due to Unix socket permissions. The authentication chain breaks because:
- The SSH_AUTH_SOCK environment variable isn't properly inherited
- Socket file permissions restrict access to the original user
- Default SSH configs don't handle multi-user forwarding well
Instead of manual hopping, configure your ~/.ssh/config
with ProxyCommand:
Host C
HostName c.example.com
User liquidity
ProxyCommand ssh -W %h:%p root@B
ForwardAgent yes
This establishes a direct tunnel while maintaining agent context. Test with:
ssh -v C
For manual connections, ensure environment continuity:
ssh -At liquidity@A \
"sudo -u root -i --preserve-env=SSH_AUTH_SOCK \
ssh -At liquidity@B \
ssh -At liquidity@C"
The key elements are:
-t
for terminal allocation--preserve-env
to maintain the auth socket- Consistent
-A
flags at each hop
When troubleshooting, these commands help identify where the chain breaks:
# Check agent forwarding status
ssh -T liquidity@A echo \$SSH_AUTH_SOCK
ssh -T root@B echo \$SSH_AUTH_SOCK
# Verify key availability
ssh-add -L
While convenient, multi-hop forwarding increases attack surface:
- Limit ForwardAgent to specific hosts in ssh_config
- Use
AllowAgentForwarding no
on critical jump hosts - Consider temporary certificates instead of prolonged forwarding
For production environments, consider SSH certificates:
# On CA server
ssh-keygen -s ca_key -I user_id -n liquidity,root user_key.pub
# In sshd_config on all servers:
TrustedUserCAKeys /etc/ssh/ca_key.pub
This eliminates forwarding dependencies while maintaining centralized key management.
When working with chained SSH connections (LOCAL → A → B → C
), authentication breaks at the final hop (B → C
) despite proper agent forwarding configuration. This typically occurs when:
- Different usernames are used between hops (e.g.,
liquidity
on A vsroot
on B) - Agent forwarding isn't properly propagated through intermediate hosts
- SSH client configurations aren't consistently applied
Standard ssh -A
works for single-hop forwarding (LOCAL → A → B
) because:
# Working single-hop example
ssh -A liquidity@A
ssh -A root@B # Works because agent is forwarded from LOCAL
But breaks in multi-hop scenarios due to:
# Broken multi-hop example
ssh -A liquidity@A
ssh -A root@B
ssh -A liquidity@C # Fails because agent forwarding chain breaks at B
Solution 1: Persistent Agent Forwarding Configuration
Add these lines to ~/.ssh/config
on ALL intermediate hosts (A and B):
Host *
ForwardAgent yes
IdentityAgent ~/.ssh/agent.sock
Solution 2: ProxyJump Alternative
Modern OpenSSH (7.3+) offers a cleaner approach:
ssh -J liquidity@A,root@B liquidity@C
Or in config:
Host C
HostName C.example.com
User liquidity
ProxyJump liquidity@A,root@B
Solution 3: Environment Variable Fix
When usernames differ between hops, ensure proper socket propagation:
# On host A after first connection:
echo 'export SSH_AUTH_SOCK=~/.ssh/agent.sock' >> ~/.bashrc
Verify agent forwarding at each hop:
# Check if agent is available
ssh-add -l
# Check socket connection
ls -la $SSH_AUTH_SOCK
# Verbose connection test
ssh -vvv liquidity@C
- Limit agent forwarding to trusted hosts only
- Consider using
ssh -J
instead of persistent forwarding - Set explicit host configurations rather than
Host *