☰
Linux本地用户与组管理:从UID/GID到权限控制实战
2026/10/14 13:45:29 网站建设 项目流程

接手一台 Linux 服务器,第一件事是什么?我见过不少新手直接 root 一把梭,平时自己折腾没问题,可一旦这台机器要交给三个人、五个项目组共用,问题马上就会冒出来:谁改了配置、谁的进程吃满了内存、为什么别人删了我建的文件。这些麻烦的背后,几乎都能归结到一件事——本地用户和组的管理没打牢地基。这篇内容不讲什么玄乎的高深架构,就把 Linux 本地账户体系从头到尾掰开揉碎过一遍,从配置文件到底层逻辑,从常用命令到真实排障,争取让你看完之后,遇到用户相关的问题不再靠猜。

1. 先想清楚:用户和组到底在解决什么问题

1.1 身份、权限与审计:一次登录背后藏着三件事

很多人以为用户管理就是“加个人、设个密码、能登录就行”,但实际生产环境里,用户体系承担的是三件完全不同的事:身份识别、权限控制和操作审计。

身份识别好理解,系统得知道“你是谁”,才能决定给你什么待遇。这个“你是谁”不是你的名字,而是系统内部的一个数字标识——UID。用户名只是给人看的,系统真正认的是 UID。权限控制则是围绕 UID/GID 展开的,文件有属主、属组,进程有发起者,内核在做任何许可判断时,看的都是数字 ID 而不是字符串。至于审计,就是当系统出问题时,你翻日志,得能回答“哪个用户、哪个进程、在什么时间干了什么”。如果没有规范的用户管理,日志里全是用户名乱飞、UID 被复用,追责和排查会变成灾难。

这里想强调一个很多教程不会细说的点:root 的 UID 永远是 0,这一点不可动摇。系统对 UID 0 有特殊信任,内核里大量权限判断直接硬编码“如果 uid 等于 0,那就放行”。所以后续你看到任何“给某个用户提权”的操作,本质上都是在想办法让进程以 UID 0 运行,比如 sudo 和 su 都是这个逻辑。

1.2 组的价值:为什么不能只靠单个用户

组这个概念新手往往重视不够,总觉得“反正我建用户就行,权限不够就 chmod 777”。但现实场景里,组几乎是批量授权的基础设施。

举个实际例子:某公司有三个项目,每个项目有五个人协作,共享一个项目目录。如果你基于单个用户授权,每加一个新人,就要把这个目录的权限分别给这个人设置一遍;每走一个人,又得清理一遍。而如果用组,整个流程就变成:创建项目组,把目录属组改成项目组,设置好组权限,然后把人员加进组就完事。后续人员变动只需要动组的成员关系,目录权限完全不用碰。

组还有一个隐藏功能:它可以承载权限的中间层。比如 backup 组可以读取所有用户家目录,docker 组可以调用 docker 命令,sudo 组可以直接提权。这些都不是靠单个用户实现的,而是通过把用户放进特定组,让组去对应具体能力。理解了这个思路,你再看系统里那些默认组,比如 wheel、adm、systemd-journal,就会有豁然开朗的感觉。

另外,本地用户和网络账户(LDAP、NSS 等)的底层逻辑其实是同构的,你先把本地用户和组搞明白,将来接统一认证体系,会发现概念完全一致,只是数据源的接入方式变了。这也是为什么基础必须要扎实。

2. 账户背后那几张表:配置文件看懂一半

2.1 /etc/passwd 不是密码文件

很多新手听到 passwd 文件名,第一反应是“这是存密码的吧”,然后试图直接改这个文件去重置密码——这是一个比较危险的误解。

/etc/passwd 每一行代表一个用户,用冒号分成 7 个字段,我顺手拆一行给你看:

