引言
你敲下 sudo 或通过 SSH 登录时,系统在背后做了一连串认证与授权决策——这些决策几乎都由两个组件掌管:**PAM(Pluggable Authentication Modules,可插拔认证模块)**负责「你是谁、能不能登录」,sudo负责「你能以谁的身份、执行什么命令」。PAM 把认证逻辑拆成可插拔的模块栈,让密码策略、账户锁定、资源限制、双因素认证都能像搭积木一样组合;sudo 则用一套精细的策略语言实现最小权限。
本文先拆 PAM 的栈模型与四类模块,讲透 /etc/pam.d 的读法与常用模块;再深入 sudoers 语法、别名、NOPASSWD 的风险与日志审计,最后给出一套加双因素认证的实战与排错方法。
前置:用户、组与文件权限基础见 Linux 用户权限与安全加固 ,本文是其认证与提权进阶篇;SSH 认证配置见 Linux SSH 服务与隧道 ,审计见 auditd 与合规取证 。
1. PAM 是什么:认证的可插拔框架
在没有 PAM 的年代,每个程序(login、su、sshd、passwd)都要自己实现认证逻辑——想加一条「密码错误三次锁定」的规则,就得改所有程序。PAM 把这些逻辑抽成独立的共享库模块,程序只调用统一的 PAM API,具体认证流程由 /etc/pam.d/ 下的配置栈决定。
应用(login/su/sshd/sudo)
│ 调用 pam_authenticate() / pam_acct_mgmt() / ...
↓
PAM 框架(libpam)
│ 读 /etc/pam.d/<服务名> 的配置栈
↓
模块(pam_unix.so、pam_faillock.so、pam_pwquality.so …)
│ 各自完成一件事,返回 success/fail
↓
最终结果 → 应用决定放行或拒绝
| 好处 | 说明 |
|---|---|
| 关注点分离 | 应用只管业务,认证交给 PAM |
| 可组合 | 密码、锁定、双因素自由叠加 |
| 集中配置 | 一处改动,所有服务生效 |
| 可插拔 | 换认证后端(本地/LDAP/SSSD)不改应用 |
心智:PAM 是「认证的中间件」——它把「怎么认证」从应用里解耦出来,变成一个可配置的模块栈。理解栈的执行顺序,就理解了 PAM。
2. PAM 栈模型:四种模块类型与控制标志
一条 PAM 配置行的格式是:类型 控制标志 模块路径 [参数]。类型决定「在认证流程的哪个阶段执行」,控制标志决定「返回值如何影响整体结果」。
| 类型 | 何时执行 | 作用 |
|---|---|---|
auth | 认证阶段 | 验证身份(密码、指纹、OTP) |
account | 授权阶段 | 账户是否有效(过期、锁定、时段限制) |
password | 改密阶段 | 更新认证凭据(改密码时) |
session | 会话阶段 | 登录前后要做什么(挂载家目录、限资源、记日志) |
| 控制标志 | 语义 |
|---|---|
required | 必须成功;失败则最终失败,但继续执行后续模块 |
requisite | 必须成功;失败则立即返回失败,不再往下 |
sufficient | 成功则立即返回成功(跳过后续),失败则忽略 |
optional | 成功或失败都不决定结果(除非它是唯一模块) |
include | 包含另一个配置文件(复用栈) |
substack | 像 include 但失败只影响子栈 |
典型认证栈的「短路」逻辑:
auth required pam_faillock.so preauth ← 先检查是否已锁定
auth [success=1 default=bad] pam_unix.so ← 密码正确则跳过下一行
auth requisite pam_deny.so ← 密码错则直接拒绝
auth sufficient pam_unix.so ← (其他认证方式)
记忆:
required会「记账后继续」,requisite会「立即止损」。密码校验常配requisite,多因素里sufficient用来「任一通过即可」。
3. /etc/pam.d 结构:从 login 到 sshd
/etc/pam.d/ 下每个文件对应一个服务,文件名即服务名。
ls /etc/pam.d/ # 看有哪些服务
cat /etc/pam.d/sshd # SSH 登录的认证栈
cat /etc/pam.d/sudo # sudo 的认证栈
# 典型的 /etc/pam.d/sshd(简化)
auth required pam_env.so
auth required pam_faillock.so preauth
auth [success=1 default=bad] pam_unix.so
auth required pam_deny.so
account required pam_unix.so
account required pam_faillock.so
session required pam_limits.so
session required pam_systemd.so
@include common-session
发行版常把通用配置抽到 common-auth、common-account、common-password、common-session,各服务再 @include 复用。改这些公共文件会影响所有服务,改前务必评估影响面。
# Debian 系的公共文件
ls /etc/pam.d/common-*
# RHEL 系用 system-auth / password-auth
ls /etc/pam.d/system-auth /etc/pam.d/password-auth
铁律:改 PAM 前先开一个备用 root 会话。PAM 配错会导致「所有登录方式同时失效」——包括 sudo 和 SSH,届时可能只能进单用户模式救援。
4. 常用 PAM 模块详解
| 模块 | 类型 | 作用 |
|---|---|---|
pam_unix.so | auth/account/password/session | 本地 /etc/shadow 认证(核心) |
pam_faillock.so | auth/account | 失败次数锁定账户 |
pam_pwquality.so | password | 密码复杂度校验 |
pam_limits.so | session | 应用 /etc/security/limits.conf |
pam_mkhomedir.so | session | 首次登录自动建家目录 |
pam_systemd.so | session | 注册 systemd 会话(cgroup、logind) |
pam_nologin.so | account | /etc/nologin 存在时禁止非 root 登录 |
pam_deny.so | 任意 | 永远失败(配合控制标志做「兜底拒绝」) |
# pam_unix 常用参数
# nullok —— 允许空密码(危险,别用)
# sha512 —— 密码哈希算法
# try_first_pass —— 用上一个模块已输入的密码
auth required pam_unix.so try_first_pass nullok_secure
# pam_limits 让 /etc/security/limits.conf 生效
cat /etc/security/limits.conf
# * soft nofile 65535
# * hard nofile 65535
# @dev hard nproc 4096
心法:
pam_limits.so是「让 ulimit 限制真正生效」的关键——/etc/security/limits.conf只声明规则,必须由pam_limits在 session 阶段应用,缺了它限制形同虚设。
5. 密码策略:pwquality 与 faillock
密码复杂度用 pam_pwquality:
sudo apt install libpam-pwquality # 提供 pam_pwquality.so
# /etc/security/pwquality.conf
minlen = 12 # 最小长度
dcredit = -1 # 至少 1 个数字
ucredit = -1 # 至少 1 个大写
lcredit = -1 # 至少 1 个小写
ocredit = -1 # 至少 1 个特殊字符
maxrepeat = 3 # 最多连续重复 3 次
minclass = 3 # 至少 3 类字符
dictcheck = 1 # 拒绝字典词
# /etc/pam.d/common-password 里启用
password requisite pam_pwquality.so retry=3
password [success=1 default=ignore] pam_unix.so sha512 shadow
账户锁定用 pam_faillock(pam_tally2 的现代替代):
# /etc/security/faillock.conf(较新写法,集中配置)
deny = 5 # 连续失败 5 次锁定
unlock_time = 900 # 锁 900 秒
fail_interval = 900 # 统计窗口
even_deny_root # root 也锁(慎用)
# /etc/pam.d/common-auth
auth required pam_faillock.so preauth # 认证前检查是否锁定
auth [success=1 default=bad] pam_unix.so
auth [default=die] pam_faillock.so authfail # 失败记账
auth sufficient pam_faillock.so authsucc # 成功则清零
# 查看/解锁被锁账户
faillock --user alice # 查看失败记录
faillock --user alice --reset # 手动解锁
| 参数 | 含义 | 建议 |
|---|---|---|
deny | 失败几次锁定 | 5 |
unlock_time | 锁定时长(秒) | 900 |
fail_interval | 失败统计窗口 | 900 |
even_deny_root | 是否锁 root | 默认否 |
铁律:配 faillock 时一定要成对写
preauth与authfail。只写authfail不写preauth,账户被锁后永远无法触发检查;只写preauth不写authfail,则永远不记账。
6. sudo 深入:sudoers 语法与别名
sudo 的策略在 /etc/sudoers(必须用 visudo 编辑,它会做语法校验)。规则格式:
用户 主机=(以谁的身份) [NOPASSWD:] 命令
# /etc/sudoers
root ALL=(ALL:ALL) ALL
%wheel ALL=(ALL:ALL) ALL # wheel 组全员可 sudo
alice ALL=(ALL) /usr/bin/systemctl restart nginx
bob ALL=(postgres) NOPASSWD: /usr/bin/psql
别名让规则更易维护:
# 用户别名
User_Alias ADMINS = alice, bob, %ops
# 主机别名
Host_Alias WEB = web1, web2
# 命令别名
Cmnd_Alias SVC = /usr/bin/systemctl restart nginx, /usr/bin/systemctl reload nginx
Cmnd_Alias NET = /usr/sbin/ip, /usr/sbin/ss, /usr/bin/ping
ADMINS WEB=(root) SVC
ADMINS ALL=(root) NET
| 别名类型 | 语法 | 用途 |
|---|---|---|
User_Alias | 用户/组 | 批量授权人 |
Host_Alias | 主机名 | 多机统一配置 |
Cmnd_Alias | 命令(全路径) | 批量授权命令 |
Runas_Alias | 目标用户 | 批量定义身份 |
# 推荐用 /etc/sudoers.d/ 分文件管理(Debian 需确认 include 已启用)
sudo visudo -f /etc/sudoers.d/ops
# 校验整个 sudoers
sudo visudo -c
心法:命令必须写全路径。
Cmnd_Alias里写systemctl无效,必须/usr/bin/systemctl——sudo 按绝对路径匹配,写相对路径等于没授权。
7. sudo 安全配置与日志审计
# /etc/sudoers 的 Defaults 常用项
Defaults env_reset # 清空危险环境变量
Defaults secure_path="/usr/sbin:/usr/bin:/sbin:/bin"
Defaults timestamp_timeout=5 # 免密窗口 5 分钟
Defaults logfile="/var/log/sudo.log"
Defaults log_input, log_output # 记录输入输出(谨慎,占空间)
Defaults passwd_tries=3
Defaults use_pty # 强制伪终端,防提权绕过
# sudo 默认日志位置(视发行版)
journalctl -t sudo
grep sudo /var/log/auth.log
# 自定义日志文件
sudo tail -f /var/log/sudo.log
| 加固项 | 做法 |
|---|---|
| 最小授权 | 只授具体命令,不授 ALL |
| 全路径命令 | Cmnd_Alias 用绝对路径 |
| 禁危险命令 | 避免授权 vi/less/find(可逃逸成 shell) |
| 强制 pty | Defaults use_pty |
| 清环境 | Defaults env_reset + secure_path |
| 审计 | logfile + log_input/output |
# 危险授权示例(等于给了 root shell,务必避免)
alice ALL=(ALL) /usr/bin/vi ← vi 内 :!sh 直接拿 shell
alice ALL=(ALL) /usr/bin/find ← find -exec /bin/sh 逃逸
alice ALL=(ALL) NOPASSWD: ALL ← 等于无密码 root
铁律:
NOPASSWD: ALL与授权vi/find/less等高危命令,等价于交出 root。它们都能「逃逸」出命令本身拿到交互式 shell——授权前先想清楚「这条命令能不能起子进程」。
8. 实战:加双因素认证
用 pam_google_authenticator 给 SSH/sudo 加 TOTP 双因素:
sudo apt install libpam-google-authenticator
# 以目标用户身份初始化(生成密钥 + 二维码 + 恢复码)
google-authenticator --time-based --disallow-reuse --rate-limit=3
# 1) SSH 认证栈启用(/etc/pam.d/sshd)
auth required pam_google_authenticator.so nullok
# 2) sshd_config 开启挑战响应
# KbdInteractiveAuthentication yes
# 3) 也可用于 sudo(/etc/pam.d/sudo)
auth required pam_google_authenticator.so
| 参数 | 作用 |
|---|---|
--time-based | 基于时间(TOTP,30 秒一换) |
--disallow-reuse | 同一验证码不能用两次 |
--rate-limit=3 | 限制尝试频率 |
nullok | 未配置的用户仍可登录(灰度用,最终应去掉) |
# 验证:登录时应先要验证码再要密码
ssh alice@host
心法:上双因素务必留「后路」——先给一个管理账户配好恢复码、或保留一个免 TOTP 的应急通道,再全量推开。否则密钥文件丢失或时间漂移会导致所有人被锁在门外。
9. 排错与调试
# PAM 调试:临时加 debug 参数,看详细执行日志
# /etc/pam.d/sshd 里某行加 debug
auth required pam_unix.so debug
# 查看日志
journalctl -u sshd -f
tail -f /var/log/auth.log
# 语法校验 sudoers
sudo visudo -c
# 检查 PAM 配置文件是否引用了不存在的模块
ls /lib/*/security/pam_*.so
| 现象 | 原因 | 对策 |
|---|---|---|
| 改 PAM 后全部登录失败 | 栈写错 | 用备用 root 会话回滚 |
| sudo 提示「不在 sudoers 中」 | 用户未授权 | visudo 加规则 |
| 密码正确但登录被拒 | account 阶段失败(过期/锁定) | 查 faillock、chage -l |
| 账户被永久锁 | unlock_time=0 或未配 preauth | faillock --reset |
| 双因素不生效 | sshd 未开 KbdInteractive | 改 sshd_config |
# 查看账户状态与密码策略
chage -l alice
faillock --user alice
铁律:改 PAM 的黄金法则是「留一个活动 root 会话 + 先备份原文件」。PAM 没有「语法检查」命令,唯一的安全网是「改完立即在另一个终端测试登录」。
10. 速查表
| 需求 | 做法 |
|---|---|
| 看服务认证栈 | cat /etc/pam.d/<service> |
| 编辑 sudoers | visudo / visudo -f /etc/sudoers.d/x |
| 校验 sudoers | visudo -c |
| 密码复杂度 | pam_pwquality + /etc/security/pwquality.conf |
| 账户锁定 | pam_faillock + /etc/security/faillock.conf |
| 解锁账户 | faillock --user X --reset |
| 应用 ulimit | pam_limits.so + limits.conf |
| 免密 sudo | NOPASSWD:(慎用) |
| sudo 日志 | Defaults logfile= / journalctl -t sudo |
| 双因素 | pam_google_authenticator.so |
| 调试 PAM | 模块加 debug 参数 |
一句话记忆:PAM 把认证拆成 auth/account/password/session 四类模块、用 required/requisite/sufficient/optional 控制短路逻辑,/etc/pam.d 一个文件一套栈;sudo 用 visudo 管策略、命令写全路径、别名助维护,NOPASSWD: ALL 与授权 vi/find 等于交出 root——改 PAM 前永远留一个活动 root 会话。
延伸阅读
- Linux 用户权限与安全加固 — 用户、组、ACL 与 sudo 入门
- Linux 安全加固实战 — 纵深防御与 CIS 基线
- Linux SSH 服务与隧道 — sshd 认证与密钥加固
- Linux 审计与合规取证 — 认证事件审计
- 安全专题 — 身份认证与攻防视角
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。