Reconnecting your SSH agent to a detached GNU screen session

I like GNU Screen. I also like SSH agent forwarding. Combining these two makes for very easy, password-less, large file transfers between two remote hosts.

When you login via SSH with agent forwarding enabled, the SSH server creates a socket in /tmp/ssh-[HASH]/agent.[PID]. This allows SSH sessions on the remote host to access your agent back on your workstation. Commands that use the agent, like ssh, scp, and rsync, find it by reading the SSH_AUTH_SOCK environment variable.

root@server:~#  echo $SSH_AUTH_SOCK 
/tmp/ssh-NshdZD1538/agent.1538

This path is readable only to the current user (and root, which is why agent forwarding is dangerous if you don’t trust the root users of servers you access). GNU Screen inherits SSH_AUTH_SOCK like any other process. However, what happens when you detach your screen session and logout? When you login again and re-attach to screen, SSH_AUTH_SOCK is pointing to the wrong file:

root@server:~# screen -r
root@server:~# echo $SSH_AUTH_SOCK 
/tmp/ssh-NshdZD1538/agent.1538
root@server:~# ls /tmp/ssh-NshdZD1538/agent.1538
ls: cannot access /tmp/ssh-NshdZD1538/agent.1538: No such file or directory
root@server:~# ssh localhost
root@localhost's password:

I have found a number of solutions of varying complexity. But here is a simple one-liner which finds an agent socket and attaches to it. This works in the current screen session (not just new shells within that session), and you don’t have to modify .bashrc or switch to zsh to use it.

export SSH_AUTH_SOCK=$(find /tmp/ssh-* -user `whoami` -name agent\* | tail -n 1)

This finds the most recent SSH agent owned by the user, and assigns it to SSH_AUTH_SOCK. This doesn’t guarantee that you’ll attach to the socket created for your current SSH session, only the most recent one. For instance, you could login with three SSH sessions and this will find the latest one. But if you’re using screen, you only really need one SSH session anyway.

Edit 2013-01-24: Remi Bergsma has a better solution. The following always takes the most recent SSH agent socket:

export SSH_AUTH_SOCK=$(find /tmp/ssh-* -user `whoami` -name agent\* -printf '%T@ %p\n' 2>/dev/null | sort -k 1nr | sed 's/^[^ ]* //' | head -n 1)

Tags: ,

  1. Mattias’s avatar

    thanks, exactly what i was looking for.

    Reply

  2. Mike’s avatar

    Thanks for this. I added a ‘2>/dev/null’ to suppress the find error when your client machine has agent forwarding disabled:

    export SSH_AUTH_SOCK=$(find /tmp/ssh-* -user `whoami` -name agent\* 2>/dev/null | tail -n 1)

    Reply

  3. MH’s avatar

    Very cool and useful! I guess the most elegant version would be this one:


    export SSH_AUTH_SOCK=$(find /tmp/ssh-* -user $USER -name agent\* 2>/dev/null | tail -n 1)

    Reply

  4. Remi Bergsma’s avatar

    Thanks for sharing this nice idea! I’ve improved the command further since it didn’t return the right socket for me. I’m using the most recently created socket, instead of the one with the highest numer:

    find /tmp/ssh-* -user `whoami` -name agent\* -printf '%T@ %p\n' 2>/dev/null | sort -k 1nr | sed 's/^[^ ]* //' | head -n 1

    I’m adding a timestamp to sort, then remove it again with sed. You now need the first one (head instead of tail) and not the last.

    The oneliner then becomes:

    SSH_AUTH_SOCK=$(find /tmp/ssh-* -user `whoami` -name agent\* -printf '%T@ %p\n' 2>/dev/null | sort -k 1nr | sed 's/^[^ ]* //' | head -n 1)

    Will write a short blog about this myself and will refer to your post.

    Reply

  5. David Corking’s avatar

    Thanks Tyler and co. This is almost perfect! For me, on Debian 7.1, Remi’s code needed a small tweak. I put backticks around whoami.

    SSH_AUTH_SOCK=$(find /tmp/ssh-* -user `whoami` -name agent\* -printf '%T@ %p\n' 2>/dev/null | sort -k 1nr | sed 's/^[^ ]* //' | head -n 1)

    Reply

    1. Tyler Wagner’s avatar

      Thanks, David. The problem with Remi’s comment appears to be a bug in WordPress involving the <code> tag and backticks. He originally included backticks. I’ve corrected it.

      Reply

      1. David Corking’s avatar

        Thanks Tyler: that makes sense. Don’t forget to correct it in the body of your article too.

        Reply

        1. Tyler Wagner’s avatar

          Right. Missed that too. Fixed now.

          Reply

        2. Damon Conway’s avatar

          Great post. I mashed up Remi’s find command with a bash function I got from http://sweetjesus26.livejournal.com/16782.html. I put the result on pastebin.

          http://pastebin.com/5AmyGqD6

          Reply

          1. Tyler Wagner’s avatar

            That’s a cool solution. I’m duplicating it here, if you don’t mind.

            # Find a usable agent
            function ssh-reagent () {
              export SSH_AUTH_SOCK=$(find /tmp/ssh-* -user `whoami` -name agent\* -printf '%T@ %p\n' 2>/dev/null | sort -k 1nr | sed 's/^[^ ]* //' | head -n 1)
              if ssh-add -l 2&>1 > /dev/null; then
                echo Found working SSH Agent:
                ssh-add -l
                return
              fi
              echo Cannot find ssh agent - maybe you should reconnect and forward it?
            }

            Reply

          2. Patrick Forget’s avatar

            I can’t seem to get this solution working.

            When I’m detached from screen my key is being offered to ssh.

            When I reattach screen, I run the script. I seen my key is listed with “ssh-add -l”

            When I connect with “ssh -v” I see that my agent keys are not being tested.

            Both in screen and on detached terminal SSH_AUTH_SOCK have the same value
            SSH_AUTH_SOCK=/tmp/ssh-tJRHm19731/agent.19731

            Any ideas what I’m doing wrong.

            Reply

          3. Damon Conway’s avatar

            Hey Patrick,
            This fixed a problem I was having and I moved on without checking back. I’m not sure what’s going on there. Ssh should be picking those keys up. I do have my screen and tmux sessions configured to always be login sessions. Not sure if that is the issue, and I wouldn’t expect it to be. If it’s still not working, I’m not really sure what to tell you. My results match yours except I can ssh with my key. I’ve used this on CentOS and Ubuntu with openssh. If you are using a different ssh on another system, it might work differently. If you are using openssh, try asking on one of their support resources. It appears properly configured to me if you can ssh with a key before you enter screen.

            One problem I’ve found with this was that it wanted to create a file named 1 in the current directory. I put a cd ~ in and moved on. After a coworker pinged me about this, I went looking at the problem again. I’ve updated the pastebin to be this:

            # Find a usable agent
            function ssh-reagent () {
            export SSH_AUTH_SOCK=$(find /tmp/ssh-* -user `whoami` -name agent\* -printf ‘%T@ %p\n’ 2>/dev/null | sort -k 1nr | sed ‘s/^[^ ]* //’ | head -n 1)
            if $(ssh-add -l > /dev/null) ; then
            echo Found working SSH Agent:
            ssh-add -l
            return
            fi
            echo Cannot find ssh agent – maybe you should reconnect and forward it?
            }

            Now it works wherever you are in the file system.

            Reply

Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.