1. 用户与组管理到底在解决什么问题
先说结论:用户与组管理是所有多用户操作系统最基础、也最容易被轻视的一道安全防线。我见过不少项目初期图省事,全部人共用一个账号,等到需要追责、隔离权限、清理环境的时候,才发现当初偷的懒全都变成了坑。
用户与组管理的核心目标只有三个:身份识别、权限控制、审计追溯。说白了,就是搞清楚“谁在用这台机器”、限制“这个人能碰什么”、记录下来“这个人做过什么”。
抛开这些明确的使用需求,“为什么必须用组而不是挨个授权”也是新手最容易想不通的地方。我用一句话解释:组是权限的批量打包。假设你的项目里有四个开发、两个测试、一个运维,如果不用组,每来一个新同事,你就得在十台机器上分别修改配置,漏掉一台就埋下一颗雷。有了组,只需要把新人加进对应的组,所有机器上的权限自动同步生效。
这个课题适合谁来读?如果你是刚接手服务器或工作站的开发、测试、运维同学,尤其是以前一直习惯用管理员账号一把梭的人,这篇文章能把用户与组管理的底层逻辑和常见坑讲透,让你后续管理再多机器都不慌。我会从底层机制讲到核心命令,再给一套可以直接抄作业的实操流程,最后是排错实录。
我强烈建议你看完这篇文章后,立刻去自己常用的那台 Linux 机器上跑几遍命令。用户管理这事儿,光看永远学不会,亲手创建、修改、删除几个用户和组,踩过几次错,才算真正过关。
2. 用户和组的底层机制:UID、GID 与权限模型的打通
2.1 三个核心配置文件,先搞懂系统怎么记录身份
用户与组管理表面上是一堆命令,背后实际上是四个文本文件在驱动一切。强烈建议你不要把它们当成“神秘的系统内部文件”,它们本质上就是普通的文本,只不过格式固定、系统启动时就会读取。
第一个是/etc/passwd。每一行代表一个用户账号,字段之间用冒号分隔。我拆开讲一下:
用户名:密码占位符:UID:GID:描述信息:家目录:登录Shell看到那个x或者*了吗?那就是密码占位符,真正的密码哈希值根本不在这里,而是放在了/etc/shadow。这个设计是历史包袱也是安全进步——早期系统真的把加密后的密码放在/etc/passwd里,但该文件需要被所有进程读取(比如ls -l要显示文件名属主),如果密码哈希也藏在这里,等于把保险柜钥匙贴在了保险柜门上。所以后来把密码挪到了只有管理员可读的/etc/shadow。
第二个是/etc/group。格式如下:
组名:组密码占位符:GID:组成员列表组密码目前实际场景使用极少,但需要知道它的存在。成员列表用逗号分隔,注意这里有个细节:如果某个用户的主组就是当前组,那么/etc/group里的成员列表通常不会包含他,因为主组的归属关系在/etc/passwd的 GID 字段里已经写清楚了。副组(附加组)才会出现在这里的成员列表中。
第三个是/etc/shadow,里面存的是真正意义上的密码哈希、最近修改时间、密码有效期、失效警告天数、宽限天数、账号失效日期、保留字段。这个文件属于可读性极差、但字段非常关键的典型。平时排查用户无法登录的问题,八成要回到这个文件里找答案。
2.2 UID 和 GID 的分配逻辑,理解编号才能理解权限
UID 和 GID 是用户与组在系统内部的唯一编号。Linux 内核在处理权限时只关心数字,不关系名。你删掉一个用户之后如果直接新建一个同名用户,系统会分配一个全新的 UID,旧文件上的属主信息拿不到同名的新用户身上。
系统对 UID 的划分大致遵循以下区间,不同发行版略有差异:
- 0:管理员账号,拥有整个系统的绝对权限。
- 1-999:系统预留账号,例如运行服务时使用的 Nobody、守护进程账号等。
- 1000-65534:普通用户可用的 UID 区间。
- 65534:通常映射为 nobody 用户,用于匿名访问等无属主场景。
理解这个区间划分的意义在于:你在批量创建用户时,如果手动指定 UID,务必避开系统预留段。我见过有人图省事直接把用户 UID 指定为 600,结果该用户的进程恰好在某些场景下被系统服务误判成同一身份,引发隐蔽的权限越界问题。
还有一个很容易忽略的概念叫LOGIN_DEFS,它的位置在/etc/login.defs。这里定义了新建用户时 UID 的起始值、密码加密算法、有效期默认值等。很多管理员不知道改用户创建默认策略要动这个文件,结果每次新建用户都要手动追加一堆参数。
2.3 文件权限与属主:用户与组存在的最终意义
你可能会想:搞这么多用户和组,图什么?图的是最终落实到文件权限上。Linux 文件权限模型包含三段权限:属主权限、属组权限、其他用户权限,每段又可细分为读、写、执行。
执行ls -l时,你看到的第三列是属主,第四列是属组。内核判断一个进程能否访问某个文件,遵循一条简洁的规则:
如果进程有效的 UID 等于文件的属主 UID,应用属主权限段;否则如果进程有效的 GID 或任一附加组的 GID 等于文件的属组 GID,应用属组权限段;否则应用其他用户权限段。
这里有一个常见的认知偏差:以为“只要我是文件属组的一员,就能享受属组权限”。实际上内核只认 GID,如果你的身份在文件属组的 GID 里匹配不上,哪怕人类肉眼看不出区别,权限也一样不生效。所以排查权限问题时,第一件事是执行id命令确认当前用户到底属于哪些组,而不是盯着文件属性猜。
文件权限的第三段还有一个粘滞位(sticky bit),和用户管理强相关。比如/tmp目录就是典型的drwxrwxrwt模式,其中的t表示粘滞位。它的作用很简单:在这个目录下,只有文件属主、目录属主或管理员才能删除或重命名文件,其他用户即使对该目录拥有写权限,也不能动手删除别人的文件。这个机制在多用户共享目录的场景中非常关键,我后面会结合实操细讲。
3. 核心实操:用户与组增删改查的常用命令与底层行为
3.1 创建用户,别只知道一个 useradd
用户创建最核心的命令当然是useradd,但这背后有一连串默认行为值得你逐条核实。不同发行版的useradd默认值差异很大。
先看最基本的创建动作:
useradd dev01执行完之后发生了什么?/etc/passwd增加一行,/etc/shadow增加一行,/home/dev01目录被创建,/etc/skel里的骨架文件被复制进去,同时一个与用户名同名的组也被建立。
这里最容易被忽略的是/etc/skel目录。你希望每个新用户的家目录里自带某些公共文件,比如.bashrc的定制版本、公司统一的 README 说明文件,直接把文件丢进/etc/skel即可。我习惯把所有新用户都需要的基础配置放在里面,省去后续逐个推送给用户的麻烦。
创建用户时比较实用的参数组合:
useradd -m -d /export/home/dev01 -s /bin/bash -c "开发一组-张同学" dev01-m:强制创建家目录,即使系统默认值不允许也会创建。-d:指定家目录路径,不再用默认的/home/用户名。-s:指定登录 Shell。如果设为/sbin/nologin,用户无法直接登录,常用于服务账号。-c:注释信息,建议写清楚人员身份或联系方式,方便后续维护。
为什么不建议不指定-m?因为部分发行版在创建系统用户时默认不建家目录,如果你误建了一个需要家目录的用户,后续补目录还要手工设置权限和骨架文件,平白多出一堆操作。
3.2 修改与删除用户:usermod 和 userdel 的边界问题
修改用户属性时,usermod是最顺手的工具。常见操作有:
usermod -aG docker dev01 usermod -s /bin/zsh dev01 usermod -L dev01 usermod -U dev01其中-aG两个参数必须一起使用。-G表示设置附加组列表,如果不加-a,系统会用新列表整体覆盖旧的附加组列表。我见过不止一次有人执行usermod -G docker dev01,结果用户原本在sudo组里的身份直接被摘掉,排查了半天才发现是覆盖问题。习惯性把-aG绑定在一起想,能避免大量低级的权限事故。
删除用户时,userdel默认不删除家目录和邮件池。如果你确定要连人带目录一起清理:
userdel -r dev01这里要特别提醒:删用户不等于删干净。用户的主组如果没有被任何文件引用,会遗留为“孤儿组”;用户曾经创建的目录和文件如果不在家目录里,也会变成 UID 对应的无名文件。后面第五部分专门讲这个问题。
3.3 用户组的管理命令和联动关系
组管理的命令比较简洁:
groupadd ops groupdel ops groupmod -n newname ops但有一个非常关键的细节容易被忽视:主组不能直接删除。如果某个用户当前的登录主组(/etc/passwd里设置的 GID)是某个组,直接执行groupdel会报错。你得先修改用户的 GID 主组,或者删除该用户,再执行组删除。
此外,当一个组被删除时,系统不会自动把该组曾经拥有的文件权限回收掉。那些文件的属组 GID 依然存在,但/etc/group中已经没有对应的组名了。你用ls -l查看时,会看到该文件属组变成一个数字 GID。这个数字如果不巧被后来新建的某个组占用,那么新组会自动获得这些文件的属组权限,隐蔽性极强,也极具风险。我在生产环境清理历史遗留文件时,专门用一条命令排查这类数字属主:
find / -nouser -o -nogroup 2>/dev/null3.4 密码管理:passwd 和 chage 联合使用
用户创建后第一件事就是设密码:
passwd dev01但密码安全除了“设置时足够强”,还要关注有效期。chage命令专门管理密码老化策略:
chage -M 90 dev01 chage -W 7 dev01 chage -E 2025-12-31 dev01-M 90:密码最长使用 90 天。-W 7:提前 7 天开始警告。-E 2025-12-31:账号在指定日期失效,适用于临时外包人员、学生账号。
如果想查看一个用户的完整密码策略:
chage -l dev01这个命令输出的信息包括上次修改时间、密码过期时间、账号失效时间等,排错时非常有价值。我遇到过用户的密码明明还没到期,但就是无法登录的情况,最后用chage -l一查,发现是管理员之前手动把账号失效日期设置在了昨天。
4. 实战:从零搭建一套多用户项目环境
接下来我提供一个可以直接照着操作的多用户环境搭建流程。场景设定是这样的:一个小型项目组,里面有四名开发人员、一名运维、两名外部协作者,外加一台跑服务的机器。所有人员共用一台 Linux 服务器,需要做到权限隔离、按组授权、临时账号到期自动失效。
4.1 用户与组结构规划
动手之前先画好规划,这是很多人跳过的步骤,但恰恰最重要。我的划分方式如下:
- 开发主组:devteam,包含四名开发人员,权限范围是
/data/project下的代码读写。 - 运维组:ops,包含一名运维,权限范围包括日志目录和部署目录。
- 外部协作者:新建一个组
external,限制他们只能访问/data/shared目录中明确开放的文件。 - 服务账号:新建一个系统用户
svc_app,没有登录 Shell,只用于跑后台进程。
汇总成一张规划表:
| 组名 | 用途 | 成员 | 家目录级别 |
|---|---|---|---|
| devteam | 开发主组 | dev01-dev04 | /data/project |
| ops | 运维人员 | ops01 | /data/logs 与部署脚本目录 |
| external | 外部协作者 | ext01-ext02 | /data/shared |
| svc_app | 服务运行账号 | 无 | 不需要家目录 |
4.2 创建用户与组的完整命令序列
按照规划逐步执行,我先创建组,再创建用户,因为用户创建时可以直接指定主组。
groupadd devteam groupadd ops groupadd external创建四名开发人员。这里我用统一的格式,描述信息写上人员简称,方便后续识别:
useradd -m -d /home/dev01 -g devteam -s /bin/bash -c "dev01-张三" dev01 useradd -m -d /home/dev02 -g devteam -s /bin/bash -c "dev02-李四" dev02 useradd -m -d /home/dev03 -g devteam -s /bin/bash -c "dev03-王五" dev03 useradd -m -d /home/dev04 -g devteam -s /bin/bash -c "dev04-赵六" dev04给运维人员创建账号,并附加到 devteam 组方便临时协助排查代码问题:
useradd -m -d /home/ops01 -g ops -s /bin/bash -c "ops01-运维端" ops01 usermod -aG devteam ops01外部协作者用-e指定账号失效日期,这里设定为 90 天后:
useradd -m -d /home/ext01 -g external -s /bin/bash -e 2025-11-30 -c "ext01-外包A" ext01 useradd -m -d /home/ext02 -g external -s /bin/bash -e 2025-11-30 -c "ext02-外包B" ext02服务账号不创建家目录,Shell 设为禁止登录:
useradd -r -s /sbin/nologin -c "应用服务账号" svc_app-r表示创建系统账号,UID 会自动落在系统区间,不会占用普通用户名额。这一步对强迫症很友好。
执行完上面的命令后,用id dev01和id ops01验证身份归属,再检查/etc/passwd末尾五行和/etc/group的内容是否与规划一致。
4.3 目录权限划分与粘滞位应用
账号建好后,开始设置目录权限。首先创建项目根目录:
mkdir -p /data/project /data/logs /data/shared设置目录属组:
chgrp -R devteam /data/project chgrp -R ops /data/logs chgrp -R external /data/shared设置目录权限:
chmod -R ug+rwX,o-rwx /data/project chmod -R ug+rwX,o-rwx /data/logs chmod -R ug+rwX,o-rwx /data/sharedug+rwX里的X是特殊参数,表示只有对目录或已有执行权限的文件才赋予执行权限,纯文本文件不会被误加执行位。这是我个人最推荐的一种授权方式,兼顾功能与卫生。
外部协作者存在一种特殊需求:希望彼此能在共享目录下互相查看文件,但不允许删除其他人的文件。这就是前面提到的粘滞位应用的典型场景:
chmod +t /data/shared设置完成后,ls -ld /data/shared可以看到类似drwxrwxrwt的输出。验证一下效果:外部协作者 ext01 在/data/shared下创建一个文件,再用 ext02 去删除,系统会拒绝,提示没有权限。这个机制保证了共享目录的稳定性,避免有人手滑删了别人的工作成果。
4.4 赋予部分用户 sudo 权限
开发人员经常需要执行一些需要管理员权限的命令,比如安装依赖包、重启服务等。不建议直接给管理员密码,也不建议全给 sudo,正确做法是给有需要的人加入一个 sudo 授权组。
在大多数主流 Linux 发行版上,属于wheel或sudo组的用户默认具备管理员提权能力。以sudo组为例:
usermod -aG sudo dev01 usermod -aG sudo ops01验证方法:
sudo -l -U dev01输出中如果列出了规则,说明这个用户可以在本机执行 sudo。出于最小化原则,外部协作者不要加入任何 sudo 相关的组。
4.5 初始密码与首次登录强制改密
在新环境里,一个标准操作是让用户首次登录后必须改密码。实现方式:
chage -d 0 dev01-d 0表示把密码最近修改时间设为 1970-01-01,系统会认为密码已经过期,强制用户在下次登录时立即修改新密码。这个机制比“手动告诉用户改密码”可靠得多,它可以防止用户在不知道自己密码的情况下长期使用已知初始密码。
批量执行可以写一个循环:
for u in dev01 dev02 dev03 dev04 ops01; do chage -d 0 $u done这条循环把 5 个用户全部设为首次登录强制改密。从安全角度看,这比让用户带着初始密码用一个月强太多。
5. 常见问题与排查技巧实录
5.1 用户被锁定:到底是谁动了密码文件
现象:用户反映无法登录,系统提示密码错误次数过多或账号已锁定。
排查路径分两条:
第一步,检查账号是否被passwd -L锁定。执行passwd -S dev01查看状态,如果输出中显示L开头,说明被锁。解锁:
usermod -U dev01第二步,查看/etc/shadow中该用户的密码哈希前缀。密码哈希字段如果以!开头,表示该账号的密码处于锁定状态。很多发行版在用户创建但未设置密码时,写入的占位符就是!,此时登录是不可能成功的。
第二种原因是多次输错密码触发了pam_faillock或类似机制的锁定。这类锁定的排查要看日志,常见位置是/var/log/secure或/var/log/auth.log。定位到具体条目后,可以用系统命令重置失败计数,不同发行版略有区别,整体思路是删除或重置该用户的失败记录。
我特别想提醒:不要动不动就“重置密码”来解决问题。先明确是哪种锁定,如果是安全策略触发的临时锁定,重置密码并不能解决根本问题,用户继续输错旧密码照样会被再次锁定。
5.2 删除用户后留下一堆数字属主文件
场景:删除某个离职员工的账号后,发现/data下有大量文件属主显示为一串 UID 数字。
排查命令:
find /data -nouser -o -nogroup 2>/dev/null输出结果会列出所有找不到属主或属组的文件。处理方式有三种:
第一种,如果确认这些文件已经无用,直接清理:
find /data -nouser -o -nogroup -delete 2>/dev/null第二种,如果文件还有价值,希望转移到某个现有用户手中:
find /data -nouser -exec chown dev01:devteam {} \;第三种,如果历史遗留太多,需要先盘点再决定,建议先导出列表归档,再逐个处理。最怕的就是不管不顾直接删掉整个/data目录,连根拔起容易误删有保留价值的备份。
这个问题的根源在于:用户删除后,系统不会主动巡视文件系统寻找该用户遗留的文件。所以在删除用户前,我习惯先执行一遍find / -user 用户名摸底,把所有归属文件列出来,再决定这些文件是转交、归档还是销毁。
5.3 UID 冲突:一个新用户继承了旧人的文件
反向场景比上一个更隐蔽:删掉用户 A 之后,系统新建用户 B 时自动分配了一个曾经被别人用过的 UID,于是 B 用户直接继承了 A 留下的所有文件权限。
Linux 内核不区分“这个 UID 以前属于谁”,它只看数字。因此一个重要的实操原则是:在定期清理用户前,把旧用户的 UID 号记录下来,后续新建用户时要么刻意避开,要么在创建后立即检查属主归属。
批量创建用户时,建议手动指定起始 UID:
useradd -u 2001 dev01 useradd -u 2002 dev02这样可以把用户和 UID 的对应关系牢牢掌握在自己手里,避免系统自动分配造成数字漂移。个人经验是:高于 6000 的 UID 段常年处于“合规检查”范畴,很多企业内部规范甚至会限制 UID 不能超过某个阈值,提前规划数值段位能减少很多合规层面的麻烦。
5.4 组权限不生效,明明用户已经在组里
这个问题的经典程度不亚于“重启解决 80% 问题”。用户说:我已经被加进 devteam 组了,但访问/data/project还是报权限不足。
第一反应不是改目录权限,而是让用户重新登录一次。附加组的变更不会动态应用到当前已登录的会话进程。用户必须注销重新登录,或者如果只是不想退出,可以执行:
newgrp devteamnewgrp命令会启动一个指定主组的新 shell,临时切换会话身份。但这个办法只影响当前终端,其他已登录会话依然保留旧组信息。
如果重新登录后仍然不行,执行id确认实际生效的 GID 列表:
id dev01输出中会列出uid=、gid=、groups=三部分。如果 groups 列表里没有 devteam,说明组加晚了,当前会话还在用旧身份;如果 groups 里能看到 devteam,那就需要检查目录权限本身,用namei -l /data/project逐级排查每一层目录的权限设置。这个命令会把路径上所有层级目录的权限完整列出来,方便定位是哪一级卡住了。很多权限问题其实不是最终的目录/文件本身权限不对,而是中间某一层目录缺少执行权限,导致路径根本无法穿透。
5.5 账号到期与密码过期混淆
外部协作者反馈“账号过期了”,但查下来发现是密码过期,不是账号失效。
密码过期意味着用户可以正常登录,但登录过程中系统强制要求修改密码;账号失效则直接拒绝登录,即使输入正确的密码也没有用。两种现象区别明显:
- 密码过期:输入正确密码后,系统提示需要设置新密码。
- 账号失效:输入正确密码后,直接提示账号已失效。
排查工具还是那两个:
chage -l ext01 chage -E -1 ext01-E -1用来清除账号失效日期,适用于“上个月该账号过期了,但这个月续签了所以需要恢复”的场景。在管理外部协作者时,我已习惯于把每个到期账号的失效日期统一维护在一张清单里,每次续签后随手更新清单,比靠脑子记忆靠谱得多。
5.6 无法删除用户:进程占用与主组残留
执行userdel时提示无法删除用户的邮件池或家目录,常见原因是该用户仍有进程在运行。
排查:
pgrep -u dev01如果有进程输出,先评估这些进程是否可以被终止。确认无误后:
pkill -u dev01 userdel -r dev01还有一种情况是主组残留。若用户的主组被设置成其他组的 GID,并且该组下还有其他成员,userdel可能不会提示错误,但逻辑上需要你关注是否要手动清理空组,或者把一个组的成员全部移出后再删除。这个细节属于低频率但高干扰的问题,遇到了就需要耐心检查ps -ef的输出,别急着强制-f删除。
6. 将用户管理整合进日常运维习惯
配置一次用户和组不难,难的是后续长期维护不出乱子。这里分享几个我已经稳定使用多年的习惯,希望你能少走弯路。
第一,把所有用户和组变更操作纳入变更记录。无论是新建账号、修改附加组,还是删除用户,都在内网记录里留痕。一次操作的代价很小,但当你三个月后回看这个账号为什么存在时,这条记录能帮你节省一整天的排查时间。
第二,定期检查孤儿文件和数字属主。我一般每个月跑一次:
find / -xdev -nouser -o -nogroup 2>/dev/null把结果和自己的变更记录比对,能及时发现是谁删除了账号但没清理数据,或者是谁迁移数据时搞乱了属主。
第三,谨慎使用递归 chown 和 chmod。chown -R dev01:devteam /data/project是很直观的一条命令,但如果你在/data级别误操作,整个目录树的所有权都会被牵连。我的习惯是先备份当前属主关系,再动手修改:
find /data/project -printf '%u:%g %p\n' > /tmp/ownership_backup.txt第四,给用户设置合理的 shell 与资源限制。普通开发人员完全不需要/sbin/nologin,但后台服务账号必须用 nologin;交互用户建议叠加资源限制,防止某个失控脚本拖垮整机。在/etc/security/limits.conf里写明白每个组的进程数限制和 CPU 时间限制,多用户环境里非常有价值。
回到开头那句话:用户与组管理看似基础,却是所有权限体系的地基。地基打得稳,后面的权限审计、等保合规、故障追责都会轻松很多;地基上一旦留下烂账,迟早要花更大的代价去填坑。如果你刚接触这块,别急着追求复杂方案,先把用户、组、权限三层关系理清楚,用id、namei、find这三个命令玩熟,你就能比大多数只会用管理员账号的人强出一大截。