Skip to content

Securing SSH with Google Authenticator: Two-Factor Authentication Setup

Published: at 06:00 PM

Table of Contents

Open Table of Contents

Overview

Securing SSH access is crucial for protecting servers from unauthorized access. This guide demonstrates how to implement two-factor authentication (2FA) using Google Authenticator, adding an extra layer of security beyond traditional password or key-based authentication.

Security Architecture

graph TB
    subgraph "Authentication Flow"
        A[SSH Client] --> B[SSH Server]
        B --> C{Auth Method}
        C --> D[Password/Key]
        D --> E{Valid?}
        E -->|Yes| F[PAM Module]
        E -->|No| G[Reject]
        F --> H[Google Auth]
        H --> I{TOTP Valid?}
        I -->|Yes| J[Grant Access]
        I -->|No| K[Reject]
    end

    subgraph "Components"
        L[Google Authenticator App]
        M[PAM Module]
        N[SSH Configuration]
        O[User Secret Keys]
    end

    L --> H
    M --> F
    N --> B
    O --> H

    style A fill:#4ecdc4,stroke:#087f5b,stroke-width:2px
    style J fill:#51cf66,stroke:#2f9e44,stroke-width:2px
    style G fill:#ff6b6b,stroke:#c92a2a,stroke-width:2px
    style K fill:#ff6b6b,stroke:#c92a2a,stroke-width:2px

Prerequisites

System Requirements

# Check system information
cat /etc/os-release
uname -a

# Ensure SSH is installed
ssh -V

# Check PAM version
rpm -qa | grep pam  # RHEL/CentOS
dpkg -l | grep libpam  # Debian/Ubuntu

Time Synchronization

# Install NTP client
sudo apt install ntp  # Debian/Ubuntu
sudo yum install ntp  # RHEL/CentOS

# Sync time
sudo ntpdate -s time.nist.gov

# Enable NTP service
sudo systemctl enable ntp
sudo systemctl start ntp

# Verify time
date
timedatectl status

Installation

Installing Google Authenticator

# Debian/Ubuntu
sudo apt update
sudo apt install libpam-google-authenticator

# RHEL/CentOS/Fedora
sudo yum install epel-release
sudo yum install google-authenticator

# From source (if packages unavailable)
git clone https://github.com/google/google-authenticator-libpam.git
cd google-authenticator-libpam
./bootstrap.sh
./configure
make
sudo make install

Verify Installation

# Check PAM module
ls -la /lib/x86_64-linux-gnu/security/pam_google_authenticator.so
# or
ls -la /lib64/security/pam_google_authenticator.so

# Test command availability
which google-authenticator

User Configuration

Generate Secret Key

# Run as the user (not root)
google-authenticator

# Interactive prompts:
# Do you want authentication tokens to be time-based? yes
# Do you want me to update your ~/.google_authenticator file? yes
# Do you want to disallow multiple uses? yes
# Increase time window? no
# Enable rate-limiting? yes

Configuration Options

graph TD
    A[Google Authenticator Setup] --> B[Time-based tokens]
    A --> C[Counter-based tokens]

    B --> D[30-second window]
    B --> E[QR Code generation]
    B --> F[Emergency codes]

    C --> G[HOTP mode]

    D --> H[Clock sync critical]
    E --> I[Mobile app scan]
    F --> J[Backup access]

    style A fill:#4ecdc4,stroke:#087f5b,stroke-width:2px
    style B fill:#74c0fc,stroke:#1971c2,stroke-width:2px
    style F fill:#ffd43b,stroke:#fab005,stroke-width:2px

Non-Interactive Setup

#!/bin/bash
# generate-2fa.sh - Non-interactive 2FA setup

# Generate secret for a user
generate_2fa_secret() {
    local user=$1

    # Generate with specific options
    su - $user -c "google-authenticator \
        --time-based \
        --disallow-reuse \
        --force \
        --rate-limit=3 \
        --rate-time=30 \
        --window-size=3 \
        --secret=/home/$user/.google_authenticator"

    # Display QR code
    su - $user -c "google-authenticator \
        --time-based \
        --qr-mode=utf8"
}

