12

Note: Adding a few lines to the .<name of shell>rc will not solve the problem here, since this particular ssh key has a password and that would not eliminate the need to keep typing it.

So I don't really understand how ssh-agent works under the hood. I just use ssh-agent and ssh-add ~/.ssh/id_rsa every time I need to add a key to access some remote resource. One I've added the key once, I don't need to add it again for the same "shell session" ("shell session" is probably not the appropriate jargon).

Unfortunately, I'm creating new shell sessions all the time. I'm running zsh under tmux on OS X and have a ssh key creatively named id_rsa. That ssh key has a password associated with it.

Every time I start a new shell I have to do the following

$ eval `ssh-agent`
$ ssh-add ~/.ssh/id_rsa
<type password>

which is really irritating.

I've noticed in the output of ssh-agent that the SSH_AGENT_PID environment variable is different every time. My hunch is that this environment variable and the SSH_AUTH_SOCK are the reason that keys don't need to be re-added within a single shell session. When I call the ssh program, it will use these environment variables to communicate with the ssh-agent and authentication will succeed.

I'm wondering if there's a way of sharing ssh-agents between sessions. Maybe the right approach is to add my SSH keys before starting tmux and configure tmux to preserve the SSH_AUTH_SOCK and SSH_AGENT_PID environment variables. I'm really not sure. What's the standard way of solving this problem?

8
  • 1
    Possible duplicate of Add keys to ssh-agent Commented Jul 29, 2016 at 22:25
  • 1
    @ThomasDickey No. It's vaguely related, but not the same issue and the possible solutions are not the same. Commented Jul 29, 2016 at 22:54
  • It's related well enough that some of the answers merely copy from it and similar questions without adding new insight. Commented Jul 29, 2016 at 23:42
  • @ThomasDickey A good answer to this question would address the fact that this is OSX, not a system where a session means an X server. This doesn't appear in the duplicate nor in similar questions I can find on this site. Commented Jul 30, 2016 at 0:12
  • @Gregory, starting a new agent and running ssh-add from the shell startup script would of course require writing the password each time, but making sure you can connect to an existing agent isn't the same. But yes, I admit to missing the mention of OS X.
    – ilkkachu
    Commented Jul 30, 2016 at 7:21

5 Answers 5

9

The way ssh-agent is intended to work is that you have one agent per session. The agent starts and ends with the session.

A session starts when you log in, not when you open a terminal or when you start a shell. Systematically running ssh-agent as part of your shell startup script is definitely wrong.

I'm not an OSX user so I may be missing something, but to my understanding, on non-ancient OSX versions (since 10.5), SSH is integrated with the OSX keychain. So you shouldn't run ssh-agent at all. The SSH_AUTH_SOCK variable should point to a launchd socket (launchd provides an ssh-agent service). See How to use Mac OS X Keychain with SSH keys? and How to prevent ssh from adding my key to ssh-agent on Snow Leopard?