devops:x:1001:1001:DevOps Engineer:/home/devops:/bin/bash
  • 用户名:devops
  • 密码占位符:x,真正的密码根本不在这里
  • UID:1001
  • GID:1001(这是用户主组的 ID)
  • 注释信息:DevOps Engineer,可以用 useradd 的 -c 参数写入,一般用来存姓名或角色
  • 家目录:/home/devops
  • 登录 Shell:/bin/bash

所以这个文件实际是“用户信息表”,密码字段用一个 x 占位,只是为了告诉系统“去 /etc/shadow 里找真正的密码”。这个设计是有历史原因的:当年影子密码机制引入之前,加密后的密码哈希确实直接存在这个文件里,但因为这个文件所有用户可读,哈希很容易被拖走做离线破解,后来才把密码挪到了只有 root 能读的 shadow 文件里。

另外注意一点:passwd 文件权限是 644,任何人都能读,这是正常的。你如果想判断一个系统里有哪些用户,直接 cat 这个文件就行。

2.2 /etc/shadow 才是真正的密码保险箱

/etc/shadow 的权限默认是 000 或 600,只有 root 能读能写。每一行同样冒号分隔,字段比 passwd 多不少,核心是这一段:

devops:$6$rounds=656000$abc...def:18945:0:99999:7:30:19000:

从左到右分别是:用户名、加密后的密码哈希、最近一次修改密码距 1970-01-01 的天数、两次修改密码的最小间隔天数、密码最大有效天数、过期前警告天数、密码失效宽限天数、账户过期时间、保留字段。

这个文件有两点新手经常会栽跟头。第一,密码哈希字段如果以 ! 或 !! 开头,说明这个账户的密码是锁定状态,直接登录不了,但账户本身还存在。你执行 passwd -l 锁定用户,实际就是在这个哈希前加个感叹号。第二,最后一个冒号后面如果有一个日期数字,代表账户到期时间,这个用 chage -E 可以设置。我曾见过有人排查“用户为什么突然登不上”,查了半天 shadow,发现是账户过期日期到了,和密码本身一点关系都没有。

2.3 /etc/group 与 gshadow:组的另一半

/etc/group 存组信息,一行四个字段:组名、组密码占位、GID、组成员列表(逗号分隔)。比如:

devops:x:1001:zhangsan,lisi,wangwu

这里有个容易混淆的点:组成员列表里的成员是“附加组成员”,不包括把该组作为主组的用户。也就是说,zhangsan 的主组可能是 staff,但他同时被加进了 devops 这个附加组。你用 id 命令查看一个用户时,会看到 gid 和 groups 两部分,前者是主组,后者是所有生效的组。

/etc/gshadow 对应的是组密码和管理员列表,日常场景用得不多,但需要知道它的存在和作用:组密码用于让非成员临时通过 newgrp 进入组,组管理员可以维护组成员。绝大多数时候,这两个功能用不到,但碰到那种历史遗留的“莫名多出来的组”,可以到这里找线索。

2.4 UID/GID 分配规则为什么要管

系统对 UID 的划分是有讲究的:0 属于 root,1-999 一般预留给系统账户和服务的伪用户,1000 以上才是普通用户。这个范围其实由 /etc/login.defs 里的 UID_MIN 和 UID_MAX 控制。系统账户是运行服务用的,比如 nginx、mysql、sshd,它们不需要登录,也不需要家目录,给低 UID 是为了和普通用户区分开。

这个规则看起来简单,但实际运维中一旦乱来就会出问题。举个例子:如果你手动指定一个系统服务账户为 UID 1005,然后又在 useradd 时随意给普通用户分配了同一个 UID,文件属主就会互相串。更危险的是,如果你不小心把某个普通用户设成 UID 0,这等于给了它和 root 一样的身份,直接在系统里横着走。所以我的建议是:普通用户创建永远不要手动指定 UID,交给系统自动分配;真要指定,先检查这个 UID 有没有被占。

3. 实操:创建、修改、删除用户和组的完整流程

3.1 创建用户前先看默认值

很多教程一上来就让你敲 useradd xxx,但实际动手之前我建议先看一眼默认配置,否则你根本不知道这个命令背后默认做了什么。

重点看两个文件:

cat /etc/default/useradd cat /etc/login.defs

/login.defs 里有几项直接决定新用户的行为:CREATE_HOME(是否默认创建家目录)、UMASK(家目录权限掩码)、PASS_MAX_DAYS(密码最长有效期)、UID_MIN/UID_MAX(自动分配 UID 范围)、GID_MIN/GID_MAX、USERGROUPS_ENAB(是否创建与用户同名的组)。

这里要特别提一下 USERGROUPS_ENAB 这个开关,很多发行版默认是 yes。它的含义是:如果创建用户时没有指定主组,系统会创建一个和用户名同名的组,并把该组设成用户的主组。这就解释了为什么你建一个 devops 用户,会自动出现一个 devops 组。这个机制本身没毛病,但如果你的环境里希望所有普通用户统一放进一个 users 组,就需要在 useradd 时显式用 -g 指定,或者调整 login.defs 配置。

3.2 useradd 实战:一个合理的默认配置

先来看一条完整的新用户创建命令:

useradd -m -d /home/devops -s /bin/bash -c "DevOps Engineer" -u 1001 -g 1001 -G docker,sudo devops
  • -m:创建家目录,并复制 /etc/skel 下的默认文件进去
  • -d:指定家目录路径,默认是 /home/用户名
  • -s:指定登录 Shell,/bin/bash 适合交互式用户;如果是纯服务账户,应该用 /sbin/nologin
  • -c:写入注释信息
  • -u:手动指定 UID
  • -g:指定主组(可以用组名或 GID)
  • -G:指定附加组,可多个,逗号分隔

家目录之所以会带出一堆隐藏文件,是因为系统在创建时会把 /etc/skel 里的内容复制过去。这是一个非常实用的定制点:比如你希望所有新用户的 .bashrc 里默认加载某个环境变量,或者统一放一个欢迎提示文件,直接改 /etc/skel/.bashrc 就行,之后所有新用户都会自动带上。

创建之后,下一步马上设置密码:

passwd devops

注意:useradd 并不会提示设置密码,新用户在没有密码之前是无法登录的。如果你在脚本里批量创建用户,可以这样用 chpasswd 一行搞定:

echo "devops:123456" | chpasswd

不过生产环境最好不要把明文密码写在命令行里,用管道从标准输入传入是相对可控的方式,再配合密码策略后续做强制修改。

3.3 usermod 的常见用法:改属组、锁账户、改 Shell

usermod 是日常改动最常用的命令,但也是踩坑率最高的命令之一。最容易出的问题在 -G 参数上。

看这个例子:你想把 devops 加进 docker 组。很多新手会写:

usermod -G docker devops

这条命令实际会把 devops 从原来的所有附加组里移出去,只保留 docker 一个。如果 devops 原来还在 sudo 组里,执行之后 sudo 权限就没了。正确写法是:

usermod -aG docker devops

-a 表示 append(追加),不加就等于重置。我把这条规则列为“必须刻在脑子里”的优先级,因为它造成的权限丢失往往不是立刻暴露的,可能过几周才发现某个人 sudo 用不了,到时候排查链路很长。

其他常用操作:

usermod -s /sbin/nologin devops # 禁止登录 usermod -L devops # 锁定密码(等价于 passwd -l) usermod -U devops # 解锁密码 usermod -d /new/home devops # 修改家目录路径

修改家目录路径后,文件并不会自动搬过去,你需要手动移动,否则下次登录可能进到一个不存在的目录,Shell 会报错或直接退到根目录。

3.4 组操作三件套:groupadd / groupmod / groupdel

组的管理命令相对简单,但有几个细节值得一说。

groupadd project_a # 创建组 groupadd -g 2001 project_a # 指定 GID 创建组 groupmod -n project_dev project_a # 改组名 groupdel project_a # 删除组

