一、引言:从一次"半夜告警"说起
凌晨两点,运维群里突然弹出一条告警:线上服务器出现大量 Permission denied 报错,某个核心服务的进程无法写入日志目录。排查一圈后发现,原来是一位同事为了调试方便,直接给测试账号赋予了过大的权限,又在不经意间修改了该账号的主组,导致服务启动时所属用户组发生漂移,目录写权限瞬间失效。
这类问题在 Linux 运维与开发工作中并不罕见。Linux 的多用户、多任务特性,决定了它天生就是一台"共享服务器",可能同时有开发、测试、运维、数据库、应用系统等多个角色在上面工作。谁能登录?登录后能访问哪些文件?能否执行 sudo?这几个问题的答案,几乎都藏在"用户"和"用户组"这两个概念里。
如果把 Linux 系统比作一栋写字楼:
- 用户(User) 就是持有门禁卡的员工,每个人有唯一的工号(UID);
- 用户组(Group) 就是部门或项目组,一群人共享某些办公区域和资源;
- 权限(Permission) 就是楼层和房间的通行规则。
而 useradd、usermod、groupadd 这三个命令,就是管理员手中最常用的"人事系统":新增员工、调整部门、新建部门。掌握它们,是每一位 Linux 工程师的必修课,也是处理权限问题、保障系统安全的基础功。
本文将从实际工作场景出发,系统讲解这三个核心命令的语法、参数、实战示例与常见误区,并补充 /etc/passwd、/etc/shadow 等底层原理,帮助你把"用户管理"这门手艺真正吃透。
二、先建立基本概念:用户、用户组与三个关键文件
在动手敲命令之前,我们先厘清几个基础概念,否则后面的参数会越看越晕。
1. 用户(User)与 UID
Linux 系统中的每个用户都拥有一个唯一的数字标识,称为 UID(User ID)。系统内核在判断文件归属时,认的是 UID 而不是用户名;用户名只是给人类看的"友好名称"。
常见的 UID 约定(以 CentOS 7/8、RHEL 7+ 为主):
| 用户类型 | UID 范围 | 说明 |
|---|---|---|
| 超级用户 root | 0 | 系统最高权限,UID 固定为 0 |
| 系统用户 | 1~999 | 供系统服务和守护进程使用,通常无登录能力 |
| 普通用户 | 1000~60000 | 由管理员创建的日常登录账号 |
注意 :在 CentOS 6 及更早版本中,系统用户范围是 1~499,普通用户从 500 开始。不同发行版的默认范围可能略有差异,
useradd实际取值受/etc/login.defs中的UID_MIN和UID_MAX控制。
2. 用户组(Group)与 GID
用户组是权限管理的最小"容器",每个组同样有一个唯一的数字标识 GID(Group ID) 。每个用户必须属于至少一个组,这个组称为主组(Primary Group) ;此外用户还可以加入多个附加组(Supplementary Groups)。
一个形象的类比:主组相当于你的"编制所在部门",附加组相当于你同时参与的"跨部门项目组"。用户在创建文件时,文件的属组默认就是其主组。
3. 知识扩展:三个"户口本"文件
Linux 把用户和组的信息保存在纯文本文件中,它们就像系统的"户口本"。理解这三个文件,能让你对很多报错瞬间豁然开朗。
(1) /etc/passwd ------ 用户账号信息文件
每一行代表一个用户,格式为 7 个冒号分隔的字段:
用户名:密码占位符:UID:GID:描述信息:家目录:登录Shell
示例:
bash
[root@server ~]# cat /etc/passwd | head -3
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
zhangsan:x:1000:1000:zhangsan:/home/zhangsan:/bin/bash
字段逐一解释:
| 字段 | 含义 |
|---|---|
| 用户名 | 登录时使用的账号名 |
| 密码占位符 | 早期直接存密码,现在统一为 x,真实密码在 /etc/shadow |
| UID | 用户 ID |
| GID | 主组 ID |
| 描述信息 | 注释字段,如姓名、部门等,称为 GECOS |
| 家目录 | 用户登录后的默认目录 |
| 登录 Shell | 用户登录后使用的解释器,/sbin/nologin 表示禁止登录 |
(2) /etc/shadow ------ 用户密码及策略文件
这个文件只允许 root 读取,格式同样以冒号分隔:
bash
[root@server ~]# cat /etc/shadow | grep zhangsan
zhangsan:$6$abc123...:19500:0:99999:7:::
核心字段包括:
| 字段 | 含义 |
|---|---|
| 用户名 | 对应 /etc/passwd 中的账号名 |
| 加密密码 | 以 $6$ 开头表示采用 SHA-512 算法,"!" 或 "*" 开头表示密码被锁定 |
| 最近修改密码时间 | 从 1970 年 1 月 1 日算起的天数 |
| 密码最小使用天数 | 在此天数内不允许改密码 |
| 密码最大使用天数 | 超过则强制改密码 |
| 警告天数 | 密码过期前多少天提示用户 |
| 不活动天数 | 密码过期后多少天账户被禁用 |
| 失效时间 | 账户的绝对失效日期 |
(3) /etc/group ------ 用户组信息文件
bash
[root@server ~]# cat /etc/group | grep -E "root|dev"
root:x:0:
dev:x:1002:zhangsan,lisi
格式为:组名:组密码占位符:GID:组成员列表。最后一列列出了把该组作为附加组的用户,主组用户不会重复出现在这里。
原理探究 :为什么密码要与
/etc/passwd分离?历史早期密码确实直接存在/etc/passwd中,但该文件对所有用户可读,导致任何普通用户都能看到别人的密码哈希,并尝试暴力破解。后来引入了/etc/shadow影文件机制,将敏感信息独立存放,并收紧读取权限,这就是"shadow(影子)"一词的由来。
三、useradd:创建用户的"入职办理"
useradd 用于在系统中创建新用户。它背后其实做了一系列动作:在 /etc/passwd 和 /etc/shadow 中追加条目、在 /etc/group 中创建同名主组、创建家目录并拷贝 /etc/skel 骨架文件等。
1. 语法格式
bash
useradd [选项] 用户名
2. 常用参数说明
| 参数 | 作用 |
|---|---|
-u |
指定用户的 UID |
-g |
指定主组(可用组名或 GID) |
-G |
指定一个或多个附加组,多个组用逗号分隔 |
-d |
指定家目录路径 |
-m |
创建家目录(默认行为之一,取决于配置) |
-M |
不创建家目录 |
-s |
指定登录 Shell |
-c |
添加描述信息(GECOS 注释) |
-e |
指定账户过期日期(格式 YYYY-MM-DD) |
-f |
密码过期后的宽限天数,-1 表示永不禁用 |
-r |
创建系统用户 |
-k |
指定骨架目录(默认为 /etc/skel) |
-D |
查看或修改创建用户的默认配置 |
3. 实战示例
示例 1:创建一个最普通的用户 zhangsan
bash
root@server:~# useradd zhangsan
root@server:~# id zhangsan
uid=1006(zhangsan) gid=1006(zhangsan) groups=1006(zhangsan)
预期结果:系统按默认配置创建了 zhangsan 用户,并自动创建了同名主组 zhangsan(GID=UID,这是多数发行版的私有组策略,简称 UPG)。此时家目录 /home/zhangsan 已生成,登录 Shell 默认是 /bin/bash。
示例 2:创建指定 UID、附加组和登录 Shell 的用户
bash
root@server:~# useradd -u 1500 -g developers -G docker,nginx -c "Backend Engineer" -s /bin/bash lisi
root@server:~# id lisi
uid=1500(lisi) gid=1002(developers) groups=1002(developers),1003(docker),1004(nginx)
说明:
-u 1500:指定 UID 为 1500;-g developers:主组设为 developers 组(该组必须已存在);-G docker,nginx:同时加入 docker 和 nginx 两个附加组;-c:写入描述信息"Backend Engineer"。
随后可以用 grep lisi /etc/passwd 查看该行信息验证描述字段。
示例 3:创建不允许登录的系统用户
部署某些服务(如 MySQL、Nginx)时,我们通常需要一个"跑服务"的账号,但又不希望它能登录系统:
bash
root@server:~# useradd -r -M -s /sbin/nologin myservice
root@server:~# grep myservice /etc/passwd
myservice:x:997:993::/home/myservice:/sbin/nologin
说明:
-r:创建系统用户,UID 会分配在系统用户区间(如 999 以内);-M:不创建家目录;-s /sbin/nologin:禁止交互式登录。
示例 4:查看与修改 useradd 的默认配置
bash
root@server:~# useradd -D
GROUP=100
HOME=/home
INACTIVE=-1
EXPIRE=
SHELL=/bin/bash
SKEL=/etc/skel
CREATE_MAIL_SPOOL=no
输出展示了默认的主组 GID、家目录父路径、密码策略、默认 Shell 等。这些默认值来自 /etc/default/useradd 与 /etc/login.defs 两个文件。
4. 注意事项
- 附加组必须先存在 :
-G指定的组如果不存在,useradd会直接报错退出。 - 主组不自动创建 :
-g指定的主组必须已经存在,与默认的"自动建同名组"机制不同。 - 用户名命名规范:通常以小写字母开头,可包含数字和下划线,避免使用纯数字或与系统用户重名,长度一般不超过 32 字符。
- 密码需单独设置 :
useradd创建的用户默认没有可用密码,/etc/shadow中密码字段为!!,必须用passwd 用户名初始化密码后才能登录。
四、usermod:修改用户属性的"调岗工具"
现实工作中,用户的信息经常需要调整:换部门(改主组)、加入新项目组(加附加组)、改登录名、锁定离职员工账号等。这些场景都要用到 usermod。
1. 语法格式
bash
usermod [选项] 用户名
2. 常用参数说明
| 参数 | 作用 |
|---|---|
-l |
修改登录用户名 |
-u |
修改 UID |
-g |
修改主组 |
-G |
重新设置附加组列表(覆盖式) |
-aG |
以追加方式加入附加组,必须与 -G 一起使用 |
-d |
修改家目录路径 |
-m |
与 -d 合用,将旧家目录内容移动到新目录 |
-s |
修改登录 Shell |
-c |
修改描述信息 |
-e |
修改账户过期日期 |
-L |
锁定账号(在密码前加 !) |
-U |
解锁账号 |
3. 实战示例
示例 1:给 lisi 追加新附加组
假设 lisi 原本属于 docker 组,现在还要让他加入 k8s 组:
bash
root@server:~# usermod -aG k8s lisi
root@server:~# id lisi
uid=1500(lisi) gid=1002(developers) groups=1002(developers),1003(docker),1005(k8s)
重点提醒 :如果不加
-a,-G会覆盖 原有的附加组列表。很多管理员因为漏写-a,导致用户之前加入的组全被清空,这是最典型的usermod坑。
示例 2:修改主组
把 lisi 的主组从 developers 调整为 ops:
bash
root@server:~# usermod -g ops lisi
root@server:~# id lisi
uid=1500(lisi) gid=1006(ops) groups=1006(ops),1003(docker),1005(k8s)
修改主组后,lisi 之后新建文件的属组会变为 ops。组中不再以附加组形式重复显示主组。
示例 3:修改用户名与家目录
老员工改名或账号规范统一时常用:
bash
root@server:~# usermod -l wangwu -d /home/wangwu -m lisi
root@server:~# ls -ld /home/wangwu
drwx------ 2 wangwu wangwu 4096 Sep 6 22:00 /home/wangwu
说明:
-l wangwu:登录名从 lisi 改为 wangwu;-d /home/wangwu:新家目录路径;-m:把原家目录/home/lisi下的内容一并搬到新目录。
改动后,/etc/passwd 会同步更新,但需要注意:正在登录的用户应先退出再改名,否则可能出现进程归属异常。
示例 4:锁定与解锁账号
员工离职交接期间,可以先锁定账号而不删除:
bash
root@server:~# usermod -L wangwu
root@server:~# grep wangwu /etc/shadow
wangwu:!$6$xyz...:19500:0:99999:7:::
密码字段前多了一个 !,表示账号被锁定。恢复时使用:
bash
root@server:~# usermod -U wangwu
4. 常见错误
| 错误表现 | 可能原因 | 解决办法 |
|---|---|---|
usermod: user xxx is currently used by process pid |
用户正在登录或有进程归属该用户 | 先让用户退出,或确认其进程已结束 |
修改 -G 后原来的组丢了 |
漏写 -a 参数 |
使用 -aG 追加 |
| 提示家目录已存在 | 只改了 -d 未改目录名或目录冲突 |
用 -m 移动,或先整理目标目录 |
| 改完名无法登录 | 相关目录或服务仍引用旧名 | 检查家目录权限、Shell、SSH 配置 |
五、groupadd:创建用户组的"建部门"
用户组是批量授权的最小单位。比如你要允许整个"前端组"访问某个共享目录,就不需要逐个用户设置,而是创建一个组,把相关用户加进去,再对组授权即可。
1. 语法格式
bash
groupadd [选项] 组名
2. 常用参数说明
| 参数 | 作用 |
|---|---|
-g |
指定 GID |
-r |
创建系统组(GID 分配在系统区间) |
-f |
若组已存在则成功退出,不报错(常配合固定 GID 使用) |
3. 实战示例
示例 1:创建普通组
bash
root@server:~# groupadd developers
root@server:~# grep developers /etc/group
developers:x:1002:
默认会从普通 GID 区间自动分配一个值。
示例 2:创建指定 GID 的组
在批量部署、多台机器保持 GID 一致时,指定 GID 非常重要:
bash
root@server:~# groupadd -g 2000 devops
root@server:~# grep devops /etc/group
devops:x:2000:
为什么需要固定 GID?如果多台服务器共用 NFS 共享存储,而两个账号在不同机器上的 UID/GID 不一致,就会出现"文件明明属于我,却显示无权限"的诡异问题。统一规划 UID/GID,是生产环境的基础规范。
示例 3:幂等地创建系统组
编写自动化脚本时,为了避免"组已存在"导致脚本中断,可以用 -f:
bash
root@server:~# groupadd -frg 900 mysysgroup
root@server:~# groupadd -frg 900 mysysgroup
root@server:~# echo $?
0
第二次执行不再报错,脚本可以继续往下跑。
4. 注意事项
- 组名不要与已有用户名冲突,也不建议使用大写字母和特殊符号(虽然多数发行版允许,但容易在跨平台脚本中出现兼容问题)。
- 删除组应使用
groupdel,且只有当该组不是任何用户的主组时才能删除。 - 修改已有组的 GID 要用
groupmod -g,而不是重复groupadd。
六、配套命令:passwd、userdel、groupdel、id
光会"建"不会"管"是不够的。下面几个命令是用户组管理的日常搭档。
1. passwd:设置与修改密码
bash
root@server:~# passwd zhangsan
New password:
Retype new password:
passwd: all authentication tokens updated successfully.
root 可以给任何用户改密码;普通用户只能改自己的密码,且会要求输入旧密码。常用选项:
-l:锁定账号密码;-u:解锁;-e:强制用户下次登录时修改密码;-S:查看密码状态。
bash
root@server:~# passwd -S zhangsan
zhangsan PS 2026-09-06 0 99999 7 -1 (Password set, SHA512 crypt.)
2. userdel:删除用户
bash
root@server:~# userdel zhangsan # 只删除账号,不删家目录
root@server:~# userdel -r zhangsan # 连同家目录和邮件池一起删除
注意 :如果该用户有正在运行的进程,删除前应先
pkill -u 用户名结束进程,否则可能残留"已删除但仍在运行"的进程,带来安全隐患。
3. groupdel:删除用户组
bash
root@server:~# groupdel developers
要成功删除,该组不能是任何用户的主组,且最好先确保没有用户以它为附加组(虽然多数情况 groupdel 不会自动清理附加组关系,需结合 gpasswd -d 处理)。
4. id / groups:查看归属信息
bash
root@server:~# id zhangsan
uid=1006(zhangsan) gid=1006(zhangsan) groups=1006(zhangsan),1003(docker),1005(k8s)
root@server:~# groups zhangsan
zhangsan : zhangsan docker k8s
这两个命令是排查"我到底属于哪些组"的最快手段,也是验证前面 useradd、usermod 是否生效的得力工具。
七、最佳实践:构建一套规范的账号管理体系
结合多年运维经验,下面梳理几条生产环境建议,供你参考:
- 统一规划 UID/GID:跨服务器保持同名用户、同 UID、同 GID,尤其是使用 NFS 共享存储的集群,避免权限错乱。
- 坚持最小权限原则 :服务运行账号用系统用户 +
/sbin/nologin,不为服务配置交互登录能力;普通员工只加必要附加组,不轻易授予 sudo。 - 修改附加组务必用
-aG:把"追加"作为默认习惯,防止覆盖式清空组列表。 - 离职账号先锁定、后清理 :先
usermod -L锁定,观察一段时间,确认无关键进程依赖后再userdel -r删除。 - 用脚本管理账号生命周期:入职、调岗、离职都可以封装成脚本,降低手工误操作概率,并留存审计记录。
- 定期审计账号与组 :执行
awk -F: '($3==0){print $1}' /etc/passwd检查是否只有 root 的 UID 为 0,防止出现"影子管理员"。
八、总结与练习
本文围绕 useradd、usermod、groupadd 三个命令,系统梳理了 Linux 用户与用户组管理的核心知识:从 UID/GID 的概念,到 /etc/passwd、/etc/shadow、/etc/group 三个底层文件,再到常用参数、实战示例与常见错误。掌握这些内容后,你将能够从容应对日常工作中的账号创建、权限调整与安全加固等任务。
练习题
-
基础题 :创建一个普通用户
alice,UID 为 1501,主组为dev,附加组包含docker,登录 Shell 为/bin/bash,写出完整命令并解释每个参数的作用。提示:使用
useradd -u -g -G -s组合,注意dev组需先创建。 -
进阶题 :用户
bob原本属于ops组,现在需要新增db附加组,但又不能丢失原来的ops组。请写出正确命令,并说明如果漏掉某个参数会导致什么后果。提示:对比
-G与-aG的行为差异。 -
故障排查题 :某用户反馈无法登录,
passwd文件里该用户条目正常,但使用su - username时提示密码错误,并观察到/etc/shadow中该用户密码字段以!开头。请分析原因并给出恢复方法。提示:结合
usermod -L与usermod -U的机制分析。 -
综合题 :为部署一个 Nginx 服务账号,要求该账号不能交互登录、不需要家目录、属于系统用户,并且为了标识用途,描述信息填
Nginx Service Account。请写出完整的创建命令。提示:考虑
useradd -r -M -s与-c的组合。 -
思考题 :为什么多台服务器之间建议保持 UID/GID 一致?如果两台机器上同名用户的 UID 不同,使用 NFS 共享文件时会遇到什么现象?
提示:NFS 权限判断基于 UID 数字,而非用户名字符串。
小建议 :练习时建议在虚拟机或容器中操作,使用
id、grep /etc/passwd等命令及时验证每一步结果,把"命令---文件---现象"三者对应起来,理解会更扎实。
希望这篇文章能帮你搭建起 Linux 用户与用户组管理的完整知识框架。把基础打牢,后续学习权限管理(chmod、chown、ACL)、sudo 配置乃至容器化环境中的用户映射,都会事半功倍。