Tuesday, April 10, 2012

Forcing public key and password in OpenSSH

Using OpenSSH with public key authentication is quite secure. In fact, if the private key is password protected, you get 2-factor authentication -- something you know (the password) and something you have (the private key).

The Problem

However, the SSH architecture does not allow the server to force a password on the private key - The whole point, and indeed the fundamental principle in public key authentication is that the private key is unknown to the server and is managed totally by the client. There simply is no way to enforce a password! This leaves open the possibility of users deleting their private key passwords, and regressing to 1-factor authentication. The password-less private key is equivalent to a login password which is in some file in plain text on the users machine. Its not too bad since the private key is a long and strong login password, but it is written in plain-text!

A simple solution for this problem is to add another authentication step on the server, independent of the public key authentication. Sadly, in OpenSSH, only one authentication step is allowed, you can tell the OpenSSH server which authentication methods you allow, but once one of them succeeds, the user is considered authenticated.

The Solution

By utilizing another one of the OpenSSH's server options, we can add another authentication step. OpenSSH has the option to force a command AFTER authentication. This can be either done per public key in each users' authorized_keys2 file, or as a general option in the OpenSSH's sshd_config file, where is can also be selectively applied by using the Match directive.

So, armed with this knowledge, we'll force another authentication step. But which one? I would go for PAM. PAM is standard on most (if not all) GNU/Linux distributions, and has modules for a lot of authentication schemes, including OTP and of course UNIX passwords. We now need a command which will authenticate with PAM. I found this PAM example application a small, very readable application written in c so it needs very little resources. It only depends on PAM and the c stdlib, so should be pretty OS agnostic. It's simple, so it's easy to audit -- nothing shady going on!

As is, the pam application is not entirely suitable since it requires a user-name in the command line. To work around this, I put the pam binary in /usr/local/bin/ and wrap it in small shell script to pass the current user (remember that the OpenSSH server uses the user's shell to run the command)

#!/bin/sh -f
/usr/local/bin/pam "$USER" || exit 0

Save this script to /usr/local/bin/auth_res.sh, and remember to make it executable!
Add all of your 2-factor required users to the aptly named group twofactor, and then add the Match block below to the end of the sshd_config file:

Match Group twofactor
ForceCommand "/usr/local/bin/auth_res.sh"
RSAAuthentication no
PasswordAuthentication no
PubkeyAuthentication yes

Voilla! Now the users in the twofactor group will always need a private key AND to authenticate to PAM!


Another advantage of these scheme is that the password resides on the server -- With password protected private keys, if someone get's a user's private key he can now try to bruteforce it at his leasure. In the proposed scheme, it will be harder to brute force the password since the server has limits on tiem between logins etc ...