Linux 基础体系 · 第 7/85 篇。示例面向现代主流 Linux 发行版;发行版差异、权限和生产风险会明确说明。

Linux 用户认证与提权:账号、PAM、sudo、密码策略和审计

Linux 上一次“登录并执行命令”的结果,不是由某个单独文件决定的。它通常同时经过以下几层:

  1. 账号解析:用户名对应哪个 UID、主组和附加组?
  2. 认证:用户能否证明自己拥有某种凭据?
  3. 账号检查:账号是否过期、被锁定或禁止登录?
  4. 会话建立:环境变量、资源限制、挂载和审计上下文如何初始化?
  5. 授权与提权:当前进程能否执行目标操作,是否允许切换到更高权限?
  6. 审计:谁在什么时间、通过什么入口、以什么身份执行了什么操作?

“认证成功”只说明凭据校验通过;它不等于“可以执行任意命令”。sudo 也不是认证机制本身,而是一个根据授权规则执行特定命令的受控提权工具。


一、先区分身份、认证、授权和审计

1. Linux 进程真正使用的是 UID/GID

Linux 内核判断权限时主要看进程凭据,而不是用户名字符串。一个进程通常具有:

  • 真实 UID(RUID):通常表示启动该进程的用户;
  • 有效 UID(EUID):普通文件权限检查主要使用它;
  • 保存的 set-user-ID(Saved UID):支持某些程序在运行期间恢复权限;
  • 真实 GID、有效 GID 和附加组列表
  • capabilities:把传统 root 的部分能力拆分出来的权限集合。

一个简化的授权模型是:

allow(p,o)=DAC(uidp,gidp,groupsp,modeo)LSM(contextp,contexto)capability(p)other policy\text{allow}(p,o) = \text{DAC}(uid_p,gid_p,groups_p,mode_o) \land \text{LSM}(context_p,context_o) \land \text{capability}(p) \land \text{other\ policy}

其中:

  • pp 是进程;
  • oo 是目标对象或操作;
  • DAC 是传统 Unix 自主访问控制;
  • LSM 可能是 SELinux、AppArmor 等强制访问控制;
  • capability 是内核能力检查;
  • 其他策略可能包括 seccomp、设备策略、容器边界等。

因此,拥有 UID 0 通常意味着权限极高,但现代系统上仍可能受到 LSM、容器、受限 capability 集合或内核配置的限制。反过来,非 root 进程也可能因为某个 capability 或 setuid 程序获得特殊能力。

例如:

id alice
getent passwd alice
getent group developers

可能得到:

uid=1001(alice) gid=1001(alice) groups=1001(alice),10(wheel),1002(developers)
1001:x:1001:1001:Alice:/home/alice:/bin/bash
developers:x:1002:alice

id 展示的是当前系统解析出的身份;getent 通过 NSS(Name Service Switch)查询账号数据库。账号来源不一定是本地文件,也可能是 LDAP、SSSD、NIS 或其他 NSS 模块。因此,直接读取 /etc/passwd 不能保证看到系统实际使用的全部账号。

认证、授权和审计的区别

  • 认证(authentication):证明“你是谁”或“你掌握什么凭据”。
  • 授权(authorization):决定“你可以做什么”。
  • 会话管理(session management):登录成功后如何建立和清理运行环境。
  • 审计(auditing):记录身份变化、命令执行和安全事件,以便追踪与取证。

下面这个反例很重要:

用户用正确密码通过了 PAM 的认证模块,但如果 /etc/sudoers 没有允许该用户执行目标命令,sudo 仍然会拒绝操作。

同样,用户已经被授权运行 /usr/bin/systemctl restart nginx,也不代表他能运行任意 shell;授权规则的对象是具体命令及其参数匹配关系,而不是“登录成功”这一状态。


二、账号数据库:/etc/passwd/etc/shadow 与 NSS

/etc/passwd 保存身份映射,不保存密码

典型行如下:

alice:x:1001:1001:Alice Example:/home/alice:/bin/bash

七个字段依次是:

用户名:密码占位符:UID:GID:GECOS:家目录:登录 Shell

现代系统通常把第二字段设为 x,实际密码哈希保存在只有 root 或特定组可读的 /etc/shadow 中。

