PAM 认证与权限提升:模块栈、密码策略与 sudo 深入

深入 Linux PAM 认证与权限提升:可插拔认证框架的栈模型、auth/account/password/session 四类模块与控制标志、/etc/pam.d 结构、pam_faillock/pam_pwquality/pam_limits 详解、sudoers 语法与别名、NOPASSWD 风险、sudo 日志审计,以及双因素认证实战。

引言

你敲下 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.soauth/account/password/session本地 /etc/shadow 认证(核心)
pam_faillock.soauth/account失败次数锁定账户
pam_pwquality.sopassword密码复杂度校验
pam_limits.sosession应用 /etc/security/limits.conf
pam_mkhomedir.sosession首次登录自动建家目录
pam_systemd.sosession注册 systemd 会话(cgroup、logind)
pam_nologin.soaccount/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)
强制 ptyDefaults 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 或未配 preauthfaillock --reset
双因素不生效sshd 未开 KbdInteractive改 sshd_config
# 查看账户状态与密码策略
chage -l alice
faillock --user alice

铁律:改 PAM 的黄金法则是「留一个活动 root 会话 + 先备份原文件」。PAM 没有「语法检查」命令,唯一的安全网是「改完立即在另一个终端测试登录」。

10. 速查表

需求做法
看服务认证栈cat /etc/pam.d/<service>
编辑 sudoersvisudo / visudo -f /etc/sudoers.d/x
校验 sudoersvisudo -c
密码复杂度pam_pwquality + /etc/security/pwquality.conf
账户锁定pam_faillock + /etc/security/faillock.conf
解锁账户faillock --user X --reset
应用 ulimitpam_limits.so + limits.conf
免密 sudoNOPASSWD:(慎用)
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」更多文章

  1. NFS 与 CIFS 网络文件系统:服务端配置、挂载调优与安全
  2. LVM 与 RAID 存储管理:卷管理、快照与软阵列运维
  3. cgroups v2 资源控制:统一层级、CPU/内存/IO 与 systemd 集成