# Usage
generate_2fa_secret "username"

PAM Configuration

Configure PAM for SSH

Edit /etc/pam.d/sshd:

# Backup original file
sudo cp /etc/pam.d/sshd /etc/pam.d/sshd.backup

# Edit PAM configuration
sudo nano /etc/pam.d/sshd

Add the following line:

# For required 2FA (must pass)
auth required pam_google_authenticator.so

# For optional 2FA (can bypass if not configured)
auth required pam_google_authenticator.so nullok

# With custom options
auth required pam_google_authenticator.so nullok grace_period=30 debug

PAM Configuration Examples

# Standard configuration (password + 2FA)
@include common-auth
auth required pam_google_authenticator.so

# Public key + 2FA only
#@include common-auth
auth required pam_google_authenticator.so

Advanced PAM Options

# Full options example
auth required pam_google_authenticator.so \
    secret=/home/${USER}/.google_authenticator \
    user=root \
    forward_pass \
    debug \
    authtok_prompt="Verification code: " \
    nullok \
    grace_period=30

SSH Configuration

Enable Challenge-Response

Edit /etc/ssh/sshd_config:

# Backup SSH config
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.backup

# Edit configuration
sudo nano /etc/ssh/sshd_config

Required settings:

# Enable challenge-response authentication
ChallengeResponseAuthentication yes

# For public key + 2FA
AuthenticationMethods publickey,keyboard-interactive

# For password + 2FA
AuthenticationMethods password,keyboard-interactive

# Or allow either method + 2FA
AuthenticationMethods publickey,keyboard-interactive password,keyboard-interactive

# Ensure PAM is enabled
UsePAM yes

# Optional: Disable password-only auth
PasswordAuthentication no

Per-User Configuration

# Match specific users for 2FA
Match User admin,root
    AuthenticationMethods publickey,keyboard-interactive

Match Group sudo
    AuthenticationMethods publickey,keyboard-interactive

Match User serviceaccount
    AuthenticationMethods publickey

Restart SSH Service

# Test configuration
sudo sshd -t

# Restart SSH service
sudo systemctl restart sshd

# Monitor logs
sudo journalctl -fu sshd

Testing and Verification

Test Authentication Flow

sequenceDiagram
    participant U as User
    participant S as SSH Client
    participant D as SSH Server
    participant P as PAM Module
    participant G as Google Auth
    participant A as Auth App

    U->>S: ssh user@server
    S->>D: Connection request
    D->>S: Public key challenge
    S->>D: Public key response
    D->>P: Verify authentication
    P->>G: Request TOTP
    G->>S: Prompt for code
    U->>A: Check code
    A->>U: Display code
    U->>S: Enter code
    S->>D: Submit code
    D->>G: Verify TOTP
    G->>P: Valid
    P->>D: Authentication success
    D->>S: Access granted

Test Commands

# Test with verbose output
ssh -v user@server

# Test specific authentication method
ssh -o PreferredAuthentications=publickey,keyboard-interactive user@server

# Test from different terminal
# Keep current session open!
ssh user@localhost

Advanced Configuration

Backup Codes

# Generate additional backup codes
google-authenticator --emergency-codes=10

# Store securely
cat ~/.google_authenticator | grep -E "^[0-9]{8}$" > ~/backup-codes.txt
chmod 600 ~/backup-codes.txt

Multiple Devices

#!/bin/bash
# share-2fa-secret.sh

# Extract secret key
SECRET=$(head -1 ~/.google_authenticator)

# Generate QR code URL
USER=$(whoami)
HOSTNAME=$(hostname)
URL="otpauth://totp/${USER}@${HOSTNAME}?secret=${SECRET}&issuer=SSH"

# Display QR code
qrencode -t UTF8 "$URL"

# Or save as image
qrencode -o ~/qr-code.png "$URL"

Conditional 2FA

# Skip 2FA for certain conditions
# Create /etc/security/access-local.conf
+ : ALL : 192.168.1.0/24
+ : ALL : LOCAL
- : ALL : ALL

