SSH OTP

Add time-based one-time codes to SSH login using Linux PAM and Google Authenticator's PAM module.

Before you run

This Ubuntu example installs an additional PAM module and changes server authentication. Keep an existing administrative session and independent console or recovery access open. Configure and test each account before enforcement; store the OTP secret and emergency codes securely and never publish them. Synchronize the server clock. Confirm that your SSH client supports keyboard-interactive prompts and that automation and recovery accounts remain usable. The sample PAM stack retains common-auth, which may require the system password as well as the OTP: public key plus keyboard-interactive does not by itself guarantee exactly two prompts or exactly two factors. PAM files, policies and service names vary by distribution; do not apply this example unchanged to macOS or Windows servers.

After applying changes, verify that you can sign in and that the intended behavior works in a new SSH session. Keep the current session open until verification succeeds, so you can restore the configuration if necessary.

Authenticator Apps

Use a TOTP authenticator on your phone to scan the enrollment QR code or enter the secret manually. Protect the QR code and secret, and store emergency recovery codes securely. The app is separate from the server’s PAM module.

2FAS offers optional backups and Premium multi-device sync; availability depends on platform and plan.

Examples

Install the PAM module on Ubuntu. The module is separate from OpenSSH and requires administrator privileges.

sudo apt update
sudo apt install libpam-google-authenticator

Run the enrollment tool as each account that will use OTP. Follow the interactive prompts, add the account to an authenticator app, and securely store the emergency codes. Do not share or log the displayed secret.

google-authenticator

In the existing SSH PAM service file (typically /etc/pam.d/sshd), add the module as an auth rule while preserving all existing account, session and other rules. This fragment retains common-auth; your PAM policy may also request a system password. Do not add nullok when OTP is required.

Open the file with nano:

sudo nano /etc/pam.d/sshd
@include common-auth
auth required pam_google_authenticator.so

Merge these settings into the effective SSH server configuration. This requires a public key and PAM keyboard-interactive authentication; existing Include order or Match blocks can change the result. Do not overwrite the entire configuration.

Open the file with nano:

sudo nano /etc/ssh/sshd_config
UsePAM yes
PubkeyAuthentication yes
KbdInteractiveAuthentication yes
PasswordAuthentication no
AuthenticationMethods publickey,keyboard-interactive:pam

Validate syntax and inspect the effective policy before applying the system's SSH service reload procedure. Use sshd -T -C with the relevant user and address when Match rules apply. From a separate session, verify key and all prompted factors before closing the original session.

sudo sshd -t && sudo sshd -T && sudo systemctl reload ssh

Optional: Public Key and OTP

To require a public key followed by an OTP without also asking for the account password, the SSH PAM auth stack must be configured accordingly. Removing common-auth from the SSH PAM service affects every account using that service and may bypass other authentication policies. Review all remaining auth rules with your administrator; preserve account and session rules. Keep an existing login and independent recovery access.

For an Ubuntu SSH PAM service, the following is an illustrative auth fragment, not a complete PAM file. Comment out the existing common-auth include only after confirming its impact. Do not add nullok. Other auth rules may still require additional factors.

Open the file with nano:

sudo nano /etc/pam.d/sshd
# /etc/pam.d/sshd: auth fragment only
# @include common-auth
auth required pam_google_authenticator.so

After merging the SSH settings above, inspect the effective values. Output keys are lowercase. Include order and Match blocks can change the result; use sshd -T -C with the relevant connection details for Match rules.

sudo sshd -T | grep -E '^(usepam|pubkeyauthentication|kbdinteractiveauthentication|passwordauthentication|authenticationmethods) '

Validate and reload using the command above. From a separate client session, connect with the enrolled account and private key, then enter the authenticator code. Verify login fails without the required key or code before closing the original session. The prompt depends on the complete PAM policy.

ssh -i ~/.ssh/id_ed25519 alice@server.example.com

Secure Pipes implements SSH keyboard-interactive prompts; enter the code requested by the server. This SSH OTP is separate from Secure Pipes app unlock. The shared OTP secret exists on the server and in the authenticator app; Secure Pipes does not need to store it for this login.

Authoritative references

Reviewed October 27, 2026