SSH Agent Forwarding Through Sudo: Preserving Keys When Switching Users


3 views

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