Linux 文件权限:rwx、chmod、chown 与超越它们的机制
系统讲清 Linux 权限模型:rwx 在文件与目录上的不同语义、数字和符号写法、chmod/chown 用法、umask 默认值、SUID/SGID/Sticky 与 ACL,附排障清单。
文件权限看上去是基本功——chmod 755 一敲就完了——但它在生产中惹出的麻烦排得上前几名:服务起不来、部署脚本默默没动静、Nginx 蹦个 403、共享目录漏成筛子、rm 偏偏不让删一个"应该能删"的文件。光记几个魔数对这些都不管用,真正能救场的是同时把三件事想清楚:
- 同样的
r/w/x三个位,在普通文件和目录上含义完全不同——绊住绝大多数人的就是目录这一侧。 - 内核的判定走的是 owner / group / others 的三段
if/else if/else,不是把匹配位加起来——所以"刚好在文件所属组里"有时反而比"完全的外人"更糟。 umask、SUID、SGID、sticky bit、ACL 各自存在都有一个具体的理由,超出那个理由去用,是系统被搞穿的常见入口。
这篇会从最小概念开始往上拼:位的语义、数字与符号两种写法、chmod/chown/chgrp、用 umask 控制默认权限、三个特殊位、用 getfacl/setfacl 玩 ACL,最后一份能直接对照排障的清单。例子都是实际碰得到的——webroot、团队共享目录、/tmp、被 +i 钉死的配置文件——而不是教材里的玩具。
权限模型:owner / group / others

Linux 是多用户系统,所以每个 inode(普通文件、目录、符号链接、设备……)都挂着三个身份接口:
- 属主(
u):拥有这个 inode 的 UID,一般就是创建者。 - 属组(
g):一个 GID,组内成员共享组级权限。 - 其他人(
o):既不是属主、也不在属组里的所有人。
内核做权限检查不是"把匹配的位加起来",而是一个严格的首匹配级联:
if 调用者 uid == 文件 uid -> 用 OWNER 位,决策即终
elif 调用者所属组集合 ∩ {文件 gid} -> 用 GROUP 位,决策即终
else -> 用 OTHERS 位
这个顺序带来一个常常吓到人的后果:owner 位禁止做某事,那么"恰好在组里"也救不了你。chmod 047 myfile 这一下,属主自己读不了自己的文件,反倒 others 能读、能写、能执行。
root(uid 0)凭借 CAP_DAC_OVERRIDE 直接绕过整个检查。唯一一个反方向的例外是 sticky bit,下面会讲。
10 个字符的 mode 字符串
ls -l 输出形如 -rwxr-xr-x,从左到右逐位看:
| 位置 | 含义 |
|---|---|
| 1 | 文件类型:- 普通、d 目录、l 软链接、c/b 字符/块设备、s socket、p FIFO |
| 2–4 | 属主的 rwx |
| 5–7 | 属组的 rwx |
| 8–10 | 其他人的 rwx |
每一位是下面三种之一:
r(4)—— 读w(2)—— 写x(1)—— 执行(在目录上是"穿过")
每一组三位相加得到一位 8 进制,三组拼起来就是熟悉的 755、644、600。如果在 x 的位置看到 s/S/t/T,说明还附带了一个特殊位——后面会展开。
rwx 在文件与目录上:最容易翻车的地方
绝大多数权限 bug 都藏在这里。同样的字母,不同的语义。
在普通文件上
| 位 | 含义 | 没它就做不到 |
|---|---|---|
r | 读字节 | cat、less、cp 当源 |
w | 覆盖、截断、O_TRUNC 打开 | > 重定向、原地编辑 |
x | 把文件当程序执行(要有合法的 ELF 头,或脚本要有 shebang) | ./prog |
注意 w 只管修改文件内容。删除文件不需要文件本身的 w——那由父目录的 w 决定。
在目录上
| 位 | 含义 | 没它就做不到 |
|---|---|---|
r | 列出目录里有哪些条目 | ls dir/ |
w | 创建、删除、重命名条目(同时还要有 x) | touch dir/x、rm dir/x、目录内 mv |
x | 在目录里按名查找、并把它作为路径的一部分穿过去 | cd dir、cat dir/已知名字、任何包含 dir 的路径访问 |
三个小实验把规则坐实:
情况 A —— 有 r 没 x(mode 644)
| |
情况 B —— 有 x 没 r(mode 311)
| |
“私有 bin"目录的小把戏就是这么来的:能穿过、不能列。
情况 C —— 有 w 没 x(mode 622)
| |
目录上单独的 w 毫无用处,必须配 x。
目录的经验法则:x 是承重位;w 只有和 x 一起才有意义;r 是锦上添花。
chmod:数字 vs 符号写法

