SSH Agent Forwarding Through Multiple Hops: Fixing Authentication Breakage in Chained Connections


4 views

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 vs root 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 *