/etc/passwd 的权限通常类似:

-rw-r--r-- root root /etc/passwd

它需要对普通进程可读,因为大量程序需要将 UID 映射为用户名。真正需要保护的是密码验证材料和其他敏感账号属性。

/etc/shadow 的密码字段

典型格式:

alice:$y$j9T$...:19800:0:99999:7:::

各字段含义包括:

  1. 用户名;
  2. 密码哈希,特殊值还可能表示锁定或禁止密码登录;
  3. 上次修改密码的日期,单位通常是自 Unix epoch 起的天数;
  4. 修改密码后最少等待多少天;
  5. 密码最多有效多少天;
  6. 到期前多少天开始警告;
  7. 密码过期后多少天禁用账号;
  8. 账号失效日期;
  9. 保留字段。

密码哈希不是“可解密的密码”。验证过程通常是:

h=H(algorithm,salt,password)h = H(\text{algorithm}, \text{salt}, \text{password})

系统从 shadow 中取出算法和 salt,使用用户输入的密码重新计算,再以抗侧信道方式比较结果。现代发行版通常使用 yescrypt、bcrypt、SHA-512 等实现,具体算法由发行版和 PAM 配置决定。不要假设所有系统都使用同一种前缀或算法。

查看密码状态:

sudo passwd -S alice
sudo chage -l alice

可能看到:

alice P 03/01/2025 0 99999 7 -1

其中 P 通常表示存在可用密码;不同发行版输出格式略有差异。chage -l 比直接解析 shadow 更适合运维检查,因为它会把日期转换成人可读格式。

锁定密码不等于禁用整个账号

以下操作语义不同:

sudo passwd -l alice
sudo usermod --shell /usr/sbin/nologin alice
sudo usermod --expiredate 1 alice
  • passwd -l 通常是在密码哈希前增加锁定标记,阻止该密码用于认证;
  • nologin 使通过该登录 shell 建立交互式会话失败,但不一定阻止所有服务使用该账号;
  • 过期账号会触发账号状态检查失败;
  • SSH 密钥认证可能不依赖密码,因此“锁密码”不一定阻止密钥登录;
  • 运行中的进程不会因为账号被锁定而自动终止。

禁用离职账号时,必须检查 SSH authorized keys、cron、systemd user service、API token、云平台凭据以及正在运行的进程,不能只执行 passwd -l


三、从用户名到认证后会话:NSS 与 PAM 的数据流

Linux 登录程序通常不会自己实现全部认证逻辑,而是通过两个不同抽象层:

  • NSS:把用户名、组名等名称解析为系统身份数据;
  • PAM:把认证、账号检查、密码修改和会话管理交给可组合模块。

典型流程如下:

flowchart TD
    A[login/sshd/sudo] --> B[NSS: 用户与组解析]
    B --> C[PAM service 配置]
    C --> D[auth: 验证凭据]
    D --> E[account: 检查账号状态]
    E --> F[password: 修改密码时使用]
    E --> G[session: 打开会话]
    G --> H[设置 UID/GID、环境、限制]
    H --> I[启动 shell 或目标命令]
    I --> J[session close 与日志]

图中的四类 PAM 管理组职责不同:

auth:验证凭据

例如密码、智能卡、OTP、指纹或 Kerberos 凭据。它回答:

当前请求者是否能通过指定的认证方式?

account:检查账号状态和访问条件

例如:

  • 密码是否过期;
  • 账号是否过期;
  • 当前时间是否允许登录;
  • 用户是否属于允许的组;
  • 远程目录服务是否允许访问。

认证成功后,账号检查仍可能失败。

password:修改认证秘密

例如执行:

passwd

时,PAM 可以检查旧密码、密码复杂度、历史密码以及新密码哈希写入方式。

session:建立和关闭会话

常见工作包括:

  • 写入登录记录;
  • 设置 ulimit
  • 创建运行时目录;
  • 设置环境;
  • 应用 SELinux 或其他安全上下文;
  • 建立或销毁 systemd/logind 会话。

并不是所有程序都会完整使用四类模块。sshdlogin、图形显示管理器、sudosu 各自使用不同的 PAM service 名称,例如:

/etc/pam.d/sshd
/etc/pam.d/sudo
/etc/pam.d/login
/etc/pam.d/su

程序通常读取 /etc/pam.d/<service>,再按顺序执行模块。模块控制标志常见为:

  • required:失败会导致最终失败,但通常继续执行后续模块;
  • requisite:失败后立即终止;
  • sufficient:本模块成功且没有更早的 required 失败时,可提前成功;
  • optional:通常不单独决定结果;
  • 方括号语法:允许更细粒度地映射返回码。

这里有一个故障边界:PAM 配置是“可执行安全策略”,不是普通配置文本。错误地修改 system-authcommon-auth 或发行版生成的聚合配置,可能同时破坏 SSH、sudo、图形登录和本地控制台。修改前应保留 root 控制台或现有 root 会话,并使用发行版提供的配置工具;不要在唯一远程会话中直接试验。

查看有效配置:

grep -vE '^\s*(#|$)' /etc/pam.d/sudo
grep -vE '^\s*(#|$)' /etc/pam.d/sshd

日志位置取决于发行版,常见查询方式:

sudo journalctl -b | grep -Ei 'pam|sshd|sudo|authentication'
sudo tail -f /var/log/auth.log      # Debian/Ubuntu 常见
sudo tail -f /var/log/secure        # RHEL/Fedora 常见

不要假定某个日志文件必然存在;systemd journal、rsyslog 配置和发行版默认值都会影响结果。


四、密码认证和密码策略:检查发生在哪里

密码策略至少包含三类不同问题:

  1. 密码内容质量:长度、字符空间、是否出现在泄漏字典中;
  2. 密码生命周期:最短修改间隔、最大有效期、过期宽限期;
  3. 在线猜测防护:失败次数、延迟、锁定或 MFA。

pam_pwquality 与密码内容

许多发行版使用 pam_pwquality,配置可能位于:

/etc/security/pwquality.conf

示例:

minlen = 14
retry = 3
dictcheck = 1

这些参数只影响通过该 PAM 路径修改密码的行为,不能自动约束:

  • LDAP 服务端密码策略;
  • 云平台控制台;
  • 应用自己的账号数据库;
  • 已存在的密码;
  • SSH 公钥或硬件密钥认证。

“强制定期改密码”也不是无条件提高安全性。频繁轮换可能促使用户使用可预测的后缀;对人类可记忆密码,更重要的通常是足够长度、阻止已泄漏密码、MFA 和登录限速。是否设置最大有效期要结合合规要求和身份系统能力,而不是机械套用单一数值。

pam_faillock 与失败锁定

现代发行版常见 pam_faillock,但配置文件、调用位置和默认行为存在差异。它可能记录连续失败次数并临时锁定账号。例如查看帮助:

man pam_faillock

不要直接复制其他发行版的 PAM 行。应先确认当前系统是否使用该模块,以及失败计数存储目录和 root 用户处理方式。

失败锁定的主要风险是拒绝服务:攻击者可以故意对合法用户名发送错误密码,使账号被锁。生产策略通常需要在“抵御在线猜测”和“避免轻易锁死账号”之间取舍,并配合 IP、设备、MFA、速率限制和告警。

排障时,不能只看“密码正确”。应按顺序检查:

id alice
sudo passwd -S alice
sudo chage -l alice
sudo journalctl -u ssh -u sshd -b
sudo faillock --user alice       # 仅在系统安装并启用该工具时使用

faillock 命令是否存在、参数是否一致取决于发行版和 PAM 版本。若确认误锁定,清除失败计数前要验证请求来源,避免把正在进行的暴力攻击隐藏掉。


五、sudo:基于规则的受控提权

sudo 的核心语义

执行:

sudo systemctl restart nginx

可抽象为:

  1. 当前用户运行 sudo
  2. sudo 解析当前用户、主机、目标用户和命令;
  3. 读取 sudoers 规则;
  4. 必要时通过 PAM 认证当前用户;
  5. 检查是否允许该命令;
  6. 创建目标执行环境;
  7. 以目标用户身份执行命令;
  8. 记录授权和执行事件;
  9. 根据 timestamp 机制决定后续是否再次询问密码。

默认目标用户通常是 root,但可以用 -u 指定其他用户:

sudo -u deploy /usr/local/bin/reload-app

