Multi Factor Authentication (MFA) on Linux computers

Secure authentication of users of Linux computers should require additional security beyond the user password. Here the use of Multi factor authentication (MFA) on Linux computers is described.

Typically, MFA is used with SSH logins and other services. Sometimes the term Two-factor authentication (2FA) is used, but this is emcompassed by MFA.

This page describes alternative MFA approaches using one-time passcodes or a Radius server.

MFA security issues

If you are interested in the security issues related to the use of MFA, here is a list of interesting articles:

Google Authenticator end-user instructions

With MFA a 6-digit code, which is your Time-based One-Time Password (OTP) which must be used with every MFA login, is displayed by a smartphone app.

The following guide is for end users with a need to use MFA to connect to an SSH login-server which requires MFA authentication:

  • Install the Google Authenticator app, the most well-known TOTP mobile app available via Google Play or Apple App store on mobile phones.

    In addition, the Microsoft Authenticator app works as well with the QR-code generated here.

  • Run the google-authenticator command to create a new secret key to be stored in the user’s $HOME/.ssh/ directory:

    $ google-authenticator --secret=$HOME/.ssh/google_authenticator
    

    Answer y (yes) to all questions to use the recommended default settings.

  • The google-authenticator command should display a QR-code (a black-white pattern) in the terminal window, similar to this test QR-code:

    qrtest.

  • Open the Google Authenticator app or Microsoft Authenticator app on your smartphone and press + to add a new account.

    Click Scan a QR-code and scan the QR-code which is displayed in your terminal window. The new account should appear on the list of accounts.

    A 6-digit code, which is your One-Time Password (OTP) which must be used with every MFA login, is displayed next to the account.

  • Important advice about the user’s secret file $HOME/.ssh/google_authenticator in order to ensure your account’s privacy and security:

    • It must be kept absolutely safe!

    • It must not be shared with other people.

    • It must not be copied to other computers.

    • It must not be backed up to other computers, such as cloud services like Dropbox.

  • The emergency scratch codes (similar to Google backup codes) are also in the secret file. You can print the codes on a piece of paper.

See also the google-authenticator manual page:

$ man pam_google_authenticator

Using MFA with an SSH login server

An SSH server with MFA enabled will both ask for the user’s password, and subsequently require the 6-digit Verification code from the Google Authenticator app:

$ ssh <username>@<SSH-server-name>
Password:
Verification code:

Redisplay of QR codes

To redisplay the QR-code of a previously generated key:

Users can use the qrencode command to generate ASCII output QR-code with:

$ qrencode -o- -t ANSI256 -d 300 -s 10 "otpauth://totp/$USER@`hostname`?secret=`head -1 $HOME/.ssh/google_authenticator`"

It may be convenient to generate also a protected image file $HOME/.ssh/google_authenticator.png containing the QR-code:

$ qrencode -o $HOME/.ssh/google_authenticator.png -d 300 -s 10 "otpauth://totp/$USER@`hostname`?secret=`head -1 $HOME/.ssh/google_authenticator`"
$ chmod 0400 $HOME/.ssh/google_authenticator.png

If the mentioned Linux commands are not available on your system, please contact your system administrator and refer to the present page for information.

System administrator guide to the Google Authenticator PAM module

The following sections are for Linux system administrators only. End users should skip these sections.

Security considerations of TOTP passcodes

Users’ secret file $HOME/.ssh/google_authenticator may be compromised by careless users. The alternative described below keeps all secret files in a location which can only be read by the root user.

Beware: If the users’ secret file are somehow compromised (for example, by obtaining root access to the system), then attackers can use the secret files to regenerate the users’ MFA QR-code giving access to two-factor authentication, which therefore becomes useless.

Google Authenticator PAM module

Google provides an example Linux PAM_module demonstrating two-factor authentication for logging into servers via SSH, OpenVPN, etc.:

Google_Authenticator provides a two-step authentication procedure using one-time passcodes (OTP). The OTP generator application is available for iOS, Android and Blackberry. Similar to S/KEY Authentication the authentication mechanism integrates into the Linux_PAM system.

Time-based One-time Password (TOTP) is a computer algorithm that generates a one-time password (OTP) which uses the current time as a source of uniqueness. TOTP is defined in RFC6238.

Documentation:

Summary:

  • Install packages from EPEL and Base:

    dnf install google-authenticator qrencode qrencode-libs
    

Secret file in a non-standard location

Optionally you may decide to store the secret file in a non-standard location, for example:

auth required pam_google_authenticator.so secret=/var/authenticator/${USER}/.google_authenticator

Then you also have to tell your users to manually move their .google_authenticator file to this location.

The folder /var/authenticator/${USER} would have to be created and protected, for example for user xxx:

mkdir -p /var/authenticator/xxx
chmod 0700 /var/authenticator/xxx
chmod 0600 /var/authenticator/xxx/.google_authenticator

The folder /var/authenticator/ would have to be replicated on all servers where users can login.

See also the description of Storage_location.

Secret files in a root-owned location

See the description of Storage_location and change the secret file location path for PAM in /etc/pam.d/sshd:

auth required pam_google_authenticator.so user=root secret=/var/authenticator/${USER}/google_authenticator

The user=root is used to force PAM to search the file using root user.

All users’ secret file must be stored in this location:

/var/authenticator/${USER}/google_authenticator

Each site will need to have a method for creating user secret files in this location, and removing them from user $HOME directories for security reasons! If multiple servers are used, the /var/authenticator folder must be replicated from the same source using, for example, rsync.

All user secret files must be readable only by the root user:

chown -R root.root /var/authenticator
chmod 0700 /var/authenticator/*
chmod 0400 /var/authenticator/*/*

sshd configuration for Time-based One-time Password

NOTE: Make sure you have an open terminal window to the server, since you can easily lock yourself out!

In /etc/ssh/sshd_config configure the use of password + one-time code:

ChallengeResponseAuthentication yes

If you do not need any user to authenticate solely with a password, configure this line in /etc/ssh/sshd_config:

PasswordAuthentication no

You should configure that the root user can only login with a public key:

PermitRootLogin without-password

Check that sshd_config is configured with:

UsePAM yes

Define one of the following AuthenticationMethods:

  1. To enforce the use of 1) public key and 2) password + one-time code for all users, including the root user:

    AuthenticationMethods publickey,keyboard-interactive
    
  2. Alternatively, first permit login with public key, and if that fails, the next method is password + one-time code:

    AuthenticationMethods publickey keyboard-interactive
    

Finally, restart the sshd service:

systemctl restart sshd

PAM configuration for google-authenticator

The Linux_PAM configuration file syntax is described in http://www.linux-pam.org/Linux-PAM-html/sag-configuration-file.html

Edit the file /etc/pam.d/sshd and find near the top of the file this line:

auth       include      postlogin

Insert after that line the following lines:

auth [success=done default=ignore] pam_succeed_if.so user = root
auth [success=ok default=ignore] pam_access.so
auth required pam_google_authenticator.so secret=${HOME}/.ssh/google_authenticator

The rules in /etc/pam.d/sshd are processed one line at a time from the top and down, see the man pam.conf manual page.

Comments:

  1. The pam_succeed_if.so skips the following checks for the root user, which can then use public key only without a password.

    This configuration goes together with the sshd configurations PermitRootLogin and AuthenticationMethods defined below.

    See the man pam_succeed_if manual page.

  2. The pam_access.so line allows special rules for users and networks. The default configuration file is /etc/security/access.conf described below, but a non-default file may be specified with accessfile=<file>.

    See the man pam_access manual page.

  3. With pam_google_authenticator.so you might add nullok in case you wish to skip check for users without a ${HOME}/.ssh/google_authenticator file:

    auth required pam_google_authenticator.so nullok secret=${HOME}/.ssh/google_authenticator
    

    See the man pam_google_authenticator manual page.

  4. Users may (by accident or by ignorance) give others permission to read the secret file ${HOME}/.ssh/google_authenticator. Fortunately, the pam_google_authenticator catches such user errors and should log them into /var/log/secure similar to this:

    sshd(pam_google_authenticator)[408484]: Secret file "/home/XXX/.ssh/google_authenticator" permissions (0644) are more permissive than 0600
    sshd(pam_google_authenticator)[408484]: No secret configured for user XXX, asking for code anyway.
    

Notice: When using the secret= option, you might want to also set the user= option. The latter forces the PAM module to switch to a dedicated hard-coded user id prior to doing any file operations. When using the user= option, you must not include “~” or “${HOME}” in the filename. The user= option can also be useful if you want to authenticate users who do not have traditional UNIX accounts on your system.

System administrator guide to the Radius PAM module

The following sections are for Linux system administrators only. End users should skip these sections. DTU users should read this page on Inside.

A convenient MFA solution for SSH is to use an existing Radius server in your organization (setting up such a server is beyond the scope of this document). For Linux servers there is a FreeRADIUS server.

