服务器刚拿到手,第一件事不是装环境也不是部署应用,而是把用户和组规划清楚。这个顺序一旦反了,后面会遇到各种莫名其妙的问题:某个进程跑起来权限不够、日志目录被同事改了属主、离职员工的家目录还挂在服务器上……这些事的根源,十有八九都在“一开始用户就建乱了”。
我做运维这些年,最深的体会就是:Linux的用户与组管理,看着简单,但它是整个系统权限模型的基石。如果你只停留在“会useradd加个用户”的程度,那很多问题其实还藏在看不到的地方。今天这篇就针对Linux用户与组管理,把从概念、配置文件到实际操作、踩坑记录完整过一遍。适合刚接触服务器管理的新手,也适合那些已经建过用户但没系统梳理过这套逻辑的运维、开发同行。
1. 为什么用户和组这么重要:先看懂权限模型
很多人学Linux会先接触命令,但真正遇到权限问题的时候才意识到:一切操作的本质都是“某一个用户以某个身份对文件或目录发起访问”。所以用户和组的意义不在“能不能登录系统”,而在于它的权限边界。
1.1 一切的起点是“身份”
Linux是一个多用户操作系统,多个用户可以同时存在、同时使用系统资源。但系统不可能让每个人随意访问所有文件,所以必须有一个机制来约束“谁能看、谁能改、谁能执行”。这个机制的第一层,就是给每个用户分配一个身份标识,也就是用户ID(UID)。内核判断你是什么身份,不看用户名,只看UID。用户名只是给人看的,UID才是给系统看的。这也解释了为什么你改用户名不会影响文件的归属,因为归属记录的是UID。
组也是同理。每个组有一个组ID(GID)。把多个用户塞进同一个组,本质上是让这批用户共享一个权限通道——某些文件或目录只要设置为对组开放权限,那么组里的所有成员都能获得对应的访问能力。这就是组存在的核心价值:批量授权。
1.2 最小权限原则:先想清楚再动手
在实际管理服务器时,我强烈建议在创建用户之前先做一个权限规划。比如这批用户是给开发环境用的还是生产环境?他们需要访问哪些目录?需要执行哪些命令?需不需要sudo权限?这些想清楚了再动手,能省掉后面一大半麻烦。
这个思路对应到操作系统层面,就是“最小权限原则”。给用户授提权能力时要保留,给用户开文件访问范围时也要收敛。我在实际项目X中遇到过一种典型场景:某测试团队申请了一台服务器,我直接给了所有人root口令。结果有一次某个新同事想清理磁盘,一条rm命令把另一个部门共享的备份目录删了。最后排查来排查去,问题就出在“一开始就给了超出需要的高权限”。后来我换成了按组授权、单独建运维账号的方式,同样的事再没发生过。
所以,凡是涉及新开用户、新建组、分配sudo的申请,先停下来说一句:这个账号到底要干什么?能干什么?不该干什么?想通这三个问题,再落到命令上。
2. 用户和组的核心分类与配置文件解读
Linux下面跟用户、组相关的文件就那么几个,网上资料很多,但真正能讲清楚每个字段含义的并不多。这块建议别跳过,因为后面所有所谓“奇怪的问题”,最后都能在这些文件里找到答案。
2.1 用户分类:从权限角度分三种
第一个是超级用户,也就是root。它的UID固定为0。root在系统里拥有绝对权限,可以绕过绝大多数的权限检查。这个账号我一般建议禁止直接SSH登录,平时都用普通用户加sudo的方式来做管理。
第二个是系统用户(或叫伪用户)。这类用户是给服务进程用的,比如nginx跑起来需要有一个身份,但这个身份不应该能登录系统也不该有家目录。系统用户的UID范围在不同发行版上略有差异,一般CentOS/RHEL是1到999,Ubuntu/Debian也是1到999。我们在创建服务账号的时候,要用参数明确指定这个用户是系统账号。
第三个是普通用户。UID一般从1000开始。这类用户是给人用的,有家目录、有登录shell,可以用来正常登录系统、运行命令。
这三种用户之间的区别,不能只看表面,更重要的是权限边界:root能干什么、系统用户通常“不需要在/etc/passwd里有可登录shell”、普通用户能登录但被限制在自己的授权范围里。把这些分清,很多权限问题的思路就会清晰很多。
2.2 四个核心配置文件的字段拆解
用户和组的信息不是存在某个数据库里,而是以文本文件的形态存在/etc目录下。这就是为什么很多老运维会直接改文件来操作账号——但不是每个场景都推荐直接改。
/etc/passwd:存储用户基本信息。每一行对应一个用户,用冒号分隔为7个字段。以一行示例来说明:
devops:x:1001:1001:运维组成员:/home/devops:/bin/bash从左到右分别是:用户名、密码占位符(真实的密码密文在/etc/shadow里,这里统一显示为x)、UID、GID、用户备注信息(GECOS字段,可以用finger命令查看)、家目录路径、登录shell。
注意最后一个字段,如果它是/sbin/nologin或/usr/sbin/nologin,说明这个用户不能登录系统。我之前建过不少服务账号,最后一栏没注意,后来就出现“服务启动失败/无法切换身份”之类的问题,因为有些服务要求执行shell不能是nologin,而有些安全策略又要求服务账号必须nologin,这个取舍要看具体服务需求。
/etc/shadow:存放加密后的密码和密码策略信息,只有root能读。一行一个用户,冒号分隔,主要字段包括:用户名、加密密码(或!表示锁定)、上次修改密码的时间(从1970-01-01开始算的天数)、密码最小修改间隔、最大有效天数、过期前提醒天数、宽限期、失效日期(留空表示永久)、保留字段。
这个文件是安全隐患的高发区。如果密码为!或!!开头,说明这个账号被锁定。很多时候用户说“密码是对的但登录不了”,我先去shadow里瞄一眼,十有八九是密码被锁了。
/etc/group:存储组信息。每组一行,冒号分隔为4个字段:组名、组密码占位符(一般用x)、GID、组成员列表(逗号分隔)。注意,这里的成员列表只包含“把该组作为附加组的用户”,不包含把该组作为基本组的用户。因此你会发现有些用户明明属于某组,但看这个组文件里却找不到他的名字——这很正常,因为他的“所属”是在passwd的第4个字段(GID)里体现的。
/etc/gshadow:组密码和组管理员信息,对应group文件的安全版本。使用频率很低,日常管理中几乎用不到组密码,但要知道它是干什么的,免得在遇到“明明在组里却切换不了组身份”这种问题时无从下手。
2.3 用户信息中的关键字段:UID/GID和家目录
有一个细节经常被忽略:用户的UID和GID并不要求必须一致,但在单用户的常规场景下,经验做法是让它们保持一致。比如UID1001的用户的默认GID是1001,此时在这个用户的个人家目录里,他既是属主也是属组,操作起来最顺。
家目录权限一般默认是755或700。如果是755,同组的其他人能进入你的家目录但不能改内容;如果是700,除了自己其他人一律进不去。我一般建议个人用户家目录用700,因为有些人的目录里可能放密钥文件、脚本密码之类的敏感内容,755等于把家目录敞开给别人浏览了。
3. 用户管理实操:创建、调整、删除
现在进入干货环节。命令本身很简单,难的是理解参数背后的意图,以及根据场景选择正确的参数组合。
3.1 创建用户:useradd的几个关键参数
创建一个普通用户,最简单的写法是:
useradd zhangsan但只敲这一条命令,系统会按默认配置行动:创建一个家目录、复制/etc/skel下的模板文件、根据/etc/login.defs里的规则分配UID和GID。这种“裸创建”适合纯个人开发机,但在正式服务器上我习惯用更明确的参数:
useradd -u 1501 -g devteam -G docker,nginx -d /home/zhangsan -s /bin/bash -m zhangsan解释一下这些参数:
-u 1501:指定UID。为什么要指定?因为默认分配可能会在重复创建/删除用户时导致UID混乱,显式指定可以保证后续用批量化脚本管理时不会出错,而且有助于跨服务器保持同一账号的UID一致。-g devteam:指定基本组,可以是组名或GID。基本组决定用户默认的GID,被记录在/etc/passwd第4字段。-G docker,nginx:指定附加组,可以同时加入多个组,之间用逗号分隔。把用户放进附加组,是为了获得该组的访问权限。-d /home/zhangsan:指定家目录路径。如果不指定,默认是/home/用户名。-s /bin/bash:指定登录shell。-m:如果家目录不存在则自动创建,并复制/etc/skel里的dotfile。
还有一个我常用但不写在常规文档里的细节:创建系统用户时,用-r参数,例如:
useradd -r -s /usr/sbin/nologin -d /var/lib/myapp myapp-r会让系统自动分配一个系统UID(CentOS上一般是200-999之间,Ubuntu上是100-999之间),不会占用普通用户的UID段,也适合给服务进程创建专用账号。
3.2 调整用户:usermod和chage
创建完用户后,很多属性都能随时调整。usermod就是干这个的,参数跟useradd基本重叠。常见的场景有:
把用户加入新组,不改基本组:
usermod -aG docker zhangsan这里特别重要,-a是追加(append)的意思,和-G搭配使用。如果不加-a,用户会直接退出其他所有附加组,只保留你指定的组。我之前有一次给同事加docker组权限,就漏了-a,结果他的其他权限全没了,他一脸懵地找不到gitlab组权限。从此以后我给自己定了一个规矩:凡是修改附加组,一定写usermod -aG。
改用户登录shell:
usermod -s /bin/bash zhangsan锁定账户(不允许登录):
usermod -L zhangsan # 老写法,锁定密码 passwd -l zhangsan # 也可以 usermod -U zhangsan # 解锁这里要说明一下,usermod -L锁的是密码验证这一层,用户在锁定的情况下不能通过密码认证登录。但如果你之前给他配置了SSH公钥登录,那公钥登录可能仍然有效。所以真正要禁用整个账号的登录,更可靠的做法是把shell改成/sbin/nologin或者锁账户之外把authorized_keys也清理掉。
修改密码策略用chage:
chage -M 90 zhangsan # 密码90天后过期 chage -W 7 zhangsan # 过期前7天提醒 chage -d 0 zhangsan # 强制下次登录改密 chage -l zhangsan # 查看策略很多企业安全审计会要求每90天改一次密码,用chage就能批量实现。-d 0这个命令特别适合用于新建账号后第一轮登录:你可以给用户设一个临时密码,然后强制他下次登录必须换一个新密码。
3.3 删除用户:userdel以及遗留数据清理
删除用户也不是删完就好,要考虑数据清理问题。
userdel zhangsan # 只删账号,默认保留家目录和mail spool userdel -r zhangsan # 删除账号同时删家目录和mail spool userdel -f zhangsan # 强制删除,即使该用户还在登录中经验教训:在正式环境里,我通常不用-r,而是先保留家目录一段时间再手动处理。原因很简单:一位同事离职后,他的家目录里可能有交接文档、项目代码的本地副本、数据库备份等。一旦加了-r,数据就永远没了。在项目X里我遇到过一次,同事离职后管理员直接userdel -r,结果几天后发现客户需要他本地某份配置文件,整个团队翻磁盘回收历史都找不回来。后来我们改成“先锁账号,再保留家目录30天,确认无用后再清理”的流程。
还有一个小细节:手动删除用户后,不要立即复用同一个UID去创建新用户。因为有些文件的属主还记录着旧的UID,新账号会莫名其妙地“继承”那些文件的属主身份,这会产生越权风险。正确做法是删用户之前,先把以他UID为属主的文件搜索出来,统一归属或归档处理:
find /home /tmp /data -uid 1501 -exec ls -l {} \;确认没遗漏之后,再说复用UID的事。
3.4 批量创建用户:提高效率的正确姿势
一次性要创建几十个用户时,useradd命令一条条敲会敲到怀疑人生。我用的方式是准备一个用户清单文件,格式与/etc/passwd类似,然后结合newusers和chpasswd批量导入。比如:
先把用户信息写入一个临时文件users.txt:
zhangsan:x:1501:1501:张三:/home/zhangsan:/bin/bash lisi:x:1502:1502:李四:/home/lisi:/bin/bash然后执行:
newusers < users.txt这一步会根据文件创建用户和家目录(需要提前建好对应目录并设置权限)。接着写密码:
echo "zhangsan:InitialPass123" | chpasswd echo "lisi:InitialPass123" | chpasswd不想把这些密码留在历史记录里的话,可以写脚本从文件读取密码,用完即删。批量创建完以后,建议写一个循环脚本去校验一下所有用户是否都出现在/etc/passwd里、家目录权限是否正确,不要批量下去就直接放行。
4. 组管理实操:理解基本组与附加组
组管理命令比用户管理的命令少,但容易出问题的地方恰恰集中在对基本组和附加组的理解上。
4.1 创建和删除组:groupadd/groupdel
groupadd devteam # 创建普通组 groupadd -g 2500 ops # 指定GID创建 groupdel devteam # 删组这里要特别强调:如果一个组被某用户作为基本组使用,那这个组不能被直接删除。删除时会报“cannot remove the primary group of user”之类的错误。解决办法是先把用户的组改到其他组:
usermod -g newgroup zhangsan然后再删组。这个坑非常常见,很多新手明明跟老员工在同一组,结果老员工改了自己的基本组,导致全组相关的授权都失效了。所以删组前一定要确认没有用户还在用这个组作为基本组。
改名组:
groupmod -n new_name old_name同时也可以改GID,但改GID的连带影响跟改UID差不多——所有以旧GID归属的文件都会“变成”新GID组所有。所以慎重。
4.2 附加组:理解成员列表的写入位置
我把这部分单独拎出来说,因为它最容易混淆。看/etc/group时,最后一列的成员列表其实只在“把该组作为附加组”时才显示。如果一个用户的/etc/passwd里的GID指向这个组,他是“基本组成员”,但/etc/group的成员列表里不一定会出现他的名字。
实操上有一种典型场景:管理员想查看哪些用户属于某个组,他直接用cat /etc/group去看,结果发现成员列表是空的,就以为这个组没人用。实际上可能有一堆用户的基本组都是这个组。所以正确的查看方式是用getent group 组名,看输出里有没有用户,同时配合getent passwd核对。
getent group devteam getent passwd | awk -F: '$4==2500 {print $1}'两条命令配合起来,才能完整还原“哪些人属于这个组”。
4.3 临时切换组身份:newgrp和sg
某些场景下,用户当前不在某个组里,但临时需要以该组的身份创建文件。这时不用找管理员改组成员,用户可以自己用newgrp命令切换:
newgrp devteam执行后会开启一个新的登录shell,身份变为devteam组的成员,之后创建的文件属组就是devteam。退出这个新shell就回到原来的身份。
sg命令跟它类似,不过不需要切换整个shell,只以目标组身份运行单条命令:
sg devteam -c "touch /shared/test.txt"这个命令在自动化脚本里非常有用。不过前提是,你得是该组的成员或知道组密码,否则系统不会让你切换。组密码一般存放在/etc/gshadow里,这个功能多数情况下都被规避了,大家都用usermod -aG来管理成员关系,比设置组密码更直观也更安全。
4.4 组管理的最佳实践:怎么规划组最省心
根据这几年的经验,我倾向于按“业务域”来划分组,而不是按“部门”来划分。比如:
- web组:管web目录发布
- ops组:运维人员专用,给一部分sudo权限
- app组:应用部署目录所有者
- backup组:备份目录和备份脚本可执行权限
这类组的特点是权限边界相对清晰。如果单纯按部门建组,在部门之间职责重叠时反而会失控。比如“开发部”组既要去写代码目录又要去读日志,日志目录就得同时授权给开发组和运维组,那组与组之间的权限关系就会越来越散。按业务域分,一个组对应一种资源,后面做权限收敛好查好管。
5. 用户、组与文件权限的联动:授权与排查
管理用户和组的最终目的,都会落到文件权限上。这里不讲太深的权限机制,只讲几个能直接解决工作问题的联动操作。
5.1 掌握属主、属组和权限位
文件权限三元组的格式大家应该都熟:rwxr-xr-x。三组分别表示属主权限、属组权限、其他人权限。常见命令是:
chown zhangsan:devteam /data/app # 修改属主为zhangsan,属组为devteam chgrp devteam /data/app # 只修改属组 chmod 750 /data/app # 设置权限 chmod -R ... # 递归修改,但慎用有一点必须提醒:chmod -R在修改海量目录权限时很好用,但如果在包含大量可执行文件的目录上粗心地全部改成755或700,可能会把原本不该有执行权限的文件都变成可执行,安全审计的时候会被记一笔。所以递归修改权限前,先用find或ls -l看看目标目录的现状,评估影响范围。
5.2 粘滞位与共享目录的授权场景
团队共用一个目录时最烦的问题:A用户在里面创建了文件,B用户删不了,但也希望B能读,同时又不希望B能删A的东西。这就需要“组协作+粘滞位”的组合。
典型操作如下:
mkdir /data/shared chown root:devteam /data/shared chmod 2770 /data/shared # 2表示setgid位,会使新文件自动继承devteam组在这里,2770里的2是setgid位,它的作用是:在这个目录下新建的文件,属组自动变成目录的属组,也就是devteam。这样成员之间可以互相覆盖文件——因为同组共享同一写权限。
如果再加一个粘滞位(sticky bit,权限中最后一位为t),就变成了3770。粘滞位的作用是:目录里的文件只能被文件属主、目录属主或root删除/改名,其他同组人写但删不了别人的文件。典型例子是/tmp。这在共享合作目录里非常好用。
5.3 借sudo合理授权:给用户“开后门”要谨慎
sudo是日常授权高权限操作的窗口。我不建议直接在/etc/sudoers里给某个人单独配置一大串复杂规则,因为sudoers文件的语法很容易踩坑,一行配错,轻则sudo无法使用,重则可能所有用户都无法提权。
最简单的写法:
zhangsan ALL=(ALL) ALL意思是允许zhangsan在任何主机上以任何用户身份执行所有命令。这种写法方便,但明显违反最小权限原则。
我自己的习惯是,对运维团队的普通成员只在sudoers里开放必要的命令白名单,比如:
%ops ALL=(ALL) NOPASSWD: /usr/bin/systemctl, /usr/sbin/reboot这样既保证他能完成职责工作,又不至于获得无限权力。
接着还要注意sudoers的修改方式。我强烈建议用visudo命令编辑,因为它在保存退出前会做语法检查,避免写错断送sudo。如果你直接改文件然后格式乱了,后续sudo会直接报“sudo: /etc/sudoers is world writable”之类的错,甚至可能把sudo彻底搞坏。
还有一个有用的参数:timestamp_timeout。默认sudo密码记住时间是5分钟,过了5分钟又要重新输密码。如果希望密码记住时间短一点,可以在sudoers里Defaults timestamp_timeout=1,对于生产环境,我一般设成1分钟。不要为了省事把密码记住时间设成0或负数,每天重复输密码会让人很烦,反而容易敷衍乱设密码。
5.4 一个完整的授权实战场景
举个有代表性的例子:一个多人的项目组(A、B、C三人)共用一台应用服务器,申请人是A。需求是:三人都能启动/重启后端服务,都能在应用日志目录里查看和清理日志文件,但不能互相删除对方上传到共享目录的资料,也不能获得root权限。
我的实施步骤是:
- 建一个app组:
groupadd app- 建三个用户并加入app组:
useradd -m -s /bin/bash -G app a_user # 另外两个用户同理- 在应用目录和日志目录上做授权:
chown -R service_user:app /opt/myapp chmod -R 2750 /opt/myapp/logs chown root:app /data/uploads chmod 3770 /data/uploads- 给A、B、C配置sudo白名单:
%app ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart myapp.service这样做完之后,他们的所有操作都被限制在预定范围内:能管理服务但拿不到原生的root shell;能在共享目录传文件但删不了别人的;能读日志也能清理日志,在权限上没有任何越出圈的动作。
6. 用户与组管理常见问题:排查思路与方法
6.1 常见报错与解决办法速查表
在实操中用得最多的其实是故障快速定位。以下是几个我在日常运维中遇到的高频问题及排查思路的整理:
| 现象 | 可能原因 | 排查与解决 |
|---|---|---|
| 用户登录被拒绝,local报错 | 密码过期、账号锁定、shell为nologin | 检查chage -l,shadow中的密码字段,passwd最后一栏的shell |
| sudo执行报“not in the sudoers file” | 用户不在sudo组里或sudoers没配置 | 切root用visudo补白名单 |
| 文件被删除后无法恢复 | 权限正确但父目录的写权限被剥夺 | 检查目录本身权限,不是只看文件 |
| 同组用户之间无法互相修改文件 | 文件属组不对或组权限位未开写 | 检查文件属组,chgrp修正并确保目录setgid |
| 新建文件属组不是想要的组 | 所在目录没有setgid位,用户基本组不同 | 在目录上加上setgid权限位(chmod g+s) |
| userdel提示设备或资源忙 | 用户还有进程在运行 | 用pkill杀掉相关进程,或先锁账号冷却后再删 |
| 家目录无法登录报No directory | passwd里的家目录字段与实际情况不符 | 检查家目录是否被移动/删除,重新usermod -d或手动建立目录并设置属主 |
| 同一用户在不同服务器上UID不一致 | 各服务器独立分配UID | 规划时统一指定UID,或用LDAP/集中认证方案 |
6.2 删不了用户的真实现场:一次处理记录
讲一个具体的案例。有一次我需要从一台生产服务器上移除某个离职同事的账号。按流程先锁账号、保留数据,一周后确认没问题了执行userdel,结果系统报错“userdel: user is currently used by process 12345”。
我第一反应是这人的定时任务还在跑,或者残留了后台进程。排查思路是这样的:
ps -ef | grep username pgrep -u username排查发现有个凌晨启动的数据同步脚本以他的身份运行,一直挂着没退出。我先用kill终止了主进程,再用ps --user username复查,确认全部进程都清理干净,重新执行userdel。这次就成功了。
这里有一个容易忽视的点:有时候进程已经从ps里看不出来了,但还有进程是从某个目录切到后台运行的。如果实在找不干净,可以先执行usermod -L锁定账号,等所有进程自然退出后再次尝试删除,别急着-f强删,避免对运行时数据造成破坏。
6.3 运维防线:锁账号、查登录记录、清理隐患用户
还有一个在用户与组管理里不该被遗漏的安全动作,就是定期盘点账号。服务器上经常躺着一些“建完就忘”的账号:
- 测试时建的临时用户
- 某个外包同事还在不在不确定的账号
- 好几个公用账号共用同一个口令
如果不定期清理,这些账号就是潜在的风险口子。可以写一个简单脚本,把所有能登录的用户的最近登录时间列出来:
for user in $(awk -F: '$7!="/sbin/nologin" && $7!="/usr/sbin/nologin" {print $1}' /etc/passwd); do printf "%-20s %s\n" "$user" "$(lastlog -u $user | tail -1)" done看到长期没有登录记录的账号,就去业务侧确认还有没有在用。没有在用的,直接锁掉或删掉,把/etc/shadow里的密码也一并处理干净。处理完之后,最好在变更记录里写一笔,方便后续审计。
排查隐患用户还有一个技巧:对比/etc/passwd里每个UID的属主文件数量。如果一个低编号的系统用户名下突然出现大量可写文件,往往是上一个被删用户的UID被复用了。这类问题最好在月度巡检时用脚本扫一遍。
6.4 初始化环境时的统一规范
最后分享一个我自己的习惯。在新装服务器上我从来不会放任默认配置直接开干,而是先把基础账号体系搭好:
- 创建运维专用管理账号(不直接用root跑日常操作)。
- 按业务域建好组并注释清楚每一个组的用途。
- 把需要登录的用户的shell都明确设置成bash,把服务账号的shell统一为nologin。
- 统一规划UID/GID段并记录在案,跨服务器使用时保持一致的编号,避免出现两台机器上同一个人UID不同、没法直接同步权限的问题。
- 每次操作前先确认当前用户的身份,在命令行提示符上看一眼用户名和主机名,避免在错误的目标机上执行危险命令。
这套规范看起来琐碎,但真能替你挡掉很多麻烦。统一了账号体系,后面再引入集中认证或自动化配置管理,都有了稳固的基础,不用回炉重造。
我个人踩过最值的坑,就是“临时账号”三个字。所有临时需求都该走同一个建号流程,该加组加组、该锁锁、该删删。临时只是需求周期短,不代表流程可以打折。你在这个系统上做的每一次加删改,都是在给未来某个时间点的自己写说明书。把这套基础打好,比临时抱佛脚记一堆命令有用得多。