先把权限算清楚:rwx 对文件和解对目录是两套语义
大多数人对权限的误解,来自把目录的 rwx 当成了文件的 rwx。对文件,r 是读内容、w 是写内容、x 是执行;对目录,r 是能列出内容、w 是能在其中建/删/改名条目、x 是能穿过这个目录(traverse)访问里面的目标。
这意味着一个"只有 -r-------- 的目录"看起来能读却进不去,因为少了 x;而一个"无 r 但有 x 的目录"你能访问里面的文件却 ls 不出名字。这类边界是无数莫名 Permission denied 的来源,先在这层把模型立正。
ll -d /srv/data
# 目录必须有 x 才能被 traverse
chmod a+x /srv/data
# 查看后最直观的校验
ls -l /srv/data/secret.txt /srv/data
# 十进制速查:r=4 w=2 x=1
umaskACL 出场:当位权限表达不了"一个人一种权"
业务常见需求是"开发能写、运维能写、其余只读",而传统 9 位只能给 group 一个口径,做不到按人分权。这时用 setfacl/getfacl 给特定用户补写权限,而不用把所有人塞进同一个组。
注意 ACL 会改变权限的呈现:ls -l 的权限位后面会多一个 +,这时原 rwx 位代表的就是 mask,而非真实读写。很多人在 + 出现后直接 chmod 覆盖,把 ACL 整个弄没,属于非常典型的坑,下面给示范与修复。
setfacl -m u:devuser:rwx -R /srv/app # 给特定用户递归写
setfacl -m u:opsuser:rwx /srv/app/deploy.sh
setfacl -x u:devuser /srv/app/secret # 撤销某人
# 错误示范:chmod 覆盖掉 ACL
chmod 644 /srv/app/deploy.sh # + 消失,ACL 被重置
# 修复对照:保留 ACL 前提下再改 mask
setfacl -m m::rwx /srv/app/deploy.sh
# 查看
getfacl /srv/app/deploy.shsetuid/SGID/sbit:三个特殊位是泳池边上的滑板
setuid 让二进制以属主身份运行,典型是 /usr/bin/passwd 以 root 运行。它非常有用,但任何给自己写的脚本加 setuid 都是事故:脚本的 shbang 以 bash 解释,root 语境风险翻倍。SGID 让目录里新建文件的属组继承目录组,适合共享协作目录。
sticky 位(1777 的尾部 1)只允许属主删自己的文件,/tmp 就是活标本。判断特殊位别只看 ls 的 s/t 字母——大写 S/T 表示"位设了但 x 没设",这时它实际上不生效且很迷惑。
ls -l /usr/bin/passwd # -rwsr-xr-x 有 s 正常
find / -perm -4000 2>/dev/null # 审计所有 setuid 文件
chmod 4755 /opt/app/bin/app # setuid(通常不要对脚本用)
chmod 2755 -R /srv/shared # SGID 共享目录 组继承
# 大写 S/T = 位设了可能没 x
ls -l /tmp # drwxrwxrwtsudo:别再给 ALL,最小授权才睡得着
把用户扔进 wheel/sudo 组等于把整台机器交给它,可运维的现实却是"那个人只要会重启 Nginx"。用 visudo 给单一命令授权,NOPASSWD 只给低频且无副作用的动作,危险的系统管理命令留在要密码的桶里。
命令名路径要写绝对路径,还要当心别名和通配符陷阱:NOPASSWD: ALL 和 NOPASSWD: /usr/sbin/reboot 天差地别。sudo -l 显示当前用户能执行什么,是验证授权的第一手工具。
sudo -l
# visudo 内容片段
# 错误示范:整机全部放开
# dev ALL=(ALL) NOPASSWD: ALL
# 修复对照:只给单一命令
dev ALL=(root) NOPASSWD: /usr/bin/systemctl restart nginx
dev ALL=(root) /usr/sbin/reboot, /usr/bin/systemctl stop firewalld
# 校验语法
visudo -c
sudo -l -U dev验证生效:用两个账号实测而不是相信位组合
纸上谈兵的权限配置很容易漏:mask 挡住了本来授权的人、find 不到 root 能看的 setuid、或是某条 rule 被更靠前的通配符吞掉。最有说服力的验证是切到受影响账号实际测一次 ls/写文件/执行,再切回确认 root 不受影响。
对 setuid 和 ACL,验证要点不同:setuid 看运行时 euid(用 id -u 或加一行 whoami 输出),ACL 看 target 用户能否读到匹配 mask 的 rwx。把验证命令固化成一个可复跑脚本,回滚的时候直接重跑即可。
# 错误示范:以为 777 一定对
# chmod 777 /srv/app
# 修复对照:按账号实测
sudo -u devuser ls -l /srv/app/deploy.sh
sudo -u devuser touch /srv/app/probe.txt
sudo -u devuser id -u # 测 setuid 时看 euid
# 脚本化验证
for u in devuser opsuser; do echo -n "$u: "; sudo -u $u test -r /srv/app/secret && echo "can read" || echo "denied"; done