Security considerations of RADIUS authentication

The Radius article states that passwords in the RADIUS protocol are protected by the cryptographically broken MD5 encryption plus the shared secret:

Passwords are hidden by taking the MD5 hash of the packet and a shared secret, and then XORing that hash with the password.

Notice the RADIUS protocol’s use of the user clear-text password. See An Analysis of the RADIUS Authentication Protocol for details of the algorithm, and for a discussion of security issues with the Radius protocol.

Beware: If the server’s RADIUS shared secret is somehow compromised (for example, by obtaining root access to the system), then all user clear-text passwords will be readable by attackers, whenever the user has been authenticating with RADIUS.

There are possible attacks on the Radius network protocol, see Using John to crack RADIUS shared secrets.

In order for this attack to work, you will need to either be able to try authentications with a specific login and password and sniff the Access-Request packets, or sniff Access-Request and corresponding Access-Reply packets.

Firewall setup

Your network firewalls between the Radius client and your Radius server must be open for two-way Radius traffic (UDP, standard port 1812).

Linux PAM Radius module

There are some more-or-less useful guides to using Radius with PAM on a Linux system, but nothing authoritative and pedagogical:

Read more about the pam_radius module including usage information.

On CentOS/RHEL 7/8 or Ubuntu Linux install this package:

dnf install pam_radius   # EL8
yum install pam_radius   # EL7
apt install libpam-radius-auth  # Ubuntu

Usage information is in the RPM file /usr/share/doc/pam_radius/USAGE.

The PAM Radius configuration file is:

/etc/pam_radius.conf       # RHEL/CentOS/EL7/EL8
/etc/pam_radius_auth.conf  # Ubuntu

containing lines about your Radius servers:

# server[:port]       shared_secret      timeout (s)
127.0.0.1     secret             1
other-server    other-secret       3

Your organization’s Radius service

You need to obtain your organization’s Radius server IP-address and a client-specific shared-secret string.

Please contact your Radius service’s system administrators in order to obtain the required information.

The only line in /etc/pam_radius.conf (Ubuntu: /etc/pam_radius_auth.conf) should be the one defining your site’s Radius information:

<radius-server-IP>:1812  <shared-secret> 30

with a 30 second timeout (or longer) for Radius authentication, since this involves a human response. The standard UDP port is 1812, so the :1812 may be omitted. If you have multiple Radius servers, you can have one line for each such server.

An example /etc/pam_radius.conf file could contain a single line like:

1.2.3.4 s3cr3t 30

Please note that the /etc/pam_radius.conf file must be protected by permissions 0600, that is readable by root, and NO ONE else:

chmod 0600 /etc/pam_radius.conf

Testing your organization’s Radius service

First you need to ensure that your Radius server is responding to requests from your server (which is a Radius client). Any problems encountered could be due to 1) the Radius server itself, or 2) more likely that a firewall somewhere in your network is blocking the UDP port 1812 traffic.

The Radius client sends UDP packets to the Radius server on one of the ports 1645, 1646, 1812, 1813.

To verify the connectivity to the Radius server, use the radclient tool. Install this package:

dnf install freeradius-utils   # EL8 uses dnf

An example command querying the Radius server (which uses the shared secret s3cr3t) is for some existing user (here: user test with password mypass):

echo "User-Name=test,User-Password=mypass" | radclient <radius-server-IP>:1812 auth s3cr3t

See the radclient manual page.

sshd configuration for Radius authentication

NOTE: Make sure you have an open terminal window to the server, since you can easily lock yourself out!

In /etc/ssh/sshd_config configure the use of password + Radius authentication:

ChallengeResponseAuthentication yes

If you do not need any user to authenticate solely with a password, configure this line in /etc/ssh/sshd_config:

PasswordAuthentication no

You should configure that the root user can only login with a public key:

PermitRootLogin without-password

Check that sshd_config is configured with:

UsePAM yes

Restart the sshd service.

PAM Radius configuration

The Linux_PAM configuration file syntax is described in http://www.linux-pam.org/Linux-PAM-html/sag-configuration-file.html

The rules in /etc/pam.d/sshd are processed one line at a time from the top and down, see the man pam.conf manual page.

Edit the file /etc/pam.d/sshd to contain the following lines at the top of the file:

#%PAM-1.0
# Allow root login first
auth sufficient pam_succeed_if.so user = root
# Access configuration file (for subnets etc.) is /etc/security/access.conf
auth optional pam_access.so
# The RADIUS pam_radius authentication module ("debug" is only used when troubleshooting)
auth sufficient pam_radius_auth.so debug

