OpenSSH won't invoke PAM at all if public key (RSA) authentication is configured and the client presents a valid key. So if you use key-based auth, you can't enforce 2FA easily.
One workaround for this limitation involves writing a helper program that will prompt for a Yubikey OTP. This helper program is arranged to be executed by sshd after the user successfully authenticates using the ForceCommand directive. Example: https://www.berrange.com/posts/2011/12/18/multi-factor-ssh-authentication-using-yubikey-and-ssh-public-keys-together/
Since I don't trust my knowledge of shell programming (handling signals, tty allocation etc) to review the helper program, so I kept looking for something different.
What are the holes in the following alternative approach?
Configure yubikey to do 2FA for ssh password-based auth.
Every login, 'X', will have a "bastion" login called 'pre-X'.
Both logins will exist on the same machine
One sshd instance
Using the Match sshd configuration directive, the ssh server will be configured to disallow public-key authentication for all logins that match the pattern 'pre-*'.
The 'bastion' login will be created with no home directory, login shell set to /bin/false, gid set to 'nogroup' and anything else can we can do to make it as weak as possible. For good measure, use the ForceCommand ssh directive for these logins to always execute /bin/false and use ChrootDirectory to sandbox it.
Use the AllowUsers directive to disallow logins other than bastion ones from logging in from any IP except localhost.
Use ProxyCommand at the ssh client end to script away the tunneling via the bastion login.
I have done this and it works well, but I'm wondering if I missed something?
Bastion users are created like this:
# password will never be typed in, so need to record it
sudo useradd -M -N -g nogroup -s /bin/false pre-user001 -p $(dd if=/dev/urandom bs=128 count=1 2>/dev/null | base64 -w0)
sshd_config looks like this:
# Nobody can login from anywhere except localhost
AllowUsers *@localhost
# ... except for logins starting with 0
AllowUsers pre-*@*
# force password auth for bastion logins
Match User pre-*
PasswordAuthentication yes
RSAAuthentication no
PubkeyAuthentication no
ForceCommand /bin/false