必须区分:

sudo command
sudo -i
sudo -s
  • sudo command:以目标身份执行一个命令;
  • sudo -i:模拟目标用户的登录 shell,加载其登录环境;
  • sudo -s:启动 shell,但环境处理方式与 -i 不同。

sudo -s 不等于安全沙箱。用户一旦获准运行可构造任意命令的解释器,就可能获得完整目标用户权限。

sudoers 的匹配模型

编辑规则应使用:

sudo visudo

visudo 会在保存前检查语法,避免直接编辑造成所有 sudo 规则失效。

一个受限示例:

Cmnd_Alias NGINX_RELOAD = /usr/bin/systemctl reload nginx
%webops ALL=(root) NGINX_RELOAD

Defaults:%webops use_pty
Defaults:%webops log_output

含义是:

  • %webops:组 webops 的成员;
  • 第一个 ALL:允许来自任意主机;
  • (root):以 root 身份执行;
  • NGINX_RELOAD:只匹配指定命令;
  • use_pty:使用伪终端,降低某些交互风险;
  • log_output:记录 I/O,但还需要确认日志存储与轮转配置。

验证规则:

sudo -l -U alice
sudo -u alice sudo -n /usr/bin/systemctl reload nginx
echo $?

-n 表示禁止交互式询问密码。若未缓存凭据或命令需要认证,它应失败而不是卡住等待输入。测试前提是你拥有 root 权限或已获授权,不要在生产服务上用“执行一次看看”的方式验证。

参数匹配的危险边界

以下规则风险很高:

alice ALL=(root) NOPASSWD: /usr/bin/vim
alice ALL=(root) NOPASSWD: /bin/bash
alice ALL=(root) NOPASSWD: /usr/bin/systemctl

原因不是这些程序本身“危险”,而是它们可以:

  • 启动 shell;
  • 读取或写入任意文件;
  • 加载插件;
  • 执行外部命令;
  • 修改服务定义;
  • 间接控制 root 运行的进程。

即使限制为某个脚本,也要检查脚本是否:

  • 使用绝对路径调用工具;
  • 清理或固定环境变量;
  • 不读取用户可写配置;
  • 不把用户参数拼接进 shell;
  • 不通过相对路径加载模块;
  • 不允许 --config--editor--pager 等改变执行流的参数。

例如,允许:

alice ALL=(root) /usr/local/sbin/rotate-app-logs

只有在该脚本及其依赖链都由 root 拥有、不可被 alice 或其组写入时才有意义:

namei -l /usr/local/sbin/rotate-app-logs
stat -c '%A %U:%G %n' /usr/local/sbin/rotate-app-logs

NOPASSWD 只取消 sudo 自身的密码确认,不会绕过文件权限、SELinux 或目标命令的其他检查;但它会让盗用的会话更容易立即提权,因此应只用于明确的自动化场景。

sudo 环境和 timestamp

sudo 默认会清理部分环境变量,并可能通过 secure_path 固定 PATH。查看有效配置:

sudo -V
sudo -l

不要依赖用户传入的 PATHLD_PRELOADPYTHONPATHPERL5OPT 等变量。动态链接器通常会对 setuid 程序忽略部分环境变量,但不能把这种行为当成通用安全边界;不同执行方式、解释器和程序实现可能产生不同结果。

sudo 的认证缓存(timestamp)意味着用户在一段时间内重复执行 sudo 可能不再输入密码:

sudo -k
sudo -v
sudo -n id
  • sudo -k:使当前用户的缓存凭据失效;
  • sudo -v:刷新或建立认证缓存,不执行命令;
  • sudo -n id:测试无交互执行。

timestamp 不是永久登录令牌,重启、TTY、配置和 sudo 版本都会影响其行为。高风险操作可以使用:

sudo -k
sudo -v
sudo systemctl restart nginx

但这仍不能替代 MFA、短时凭据和良好的会话管理。


六、su、setuid 和 capabilities:提权不是只有 sudo

susudo 的区别

su 的核心动作是切换到另一个用户,常见用法:

su - alice
su -c '/usr/bin/id' alice

su - 会更接近目标用户的登录环境;不带 - 则保留更多当前环境。系统可能通过 /etc/pam.d/supam_wheel 限制哪些用户可以切换到 root。

