玩Linux这些年,被问得最多的就是“Linux用户权限”,尤其是一批刚接手服务器的运维同事,一上来就习惯性切到root,最后权限崩了都不知道怎么排查。其实用户权限这个事,说难不难,但真要吃透,得从底层的用户模型、文件权限位、sudo授权再到故障排查串起来看。这篇文章我就按自己实际管理服务器的经验,从零把整条链路捋一遍,既能给刚入门的朋友当教程,也能让有经验的同行当作排查手册参考。
1. 先搞懂Linux是怎么看待“用户”的
1.1 UID/GID:你喊他老王,系统只认数字
Linux系统里出现的每个用户,本质上都不是一个“名字”,而是一个数字。这个数字就是UID(用户ID),对应的组也有GID(组ID)。你在终端里看到root、zhangsan这类名字,那只是给人看的“翻译”,系统内核在判断文件属主、进程权限时,用的全是UID和GID。
讲个我自己踩过的坑:有一次我从旧服务器打包了一个网站目录,解压到新机器后发现文件属主显示503而不是某个用户名。原因就是压缩包里记录的UID是503,而新机器上没有对应UID的用户,系统就干脆直接显示数字了。所以理解UID/GID,对后续排查文件属主错乱、数据迁移这类问题非常有帮助。
查看一个用户的完整身份信息,用id命令就够:
id zhangsan # 输出类似:uid=1001(zhangsan) gid=1001(zhangsan) groups=1001(zhangsan),10(wheel)1.2 三类用户角色:管理员、系统服务、普通用户
Linux用户大致可以分成三类,它们的UID范围和用途完全不同:
| 用户类型 | UID范围 | 典型账号 | 主要用途 |
|---|---|---|---|
| 超级用户 | 0 | root | 系统的最高权限,能做任何操作 |
| 系统用户 | 1-999(CentOS/RHEL) | bin、daemon、nobody、www-data | 运行系统服务,一般不能登录 |
| 普通用户 | 1000及以上 | zhangsan、devops | 日常登录、跑业务、开发调试 |
这里要特别提醒一下:很多人以为系统用户没用,其实它们是“安全设计”的一部分。比如Nginx默认就是用一个低权限用户去跑worker进程,而不是直接用root。这样即使Web应用被攻破,攻击者拿到的也只是低权限shell,没法直接控制系统。所以创建服务账号时,尽量给一个独立系统用户,而不是把所有服务都塞在root下跑。
1.3 四个配置文件:权限体系的“户口本”
用户和组的信息并不是存在某个数据库里,而是散落在四个纯文本配置文件中。理解这些文件,你才能真正看懂useradd、usermod这些命令背后的动作。
/etc/passwd:用户基本信息,包含用户名、UID、GID、家目录、登录shell等。/etc/shadow:用户密码加密后的哈希值,以及密码策略(有效期、修改间隔等)。/etc/group:组基本信息,包含组名、GID、组内成员。/etc/gshadow:组密码和组管理员信息,日常用得比较少。
拿/etc/passwd里的一行举例:
zhangsan:x:1001:1001::/home/zhangsan:/bin/bash字段依次是:用户名、密码占位符、UID、GID、备注、家目录、登录shell。注意这个x不是密码本身,加密后的密码都在/etc/shadow里,这也是为什么普通用户读取不了shadow文件的原因。
提示:几乎不要手动去编辑这四个文件,就算要改,首选
vipw和vigr命令,它们会帮你做文件锁和语法检查。我就是早期图省事直接vim编辑,结果字段写错了,搞得某用户登录直接被拒,排查半天。
2. 用户的创建、管理与删除:别只会useradd
2.1 创建用户这一步,参数远比你想的重要
创建一个用户看似简单,就一句useradd zhangsan,但如果只敲这一句,你会发现新用户连家目录都没有。因为不同发行版对useradd的默认行为不一样,有些默认创建家目录,有些不创建。所以我在生产环境里一般会写成这样:
useradd -m -u 1050 -s /bin/bash -g dev -G docker,svn zhangsan参数解释:
-m:创建家目录。-u:指定UID,方便统一管理,避免迁移时属主错乱。-s:指定登录shell,一般给/bin/bash;如果只是跑服务,可以设成/sbin/nologin禁止登录。-g:指定主组。-G:指定附加组,可以有多个,用逗号隔开。
创建完用户后还要记得设初始密码:
passwd zhangsan如果你在写自动化脚本,不想交互式输密码,可以用:
echo "Initial@123" | passwd --stdin zhangsan但生产环境不建议这种方法,密码会留在shell历史里,安全隐患很大。
2.2 修改用户和密码策略:usermod、passwd、chage
用户建好后不是一劳永逸的,改权限、改组、锁定账号都会用到usermod。我常用这几个场景:
# 把zhangsan加入docker组(注意是 -aG,不是 -G) usermod -aG docker zhangsan # 锁定账号,禁止登录 usermod -L zhangsan # 解锁账号 usermod -U zhangsan # 设置账号过期时间,常用于临时账号 usermod -e 2025-12-31 zhangsan这里有个血泪教训:-G和-aG看起来差不多,但实际效果天差地别。usermod -G docker zhangsan会把zhangsan从原来的附加组中全部移除,只保留docker一个组。我就见过同事这样操作后,用户直接失去了wheel组的sudo权限,然后一脸懵地找我问为什么不能sudo了。所以,想添加附加组、保留原有组时,务必用-aG。
密码策略方面,用chage管理:
# 查看密码策略 chage -l zhangsan # 强制90天后必须改密码,提前7天提醒 chage -M 90 -W 7 zhangsan2.3 删除用户和清理残留:userdel的常见坑
删除用户也有讲究。userdel zhangsan只删用户本身,不删家目录和邮件,所以我在清理员工离职账号时会用:
userdel -r zhangsan-r会连家目录和mail spool一起删掉。但要注意,这个命令不会删除该用户在其他地方遗留的、属主为该用户的文件。如果你不确定,建议先查找再处理:
find / -user zhangsan -ls 2>/dev/null还有另一种情况:用户已经删了,但系统里留下了大量UID未知的文件。这种文件看着很碍眼,处理办法一般是找出来重新chown给现有用户,或者直接删除。这里也提醒大家,在大批量删除用户前,先把它们的文件备份或转移,不然数据丢了真没法交代。
3. 文件权限:rwx背后的完整逻辑
3.1 权限位、数字和目录的特殊性
Linux每个文件和目录都有三组权限,分别对应属主(u)、属组(g)和其他人(o)。每组权限用rwx表示,翻译成数字就是r=4、w=2、x=1。
举个例子,chmod 755 script.sh的含义是:
- 属主:7 = rwx,可读可写可执行。
- 属组:5 = r-x,可读可执行,不可写。
- 其他人:5 = r-x,可读可执行,不可写。
这个规则大家基本都懂,但目录权限和文件权限的理解容易出偏差。文件的话,r是能看内容,w是能改内容,x是能执行。目录则完全不同:
r:能列出目录里的文件名。w:能在目录里创建、删除、改名文件。x:能进入目录,也就是cd进去的基础权限。
这意味着就算你对某个目录拥有写权限,如果没有执行权限,你也进不去目录,更不可能在里面删除文件。常见场景就是给Web项目目录配置权限时,只给755而忘了x,结果其他用户能列目录却进不去,404一片。
3.2 属主属组调整:chmod、chown、chgrp的正确用法
实战中改权限和属主是最高频的操作。我常用这几个组合:
# 数字方式,一次设置三组权限 chmod 750 /data/project # 符号方式,只给属组加写权限 chmod g+w /data/project # 递归修改目录及内部文件 chmod -R 755 /data/project # 同时修改属主和属组 chown devops:dev /data/project # 只改属主 chown devops /data/project # 只改属组 chgrp dev /data/project这里我要强烈反对一个操作:chmod -R 777。很多人权限不对就直接丢777,等于把文件权限全放开了。一次两次看起来没事,但一旦服务器上有其他低权限进程被利用,777目录里的敏感数据就可能被拖走。正确做法是,先想清楚谁需要读、谁需要写、谁需要执行,然后给最小权限。
另外,使用chown时有几个细节容易踩坑:
chown dev:dev表示属主和属组都改成dev用户和dev组。chown :dev只改属组不改属主。- 递归操作
-R一定要确认路径没问题再执行。我就有同事把chown -R的路径写错,直接把整个根目录的属主改成普通用户,系统差点起不来。
3.3 SUID、SGID、Sticky Bit:三个容易被忽略的特殊权限
除了基础的rwx,Linux还有三个特殊权限位,日常排查权限问题和安全加固时经常遇到。
SUID(Set User ID):当一个可执行文件设置了SUID,那么任何用户运行它时,进程的属主会变成文件属主,而不是运行者。最典型的例子是/usr/bin/passwd,它的权限是-rwsr-xr-x,这个s就代表SUID。普通用户执行passwd时,能以root身份修改/etc/shadow中的密码,然后进程结束后权限又恢复普通用户。
SGID(Set Group ID):文件上有SGID时,运行进程的属组会变成文件属组;目录上有SGID时,在该目录新建的文件会自动继承目录的属组。这条在协作目录里特别有用:
chown :dev /data/team-project chmod 2770 /data/team-project # 2 就代表 SGID这样只要成员属于dev组,在这个目录里创建的文件,组身份都会自动是dev,不会因为谁创建的文件就归谁私有,省去一堆互相chgrp的麻烦。
Sticky Bit(粘滞位):最常见的场景是/tmp目录,权限是drwxrwxrwt,最后的t就是粘滞位。它的作用是:在设置了粘滞位的目录中,用户只能删除自己拥有的文件,即使目录的写权限让所有人都能创建文件,也不能乱删别人的。临时目录如果没这个位,就会出现用户A删掉用户B文件的情况。
查看特殊权限可以用ls -l,但特殊权限在数字上也有表示:SUID=4、SGID=2、Sticky=1。所以:
chmod 4777 file:设置SUID。chmod 2770 dir:设置SGID。chmod 1777 /tmp:设置粘滞位。
从安全角度讲,SUID是提权类问题的重点关注对象。运维日常巡检时,我会定期扫一遍系统里所有SUID文件:
find / -perm -4000 -type f 2>/dev/null如果发现莫名其妙多出的SUID文件,尤其是有x权限的可执行文件,大概率有问题,重点排查。这类文件一旦被利用,普通用户可能获得root权限,是管理员最容易忽视的隐患。
4. sudo与授权管理:把root“借”出去的正确姿势
4.1 用visudo做最小授权,安全又省心
很多团队的坏习惯是把root密码给所有开发,一人一个root,出了问题根本不知道是谁干的。正确做法是用sudo做细粒度授权,把部分命令的权限下放给普通用户。
修改sudo配置务必使用visudo:
visudo不要直接vim编辑/etc/sudoers,因为visudo会做语法检查。一旦写错,sudo可能直接瘫痪,到时候修起来更麻烦。
sudo配置行的基本格式是:
用户 主机=(角色) 命令几个常见写法:
# 让zhangsan能执行所有命令,但仍需要输入自己的密码 zhangsan ALL=(ALL) ALL # 让dev组的成员无需密码就能重启nginx %dev ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart nginx # 只允许zhangsan以root身份运行useradd和usermod zhangsan ALL=(root) /usr/sbin/useradd, /usr/sbin/usermod我个人在配置sudo时的原则是:能不给的权限坚决不给,能限制范围的尽量限制范围。比如开发人员需要查看系统日志,我就只授权tail和grep的某些日志路径;需要重启服务,就只授权对应的systemctl restart xxx。这样才能在方便和安全之间找到平衡。
这里还要注意NOPASSWD别乱用。虽然免密很爽,但你机器上任何一个普通账号被入侵,就等于直接给了对方root权限。我一般只在自动化脚本和CI/CD专用的账号上设置NOPASSWD,人用账号都要求输密码。
4.2 实战:为SVN服务创建独立用户并精细授权
结合“svn用户权限”这个常见场景,我来完整演示一个案例。假设我们需要为SVN版本库创建专用系统用户,并让运维同事能通过sudo来管理SVN服务,但又不能让他们拿到root。
第一步,创建专用系统账号,禁止登录:
useradd -r -s /sbin/nologin svn-r表示创建系统用户,-s /sbin/nologin保证它不能登录shell。
第二步,创建SVN仓库目录并设置属主属组:
mkdir -p /data/svn/repos chown -R svn:svn /data/svn chmod -R 750 /data/svn这样普通用户无法直接访问仓库目录,避免了代码被误删或泄露。
第三步,在visudo中给运维同事授权SVN管理命令:
ops ALL=(root) /usr/bin/svnadmin, /usr/bin/svnlook, /usr/bin/systemctl restart svnserve这样运维只能执行和SVN相关的管理命令,不能随便改系统配置。
第四步,用sudo执行管理操作:
sudo -u svn /usr/bin/svnadmin create /data/svn/repos/newproject sudo svnlook tree /data/svn/repos/newproject提示:这个方案的核心思路就是“一个服务一个账号,一组管理一条命令通道”。不管SVN还是Git、Jenkins,我都建议用独立账号跑,配合sudo做命令白名单。这样哪怕某个服务的账号被攻破,影响范围也限定在对应目录内,不至于拖垮整个服务器。
5. 权限问题排查与安全加固
5.1 我整理的高频故障清单
多年运维下来,权限相关的故障来来去去就那么几个类型。我把高频的整理成了一张速查表:
| 故障现象 | 常见原因 | 排查命令 | 解决思路 |
|---|---|---|---|
Permission denied | 文件属主/属组不对,或缺少x权限 | ls -l、namei -l 路径 | 修正属主属组或权限位 |
sudo: command not found | PATH环境变量没包含对应目录 | echo $PATH、which sudo | 修复用户PATH,尤其检查anaconda等自装软件的家目录路径 |
| 文件属主显示为数字 | 原用户已删除,UID无归属 | find / -uid 1005 -ls | 将文件chown给现有用户,或重建相同UID的用户 |
| 新创建文件权限不对 | umask设置不合理 | umask | 修改全局或用户的umask |
| 无法删除目录里的文件 | 目录缺少w和x权限,或触发了Sticky Bit保护 | ls -ld 目录 | 检查目录权限,确认粘滞位是否在起作用 |
| 切换用户后命令行提示符不对 | 用户shell配置缺失或家目录权限异常 | ls -l /home/用户 | 检查家目录属主是否为该用户,配置文件是否完整 |
这里特别说下sudo: command not found,这锅经常不是sudo的,而且用户的PATH变量里没有命令所在目录。比如我用anaconda装过Python环境,有些用户环境变量配置在~/.bashrc里,但通过sudo执行时,secure_path会覆盖PATH,导致sudo下找不到python或conda。解决办法是在visudo里调整secure_path,或者直接用sudo /usr/bin/python3这类全路径方式调用。这不是权限本身的问题,但非常常见,排查时报错信息又很混淆,容易让人误判。
5.2 权限审计与日常加固建议
权限安全不是一次配置完就高枕无忧的,要定期做一下几件事:
- 检查是否存在空密码用户
awk -F: '($2==""){print $1}' /etc/shadow有输出就说明有用户密码为空,必须立刻设密码或锁定。
- 检查UID为0的用户
UID为0等同于root权限,正常系统里只能有root一个:
awk -F: '($3==0){print $1}' /etc/passwd如果多出来别的用户,基本可以断定有异常。
- 检查sudoers配置
grep -v '^#' /etc/sudoers | grep -v '^$'重点看有没有给普通用户配了不必要的ALL权限。
- 检查SUID文件变化
建议第一次先扫描建立基线,之后定期比对:
find / -perm -4000 -type f 2>/dev/null- 检查用户家目录权限
用户家目录如果是777,别人就能随意看代码、删配置:
ls -ld /home/*正常家目录权限应该是700或755,不要放开到777。
我在实际运维中还养成了一个习惯:权限变更前先记录原始属主和权限,用小脚本或者ls -l快照都行。一旦变更后出现问题,可以快速回滚。批量操作时也先拿一两个目录试点,确认无误再铺开。别嫌麻烦,权限这东西,出问题往往是连锁反应,一次大意就是一次事故。
最后分享一点个人体会
权限管理这件事,表面上是命令和参数的问题,本质上是对系统安全的敬畏。用户权限这个坑,我自己也踩过不少,最痛的一次是批量chown写错路径,差点把生产环境搞瘫。从那以后,凡是涉及权限变动的操作,我都强制自己先备份后操作,绝不裸奔。希望看完这篇文章的朋友,哪怕记不住命令,也一定要记住“最小权限”这四个字。平时多留意配置文件、多跑几次审计命令,服务器真的会少很多意外。