两种写法最终写入的都是相同的九位。差别在于:你是在声明一个绝对的目标,还是在做一个相对的修改。
数字写法 —— 绝对
按身份把 r=4、w=2、x=1 加起来,三段拼成三位:
| |
适合已知目标状态的场景:脚本化部署、新生成的文件,反正之前是什么不重要。
符号写法 —— 相对
身份(u/g/o/a)+ 操作符(+/-/=)+ 位(r/w/x/X/s/t):
| |
对目录树来说,大写的 X 是杀手锏:
| |
X 只会在目录、以及"原本就至少有一个 x“的文件上加 x。没有它,chmod -R 755 project/ 会把每个 .md、.png、.csv 也变成"可执行”——本身没什么破坏力,但很丑,对扫描错配 webroot 的人来说也是免费情报。
符号写法适合只动一个维度而不去碰其它位的场景。
chown / chgrp:改所有权
| |
几条底线:
- 只有 root 能任意改所有权。普通用户不能把文件"送给"别人——否则就能用"把文件改成别人的"绕开磁盘配额。
- 属主可以
chgrp到自己所属的任意组;不能把文件丢进自己不在的组里。 chown出于安全考虑,会清除普通文件上的 setuid/setgid 位——重要细节:如果你chown root了一个 SUID 二进制,事后必须重新打上 SUID。
几个会写一百遍的常见模式:
| |
umask:默认权限的过滤掩码
umask 是新 inode 创建时要从系统默认里减掉的位。系统默认是:
- 普通文件
0666(不给x,避免数据文件意外可执行) - 目录
0777(目录需要x才能穿过)
实际权限 = 默认 AND NOT umask。
| umask | 新文件 | 新目录 | 适用 |
|---|---|---|---|
022 | 644 | 755 | 桌面默认;世界可读 |
027 | 640 | 750 | 服务器 / 生产 —— 属主之外只允许同组访问 |
002 | 664 | 775 | 用了 USERGROUPS(每个用户一个独立主组)的开发协作机 |
077 | 600 | 700 | 最严 —— ~/.ssh、密钥目录 |
查看与修改:
| |
系统级默认在 /etc/login.defs(UMASK)和 /etc/profile、/etc/pam.d/* 里。systemd 服务请在 unit 文件里写 UMask=,不要指望它读 ~/.bashrc——服务进程从来不读。
特殊权限位:SUID、SGID、sticky

chmod 实际上接受的是四位八进制。最高位打包了三个开关:4(SUID)、2(SGID)、1(sticky)。可以叠加:chmod 6755 就是 SUID + SGID + 755。
SUID(4xxx)—— 以属主身份运行
加在可执行文件上之后,进程以文件属主的有效 UID 运行,无论是谁启动的。最经典的例子:
| |
passwd 要写 /etc/shadow(0640 root:shadow),但普通用户必须能改自己的密码。SUID + 一个被仔细审计过的小程序,是这个矛盾的经典解法。
| |
小写 s 表示 SUID 且 底下的 x 也置上了;大写 S 表示 SUID 在但 x 没有——基本一律是配错。
SUID 是真的危险。SUID-root 二进制里一个 bug 就是本地提权。要定期审计:
| |
凡是不在标准列表里(passwd、sudo、mount、su、老发行版的 ping 等)的,都得说出存在的理由。
SGID(2xxx)—— 两种用法
加在可执行文件上:进程的有效 GID 变成文件的属组。常见于需要访问某个私有组资源的工具,比如 wall 写 tty 组拥有的 /dev/tty*。
加在目录上(远更常见的用法):在该目录里创建的文件和子目录自动继承该目录的属组,而不是创建者的主组。这才是搭团队共享目录的正路:
| |
之后 developers 组里任何人在里面建的文件都是 group=developers,组里别的人能读能写。没有 SGID,每次创建后你都要记得 chgrp ——而人就是会忘。
Sticky bit(1xxx)—— 受限删除
加在目录上时它改写了一条规则:即使目录对所有人可写,里面的条目也只能由文件的属主(或 root)unlink 或重命名。/tmp 是教科书案例:
| |
如果没有 sticky,1777 的 /tmp 就成了任人删彼此 socket、锁文件的乱场。
| |
加在文件上的 sticky 是历史遗留(曾经表示"text 段常驻 swap”),现代 Linux 直接忽略。
常见场景 —— 可直接拷走,附原理

1. 跑脚本提示 “Permission denied”
| |
没有 x。修:
| |
如果改完仍然报 exec format error,是脚本第一行缺 shebang(如 #!/usr/bin/env bash),内核不知道用哪个解释器。
2. Web 服务器返回 403
nginx/apache 在 Debian/Ubuntu 上以 www-data 跑,在 RHEL 系上以 nginx 跑。它要的是:
- 目标文件的
r - 从根到这个文件的每一段目录的
x
后面这条最容易被忽视——/home/alice/site/ 一般是 700,www-data 连进都进不去。要么把 docroot 挪到 /var/www/ 下,要么给路径放行:
| |
3. 团队共享项目目录
目标:developers 组里所有人都能读写所有内容,组外完全看不到。
| |
2(SGID)保证继承;770 把外人挡掉;umask 002 让新文件落到 664 而不是默认的 644,组内才能改。
4. 多用户临时目录
发行版已经替你做好了——/tmp 就是 1777。如果你要类似的共享暂存区:
| |
5. 锁紧私钥
| |
ssh 拒绝使用任何组或世界可读的私钥——这是特性,不是 bug。
ACL:当三个桶不够用时

经典 mode 位只给三个桶。现实需求经常超出这个范围:比如"让审计员 eve 读这份报告,但她不在 developers 组里",或者"封掉一个特定外包同学对这个目录的访问"。POSIX ACL 就是干这个的。
看 ACL
| |
ls -l 末尾的 +(如 -rw-r-----+)就是 ACL 存在的可见信号。
设 ACL
| |
对目录,-R 是递归,默认 ACL 还能传给将来在里面创建的子项 —— 类似 SGID,但是按用户粒度的:
| |
ACL mask 这个坑
mask:: 那一行是除 user::(属主)和 other:: 之外,所有"额外条目"的有效权限上限。一个常见惊吓:在带 ACL 的文件上跑 chmod g=... 实际改的是 mask,而不是真正的 group 条目。要直接改 group 条目:
| |
文件系统得挂载时支持 ACL。现代 ext4/xfs/btrfs 默认就支持,可以用 tune2fs -l 或 mount | grep acl 确认。
chattr / lsattr:文件系统层的属性
chattr 设置的是 ext4/xfs 的文件属性,位于权限系统之下——对 root 也生效。
| |
+i 适合钉死关键配置(/etc/fstab、/etc/passwd、/etc/sudoers),让一次手抖的 sed -i 不至于把救援启动也毁掉。+a 适合钉日志文件,让事后篡改更难。两者都是回答"怎么防止 root 不小心删掉这个"的正解——不先 chattr -i,root 也删不掉。
排障清单
按下面的顺序走,能覆盖大约 9 成的权限问题。
Step 1 —— 我究竟是谁?
| |
如果"消费者"是个服务,相关身份是 systemd unit 里的 User= / Group=,不是你登录的那个。
Step 2 —— 检查路径上的每一段目录
只有叶子文件的 r+x 是不够的;内核会对路径上的每一段重新检查 x。最快的工具是 namei:
| |
第一行"相关身份缺 x“的目录就是元凶。
Step 3 —— 是不是 ACL?属性?挂载选项?
| |
/tmp 如果挂了 noexec,无论你 chmod 怎么改,./script.sh 都会被默默拒绝。
Step 4 —— SELinux / AppArmor
RHEL/Fedora/CentOS 上,getenforce 是 Enforcing?那就 ls -lZ file 看 SELinux 标签,audit2why 解释最近的拒绝。Ubuntu 上,aa-status 列出 AppArmor 配置。这些机制可以在经典权限"看上去允许"的情况下照样拒绝。
常见症状对照
已知没问题的脚本仍然 Permission denied → 缺 x、缺 shebang,或者在 noexec 挂载点上。
Web 服务器 403 → www-data 在哪都是 others;用 namei -l 找路径上缺 x 的那一段。
rm: cannot remove ...: Operation not permitted(注意:不是 Permission denied)→ lsattr 会显示 +i;先 chattr -i。
居然能写一个不属于自己的文件 → 看父目录的 w。文件属主对删除/重命名不重要。
心智模型与延伸阅读
把下面三条嚼透,几乎所有真实情况都能扛过去:
- 文件 vs 目录:在文件上
x是"它是不是程序”。在目录上x是唯一通向内容的闸门。 - 首匹配级联:owner 或 group 或 others——永远不是相加。审计的时候问自己"调用者落进哪个桶?"
- 特殊位各司其职:SUID = “让无权调用者去做某件被精确审计过的特权操作”;目录上的 SGID = “团队目录”;sticky = “公共可写但不能互删”。在这些场景之外,不要乱开。
继续往深里走的方向:
man 1 chmod、man 2 chmod、man 5 acl、man 1 chattr—— 权威来源,意外地好读。- 《Linux 用户管理》(本系列下一篇)——
/etc/passwd、/etc/shadow、组、sudoers、PAM。 - 《Linux 管道与重定向》 —— 在文件描述符与
stdin/stdout/stderr之上继续搭。 - 强制访问控制(MAC)框架:SELinux(RHEL 系)和 AppArmor(Debian/SUSE 系)在我们这里讲的自主访问控制之上再加一层。同样的问题,不同的答案。
如果现在让你看 drwxr-s---+ 4 alice developers 这一行,你能立刻说出属主、属组、设了哪个特殊位、还有一份 ACL 在场,并且说出 “developers 组里的 bob” 和 “组外的 eve” 各自能做什么——那模型就在你脑子里了,剩下就是肌肉记忆。