做运维这些年,最容易被新手忽略、又最容易吃亏的,就是 Linux 的用户和组管理。前几天帮同事排查一个烦了半天的场景:某个管理界面能打开,但用 admin 用户就是创建不了虚拟主机,翻遍日志发现根本不是服务端故障,而是用户被加进了错误的组,角色映射不到对应权限上。这类问题几乎每天都在生产环境里重演。用户和组这套体系,决定了谁能登录、谁能读写文件、谁能执行特权命令,是 Linux 安全模型的根基。不管你是刚入门的学生、准备面试的求职者,还是在生产环境救过火的运维,把这套东西吃透,都稳赚不赔。
这篇内容我结合了实际运维中踩过的坑,从模型、命令、权限设计、密码策略到问题排查,把用户与组管理完整拆开讲。不堆概念,全部是可以直接抄走的实操经验。
1. 用户模型与文件权限的基本关系
1.1 Linux 为什么把权限绑定在“用户”上
很多人第一次接触 Linux 权限时,会被 drwxr-xr-x 这串字符吓到,但实际上它背后就三组身份:文件所有者、文件所属组、其他用户。Linux 的哲学非常简单——一切皆文件,文件上记录的是“谁拥有它”和“谁能碰它”。这里的“谁”,本质就是 UID 和 GID,而不是你在登录框里敲的用户名。用户名是人类读的,系统只认数字。
这套“用户-组-权限”模型能如此长久地统治服务器世界,是因为它足够简单,又能组合出复杂策略。举一个生活化的例子:公司里一个部门对应一个“组”,部门共享的资料夹设成“组内可读写”,新同事入职只要把他加进这个组,所有资料权限自动到位;离职时把他移出组就够了。不用一个个文件去改权限,这大大降低了管理成本,尤其是当你面对成百上千台服务器时,靠手工 chmod 会把人逼疯。
1.2 三个核心配置文件:passwd、shadow、group
用户和组不是一个“数据库”,而是几个纯文本文件。维护 Linux 用户管理,本质上就是在维护这些文件,虽然日常通过命令操作,但有些极端场景(比如系统进单用户模式救援),你还是要直接编辑它们。
/etc/passwd:存放账号基础信息,每行七个字段,冒号分隔:用户名、密码占位符(通常是x,真正的密码不在这里)、UID、GID、注释、家目录、登录 shell。/etc/shadow:真正存放加密密码和密码策略的地方。权限必须是000或者0400,只有 root 能读。里面的字段包括:加密密码、最近一次修改密码的日期、密码最小/最大使用天数、过期警告天数、宽限期、失效日期等。/etc/group:存放组信息,每行四个字段:组名、密码占位符、GID、组成员列表(逗号分隔)。
有一个最常见的坑:/etc/passwd 应该是全局可读的,因为它不只是给你 cat 看的,很多系统服务都要读它做用户名和 UID 的映射。如果你把它的权限改成了 600,看起来“更安全”,结果可能就是系统服务异常、命令解析用户失败,甚至登录出问题。我见过不止一个人在这里自作聪明栽跟头。
1.3 UID 与 GID:数字身份才是底层真身
Linux 对 UID 有约定俗成的分法:0 是 root,1~999 通常分配给系统服务账号(比如 sshd、nginx、mysql),1000 以上才给普通用户。具体起止数值跟发行版有关,CentOS 从 1000 开始,Ubuntu 从 1000 开始,但很多新版从 1000 开始,有些发行版系统账号范围不同,务必以实际系统为准。
删除用户、再按相同用户名重新创建,会产生一个“新 UID 的旧名字”——从权限角度讲,它跟旧用户没有任何关系。如果你误删了用户,新用户无法继承旧家目录的文件所有权,因为家目录里的文件记录的是旧 UID。系统不会因为你用户名一样就认为它们是同一个人。这是生产环境删用户之前必须想清楚的威慑:先确认这个用户的文件归属,或者先备份、迁移,再去 userdel。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 用户账号的完整生命周期管理
2.1 创建用户:useradd 的参数与默认策略
把用户管理的操作比作一个账号的“出生到注销”,第一步就是创建。useradd 命令我用了十年,到现在依然建议把关键参数写在明面上,不要依赖发行版默认值。最稳妥的创建方式是:
bash复制useradd -m -d /home/zhangsan -s /bin/bash -c "Zhang San" zhangsan
解释一下参数:-m 强制创建家目录,-d 指定家目录路径,-s 指定登录 shell,-c 写入备注信息。很多面试题会问“useradd 和 adduser 的区别”,说人话:Red Hat 系的 adduser 是 useradd 的软链接,Debian/Ubuntu 系则是交互式友好的 Perl 脚本,名字相同但行为不同。跨平台别想当然。
创建用户之后至少要做两件事:设置初始密码、检查锁定状态。设置密码是 passwd zhangsan 然后交互输入;如果你在写自动化脚本,可以用管道方式:
bash复制echo 'InitialPass123! | chpasswd
这句背后的含义是:把明文密码交给 chpasswd,它会按照系统 PAM 策略加密后写入 /etc/shadow。需要提醒的是,脚本里写明文密码一定要记得清理历史记录,建议配合密钥认证或密码保险箱工具来管理初始密码。
2.2 批量创建用户的脚本化思路
批量创建用户这件事,纯手工敲命令在 10 台以内还能忍,50 台以上会把自己搞崩。我一般先把用户列表写进一个文件,然后用循环处理,脚本骨架长这样:
bash复制#!/bin/bash
USER_LIST=/tmp/users.txt
while IFS= read -r user; do
useradd -m -d "/home/${user}" -s /bin/bash "${user}"
echo "${user}:${user}@Init2024" | chpasswd
chage -d 0 "${user}"
done < "${USER_LIST}"
注意最后一步 chage -d 0,它把“最近修改密码日期”强制归零,意思是:用户第一次登录时必须立即改密码。这是初始化账号最该养成的习惯,否则初始密码会一直有效,等于留了一扇长期打开的窗。
批量脚本里经常忽略的细节是幂等性:万一脚本跑了两次怎么办?最好先判断用户是否已存在:
bash复制if id "${user}" &>/dev/null; then
echo "${user} already exists, skip"
continue
fi
不加这个判断,第二次跑脚本会在 useradd 上报错退出。生产环境的自动化脚本,宁可多写几行判断,也别让脚本在半夜报警。
2.3 修改、锁定、删除用户的操作细节
日常管理里,usermod 是最常用的“外科手术刀”。下面是几个场景:
- 把用户加到补充组:
usermod -aG docker zhangsan。注意-a是 append,加组的语义;如果不带-a,会把用户从原有附加组里全部踢出来,只保留你指定的组。这是很多人搞挂生产账号权限的高频原因。 - 修改家目录并迁移:
usermod -d /data/zhangsan -m zhangsan,-m会把原家目录内容搬过去。 - 临时禁用账号登录:
usermod -L zhangsan锁定密码,或者用chage -E 0 zhangsan让账号立即过期。两者差别在于:锁定密码是让密码无效,过期是让账号整体失效。配合/etc/nologin文件还能在维护窗口期禁止所有非 root 登录,这个用法在应急时很管用。
删除用户同样有讲究。裸 userdel zhangsan 只会删账号,不会删家目录和邮件池。想干净地清掉家目录,用 userdel -r zhangsan。但我强烈建议不要急着删,生产环境里稳妥的做法是:
- 先锁定账号:
usermod -L zhangsan - 改掉用户 shell 为
/sbin/nologin - 把家目录打包保留一段时间,确认没有服务依赖后再清理
很多事故都发生在“上线前顺手删了一个测试账号,结果某子系统还在用这个 UID 跑定时任务”。
3. 组的妙用与共享目录权限
3.1 组管理与成员调整
组管理和用户管理是双胞胎。创建组用 groupadd devops,删除组用 groupdel devops,查看组成员可以用 groupmems -g devops -l,或直接 grep devops /etc/group。还有一个被低估的命令是 groups,它能同时显示当前用户的所有组成员关系,排查“为什么我有权限但没生效”时第一步就该敲它。
把用户加入一个组时,我刚才强调过的 -aG 再重申一次:一定要带 -a。不带 -a 等于重置附加组列表,会造成“用户突然失去了一堆组的权限”。这种现象在面试题里经常出现,实际工作中也真的是生产事故高发点。
3.2 共享目录的 setgid 设计
组存在的最大价值就是做“共享协作”。假设一个项目组有 5 个人,需要共用一个目录存放资料,最标准的做法是:
bash复制groupadd project-alpha
usermod -aG project-alpha zhangsan
usermod -aG project-alpha lisi
mkdir -p /data/project-alpha
chown root:project-alpha /data/project-alpha
chmod 2770 /data/project-alpha
chmod 2770 里的 2 是 setgid 位。它解决了一个真实痛点:普通用户在目录里新建文件时,文件所属组默认是“创建者自己的初始组”,而不是父目录的组。加上了 setgid,这个目录下新建的文件会自动继承 project-alpha 组,团队成员之间才能互相编辑,不需要 root 隔三差五去 chgrp。
权限位 2770 拆开解释:2 是 setgid,7 是 owner 读写执行,7 是 group 读写执行,0 是其他人无权限。如果把 0 改成 5,外部用户就只能读和执行目录,不能写入,这也是一种常见的外围只读方案。
3.3 初始组与附加组的选择
每个用户在 /etc/passwd 里都有一个主组(初始组),在 /etc/group 里还能挂多个附加组。文件的所有组字段只能填一个组:创建文件的用户的初始组,或者目录 setgid 继承的组。附加组的作用是决定“你能访问哪些组的资源”,不参与文件的“所有组”关系。
什么时候该调整初始组?比如一个账号 zhangsan 初始组本来是 zhangsan,但公司要求他所有新建文件都属于 devops 组,两种思路:一种是修改他的主组 usermod -g devops zhangsan;另一种是依赖项目目录的 setgid。我个人的习惯是:系统层面统一账号的初始组保持与用户名一致,不做特殊修改;项目协作全部走 setgid 目录加附加组。这样权限边界清晰,出问题时审计也容易定位。
4. 密码、sudo 与提权边界
4.1 密码策略:chage 和 /etc/shadow 的字段
密码策略是用户管理里最容易被忽略、又最关系到安全的一环。chage 命令专门管密码有效期,下面这组策略很常见:
bash复制chage -M 90 -m 7 -W 14 zhangsan
含义分别是:密码最长使用 90 天、最短使用 7 天(防止用户立刻改回原密码)、过期前 14 天开始警告。如果希望密码永不过期,可以用 chage -M -1,但我只在特殊服务账号上这么做,普通账号不建议。
/etc/shadow 里密码字段如果以 ! 或 !! 开头,代表账号密码被锁定。第 8 个字段是账号失效日期(从 1970-01-01 起算的天数),把它设成一个过去日期就能立刻让账号无法登录。运维排障时,如果用户说“密码是对的但登录不了”,先去 cat /etc/shadow 看一下密码字段是不是被意外加上了 !,这种问题纯看用户侧日志永远找不到答案。
4.2 sudo 授权的最小化实践
“提权”这个热词在安全圈被说烂了,但很多提权漏洞的根源不是 Linux 内核出了问题,而是 sudo 授权太宽松。给一个用户全部 root 权限最简单的写法是 usermod -aG wheel zhangsan,但这样该用户就能执行一切 root 操作。如果用户只需要重启某个服务,正确做法是编辑 /etc/sudoers.d/zhangsan:
code复制zhangsan ALL=(root) NOPASSWD: /usr/bin/systemctl restart nginx,/usr/bin/systemctl reload nginx
这行的语法拆开看:用户、来源主机、可切换身份、免密标志、允许执行的精确命令。用 sudo -l 可以查看当前用户被允许哪些命令,这是排查权限问题、做安全审计的第一条命令。
必须提一句:编辑 sudoers 文件别直接 vim /etc/sudoers,要用 visudo。它会做语法校验,防止你写错一行就导致所有 sudo 崩溃。更小的维护范围是丢在 /etc/sudoers.d/ 目录里,但记得通过 visudo -c 校验一次整体语法。
4.3 提权限权与审计
提权方向的安全意识也分两面:一面是“防止别人提权”,另一面是“合理审计谁在用什么权限”。系统账号遗留、临时账号不清理、sudo 规则形同虚设,都是提权攻击的帮凶。我曾在一台“退役”服务器上发现一个 UID 0 的账号,伪装成普通用户名,一看就是早期某个脚本创建的隐藏后门。排查方法很简单:
bash复制awk -F: '$3 == 0 {print $1}' /etc/passwd
正常情况下只有 root 一个。出现第二个 UID 0 用户就必须立刻调查。定期审计 sudoers、筛一遍 /etc/passwd 里带 shell 的账号,都是我每次安全巡检的固定动作。
5. 高频问题排查与经验总结
5.1 常见错误与解决思路
挑几个我在真实环境里反复遇到、也最常被问到的问题做一张速查表,供面试和实操时快速对照:
| 症状 | 常见原因 | 排查命令 |
|---|---|---|
| 用户能登录但没权限访问某些文件 | 附加组没生效或缺少 -a |
groups username、id username |
| 新建文件默认组不对 | 目录缺 setgid 位或用户主组不一致 | ls -ld /path 确认权限位 |
| sudo: user not in sudoers | 用户没在 sudo 组,或 sudoers.d 没配 | grep username /etc/sudoers.d/* |
| passwd: user is locked | /etc/shadow 密码字段以 ! 开头 |
passwd -u username 解锁 |
| 创建用户时家目录没生成 | 忘加 -m,或系统默认模板缺失 |
手动 mkdir 并 cp /etc/skel/ |
| 删除用户后重启某些服务报 UID 冲突 | 文件所有 UID 残留 | find / -uid 1001 查找残留文件 |
“家目录没生成”这个坑经常出现在默认配置被改过的系统上。useradd 默认是否创建家目录,取决于 /etc/login.defs 里的 CREATE_HOME 参数。生产环境我习惯显式加 -m,不赌默认值。
5.2 安全事故整理与补救路径
有一次,同事为了图方便给某应用账号加入了 root 组,结果该应用被攻破后,攻击者直接获得了全系统控制权。没有权限隔离的应用账号,等于给漏洞装上了直达内核的通票。正确的服务账号做法是:专用 UID、专用组、只给需要的目录 ACL 权限,绝不放进 root 组。软件包通过 systemd 运行时创建动态用户就是一个好选择,例如在 service 文件里写 DynamicUser=yes,系统自动分配临时 UID/GID,退出即释放,从设计上减少了权限残留。
万一真发生权限扩散,补救顺序是:先锁定相关账号,然后导出 sudoers 和登录记录,接着用 awk 脚本过滤 UID 0、附加组异常、shell 异常三类账号,最后在最小化授权基础上重建规则。切忌大范围直接删账号,很多账号和业务绑定,删完系统就起不来了。
5.3 一些我自己踩过坑后的建议
用户和组管理看似基础,但它的影响半径可能是一个文件的读写、一个服务的启停,甚至整个系统的沦陷。我给几条长期沉淀下来的经验:
- 命名字段要规范。用户名的命名规则提前定好,追加数字后缀比乱加下划线更可读;备注字段
-c写清用途和负责人,半年后回头看能省很多沟通成本。 - 能用组解决的协作问题,不要用 ACL 硬凑。ACL 灵活但复杂,传承性和可读性差。先尝试用 setgid 加组权限把需求解决掉,不行再考虑 ACL 补充。
- 定期巡检比事后补救香。写一个简单的巡检脚本,每周末扫一遍
/etc/passwd、/etc/shadow、/etc/sudoers.d/,把异常输出到日志,收到告警再处理。这些动作不花多少时间,但能把你从救火现场变成问题发现者。
用户与组管理是所有 Linux 操作的底座。它的命令不多,难的是理解背后的设计意图,并把这种意图落到自己的管理习惯里。无论你接下来是要考认证、面运维岗,还是已经在维护成百上千台服务器,把每一个用户、每一组权限都理清楚,你在权限问题上就再也不用靠运气了。
