Linux 权限与属主管理:chmod、chown 从数字模式到特殊权限位实战
一个目录权限乱了——可能是遗留脚本用 root 建的文件,也可能是 SCP 传过来的文件属主对不上——网站访问直接 403,部署脚本报 Permission denied。这时候大部分人的第一反应是抄一条 chmod -R 777,网站是能访问了,但等于把整个目录对所有人开放读写执行,治标不治本,出了安全事故说不清楚。
chmod 和 chown 是治本的工具,但数字模式怎么算、setuid/setgid/sticky bit 到底在干嘛,很多人一直没搞清楚过。这篇把这两个命令的实际用法过一遍,最后用一个真实场景收尾。
这是 Linux 常见命令系列的第一篇详细教程,承接 Linux 常见命令与工具地图。
权限模型回顾:读懂 ls -l 那串字符
ls -l 输出的第一列长这样:-rwxr-xr--。十个字符拆成四段:
- 第 1 位:文件类型(
-普通文件,d目录,l符号链接) - 第 2-4 位:owner(文件所有者)的读写执行权限
- 第 5-7 位:group(所属组)的读写执行权限
- 第 8-10 位:other(其他所有人)的读写执行权限
每一组里 r/w/x 对应的数字是固定的:r=4、w=2、x=1,一组权限的数字就是这三个数加起来。rwxr-xr-- 换算成数字就是 754——owner 是 4+2+1=7,group 是 4+0+1=5,other 是 4+0+0=4。下一节的数字模式全靠这套加法。
chmod 数字模式:三位数背后的加法
数字模式就是把 owner、group、other 三组权限各自换算成 0-7 的数字,拼在一起传给 chmod:
1 | chmod 644 config.yaml |
记这三组数字对(644 配置、755 可执行、600 私钥)就能覆盖日常八成场景,其余情况现算:想清楚 owner/group/other 各自要不要读、写、执行,三个数字加出来拼在一起就是。
chmod 符号模式:什么时候比数字模式好用
数字模式的前提是你想清楚了三组权限的最终状态。但很多时候你只想”在现有权限基础上加一位”,这时候符号模式更顺手:
1 | chmod u+x run.sh |
u/g/o/a 决定改谁,+/-/= 决定加、减还是直接赋值,r/w/x/X 决定改哪个权限位。这四个维度组合起来,比每次都要心算数字模式更不容易出错。
特殊权限位:setuid、setgid、sticky bit 到底在干嘛
回顾第一节的权限模型:rwx 三组权限,每一位要么是字母要么是 -。但用 ls -l 查看某些文件或目录时,你会碰到不在这个范围内的符号:
1 | -rwsr-xr-x 1 root root /usr/bin/passwd |
第一行 owner 的 x 位置变成了 s,第二行 group 的 x 位置也是 s,第三行 other 的 x 位置变成了 t。这三个符号不属于 rwx 常规模型,就是这一节要讲的特殊权限位:setuid、setgid、sticky bit,分别顶替 owner / group / other 三组里的 x 位置。它们不是额外发明出来的权限,而是在执行权限基础上叠加的特殊标记,各自解决一个具体问题,一个一个来看。
setuid:让别人执行你的程序时,暂时”变成你”
普通用户能改自己的密码,但密码是存在 /etc/shadow 里的,这个文件只有 root 能写。矛盾就在这——普通用户没有权限写 /etc/shadow,却要靠 passwd 这个程序去改它。
setuid 就是解决这个矛盾的:给 passwd 这个可执行文件加上 setuid,效果是不管谁来执行它,执行的过程中都”变成”这个文件的所有者(也就是 root)。所以普通用户执行 passwd,程序运行期间临时有了 root 的权限,改完密码,权限又还回去。
1 | chmod u+s /usr/bin/passwd |
只对可执行文件生效,这个属性本身也有风险——如果一个装了 setuid 的程序本身有漏洞,攻击者可能借着它拿到 root 权限,所以只在真正需要的地方加,不要乱设。
setgid:让共享文件夹里新建的文件自动归到同一个组
团队共享一个目录时经常遇到这种烦心事:A 建的文件属组是 A 的主组,B 建的文件属组是 B 的主组,两人互相没权限改对方的文件,得手动 chgrp 一遍遍改。
setgid 加在目录上就能解决:这个目录下新建的任何文件和子目录,属组都自动跟目录本身一致,不看是谁建的。
1 | chmod g+s /srv/shared-project |
(setgid 加在可执行文件上也有效果,跟 setuid 类似——变成文件所属组的身份,但这个用法比加在目录上少见得多,日常基本用不到。)
sticky bit:谁建的文件谁能删,别人删不了
想象小区里的公共快递柜:谁都能往里放包裹,但只有放的人自己能取走,别人拿不走你的包裹。/tmp 目录就是这个逻辑——所有人都能在里面写文件(不然没法用),但如果没有额外限制,任何人也能删掉别人的临时文件,那就乱套了。
sticky bit 加在目录上,效果是:即使某人对这个目录有写权限,也只能删自己建的文件,删不了别人的。
1 | chmod +t /tmp |
三个位对应的数字,以及 ls -l 怎么看
三个特殊位在数字模式里对应第四位(写在平时那三位数字前面),可以叠加:
| 权限位 | 数字 | 加在哪 | ls -l 里顶替谁的 x |
一句话 |
|---|---|---|---|---|
| setuid | 4 | 可执行文件 | owner 组的 x(第 3 位) | 别人执行时临时”变成”文件所有者 |
| setgid | 2 | 目录 | group 组的 x(第 6 位) | 目录下新建的文件自动跟目录同一个组 |
| sticky bit | 1 | 目录 | other 组的 x(第 9 位) | 谁建的文件谁能删,别人删不了 |
三个位置各管各的,不会挤在一起:setuid 是 owner 的事,setgid 是 group 的事,sticky 是 other 的事,加在哪组权限上就顶替哪组的 x。
1 | chmod 4755 /usr/bin/passwd # setuid(4) + 标准的 755 |
开头那三行 ls -l 输出里的 s/t 都是小写,这是正常情况——owner(或 other)本身也有执行权限。看大小写的规则就一条:**小写表示正常,大写表示”这个位置本该有执行权限,但没有”**。比如 rwSr--r--,大写 S 说明 setuid 生效了,但 owner 没有执行权限,这种设置通常没意义,多半是配错了。看到大写的 S 或 T,基本可以判断是漏加了执行位,回去检查一下。
chmod -R 递归的坑:符号链接会不会被改
chmod -R 递归修改目录下所有内容时,符号链接是这里最容易被误解的地方。符号链接本身的权限在 Linux 上不生效(永远显示 lrwxrwxrwx,改它没有意义)。默认行为其实比较克制:只有当符号链接直接出现在命令行参数里、并且指向一个目录时才会跟随继续递归;递归过程中在目录内部碰到的符号链接不会被跟随,所以正常情况下一条 chmod -R 755 . 不会意外爬到符号链接指向的别处。真正危险的是显式加了 -L——这个选项让 chmod 遇到任何目录型符号链接都跟随,如果目录里的链接指向了 / 或者其他敏感路径,递归范围会远超预期。
三个选项控制这个行为,只有最后一个生效(重复指定以最后一个为准):
1 | chmod -R -H 755 project/ # 默认行为:命令行里直接给出的符号链接,如果指向目录会跟随; |
不确定目录里有没有埋着符号链接时,先跑一遍 find project/ -type l 心里有数,比事后排查权限为什么跑到别处去了省事得多。
chmod 常用参数速查表
前面几节讲的都是这张表里的内容,汇总在一起方便直接查:
| 参数 | 作用 |
|---|---|
-R, --recursive |
递归处理目录下所有文件和子目录 |
-v, --verbose |
每处理一个文件都输出结果 |
-c, --changes |
和 -v 类似,但只在权限真的发生变化时才输出 |
-f, --silent, --quiet |
屏蔽大部分错误提示 |
--reference=文件 |
权限抄自参考文件,不用自己写数字或符号模式 |
-H |
递归时只跟随命令行直接给出的符号链接(默认行为) |
-L |
递归时跟随所有遇到的目录型符号链接(最危险) |
-P |
递归时完全不跟随任何符号链接 |
u / g / o / a |
符号模式里指定改谁:owner / group / other / all |
+ / - / = |
符号模式里指定加上、去掉还是直接赋值 |
r / w / x / X |
读 / 写 / 执行 / 条件执行(目录总加,文件按原有执行权限判断) |
数字模式前导 4/2/1 |
特殊权限位:setuid / setgid / sticky bit,可叠加写在三位数字模式前面 |
chown:属主属组一起改,还是分开改
chown 修改文件的所有者和所属组,语法是 chown 属主:属组 文件:
1 | chown deploy:www-data app.log |
chgrp 和 chown :group 做的是同一件事——chgrp 只是历史遗留的专用命令,只能改组,功能被 chown 的 :group 语法完全覆盖。新写脚本用 chown :group 更统一,不用记两个命令。
用户名在 /etc/passwd 里查不到(比如跨系统同步过来的文件,UID 在本机没有对应用户名)时,直接传数字 UID:GID 也行:
1 | chown 1001:1001 orphaned_file |
-R 递归的语义比 chmod 简单得多——符号链接本身没有属主属组的概念(继承自它指向的目标),chown 的递归不存在上面 chmod 那种”改错地方”的风险。
还有两个不算高频、但排查时很有用的参数:--from=旧属主:旧属组 只在文件当前属主属组和指定值一致时才真正执行改动,批量操作时能防止误伤不该改的文件;-h/--no-dereference 改的是符号链接本身而不是它指向的目标——默认情况下 chown 是解引用符号链接、改目标,这一点和很多人的直觉相反,需要改链接本身时必须显式加 -h。
chown 常用参数速查表
| 参数 | 作用 |
|---|---|
-R, --recursive |
递归处理目录下所有文件和子目录 |
-v, --verbose |
每处理一个文件都输出结果 |
-f, --silent, --quiet |
屏蔽大部分错误提示 |
-h, --no-dereference |
改符号链接本身,不改它指向的目标(默认是改目标) |
--reference=文件 |
属主属组抄自参考文件 |
--from=旧属主:旧属组 |
只有当前属主/属组和这里指定的一致才真正执行改动,防误伤 |
user:group |
同时指定属主和属组 |
:group |
只改属组(前面留空),等价于 chgrp |
实战:修一个权限乱掉的部署目录
场景:Nginx 的静态资源目录 /var/www/site,本来该属于 www-data 用户和组,结果某次运维手滑用 sudo 直接解压部署包,整个目录变成了 root:root,Nginx 进程读不了文件,网站全站 403。
第一步,改回属主属组:
1 | sudo chown -R www-data:www-data /var/www/site |
第二步,权限也得配套——目录需要可进入(执行位),文件不需要:
1 | sudo find /var/www/site -type d -exec chmod 755 {} \; |
这里不能图省事直接 chmod -R 755 /var/www/site——那样会把所有静态文件也变成可执行,权限比实际需要的更开放。目录和文件分开处理才是准确的做法,find + chmod 这个组合在权限修复场景里比手写循环更简洁可靠。
权限之外
sudo 决定”谁能用 root 身份执行命令”,和 chmod/chown 决定”文件本身谁能读写执行”是两个独立的问题,值得单开一篇讲。Linux 命令系列下一篇计划写 rsync——它的参数比 chmod/chown 多得多,增量同步、排除规则、远程传输这些够写满一整篇。





