ACL 权限控制
传统 rwx 权限只有三组位:所有者、所属组、其他人。这个模型在"一人一文件"的世界里够用,但现实中的共享目录几乎总是提出三类位无法表达的需求——/srv/projects 属组是 devops,却要让只属于 guest 组的外包同学写其中一个子目录;家目录要让备份脚本以 backup 用户读,但不能把属主改掉。逐个 chmod 会破坏组语义,chown 会打断属主管线,于是 Linux 提供了 ACL(Access Control List):在文件系统元数据里追加一张"针对任意用户/组的授权表",与传统三位权限并行生效。理解 ACL 的关键不只是会敲 setfacl,而是搞懂 mask 字段——它像一道闸门,会悄悄收窄你精心设置的权限,也是"为什么我明明给了 rw 却写不进去"这类诡异问题的根源。
内容参考自 Arch Wiki 与
man setfacl/man getfacl,见文末参考资料。
学习目标
- 说清 ACL 解决了传统 rwx 的什么盲区
- 读懂
getfacl输出中每一行(尤其是mask)的含义 - 掌握
setfacl的修改、递归与默认(default)ACL - 会备份/恢复目录 ACL,知道 ext4 与 XFS 的支持情况
1. 为什么需要 ACL
传统模型的表达能力上限是"三组人"。当你要表达的是"张三这个特定用户对这个文件可读写",而张三既不是属主、其主组也不是属组时,传统模型只剩下"其他人"这一档——给他开 o+w 意味着所有非属主非属组的人都能写。组方案也治标不治事:把张三临时拉进属组能解决一个文件,却会顺带打开该组能访问的全部文件。
ACL 把授权对象从三类扩展到任意数量的"具名用户/组"条目,同时保持向后兼容——不支持 ACL 的老程序调用 stat() 拿到的仍是传统三位权限(对应 ACL 里的 user::、group::、other::)。三大发行版家族的文件系统默认都支持 ACL:ext4、XFS、Btrfs、tmpfs 均开箱即用,无需内核参数调整;需要安装的只是用户态工具包。
$ sudo apt install acl # Debian/Ubuntu
$ sudo pacman -S acl # Arch
$ sudo dnf install acl # RHEL/CentOS/Rocky2. 读懂 getfacl 输出
getfacl 的输出与 ls -l 是一一对应的:user:: 对应属主三位,group:: 对应属组三位,other:: 对应其他人三位;中间可能多出若干 user:名字: / group:名字: 具名条目,以及一行 mask::。未经修改的文件与 ls -l 完全一致;给具名用户授权后,多出来的就是一条具名 ACL 条目,同时 mask 会被自动抬升以容纳新权限。
$ getfacl /srv/projects/notes.txt
# file: srv/projects/notes.txt
user::rw-
user:guest:rw- ← 新增:针对具名用户的授权
group::r--
mask::rw- ← 有效权限上限(见第 4 节)
other::r--对比 ls -l 会发现权限位串变长了——末尾出现的 + 号就是"存在 ACL"的标记(-rw-rw-r--+)。也可以直接为组授权,语义与具名用户一致,只是匹配对象换成组 GID:sudo setfacl -m g:auditors:r file.txt。
3. setfacl 常用操作
setfacl -m 修改条目,-x 删除单条,-b 清空全部扩展 ACL 只留传统三位;多条授权用逗号分隔一次写入;-R 递归应用到目录下调用那一刻已经存在的文件。
$ sudo setfacl -m u:guest:rw,u:carol:r file.txt
$ sudo setfacl -R -m u:guest:rw /srv/projects/drafts/
$ sudo setfacl -x u:guest file.txt # 删除某一条具名 ACL
$ sudo setfacl -b file.txt # 删除全部扩展 ACL
$ sudo setfacl -m m:r file.txt # 修改权限掩码注意 -R 只影响存量文件;之后新建的文件不会自动继承 ACL。要让新文件也带上授权条目,需要设置默认 ACL(default)——它附着在目录上,像模板一样被 open()/creat() 创建的子文件与子目录继承,d: 前缀只在目录上有效:
$ sudo setfacl -m d:u:guest:rw /srv/projects/drafts/
$ touch /srv/projects/drafts/new.txt
$ getfacl /srv/projects/drafts/new.txt
user:guest:rw- ← 新文件自动带上,无需再手动 setfacl
$ sudo setfacl -k /srv/projects/drafts/ # 清除默认 ACL默认 ACL 的权限受目录自身 ACL 约束,若父目录 group::r--,子文件也继承不出写权限——权限是逐级"与"下来的。
4. mask:最容易踩的坑
mask 是 ACL 里的有效权限上限,它对所有非属主的 ACL 条目(具名用户、具名组、属组)做按位与运算。任何一条条目被 mask 收窄后,实际生效权限都会低于你设置的值——getfacl 显示的具名行就是"与过 mask 之后"的实际生效权限。
$ sudo setfacl -m m:r file.txt
$ getfacl file.txt
user:guest:r-- ← 已被 mask 截断,不再是当初给的 rw
mask::r--
$ sudo -u guest tee -a file.txt < /dev/null
tee: file.txt: Permission denied调用 setfacl -m u:...:rw 时,工具会尝试把 mask 自动抬到足以容纳新权限(所以你很少主动察觉它的存在);但当有人显式设置过 mask、或用 -b 之外的方式部分清除过条目时,mask 可能停留在旧值。排查"权限明明设了却无效",第一件事就是 getfacl 看 mask 那一行,这比反复重复 setfacl 有用得多。
具名组条目与属组条目(group::)共享同一个 mask。换句话说,mask 是"除属主和 other 之外所有授权的公共上限",这一设计保证了不识别 ACL 的旧程序看到的 ls -l 第二组权限位(即属组权限)不会超过管理员的真实意图。
5. 三系默认 ACL 策略对照
三大发行版家族在 ACL 上的行为几乎一致,差别集中在默认挂载选项与文档习惯上:
| 主题 | Debian/Ubuntu | Arch | RHEL/CentOS/Rocky |
|---|---|---|---|
| 工具包 | acl(默认已装) | acl(base 组包含) | acl(默认已装) |
| ext4 挂载选项 | 默认即可(ACL 按需启用,无需 acl 选项) | 同左 | 同左 |
| 常见误用 | 用 chmod 777 代替 ACL | 同左 | 同左 |
| 查看支持 | tune2fs -l /dev/sdX1 | grep -i acl | 同左 | xfs_info 默认开启 |
需要说明的是,历史上 ext2/3 需要在挂载时显式加 acl 选项,ext4 起内核默认启用 ACL 支持;XFS 则一直默认开启。因此三系上你都不需要改 /etc/fstab,装好 acl 包即可直接用 setfacl。
6. 备份与恢复
ACL 存储在文件系统元数据中,cp 一般不保留(除非用 cp -a/--preserve=xattr),迁移目录树时最稳妥的方式是先导出再回放。getfacl -R 的输出是可回放文本(带 # file: 注释行做定位),setfacl --restore 期望的正是这个格式:
$ getfacl -R /srv/projects > /backup/projects.acl
$ sudo setfacl --restore=/backup/projects.acl
$ getfacl -R /srv/projects | sudo setfacl --restore=/dev/stdin做完整迁移(rsync 到新机器)时,把 ACL 导出文件与数据一起归档,恢复顺序是"先 rsync 数据,再 setfacl --restore",反过来会导致 ACL 条目因目标文件不存在而被跳过。
7. 常见坑
设了 ACL 却不生效。先
getfacl看mask是否收窄;再ls -l看是否有+号;最后id确认当前进程凭证里的 UID/GID 确实匹配条目。三步排查覆盖 90% 的场景。-R与 default ACL 混淆。-R管存量文件,d:管未来文件;只做其一都会留下"一半文件有权限、一半没有"的烂摊子。正确的批量授权姿势是两者一起做:bash$ sudo setfacl -R -m u:guest:rw /srv/projects/drafts/ $ sudo setfacl -m d:u:guest:rw /srv/projects/drafts/用 ACL 绕过本该用组解决的问题。如果十个人需要同样权限,建一个组然后
setfacl -m g:team:rw比维护十条用户条目更易管理。ACL 的定位是"个别例外",不是"替代组模型"。备份时丢掉 ACL。普通
tar不含 xattr(ACL 存于 xattr 的system.posix_acl_*键)。用tar --xattrs --xattrs-include='*'或直接按第 6 节导出getfacl文本。在不支持 ACL 的文件系统上操作。FAT32、exFAT(U 盘常用)不支持 POSIX ACL,
setfacl会报Operation not supported。跨系统共享 U 盘请改用挂载时的umask/fmask/dmask选项控制默认权限。误以为 ACL 能限制 root。ACL 与传统 DAC 一样对 UID 0 无效;真正能约束 root 的是 SELinux/AppArmor 之类的 MAC,见安全基础。
参考资料
- Arch Wiki - Access Control Lists — wiki.archlinux.org
- 鸟哥的私房菜 - 文件权限与 ACL — linux.vbird.org
man getfacl、man setfacl、man acl- Debian 手册 - 权限与 ACL — debian.org
- RHEL 9 - 使用 ACL — docs.redhat.com