04 January, 2024

Set-up SSH with PAM tokens under Fedora 39


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:

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


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.