The ProblemHowever, 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 SolutionBy 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)
/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
Matchblock below to the end of the
Match Group twofactor
Voilla! Now the users in the twofactor group will always need a private key AND to authenticate to PAM!