If you want to stick with ssh-agent for some reason, and you want to have a single agent for all sessions (which also makes sense), you can use a fixed path for the SSH socket. (I've done that on Windows.) In your .profile, set SSH_AUTH_SOCK to a fixed path, and start ssh-agent if it isn't already running.

export SSH_AUTH_SOCK=~/.ssh/auth_sock
if ! fuser "$SSH_AUTH_SOCK" >/dev/null 2>/dev/null; then
  # Nothing has the socket open, it means the agent isn't running
  ssh-agent -a "$SSH_AUTH_SOCK" -s >~/.ssh/agent-info
fi

Note that my code doesn't try to kill the agent. If you want to kill the agent when you log out, add a kill instruction to your logout scripts. Of course, if you kill all the processes running as your user, that includes the agent.

4

Here's a little snippet from my login scripts to handle what you want:

SSHAF=$HOME/.ssh_agent_env
[ -f $SSHAF ] && . $SSHAF

# if ssh-agent not really running?
if [ -n "$SSH_AGENT_PID" ] && ! kill -0 $SSH_AGENT_PID >/dev/null
then ssh-agent >$SSHAF 
     . $SSHAF
     ssh-add # add list of more keys here if needed
fi

Explained: I'm saving the environment setting variables from ssh-agent in a dotfile in $HOME (.ssh_agent_env) I source that file if it exists (line 2) to get the process id of the (possibly) running ssh-agent. (SSH_AGENT_PID variable) If that SSH_AGENT_PID variable is set, I check to see if the agent is running with a kill -0 on it's PID (kill -0 sets return code to 0 (true) if the process is running, 1 (false) otherwise. If the agent isn't running I start it and save the output in the .ssh_agent_env file, source that file to add the values to my shell session, and run ssh-add to cache my keys.

The first time on the system, no file exists so the agent is started. Any time after that the file exists, we verify if the agent is running. If not we do a startup.

Note: this is my running implementation of the solution 2 of the first answer above.

2
  • By default answers are ordered by votes (and randomized on page load in case of ties), so “first answer above” changes over time. If you refer to other answers, you should use a link and not refer to position. Commented Jul 29, 2016 at 22:53
  • I think that a fixed path to the socket is simpler than storing the socket name from a file. Commented Jul 30, 2016 at 0:13
2

Basically, what ssh-agent does, is that it starts itself in the background, and prints a bunch of environment variables that the ssh client utility uses to know how to reach the agent (mostly the path of the socket in SSH_AUTH_SOCK). If you start the agent again, you get multiple copies running, but you can usually reach only one from a single shell... (check with something like ps uax | grep agent to see how many you are running.) When ssh-add is run, it contacts the agent, and asks it to read a key to memory. After that any ssh clients that connect to the same ssh-agent instance can take advantage of the saved key.


Couple of choices to run only one ssh-agent:

1) Start ssh-agent before you start your tmux session (or screen for that matter). The environment variables set at that point should be inherited to all shells you run under tmux or screen. (I don't know if tmux needs special configuration to not clean environment variables.)

I've been (the wrong type of) lazy, so I've just done that manually each time before starting my screen session, but it would be easy to create a script to both start the agent and then the screen/tmux session.

If you consider something like a graphical desktop environment, you should start the agent when the DE starts, if possible, so that the environment variables will be available to any terminals you start.

2) Your system may already start an ssh-agent when you log in. In that case you should be able to just ssh-add keys to it without starting a new copy of the agent. In particular OS X starts an agent when logging in, and has modified SSH tools that ask for the key passphrase through the GUI and automatically save keys used on the ssh command to the agent. You can also use ssh-add as usual.

3) If you want to use a single agent from completely separate shells (and therefore cannot inherit the environment variables), check if the agent is running from your shell's startup scripts, and start it if not. Either save the agent's socket address to a file, or use a fixed path to the socket.


In any case, the issue is with pointing the environment variables to the same ssh-agent. Any keys you add to one instance of the agent should be visible to all users of the same agent, regardless of if what shell you ssh-add them from.

As an aside, ssh-add can take an argument to define how long the keys shall be saved: running ssh-add -t 3600 my-key-file will tell the agent to forget about the key after an hour. This may be useful to reduce the time during which the keys are unencrypted in memory.

1

The primary variable is the SSH_AUTH_SOCK variable; the SSH_AGENT_PID is only used with ssh-agent -k to kill the running agent.

So the standard approach is to run ssh-agent, then ssh-add your keys, then run tmux.

Now the fun part of this is that you can reconnect to the tmux daemon and your ssh agent session will still be there and running. So you can do things like disconnect, logout, login again, reconnect to running tmux and not need to re-enter your key password.

(This is good on a single-user machine; not so good on a shared machine 'cos the root user could abuse your keys!)

3
  • 2
    If you don't trust root, you've lost already, you shouldn't have private keys or type passwords on that machine. Loading the keys into the agent doesn't make things worse. Commented Jul 29, 2016 at 22:51
  • It is far far simpler for root to do SSH_AUTH_SOCK=/your/socket than it is for him to steal key presses. I've plenty of experience of SAs with the first level of knowledge than of the second and many data leaks have been "simple". Security isn't binary :-) Commented Jul 29, 2016 at 23:11
  • Granted, I've known sysadmins who couldn't google “keylogger” with both hands too. But that doesn't really make the bar higher. Incompetent sysadmins is something you should expect but not something you can rely on. Commented Jul 29, 2016 at 23:32
1

Ideally, you'd start ssh-agent only once, and run ssh-add only once, and the subsequent shells will inherit the agent.

If you're logging in through a GUI and your window manager uses .xsession or .xinitrc, you may add the following to that file:

if [ -z "$SSH_AUTH_SOCK" ]; then
  eval $(ssh-agent)
  ssh-add
fi

This means that the agent will be inherited for any process (xterm or other terminal emulator) that the window manager spawns.

If you work without X11, then feel happy for a bit because you're awesome, and then add those lines to your .bash_profile or .profile shell initialization script.

The eval will take the output from ssh-agent and evaluate it in the current shell, setting SSH_AUTH_SOCK and SSH_AGENT_PID to whatever the values are. Then ssh-add will be run, pretty much as you're doing manually. The agent will then be inherited by the tmux session you're likely to start soon after logging in (unless you've set tmux to be your actual login shell, which is possible).

In a perfect world, you then only have one running SSH agent and won't have to give any passwords ever again... until you log out and in again, or log in on another console.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .