One Time Passwords for SSH on Ubuntu and OS X
I have been meaning to beef up the security on my various servers for a while. Everything was configured in a way that was relatively closed, but ultimately I decided that convenience outweighed absolute security. To that end, my passwords are not as good as they could be (ie. I can remember them easily and type them quickly (although they were designed to be...)), SSH continues to serve from the default port, and one could SSH to root just with its (enormous) password.
Walking home from the train yesterday I decided to finally fix this. My original idea was to setup a pluggable authentication module (PAM) for Steve Gibson's "Perfect Paper Passwords", but I soon discovered the (slightly more official) Initiative for Open Authentication (OATH). OATH provides specifications for two types of one time passwords (OTPs): event based (HOTP) or time based (TOTP).
Event based OTPs are generated from a counter that increments every time you ask for a password. The servers keep track of the current counter so they will never accept previous passwords again (eg. if someone watches over your shoulder or there is a key logger). Time based OTPs do much the same, except they are based off of the current time and so are only valid for the current 30 second block.
These sorts of two-factor authentication schemes often rely upon proprietary hardware and expensive service plans, but the openness of OATH allows for free apps for iOS, Android, and many more. Another open source project, OATH Toolkit, provides the server side code including a PAM.
Installation
The general procedure for setting up the server side is:
- Make sure the time is accurate on your server, or the time based OTPs will not validate.
- Install the OATH-Toolkit.
- Setup keys.
- Configure sshd.
- Configure PAM.
Installation on Ubuntu
The code presented here is to be run within a shell in which you have run sudo -s
(ie. have root privileges, but some elements of your account are preserved). I'm also running Ubuntu 10.04 LTS over here...
1. Make sure the time is accurate on your server, or the time based OTPs will not validate.
NTP will synchronize our clock for us. This snippet installs the software and runs it once. If you want to continually sync the clock then you could install the ntp daemon (apt-get install -y ntp
) or setup a cron job to do it.
1 2 | apt-get install -y ntpdate ntpdate pool.ntp.org |
2. Install the OATH-Toolkit.
We need to install some dependencies, grab the most recent version, compile it, and create some symlinks into the places where PAMs are expected to be found.
1 2 3 4 5 6 7 8 9 | apt-get install -y libpam-dev
wget http://mirror.csclub.uwaterloo.ca/nongnu/oath-toolkit/oath-toolkit-1.10.0.tar.gz
tar -xzf oath-toolkit-1.10.0.tar.gz
cd oath-toolkit-1.10.0
./configure
make
make install
ln -s /usr/local/lib/security/pam_oath.so /lib/security/
ln -s /usr/local/lib/liboath.* /lib/
|
3. Setup keys.
We need to generate a random key, and save it into a file only readable to root. This is where the assumption that you have sudo -s
, since $SUDO_USER would be your normal user name.
1 2 3 4 | KEY=$(head -c 1024 /dev/urandom | openssl sha1) echo "Your secret key is: $KEY" echo "HOTP/T30/6 $SUDO_USER - $KEY" >> /etc/users.oath chmod go-rw /etc/users.oath |
In this case we are setting this to be a 6 digit time based OTP with a window of 30 seconds. Copy this key to somewhere safe, and/or immediately setup your token (eg. your phone) with this key.
4. Setup sshd.
This was the most frustrating part of getting OATH running as I would expect on Ubuntu. sshd
defaults to not permitting PAMs to issue their own challenges (eg. "What is your password?") and instead issues a single generic password request on behalf of ALL of the PAMs. Since I wanted to leave the password authentication in place and simply add OATH on top, this would never work as sshd
would be providing the same string to both PAMs. If you open up your sshd_config
and set ChallengeResponseAuthentication
to yes
, then it will allow for PAMs to issue their own challenges.
Lets use the magic of sed
to do this in one line:
1 | sed -i.bak -E -e 's/(ChallengeResponseAuthentication) no/ yes/' /etc/ssh/sshd_config
|
5. Configure PAM.
The default PAM authentication stack will abandon authentication requests of subsequent PAMs after the password check has failed. Ergo, if we added the oath PAM after the password PAM you would never be asked for your OTP unless you got the password right. We want to avoid this as it is an information leakage. Ergo, we are going to completely replace the default PAM auth stack for sshd
.
Open up /etc/pam.d/sshd
, and find the part that looks like:
# Standard Un*x authentication. @include common-auth
If you are curious, take a look at /etc/pam.d/common-auth
to see what it normally does, and them mercilessly replace the @include
in /etc/pam.d/sshd
thusly:
# Standard Un*x authentication. # Normally we would `@include common-auth`, but that leaks info with OATH auth required pam_unix.so nullok_secure # OATH OTP auth required pam_oath.so usersfile=/etc/users.oath
Then all we need to do it restart sshd
and you should be good to go!
1 | service ssh restart |
Installation on OS X
These instructions may be a little rougher around the edges as I only really did this the once. Again we are assuming a shell via sudo -s
, and I'm running on Snow Leopard (10.6).
1. Make sure the time is accurate on your server, or the time based OTPs will not validate.
This should already be taken care of by the system! Hooray!
2. Install the OATH-Toolkit.
First you must have either Homebrew or MacPorts installed. Afterward you can install OATH and link the PAM into place:
1 2 3 4 5 | brew install oath-toolkit ln -s $(brew --prefix oath-toolkit)/lib/security/pam_oath.so /usr/lib/pam/ # -- or -- port install oath-toolkit ln -s /opt/local/lib/security/pam_oath.so /usr/lib/pam/ |
3. Setup keys.
Same purpose as for Ubuntu:
1 2 3 4 | KEY=$(head -c 1024 /dev/urandom | openssl sha1 | cut -d ' ' -f 2) echo "Your secret key is: $KEY" echo "HOTP $SUDO_USER - $KEY" >> /etc/users.oath chmod go-rw /etc/users.oath |
In this case we are setting up an event based OTP, as the version of OATH-Toolkit available via MacPorts does not support PAM authentication of time based OTPs. It only started doing that 4 days ago anyways, so I'm not going to give MacPorts a hard time (yet).
**EDIT: You can now use TOTPs with oath-toolkit.
4. Configure sshd.
Nothing to do here; we should be good to go with the defaults.
5. Configure PAM.
Just need to add a single line to the default sshd
PAM configuration in order to do what we want. I'll leave it as an exercise to the reader to guess which line (hint: it is the pam_oath.so
one), but your /etc/pam.d/sshd
should end up something like this:
# sshd: auth account password session auth optional pam_krb5.so auth optional pam_mount.so auth sufficient pam_serialnumber.so serverinstall legacy auth required pam_opendirectory.so auth required pam_oath.so usersfile=/etc/users.oath window=20 account required pam_nologin.so account required pam_sacl.so sacl_service=ssh account required pam_opendirectory.so password required pam_opendirectory.so session required pam_launchd.so session optional pam_mount.so
This also differs from the Ubuntu configuration in that I have added the window=20
option. This means that my token (ie. my iPhone) can be 20 OTPs ahead of the server and still be accepted. Ergo, I can show 20 people how the token software works before my server will no longer accept it.
There isn't much need to do this on the Ubuntu machine with the time based tokens as the clocks should be synced, and window
defaults to 5 (ie. 2.5 minutes).
The only thing left is to restart sshd
:
1 2 | service ssh stop service ssh start |
What do we get from all this?
You can now only ssh into accounts which have an OATH key set, and you can only do so if you have a device which contains the secret key. Even if someone is watching exactly what you type in it will have only compromised your password, but they will not be able to get into the account.
Also note that SSH will not even bother with PAMs if you have a keypair setup, so in the case of working on one of my own machines I never have to deal with this (and this maintains the convenience for me). Only when I am away from my machines do I need to use this, and it is actually kinda fun to do anyways.
You can also do some fancy things with pam_access
to prevent the OATH PAM kicking in coming from certain hosts (eg. the local network, or otherwise truster machines). Easy enough to do on Ubuntu which comes with pam_access
, but I have also discovered that it is possible to modify the sources for most of the default Ubuntu PAMs to compile on OS X, but I will leave discussing that to another time.