Search

Enter a search word or two and press return to see the search results.

Who am I?

Hi, I’m Graeme and these are my notes, from my messy desk. I started this blog because Google proved to be more useful at finding content than anything else I’ve used.

So I started adding my own content in the hopes that Google would index it and allow me to find things again in the future.

It works.

You can find out more about me here, and you should follow me on Twitter here.

Keeping up

You can automatically receive new content here by subscribing to the “Blog RSS” (link below). This is the easiest way to keep up with what I write here.  See this BBC article for a good introduction on RSS and keeping up with the goings on of the Internet more easily.

« Keyboard shortcuts in Mac OS X | Main | How to annoy your web host »
Wednesday
Aug172005

Using ssh-agent and screen together

I've been meaning to 'fix' this for ages. I use public-key authentication for my ssh connections wherever possible. I also use screen all the time. (If you use ssh regularly and haven't discovered it already, go look now! There's a bit of a learning curve, but it's well worth it!) But the SSH_AUTH_SOCK isn't always set correctly inside a screen session, so you can't then use the ssh key on the client computer to authenticate against other hosts. (Oh, I also have a reasonably strict policy of only ever create SSH keys for hosts that I am actually, physically, at the console of, not for hosts I merely ssh into now and then. There are, of course, exceptions to this rule!)

Let's describe the problem a little more clearly. I have two Macs, tandoori and dream. Tandoori is my work laptop, dream is my iMac at home. And my web hosting company allows shell access with ssh to woss.name. First thing in the morning, I'm logging in from dream to check some logs, then detaching from the session. The screen session is a new one, since the shell server got rebooted in the night. Here is the session, (minus what I actually did!):

[code]mathie@Tandoori:mathie$ ssh -A -t woss.name screen -xRR
[ ... my shell starts up inside screen ... ]
mathie@napoleon:mathie$ echo $SSH_AUTH_SOCK
/tmp/ssh-XXjqTtiY/agent.25944
mathie@napoleon:mathie$ ssh-add -l
1024 f8:c2:ae:f9:5c:8f:28:67:ba:fb:c6:d8:60:2f:f5:52 /Users/mathie/.ssh/id_dsa (DSA)
mathie@napoleon:mathie$
[detached]
Connection to napoleon.dreamhost.com closed.
mathie@Tandoori:mathie$[/code]

As you can see, it is happily talking to my ssh agent on my iMac and using the identity stored there. Now, later on I login from tandoori to check something else:

[code]mathie@Tandoori:mathie$ ssh -A -t woss.name screen -xRR
[ ... my shell starts up inside screen ... ]
mathie@napoleon:mathie$ echo $SSH_AUTH_SOCK
/tmp/ssh-XXjqTtiY/agent.25944
mathie@napoleon:mathie$ ssh-add -l
Could not open a connection to your authentication agent.
mathie@napoleon:mathie$
[detached]
Connection to napoleon.dreamhost.com closed.
mathie@Tandoori:mathie$[/code]

Why couldn't it connect this time? Well, because the environment variable SSH_AUTH_SOCK, which it inherited from the environment when screen was first started, points to the old agent socket from the first session of the morning, not the current socket.

So, what to do about it? Well, here's my solution, from my ~/.bashrc:

[code]if [ -z ! "$SSH_AUTH_SOCK" ]; then
screen_ssh_agent=${HOME}/usr/state/ssh-agent-screen
if [ "$TERM" = "screen" ]; then
SSH_AUTH_SOCK=${screen_ssh_agent}; export SSH_AUTH_SOCK
else
ln -snf ${SSH_AUTH_SOCK} ${screen_ssh_agent}
fi[/code]