# Update PAM configuration
auth [success=1 default=ignore] pam_access.so accessfile=/etc/security/access-local.conf
auth required pam_google_authenticator.so

Security Hardening

SELinux Configuration

# Check SELinux status
getenforce

# Allow Google Authenticator in SELinux
sudo setsebool -P authlogin_yubikey on

# Create custom policy if needed
sudo ausearch -c 'sshd' --raw | audit2allow -M my-sshd-2fa
sudo semodule -i my-sshd-2fa.pp

File Permissions

# Secure Google Authenticator files
chmod 600 ~/.google_authenticator
chmod 700 ~

# System-wide permissions
sudo chmod 644 /etc/pam.d/sshd
sudo chmod 644 /etc/ssh/sshd_config

Audit Logging

# Enable detailed PAM logging
# Add to /etc/pam.d/sshd
auth required pam_google_authenticator.so debug

# Monitor authentication attempts
sudo tail -f /var/log/auth.log  # Debian/Ubuntu
sudo tail -f /var/log/secure    # RHEL/CentOS

# Configure rsyslog for 2FA events
echo "auth.*  /var/log/2fa.log" | sudo tee -a /etc/rsyslog.conf
sudo systemctl restart rsyslog

Automation Scripts

Bulk User Setup

#!/bin/bash
# bulk-2fa-setup.sh

USERS_FILE="users.txt"
QR_OUTPUT_DIR="/tmp/qr-codes"

mkdir -p "$QR_OUTPUT_DIR"

while IFS= read -r username; do
    echo "Setting up 2FA for $username..."

    # Generate secret
    su - "$username" -c "google-authenticator \
        --time-based \
        --disallow-reuse \
        --force \
        --rate-limit=3 \
        --rate-time=30 \
        --window-size=3"

    # Extract secret and generate QR
    SECRET=$(su - "$username" -c "head -1 ~/.google_authenticator")
    URL="otpauth://totp/${username}@$(hostname)?secret=${SECRET}"

    # Save QR code
    qrencode -o "$QR_OUTPUT_DIR/${username}-qr.png" "$URL"

    echo "QR code saved to: $QR_OUTPUT_DIR/${username}-qr.png"
done < "$USERS_FILE"

Monitoring Script

#!/bin/bash
# monitor-2fa-auth.sh

LOG_FILE="/var/log/auth.log"
ALERT_EMAIL="admin@example.com"

# Monitor for 2FA failures
tail -F "$LOG_FILE" | while read line; do
    if echo "$line" | grep -q "Failed to authenticate"; then
        echo "2FA failure detected: $line" | \
            mail -s "2FA Authentication Failure" "$ALERT_EMAIL"
    fi
done

Recovery Procedures

Lost Device Recovery

graph TD
    A[Lost Device] --> B{Backup Codes?}
    B -->|Yes| C[Use Backup Code]
    B -->|No| D{Root Access?}

    C --> E[Login Success]

    D -->|Yes| F[Disable 2FA Temporarily]
    D -->|No| G[Contact Admin]

    F --> H[Regenerate Secret]
    G --> I[Admin Assistance]

    H --> J[Re-enable 2FA]
    I --> J

    style A fill:#ff6b6b,stroke:#c92a2a,stroke-width:2px
    style E fill:#51cf66,stroke:#2f9e44,stroke-width:2px
    style J fill:#74c0fc,stroke:#1971c2,stroke-width:2px

Emergency Access

# As root, temporarily disable 2FA for a user
# Option 1: Remove .google_authenticator file
sudo mv /home/user/.google_authenticator /home/user/.google_authenticator.disabled

# Option 2: Comment out PAM line
sudo sed -i 's/^auth required pam_google_authenticator.so/#&/' /etc/pam.d/sshd
sudo systemctl restart sshd

# Option 3: Add bypass for specific user
echo "auth sufficient pam_succeed_if.so user = emergencyuser" | \
    sudo tee -a /etc/pam.d/sshd

Regenerate Secret

#!/bin/bash
# regenerate-2fa.sh