sudo 则是“当前用户请求执行某条命令”,规则可以按用户、主机、目标用户和命令细分,并通常留下更明确的审计记录。生产环境不应把“所有运维人员知道 root 密码”作为主要授权模型。

setuid 的数据流

一个 owner 为 root 且设置 setuid 位的可执行文件,执行时可能产生:

执行前: RUID=1001, EUID=1001
execve(setuid-root-program)
执行后: RUID=1001, EUID=0

内核据此允许程序以 EUID 0 访问资源。程序必须自行验证调用者、参数和环境;一旦存在路径、参数、内存或配置漏洞,就可能变成任意 root 代码执行。

查找 setuid 文件:

sudo find / -xdev -type f -perm -4000 -ls 2>/dev/null

-xdev 限制在当前文件系统,避免跨入其他挂载点;这不是完整资产清单,容器、网络文件系统和 capability 文件仍需单独检查。

capabilities

Linux capabilities 将部分 root 能力拆开,例如:

  • CAP_NET_BIND_SERVICE:绑定低端口;
  • CAP_NET_ADMIN:部分网络管理操作;
  • CAP_SYS_ADMIN:范围极其广,常被称为“杂项超级能力”,不应轻视。

查看文件和进程能力:

getcap -r /usr/bin /usr/sbin 2>/dev/null
grep '^Cap' /proc/$$/status

文件 capability 可能让一个非 root 二进制执行敏感操作;因此审计提权面不能只查 setuid。CAP_SYS_ADMINCAP_SYS_PTRACECAP_DAC_OVERRIDE 等能力尤其需要结合实际服务边界评估。


七、SSH 登录与本地认证的关系

OpenSSH 服务端通常由 sshd 接收连接,并按照 sshd_config 允许的认证方法进行协商。常见方式包括:

  • 公钥认证;
  • 密码认证;
  • keyboard-interactive,常被 PAM 用于 OTP 或其他交互式认证;
  • GSSAPI/Kerberos;
  • 基于证书的公钥认证。

检查实际生效配置:

sudo sshd -T
sudo sshd -T -C user=alice,addr=203.0.113.10,host=server.example

配置文件中常见:

PubkeyAuthentication yes
PasswordAuthentication no
KbdInteractiveAuthentication yes
UsePAM yes
PermitRootLogin no

这些选项的实际支持情况受 OpenSSH 版本和发行版补丁影响。修改后先检查语法:

sudo sshd -t

成功通常没有输出;有错误会显示文件和行号。确认新连接能够使用后,再重载服务:

sudo systemctl reload sshd   # 某些 Debian 系统服务名为 ssh

PasswordAuthentication no 只关闭 SSH 的 password 方法,不等于关闭所有基于 PAM 的认证;keyboard-interactive 仍可能触发 PAM。另一方面,锁定本地密码也不一定阻止 SSH 公钥登录。

公钥登录常见文件是:

~/.ssh/authorized_keys

其权限和所有权应严格检查:

chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
chown -R alice:alice /home/alice/.ssh

如果启用了 StrictModes,家目录、.ssh 目录或 authorized_keys 可被不可信用户写入,sshd 可能拒绝使用它。审计 SSH 登录时,结合以下信息:

last -a
sudo journalctl -u sshd --since today
sudo ausearch -m USER_LOGIN,USER_AUTH -ts today

日志字段可能包含远端地址、认证方法和结果,但不能把日志中的用户名直接视为已完成授权;还要结合会话建立和后续 sudo 记录。


八、审计:记录“谁做了什么”,而不是只记录“登录成功”

sudo 日志

常见查询:

sudo journalctl _COMM=sudo --since today
sudo grep -i sudo /var/log/auth.log

可能记录:

alice : TTY=pts/2 ; PWD=/home/alice ; USER=root ; COMMAND=/usr/bin/systemctl reload nginx

这能回答“sudo 试图以谁执行什么命令”,但不一定记录命令产生的全部输出。若启用 I/O logging,需要配置安全的集中存储、访问控制和轮转。I/O 日志可能包含密码、令牌或业务数据,不能因为它是“审计数据”就无限期保存。

Linux Audit

