Linux SSH 2FA with Google Authenticator PAM

This article explains how the Linux operating system implements two-factor authentication (2FA) for Secure Shell (SSH) access using the Google Authenticator Pluggable Authentication Module (PAM). It covers the underlying PAM architecture, how the OpenSSH daemon coordinates the authentication flow, how Time-based One-Time Passwords (TOTP) are generated and validated, and the configuration steps required to enforce secondary verification.

The Architecture: PAM and OpenSSH

Linux delegates user authentication to Pluggable Authentication Modules (PAM). PAM provides a modular architecture that allows system services, such as the OpenSSH daemon (sshd), to authenticate users without needing to understand the underlying authentication mechanisms.

When a user attempts an SSH login, sshd queries PAM to determine whether the user is authorized. By attaching the pam_google_authenticator.so module to the SSH authentication stack, the system inserts an additional layer of verification alongside traditional passwords or public keys.

How the TOTP Mechanism Works

The Google Authenticator PAM module relies on the Time-based One-Time Password (TOTP) algorithm defined in RFC 6238.

  1. Shared Secret: When the user initializes the authenticator utility on the server, a cryptographic shared secret is generated and saved in the user's home directory (typically ~/.google_authenticator). This secret is also imported into the user's mobile authenticator app via a QR code or manual key entry.
  2. Time Step Calculation: Both the server and the authenticator app use the current Unix epoch time divided into 30-second windows.
  3. Hash Generation: The algorithm creates an HMAC-SHA1 hash using the shared secret and the current time interval. The hash is truncated into a six-digit numerical code.
  4. Validation: When prompted during login, the user enters the current six-digit code displayed on their app. The server independently calculates the expected code using its own system clock and the stored secret. If the submitted code matches the calculated code (within an acceptable drift window), PAM grants access.

Configuration Workflow

Implementing this mechanism requires changes across two main configuration components:

1. Configuring PAM (/etc/pam.d/sshd)

To require the second factor, the administrator appends the Google Authenticator shared library to the SSH PAM configuration:

auth required pam_google_authenticator.so

Setting the control flag to required ensures that authentication fails entirely if the one-time code is invalid or missing, regardless of whether the user provided a correct password.

2. Configuring the OpenSSH Daemon (/etc/ssh/sshd_config)

The SSH daemon must be instructed to support interactive challenge-response authentication to prompt the user for their TOTP token:

KbdInteractiveAuthentication yes
UsePAM yes

To enforce both an SSH key (or password) and the TOTP token simultaneously, the AuthenticationMethods directive is defined:

AuthenticationMethods publickey,keyboard-interactive

This instruction forces sshd to validate the client's public SSH key first, followed immediately by the keyboard-interactive PAM stack where the one-time password prompt is presented.

Handling Security and Edge Cases

The implementation includes specific safeguards to maintain security and reliability: