Thursday, December 3, 2015

Scripting ssh passwords

One of the most powerful communications tools available is ssh.  Pretty much the only version on Linux is OpenSSH, and most of the versions I've come across on other platforms are derived from it.  I assume you know that, and I assume you also know that the best way to use it is with pre-shared keys so that you don't have to worry about passwords.  Unfortunately, that isn't always an option.

Recently I was working on a script that needed to use ssh to connect to an embedded system.  There are no keys on the remote system, so you have to use a password.  You can look the password up in a database.  Asking the user to do that and type it in manually would be a pain, and in this case, would add no security.

Openssh does everything it can to make scripting passwords difficult, which is only reasonable from a security standpoint.  But for anyone who has been around Unix systems for a while knows, there's a program called "expect" that solves the problem.  Expect allocates a pseudo TTY device, spawns a target program, and controls it through the TTY.  It can watch for prompts and issue responses, and it can eventually return control to the parent TTY, allowing a user to interact manually.

So I set up a script using expect to enter the password for ssh, and all was good.  I invited everyone else in my department to use the same script.  Most people loved it.  Then I started getting complaints.  It seems that while I would expect expect to be installed everywhere, that expectation was flawed.  I could ask everyone to install expect (and I did), but it seems that everyone is managing their own Linux systems, and many developers don't really know much about doing so.

I needed a better solution.

I found a better solution.

Ssh has a feature where it can run a GUI program to ask for a password.  That is exactly where we'll get our scripted password inserted.  This requires two things:  Set the environment variable SSH_ASKPASS to an executable that will write the password to stdout, and have the ssh process not be connected to a tty.

The first part is easy.  Just create a script that echos the password:

   echo echo $PW > /tmp/pw.$$
   chmod 700 /tmp/pw.$$

The second part is a little more tricky.  Also, it means that we can't interact with ssh once it connects.  Well, maybe we could with some additional trickery, but fortunately, my use of ssh didn't require interacting with it once the connection was established--I was just forwarding ports.  The obvious solution of redirecting stdin from /dev/null doesn't work, and neither does outright closing stdin.  The tty is part of a process state independent of the file descriptors, so we have to actually detach it.  What we want here is a program called 'setsid.'  This is part of the util-linux package, and it seems to be on every system I've been able to find, including the ones that didn't have expect.

Now there's one problem with setsid.  It immediately runs the program in the background.  I wanted to check the exit status of ssh to verify that everything was good.  I thought I was in luck, seeing that there's an option to do just this:  --wait.  Then I found that most of the other developers have older systems from before this option was added (in 2013, apparently).  This required creating another temporary script and a temporary results file, with the parent script waiting in a loop for it to finish.  So in /tmp/dossh.$$, I put the ssh command, followed by saving of the status ($?) in /tmp/dossh.$$.result.  And to keep everything clean, the last line of the script removes the script itself from /tmp.

So the parent script runs 'setsid /tmp/dossh.$$' then sits in a loop:
   while ! [ -f /tmp/dossh.$$.result]; do sleep .1; done
Then the parent script can grab the result, remove the remaining temporary files, and make use of the ssh tunnel. No manual password entry required.

No expect required!  (And that's a good thing.  Expect is a pain to use, in large part due to TCL being a painful language, but also due to issues where subtle changes in software versions can break everything, such as if a prompt changes slightly.)

Everything works exactly as the user would expect it to.

1 comment:

  1. I had one person comment on my Facebook post about this. He said to absolutely never do this. I mostly agree. The particular application I described above is a special case. This is to work around what is essentially fake security to begin with. The password in question is posted on an internal web site. The point in this case is to run with the same security internally as would be used externally, so that the software is all the same, but the password is a dummy.

    ReplyDelete