auditd 可以在内核审计层记录登录、身份变化、执行和策略事件。示例查询:

sudo ausearch -m USER_LOGIN -ts today
sudo ausearch -m EXECVE -x /usr/bin/sudo -ts today
sudo aureport -au

不同发行版的规则加载方式不同,常见目录包括:

/etc/audit/rules.d/

规则应以发行版推荐方式加载,而不是直接覆盖运行中的规则。审计规则过于宽泛会造成高事件量、磁盘增长和性能压力;过于狭窄则无法还原攻击链。生产审计至少应覆盖:

  • 身份认证成功与失败;
  • sudo 授权和命令执行;
  • UID/EUID 变化;
  • 关键账号数据库修改;
  • SSH 配置和 authorized_keys 修改;
  • 关键服务、systemd unit 和计划任务变化;
  • 审计服务自身被停止或规则被修改。

审计记录的完整性还取决于日志主机权限。如果攻击者取得 root,通常也可能篡改本地日志。因此高价值环境需要远程转发、集中存储、时间同步、访问隔离和告警规则;本地日志适合实时排障,但不能单独作为不可抵赖证据。

一次提权事件的关联方法

假设发现某服务配置被修改,应按时间线关联:

SSH 登录
  -> PAM 认证与 session_open
  -> sudo 授权
  -> 目标命令 execve
  -> 文件修改
  -> sudo session_close / audit 事件

实际调查时使用:

sudo journalctl --since '2025-03-08 10:00' --until '2025-03-08 10:30'
sudo ausearch -ts 10:00:00 -te 10:30:00 -m USER_LOGIN,USER_AUTH,EXECVE,PATH
last -Fai

时间同步非常关键。没有可靠的 NTP/chrony,同一事件在 SSH、sudo、audit 和应用日志中的时间可能无法正确排序。


九、常见失败表现与诊断路径

1. “密码正确,但 SSH 仍然拒绝”

依次检查:

getent passwd alice
sudo passwd -S alice
sudo chage -l alice
sudo sshd -T | grep -Ei 'passwordauthentication|kbdinteractiveauthentication|usepam|allowusers|denyusers'
sudo journalctl -u sshd -b

可能原因包括:

  • 账号不存在于实际 NSS 源;
  • 账号过期或 shell 为 nologin
  • AllowUsersAllowGroupsDenyUsers 规则拒绝;
  • PasswordAuthentication 被关闭;
  • PAM account 模块拒绝;
  • 失败锁定;
  • 家目录或 authorized_keys 权限问题(公钥认证时);
  • SELinux 或文件系统策略阻止访问。

不要通过关闭 SELinux、放宽目录权限来“验证是否是权限问题”;应先查看对应审计拒绝记录。

2. “sudo 说用户不在 sudoers”

执行:

sudo -l -U alice
getent group sudo
getent group wheel

Debian/Ubuntu 常见管理组是 sudo,RHEL/Fedora 常见是 wheel,但实际环境可能被定制。用户刚加入组后,已有登录会话中的附加组列表通常不会自动更新,需要重新登录;不能简单认为 id 的旧输出代表系统配置未生效。

3. “sudo 允许了脚本,但脚本仍然能被利用”

检查完整依赖链:

sudo namei -l /usr/local/sbin/rotate-app-logs
sudo find /usr/local/sbin -maxdepth 1 -type f -writable -ls

重点审查:

  • 脚本解释器是否可被替换;
  • 工作目录是否可写;
  • 配置文件是否可写;
  • 外部命令是否使用绝对路径;
  • 参数是否经过严格解析;
  • 是否调用编辑器、分页器或 shell;
  • 运行用户是否能修改脚本使用的插件、模块或 socket。

sudoers 只约束入口命令;如果入口命令能间接执行任意代码,形式上的命令限制就失去意义。

4. “锁了账号,但用户仍能访问”

检查所有身份入口:

sudo passwd -S alice
sudo find /home/alice/.ssh -type f -maxdepth 2 -ls 2>/dev/null
ps -u alice -f
sudo crontab -u alice -l
sudo systemctl --user -M alice@ list-units 2>/dev/null

还要检查服务账号使用的 API token、云凭据、容器内账号和共享密钥。账号锁定只影响特定认证路径,不会撤销已经签发的凭据或杀死现有进程。


