// 安全研究 · 2025-06-02

Linux ACL 权限管理详解:超越传统 UGO 的精细化控制

导读:当传统的 chmod 744 无法满足“让 A 用户只读、B 用户读写、其他人无权限”这类复杂需求时,ACL(访问控制列表)就是你的答案。本文将深入剖析 ACL 的原理、用法及其在生产环境中的最佳实践。

1. 引言:传统权限模型的局限

Linux 传统的 UGO(User-Group-Other)权限模型简单高效,但在以下场景中明显力不从心:

场景 传统 UGO 的困境 ACL 解决方案
多团队协作 一个文件只能属于一个组 可为多个用户/组分别设置权限
临时授权 需将用户加入组,事后忘记移除 单独为用户添加 ACL,可随时撤销
不同权限级别 只能设置单一组权限 不同用户/组可有不同读写执行组合
默认权限继承 依赖 umask,无法精细控制 可设置默认 ACL,新文件自动继承

核心价值:ACL(Access Control List)在传统 UGO 模型之上,为每个文件/目录增加了一个扩展权限列表,允许为任意多个用户或组设置独立权限。

2. ACL 的核心概念

2.1 ACL 的组成元素

每个 ACL 条目(Access Control Entry, ACE)包含三部分:

组成 说明 示例
主体类型 用户、组、掩码、默认 user, group, mask, default
主体名称 具体的用户名或组名 alice, developers
权限 r(读)、w(写)、x(执行) rwx, r-x, r--

2.2 三种 ACL 类型

类型 含义 作用对象
访问 ACL 控制特定用户/组对该文件的访问 已存在的文件/目录
默认 ACL 指定新建文件/目录继承的权限 仅作用于目录
掩码 ACL 限制 ACL 条目的最大有效权限 所有命名用户/组
graph TD
    A[目录 /data] -->|设置| B[默认 ACL]
    B -->|新文件继承| C[子文件]
    B -->|新目录继承| D[子目录]
    D -->|继承| E[子目录的默认 ACL]

2.3 权限检查顺序

当进程访问一个设置了 ACL 的文件时,内核按以下顺序检查权限:

1. 进程 UID 是否匹配文件 owner?
   └─ 是 → 使用 owner 权限
   └─ 否 ↓
2. 是否有匹配进程 UID 的命名用户 ACL?
   └─ 是 → 使用该 ACL 权限(受 mask 限制)
   └─ 否 ↓
3. 进程 GID 是否匹配文件的 owning group 或命名组 ACL?
   └─ 是 → 使用匹配组中最宽松的权限(受 mask 限制)
   └─ 否 ↓
4. 使用 other 权限

3. 基础操作命令

3.1 查看 ACL

# 查看文件的 ACL 设置
getfacl file.txt

# 递归查看目录及子文件
getfacl -R /data/project

# 不显示注释(便于脚本处理)
getfacl --omit-header file.txt

# 查看当前目录的 ACL(. 表示当前目录)
getfacl .

输出示例:

$ getfacl /data/shared
# file: data/shared
# owner: root
# group: root
user::rwx
user:alice:rw-
user:bob:r--
group::r-x
mask::rw-
other::---

default:user::rwx
default:user:alice:rw-
default:group::r-x
default:mask::rwx
default:other::---

3.2 设置 ACL

# 为特定用户添加权限
setfacl -m u:alice:rw file.txt

# 为特定组添加权限
setfacl -m g:developers:rx /data/project

# 移除特定用户的 ACL
setfacl -x u:alice file.txt

# 移除所有扩展 ACL(恢复传统权限)
setfacl -b file.txt

# 递归设置目录 ACL(已有文件会继承吗?见下文陷阱)
setfacl -R -m u:alice:rw /data/project

3.3 默认 ACL(目录专用)

# 设置默认 ACL:新文件自动继承
setfacl -m d:u:alice:rw /data/project

# 同时设置访问 ACL 和默认 ACL
setfacl -m u:alice:rw -m d:u:alice:rw /data/project

# 移除默认 ACL
setfacl -k /data/project

关键点:默认 ACL 只影响之后在该目录下创建的文件/目录,对已存在的子文件/目录无效。

4. 掩码(Mask)机制

4.1 掩码的作用

掩码(mask)是 ACL 中的一个特殊条目,它限制所有命名用户、命名组和 owning group 的最大有效权限。

# 查看掩码(输出中的 mask 行)
getfacl file.txt

示例:

$ getfacl file.txt
user::rw-
user:alice:rwx     # 本想给 rwx
group::r--
mask::r--          # 但掩码限制为 r--
other::---

# 实际上 alice 的有效权限是 r--(受 mask 限制)

4.2 掩码的自动计算

当使用 setfacl -m 添加 ACL 时,系统会自动重新计算掩码:将所有 ACL 条目的权限做“按位或”运算。

