So in years gone past, I've had systems with SSH and PAM configured "nicely" to allow users to log-in using either a pre-shared SSH public key pair or a password together with a one-time token from the Google authenticator app running on my Android phone.
When I tried to set this up again recently with the current Fedora release (39), it didn't work as I expected. So I spent a couple of head-scratching days fiddling with every little nitty-gritty setting trying to find-out the problem. It turned-out that there are several things that needed to be changed, so I thought I might as well blog it for posterity.
There are a few initial steps first that I'm going to largely gloss-over (as they're pretty straightforward):- Have a fairly "plain vanilla" Fedora39 (or contemporary) install with sshd enabled and running.
- Create and distribute relevant SSH key pair components.
- Install the "google-authenticator" package.
- Use the "google-authenticator" application to generate a token and synchronise this with the mobile app.
So now we've got to set-up PAM and sshd to use authenticator tokens for log-in. Unfortunately, because we're in "security stuff" both systems are a bit fragile and don't work properly out of the box. We have to make a couple of fixes to the sshd and PAM configs along the way.
Problem #1: SELinux strikes again
SELinux seeks to hamper malicious security exploits by adding restrictive context tags on files and processes. It effectively prevents "wrong processes" from accessing files they shouldn't need to. I started writing a nutshell SELinux recap here, but it was both too long and too incomplete, so I skipped it. There are better places to read-up on SELinux and I'm no expert, so I'll just stickto the bits that matter to this article. The tags themselves have several components, but the one of relevance is the "type".
The problem arises because sshd runs the authenticator PAM module using the "ssh_t" type, but by default the PAM module tries to use the token file "~/.google_authenticator", which automatically gets the "user_home_t" type (You can manually change that if you want). To make matters worse, when the PAM module is used, it updates this token file by firstly writing a new temporary file in the user's home directory and then renaming it over the top of the original (to make it an atomic update). This requires writing into the home directory, which has the "user_home_dir_t" type (changing that would have some finicky follow-on problems). The net outcome is the SELinux policies deny the sshd process proper access to users' token files in their default locations.
Move the authenticator token
There are a few discussion threads online about how to best address this conflict, but we really need the maintainers of Fedora and google-authenticator to reach an agreement about it. In the meantime, my preferred solution is to move the ".google_authenticator" file to ".ssh/google_authenticator" (Note: I removed the file-hiding "." prefix because it's now in a hidden directory). Then I just need to configure the PAM module to access this new file location for SSH connections. Note that this will probably still interfere with any other users of the authenticator token file (Like graphical logins, etc.) - but I'm only using it for SSH, so "works for me".
Firstly move the authenticator token (All users with tokens will need to do this):
$ mv ~/.google_authenticator ~/.ssh/google_authenticator
Modify sshd PAM configuration for sshd:
Now we just need to modify the sshd PAM configuration file ("/etc/pam.d/sshd") to add the google authenticator "auth" module (with the parameter specifying the new location of the token files) after the "password-auth" substack, but before the "postlogin" inclusion. This is what my config file looks like now:
#%PAM-1.0
auth substack password-auth
auth required pam_google_authenticator.so secret=/home/${USER}/.ssh/google_authenticator
auth include postlogin
account required pam_sepermit.so
account required pam_nologin.so
account include password-auth
password include password-auth
# pam_selinux.so close should be the first session rule
session required pam_selinux.so close
session required pam_loginuid.so
# pam_selinux.so open should only be followed by sessions to be executed in the user context
session required pam_selinux.so open env_params
session required pam_namespace.so
session optional pam_keyinit.so force revoke
session optional pam_motd.so
session include password-auth
session include postlogin
Problem #2: Fedora's sshd config
Back at the start I said the way I want this to work is: If a user has an authorised SSH key, they can log-in using just that. Otherwise, the user should then be prompted for both a password and then an authenticator token (also: always ask for password and token, regardless of whether the user exists or not, or the password is good or bad). The way to do this is to add the following directive to one of the sshd config files (NOTE: only space separators, no commas - that would mean "must have pub-key and password and token"):
AuthenticationMethods publickey keyboard-interactive
The next problem is that by default "keyboard-interactive" is disabled in Fedora, and it took me ages to work out what the problem was. It turns-out that the supplemental sshd config file "/etc/ssh/sshd_config.d/50-redhat.conf" has the setting "ChallengeResponseAuthentication no" (This is the old/deprecated name for the "KbdInteractiveAuthentication" setting). I believe this setting is here because keyboard-interactive (i.e. PAM) authentication can interfere with the setting "PermitRootLogin without-password". I don't intend to use that setting, so I'll just comment-out the "ChallengeResponseAuthentication" denial. Seeing as I have to add the "AuthenticationMethods" setting somewhere as well, I put it next to this commented-out line as the changes are related. This is what my "/etc/ssh/sshd_config.d/50-redhat.conf" file looks like now:
# This system is following system-wide crypto policy. The changes to
# crypto properties (Ciphers, MACs, ...) will not have any effect in
# this or following included files. To override some configuration option,
# write it before this block or include it before this file.
# Please, see manual pages for update-crypto-policies(8) and sshd_config(5).
Include /etc/crypto-policies/back-ends/opensshserver.config
SyslogFacility AUTHPRIV
AuthenticationMethods publickey keyboard-interactive
#ChallengeResponseAuthentication no
GSSAPIAuthentication yes
GSSAPICleanupCredentials no
UsePAM yes
X11Forwarding yes
# It is recommended to use pam_motd in /etc/pam.d/sshd instead of PrintMotd,
# as it is more configurable and versatile than the built-in version.
PrintMotd no
Finishing-up
Once all the config file modifications have been made, it's a simple task of restarting sshd and everything should be working as intended.
$ sudo systemctl restart sshd
You should now be able to log-in using a pre-shared public key pair, or a password + OTP-token pair if you don't have a matching key pair.
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.