十、生产环境中的配置、验证与恢复

变更前保留回退路径

修改 SSH、PAM、sudoers 前:

  1. 保留一个已验证可用的 root 控制台或独立管理会话;
  2. 备份目标配置并记录校验值;
  3. 使用语法检查工具;
  4. 先建立新会话验证,再关闭旧会话;
  5. 准备恢复命令或带外控制台。

例如 sudoers:

sudo cp -a /etc/sudoers /root/sudoers.$(date +%F-%H%M%S)
sudo visudo -c

SSH:

sudo cp -a /etc/ssh/sshd_config /root/sshd_config.$(date +%F-%H%M%S)
sudo sshd -t

PAM 没有一个能保证所有发行版配置都正确的通用“检查命令”。恢复 PAM 的可靠方式通常是使用带外控制台、单用户/救援模式或发行版安装介质,恢复已知正确的配置,并检查相关包提供的默认文件。

授权验证必须测试正面和负面路径

假设希望 webops 只能重载 Nginx,应测试:

sudo -u alice sudo -n /usr/bin/systemctl reload nginx
sudo -u alice sudo -n /usr/bin/systemctl restart nginx
sudo -u alice sudo -n /usr/bin/systemctl reload sshd

预期是第一条按策略成功,后两条失败。若第一条失败,检查:

  • 命令路径是否与 sudoers 完全一致;
  • 用户是否已重新登录取得新组;
  • sudoers 是否有更早或更宽泛的冲突规则;
  • systemd 本身是否允许该操作;
  • 目标服务是否存在及是否运行。

验证“失败路径”很重要。只测试允许命令,无法发现意外的通配符、别名展开或组继承。

发行版差异不能靠猜

以下内容经常不同:

  • 管理组名称:sudowheel
  • SSH 服务名:sshsshd
  • 认证日志:/var/log/auth.log/var/log/secure 或仅 journal;
  • PAM 聚合文件:common-*system-authpassword-auth
  • 失败锁定模块和默认目录;
  • 密码哈希默认算法;
  • SELinux、AppArmor 及其默认启用状态。

因此应以以下命令确认实际状态,而不是根据发行版名称推断:

cat /etc/os-release
systemctl status ssh sshd 2>/dev/null
sudo sshd -T
sudo -V

十一、一个完整的安全判断框架

对于“用户 Alice 能否以 root 重载 Nginx”这个问题,不能只问“她有没有 sudo 权限”。完整条件可写成:

Allow=NSS resolves AlicePAM authentication succeeds, if requiredPAM account checks succeedsudoers matches user, host, target, command, argsexecution environment is acceptablekernel DAC/LSM/capability checks allow the operation\begin{aligned} \text{Allow} ={}& \text{NSS resolves Alice} \\ &\land \text{PAM authentication succeeds, if required}\\ &\land \text{PAM account checks succeed}\\ &\land \text{sudoers matches user, host, target, command, args}\\ &\land \text{execution environment is acceptable}\\ &\land \text{kernel DAC/LSM/capability checks allow the operation} \end{aligned}

逐步代入:

  1. getent passwd alice 找到 UID 和 shell;
  2. SSH 或 sudo 通过相应 PAM service 完成认证;
  3. pam_unixpam_faillock、账号过期模块等返回允许;
  4. sudoers 精确匹配 /usr/bin/systemctl reload nginx
  5. sudo 固定 PATH、处理环境并建立目标身份;
  6. systemd、SELinux 和内核权限允许 systemctl 与服务管理;
  7. sudo、PAM、audit 和 journal 产生可关联记录。

任何一步失败,最终结果都可能是“拒绝”;但错误表现和日志位置不同。理解这条链路,才能把“密码问题”“PAM 问题”“sudo 规则问题”和“内核权限问题”分开诊断,而不是反复修改同一个配置文件。

Linux 用户认证与提权的安全边界,最终不在某一个开关,而在于:身份来源可验证、认证路径可解释、授权规则足够窄、提权程序依赖链可信、日志能够关联并且不只保存在本机。


系列导航与关联阅读

官方资料

本文依据 Linux 内核、systemd 与主流发行版官方文档重新梳理;正文与实验由 WR BLOG 编写。