SSH双因素认证实战:Google Authenticator+PAM配置详解

1. 项目概述

最近在梳理团队服务器的安全基线,发现一个普遍但危险的现象:很多同事还在用“用户名+密码”这种单一方式登录生产环境的SSH服务器。一旦密码泄露或者被暴力破解,整个服务器就门户大开。为了堵上这个安全漏洞,我决定给所有关键服务器部署SSH双因素认证(2FA),核心方案就是Google Authenticator(GA)的TOTP动态验证码。这个方案的好处是,登录时除了要知道密码,还得有手机上那个30秒刷新一次的6位动态码,两者缺一不可,安全性直接提升一个量级。

我花了几天时间,在CentOS、Rocky Linux和Ubuntu几个主流系统上都折腾了一遍,也把Xshell、PuTTY、MobaXterm这几个最常用的SSH客户端都测通了。过程中踩了不少坑,比如PAM模块顺序搞反导致登录循环失败,或者客户端配置不对永远只弹一个密码框。这篇文章就是我这趟“填坑之旅”的完整记录,目标很明确: 让你看完就能一次配通,不走弯路 。无论你是运维工程师、开发者还是个人站长,只要你的服务器需要更安全的SSH访问,这套方案都值得你花半小时部署上。

2. 核心原理与登录流程拆解

在动手之前,我们必须搞清楚SSH双因素认证到底是怎么工作的。很多教程只给命令,不说原理,结果一出问题就抓瞎。我把整个流程拆解成几个核心组件,你理解了它们之间的关系,后面排错就会非常轻松。

2.1 核心组件分工与协作

整个2FA体系不是靠一个软件完成的,而是多个系统组件协同工作的结果。我们可以把它想象成一道需要两道关卡的门卫系统。

  • Google Authenticator PAM模块 ( libpam-google-authenticator ) : 这是安装在Linux服务器上的“第一道门卫”。它的职责单一且明确:校验用户输入的6位动态验证码是否正确。它本身不生成验证码,只负责验证。验证码的“种子”(Secret)保存在每个用户家目录下的一个隐藏文件( ~/.google_authenticator )里。这个模块通过PAM(可插拔认证模块)框架与系统集成。
  • PAM配置文件 ( /etc/pam.d/sshd ) : 这是“门卫的工作手册”。它定义了SSH登录时,需要依次调用哪些PAM模块进行认证,以及它们的顺序。 顺序是这里的命门 。我们必须确保先调用Google Authenticator模块(检查验证码),再调用系统密码认证模块。如果顺序反了,整个流程就乱套了。
  • SSH服务端配置 ( /etc/ssh/sshd_config ) : 这是“门的通信协议”。它告诉SSH守护进程(sshd):“我们这里登录要走‘键盘交互’模式,别走简单的密码模式了。”关键参数是 KbdInteractiveAuthentication yes PasswordAuthentication no 。前者开启复杂的多轮问答式认证,后者关闭简单的单次密码认证通道,强制所有密码登录都走PAM流程。
  • SSH客户端 (Xshell/PuTTY等) : 这是“访客”。它必须支持并正确配置“键盘交互”认证模式。如果客户端错误地只使用“密码”认证,它会直接尝试把密码发送给服务器,而服务器此时期待的是验证码,对话根本对不上,必然导致“Access denied”。
  • 手机验证器App (Google Authenticator/Microsoft Authenticator等) : 这是“动态口令生成器”。它根据和服务器共享的“种子”(Secret),结合当前时间,每30秒生成一个新的6位码。它和服务器上的PAM模块通过基于时间的TOTP算法保持同步。

2.2 一次成功的登录交互时序

理解了组件,我们来看一次成功的登录过程中,客户端和服务器到底在“聊”什么。这是排查问题的关键参照。

  1. 客户端发起SSH连接,服务器说:“我支持键盘交互认证。”
  2. 客户端回应:“好的,就用键盘交互。”
  3. 服务器通过PAM发起第一轮询问(根据PAM配置,这是Google Authenticator模块在提问): Verification code: 。有些系统可能提示语仍是 Password: ,但 顺序是第一位的
  4. 用户必须在第一个输入框里,输入手机App上当前的6位动态码。
  5. 服务器验证动态码正确后,PAM流程继续,发起第二轮询问(系统密码模块在提问): Password:
  6. 用户在第二个输入框里,输入Linux系统的用户密码(如root密码)。
  7. 密码验证通过,登录成功。

这里有一个99%的新手都会踩的坑 :当第一个框弹出时,如果你习惯性地输入了系统密码,服务器会用验证码的逻辑去校验你的密码,当然对不上,于是立刻返回“Access denied”,并且 不会 给你第二次输入的机会。这就是为什么很多人配置完后,一直登录失败,却搞不清原因。记住这个顺序: 先验证码,后系统密码

2.3 架构示意图与数据流

为了更直观,我们可以看下数据是如何流动的:

[手机 App]                    [SSH 客户端]                 [Linux 服务器]
  TOTP 6位码  ──键盘交互──►  Xshell/PuTTY  ──SSH:22──►  sshd
  (每30秒刷新)   (先验证码)      (仅启用              (UsePAM yes)
                  (后密码)     Keyboard-Interactive)   └──► PAM栈
                                                   1. pam_google_authenticator.so
                                                   2. pam_unix.so (密码验证)

这个流程图清晰地展示了:动态码从手机产生,通过客户端以“键盘交互”模式传输给服务器端的SSH,SSH再交给PAM处理,PAM则严格按照配置的顺序,先过GA验证码关,再过系统密码关。

3. 服务器端配置:五步一次成功指南

理论说完,开始实战。服务器端的配置是基础,务必按顺序操作。 强烈警告:在修改PAM和SSH配置前,务必通过VNC、控制台或另一个已建立的SSH会话保持一个有效的root连接。 这是你的“救命通道”,万一配置出错导致所有SSH登录被锁,你还能通过这个通道修复。

3.1 步骤一:预处理SELinux(针对RHEL/CentOS/Rocky/AlmaLinux)

SELinux是增强安全模块,但有时会阻止PAM模块写入用户家目录的配置文件。在内网测试或学习环境,为了快速通过,可以临时禁用。生产环境建议配置正确的SELinux上下文。

# 临时将SELinux设置为宽松模式(Permissive),当前生效
setenforce 0

# 永久禁用SELinux(重启后生效),编辑配置文件
sed -i 's/^SELINUX=.*/SELINUX=disabled/' /etc/selinux/config

# 查看当前SELinux状态
getenforce # 应该显示 Permissive 或 Disabled

注意 :对于Ubuntu/Debian系统,它们使用AppArmor,通常不需要此步骤,除非遇到特定权限错误。

3.2 步骤二:安装必要的软件包

我们需要安装两个包: google-authenticator (PAM模块)和 qrencode (用于生成二维码,方便手机扫描)。

  • RHEL/CentOS/Rocky/AlmaLinux 系列

    # 确保EPEL仓库已启用(提供额外软件包)
    yum install -y epel-release
    # 安装核心软件
    yum install -y google-authenticator qrencode
    
  • Ubuntu/Debian 系列

    apt update
    apt install -y libpam-google-authenticator qrencode
    

3.3 步骤三:为每个SSH用户生成密钥并绑定手机

这是关键一步,需要 在每个需要启用2FA的用户下执行 。如果你用root用户登录,那就是给root配置;如果用普通用户 webadmin 登录,就要切换到 webadmin 用户下执行。 切忌用root用户替普通用户生成密钥文件 ,因为密钥文件会保存在执行命令的用户家目录下。

# 假设我们为root用户配置
# 首先,可选择性删除旧的配置文件(如果是重新配置)
rm -f ~/.google_authenticator

# 执行配置向导
google-authenticator

执行命令后,会进入一个交互式配置向导。以下是我推荐的配置选项,兼顾安全与便利:

提示问题 推荐选择 说明与理由
Do you want authentication tokens to be time-based (y/n) y 选择基于时间的TOTP模式,这是主流标准,与手机App兼容。
Do you want me to update your "/root/.google_authenticator" file (y/n) y 确认将配置写入文件。
Do you want to disallow multiple uses of the same authentication token? y 重要 :禁止同一令牌重复使用。防止攻击者截获一个验证码后重复使用。
By default, tokens are good for 30 seconds... Do you want to enable so-called “rate-limiting” (y/n) y 启用速率限制。30秒内最多允许3次登录尝试,有效抵御暴力破解。
Do you want to enable rate-limiting (y/n) y 同上,确认启用。

配置完成后,终端会显示一个大大的 二维码 ,以及一行“Your new secret key is:”开头的文本(一串Base32编码的密钥)。同时会给出几个紧急备用码(Emergency Scratch Codes), 务必妥善保存这些备用码 ,万一手机丢失,可以用它们登录。

现在,立刻打开你手机上的验证器App(Google Authenticator、Microsoft Authenticator、Authy等都可以),扫描屏幕上的二维码,或者手动输入那串密钥。 你的手机App里就会新增一个条目,开始每30秒刷新一个6位码。

最后,检查生成的密钥文件:

ls -la ~/.google_authenticator

文件权限应该是 400 600 ,属主是当前用户。这是PAM模块能正常读取的关键。

3.4 步骤四:配置PAM认证栈(核心中的核心)

这是整个配置中最容易出错的一步。文件路径是 /etc/pam.d/sshd 。我们需要修改它,告诉系统在SSH登录时,如何调用认证模块。

核心原则:顺序必须是 pam_google_authenticator.so 在前,系统密码认证在后。

  • 针对 RHEL/CentOS/Rocky/AlmaLinux 8+ 系统 : 这些系统通常使用 password-auth 子栈来管理密码认证。我们需要在子栈调用之前插入GA模块。

    # 备份原始文件是个好习惯
    cp /etc/pam.d/sshd /etc/pam.d/sshd.bak
    
    # 使用vim或nano编辑 /etc/pam.d/sshd
    # 在文件靠前的位置(通常在 `auth substack password-auth` 这一行之前),添加下面这行:
    auth       required     pam_google_authenticator.so
    

    添加后,相关部分看起来应该像这样:

    #%PAM-1.0
    auth       required     pam_sepermit.so
    auth       required     pam_google_authenticator.so  # <-- 我们添加的行
    auth       substack     password-auth
    auth       include      postlogin
    ... 其他内容 ...
    
  • 一个至关重要的排查点 : 很多RHEL系系统的默认PAM配置中,包含下面这样一行:

    auth       requisite     pam_succeed_if.so uid >= 1000 quiet
    

    这行的意思是“只有UID大于等于1000的用户才允许通过此认证”。而root用户的UID是0, 这行规则会直接阻止root用户通过SSH登录! 如果你需要root也能SSH登录,必须 注释掉或删除这行

    #auth       requisite     pam_succeed_if.so uid >= 1000 quiet  # 注释掉它
    
  • 针对 Ubuntu/Debian 系统 : 这些系统可能使用 @include common-auth 。你需要确保 pam_google_authenticator.so @include common-auth 之前

    auth    required    pam_google_authenticator.so
    @include common-auth
    
  • 可选参数 nullok : 如果你团队中有些用户还没配置2FA,但又希望他们暂时能登录,可以在模块后加 nullok 参数。这样,对于没有 ~/.google_authenticator 文件的用户,这个模块会被跳过。

    auth       required     pam_google_authenticator.so nullok
    

    警告 nullok 参数会降低安全性,因为它为未配置2FA的用户留下了后门。仅在过渡期使用,并确保所有用户最终都完成配置。

3.5 步骤五:配置SSH服务端并重启

现在来配置SSH服务本身,让它使用我们刚设好的PAM流程。编辑 /etc/ssh/sshd_config 文件。

# 找到并修改以下参数,如果不存在则添加
UsePAM yes
ChallengeResponseAuthentication yes  # 旧版参数,与兼容性相关
PasswordAuthentication no            # 关键!关闭纯密码认证通道
KbdInteractiveAuthentication yes     # 关键!启用键盘交互认证

# 可选:如果你希望同时要求公钥和2FA,可以设置(但本文以密码+2FA为主)
# AuthenticationMethods publickey,keyboard-interactive

参数详解

  • PasswordAuthentication no :这是 最重要 的一步。它关闭了SSH协议自带的、简单的密码认证方法。如果不关闭,客户端可能直接走这个“后门”,完全绕过我们精心设置的PAM双因素流程。
  • KbdInteractiveAuthentication yes :告诉SSH服务端,“我支持键盘交互认证”,这是触发PAM多轮问答的前提。
  • ChallengeResponseAuthentication yes :一个历史遗留参数,在一些旧版本(如CentOS 7)的文档中仍被强调。在新版OpenSSH中,它可能被标记为已弃用,其功能已由 KbdInteractiveAuthentication 涵盖。但为了最大兼容性,两者都设为 yes 也无妨。

配置检查与重启 : 修改完配置后,千万不要直接重启。先进行语法测试,避免配置错误导致SSH服务无法启动。

# 测试sshd配置文件语法,无输出表示正确
sshd -t

# 语法检查通过后,重启sshd服务
systemctl restart sshd
# 或 systemctl reload sshd

时间同步检查 : TOTP基于时间,服务器和手机的时间必须同步(误差通常要在30秒内)。如果总是提示验证码错误,首先检查时间。

# 启用并启动NTP时间同步服务
timedatectl set-ntp true
# 查看时间状态
timedatectl status

确保你的手机也开启了“自动设置日期和时间”。

4. 客户端配置详解:Xshell、PuTTY、MobaXterm

服务器端配好了,如果客户端配置不对,照样登录不上。核心就一点: 让客户端使用“键盘交互”认证模式,而不是“密码”模式。

4.1 Xshell 8 配置(推荐,交互最清晰)

Xshell的配置界面比较直观,是验证服务器配置是否正确的理想工具。

  1. 新建或复制会话 :打开Xshell,点击“文件”->“新建”,或右键一个已有会话选择“属性”。
  2. 连接设置 :在“连接”页面,填写主机IP地址和端口(默认22)。
  3. 关键步骤 - 用户身份验证
    • 点击左侧的“用户身份验证”。
    • 在右侧的“方法”下拉列表中, 只选择“Keyboard Interactive”
    • 务必取消勾选“Password”和“Public Key”等其他所有方法 。如果这里勾选了“Password”,Xshell可能会尝试直接发送密码,导致认证失败。
  4. 连接 :保存会话并连接。Xshell会弹出一个“用户身份验证”窗口。
  5. 输入顺序
    • 第一个输入框(可能提示“Verification code”或“Password”), 输入手机上的6位动态验证码
    • 点击“确定”或“下一步”后,出现第二个输入框(明确提示“Password”), 输入Linux系统的用户密码
    • 如果两次输入都正确,即可成功登录。

4.2 PuTTY 配置(经典轻量客户端)

PuTTY配置稍隐晦,但一旦理解,也非常稳定。

  1. 会话设置 :打开PuTTY,在“Session”页面,输入主机名(或IP)和端口。
  2. 连接类型 :确保“Connection type”是“SSH”。
  3. 自动登录用户(可选) :在“Connection” -> “Data” -> “Auto-login username”中,可以预先填写用户名(如root),这样连接时就不用手动输入了。
  4. 关键步骤 - 认证方法
    • 不要 在“Connection” -> “SSH” -> “Auth”页面加载任何私钥文件(.ppk),除非你明确要使用“公钥+2FA”的组合认证。对于“密码+2FA”,这里应该留空。
    • PuTTY的认证顺序是自动协商的,我们主要通过服务器端的 PasswordAuthentication no 来强制它走键盘交互路径。
  5. 保存会话 :回到“Session”页面,输入一个会话名称,点击“Save”,方便下次使用。
  6. 连接与登录
    • 点击“Open”连接。首次连接会弹出主机密钥确认窗口,点击“Accept”接受。
    • 在终端窗口里,你会看到登录提示。 记住顺序:先出现 Verification code: (或第一个 Password: ),输入动态码;回车后出现第二个 Password: ,输入系统密码。

PuTTY常见问题排查

  • 现象 :连接后只出现一个 Password: ,输入密码后立刻 Access denied ,没有第二次输入机会。
  • 原因 :客户端可能错误地尝试了“密码”认证,或者服务器端 PasswordAuthentication 仍为 yes
  • 解决 :确认服务器端 sshd_config PasswordAuthentication 已设为 no 并重启了sshd。PuTTY端可以尝试在“Connection” -> “SSH” -> “Auth”下,勾选“Attempt authentication using Pageant”和“Attempt TIS or CryptoCard auth”,但这通常不是主因,核心在服务器配置。

4.3 MobaXterm 配置(功能强大的全能终端)

MobaXterm的配置逻辑与PuTTY类似。

  1. 新建会话 :点击“Session” -> “SSH”,输入远程主机和用户名。
  2. 高级设置 :点击“Advanced SSH settings”选项卡。
  3. 私钥设置 除非使用公钥认证,否则请确保“Use private key”选项未勾选,或路径为空 。如果这里错误地指向了一个.ppk文件,MobaXterm会尝试公钥认证,可能干扰键盘交互流程。
  4. 连接登录 :保存并连接。登录提示顺序与PuTTY一致:先验证码,后系统密码。

重要提示 :在“键盘交互”认证模式下,Xshell、PuTTY、MobaXterm通常 无法像普通密码模式那样保存密码 。这是出于安全设计,因为动态验证码是变化的。这意味着每次登录都需要手动输入密码和当时的验证码。

5. 自动化与脚本集成:Python实现自动登录

对于需要自动化执行的运维脚本、CI/CD流水线,每次手动输入验证码显然不现实。我们可以通过Python脚本,在受控的安全环境中自动生成TOTP码并完成登录。 请注意,此方法将第二因素(TOTP种子密钥)放在了脚本或环境变量中,如果脚本泄露,则双因素认证形同虚设。请仅在可控的、安全的环境中使用。

5.1 环境准备与脚本编写

首先,在能运行脚本的机器上安装必要的Python库。

pip install paramiko pyotp

然后,编写自动化登录脚本 ssh_2fa_auto.py

#!/usr/bin/env python3
import os
import sys
import paramiko
import pyotp

# 从环境变量读取敏感信息,避免硬编码在脚本中
HOST = os.environ.get("SSH_HOST")
USER = os.environ.get("SSH_USER")
PASSWORD = os.environ.get("SSH_PASSWORD")
SECRET = os.environ.get("TOTP_SECRET")  # 这是你在 `google-authenticator` 命令中得到的Base32密钥

if not all([HOST, USER, PASSWORD, SECRET]):
    print("错误:请设置 SSH_HOST, SSH_USER, SSH_PASSWORD, TOTP_SECRET 环境变量。")
    sys.exit(1)

# 清理Secret中的空格(有时显示会带空格)
SECRET = SECRET.replace(" ", "")

def keyboard_interactive_handler(title, instructions, prompt_list):
    """
    处理键盘交互认证的回调函数。
    paramiko会调用此函数来回答服务器提出的问题。
    """
    replies = []
    # 初始化TOTP生成器
    totp = pyotp.TOTP(SECRET)

    for prompt, _echo in prompt_list:
        prompt_lower = prompt.lower()
        # 判断问题类型:如果是验证码请求
        if "verification" in prompt_lower or "code" in prompt_lower:
            replies.append(totp.now())  # 填入当前动态码
        # 如果提示是密码(通常是第二个问题)
        elif "password" in prompt_lower:
            replies.append(PASSWORD)  # 填入系统密码
        else:
            # 对于无法识别的问题,返回空字符串(通常不会发生)
            replies.append('')
    return replies

try:
    # 创建SSH传输层
    transport = paramiko.Transport((HOST, 22))
    transport.connect(username=USER)

    # 发起键盘交互式认证,并传入我们的处理器
    transport.auth_interactive(USER, keyboard_interactive_handler)

    if transport.is_authenticated():
        print("认证成功!")
        # 在此处可以打开SFTP通道或执行命令
        # ssh = paramiko.SSHClient()
        # ssh._transport = transport
        # stdin, stdout, stderr = ssh.exec_command('ls -la')
        # print(stdout.read().decode())
        transport.close()
    else:
        print("认证失败。")
        transport.close()
        sys.exit(1)

except Exception as e:
    print(f"连接或认证过程中发生错误: {e}")
    sys.exit(1)

5.2 运行脚本

在运行脚本前,需要设置环境变量。在Linux/Mac的终端或Windows的PowerShell中:

# Linux/Mac
export SSH_HOST='your.server.ip'
export SSH_USER='root'
export SSH_PASSWORD='your_linux_password'
export TOTP_SECRET='JBSWY3DPEHPK3PXP' # 替换为你的真实Base32密钥

# 然后运行脚本
python3 ssh_2fa_auto.py
# Windows PowerShell
$env:SSH_HOST = 'your.server.ip'
$env:SSH_USER = 'root'
$env:SSH_PASSWORD = 'your_linux_password'
$env:TOTP_SECRET = 'JBSWY3DPEHPK3PXP' # 替换为你的真实Base32密钥

# 然后运行脚本
python ssh_2fa_auto.py

安全警告

  1. 密钥保管 TOTP_SECRET 是最高机密,等同于密码。切勿提交到代码仓库。
  2. 环境隔离 :最好在专用的、访问受控的“跳板机”或“CI Runner”上运行此类脚本。
  3. 使用密钥对 :对于自动化场景,更安全的做法是结合SSH公钥认证。在 sshd_config 中设置 AuthenticationMethods publickey,keyboard-interactive ,这样脚本需要同时拥有私钥和TOTP种子才能登录,安全性更高。

6. 故障排查与常见问题实录

配置过程中遇到问题很正常。我把自己和同事们踩过的坑整理成了下面这个排查表,你可以像查字典一样快速定位问题。

故障现象 最可能的原因 排查与解决步骤
只有一个 Password: 提示,输入后立刻 Access denied 1. 客户端错误使用了“Password”认证方式。
2. 服务器端 PasswordAuthentication 仍为 yes ,客户端直接走了密码通道,绕过了2FA。
3. 用户操作错误 :在第一个提示(本应是验证码)输入了系统密码。
1. 检查客户端 :确保只启用了“Keyboard Interactive”(Xshell)或未指定私钥(PuTTY/MobaXterm)。
2. 检查服务器 :确认 /etc/ssh/sshd_config PasswordAuthentication 已设为 no ,并执行 systemctl restart sshd
3. 牢记顺序 :第一个提示框 必须 输入手机上的6位动态码。
连接后根本不出现 Verification code 提示 SSH服务端未正确启用键盘交互认证。 1. 检查 /etc/ssh/sshd_config :确保 KbdInteractiveAuthentication yes UsePAM yes
2. 检查PAM配置 /etc/pam.d/sshd ,确认 pam_google_authenticator.so 行已添加且位置正确。
3. 重启sshd服务。
root用户无法登录,但普通用户可以 PAM配置中包含了限制root的规则。 检查 /etc/pam.d/sshd ,找到类似 auth requisite pam_succeed_if.so uid >= 1000 quiet 的行,将其注释掉(前面加 # )。
执行 google-authenticator 命令时报权限错误 SELinux安全上下文阻止写入用户家目录。 1. 临时解决:执行 setenforce 0
2. 永久解决(测试环境):编辑 /etc/selinux/config 设置 SELINUX=disabled 后重启。
3. 生产环境:应使用 chcon 命令设置正确的文件上下文。
验证码总是错误,即使手机App显示新码 服务器与手机时间不同步。TOTP算法对时间极其敏感。 1. 服务器 :运行 timedatectl status 查看时间。执行 timedatectl set-ntp true 启用同步。
2. 手机 :进入系统设置,确保“自动设置日期和时间”已开启。
3. 允许时间漂移:在 google-authenticator 配置向导中,对“是否放宽时间窗口”选择 y
用户A登录,却提示用户B的验证码 在错误的用户下执行了 google-authenticator 命令。 为谁配置,就用谁登录执行 。如果需要为用户 alice 配置,请切换到 alice 用户 ( su - alice ) 后再运行 google-authenticator 。密钥文件 ~/.google_authenticator 是用户独立的。
多次输错验证码/密码后,账户被锁定 系统触发了PAM失败锁定机制(如 pam_faillock )。 1. 通过VNC或控制台登录服务器。
2. 重置指定用户的失败计数:
- 对于 faillock : faillock --user <用户名> --reset
- 对于 pam_tally2 (较旧系统): pam_tally2 --user <用户名> --reset
MobaXterm/PuTTY提示私钥文件(.ppk)错误 在认证配置中指定了错误的或不需要的私钥文件路径。 在客户端设置中, 移除 “私钥认证”相关的配置(如MobaXterm的“Use private key”,PuTTY的Auth中的私钥路径),因为我们使用的是“密码+验证码”模式。

7. 生产环境部署清单与进阶考量

如果你需要在新服务器上批量部署,或者作为上线前的检查清单,下面这个“10秒速查清单”可以帮你快速回顾所有关键步骤。

服务器端清单

  • [ ] SELinux :根据策略决定是禁用 ( setenforce 0 ) 还是配置正确上下文。
  • [ ] 安装软件 yum/apt install google-authenticator qrencode
  • [ ] 生成用户密钥 :切换到目标用户,执行 google-authenticator ,扫码绑定。
  • [ ] 配置PAM :编辑 /etc/pam.d/sshd ,确保 auth required pam_google_authenticator.so 位于密码认证栈之前,并移除 uid >= 1000 等限制root的规则。
  • [ ] 配置SSHD :编辑 /etc/ssh/sshd_config ,设置 UsePAM yes , ChallengeResponseAuthentication yes , PasswordAuthentication no , KbdInteractiveAuthentication yes
  • [ ] 重启服务 :执行 sshd -t && systemctl restart sshd
  • [ ] 时间同步 :确认 timedatectl status ,启用NTP。
  • [ ] 备用通道 :始终保留一个通过VNC/控制台或已有会话的登录方式,直到测试完全成功。

客户端清单

  • [ ] 认证方法 :仅启用 Keyboard Interactive (Xshell) 或保持默认不指定私钥 (PuTTY/MobaXterm)。
  • [ ] 登录顺序 :牢记 先输入6位动态验证码 后输入系统密码
  • [ ] 测试连接 :使用新配置的客户端会话进行测试, 不要关闭已有的保障性会话

进阶安全考量

  1. 备用码管理 :将 google-authenticator 命令生成的5个8位备用码(Emergency Scratch Codes)安全地存储起来(如加密的密码管理器)。这是手机丢失时的救命稻草。
  2. 分步实施 :对于团队,可以先在PAM配置中使用 nullok 参数,让已配置的用户走2FA,未配置的仍用密码。给团队成员一个缓冲期去完成绑定,最后再移除 nullok
  3. 结合公钥认证 :最高安全级别是“公钥+动态码”。在 sshd_config 中设置 AuthenticationMethods publickey,keyboard-interactive 。这样,登录需要:① 正确的私钥;② 正确的动态码。即使私钥泄露,没有动态码也无法登录。
  4. 日志监控 :配置集中的日志系统(如rsyslog, ELK),监控 /var/log/secure /var/log/auth.log ,关注认证失败事件,及时发现暴力破解尝试。

整个配置的核心逻辑可以浓缩为一句话: 服务器端关闭纯密码通道,迫使SSH走PAM流程;PAM流程里先校验Google Authenticator的动态码,再校验系统密码;客户端配合使用键盘交互模式,并按照先码后密的顺序输入。 只要理解了这三个环节的衔接,任何相关问题都能迎刃而解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值