# 假设已有 ACL:alice=rw, bob=r
# 掩码会被计算为 rw(rw | r = rw)

# 手动设置掩码
setfacl -m mask::rx file.txt

4.3 掩码的最佳实践

场景 建议
不希望掩码自动变化 先手动设置掩码,再添加用户 ACL
确保掩码不限制权限 设置 mask::rwx 然后添加具体权限
脚本自动化 每次修改后检查掩码,必要时显式设置
# 安全添加 ACL 的脚本模式
setfacl -m mask::rwx /data/share
setfacl -m u:alice:rw /data/share
setfacl -m u:bob:r /data/share
# 此时掩码仍为 rwx(不会降级)

5. ACL 的继承机制

5.1 文件与目录继承规则

父目录设置 新建文件继承 新建子目录继承
u:alice:rw 不继承 不继承
d:u:alice:rw 复制为 u:alice:rw 复制为 d:u:alice:rw
d:g:dev:rx 复制为 g:dev:rx 复制为 d:g:dev:rx
d:mask::rw 复制为 mask::rw 复制为 d:mask::rw

5.2 递归设置 vs 继承

# 递归修改:影响已存在的子文件/目录
setfacl -R -m u:alice:rw /data/project

# 默认 ACL:只影响未来的子文件/目录
setfacl -m d:u:alice:rw /data/project

两者结合是最佳实践:

# 同时处理已存在和未来文件
setfacl -R -m u:alice:rw /data/project
setfacl -m d:u:alice:rw /data/project

6. 备份与恢复

6.1 备份 ACL

# 备份单个文件的 ACL
getfacl file.txt > file.acl

# 递归备份目录的 ACL(含子文件)
getfacl -R /data/project > project_acl.bak

6.2 恢复 ACL

# 恢复单个文件
setfacl --restore=file.acl

# 注意:恢复文件必须包含路径信息
# getfacl 输出的第一行是 # file: /path/to/file

跨系统恢复的注意事项:

问题 解决方案
路径不同 手动编辑 .acl 文件或使用 sed 替换路径
用户/组不存在 先创建用户/组,恢复时添加 --skip-no-user
# 跳过目标系统上不存在的用户/组
setfacl --restore=project_acl.bak --skip-no-user

7. 进阶实战场景

7.1 场景一:多团队共享目录

需求:/data/share 目录,alice(研发)可读写,bob(运维)只读,其他研发成员可读写,其他运维成员只读。

# 创建组
groupadd devops
groupadd ops

# 将用户加入组
usermod -aG devops alice
usermod -aG ops bob

# 设置目录权限和 ACL
mkdir -p /data/share
chown root:devops /data/share
chmod 770 /data/share                    # 本组(devops)rwx

# 为运维组添加读权限
setfacl -m g:ops:rx /data/share

# 为特定用户设置权限
setfacl -m u:alice:rwx /data/share      # alice 读写执行
setfacl -m u:bob:r-x /data/share        # bob 只读执行

7.2 场景二:临时授权且自动过期

需求:审计员需要临时读取 /var/log 日志,7 天后自动失效。

# 创建审计专用账号
useradd auditor

# 添加 ACL
setfacl -m u:auditor:r-x /var/log

# 设置 cron 任务自动清理
(crontab -l 2>/dev/null; echo "0 0 * * 0 setfacl -x u:auditor /var/log") | crontab -

7.3 场景三:Web 上传目录的精细控制

需求:/var/www/uploads 目录,用户上传的文件自己可改,其他用户只读。

mkdir -p /var/www/uploads
chown www-data:www-data /var/www/uploads

# 默认 ACL:新文件使用 rw-r-----(用户自己读写,同组只读)
setfacl -m d:u::rwx /var/www/uploads
setfacl -m d:g::r-x /var/www/uploads
setfacl -m d:o::--- /var/www/uploads
setfacl -m d:mask::rwx /var/www/uploads

# sticky bit:防止用户删除非自己创建的文件
chmod +t /var/www/uploads

7.4 场景四:邮件目录的多用户访问

需求:用户 alice 的邮件目录,需要临时允许 bob 读取。

# 检查传统权限是否允许
chmod 755 /home/alice/Maildir
setfacl -m u:bob:rx /home/alice/Maildir
setfacl -R -m u:bob:r /home/alice/Maildir/cur      # 新邮件目录

8. 性能与兼容性

8.1 文件系统支持

文件系统 ACL 支持
ext2/ext3/ext4 ✅ 需挂载时启用 acl 选项
XFS ✅ 默认支持
Btrfs ✅ 默认支持
ZFS ✅ 默认支持
tmpfs ✅ 默认支持
NFS ⚠️ 取决于服务端和客户端配置

启用 ext4 的 ACL:

# 修改 /etc/fstab
/dev/sda1 /data ext4 defaults,acl 0 1

# 重新挂载
mount -o remount,acl /data

8.2 与备份工具的关系

工具 ACL 支持 需要额外参数
tar ✅ 支持 --acls 参数
rsync ✅ 支持 -A 或 --acls
cp ⚠️ 部分支持 -a 或 --preserve=all
rsync 版本注意 ⚠️ 不同版本 ACL 保存能力有差异 测试后再投入生产
# tar 备份时保留 ACL
tar --acls -czf backup.tar.gz /data/project

# rsync 同步保留 ACL
rsync -avA /data/project user@backup:/backup/

8.3 与版本控制工具(Git)

Git 不支持记录 ACL。将 ACL 配置脚本纳入仓库管理:

# 仓库中保存 setup-acl.sh
#!/bin/bash
setfacl -R -m u:alice:rwx /var/www/project
setfacl -R -m g:devops:rx /var/www/project

9. 常见陷阱与排查

9.1 陷阱一:递归设置不影响已存在文件

# 错误认知:设置了目录的默认 ACL,已存在的子文件就有了权限
setfacl -m d:u:alice:rw /data/project   # 只影响未来文件!

# 正确做法:先递归添加 ACL
setfacl -R -m u:alice:rw /data/project
setfacl -m d:u:alice:rw /data/project

9.2 陷阱二:掩码“吃掉”了权限

# 症状:明明设置了 alice:rwx,实际只有 r--
$ getfacl file.txt
user:alice:rwx
mask::r--
# 有效权限 = alice:rwx & mask:r-- = r--

# 解决:重置掩码
setfacl -m mask::rwx file.txt

9.3 陷阱三:FTP/SFTP 客户端不保留 ACL

# 症状:通过 FTP 上传的文件没有 ACL

# 解决方案
# 1. 使用 scp/rsync 替代 FTP
# 2. 服务器端设置目录默认 ACL
setfacl -m d:u:alice:rw /incoming

9.4 陷阱四:ACL 条目顺序敏感

ACL 条目的顺序影响 getfacl 的输出,但不影响内核的权限检查顺序。内核按以下固定顺序检查:

  1. owner
  2. 命名用户
  3. owning group 或命名组
  4. other

9.5 排查命令汇总

# 检查文件是否设置了 ACL
ls -l file.txt
# 输出中权限位后的 '+' 表示有扩展 ACL

# 查看详细信息
getfacl file.txt

# 检查文件系统是否支持 ACL
mount | grep -w "acl"

# 查看内核是否启用 ACL 支持
grep ACL /boot/config-$(uname -r)

10. 最佳实践总结

10.1 核心原则

原则 说明
最小权限 只授予完成工作所需的最小权限
默认 ACL 优先 目录应优先使用默认 ACL 而非递归设置
组优于用户 优先使用组 ACL,避免用户 ACL 膨胀
定期审计 getfacl -R /important > acl_backup.txt 定期备份

10.2 权限设计决策树

graph TD
    A[需要设置权限] --> B{是否只为 owner/group/other?}
    B -->|是| C[使用传统 chmod]
    B -->|否| D{需要几个额外主体?}
    
    D -->|少量| E[使用命名用户/组 ACL]
    D -->|大量| F[考虑新建专用组]
    
    C --> G[传统 UGO 模式]
    E --> H[ACL 扩展模式]
    F --> G

10.3 常用命令速查

操作 命令
查看 ACL getfacl <file>
添加用户权限 setfacl -m u:alice:rw <file>
添加组权限 setfacl -m g:dev:rx <dir>
移除用户 setfacl -x u:alice <file>
移除所有扩展 setfacl -b <file>
设置默认 ACL setfacl -m d:u:alice:rw <dir>
递归修改 setfacl -R -m u:alice:rw <dir>
备份 ACL getfacl -R <dir> > backup.acl
恢复 ACL setfacl --restore=backup.acl

10.4 检查清单

部署 ACL 前,确认以下事项:

  • 文件系统挂载时启用了 ACL(mount | grep acl)
  • 备份策略包含了 ACL(tar --acls / rsync -A)
  • 已设置默认 ACL 的目录,同时处理了已存在的子文件
  • 掩码(mask)未意外限制所需权限
  • ACL 变更已通过版本控制记录(如 setup-acl.sh 脚本)

ACL 是传统权限模型的自然扩展,而不是替代。在绝大多数场景下,传统的 UGO + umask 仍是最简洁的选择。当需求超出传统模型时,ACL 提供了精细化的解决方案,但也要清醒地认识到:权限越精细,管理复杂度越高。在生产环境中,应先问自己“传统 chmod 真的不够用吗?”,确认后再引入 ACL。

原文 https://blog.csdn.net/2301_79518550/article/details/147806368