USER=$1

if [ -z "$USER" ]; then
    echo "Usage: $0 <username>"
    exit 1
fi

# Backup old secret
sudo mv /home/$USER/.google_authenticator \
       /home/$USER/.google_authenticator.old

# Generate new secret
sudo -u $USER google-authenticator \
    --time-based \
    --disallow-reuse \
    --force \
    --rate-limit=3 \
    --rate-time=30 \
    --window-size=3

echo "New 2FA secret generated for $USER"

Troubleshooting

Common Issues

  1. Time Sync Problems

    # Check time difference
    ntpdate -q time.google.com
    
    # Force sync
    sudo ntpdate -s time.google.com
    
    # Increase time window
    auth required pam_google_authenticator.so window_size=10
    
  2. SELinux Denials

    # Check SELinux logs
    sudo ausearch -m avc -ts recent
    
    # Generate policy
    sudo audit2allow -a -M google_auth
    sudo semodule -i google_auth.pp
    
  3. PAM Module Not Found

    # Find PAM module
    find /lib* -name "pam_google_authenticator.so"
    
    # Create symlink if needed
    sudo ln -s /usr/lib64/security/pam_google_authenticator.so \
               /lib64/security/pam_google_authenticator.so
    

Debug Mode

# Enable SSH debug
sudo /usr/sbin/sshd -d -p 2222

# Enable PAM debug
auth required pam_google_authenticator.so debug

# Check system logs
journalctl -xe | grep -i "pam\|ssh\|google"

Best Practices

Security Recommendations

graph TD
    A[2FA Best Practices] --> B[Regular Updates]
    A --> C[Secure Storage]
    A --> D[Monitoring]
    A --> E[Backup Plans]

    B --> F[Update PAM modules]
    B --> G[Patch SSH server]

    C --> H[Encrypt backup codes]
    C --> I[Secure secret files]

    D --> J[Log analysis]
    D --> K[Failed attempts alerts]

    E --> L[Multiple admins with 2FA]
    E --> M[Recovery procedures]

    style A fill:#4ecdc4,stroke:#087f5b,stroke-width:2px
    style C fill:#ff6b6b,stroke:#c92a2a,stroke-width:2px
    style D fill:#74c0fc,stroke:#1971c2,stroke-width:2px

Implementation Checklist

Integration Examples

Ansible Playbook

---
- name: Configure Google Authenticator 2FA
  hosts: all
  become: yes
  tasks:
    - name: Install Google Authenticator
      package:
        name: google-authenticator
        state: present

    - name: Configure PAM
      lineinfile:
        path: /etc/pam.d/sshd
        line: "auth required pam_google_authenticator.so nullok"
        insertafter: "@include common-auth"

    - name: Configure SSH
      lineinfile:
        path: /etc/ssh/sshd_config
        regexp: "^ChallengeResponseAuthentication"
        line: "ChallengeResponseAuthentication yes"
      notify: restart sshd

  handlers:
    - name: restart sshd
      service:
        name: sshd
        state: restarted

Docker Container Support

FROM ubuntu:22.04

RUN apt-get update && \
    apt-get install -y openssh-server google-authenticator

# Configure SSH for container
RUN mkdir /var/run/sshd && \
    echo 'ChallengeResponseAuthentication yes' >> /etc/ssh/sshd_config && \
    echo 'UsePAM yes' >> /etc/ssh/sshd_config

# Add PAM configuration
RUN echo "auth required pam_google_authenticator.so nullok" >> /etc/pam.d/sshd

EXPOSE 22
CMD ["/usr/sbin/sshd", "-D"]

Conclusion

Implementing Google Authenticator for SSH provides robust two-factor authentication that significantly enhances server security. Key benefits include:

Best practices for deployment:

  1. Always test in a non-production environment first
  2. Keep backup codes in a secure location
  3. Document recovery procedures
  4. Monitor authentication logs
  5. Regularly update security components
  6. Train users on proper 2FA usage

With proper implementation, Google Authenticator provides enterprise-grade security for SSH access while maintaining usability.