SecuryBlack LogoSecuryBlack
← Blog/Security

How to Configure 2FA / TOTP in SSH to Shield Access to Your VPS

·9 min read
How to Configure 2FA / TOTP in SSH to Shield Access to Your VPS

Even if you disable passwords and rely exclusively on cryptographic SSH keys, a residual risk remains if the private key stored on your local machine is compromised. To achieve maximum protection on your servers, following a structured VPS hardening and security guide is essential.

In this tutorial, you will learn how to configure two-factor authentication (2FA/TOTP) in OpenSSH using PAM (Pluggable Authentication Modules) and standard authenticator apps like Google Authenticator, Authy, or 1Password.


1. Why combine SSH keys with 2FA?

Traditional setups usually force you to choose between password + 2FA or SSH keys. However, OpenSSH allows you to require both factors simultaneously:

  1. Factor 1 (Something you have): The private SSH key stored on your local machine.
  2. Factor 2 (Something you generate on the fly): A temporary 6-digit code (TOTP) on your smartphone.

With this setup, even if an attacker manages to copy your id_ed25519 file, they cannot access the VPS without the one-time code.


2. Step 1: Install the Google Authenticator PAM module

Connect to your VPS via SSH and install the official package on Debian or Ubuntu systems:

sudo apt update && sudo apt install libpam-google-authenticator -y

3. Step 2: Generate the secret key and recovery codes

Run the interactive wizard under the user account you use to connect (avoid doing this directly as root):

google-authenticator

The wizard will prompt you with several security questions. We recommend answering as follows:

  • Do you want authentication tokens to be time-based?: y.
  • Scan the QR code displayed in the terminal with your authenticator app (or enter the secret key manually).
  • Save the 5 emergency scratch codes in a secure offline location. If you lose your phone, these will be the only way to regain access.
  • Update your ~/.google_authenticator file?: y.
  • Disallow multiple uses of the same authentication token?: y (Prevents replay attacks).
  • Permit time skew (increase window)?: n (Maintains a strict 30-second window).
  • Enable rate-limiting?: y (Limits attempts to a maximum of 3 every 30 seconds).

4. Step 3: Configure PAM to include the module

Open the PAM configuration file for SSH:

sudo nano /etc/pam.d/sshd

If you are using SSH keys + 2FA, add the following line at the end of the file:

auth required pam_google_authenticator.so nullok

(The nullok directive allows users who have not yet configured 2FA to log in. Once everyone has enabled it, remove nullok to make 2FA mandatory for all).


5. Step 4: Adjust OpenSSH configuration (sshd_config)

Open the main SSH configuration file:

sudo nano /etc/ssh/sshd_config

Make sure the following parameters are configured:

# Enable PAM integration
UsePAM yes

# Allow interactive authentication to prompt for the TOTP code
KbdInteractiveAuthentication yes

# Require BOTH SSH key AND TOTP code
AuthenticationMethods publickey,keyboard-interactive

Save the changes and test the syntax before restarting:

sudo sshd -t

If there are no syntax errors, restart the SSH daemon:

sudo systemctl restart ssh

6. Step 5: Test the connection (Without closing your current session!)

Do not close your current terminal window. Open a second window and attempt to connect to your server:

ssh your_username@YOUR_VPS_IP

The server will first validate your SSH key and then prompt you:

Verification code:

Enter the 6-digit code generated by your mobile app. If authentication succeeds, you have logged in with two-factor authentication.


7. Continuous security auditing with SecuryBlack

Configuring 2FA in SSH is an excellent step, but server security also requires periodic auditing of other critical areas (exposed ports, file permissions, UFW rules, and kernel updates).

The open-source FerroSentry security agent integrated into SecuryBlack continuously audits your SSH daemon and firewall configurations, alerting you immediately if any security parameter is accidentally misconfigured.