When using SSH agent forwarding to hop between servers, we often hit a frustrating roadblock: sudo -u otheruser
breaks our forwarded authentication. Here's why this happens:
# Works fine with original user
$ sudo -HE ssh git@github.com
Hi username! You've successfully authenticated...
# Fails when switching users
$ sudo -HE -u otheruser ssh git@github.com
Permission denied (publickey)
Two mechanisms conspire against us:
- Environment sanitization: Sudo strips most environment variables by default, including
SSH_AUTH_SOCK
- Socket permissions: The SSH agent socket (
/tmp/ssh-*/agent.*
) typically has 600 permissions
1. Sudo Environment Preservation
First, configure sudo to preserve the critical environment variable:
# In /etc/sudoers or /etc/sudoers.d/ssh_forwarding
Defaults env_keep += "SSH_AUTH_SOCK"
Then use sudo with -E
flag:
sudo -HE -u otheruser ssh git@github.com
2. Socket Permission Fix
If the socket permissions are the issue, either:
# Option A: Change socket permissions (temporary)
chmod 777 $(dirname $SSH_AUTH_SOCK)
# Option B: Create shared directory
mkdir -p /tmp/shared-ssh
chmod 777 /tmp/shared-ssh
3. SSH Config Alternative
For frequent use, set up a dedicated SSH config:
# ~/.ssh/config
Host jump-through-sudo
User otheruser
HostName localhost
ForwardAgent yes
IdentityFile ~/.ssh/id_rsa
Then chain your commands:
ssh -A serverA
sudo -u otheruser ssh jump-through-sudo
Here's how to clone a repo through sudo:
# With proper sudo configuration
sudo -HE -u deployuser git clone git@github.com:org/repo.git
# Alternative approach
ssh -A deploy@production
sudo -u deployuser --preserve-env=SSH_AUTH_SOCK git pull
- Agent forwarding exposes your keys to the remote system
- Only use with trusted servers and for specific purposes
- Consider using
ssh-add -c
for confirmation dialogs - For production systems, prefer deployment keys over personal keys
When working with remote servers, we often chain operations like:
local → ssh → server A → sudo → otheruser → ssh → server B
The SSH agent forwarding breaks at the sudo boundary because:
- Environment variables (SSH_AUTH_SOCK) get reset
- The Unix domain socket has restrictive permissions (typically 0700)
This workflow is critical when:
- Deploying code via git from intermediate jump hosts
- Running orchestration scripts across multiple servers
- Maintaining security through privilege separation
Solution 1: Preserving Socket Access
Modify sudoers to preserve the SSH_AUTH_SOCK variable:
# /etc/sudoers.d/ssh_agent
Defaults env_keep += "SSH_AUTH_SOCK"
Then adjust socket permissions temporarily:
$ chmod 777 $SSH_AUTH_SOCK # Before sudo
$ sudo -u otheruser ssh git@github.com
$ chmod 700 $SSH_AUTH_SOCK # After use
Solution 2: SSH Config ProxyCommand
Create a seamless tunnel in ~/.ssh/config:
Host serverB-via-A
HostName serverB.example.com
ProxyCommand ssh -q serverA.example.com sudo -u otheruser nc %h %p
ForwardAgent yes
Solution 3: Sudo Wrapper Script
Create /usr/local/bin/ssh_sudo:
#!/bin/bash
TMP_SOCK=$(mktemp -u /tmp/ssh_agent.XXXXXX)
chmod 777 $TMP_SOCK
socat UNIX-LISTEN:$TMP_SOCK,fork UNIX-CONNECT:$SSH_AUTH_SOCK &
sudo -u $1 SSH_AUTH_SOCK=$TMP_SOCK "${@:2}"
kill %1
rm -f $TMP_SOCK
Usage:
$ ssh_sudo otheruser ssh git@github.com
- Always restrict sudo privileges to specific commands
- Use temporary permission changes rather than permanent
- Consider SSH certificates instead of agent forwarding for complex scenarios
For frequent use, set up proper jump host configuration:
Host jump-server
HostName serverA.example.com
User myuser
ForwardAgent yes
Host target-server
HostName serverB.example.com
ProxyJump jump-server
User otheruser