shadow-utils 完全指南:Linux 用户账户管理的基石
引言
在 Linux 系统的日常运维中,我们频繁地使用 useradd 添加用户、passwd 设置密码、chage 管理密码过期策略。这些看似简单的命令背后,都依赖同一个基础软件包——shadow-utils。它是几乎所有 Linux 发行版中用于管理用户账户、用户组和密码的底层核心工具集。
本文将从概念、历史演进、核心命令、配置文件体系、数据文件格式、内部架构原理到现代应用场景,全面而深入地介绍这个系统不可或缺的核心组件。
一、历史演进 —— 从”公开密码”到”影子密码”
1.1 早期 Unix 时代:单一 passwd 文件
在早期 Unix 系统中,所有用户信息都存储在 /etc/passwd 文件中,格式如下:
username:encrypted_password:UID:GID:GECOS:home_directory:shell
这个文件存在一个根本性的安全矛盾:
- 许多程序(如
ls、ps)需要读取/etc/passwd来将 UID 转换为用户名 - 因此该文件必须保持全局可读(权限 644)
- 但密码哈希也因此暴露给了所有用户
任何普通用户都可以读取并获取密码哈希,进行离线暴力破解。
1.2 影子密码革命
shadow-utils 的出现彻底解决了这个问题。它将加密密码从 /etc/passwd 中移出,存入一个新的、只有 root 可读的文件 —— /etc/shadow(权限 000 或 600)。
/etc/passwd的密码字段统一替换为x或*占位符/etc/shadow存储真正的密码哈希和相关的时效信息- 这一创新被称为 影子密码(Shadow Password)机制
shadow-utils 也因此得名 —— 它是管理这套”影子”机制的工具集。
二、核心工具集 —— 你每天都在用的命令
shadow-utils 提供了一套完整的命令行工具,覆盖用户、组和密码管理的方方面面。
2.1 用户管理命令
| 命令 | 功能 | 操作的文件 | 典型示例 |
|---|---|---|---|
useradd | 添加新用户 | passwd, shadow, group | useradd -m -s /bin/bash alice |
usermod | 修改用户属性 | passwd, shadow | usermod -aG sudo alice |
userdel | 删除用户 | passwd, shadow, group | userdel -r alice |
2.2 用户组管理命令
| 命令 | 功能 | 操作的文件 | 典型示例 |
|---|---|---|---|
groupadd | 添加新用户组 | group, gshadow | groupadd developers |
groupmod | 修改组名或 GID | group, gshadow | groupmod -n dev developers |
groupdel | 删除用户组 | group, gshadow | groupdel dev |
2.3 密码与安全管理命令
| 命令 | 功能 | 说明 |
|---|---|---|
passwd | 设置或修改用户密码 | 验证密码强度后更新 shadow 文件 |
chage | 管理密码过期策略 | 设置有效期、警告期、非活动期 |
chfn | 修改用户真实姓名等信息 | 更新 passwd 中的 GECOS 字段 |
chsh | 修改用户的登录 Shell | 更新 passwd 中的 shell 字段 |
2.4 批量与辅助命令
| 命令 | 功能 | 典型场景 |
|---|---|---|
pwconv / pwunconv | 传统密码与影子密码格式互转 | 系统迁移或恢复 |
grpconv / grpunconv | 传统组密码与影子组密码互转 | 系统迁移或恢复 |
newusers | 批量创建/更新用户 | 从批量导入用户数据 |
chpasswd | 批量更新用户密码 | 批量重置密码 |
三、配置文件体系 —— 三层配置架构
shadow-utils 的配置并非单一文件,而是一套完整的分层配置体系。
3.1 三层配置架构概览
| 配置文件 | 作用范围 | 主要职责 |
|---|---|---|
/etc/login.defs | 全局默认值 | 所有 shadow 工具的默认行为 |
/etc/default/useradd | useradd 专用 | useradd 命令的个性化默认值 |
/etc/pam.d/ 目录 | PAM 认证模块 | 密码复杂度、登录限制等认证策略 |
3.2 /etc/login.defs —— 主配置文件
这是整个 shadow-utils 的核心配置文件,几乎所有命令(useradd、usermod、userdel、passwd 的底层逻辑)都会读取它。
(1)UID/GID 范围控制
# 普通用户 ID 范围
UID_MIN 1000
UID_MAX 60000
# 系统用户 ID 范围(用于守护进程等)
SYS_UID_MIN 201
SYS_UID_MAX 999
# 普通组 ID 范围
GID_MIN 1000
GID_MAX 60000
# 系统组 ID 范围
SYS_GID_MIN 201
SYS_GID_MAX 999
当执行 useradd 而不指定 -u 参数时,系统会自动从 UID_MIN 到 UID_MAX 范围内分配一个未使用的 UID。
(2)密码时效策略
# 密码最长使用天数(99999 ≈ 273 年,表示永不过期)
PASS_MAX_DAYS 99999
# 两次修改密码之间的最短天数(0 表示无限制)
PASS_MIN_DAYS 0
# 密码过期前多少天开始警告用户
PASS_WARN_AGE 7
(3)密码加密算法
# 支持的算法:DES, MD5, SHA256, SHA512, BCRYPT, YESCRYPT
ENCRYPT_METHOD SHA512
# SHA 算法的加密轮数(越大越安全,但也越消耗 CPU)
SHA_CRYPT_MAX_ROUNDS 5000
# yescrypt 算法的成本因子(范围 1~11)
YESCRYPT_COST_FACTOR 5
算法对比:
| 算法 | 安全性 | 现状 | 标志符 |
|---|---|---|---|
| DES | 极弱 | 已淘汰 | $1$ |
| MD5 | 弱 | 不推荐 | $1$ |
| SHA256 | 中等 | 较少用 | $5$ |
| SHA512 | 较强 | 主流默认 | $6$ |
| YESCRYPT | 强 | 新兴发行版默认 | $y$ |
(4)家目录与权限
# 是否自动创建家目录(useradd 的默认行为)
CREATE_HOME yes
# 新家目录的权限模式(0700 = 仅所有者可访问)
HOME_MODE 0700
# 默认 umask 值(022 = 新文件 644,新目录 755)
UMASK 022
(5)用户删除行为
# 删除用户时是否同时删除其专属组(若组内无其他成员)
USERGROUPS_ENAB yes
# 用户删除后执行的自定义清理脚本(如清理 cron 任务)
#USERDEL_CMD /usr/sbin/userdel_local
(6)子 ID 映射(用于 rootless 容器)
# 为每个用户分配的额外 UID 范围(用于容器映射)
SUB_UID_MIN 100000 # 起始值
SUB_UID_MAX 600100000 # 最大值
SUB_UID_COUNT 65536 # 每个用户分配的数量
SUB_GID_MIN 100000
SUB_GID_MAX 600100000
SUB_GID_COUNT 65536
这些配置使得 /etc/subuid 和 /etc/subgid 能够自动为新建用户分配 UID 范围,这是 rootless 容器(如 Podman 无根模式)得以运行的关键基础。
3.3 /etc/default/useradd —— useradd 专用配置
如果说 /etc/login.defs 是全局的”宪法”,那么 /etc/default/useradd 就是专门为 useradd 命令准备的”实施细则”。
# 新用户的默认主组(当 USERGROUPS_ENAB 为 no 时使用)
GROUP 100
# 家目录的基路径
HOME /home
# 密码过期后的非活动期(-1 表示永不禁用)
INACTIVE -1
# 账户过期的绝对日期(空表示不设置)
EXPIRE
# 新用户的默认登录 Shell
SHELL /bin/bash
# 家目录的骨架模板目录(新家目录会复制此目录的内容)
SKEL /etc/skel
# 是否创建邮箱文件(位于 MAIL_DIR 目录下)
CREATE_MAIL_SPOOL yes
3.4 PAM 配置文件 —— 认证策略的真正执行者
当系统启用了 PAM(可插拔认证模块)时,密码策略的实际执行者是 PAM,而非 shadow-utils 本身。
核心区别
| 对比项 | /etc/login.defs | /etc/pam.d/ |
|---|---|---|
| 控制对象 | shadow-utils 工具本身 | 所有使用 PAM 认证的程序 |
| 密码复杂度 | 不支持(PASS_MIN_LEN 已废弃) | 通过 pam_pwquality.so 配置 |
| 登录失败限制 | 不支持 | 通过 pam_faillock.so 配置 |
| 密码加密 | 支持(ENCRYPT_METHOD) | 支持(pam_unix.so 的参数) |
典型 PAM 配置示例(/etc/pam.d/passwd 或 /etc/pam.d/system-auth)
# 密码复杂度检查:最小长度12,至少3个字符与旧密码不同,重试3次
password required pam_pwquality.so retry=3 minlen=12 difok=3
# Unix 认证:使用 SHA512 加密,更新 shadow 文件
password required pam_unix.so sha512 shadow
常见误区
很多管理员会尝试修改 /etc/login.defs 中的 PASS_MIN_LEN 来强制密码最小长度,但这个配置项在 PAM 环境下已经失效。正确的做法是在 PAM 配置中设置 pam_pwquality.so 的 minlen 参数。
四、核心数据文件 —— 用户信息的存储基石
4.1 /etc/passwd —— 用户账户信息文件
文件格式:每行代表一个用户,由 7 个冒号分隔的字段组成。
用户名:密码占位符:UID:GID:GECOS:家目录:登录Shell
字段详解:
| 字段 | 名称 | 说明 |
|---|---|---|
| 1 | 用户名 | 登录时使用的名称,唯一标识 |
| 2 | 密码占位符 | 通常为 x 或 *,表示密码已迁移到 shadow 文件 |
| 3 | UID | 用户 ID(User ID),系统使用数字标识用户 |
| 4 | GID | 主组 ID(Group ID),用户登录时默认所属的组 |
| 5 | GECOS | 用户信息字段,通常存储真实姓名、电话等,由 chfn 修改 |
| 6 | 家目录 | 用户登录后的初始目录,通常为 /home/用户名 |
| 7 | 登录 Shell | 用户登录后执行的程序,通常为 /bin/bash |
权限:-rw-r--r-- (644),所有人都可读,但只有 root 可写。
示例:
root:x:0:0:root:/root:/bin/bash
alice:x:1000:1000:Alice Johnson:/home/alice:/bin/bash
4.2 /etc/shadow —— 影子密码文件(核心安全文件)
这是 shadow-utils 守护的核心文件,存储真正的密码哈希和时效信息。
文件格式:每行代表一个用户,由 9 个冒号分隔的字段组成。
用户名:加密密码:上次修改:最小间隔:最大期限:警告期:非活动期:过期时间:保留
字段详解:
| 字段 | 名称 | 说明 |
|---|---|---|
| 1 | 用户名 | 与 /etc/passwd 中的用户名一致 |
| 2 | 加密密码 | $算法ID$盐值$哈希值 格式;* 或 ! 表示账户被锁定 |
| 3 | 上次修改时间 | 从 1970-01-01 到上次密码修改的天数(Unix 时间戳的天数) |
| 4 | 最小修改间隔 | 两次密码修改之间必须经过的最少天数(0 表示无限制) |
| 5 | 最大使用期限 | 密码有效的最长天数,过期后必须修改 |
| 6 | 警告期 | 密码过期前多少天开始警告用户 |
| 7 | 非活动期 | 密码过期后多少天账户被锁定 |
| 8 | 过期时间 | 账户被禁用的绝对日期(天数格式) |
| 9 | 保留字段 | 预留未使用 |
权限:-rw------- (600) 或 ---------- (000),只有 root 可读写。
密码加密格式:$id$salt$hashed
| ID | 算法 | 示例哈希前缀 |
|---|---|---|
| 1 | MD5 | $1$salt$hashed |
| 2a | Blowfish | $2a$08$salt$hashed |
| 5 | SHA-256 | $5$rounds=5000$salt$hashed |
| 6 | SHA-512 | $6$rounds=5000$salt$hashed |
| y | yescrypt | $y$j9T$salt$hashed |
4.3 /etc/group 和 /etc/gshadow
与用户管理类似:
| 文件 | 格式字段 | 说明 |
|---|---|---|
/etc/group | 组名:密码占位符:GID:成员列表 | 组信息,所有人可读 |
/etc/gshadow | 组名:加密密码:管理员:成员列表 | 组的影子密码,仅 root 可读 |
五、内部架构原理
5.1 commonio 子系统 —— 统一的数据库接口
shadow-utils 的核心是 commonio(Common I/O)子系统,它为所有账户数据库文件(passwd、shadow、group、gshadow)提供了统一的访问接口。
struct commonio_ops {
void *(*dup)(const void *); // 复制条目
void (*free)(void *); // 释放条目
const char *(*getname)(const void *); // 获取条目名称
void *(*parse)(const char *); // 从字符串解析条目
int (*put)(const void *, FILE *); // 将条目写入文件
char *(*fgets)(char *, int, FILE *); // 从文件读取行
int (*fputs)(const char *, FILE *); // 将行写入文件
int (*open_hook)(void); // 打开文件后的回调
int (*close_hook)(void); // 关闭文件前的回调
};
每个数据库类型都实现这个接口:
| 数据库 | 文件路径 | 操作模块 | 锁定文件 |
|---|---|---|---|
| 密码数据库 | /etc/passwd | pwio | /etc/passwd.lock |
| 影子密码库 | /etc/shadow | shadowio | /etc/shadow.lock |
| 组数据库 | /etc/group | groupio | /etc/group.lock |
| 影子组库 | /etc/gshadow | sgroupio | /etc/gshadow.lock |
5.2 事务性操作与并发控制
为了保证数据一致性,shadow-utils 实现了完整的文件锁机制:
- 打开与锁定:
commonio_open()读取数据库到内存,commonio_lock()创建.lock文件获得排他锁 - 定位与修改:
commonio_locate()查找条目,commonio_update()或commonio_remove()修改数据 - 关闭与解锁:
commonio_close()将更改写回磁盘,commonio_unlock()释放锁
锁文件中会写入持有锁的进程 PID,用于检测并清理进程崩溃后残留的”僵尸锁”。
5.3 缓存同步机制
修改密码文件后,NSCD(名称服务缓存守护进程)和 SSSD(系统安全服务守护进程)可能仍持有旧数据的缓存。shadow-utils 会在释放锁时自动向这些服务发送刷新通知,确保更改立即生效。
5.4 密码加密流程
用户设置密码时,passwd 命令调用 pw_encrypt() 函数,流程如下:
- 从
/etc/login.defs读取ENCRYPT_METHOD和加密轮数配置 - 生成随机盐值(通常是 16 字节的随机数据)
- 使用指定算法对”盐值 + 密码”进行哈希计算
- 将结果格式化为
$id$salt$hash格式 - 更新
/etc/shadow中的密码字段和时效字段
六、PAM 集成 —— 可插拔的认证框架
在现代 Linux 系统中,shadow-utils 通常与 PAM(Pluggable Authentication Modules) 深度集成。
6.1 各命令使用的 PAM 服务名
| 工具 | PAM 服务名 | 典型用途 |
|---|---|---|
login | login | 控制台登录认证 |
su | su | 切换用户身份 |
passwd | passwd | 修改密码策略(复杂度检查) |
chsh | chsh | 修改 Shell 的权限控制 |
chfn | chfn | 修改用户信息的权限控制 |
6.2 PAM 认证流程
当使用 PAM 时,典型的认证流程如下:
- 初始化:
pam_start(service_name, user, conv, &pamh)创建 PAM 上下文 - 认证:
pam_authenticate(pamh, 0)验证用户凭证(密码) - 账户管理:
pam_acct_mgmt(pamh, 0)检查账户是否过期、是否允许登录 - 凭证设置:
pam_setcred(pamh, 0)建立用户凭证 - 会话管理:
pam_open_session(pamh, 0)初始化用户会话(如记录登录日志) - 清理:
pam_close_session(pamh, 0)和pam_end(pamh, PAM_SUCCESS)关闭会话
6.3 与传统认证的区别
| 对比项 | 传统方式 | PAM 方式 |
|---|---|---|
| 密码验证 | 直接读取 /etc/shadow 并调用 pw_encrypt() 比较 | 交给 PAM 模块处理 |
| 账户状态检查 | 工具自身检查密码过期等 | PAM 的 pam_acct_mgmt() 处理 |
| 配置位置 | /etc/login.defs | /etc/pam.d/ 目录下的配置文件 |
| 扩展性 | 需修改 shadow-utils 源码 | 添加/修改 PAM 模块即可 |
七、现代应用 —— rootless 容器
随着容器技术的普及,shadow-utils 找到了新的应用场景。rootless 容器(如 Podman、Docker 的无根模式)依赖于 shadow-utils 提供的两个关键工具:newuidmap 和 newgidmap。
7.1 工作原理
这两个程序被赋予了特殊的能力:
$ getcap /usr/bin/newuidmap
/usr/bin/newuidmap = cap_setuid+ep
$ getcap /usr/bin/newgidmap
/usr/bin/newgidmap = cap_setgid+ep
+ep 表示这些程序拥有 CAP_SETUID 和 CAP_SETGID 能力,允许非 root 用户在自己的用户命名空间中建立 UID/GID 映射。
7.2 映射示例
当非特权用户运行 podman run 时:
$ cat /etc/subuid
dwalsh:100000:65536
$ podman run alpine cat /proc/self/uid_map
0 3267 1
1 100000 65536
这个映射表示:
- 容器内的 UID 0(root)映射到宿主机的 UID 3267(用户 dwalsh)
- 容器内的 UID 1 映射到宿主机的 UID 100000
- 容器内的 UID 2 映射到宿主机的 UID 100001
- … 共 65536 个映射
这使得普通用户可以在容器内”变成 root”,但实际权限仍被限制在宿主机的用户命名空间内,不会影响系统的其他部分。
7.3 涉及的配置
/etc/subuid:为用户分配子 UID 范围/etc/subgid:为用户分配子 GID 范围newuidmap/newgidmap:建立实际映射
八、如何获取与安装
shadow-utils 是如此基础,以至于它会被默认包含在几乎所有的 Linux 发行版中。如果你发现它意外缺失,可以使用对应的包管理器轻松安装:
| 发行版系列 | 安装命令 |
|---|---|
| Debian / Ubuntu | sudo apt update && sudo apt install shadow-utils |
| CentOS / RHEL / Fedora | sudo yum install shadow-utils 或 sudo dnf install shadow-utils |
| Arch Linux | sudo pacman -S shadow |
源码编译(如需自定义编译选项):
git clone https://github.com/shadow-maint/shadow.git
cd shadow
./autogen.sh
./configure --with-libpam --enable-shared
make && sudo make install
总结
shadow-utils 是 Linux 系统账户管理的基石,其核心价值可以概括为以下五点:
1. 安全性
通过影子密码机制将加密密码与用户信息分离,从根本上解决了 /etc/passwd 可读带来的密码暴露风险。
2. 完整性
提供从用户管理(useradd、usermod、userdel)、组管理(groupadd、groupmod、groupdel)到密码策略(passwd、chage)的完整工具链。
3. 可配置性
通过 /etc/login.defs(全局规则)、/etc/default/useradd(命令个性配置)、PAM(认证策略)等多层配置体系,灵活适应不同场景。
4. 可扩展性
- commonio 子系统:为所有账户数据库提供统一的访问接口和事务性操作
- PAM 集成:无需修改 shadow-utils 代码即可灵活调整认证策略
5. 前瞻性
通过子 UID/子 GID 映射机制(/etc/subuid、newuidmap),为 rootless 容器等现代应用提供关键支持。
一句话概括:
shadow-utils是 Linux 账户管理的”三合一”:提供管理命令(useradd/passwd)、定义全局规则(/etc/login.defs)、守护影子密码(/etc/shadow),并通过 PAM 和 commonio 实现可插拔认证与数据一致性。
理解 shadow-utils,不仅有助于正确配置用户和密码策略、避免”配置不生效”的困惑,更是深入理解 Linux 系统安全与管理模型的重要一步。