After this follows the usual lines in the system’s default /etc/pam.d/sshd file.

The auth sufficient actually makes the authorization proceed to the next method in the file, so if your Radius server fails the login, a local password login is attempted next. This may not be what you want for a strict security. In stead you can configure login failure if the Radius authentication failed by using in stead these lines:

#%PAM-1.0
# Allow root login first
auth sufficient pam_succeed_if.so user = root
# Access configuration file (for subnets etc.) is /etc/security/access.conf
auth optional pam_access.so
# The RADIUS pam_radius authentication module ("debug" is only used when troubleshooting)
auth [success=done default=die] pam_radius_auth.so debug

The pam_access.so line allows special rules for users and networks. The default configuration file is /etc/security/access.conf described below, but a non-default file may be specified with accessfile=<file>.

Note that each of the four keywords: required, requisite, sufficient, and optional have an equivalent expression in terms of the […] syntax as follows:

  • required:

    [success=ok new_authtok_reqd=ok ignore=ignore default=bad]

  • requisite:

    [success=ok new_authtok_reqd=ok ignore=ignore default=die]

  • sufficient:

    [success=done new_authtok_reqd=done default=ignore]

  • optional:

    [success=ok new_authtok_reqd=ok default=ignore]

Testing the SSH server with Radius authentication

With the above configuration, and having restarted the sshd service, test logins to the SSH server:

  1. Try to login as root with a previously configured SSH public-key:

    root@other-server$ ssh root@ssh-server
    

    The SSH public-key should allow root login here.

  2. Try to login as a normal, external user:

    user@external-host$ ssh user@ssh-server
    Password: *****
    

    When the user’s correct password has been entered, the user will be authenticated by your Radius server. The Radius server may use different MFA methods such as SMS codes or smartphone authenticator popups. If all is well, the user will be logged in through the SSH session.

Azure Active Directory authentication

Azure provides a Remote Desktop Gateway and Azure Multi-Factor Authentication Server using RADIUS, but there is no offering of Linux PAM integration.

The pam_aad project aims to provide Azure Active Directory authentication for Linux, but this project seems not to be developed actively any more.

YubiKey hardware authentication device

The YubiKey is a hardware authentication device manufactured by Yubico to protect access to computers, networks, and online services that supports one-time passwords (OTP), public-key cryptography, and authentication, and the Universal 2nd Factor (U2F) and FIDO2 protocols developed by the FIDO Alliance. It allows users to securely log into their accounts by emitting one-time passwords or using a FIDO-based public/private key pair generated by the device. YubiKey also allows for storing static passwords for use at sites that do not support one-time passwords.

There is a list of YubiKey Current Products. In Denmark you can buy devices from this vendor, among others.

There is a guide to use two factor authentication for YubiKey_and_SSH. See also the page SSH Public key+MFA with Yubikey on Centos 8/Ubuntu 20.4 LTS.

Steps to enable YubiKey on RHEL Linux and clones:

  1. Enable the EPEL repository as instructed in that page.

  2. Install this package (including prerequisites):

    yum/dnf install pam_yubico
    

Configuring YubiKey on the system

Read YubiKey_and_SSH. In Administrative level, system administrators hold right to configure the user and YubiKey token ID mapping. Administrators can achieve this by creating a new file that contains information about the username and the corresponding IDs of YubiKey(s) assigned.

Create a YubiKey key mapping file, for example /etc/yubikeyid, with contents in this format:

<first user name>:<YubiKey token ID1>
<second user name>:<YubiKey token ID2>:<YubiKey token ID3>:…

Append the following line to the beginning of the /etc/pam.d/sshd file:

auth required pam_yubico.so id=16 debug authfile=/etc/yubikeyid

Require MFA for external networks only

Sometimes we just want to enable the MFA capability only when we connect from external networks to our local network.

To achieve this, append to /etc/security/access.conf the specific networks from where you want to be able to bypass the MFA, for example:

# Logins from local IP range (192.168.0.0/24) and crond
+:root:192.168.0.0/24
+:root:cron
-:root:ALL
+:ALL:192.168.0.0/24
# Hosts with no "." in the name
# +:ALL:LOCAL

The lines with root will disallow root logins from other than 192.168.0.0/24.

NOTE:

  • There should not be any spaces around the “:” separators, see man access.conf.

  • The default configuration file for pam_access.so (in /etc/pam.d/sshd) is /etc/security/access.conf, but a non-default file may be specified with accessfile=<file>.