删除组之前,系统会检查有没有用户的主组指向它。如果有,groupdel 会报错并拒绝删除。这是好事,防止你把某个用户的主组删掉导致用户变成“无组用户”。如果你想删除,只能先把用户的 -g 主组改到其他组,或者先删除用户。

还有一个容易被忽略的点:gpasswd 这个命令用来维护组成员。它比直接编辑 /etc/group 安全得多,尤其是需要让非 root 管理员也能操作组成员时。常见用法:

gpasswd -a devops docker # 把用户 devops 加入 docker 组 gpasswd -d devops docker # 从 docker 组移除 gpasswd -A devops project_a # 指定组管理员

3.5 删除用户的注意事项

删除用户,命令只有一行,但清理工作不止一行。不要上来就 userdel 一把梭。先看看这个用户还有没有正在跑的进程,有没有文件遗留。

最温和的做法是:

userdel devops

这个命令默认只删除 passwd/shadow 里的账号记录,不删除家目录、邮件池等相关数据。如果确认要连家目录一起删,加 -r:

userdel -r devops

生产环境我一般不建议马上删除,尤其是离职人员,建议先锁账户、改 Shell 为 nologin、保留数据和日志一段时间,确认没有未完成的定时任务、没有在跑的进程、没有文件占用之后,再执行清理。直接删除会带来一个麻烦:那个用户曾经拥有的文件虽然显示为数字 UID,但如果你后续新建用户恰好复用了同样的 UID,这些文件就变成新用户的了。这在合规审计里是比较忌讳的。

4. 用户和组怎么跟权限、进程联动

4.1 文件权限字段里藏着两个 ID 的含义

你执行 ls -l,看到类似:

drwxr-xr-x 2 devops devops 4096 Mar 10 10:00 project

第三个字段是属主,第四个字段是属组。内核判断你能不能读、写、执行这个文件,规则是这样的:先看进程的 UID 是否等于文件的 UID,等于就套用 owner 权限位;否则看进程的 GID 或附加组里是否有文件属组,有就套用 group 权限位;再否则套用 other 权限位。注意顺序是严格优先级的,owner 匹配了就不看 group。

这带出一个常见问题:为什么我把用户加进了某个组,他还是无法访问该组的文件?原因很多,最常见的是“改了组没重新登录”。会话里的附加组信息是登录时固定的,后面无论你怎么修改组成员关系,已经打开的会话都不会自动刷新。这个问题下面单独展开,因为它值得一个专门的小节。

4.2 进程的有效身份:为什么有时候文件权限看着没问题却打不开

Linux 里进程实际有两个身份:真实 UID 和有效 UID。普通场景下两者一样,但涉及 setuid 程序,比如 passwd、sudo,它们运行时有效 UID 会是 0,这样才有权限去改 /etc/shadow。这个概念在排查“明明我是属主,还执行不了”之类的问题时很有用。

组维度也一样,进程有主 GID 和一组附加 GID。主 GID 通常来自 /etc/passwd 的第四字段,附加 GID 来自 /etc/group 里该用户所属的所有组。内核在检查文件属组权限时,主 GID 和附加 GID 都会被纳入考虑。所以你看到文件属组是 docker,用户主组不是 docker 但附加组里有 docker,他同样能访问,这就是附加组的实际意义。

4.3 为什么新加入的组不生效:一个小问题背后的机制

这是实际运维里高频出现的问题之一。某人被加进了某个组,但他自己 id 一看,新组没出现;或者出现了,访问文件还是 Permission denied。

先说 id 能看到新组但访问不了,十有八九是登录会话没更新。因为附加组的列表是在登录时通过 initgroups 机制一次性加载的,之后你对 /etc/group 的任何修改,都不会实时推送到现有会话。解决办法最直接的就是重新登录,或者执行 newgrp 切换:

newgrp dockergroup

这条命令会以新的组身份启动一个新 Shell,适合临时处理文件时用。如果是在脚本里,可以用 sg 命令。这里顺便提醒一句:用 SSH 密钥登录的自动化任务,如果用户被加了新组,记得重启对应的 systemd 服务或定时任务,因为它们继承的是启动时的会话身份,而不是最新状态。

5. 常见问题与排查技巧实录

5.1 userdel 报错“用户正在被使用”怎么办

我们项目里曾经过来一次删除用户的操作,命令敲下去直接报错:

userdel: user devops is currently used by process 12345

这个报错说明该用户还有进程在跑。千万别用 kill -9 胡乱杀,先查清楚这个进程是什么:

ps -u devops -o pid,cmd

确认是用户的遗留进程,再终止它。如果这个进程其实很关键,只是暂时以这个用户身份跑着,就得先考虑迁移服务或改属主,再删用户。处理完进程之后还要检查定时任务、systemd 单元里的 User= 配置,否则即便删了用户,服务也可能以数字 UID 继续运行。

5.2 误删用户后一堆文件变成数字属主怎么办

我见过比较痛的一种情况:某管理员为了图省事,userdel -r 一条命令删掉了一个用户的整个家目录,结果发现这个用户在 /var/www、/opt、/data 等多个路径下还有重要数据。文件属主已经变成数字 UID,普通工具无法直接定位。

这时候可以这样处理:先确认那个 UID 是多少,然后全盘搜索:

find / -uid 1001 -ls 2>/dev/null

这条命令会列出所有属主为 1001 的文件和目录。拿到清单后,你可以决定把这些文件 chown 给另一个保留用户,或者重新创建 UID 1001 的新用户来接收。这里提个醒:重新创建用户时,即使你不指定 UID,系统自动分配也可能立刻复用 1001,这样文件就又“自动归属”给新用户了。如果你不想让新用户继承这些文件,就要手动指定一个新的 UID,并且在 login.defs 的 UID 分配区间里避开这个值。逻辑虽然简单,但现实中很多人没意识到 UID 复用带来的数据归属问题。

5.3 用户被锁定和 Shell 被禁用是两码事

排查登录失败时,最容易混淆的两件事:密码锁定和 Shell 禁用。

密码锁定表现为 shadow 哈希前缀有 !,或者passwd -S输出标记为 L。账户被锁时,即便输入正确密码也登录不了,但可以使用 sudo 或 su 切换到(前提是有权限)。Shell 禁用就是把登录 Shell 改成 /sbin/nologin 或 /bin/false,这种账户的网络登录会被直接拒绝,报错通常是“This account is currently not available”。

这两个经常同时存在,用 chage 或者 usermod 做操作时,要注意区分:解锁密码不代表能登录,改回 Shell 也不代表密码有效。我在排查问题时的顺序是:先看 shadow 密码哈希状态,再确认 Shell,最后查账户过期时间。按这个顺序走,能很快把登录链路里的障碍定位清楚。

5.4 几个极其有用的检查命令

  • id:看用户的所有 UID、GID 和附加组
  • getent passwd username / getent group groupname:从所有数据源(本地、LDAP 等)查询信息
  • who / w:当前登录用户
  • lastlog:所有用户最近一次登录时间
  • passwd -S username:查看密码状态
  • chage -l username:查看完整密码策略

配合使用示例:

id devops getent group sudo passwd -S devops chage -l devops

这些命令的优先级比直接 cat 文件高,因为它们走的是系统的 NSS 查询链,看到的是最终生效结果。尤其当你以后接入了 LDAP 或集中认证,cat /etc/passwd 会漏掉远程用户,getent 则不会。

最后分享一个我的工作习惯:每创建一个用户,我都会记录这个账号的用途、创建时间、谁申请的、加进了哪些附加组。小环境可能觉得没必要,但一旦机器多起来、人员流动频繁,这些记录就是排查问题的源头。用户管理没有多高深,真正拉开差距的,是处理边界情况时的判断力——比如 UID 会不会冲突、删除时要不要留数据、组权限会不会被误覆盖。把这些基础环节想通,你其实已经超过了很多只会敲命令的“熟练工”。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询