Skip to content

Instantly share code, notes, and snippets.

@martijnvermaat
Created December 21, 2013 15:06
Show Gist options
  • Save martijnvermaat/8070533 to your computer and use it in GitHub Desktop.
Save martijnvermaat/8070533 to your computer and use it in GitHub Desktop.
SSH agent forwarding and screen

SSH agent forwarding and screen

When connecting to a remote server via SSH it is often convenient to use SSH agent forwarding so that you don't need a separate keypair on that server for connecting to further servers.

This is enabled by adding the

ForwardAgent yes

option to any of your Host entries in ~/.ssh/config (or alternatively with the -A option). Don't set this option in a wildcard Host * section since any user on the remote server that can bypass file permissions can now als use keys loaded in your SSH agent. So only use this with hosts you trust.

The problem with screen

Unfortunately, this doesn't work as-is with GNU screen. On every new SSH connection, agent forwarding is setup via a socket specified in the SSH_AUTH_SOCK environment variable (usually somewhere in /tmp). So the socket location will be different on each connection. However, your typical screen session will live over several SSH connections and the shells in your screen session won't know where to find the current socket (their environments are not updated).

Fixing agent forwarding with screen

A simple fix is to symlink to the current socket from a fixed location on each new connection and have SSH look for the socket in that fixed location (specified by the SSH_AUTH_SOCK environment variable). We'll use ~/.ssh/ssh_auth_sock for the symlink location.

To have SSH within a screen session use the symlink, add the following line to ~/.screenrc:

setenv SSH_AUTH_SOCK $HOME/.ssh/ssh_auth_sock

To update the symlink we'll use the ~/.ssh/rc file which is executed by SSH on each connection. This can be any executable file, so something like the following script will do:

if test "$SSH_AUTH_SOCK" ; then
    ln -sf $SSH_AUTH_SOCK ~/.ssh/ssh_auth_sock
fi

Unfortunately, this will break X11 forwarding because SSH runs xauth on each connection, except when there is a ~/.ssh/rc file. We can fix this by running xauth from our ~/.ssh/rc as suggested in the sshd(8) manual page.

This is our complete ~/.ssh/rc file:

#!/bin/bash

# Fix SSH auth socket location so agent forwarding works with screen.
if test "$SSH_AUTH_SOCK" ; then
    ln -sf $SSH_AUTH_SOCK ~/.ssh/ssh_auth_sock
fi

# Taken from the sshd(8) manpage.
if read proto cookie && [ -n "$DISPLAY" ]; then
        if [ `echo $DISPLAY | cut -c1-10` = 'localhost:' ]; then
                # X11UseLocalhost=yes
                echo add unix:`echo $DISPLAY |
                    cut -c11-` $proto $cookie
        else
                # X11UseLocalhost=no
                echo add $DISPLAY $proto $cookie
        fi | xauth -q -
fi

Credits go to this blog post: Managing SSH Sockets in GNU Screen

@cinderblock
Copy link

cinderblock commented Jul 6, 2023

The persistent issue here is: what to do when existing connections close or when there are multiple agent connections open. Without a dedicated (user) daemon on the system to handle this moving target optimally for all cases, we get to pick some default simple strategy for ourselves.

My strategy is that a new connection with an agent gets priority and replaces any existing agent. This is accomplished simply by two small additions to the end of .bashrc:

# if $SSH_AUTH_SOCK is a socket, replace .ssh/auth.sock with a new symlink pointing to it
[ -S "$SSH_AUTH_SOCK" ] && ln -snf "$SSH_AUTH_SOCK" ~/.ssh/auth.sock

# if running in screen or tmux, set $SSH_AUTH_SOCK to the shared auth.sock location, regardless if there is an existing auth sock.
case "$TERM" in
    screen*|tmux*)
        export SSH_AUTH_SOCK=~/.ssh/auth.sock
        ;;
esac

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment