Rocky 9.6 SSH双因子认证实战:Google Authenticator配置全解

发布时间:2026/10/9 6:21:34
Rocky 9.6 SSH双因子认证实战:Google Authenticator配置全解 给 Rocky 9.6 服务器加装 Google Authenticator 做双因子认证是我最近处理过的一件高频需求。服务器刚交付使用时被公网扫描和弱口令爆破骚扰是家常便饭单纯靠密码甚至密钥都免不了担心。把 TOTP 动态验证码接入 SSH 登录流程等于给安全加了第二道闸门。这篇文章我直接按生产环境实操从头讲EPEL 源安装、google-authenticator 客户端与 PAM 模块、sshd 配置的关键参数再到手机端绑定验证码、常见坑的排查办法。适合手里管着 Rocky/RHEL 系服务器、想给登录增加一道保险的运维和开发同学参考。1. 为什么要在 SSH 登录上加第二道锁1.1 密码认证的天然短板很多人在刚接触服务器的时候第一反应就是设个复杂的密码。复杂密码确实能挡住一部分脚本但挡不住有针对性的爆破。公网上的服务器每天被扫描端口、尝试弱口令几乎是地方产业尤其 22 端口一天下来 /var/log/secure 里能堆上百条 Failed password 记录。我也见过不少把密码设成大小写加数字加符号还带特殊字符的照样被撞库撞出来的案例。原因很简单很多人喜欢在多个环境复用同一套密码只要其中一个平台的密码泄露攻击者拿过来就是数据库式的轮番尝试。密钥认证比密码安全不少几百位的非对称密钥理论上无法暴力枚举。但密钥文件本身存在丢失和泄露的风险如果你把私钥放在笔记本里笔记本丢了或者被同事拷走了那对方就直接拿到了你服务器的入场券。就算给私钥加了 passphrase这依然属于你知道的东西这一因子本质还是单因子认证只是把攻击面从服务器转移到了你的客户端和私钥文件上。我个人的判断标准很简单只要服务器暴露在公网或者多人共用账号管理就必须把其中一层认证做成只有用户手里才有的东西。双因子认证就是这类方案它的意义不是替代密码而是即便密码泄露攻击者少了第二个因子照样进不来。1.2 TOTP 动态验证码是怎么工作的TOTP 的全称是 Time-based One-Time Password即基于时间的一次性密码标准定义在 RFC 6238 里。核心思路是客户端和服务器端共享一个秘密种子secret把这个种子和当前时间戳切成的时间窗口一起做 HMAC-SHA1 运算再从结果里截出一段数字作为验证码。因为双方用的是同一个秘密、同一个算法、同一个时间窗口所以在同一时刻算出来的验证码是一致的。具体来说服务端和手机 App 的时间被切成 30 秒一格每一格对应一个新的 6 位数字。服务器收到验证码后会用当前时间窗口重算一遍再和你输入的验证码比对。匹配就通过超过时间窗口自动作废。这也是为什么验证码每 30 秒变一次——不是 App 在随机乱跳而是算法在按时间片推进。用生活化的方式理解这就好比保险柜的密码锁每 30 秒自动换一组密码而你和银行约定好同一本密码手册和同一个对表时钟只有你手里那本手册才能算出当前时刻该输哪个密码。攻击者就算偷看你输了一次下一分钟这个密码就过期了偷看毫无意义。这个机制天然抗重放攻击。就算有人在网络上抓到了你输入的验证码他也没法在当前时间片之外复用它而且常见的配置里每个时间片内的验证码都会被限制使用次数。把 TOTP 接到 SSH 登录上等于让每一次登录都变成一次动态挑战而不是一个固定不变的凭证。1.3 为什么选 Google Authenticator而不是其他方案TOTP 是开放标准Google Authenticator 只是其中一种实现。选它做服务端的 PAM 模块原因很现实一是 google-authenticator 这个包在社区里维护得久RHEL/Rocky 生态的 EPEL 仓库直接提供装上就能用二是它的 pam_google_authenticator.so 模块和 sshd 的键盘交互式认证配合得非常顺滑不需要额外开端口、跑服务三是客户端生态足够通用你手机里随便一个支持 RFC 6238 的 AppAuthy、FreeOTP、Microsoft Authenticator甚至密码管理器内置的 TOTP 功能都能扫码绑定不一定非要装谷歌家的 App。有些团队会考虑硬令牌或者类似 pam_duo 的在线验证服务它们各有优势但都需要额外的接入成本和外部依赖。对于大多数单机或中小规模集群google-authenticator 加 PAM 是最轻量、最容易维护的方案。这里我并不是说其他方案不好而是从改造成本最低、后续最省心的角度它确实是上手最快的选择。2. 环境准备与安装EPEL 这一步别漏2.1 先确认系统和基础环境动手之前先把底摸清楚。Rocky 9.6 是基于 RHEL 9 分支的企业级发行版默认装的 OpenSSH 版本在 8.7p1 左右PAM 版本也比较新这些都会影响后面的参数选择。用一条命令确认系统版本cat /etc/rocky-release接下来检查 sshd 是否正常运行、当前能否用现有方式登录以及时间同步服务是否开启systemctl status sshd systemctl status chronyd timedatectlTOTP 对时间同步极度敏感客户端和服务器的时间差一旦超过允许窗口验证码就会一直显示错误。所以开机自带的 chronyd 必须保持在运行状态。如果服务器没连外网或者防火墙挡掉了 NTP 端口后面验证码会是一道很难排查的暗坑这块我在第五章会专门说。还要顺手确认 SELinux 状态。Rocky 默认是 Enforcing正常配置下它不会拦 google-authenticator但如果你后面要手工挪文件、改目录就得注意安全上下文的问题不然又会出现一个配置明明对了却登录不了的诡异现象。2.2 安装 EPEL 和 google-authenticatorgoogle-authenticator 并不在 Rocky 的 BaseOS 或 AppStream 仓库里它在 EPELExtra Packages for Enterprise Linux扩展仓库中。EPEL 是 Fedora 社区维护的扩展包仓库对企业级发行版几乎是标配。安装顺序应该是先装 epel-release再装 google-authenticatordnf install -y epel-release dnf install -y google-authenticator如果你之前已经装过 epel-release直接跳过第一步。安装完成后用 rpm 验证包里的内容rpm -ql google-authenticator正常你会看到 /usr/bin/google-authenticator 这个命令行工具以及 /usr/lib64/security/pam_google_authenticator.so 这个 PAM 模块。前者用来给用户生成秘密种子和二维码后者在 SSH 认证阶段被 PAM 调用负责校验验证码。2.3 安装后的依赖自检PAM 模块本质上是动态库依赖系统的 libpam 等基础库。如果哪天发现模块加载不了可以用 ldd 查一下依赖是否完整ldd /usr/lib64/security/pam_google_authenticator.so这一步排查在正常安装下很少出问题但当你从别的机器拷贝文件过来或者系统基础库被更新到不兼容版本时ldd 会立刻暴露问题。另外建议随便切到一个普通用户目录下执行/usr/bin/google-authenticator -h确认命令能跑起来再把下面的步骤继续往下做。有一点要提前说清楚这个包装好后并不会自动启用它只是把工具和模块摆到位了。真正的开关在 /etc/pam.d/sshd 和 /etc/ssh/sshd_config 里这也是新手最容易卡住的地方。3. 核心改造PAM 栈怎么和 sshd 配合3.1 PAM 配置怎么改才安全PAM 全称 Pluggable Authentication Modules是 Linux 下的可插拔认证框架。SSH 登录时sshd 会调用 PAM 去做实际的账号密码校验而 PAM 会按 /etc/pam.d/sshd 文件里声明的模块顺序一个接一个地执行认证。我们要做的就是把 pam_google_authenticator.so 插进这个链条里。用编辑器打开 /etc/pam.d/sshd在文件的 auth 部分加一行auth required pam_google_authenticator.so关键点在于这个模块放的位置。我通常放在最前面也就是pam_sepermit.so之前#%PAM-1.0 auth required pam_google_authenticator.so auth required pam_sepermit.so auth substack password-auth这样做的效果是认证时先要求输入 Google Authenticator 验证码然后再走系统的密码认证。两个模块都是 required意味着两关都要过任何一关失败都登录失败。这里有个容易踩的坑直接在 auth 段加 pam_google_authenticator.so 时如果用户目录下还没有生成 .google_authenticator 文件模块会直接拒绝认证导致所有用户都进不去。所以我通常建议先在文件里加上nullok选项auth required pam_google_authenticator.so nulloknullok 的意思是用户没有密钥文件时允许通过等所有需要 2FA 的用户都完成初始化绑定后再把这行里的 nullok 去掉强制全员都必须有验证码才能登录。生产环境我强烈建议按这个顺序来做别一上来就强制。3.2 sshd_config 里真正要改的项改完 PAM还要让 sshd 使用键盘交互认证把验证码的输入提示抛给用户。这里有一个版本相关的老坑很多旧教程会叫你设ChallengeResponseAuthentication yes但 OpenSSH 8.7 开始这个选项已被标记为废弃Rocky 9 里对应的正确写法是KbdInteractiveAuthentication yes在 /etc/ssh/sshd_config 里确认或添加以下几项UsePAM yes KbdInteractiveAuthentication yesUsePAM 默认就是 yes但如果之前有人手贱改过一定要改回来否则 PAM 那套 Google Authenticator 校验根本不会执行。PasswordAuthentication 保持 yes 也没关系因为密码已经在 PAM 栈里被要求了这里主要是放开键盘交互通道。如果你只想用密码 验证码这一种组合可以再加一行强制认证方式AuthenticationMethods keyboard-interactive加了这行之后sshd 只接受键盘交互方式登录外部用纯密码或者纯密钥直接连会被拒绝。如果只想对特定用户组启用还可以用 Match 块单独控制Match Group sysadmin AuthenticationMethods keyboard-interactive要特别提醒的是改 sshd_config 之前先开一个 second session 或者 screen/tmux防止配置写错导致所有会话都断掉。这个习惯我后面还会再强调因为它是很多人血泪换来的教训。3.3 模块参数与文件权限的讲究pam_google_authenticator.so 支持的参数不少常用的是这几个参数作用推荐值window-size允许的时间窗口偏移量3time-step-size时间片长度秒30rate-limit限制失败尝试次数3no-concurrent-login禁止同一账号并发登录可选这些参数既可以写在 PAM 行里覆盖全局也可以在每个用户各自的 .google_authenticator 文件里设置。用户文件里写的配置通常比全局更灵活比如某个账号允许稍大窗口、另一个账号启用并发限制互不影响。再强调一下文件权限。google-authenticator 生成的 .google_authenticator 位于用户家目录里面保存着秘密种子和应急码权限必须收紧chmod 0400 ~/.google_authenticator如果权限过宽模块会直接拒绝读取并报错防止密钥泄露。这一步其实是在保护你自己别小看它。4. 完整实操从手机绑定到验证登录4.1 运行初始化命令生成秘密种子配置改完之后每个要使用 2FA 的用户需要登录一次服务器在自己的家目录下执行google-authenticator这是一段交互式向导几个问题我逐个说清楚。第一个问题Do you want authentication tokens to be time-based选择 y也就是基于时间第二个问是否更新配置文件选 y 把生成结果写进 ~/.google_authenticator第三个问是否禁止并发登录按需选一般选 y 会更严格第四个问是否启用频率限制强烈建议选 y这能限制每 30 秒内最多尝试 3 次防止验证码被暴力枚举。向导结束后终端会显示一大块 ASCII 二维码、一个 Base32 格式的 Secret Key以及 5 个紧急备用码。这 5 个代码是单次使用的每个 8 位一定要复制到密码管理器里存好最好别存在同一台服务器上。真到了手机丢失、App 重置的时候这几个代码是你唯一的救命绳。4.2 手机端绑定与替代 App拿出手机打开 Google Authenticator选添加账户扫码即可。如果扫码不方便也可以手动输入终端里显示的 Base32 Secret Key。这里要说明一点TOTP 是开放协议你未必非要用 Google Authenticator 这个 App。我用过 Authy、FreeOTP、Microsoft Authenticator扫同一个码都能绑定。有些密码管理器也内置 TOTP可以把验证码和账号密码放一起这种方式有得有失看团队取舍。绑定成功之后App 里会显示一个 6 位数字每 30 秒刷新一次。你可以先在本机对照终端里显示的验证码确认 App 计算的和服务器计算的一致。如果数值对不上八成是手机时间和服务器时间差太多先把手机时间同步打开再回服务器上执行timedatectl看系统时间。4.3 重启 sshd 并验证登录流程服务端配置改完需要让它生效systemctl restart sshd注意这里是 restart 不是 reload。reload 对 sshd_config 的部分改动有效但 PAM 模块和认证方式的变更最好用 restart确保新配置完全加载。重启后千万别急着关掉当前连接先开一个新终端测试确认能登录再放手。成功场景下你会看到这样的登录过程输入 ssh 用户名和 IP提示输入验证码然后提示输入密码。两个都对了才能登录进去。在 /var/log/secure 里能看到类似 Accepted keyboard-interactive 的记录证明走的是键盘交互认证。4.4 进阶玩法密钥 验证码双保险如果你的一贯习惯是密钥登录也可以要求密钥 验证码组合。方法是在 sshd_config 里写AuthenticationMethods publickey,keyboard-interactive逗号表示并且意思是客户端必须先完成公钥认证再完成键盘交互也就是 PAM 里的验证码和密码缺一不可。这样即使私钥泄露别人没有验证码也进不来。注意这个配置会完全替代默认的认证方式所以在改动前务必保持当前会话存活别有侥幸心理。这种模式下客户端会先走密钥校验通过后再弹验证码提示。我在实际使用中觉得这个组合是当前 SSH 登录里最稳的形态密钥负责你拥有的凭证验证码负责你手上的动态凭证两边互补单边失守都不至于直接沦陷。4.5 多用户场景与备份注意事项如果是多用户服务器每个用户都必须单独执行一次 google-authenticator每个账号对应不同的秘密种子。从安全角度我不建议把同一个用户的 .google_authenticator 文件复制给其他账号用一旦有人泄露种子所有共用账号全部受影响排查起来非常痛苦。对于监控脚本、自动化任务这类服务账号需要提前想清楚策略。简单粗暴的排除账号虽然省事但会留下一个 2FA 的缺口。我见过一种做法是把服务账号的 TOTP secret 存到内部密码管理系统里让自动化工具每次动态取码也见过在 PAM 层面用 pam_succeed_if 按用户名跳过 2FA 的那个适合内网强隔离环境公网环境不太推荐。备份方面除了前面说的应急码也可以把 .google_authenticator 文件内容加密后存入公司内部的密码库。这样即使服务器重装、用户文件丢失也能恢复出原始秘密重新绑定手机 App而不必让用户重新跑一遍初始化流程。5. 常见问题与排查实录5.1 登录提示 Permission denied 但密码明明是对的这种场景最让人抓狂。排查思路按顺序来先看是不是 PAM 配置没生效执行journalctl -u sshd -f再开一个终端尝试登录观察日志。如果报类似 pam_google_authenticator.so: Module is unknown 的错误说明模块文件没被识别回到 2.2 节重装包如果是 Could not open file /home/user/.google_authenticator说明这个用户还没执行过初始化或者 nullok 没加导致直接拒绝。另一个隐蔽原因是 sshd_config 里把 KbdInteractiveAuthentication 设成了 no导致键盘交互认证根本没被调用检查一下那行。5.2 验证码总是提示无效验证码不对十有八九是时间问题。TOTP 的容差窗口默认只有 3 个时间片也就是前后各 90 秒左右。先看服务器时间date timedatectl status再看 chrony 是否同步chronyc tracking如果显示既没建立连接又没同步就需要检查到 NTP 服务器的网络连通性和防火墙规则。手机端也一样别开省电模式导致 App 的时间基准被冻结。另外如果验证码在某个时间片刚切换时录入建议等几秒再输避免边界问题。如果时间完全同步还是不行可以尝试在 ~/.google_authenticator 文件里手动把窗口调大一点把 WINDOW_SIZE 3改成 WINDOW_SIZE 5。这只是临时排查手段长期不要放大窗口否则暴力破解面会变大。5.3 SELinux 和文件权限导致的奇怪拒绝Rocky 默认 SELinux 是 Enforcing。正常情况下 google-authenticator 读自己家目录下的普通文件不会触发拦截但如果你是从备份恢复、用 root 帮用户生成了文件、或者文件从别的服务器拷贝过来就可能出现模块明明装了、配置也没错但认证还是失败的情况。这时看 SELinux 日志ausearch -m AVC -ts recent如果确实有类型拦截用 restorecon 修复一下文件上下文restorecon -Rv /home/user/.google_authenticator同时确认文件属主是登录用户权限不要超过 0400。这里有一个小窍门实际排障时可以临时把 SELinux 切到 Permissive 对比一下确认问题后记得切回 Enforcing别为了省事长期关掉它。5.4 最坏情况把自己锁在门外在不巧连 restart sshd 都没能保住当前会话、验证码又一直不对的情况下只能走带外通道比如物理显示器、IPMI/iLO、云厂商的 VNC 控制台。进入系统后先停掉 sshd把 /etc/pam.d/sshd 里那行 google-authenticator 注释掉重启 sshd 恢复到无验证码模式然后排查时间问题或者重新初始化用户。应急码也是重要保底手段锁死的情况下用其中一张一次性代码能直接登录登录后再重新审视配置。我自己的习惯是每次改认证配置前都会先写好一条恢复路径比如准备一个还能用的控制台会话或者确认 IPMI 可以访问。这不是小题大做是生产环境的基本素养。下面是常见问题的速查表实际排障时可以照着过一遍。现象大概率原因快速处理Permission denied 但密码正确模块未装或 PAM 未生效确认 EPEL 与包检查 PAM 行Permission denied 且日志无模块记录KbdInteractiveAuthentication 为 no置为 yes 并 restart sshd验证码无效时间不同步chronyc tracking 手机时间修正验证码无效且时间正常窗口太小或种子不一致临时放宽窗口或重新绑定提示无法读取配置文件权限或属主或 SELinuxchmod 0400 restorecon说实话这套东西配置起来门槛并不高真正考验人的是对每个选项的理解。我个人的习惯是先在一台测试机上把 PAM 和 sshd 的交互流程彻底跑通再上生产。google-authenticator 这种方案一旦用顺手会变成你服务器安全配置里最不起眼但最可靠的一环。最后分享一个小技巧如果你有多台服务器每台都生成自己独立的 secret千万别图省事直接复制同一个 .google_authenticator 文件否则一台失守等于整片线上全部暴露。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询