新同事 chmod -R 777 /usr 之后整台机器的 sudo 都废了——Linux 权限位深度解析
本文目录
先说结论
第一条:Linux 权限不是 3 位是 4 位:特殊位(setuid/setgid/sticky)+ user/group/other 的 rwx,完整 mode 是 4 位八进制比如 4755、2775、1777。
第二条:chmod 777 是'抹平'——它会把所有特殊位清掉(如果没显式写 4/2/1),这也是为什么 chmod -R 777 /usr 是灾难。
第三条:生产上永远不要对系统目录用 chmod -R;真要改权限,先 ls -l 看当前值,用 find + -perm 精准改,出事前做快照。
先把这段讲顺;面试官追问时,再展开下面的原理和细节。
详细讲解
某出海硬件品牌真实事故:一个刚入职的实习生看到 /usr/local/xxx 某个二进制报 Permission denied,图省事直接 chmod -R 777 /usr。命令跑完的瞬间,整个机器的 sudo 就用不了了——因为 sudo 依赖 setuid bit(也就是 4755 里那个 4),chmod 777 把 setuid 位抹掉了,sudo 变成普通文件,root 权限提权直接废掉,整台机器必须物理重启救援模式才能恢复。
这道题考的不是 rwx 是什么意思(那太基础了),而是——你知道 setuid/setgid/sticky 这三个特殊位吗?你知道 umask 怎么算?你知道 chmod -R 为什么是生产上最危险的命令之一吗?
权限位(1):rwx 之外的那 4 种你必须知道
普通的 user/group/other 三组 rwx 大家都懂,这里只讲面试常考但多数人答不清的特殊位:
bash - 特殊位速查
# 完整 mode 是 4 位八进制:SUGO
# S 是特殊位组合,U/G/O 是 owner/group/other 的 rwx
#
# S 位(高位)的三个 bit:
# 4 = setuid 设在可执行文件上:执行时以文件 owner 身份运行
# 2 = setgid 文件:以 group 身份运行 / 目录:新文件继承目录 group
# 1 = sticky 目录:只有文件 owner 才能删除(/tmp 就是 1777)
# 查看特殊位:ls -l 看第 4 个字符 / 7 个字符
ls -l /usr/bin/sudo
# -rwsr-xr-x 1 root root ... /usr/bin/sudo
# ^ 这个 s 就是 setuid(原本 x 的位置变成 s)
ls -l /usr/bin/wall
# -rwxr-sr-x 1 root tty ... /usr/bin/wall
# ^ 这个 s 是 setgid
ls -ld /tmp
# drwxrwxrwt 20 root root ... /tmp
# ^ 这个 t 是 sticky bit
setuid 的真实作用:sudo、passwd、ping、mount 这些命令都需要 root 权限才能干活,但普通用户要能执行它们。解决办法就是给它们加 setuid 位,owner 是 root——用户执行时,进程的 effective uid 临时变成 root。这是 Linux 权限体系的基石之一。
为什么 chmod 777 是灾难:因为 777 对应的完整 mode 是 0777,前面那个 0 把特殊位全清了。sudo 原本是 4755(setuid + rwxr-xr-x),一 chmod 777 变成 0777,setuid 没了——sudo 就是一个普通程序,再跑 sudo xxx,effective uid 还是你自己,root 提权直接失效。
权限位(2):umask 到底在管什么
umask 是新建文件/目录时的默认权限掩码——注意是'掩码',也就是说 umask 里的位会从默认权限里'扣掉'。
umask
# 0022(最常见的个人账户默认)
# 新建文件默认 666,扣掉 022 = 644
touch /tmp/a.txt && ls -l /tmp/a.txt
# -rw-r--r-- 1 wood wood 0 ... /tmp/a.txt
# 新建目录默认 777,扣掉 022 = 755
mkdir /tmp/d && ls -ld /tmp/d
# drwxr-xr-x 2 wood wood ... /tmp/d
# 为什么新建文件不给 x?因为默认文件的 max 就是 666(不该自动可执行)
# 这是 Linux 的一个'聪明'设计:不是简单的 777 - umask
# 生产服务器通常用 027(组可读不可写,其他全屏蔽)
umask 027
touch /tmp/b.txt && ls -l /tmp/b.txt
# -rw-r----- 1 wood wood 0 ... /tmp/b.txt
面试陷阱:有人会问'umask 022 新建文件是 755 吗?'——错。文件的基准是 666(不是 777),目录的基准是 777。所以 umask 022 新建文件是 644,新建目录是 755。这个细节是面试官的送分题。
权限位(3):ACL 和 chattr,rwx 管不住的时候怎么办
rwx 只能管 owner / group / other 三类主体,但真实场景里你经常需要'A 用户可读,B 用户可写,C 组只读'——这时候就要上 ACL(Access Control List)。
# 安装:centos 默认有,ubuntu 可能需要 apt install acl
# 文件系统需要 mount 时支持 acl(ext4/xfs 默认支持)
# 给单个用户授权(不动 owner)
setfacl -m u:alice:rx /data/shared/report.csv
# m = modify,u:alice:rx = 给用户 alice 加 r+x
# 查看 ACL
getfacl /data/shared/report.csv
# user::rw-
# user:alice:r-x ← 多出来的一行
# group::r--
# mask::r-x
# other::r--
# 设置默认 ACL(目录下新建文件继承)
setfacl -d -m u:alice:rwx /data/shared/
# 删除某条 ACL
setfacl -x u:alice /data/shared/report.csv
# 清空所有 ACL
setfacl -b /data/shared/report.csv
# chattr:文件系统级的'不可变'属性,防删防改
chattr +i /etc/resolv.conf
# +i = immutable,root 也不能改/删,直到 chattr -i
lsattr /etc/resolv.conf
# ----i--------------- /etc/resolv.conf
生产用法:chattr +i 我经常用在那些'被人一不小心改了就完蛋'的关键配置上,比如 /etc/resolv.conf、/etc/fstab、/etc/nginx/nginx.conf。加了 +i 之后哪怕是 root 也改不了,必须先 chattr -i 才能改——这多一道手续能挡住 90% 的误操作。
权限工具对比
| 需求', '工具', '典型用法', '注意 |
|---|
| 改基础 rwx', 'chmod', 'chmod 644 file', '慎用 -R |
| 改所有者', 'chown', 'chown wood:data file', '-R 同样危险 |
| 精细授权单用户', 'setfacl', 'setfacl -m u:x:rx file', '需 FS 支持 acl |
| 防误删误改', 'chattr +i', 'chattr +i config.ini', 'root 也改不了 |
| 默认新建权限', 'umask', 'umask 027', '建议写 /etc/profile |
| 查看完整权限', 'getfacl', 'getfacl file', '显示 ACL + mask |
面试高频追问
追问一:你怎么给一个新接手的服务器做'权限体检'?
我有一个标准清单,新接手机器第一天必跑:① find / -perm -4000 -type f 2>/dev/null 列出所有 setuid 文件,对比标准清单(sudo/passwd/ping 等),有没有被加过奇怪的 setuid;② find /tmp -type f -perm -0002 找出 other 可写的文件,判断有没有文件放错位置;③ ls -lR /etc | grep -v 'root root' 看有没有非 root 的配置文件;④ lsattr /etc/ 看有没有 +i 锁定的文件。这四个命令跑完,基本能排除大部分权限配置问题。
追问二:chown 和 chmod 同样危险,你怎么防止 chmod -R / chown -R 跑错目标?
三条硬规矩:一是绝对不在根目录或 /etc、/usr、/var 上跑 -R;二是任何 -R 操作必须先 find ... -perm ... 或 find ... -user ... 看一眼受影响的文件列表,确认没超出预期;三是关键目录操作前先打快照(LVM snapshot、EBS snapshot,甚至 tar czf 备份一下都行)。某出海硬件品牌那次事故后,我们团队定的规矩是'任何 chmod/chown -R 操作必须结对执行',一个人敲命令,另一个人复核。看起来麻烦,但真的挡住过好几次事故。
追问三:Docker 容器里的权限和宿主机怎么映射?
容器里的 UID 和宿主机是共享的——容器里的 root(0) 就是宿主机的 root(0),容器里的 1000 就是宿主机的 1000。这就是为什么 docker run 默认用 root 跑是个安全问题,一旦逃逸就是宿主机 root。生产做法是:① 容器里用 USER 指定非 root 运行;② 挂载宿主机目录时用 -u $(id -u):$(id -g) 让容器内进程以宿主机 UID 跑,避免产生 root 持有的文件;③ K8s 环境下用 runAsUser + fsGroup 统一管理;④ 敏感场景启用 user namespace 把容器 UID 和宿主机隔离。
实战踩坑:那次 chmod -R 777 /usr 是怎么救回来的
事故当时大概长这样:实习生 chmod -R 777 /usr 按下回车,10 秒后想 sudo 回滚——提示'sudo: effective uid is not 0, is /usr/bin/sudo on a file system with the nosuid option set or an NFS file system without root privileges?',sudo 已经废了。他 panic 了叫我,我一看只能走物理救援。
救援步骤:① 从 grub 进单用户模式(rescue),init=/bin/bash 直接拿 root shell;② mount -o remount,rw / 让根分区可写;③ 从另一台同版本同发行版的机器上 ls -l /usr/bin/sudo 拿到正确权限(4755),手动 chmod 4755 回去;④ 但这只是 sudo 单个文件,整个 /usr 下还有几百个 setuid 文件不知道正确值。最后只能重装 coreutils、util-linux、shadow-utils 这几个包:rpm --setperms coreutils util-linux shadow-utils sudo(rpm 支持从 spec 文件重置权限);⑤ 重启。
bash - 事后防复发
# 事后给运维组发的统一规范
# 1) 所有 chmod/chown -R 操作必须先 dry-run
find /target -type f -printf '%m %u:%g %p\n' | head -20
# 2) 关键系统文件用 rpm -V 定期校验
rpm -Va 2>/dev/null | grep -E '^.{1,8}M'
# 输出:.M....... /usr/bin/sudo ← M 表示 mode 被改过
# 3) rpm --setperms 是救命稻草:按 spec 重置包内所有文件权限
rpm --setperms coreutils util-linux shadow-utils sudo
# 前提是包没被删除,只是权限乱了
# 4) 重要目录用 chattr +i 锁死
chattr +i /etc/sudoers
chattr +i /etc/sudoers.d/*
血泪教训:这次事故后团队多了一条'红线规则'——生产服务器上禁止任何 chmod -R / chown -R 作用在 /、/usr、/etc、/var、/lib、/bin、/sbin 上。要做,必须先在测试机复现命令 + code review + 双人操作。规则看起来死板,但它救过不止一次事故。
总结
Linux 权限不只是 rwx,特殊位(setuid/setgid/sticky)、umask、ACL、chattr 构成一个完整体系。面试时你能把 chmod 4755 里那个 4 讲明白、能说清 /tmp 为什么是 1777、能讲 umask 022 和文件的基准 666 的关系,已经能打败 80% 的候选人。至于 chmod -R 777 /usr 的故事——每个做 Linux 的人最好都听过一次,最好永远别亲身经历。
下一篇预告:crontab 里脚本白天跑得好好的,凌晨 3 点就不跑了——cron 踩坑 5 连击。
你或你身边同事有没有过'一条命令毁掉一台机器'的惊险时刻?欢迎评论区分享。
关注「Wood聊大数据」| 9年大数据老兵,聊技术、聊职场、聊成长