权限问题的标准解法是 chmod 777,然后问题解决,然后没人再管。这事的麻烦在于:三个月后另一个人也遇到"没权限",也 777,最后整个数据目录对所有人可写,谁删的你都查不出来。
Linux 权限其实就三层,搞清楚之后大部分问题有更干净的解法。
基础:rwx 到底是谁的
ls -l /data/app.log
# -rw-r--r-- 1 appuser appgroup 1024 Sep 8 10:00 /data/app.log
# ↑ ↑ ↑ ↑ ↑
# | | | | └─ 所属组
# | | | └── 其他人(other)权限 r--
# | | └───── 组(group)权限 r--
# | └──────── 属主(user)权限 rw-
# └─────────── 文件类型 - 普通文件 d 目录 l 链接
目录的 rwx 含义和文件不一样,这点常混淆:
| 权限 | 文件 | 目录 |
|---|---|---|
| r | 能读内容 | 能列出里面有什么(ls) |
| w | 能改内容 | 能在里面增/删/改名(不看文件本身权限) |
| x | 能执行 | 能进入(cd) |
倒数第二条最容易踩:能不能删一个文件,取决于它所在目录有没有 w,和文件自己的权限无关。所以共享目录要给粘滞位(下面说)。
umask:新建文件的默认权限
umask # 看当前,常见 022 或 002
umask 0027 # 临时改
算法:文件满权限 666、目录 777,减去 umask。
umask 022→ 文件 644(rw-r--r--)、目录 755umask 027→ 文件 640、目录 750(组内可读写,其他人不给)umask 077→ 文件 600、目录 700(只有自己)
多人协作的共享目录,把用户的 umask 设成 002,组才有写权限,否则 A 建的文件 B 改不了。写进 /etc/profile 或 ~/.bashrc 持久化。
注意:
umask减出来的权限不会自动给 x。文件本来就没执行位,只有程序编译或chmod +x才有。
三个特殊位
chmod u+s file # setuid:执行时以文件属主身份运行
chmod g+s dir # setgid:目录里新建的文件自动继承目录的组
chmod +t dir # 粘滞位:只有文件属主能删自己的文件
-
setuid:典型是
/usr/bin/passwd,普通用户执行它改/etc/shadow,靠的就是运行时临时拿到 root 身份。看到不该有 s 位的文件要警惕,这是提权的常见入口:find / -perm -4000 -type f 2>/dev/null # 找出所有 setuid 文件 -
setgid(目录上):共享目录必备。设了之后,任何人在这个目录里建的文件,组都自动是目录的组,不会出现"A 建的文件组是 A,B 访问不了"的情况。
mkdir /data/shared chgrp devteam /data/shared chmod 2775 /data/shared # 2 就是 setgid -
粘滞位:
/tmp就是drwxrwxrwt。所有人可写,但只能删自己的文件。共享目录必须有它,否则任何人都能删别人的东西。chmod 1777 /data/shared # 1 是粘滞位组合起来共享目录常用
chmod 3775(setgid + 粘滞位)。
什么时候该用 ACL
rwx 只能控制"属主/组/其他"三类人。要给第四个人单独开权限,或者给多个组不同权限,基础位就无能为力了——这时候上 ACL。
# 给用户 alice 单独加读写(不影响其他人)
setfacl -m u:alice:rw /data/report.csv
# 给组加
setfacl -m g:ops:rwx /data/deploy/
# 让目录里新建的文件也继承(d: 前缀 = default)
setfacl -m d:u:alice:rw /data/shared/
# 查看
getfacl /data/report.csv
# 删除某条
setfacl -x u:alice /data/report.csv
# 清空所有 ACL
setfacl -b /data/report.csv
开了 ACL 后 ls -l 的权限位末尾会多个 +:
-rw-rw-r--+ 1 appuser appgroup 1024 Sep 8 10:00 report.csv
# ↑ 有 ACL
ACL 的取舍:灵活,但增加理解成本——别人 ls 看到 rw-r--r-- 以为 alice 不能写,实际 ACL 里给了。所以能用"建个组、把人加进去、用 setgid 共享目录"解决的,优先用组,ACL 留给临时授权和特殊例外。
还有一个坑:ACL 在部分老的文件系统或挂载选项下不生效,用之前先确认:
mount | grep -w / # ext4/xfs 默认都支持
tune2fs -l /dev/sda1 | grep acl # ext 系列看 Default mount options
备份时也要注意,tar 默认不带 ACL,得加 --acls;rsync 用 -A。
sudo:别直接给 ALL
# /etc/sudoers 或 /etc/sudoers.d/xxx(用 visudo 编辑!)
# visudo 会检查语法,直接 vi 改错了可能把自己锁在门外
几种写法:
# 完全放开(只有你自己用的机器才这么干)
alice ALL=(ALL) ALL
# 免密完全放开 —— 等于把 root 给他
alice ALL=(ALL) NOPASSWD: ALL
# 只允许特定命令(推荐)
alice ALL=(ALL) /usr/bin/systemctl restart nginx, /usr/bin/systemctl status nginx
# 只允许以某个用户身份跑
bob ALL=(appuser) /opt/app/bin/deploy.sh
# 用别名管理一组命令
Cmnd_Alias NGINX_CMD = /usr/bin/systemctl restart nginx, /usr/bin/systemctl reload nginx
%webteam ALL=(ALL) NOPASSWD: NGINX_CMD # % 表示组
命令要写绝对路径。写 systemctl 而不是 /usr/bin/systemctl,用户可以放一个同名脚本在 PATH 前面绕过限制。
NOPASSWD 慎用:给了免密等于这台机器对该用户的相关权限完全开放。CI/CD 场景确实需要,但要配合 sudo -l 定期审计。
看看自己有什么权限:
sudo -l # 列出当前用户能跑的命令
一个常见的排查顺序
"Permission denied" 的时候按这个查:
id # 1. 我是谁,在哪些组
ls -ld /path/to/dir # 2. 目标目录的权限和属主(注意是目录不是文件)
ls -l /path/to/file # 3. 文件本身
getfacl /path/to/file # 4. 有没有 ACL
namei -l /path/to/file # 5. 逐级看路径上每一层——最常被忽略
mount | grep <挂载点> # 6. 是不是 ro 挂载、nosuid、noexec
第 5 步最关键:/a/b/c.txt 要能访问,/a、/a/b 每一层都必须至少有 x 权限。很多人只查了文件本身,卡在中间某层目录上。
还有个容易忘的:改了用户所属的组,要重新登录才生效。
usermod -aG docker alice # -a 是追加,漏了会覆盖掉原有附加组!
newgrp docker # 不想重登的话用这个临时切
id alice # 确认
usermod -G(没有 -a)会把用户从其他附加组里踢掉,这是个能让人瞬间失去 sudo 权限的操作,务必用 -aG。
几个建议
- 服务进程用独立的系统用户跑,别用 root;
useradd -r -s /sbin/nologin appuser - 共享目录:组 + setgid + 粘滞位,比 ACL 清爽
- 定期
find / -perm -4000和审计sudo -l,权限 creep 是慢慢发生的 - 给出去的权限记在文档里,人离职的时候才知道要回收什么