资讯动态

Linux用户管理与MD5校验:账户体系与文件完整性实践

发布时间:2026/10/24 2:39:36 来源:尧图企业网站定制
很多运维和后端同学第一次碰 Linux 用户管理都是从一条useradd开始的。命令敲下去用户建好了看似一切正常等到某天发现这个账号登不上去、sudo 不给权限、或者把某个用户从附加组里顺手踢掉了才回头去翻/etc/passwd和/etc/shadow一脸茫然。更麻烦的是改完这些系统文件之后很多人根本没有意识去确认我到底改了哪几行、改坏没有。这篇是 Linux 基础系列里比较硬的一节主题是两个看起来不搭界、实际上经常一起出现的东西用户管理和MD5 校验。前者是账户体系的骨架后者是改动前后的指纹比对手段。把它们放在一起讲不是为了凑章节而是因为真实运维场景里这两件事几乎是绑定的——批量建号、批量改密、批量分发配置文件之后你需要一个低成本、可复现的方式确认结果是否符合预期。先说结论Linux 用户管理的难点从来不在命令有多少个参数而在于你是否理解账户信息被拆成了哪几个文件、密码到底以什么形式存放、权限的最小边界在哪里MD5 校验的价值也不在加密或安全它只是一个摘要比对工具用来快速判断两份内容是否一致。把 MD5 当成密码保护手段是这一节里最容易踩、后果也最严重的坑。读完这篇你应该能做到几件事独立完成用户与用户组的增删改查并解释每个关键参数的作用看懂/etc/shadow里那串密码哈希的前缀含义用md5sum对关键配置文件做改动前后的完整性比对写出一个可复用的批量建号脚本并且清楚知道什么场景下必须放弃 MD5 改用更强的算法。1. 这篇文章真正要解决的问题先还原一个很典型的现场。假设你接手了一台应用服务器需要为三个新同事开账号同时要求他们只能访问/data/app目录、只能用密钥登录、并且 90 天必须改一次密码。这件事听起来是建三个用户实际涉及 UID 分配、主组与附加组的区别、家目录权限、shell 指定、密码老化策略、sudo 授权范围这几层内容。任何一层漏掉最后都会以某某登不上或某某权限过大的形式暴露出来。第二个现场更隐蔽。你在做配置变更改了/etc/ssh/sshd_config、/etc/sudoers、/etc/login.defs三个文件改完之后服务正常你以为万事大吉。两周后某个用户投诉登不上你回头排查发现自己根本不记得当时改了哪几行diff也没有基线可对比。这时候如果当初在改动前执行过一次md5sum情况会完全不同——至少你能立刻定位到是哪个文件被动了。所以在实际项目中用户管理解决的是谁能进来说了算的问题MD5 校验解决的是东西有没有被改的问题。前者是边界定义后者是变更证据。它们共同服务于同一个目标让系统状态可解释、可追溯。这也是为什么很多老运维会养成一个习惯——动/etc下的任何文件之前先算一遍摘要存起来。需要提前说明的是这篇文章更适合已经能在 Linux 上完成基本操作、但账户体系还没完全理顺的读者如果你是纯应用开发平时只接触容器里的只读文件系统那么第 2 章和第 4 章仍然值得看因为容器里的 UID 映射、Dockerfile 里的USER指令底层逻辑完全一致。2. Linux 用户与用户组的核心概念理解用户管理第一步是接受一个事实Linux 里的用户不是一个对象而是散落在多个文件里的一组记录。任何一个文件出问题都会导致账户行为异常。/etc/passwd是账户的名片全局可读存储的是用户名、UID、GID、描述、家目录、登录 shell 这七段信息。注意它不存密码早期确实存在第二字段里后来因为安全原因被迁走了。你在里面看到的x只是一个占位符。/etc/shadow才是密码真正待的地方默认权限通常是000或400只有 root 可读。它的字段比 passwd 多包括密码哈希、上次修改时间从 1970-01-01 起算的天数、最小间隔、最大有效期、警告天数、失效天数、账户过期时间、保留字段。很多人做密码策略配置时只改/etc/login.defs却不知道已经存在的用户不会自动继承新策略需要用chage单独调整。/etc/group存组信息和附加成员列表/etc/gshadow存组密码基本不用但存在。此外还有/etc/login.defs控制 UID 分配范围和默认密码老化策略/etc/default/useradd控制useradd的默认行为/etc/skel决定新用户家目录里会复制哪些初始文件。用户类型上UID 为 0 的是 rootUID 1 到 999不同发行版边界略有差异通常是系统用户给服务进程用一般不分配登录 shellUID 1000 起是普通用户。这个划分不是形式主义它直接决定了某些服务在启动时会不会因为UID 太小而拒绝运行。文件主要作用默认权限常见误操作/etc/passwd账户基本信息644手改字段错位导致登录失败/etc/shadow密码哈希与老化策略000/400直接编辑覆盖哈希用户被锁死/etc/group组与附加成员644误删组导致文件属组悬空/etc/login.defs默认 UID 范围与密码策略644改了但未对存量用户生效/etc/skel新用户家目录模板755模板里有敏感文件被批量复制再往下一层是认证链路。当你输入密码登录时真正做校验的不是login程序本身而是 PAM可插拔认证模块。/etc/pam.d/system-authRHEL 系或/etc/pam.d/common-passwordDebian 系里配置了密码复杂度、失败锁定、哈希算法等规则。这意味着你在/etc/shadow里看到的哈希形式是由 PAM 和 libxcrypt 的配置共同决定的不完全是passwd命令自己的选择。理解这一点后面看$1$、$6$这类前缀时才不会困惑。3. 环境准备与前置条件这一节的所有操作都建议在测试环境或本地虚拟机、容器中完成不要直接在生产服务器上边学边试。用户与权限的变更影响面很大一个错误的usermod -G就可能让某个服务账号失去必要的组权限进而导致服务启动失败。环境方面主流发行版都可以命令基本通用。RHEL/CentOS/Rocky 系和 Debian/Ubuntu 系在 PAM 配置文件路径、默认哈希算法、useradd默认行为上有差异文中会分别标注。具体版本请以你实际使用的环境为准本文重点演示通用思路不绑定某个特定版本号。需要的前置条件一个具备sudo权限或 root 权限的账号。本文所有示例默认以 root 身份执行如果是普通用户请在命令前加sudo。系统已安装shadow-utils提供useradd、usermod、userdel、chage、passwd和coreutils提供md5sum、sha256sum。这两个包在绝大多数发行版中是默认安装的。如果要演示密码哈希生成可能需要openssl命令行工具通常也已预装。建议先做一次快照或备份尤其是/etc目录。# 确认关键工具是否存在以及系统身份 whoami id which useradd usermod userdel chage passwd md5sum sha256sum openssl # 备份关键账户文件这是动手前的必要动作 mkdir -p /root/backup-$(date %F) cp -a /etc/passwd /etc/shadow /etc/group /etc/gshadow /root/backup-$(date %F)/ ls -l /root/backup-$(date %F)/这里有一个容易被忽略的细节备份/etc/shadow时务必用cp -a或cp -p保留权限否则备份文件的权限会变成默认的644相当于把全系统的密码哈希暴露给所有本地用户。顺手用ls -l确认一下权限是很值得养成的习惯。4. 用户管理核心操作全流程4.1 创建用户useradd 的关键参数useradd是最容易看起来会了、实际用错的命令。它的默认行为由/etc/default/useradd和/etc/login.defs共同决定在不同发行版上可能不一样比如是否自动创建家目录。所以更稳妥的做法是显式指定你需要的每一个参数而不是依赖默认值。# 创建一个完整配置的普通用户 # -m 创建家目录-s 指定登录 shell-u 指定 UID # -g 指定主组-G 指定附加组-c 添加注释-d 指定家目录路径 useradd -m -s /bin/bash -u 1500 -g developers -G wheel, docker \ -c Zhang San, backend team -d /home/zhangsan zhangsan # 设置初始密码交互式推荐避免密码进入命令历史 passwd zhangsan # 要求用户首次登录必须改密码 passwd -e zhangsan # 查看结果账户信息、UID/GID、组成员关系 id zhangsan getent passwd zhangsan getent group developers参数里最需要解释的是-g和-G的区别。-g指定的是主组每个文件在创建时默认继承这个组-G是附加组决定用户还能额外获得哪些组的权限。一个用户只有一个主组但可以有多个附加组。这个区别在多团队协作的服务器上至关重要——主组错了新建文件的属组就错了附加组漏了docker、wheel这类权限就全都拿不到。另一个高频坑是usermod -G。很多人的直觉是加到某个组里于是写成usermod -G docker zhangsan。这条命令的实际语义是把用户的附加组设置为只有 docker原来在wheel组里的身份会被直接抹掉。正确的写法必须带-a# 错误写法会把用户的附加组覆盖成只有 docker # usermod -G docker zhangsan # 正确写法追加到附加组保留原有组 usermod -aG docker zhangsan # 验证是否追加成功 id zhangsan4.2 修改与删除usermod、userdelusermod能改的东西很多包括用户名、UID、家目录、shell、注释、锁定状态。改用户名和改 UID 都属于高风险操作前者会影响家目录路径和部分应用配置后者会导致原本属于旧 UID 的文件变成无主文件。如果确实要改 UID需要配合find / -uid 旧UID -exec chown 新UID {} \;一起处理而且要先停掉该用户的所有进程。# 修改登录 shell 和注释 usermod -s /bin/zsh -c Zhang San, infra team zhangsan # 锁定账户在 shadow 密码字段前加 !不删除密码 usermod -L zhangsan # 解锁 usermod -U zhangsan # 修改家目录并迁移内容 usermod -d /data/home/zhangsan -m zhangsan # 删除用户-r 同时删除家目录和邮件队列 userdel -r zhangsanuserdel -r的-r参数必须慎用。它会连家目录一起删掉如果这个目录里恰好挂了数据盘或者有业务文件后果不可逆。更保守的做法是先userdel只删账户保留家目录确认没有需要保留的内容后再手动rm -rf。删除用户前务必先用ps -u 用户名或pgrep -u 用户名确认没有残留进程否则会出现用户删了但进程还在跑、文件属主显示为 UID 数字的尴尬状态。4.3 密码策略chage 与 login.defs密码老化策略分为两层/etc/login.defs是全局默认值只对之后新建的用户生效chage是针对单个存量用户的调整工具。很多人改完login.defs就以为全系统生效了实际上老账号完全没有变化。# 查看某个用户的密码策略详情 chage -l zhangsan # 设置最短间隔 1 天最长有效期 90 天提前 7 天警告 # 密码过期后 30 天内仍可登录但强制改密账户在指定日期后彻底失效 chage -m 1 -M 90 -W 7 -I 30 -E 2027-12-31 zhangsan # 强制下次登录改密 chage -d 0 zhangsan/etc/login.defs里值得关注的是PASS_MAX_DAYS、PASS_MIN_DAYS、PASS_WARN_AGE、UID_MIN、UID_MAX、CREATE_HOME这几项。修改前建议先备份并注意不同发行版对UID_MIN的默认值不一样随意调小可能和已有系统用户冲突。5. MD5 校验的原理与两类真实用途5.1 MD5 到底在做什么MD5 是一种消息摘要算法输入任意长度的数据输出固定 128 位、也就是 32 个十六进制字符的结果。它的核心特性是同样的输入永远得到同样的输出输入改动一个字节输出会产生巨大变化从输出反推输入在计算上不可行。请特别注意这段话里没有出现加密两个字。MD5 是单向摘要不是加密算法它没有密钥也无法解密还原。你能做的只有一件事对同样的内容再算一次比对两个摘要是否一致。这就是校验的全部含义。这也是本节最需要纠正的认知偏差。很多初学者看到/etc/shadow里一长串字符就认为密码被 MD5 加密了很安全。实际上那串东西确实可能是 MD5 的变体crypt家族的 MD5 版本但它早就不该被用于密码存储了。5.2 用途一文件完整性校验这是 MD5 最正当、最常用的场景。下载一个安装包、分发一份配置、备份一批文件之后用md5sum生成基线摘要之后任何时候重新计算并比对就能快速判断内容是否发生变化。它的优势是速度快、输出短、对比直观、几乎所有 Linux 系统自带。局限也很明确MD5 已被证明存在碰撞攻击也就是说攻击者可以构造两个内容不同但 MD5 相同的文件。所以它适合用来发现意外改动比如误编辑、传输损坏、磁盘错误不适合用来抵御恶意篡改。对抗主动攻击者需要用 SHA-256 以上的摘要或者更彻底地用 GPG 签名验证来源。5.3 用途二shadow 中的密码哈希历史场景/etc/shadow第二字段的格式通常是$算法标识$盐值$哈希值。常见的标识有前缀算法现状$1$MD5 crypt已不推荐仅存在于老系统$5$SHA-256 crypt可用但逐渐被替代$6$SHA-512 crypt目前较常见$y$yescrypt部分新版本发行版采用较老的发行版默认使用$1$较新的发行版一般默认$6$一些新版本已开始引入更强的算法。你可以直接查看自己的系统# 查看指定用户密码哈希的前缀注意不要把完整哈希贴到公开渠道 getent shadow zhangsan | cut -d: -f2 | cut -c1-3 # 查看系统默认的加密方法配置不同发行版路径不同 grep -i ^ENCRYPT_METHOD /etc/login.defs 2/dev/null grep -i password.*pam_unix /etc/pam.d/system-auth 2/dev/null如果发现存量账号还是$1$比较稳妥的做法不是手动去改 shadow 文件而是让用户下次改密时自动升级或者由管理员用passwd 用户名重设一次密码让系统按当前配置重新生成哈希。手改 shadow 的风险极高一个字符错位就可能导致该账号永久无法登录而且难以回滚。6. 完整示例与代码实现下面用三个可复制的示例把用户管理和 MD5 校验串起来。6.1 示例一带校验的批量建号脚本这个脚本做四件事读取用户清单、逐个创建用户、设置初始密码、在操作前后对账户文件做摘要比对。这样即使中途出错你也能立刻知道哪些文件被改动过。#!/bin/bash # 文件路径/usr/local/bin/batch_add_users.sh # 用途从清单文件批量创建用户并在操作前后对账户文件做 MD5 摘要比对 # 注意请在测试环境验证后再用于生产执行前确保已备份 /etc 下的账户文件 set -euo pipefail USER_LIST${1:-/root/userlist.txt} BACKUP_DIR/root/account-backup-$(date %Y%m%d%H%M%S) TARGET_FILES/etc/passwd /etc/shadow /etc/group /etc/gshadow if [[ ! -f ${USER_LIST} ]]; then echo 用户清单不存在: ${USER_LIST} exit 1 fi mkdir -p ${BACKUP_DIR} echo 操作前摘要 md5sum ${TARGET_FILES} | tee ${BACKUP_DIR}/before.md5 while IFS: read -r username comment group; do # 跳过空行和注释行 [[ -z ${username} || ${username} ~ ^# ]] continue if id ${username} /dev/null; then echo [跳过] 用户 ${username} 已存在 continue fi useradd -m -s /bin/bash -g ${group} -c ${comment} ${username} echo [创建] ${username} 完成主组 ${group} done ${USER_LIST} echo 操作后摘要 md5sum ${TARGET_FILES} | tee ${BACKUP_DIR}/after.md5 echo 差异对比 diff ${BACKUP_DIR}/before.md5 ${BACKUP_DIR}/after.md5 || true echo 备份目录: ${BACKUP_DIR}配套的用户清单文件格式如下冒号分隔# 文件路径/root/userlist.txt # 格式用户名:注释:主组 zhangsan:Zhang San backend:developers lisi:Li Si frontend:developers wangwu:Wang Wu ops:ops这个脚本里有两个设计值得说明。第一是set -euo pipefail它让脚本在任意命令失败时立即退出避免报错了还继续往下建号的半成品状态。第二是用diff对比前后摘要输出会直接告诉你哪几行不同配合md5sum的文件内容变化则摘要必变特性能快速锁定被修改的文件范围。6.2 示例二配置文件的完整性巡检这个场景更贴近日常运维把关键配置的摘要存成基线文件定期巡检时比对发现异常就报警。#!/bin/bash # 文件路径/usr/local/bin/config_integrity_check.sh # 用途对关键配置文件做摘要巡检与基线比对 # 建议配合 cron 定期执行基线文件需妥善保管 set -uo pipefail BASELINE/var/lib/config-baseline.md5 WATCH_FILES/etc/passwd /etc/shadow /etc/group /etc/ssh/sshd_config /etc/sudoers /etc/login.defs if [[ ${1:-} init ]]; then md5sum ${WATCH_FILES} ${BASELINE} chmod 600 ${BASELINE} echo 基线已生成: ${BASELINE} exit 0 fi if [[ ! -f ${BASELINE} ]]; then echo 基线文件不存在请先执行: $0 init exit 2 fi echo 开始巡检 $(date %F %T) if md5sum -c ${BASELINE} 21; then echo 结果全部一致 else echo 结果存在差异请立即排查变更来源 exit 1 fimd5sum -c是这里的关键。它会逐行读取摘要文件重新计算每个文件的摘要并比对输出文件名: OK或文件名: FAILED只要有任意一项失败命令的退出码就是非 0。这个退出码语义让它可以很方便地接入监控系统或 CI 流程。6.3 示例三用 Python 对比 MD5 与 SHA-256如果要在应用层做校验或者想直观理解不同算法输出长度和计算耗时的差异可以看下面这段代码。# 文件路径/root/scripts/hash_compare.py # 用途对比 MD5 与 SHA-256 的摘要长度和计算耗时 # 运行python3 /root/scripts/hash_compare.py /etc/passwd import hashlib import sys import time def digest(path: str, algo: str, chunk_size: int 8192) - tuple[str, float]: 分块读取文件并计算摘要避免大文件占满内存。 h hashlib.new(algo) start time.perf_counter() with open(path, rb) as f: while True: chunk f.read(chunk_size) if not chunk: break h.update(chunk) cost time.perf_counter() - start return h.hexdigest(), cost def main() - None: if len(sys.argv) ! 2: print(用法: python3 hash_compare.py 文件路径) sys.exit(1) target sys.argv[1] for algo in (md5, sha256): value, cost digest(target, algo) print(f{algo:6} | 长度 {len(value):3} | 耗时 {cost * 1000:.3f} ms | {value}) if __name__ __main__: main()运行后你会看到 MD5 输出 32 个字符、SHA-256 输出 64 个字符。对于普通大小的配置文件两者的耗时差异在日常场景里完全可以忽略这也说明因为性能所以选 MD5在现代硬件上基本不成立。既然代价差不多就没有理由在安全相关的场景里退而求其次。7. 运行结果与效果验证先验证用户是否创建成功。正确的输出应该包含用户 ID 和组信息id zhangsan预期输出类似uid1500(zhangsan) gid1001(developers) groups1001(developers),10(wheel),999(docker)。如果groups里少了预期的附加组说明-G参数写错或者用了usermod -G覆盖掉了。如果gid对不上说明主组指定有误。再验证密码哈希是否按预期生成getent shadow zhangsan | cut -d: -f2 | cut -c1-3输出$6$说明使用了 SHA-512输出$1$则说明沿用了老配置。如果输出是!!或!说明账号被锁定或还没有设置密码需要排查是不是passwd那一步没执行成功。然后验证摘要比对是否生效。故意改一个文件再跑一次巡检# 增加一行无害注释模拟配置变更 echo # test change $(date) /etc/login.defs # 执行巡检应该报告 FAILED /usr/local/bin/config_integrity_check.sh # 验证完毕后恢复生产环境请从备份还原不要手写 sed -i /# test change/d /etc/login.defs预期能看到/etc/login.defs: FAILED和最终的md5sum: WARNING: 1 computed checksum did NOT match。如果命令返回 0 且显示全部 OK说明基线文件里可能没有包含这个路径需要检查WATCH_FILES变量的内容。如果任何一步失败排查顺序建议是先看命令的退出码echo $?再看 stderr 的报错原文最后检查/var/log/secure或/var/log/auth.log里的认证日志。用户管理相关的失败九成以上能在认证日志里找到直接原因。8. 常见问题与排查思路问题现象可能原因排查方式解决方案useradd成功但无法登录shell 被设为/sbin/nologin或账户被锁定getent passwd 用户名看第七字段passwd -S 用户名看状态用usermod -s /bin/bash修正或用passwd -u解锁用户登不上且提示密码错误shadow 中密码字段为!!或!开头getent shadow 用户名 | cut -c1-3用passwd 用户名重设避免手改 shadow附加组权限不生效用了usermod -G覆盖了原有组或用户会话未刷新id 用户名对比预期组列表用usermod -aG追加用户需重新登录才生效md5sum -c全部 FAILED基线文件里的路径与当前工作目录不匹配打开基线文件看路径是相对还是绝对生成基线时使用绝对路径md5sum报No such file文件被移动或删除或路径含空格未转义逐个ls -l检查路径修正路径含空格时加引号删除用户后文件属主变成数字userdel后残留文件属主 UID 已无对应账户find / -nouser -print确认无用后chown给其他账号或归档删除改了login.defs但老用户策略没变该文件只影响新建用户chage -l 用户名查看实际策略对存量用户用chage逐个调整passwd提示密码过于简单PAM 的 pwquality 模块拒绝查看/etc/security/pwquality.conf提高密码复杂度而不是关掉策略这里要特别强调第一条和第二条的区别。很多人把登不上笼统归因为密码错了实际上 shell 是nologin、账户被passwd -l锁定、账户已过chage -E指定日期、密码已过期未改这四种情况的表现都是登不上但修复方式完全不同。passwd -S 用户名会直接给出状态如PS表示已设密码、LK表示锁定、NP表示空密码是排查这类问题的第一条命令。9. 最佳实践与工程建议关于用户管理。生产环境里更推荐一人一号 组授权 sudo 精细化的组合而不是共享账号。共享账号的问题不是安全教条而是出事后日志里全是同一个用户名根本追不到人。sudo 授权也不要图省事写ALL(ALL) ALL应该按命令白名单配置并用visudo编辑它会做语法检查直接vi /etc/sudoers写错一个字符就可能让所有人失去 sudo 权限。关于 UID 规划。有多个节点的集群环境最好提前规划 UID 段让同一批人在不同机器上的 UID 一致。否则 NFS 共享目录会出现文件属主错乱的问题——在 A 机器上是 zhangsan挂到 B 机器上就变成了别人的用户名因为 UID 1500 在 B 机器上对应的是另一个人。这个坑在没有统一认证体系如 LDAP的小规模环境里非常常见。关于哈希算法选择。新系统在装机时就应确认默认哈希算法是$6$或更强而不是沿用老镜像里的$1$。如果存量系统里还有 MD5 哈希制定一个渐进式迁移计划优先处理有 sudo 权限和高权限服务的账号通过强制改密自然升级不要批量脚本直接改写 shadow。关于摘要校验的定位。用 MD5 做完整性巡检是有价值的但要清楚它的边界。建议的做法是内部变更发现用 SHA-256代价几乎一样对外部下载的软件包同时校验摘要和签名对/etc下的关键文件建立基线并纳入定期巡检基线文件本身要设成600权限并单独备份。不要把摘要文件和数据文件放在同一个可写目录里否则攻击者改完文件顺手改摘要校验就形同虚设。关于操作纪律。动/etc下的账户文件之前先备份、先算摘要、先在测试环境验证删除用户前先确认无进程、无关键数据修改组成员关系后提醒用户重新登录所有这些动作都建议写进团队的运维手册而不是靠个人记忆。这些不是流程负担而是把不可逆操作变成可回滚操作的最低成本手段。10. 收尾把两件事真正连起来回到文章开头那两个现场。第一个现场的核心不是命令用错而是没有理解主组决定文件属组、附加组决定额外权限这条基本规则第二个现场的核心不是记性差而是缺少一个廉价、可靠、可自动化的变更证据机制。用户管理让你定义清楚系统的访问边界MD5 校验让你在边界被触碰时能第一时间发现。前者是设计后者是验证缺了任何一个运维工作都只能靠感觉没出问题来推进。如果这篇文章只能留下一句话那应该是下次动/etc/shadow之前先md5sum一份备份确认清楚你要改的是哪一行、改完之后怎么验证、出问题怎么回滚再动手。这一步花掉的三十秒往往能省下后面三个小时的排查。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