跳至正文
老丹的足迹 —— 代码写给机器,游记写给自己,感悟写给时间
老丹的足迹 老丹的足迹
老丹的足迹 老丹的足迹
  • 首页
  • 示例页面
  • 首页
  • 示例页面
老丹的足迹 老丹的足迹
老丹的足迹 老丹的足迹
  • 首页
  • 示例页面
  • 首页
  • 示例页面

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, groupuseradd -m -s /bin/bash alice
usermod修改用户属性passwd, shadowusermod -aG sudo alice
userdel删除用户passwd, shadow, groupuserdel -r alice

2.2 用户组管理命令

命令功能操作的文件典型示例
groupadd添加新用户组group, gshadowgroupadd developers
groupmod修改组名或 GIDgroup, gshadowgroupmod -n dev developers
groupdel删除用户组group, gshadowgroupdel 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/useradduseradd 专用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 文件
3UID用户 ID(User ID),系统使用数字标识用户
4GID主组 ID(Group ID),用户登录时默认所属的组
5GECOS用户信息字段,通常存储真实姓名、电话等,由 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算法示例哈希前缀
1MD5$1$salt$hashed
2aBlowfish$2a$08$salt$hashed
5SHA-256$5$rounds=5000$salt$hashed
6SHA-512$6$rounds=5000$salt$hashed
yyescrypt$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/passwdpwio/etc/passwd.lock
影子密码库/etc/shadowshadowio/etc/shadow.lock
组数据库/etc/groupgroupio/etc/group.lock
影子组库/etc/gshadowsgroupio/etc/gshadow.lock

5.2 事务性操作与并发控制

为了保证数据一致性,shadow-utils 实现了完整的文件锁机制:

  1. 打开与锁定:commonio_open() 读取数据库到内存,commonio_lock() 创建 .lock 文件获得排他锁
  2. 定位与修改:commonio_locate() 查找条目,commonio_update() 或 commonio_remove() 修改数据
  3. 关闭与解锁:commonio_close() 将更改写回磁盘,commonio_unlock() 释放锁

锁文件中会写入持有锁的进程 PID,用于检测并清理进程崩溃后残留的”僵尸锁”。

5.3 缓存同步机制

修改密码文件后,NSCD(名称服务缓存守护进程)和 SSSD(系统安全服务守护进程)可能仍持有旧数据的缓存。shadow-utils 会在释放锁时自动向这些服务发送刷新通知,确保更改立即生效。

5.4 密码加密流程

用户设置密码时,passwd 命令调用 pw_encrypt() 函数,流程如下:

  1. 从 /etc/login.defs 读取 ENCRYPT_METHOD 和加密轮数配置
  2. 生成随机盐值(通常是 16 字节的随机数据)
  3. 使用指定算法对”盐值 + 密码”进行哈希计算
  4. 将结果格式化为 $id$salt$hash 格式
  5. 更新 /etc/shadow 中的密码字段和时效字段

六、PAM 集成 —— 可插拔的认证框架

在现代 Linux 系统中,shadow-utils 通常与 PAM(Pluggable Authentication Modules) 深度集成。

6.1 各命令使用的 PAM 服务名

工具PAM 服务名典型用途
loginlogin控制台登录认证
susu切换用户身份
passwdpasswd修改密码策略(复杂度检查)
chshchsh修改 Shell 的权限控制
chfnchfn修改用户信息的权限控制

6.2 PAM 认证流程

当使用 PAM 时,典型的认证流程如下:

  1. 初始化:pam_start(service_name, user, conv, &pamh) 创建 PAM 上下文
  2. 认证:pam_authenticate(pamh, 0) 验证用户凭证(密码)
  3. 账户管理:pam_acct_mgmt(pamh, 0) 检查账户是否过期、是否允许登录
  4. 凭证设置:pam_setcred(pamh, 0) 建立用户凭证
  5. 会话管理:pam_open_session(pamh, 0) 初始化用户会话(如记录登录日志)
  6. 清理: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 / Ubuntusudo apt update && sudo apt install shadow-utils
CentOS / RHEL / Fedorasudo yum install shadow-utils 或 sudo dnf install shadow-utils
Arch Linuxsudo 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 系统安全与管理模型的重要一步。

作者

老丹

关注我
其他文章
上一个

指尖下的基础:GNU Coreutils 完全解析

下一个

Linux 用户管理:adduser 与 addgroup 完全指南

关于博主

    老丹是一名C/C++后台开发工程师,信奉“无抽象不设计,无性能不生产”。

  • 技术栈:Modern C++、Linux环境编程、多线程/并发、网络编程等。
  • 信条:能用constexpr解决的问题绝不拖到运行时,能靠RAII避免的泄漏绝不写析构。
  • 正在填坑:从解封装到渲染的C++全链路实现,正在驯服FFmpeg与H.264/H.265。
  • 输出原则:这里的每一段代码都经过-Wall -Wextra -Werror -O2的洗礼。

搜索

近期文章

  • Bash命令行参数完全指南 2026年6月17日
  • Bash 数组完全指南:从设计思想到实战应用 2026年6月16日
  • Bash 变量内容操作完全指南 2026年6月16日
  • usbutils:Linux下USB设备查看与调试的完整指南 2026年6月16日
  • MQTT协议完全指南:从核心概念到实践应用 2026年6月16日
  • ICO文件格式完全解析 2026年6月15日

文章分类

  • C/C++开发 (10)
  • Linux工具包 (6)
  • Linux服务配置 (7)
  • Shell脚本 (3)
  • 计算机理论 (9)
联系我们:📍 地址:中国·广东省深圳市   |   ✉️ 邮箱:support@tanglinux.com   |   💬 QQ:870866607
版权所有:老丹的足迹粤ICP备2026061170号-1       公安备案图标 粤公网安备44030002013274号