So, if the current terminal is inside a screen session, it will use a fixed, known path to an agent socket. And if the current terminal is not a screen session (say the login shell that precedes reconnecting to screen!), it will update the symlink. (Of course, my ~/.bashrc is a little more complicated than that -- you can see the ssh-related stuff in usr/etc/profile.d/ssh.sh. Or at least you would be able to if websvn wasn't a pile of poo.)

There is one failure scenario I can think of. Say you have two terminal windows open on your desktop, with one connected to the screen session on woss.name But then you want another view onto the current screen session, so ssh in from the other terminal window. Then you log out from the second window. Since logging in from the second window had the effect of updating the symlink to point to its agent, the first no longer has a valid connection to its agent. But it's a rare enough case that I am not too worried about it.

PrintView Printer Friendly Version

EmailEmail Article to Friend

Reader Comments (3)

Hi,

We have a problem with ssh-agent and ssh-add.

We have our program which starts the ssh-agent and stores its
environment variables in a hash dictionary. Another function in the
same program would read these environment variables, set the same
(using putenv()), start a pty and then add the key to the agent using
ssh-add (pty is started in order to give the passphrase as the input).

But, while executing ssh-add, we are getting the error "Could not open
a connection to your authentication agent.". We have ensured that
environment variables are set by executing getenv().

Within a process, we are starting the agent like this,
code snippet:

fp = popen("ssh-agent -s","r");
while(fgets(temp_buff,100,fp) !=NULL)
{
if(line_count ==2){
break;}
line_count++;

char *buff;
buff= new char[100];
bzero(buff,100);

int i =0, buff_len =0, rc1;
for(int j=0;jagentEnvs->env1 = new char[buff_len+1];
strncpy(env_value->agentEnvs->env1,buff,buff_len);
env_value->agentEnvs->env1[buff_len] = '';
env_value->len1 = buff_len+1;
rc1= putenv(env_value->agentEnvs->env1); //Here !!
bzero(buff,100);
break; //Out of the for loop
}
else if(line_count == 2)
{
env_value->agentEnvs->env2 = new char[buff_len+1];
strncpy(env_value->agentEnvs->env2,buff,buff_len);
env_value->agentEnvs->env2[buff_len] = '';
env_value->len2 = buff_len+1;
rc1= putenv(env_value->agentEnvs->env2); //Here !!
bzero(buff,100);
break; //Out of the for loop
}
}
buff[i++] = temp_buff[j];
}
}
pclose(fp);
system("ssh-add -l"); //testing purpose

Both ssh-agent and ssh-add are run by the same user.

While we start the agent, we are capturing the ssh-agent environment
variables (SSH_AUTH_SOCK and SSH_AGENT_PID) and storing it.
i.e., the cmd "ssh-agent -s" would result in an output like this,
SSH_AUTH_SOCK=/tmp/ssh-QDL22304/agent.22304; export SSH_AUTH_SOCK;
SSH_AGENT_PID=22306; export SSH_AGENT_PID;
echo Agent pid 22306;
Here, we are cutting the first env SSH_AUTH_SOCK until we find the
delimiter ";" and then we are appending a null character "" for the
cut string. Similarly, two env variables are extracted and stored.
Well, then this string is given as input for putenv().

It would be very helpful and greatly appreciated if you can help us
in solving this problem.

Thanks
Guru
sgprasad@gmail.com

November 8, 2005 | Unregistered CommenterGuru

Thanks! This is the most elegant solution I've found yet. I've slightly modified it so that I can ssh into another host (with the same bashrc) and then ssh from that host onwards; the terminal type of the remote shell will be 'screen', but it won't have access to the ${screen_ssh_agent} socket. So I just avoid changing the socket if the permanent one doesn't exist.

----------------
# slightly modified from http://woss.name/2005/08/17/using-ssh-agent-and-screen-together/
if [ -n "$SSH_AUTH_SOCK" ]; then
user=`whoami`
screen_ssh_agent="/tmp/${user}-screen-ssh-agent.sock"
if [ "$TERM" = "screen" ]; then
# shell in a screen. If we're on the host with the permanent name
# for the ssh agent, use that instead. if this shell is
# in an ssh to a different host (which happens to be in a screen),
# though, we'll keep the variable name (which will be
# persistent as long as that ssh remains up, exactly what we want).
if [ -e ${screen_ssh_agent} ]; then
export SSH_AUTH_SOCK=${screen_ssh_agent}
fi
else
ln -snf ${SSH_AUTH_SOCK} ${screen_ssh_agent}
fi
fi
----------------

January 12, 2006 | Unregistered CommenterRick Cox

Great solution but using STY is more reliable then matching against the TERM env.

if [ -n "$SSH_AUTH_SOCK" ]; then
screen_ssh_agent="/tmp/${USER}-screen-ssh-agent.sock"
if [ ${STY} ]; then
if [ -e ${screen_ssh_agent} ]; then
export SSH_AUTH_SOCK=${screen_ssh_agent}
fi
else
ln -snf ${SSH_AUTH_SOCK} ${screen_ssh_agent}
fi
fi

August 8, 2006 | Unregistered Commenterzim

PostPost a New Comment

Enter your information below to add a new comment.

My response is on my own website »
Author Email (optional):
Author URL (optional):
Post:
 
Some HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>