搞Linux系统管理,权限管理永远是绕不开的核心话题。不管你是刚接触 Linux 的新手,还是已经在运维、嵌入式、后端开发领域摸爬滚打多年的老手,用户、用户组、文件权限这三件事几乎每天都在打交道。很多人遇到“权限不够”“无法写入”“Permission denied”就习惯性chmod 777一把梭,或者直接切 root 操作,结果埋下一堆安全隐患,甚至导致服务被入侵、数据被误删。
这篇文章不说教科书式的废话,直接按实际干活的经验来梳理一遍 Linux 用户组与权限管理的核心逻辑,涵盖用户与组的底层设计、常用命令的实操细节、rwx 权限位的计算原理、SUID/SGID/Sticky Bit 特殊权限、ACL 访问控制列表,以及 sudo 提权配置的坑。每个知识点都用场景化方式拆解,尽量让零基础的朋友也能看懂,让有经验的朋友能从中查漏补缺。
1. 先想清楚:用户和组到底是什么
1.1 为什么系统不直接认用户名
很多初学者会困惑:我创建一个用户叫 zhangsan,系统里也有这个字符串,为什么文件属主不是直接记录“zhangsan”,而是记录一串数字?这就要理解 Linux 作为一个多用户操作系统的底层设计思路。
Unix/Linux 从诞生起就是“多人共用一台机器”的形态。如果文件系统里直接存用户名,每次读写文件都要解析字符串,效率低;更麻烦的是,用户名可以随便改,如果文件记录的名字被别人改掉了,权限归属就全乱了。所以内核只认数字 ID,也就是 UID(用户 ID)和 GID(组 ID),用户名和用户组名只是给人看的“标签”。/etc/passwd文件里存的是 UID 到用户名的映射,/etc/group里存的是 GID 到组名的映射。
用id命令可以很直观地看到这一切:
[root@localhost ~]# id zhangsan uid=1001(zhangsan) gid=1001(zhangsan) groups=1001(zhangsan)这里有一个非常容易踩的坑:如果你手动改过/etc/passwd或者用useradd时指定了不合适的 UID,系统里可能出现两个用户名对应同一个 UID 的情况。这时候文件属主显示的是第一个匹配到 UID 的用户名,而实际权限归属是 UID 而不是用户名。排查权限问题的时候,如果发现用户名对不上,先ls -n看数字 ID,不要被字符串误导。
1.2 用户分类和组分类的区分逻辑
Linux 用户从功能上分三类:超级用户(root,UID 0)、系统用户(UID 1-999,供守护进程使用)、普通用户(UID 1000 以上)。系统用户也叫伪用户,它们一般没有家目录、不能登录,专门用来跑 nginx、mysql、redis 这类服务。组也同理,系统组 GID 1-999,普通组 1000 以上。
类和组的设计意义在于职责隔离。举个最典型的场景:你部署一个 Web 服务,如果直接用 root 跑 nginx,那 nginx 一旦被利用,攻击者直接拿到整台机器的最高权限;如果单独创建一个nginx用户,让 nginx 进程以这个用户身份运行,攻击者即使拿下了这个进程,能访问的文件也仅限于该用户权限范围内的内容。
组的目的则是把“一类需要共享某些资源的人”打包。比如公司的运维组、开发组、财务组,同一个组内的成员可以共享某些目录的读写权限,组外的默认无法访问。这样的模型相比 Windows 的“处处问权限”来说,更简单,更高效,但也更依赖管理员规划的合理性。
1.3 文件上的属主属组是怎么记录和变化的
用ls -l看到的文件信息里,第三列是属主,第四列是属组。系统实际存储的是属主 UID 和属组 GID,ls命令展示时再通过/etc/passwd和/etc/group换算成名字。
文件的属主在创建时默认是创建者本人,属组默认是创建者的主要组(initial group)。这个概念要特别强调:很多初学者以为useradd zhangsan之后,他创建的文件属组就是root或者随便一个默认组,其实不对,属组是他自己的私有组zhangsan(GID 与 UID 相同,这是大多数发行版的默认策略)。
修改属主用chown,修改属组用chgrp,也可以一条命令搞定:
chown zhangsan:develop /opt/project chown -R zhangsan:develop /opt/project # 递归处理所有子文件和目录这里提醒一句:chown的语法是先属主后属组,中间用冒号分隔。如果你只写chown zhangsan file,只改属主;只写chown :develop file,只改属组。很多人记反了或者漏了冒号,导致命令执行后权限归属和预想完全不一样。
2. 用户与组的实操管理
2.1 建用户时那些参数到底怎么选
日常建用户,我几乎不用裸useradd zhangsan这种方式,因为默认参数并不总是符合预期。不同发行版对useradd默认值的规定不同,比如有的会创建同名私有组,有的不会;有的会创建家目录,有的不会。最稳妥的做法是显式指定关键参数。
举一个实际例子:给一个部署应用的人建用户,同时把它加到运维组,并设置密码和过期时间:
useradd -u 1501 -g ops -G develop -m -d /home/zhangsan -s /bin/bash -e 2026-12-31 zhangsan passwd zhangsan各参数含义如下:
-u 1501:自定义 UID,方便和公司内部统一身份管理对接。-g ops:指定主组,注意这个组必须已经存在,否则会报错。-G develop:指定附加组,用户同时属于多个组时用这个参数。-m:创建家目录。-d:指定家目录路径。-s /bin/bash:指定登录 shell,如果不需要登录,可以填/sbin/nologin。-e:账户过期时间,格式是 YYYY-MM-DD。
给系统服务建用户则是另一套思路:
useradd -r -s /sbin/nologin -M nginx-r表示创建系统用户,UID 从系统范围分配;-s /sbin/nologin禁止登录;-M不创建家目录。服务类用户不需要交互式登录,也没必要有家目录,这是最小权限原则的体现。
2.2 改用户、删用户时容易踩的三个坑
第一个坑是usermod -G和usermod -aG的区别。-G是直接覆盖用户的附加组列表,-aG是在原有基础上追加。我曾经在处理一个同事的需求时,想给他加个 docker 组,写成了usermod -G docker zhangsan,结果把他原来所在的 develop 组给顶掉了,导致他访问不了项目目录。这个教训很深刻:修改附加组时,除非常明确要清理原有组,否则一律用-aG。
第二个坑是userdel默认不删家目录。RHEL/CentOS 系的userdel zhangsan只删除用户账号本身,家目录、邮件文件都残留着。想整体清理用userdel -r zhangsan,但这一步不可逆,执行前确认一下家目录有没有需要备份的东西。我见过有人删完用户才发现家目录里存着没提交的代码,这时候哭都来不及。
第三个坑是修改用户主组时牵连了历史文件。用户的主组不是“改了立即全局生效”那么简单。/etc/passwd中的 GID 改变了,但是该用户之前创建的文件属组不会自动跟着变。如果你usermod -g newgroup zhangsan,旧文件的属组还是 oldgroup,需要手动find /home/zhangsan -group oldgroup -exec chgrp newgroup {} \;来清理。
2.3 组的管理:组密码到底有没有用
组相关的命令主要有groupadd、groupdel、groupmod和gpasswd。日常管理组,关注最多的是组密码和组成员管理。
组密码是个冷门知识点。当你执行gpasswd ops设置了组密码后,不属于 ops 组的用户可以通过newgrp ops命令并输入组密码临时切换到 ops 组身份,从而获得该组对应的权限。这在某些需要临时授权的内部场景是有用的,但安全上要谨慎:密码一旦泄露,等于把组内所有资源的访问权发给了外部人员。生产环境我基本不设置组密码,统一用 sudo 或者把用户加入组的方式来管理。
组成员管理可以用gpasswd -a 用户名 组名添加成员,gpasswd -d 用户名 组名移出成员。也可以直接编辑/etc/group文件,在组名的最后一列用逗号分隔用户名。注意:用户重新登录后,新加入的组才会生效。如果用户当前已经登录,需要重新登录或者执行newgrp 组名临时切换,不然id命令看不到新组,文件访问权限也不会立刻变化。这个问题非常容易在排障时被忽略。
3. 文件权限:rwx 背后的计算逻辑
3.1 三类身份和三个权限位的真实含义
Linux 文件权限针对三类身份分别设置:属主(u)、属组(g)、其他(o)。每个身份对应三个权限位:读(r)、写(w)、执行(x)。ls -l输出的第一列有 10 个字符,第一个表示文件类型(-普通文件、d目录、l软链接等),后面 9 个字符每 3 个为一组,对应属主、属组、其他的权限。
很多新手对“目录的执行权限”感到困惑。对文件来说,x 表示可以执行;对目录来说,x 表示可以“穿过”该目录,也就是可以cd进去并访问里面的文件和子目录。如果目录只有 r 没有 x,你能ls看到里面的文件名列表,但无法访问文件的 inode 信息,也就无法读取文件内容;如果只有 x 没有 r,你可以访问知道路径的文件,但ls列不出里面有什么。所以实际部署中,目录的权限组合最常见的是755或750——没有 x 的目录基本等于没用。
3.2 chmod 数字模式和符号模式的换算
数字模式本质是二进制到八进制的转换。r=4、w=2、x=1,把三个权限位相加得到最终数字。rwx = 7、rw- = 6、r-x = 5、r-- = 4,即属主权限 = 第一位数字、属组权限 = 第二位数字、其他权限 = 第三位数字。
我整理一张速查表,方便大家对照:
| 权限组合 | 数字值 | 含义 |
|---|---|---|
| --- | 0 | 无任何权限 |
| --x | 1 | 仅可执行/穿过 |
| -w- | 2 | 仅可写 |
| -wx | 3 | 可写可执行 |
| r-- | 4 | 仅可读 |
| r-x | 5 | 可读可执行 |
| rw- | 6 | 可读可写 |
| rwx | 7 | 完全控制 |
实际操作中,给文件chmod 644是最常见的:属主读写,其他人只读,适合配置文件;给脚本或二进制程序chmod 755:属主读写执行,其他人读执行,适合对外提供的可执行文件;目录共享场景则常用chmod 770:属主和属组完全控制,其他无权限。
符号模式则是用u、g、o、a(all)指定身份,用+、-、=增加、删除、精确设置权限。比如chmod u+x script.sh给属主加执行权限,chmod o-w file去掉其他身份的写权限,chmod a=r file把所有人权限设置为只读。符号模式的好处是可以只改某一位,而不影响其他位;数字模式每次都是全量赋值,容易把原有特殊权限位清掉。
3.3 chown/chgrp 的使用边界
chown只有 root 可以执行,普通用户不能把文件属主改成别人,也不能随便把属组改成自己不在的组。这其实是 Linux 一个很基本的安全约束:防止用户通过更改属主来绕过磁盘配额或获取他人文件的控制权。
日常场景里,chown最常见的用途是把解压后的文件统一归属到某个运行用户或项目组。比如用 tar 从别的机器迁移过来一堆文件,属主可能都是原来的 UID,这时候一条递归修改就能解决:
tar xzf backup.tar.gz chown -R zhangsan:develop /data/project但chown -R必须谨慎使用。我见过有人把整个/usr目录chown -R zhangsan:zhangsan之后系统崩掉的案例。系统目录下的二进制、库文件、配置文件的属主错乱后,服务起不来,安全模块也可能拒绝加载,排查起来非常痛苦。所以递归 chown 前,务必确认目标路径的范围没有扩大。
3.4 umask 默认权限是怎么来的
你新建一个文件,ls -l看到的默认权限通常是644;新建目录是755。这个结果不是凭空来的,是受到 umask 影响。
umask 是一个“权限掩码”,它表示“默认要屏蔽掉的权限位”。文件最大默认权限是 666(没有执行位,因为普通文件默认不应该是可执行的),目录最大默认权限是 777。666 - 022 = 644,777 - 022 = 755,这就是 root 用户下常见 umask 022 的算法。
但严格来说,这个“减法”是一种方便理解的简化说法,真正的逻辑是按位取反后再按位与:
文件权限 = 666 & (~umask) 目录权限 = 777 & (~umask)umask 022 的二进制是 000 010 010,取反后属主位全保留,属组位和其他位清除了写权限,所以文件是rw-r--r--,目录是rwxr-xr-x。
查看当前 umask 用umask命令,临时修改直接umask 027,永久修改需要写进/etc/profile或用户自己的~/.bashrc。这里要给一句话:在高安全要求的环境里,umask 027 比 022 更稳妥,因为其他用户连读取权限都没有,避免敏感文件被同机器的其他用户扫到。
4. 特殊权限位:SUID、SGID、Sticky Bit
4.1 为什么有了 rwx 还不够
普通 rwx 权限是按“身份”判断的,无法解决两个经典问题:普通用户要临时以属主身份执行某个程序;多个用户在共享目录里协作,新文件的属组总是对不上。这就需要特殊权限位登场。特殊权限位也是 9 个权限位之外附加的标志位,通过chmod的第四个八进制数字或者符号模式设置。
| 特殊权限 | 八进制值 | 符号表示 | 对文件的作用 | 对目录的作用 |
|---|---|---|---|---|
| SUID | 4 | u+s | 执行时以文件属主的身份运行 | 无实际意义 |
| SGID | 2 | g+s | 执行时以文件属组的身份运行 | 目录内新建文件自动继承目录属组 |
| Sticky Bit | 1 | o+t | 无实际意义 | 只有文件属主或 root 能删除 |
设置方法举例:
chmod 4755 somebinary chmod u+s somebinary chmod 2770 /data/shared chmod g+s /data/shared chmod 1777 /tmp chmod o+t /tmp4.2 SUID:普通用户为什么能改密码
/etc/shadow文件里存着所有用户的密码哈希,权限是----------,600 都不给,只有 root 能读写。那普通用户运行passwd命令为什么能改自己的密码?因为passwd这个程序设置了 SUID:
[root@localhost ~]# ls -l /usr/bin/passwd -rwsr-xr-x. 1 root root 32680 Jan 30 2024 /usr/bin/passwd看到那个s了吗,它在属主执行权限位的位置,表示该程序执行时,进程的有效 UID 会变成 root,而不是运行者的 UID。所以普通用户执行passwd时,进程以 root 身份修改/etc/shadow,完成后立即退出,root 权限没有留下任何常驻入口。这是一个非常聪明的设计:把需要临时提权的程序做成精准授权工具,而不是直接把 root 密码给用户。
SUID 同时也是风险最高的权限位。如果一个不怀好意的用户拿到一个你可以执行的、带有 SUID 且属主是 root 的脚本或者二进制,他就能以 root 身份运行它做各种操作。排查系统安全时,找 SUID 文件是必做项:
find / -perm -4000 -type f 2>/dev/null这个命令列出全盘所有带 SUID 的普通文件,逐一确认是不是系统自带程序。如果发现/tmp下冒出来一个 SUID 的二进制,大概率是被入侵过,要立刻处置。
4.3 SGID 适合共享目录场景
SGID 体现在ls -l中,属组执行的 x 位变成s。对于文件,它的作用和 SUID 类似,只是把有效 GID 改成文件属组。对于目录,SGID 有更强的作用:目录设置了 SGID 后,任何人在这个目录下新建文件或子目录,新文件的属组都会被强制设为目录的属组,而不是创建者自己的私有组。
场景举例:团队有一个共享目录/data/teamshare,属组设为develop,权限2770,所有开发人员都在develop组里。正常情况下,zhangsan 在里面新建一个文件,属主是 zhangsan,属组应该是 zhangsan 自己的主组;但设置了 SGID 后,新建文件属组自动变成develop。这样团队里所有成员都能正常读写彼此创建的文件,不用每次手动 chgrp,也避免因为属组错乱导致互相访问不了文件。
使用 SGID 时有一个坑:默认 umask 会限制组写权限,如果创建者 umask 是 022,即使目录是 2770,新建文件权限仍是 644,组内其他人无法修改文件。解决办法是把共享目录的默认 umask 设置为 002 或 007,让组权限默认带上写位,或者用下面会讲到的 ACL 来更精细地控制。
4.4 Sticky Bit 让 /tmp 不乱套
Sticky Bit 的位置体现在其他身份的 x 位上,/tmp目录显示为drwxrwxrwt。它解决的核心问题是:在一个“所有用户都有写权限”的目录里,允许用户间互相覆盖对方的文件吗?
如果不设置 Sticky Bit,/tmp权限是 777,任何用户都能删除别人放在里面的临时文件,这显然是灾难。设置了 Sticky Bit 之后,即使目录是 1777,也只有满足以下条件之一的人才能删除或重命名文件:文件的属主本人、目录的属主、root 用户。这样就保证了/tmp这个公共空间既允许所有人创建文件,又不会被人乱删。
很多初学者会把chmod 777当成万能解药,但用私有服务器上可能问题不大,一旦放到多人环境或者被恶意软件利用,777 的公共目录配合没有 Sticky Bit,基本等于把文件安全交了出去。习惯上,公共临时目录必须chmod 1777,而不能是777。
5. 权限不够,或者权限太够:ACL 和 sudo
5.1 ACL 给单个用户放行单个文件
前面对话一直建立在“属主、属组、其他”三种身份之上,但现实中有一个很常见的情况:有 5 个用户的组是develop,可你只想给其中一个叫zhangsan的用户单独放开某个目录的读写权限,又不想把它加进组里带偏其他人。基础的 rwx 模型根本表达不了这种需求。这时候就要用 ACL,访问控制列表。
ACL 的本质是在传统 9 位权限基础上扩展出额外的用户和组权限项。查看文件 ACL 用getfacl,设置用setfacl。给 zhangsan 单独开放目录读写执行权限的写法:
setfacl -m u:zhangsan:rwx /data/project注意:如果对目录设置 ACL 但没有加-R,只对目录本身生效,不能递归到已有子文件。想对所有后续新建的文件默认生效,需要设置默认 ACL:
setfacl -m d:u:zhangsan:rwx /data/projectACL 设置成功后,ls -l的权限位末尾会多一个+号,比如drwxrwx---+。此时再用chmod修改权限要格外小心:ACL 中有一个 MASK 字段,它限制了所有“命名用户、属组、命名组”的最大权限范围。比如 mask 是 r-x 时,哪怕你setfacl -m u:zhangsan:rwx file设置了 rwx,实际有效权限也只有 r-x。
排查 ACL 问题时,先getfacl看 mask,再ls -l看基础位。在排障现场,我经常遇到“ACL 设置了还是说权限不够”的情况,十有八九是 mask 没放开。
5.2 sudo 配置与 sudo su 的坑
sudo 是生产环境提权的事实标准,它的配置只应该通过/etc/sudoers文件进行,并且必须用visudo命令打开编辑。visudo会在保存时做语法校验,如果语法写错,它会拒绝保存,从而避免你把 sudoers 弄坏导致整个系统无法提权。
/etc/sudoers的基本逻辑是这样的:给 zhangsan 授权所有命令:
zhangsan ALL=(ALL) ALL常见拆解:
- 第一个
ALL:允许在哪些主机上执行,通常保持 ALL。 - 括号里的
ALL:可以以哪个用户的身份执行。 - 最后一个
ALL:可以执行哪些命令。
如果只想授权特定的几个命令,例如控制服务重启:
zhangsan ALL=(ALL) /usr/bin/systemctl restart nginx, /usr/bin/systemctl reload nginx给整个组授权:
%ops ALL=(ALL) ALL免密执行某个命令:
zhangsan ALL=(ALL) NOPASSWD: /usr/bin/rsync配置 sudo 时最容易出问题的有两个地方。一个是 PATH 问题:sudo 执行命令时使用的是 secure_path,不是你当前 shell 的 PATH,如果你在 sudoers 里授权了systemctl但系统里它实际在/usr/bin/systemctl,那授权路径必须写全,不然执行时提示找不到命令。另一个是sudo su -的风控问题。sudo su会直接切到 root 的交互式 shell,等于给了用户一个持续的 root 环境,他后续执行的任何命令都不经过 sudo 日志审计。安全要求高的环境里,能不开放sudo su就不开放,尽量引导用户用sudo -i或者直接sudo 具体命令。
5.3 最小权限原则的落地建议
权限管理的终极原则只有一句话:给够用的权限,不要多给。我见过很多公司内部服务器权限混乱,根本原因是图省事:root 密码共享给所有人,或者把普通用户直接加进 wheel 组,免密 sudo ALL。这种管理方式看似方便,实际上最危险——你根本无法控制谁在什么时候执行过什么危险命令。
落地层面我的建议是:
- 日常操作不要用 root,root 只保留给确实需要系统级操作的场景。
- 所有需要提权的人都走 sudo,并且按命令粒度和机器粒度拆分授权。
- 定期用
journalctl -u sudo或者/var/log/secure审计 sudo 调用记录,看有没有异常命令出现。 - 服务进程一律使用独立的系统用户运行,不要把整个服务目录放到 root 权限下。
- 数据目录、配置文件目录的权限宁可收紧一点,出了访问问题时用 setfacl 细化放行,也不要直接 777。
6. 权限故障排查思路和速查表
6.1 先从一条命令开始
遇到权限问题,我先不急着 chmod,而是用一套固定的节奏来定位问题根源:
id # 确认当前用户、主组、附加组 ls -ldn 目标路径 # 看目录属主、属组、权限位,-n 可以显示数字 UID/GID stat 目标文件 # 看完整的权限、ACL、umask 上下文 getfacl 目标文件 # 看是否被 ACL 干扰 mount | grep 目标路径 # 排除挂载选项限制(nosuid、noexec、ro 等)特别是最后的mount检查,非常容易被忽略。很多权限问题其实是文件系统挂载选项导致的,比如noexec导致脚本无法执行,nosuid导致 SUID 不生效,ro导致整个分区只读。这些都不是通过 chmod 能解决的。
6.2 常见错误提示对应哪些原因
| 错误提示 | 大概率原因 | 处理方式 |
|---|---|---|
| Permission denied | 权限不足,可能是属主、属组、ACL 或 SELinux 拦截 | 按 id、ls、getfacl 顺序排查 |
| Operation not permitted | 某些受内核或安全模块限制的操作 | 检查 SELinux、AppArmor、capabilities |
| 无法删除文件 | 目录没有写权限,或未满足 Sticky Bit 条件 | 检查目录权限和文件属主 |
| sudo: no valid sudoers sources | 用户不在 sudoers 白名单 | 用 root 执行 visudo 添加授权 |
| 新建文件属组不对 | 未设置 SGID 或未显式使用 newgrp | 目录加 g+s 或修正创建者主组 |
| chmod 后 ACL 仍然提示权限不够 | MASK 限制 | getfacl 查看 mask,setfacl -m m::rx 调整 |
第六项里 SELinux 是另一个世界的大坑。在 RHEL/CentOS/Fedora 系列系统上,即使传统权限全部正确,SELinux 的布尔值或文件上下文错误一样会拒绝访问。排查时可以先用getenforce确认状态,再用ausearch -m avc -ts recent查拦截日志。如果确认是文件上下文问题,用restorecon -R 路径恢复默认上下文,或者根据服务需求设置正确的semanage fcontext标签。
6.3 经验总结:权限管理的最佳习惯
最后分享一些我自己在实际运维中沉淀下来的习惯,希望对你有参考价值。
第一个习惯是“目录权限优先于文件权限”。大多数权限问题都出在目录上,因为目录权限决定你能不能进入目录、能不能创建文件、能不能删除文件。查看问题时要先看目录的属主、属组、权限位,再往下看文件。
第二个习惯是“不要用 777 解决问题”。777 意味着任何用户都可以读写执行,这在单机开发环境也许能糊弄过去,但一旦服务器暴露在公网或多人使用环境,777 目录等于给所有本机用户打开了后门。要从根源上搞清楚是谁需要什么权限,用组、ACL、sudo 去精确授权。
第三个习惯是“修改权限之前先备份属主和属组信息”。在批量改权限之前,先跑一条命令把当前属主属组记录下来:
find /data/project -printf "%p %u %g %m\n" > /tmp/perm_backup.txt万一修改之后发现搞错了,可以按这个备份恢复。这个习惯救过我很多次,尤其是遇到递归 chown 后出现异常场景时,有备份就能一分钟还原,没备份就只能加班修。
第四个习惯是“配置完权限后,用另一个身份验证一下”。比如你给 nginx 用户配好了静态目录权限,就用su -s /bin/bash nginx -c "ls /data/www"去验证,而不是停留在“看起来权限正确”的印象上。真正可用的权限是要靠目标用户实际访问测试后才知道的。
Linux 权限管理的本质,就是一套“身份 + 范围 + 动作”的判定模型。理解了用户和 UID/GID 的关系,搞清楚 rwx 在文件和目录上的不同含义,再熟悉特殊权限位、ACL 和 sudo 的适用场景,你就能在绝大多数权限问题上游刃有余。下次遇到权限拒绝的时候,别急着执行chmod 777,按上面说的排查节奏走一遍,你会发现大多数问题都能精准定位到具体原因,并且用最小改动来解决。