技术博客

Linux ACL 与 sudo 细粒度权限管理实战

在 chmod/chown 之上深入讲解 Linux 扩展访问控制列表(ACL)的配置与排查,以及 sudoers 文件的精细化授权——包括按用户、按命令、按时间窗口的最小权限配置,适用于多人协作的生产服务器。

LinuxACLsudo权限管理安全运维sudoers

标准的 chmod/chown 只支持“属主、属组、其他”三级权限,满足不了“A 用户只读、B 用户读写、C 用户无权限”这类多用户细粒度需求。Linux ACL(Access Control List)正是为此而生。与此同时,sudo 的 sudoers 配置如果只会写 ALL=(ALL) ALL,就等于把根钥匙交给了每一个运维——本文讲清楚这两个工具的生产级用法。

一、ACL 基础

检查文件系统是否支持 ACL

# 查看挂载选项
mount | grep "on / "
# /dev/sda1 on / type ext4 (rw,relatime,acl)
# 看到 acl 表示已支持

# 如果没有 acl 选项,临时挂载启用
mount -o remount,acl /

# 永久生效:编辑 /etc/fstab
# /dev/sda1  /  ext4  defaults,acl  0 1

# XFS 文件系统默认支持 ACL,无需额外配置

ACL 核心命令

# 查看文件 ACL
getfacl /data/project/

# 典型输出:
# file: data/project/
# owner: root
# group: devteam
# user::rwx          ← 属主权限(等同 chmod u)
# group::r-x         ← 属组权限(等同 chmod g)
# other::---         ← 其他权限(等同 chmod o)

# 设置 ACL
setfacl -m u:alice:rwx /data/project/    # alice 读写执行
setfacl -m u:bob:r-x   /data/project/    # bob  只读
setfacl -m u:carol:---  /data/project/   # carol 无权限(显式拒绝)
setfacl -m g:ops:r-x    /data/project/   # ops 组只读

# 递归设置(-R)
setfacl -R -m u:alice:rwx /data/project/

# 删除某用户的 ACL 条目
setfacl -x u:bob /data/project/

# 清除所有 ACL(回到纯 chmod 状态)
setfacl -b /data/project/

默认 ACL(新建文件自动继承)

普通 ACL 只作用于已有文件,新建的文件不会自动继承。默认 ACL 解决这个问题:

# 为目录设置默认 ACL
setfacl -d -m u:alice:rwx /data/project/
setfacl -d -m g:ops:r-x   /data/project/
setfacl -d -m o::---       /data/project/

# 验证:查看 default 部分
getfacl /data/project/
# default:user::rwx
# default:user:alice:rwx
# default:group::r-x
# default:group:ops:r-x
# default:other::---

# 测试:新建文件自动继承
touch /data/project/newfile.txt
getfacl /data/project/newfile.txt
# user:alice:rwx    ← 自动继承

mask:ACL 的上限控制

mask 是 ACL 中最容易被忽略的概念——它是“命名用户/命名组的权限上限”:

# 当 mask 为 r-x 时,即使 alice 被设置了 rwx,实际生效权限是 rwx & r-x = r-x
setfacl -m m::r-x /data/project/

getfacl /data/project/
# user:alice:rwx        #effective:r-x   ← 实际权限被 mask 限制为 r-x
# group::r-x            #effective:r-x
# mask::r-x

# chmod 修改属组权限会同步更新 mask!
chmod g+w /data/project/  # 这会把 mask 变为 rwx,间接放开了所有 ACL 用户的写权限
# 所以:启用 ACL 后,不要用 chmod g± 来控制权限,而应该直接设置 mask
setfacl -m m::r-x /data/project/  # 强制恢复 mask

实战:多团队共享目录

# 场景:/data/web 由 frontend 团队读写,backend 团队只读,audit 用户全权

# 1. 基础权限(属组 frontend)
chown root:frontend /data/web
chmod 750 /data/web

# 2. 设置 ACL
setfacl -m g:backend:r-x  /data/web    # backend 组只读
setfacl -m u:audit:rwx    /data/web    # audit 用户全权

# 3. 设置默认 ACL(新文件继承)
setfacl -d -m g:frontend:rwx  /data/web
setfacl -d -m g:backend:r-x   /data/web
setfacl -d -m u:audit:rwx     /data/web
setfacl -d -m o::---          /data/web

# 验证
getfacl /data/web

二、sudo 精细化授权

sudoers 文件结构

# 永远用 visudo 编辑(有语法检查)
visudo

# 文件位置:/etc/sudoers
# 推荐将自定义配置放到 /etc/sudoers.d/ 目录下(避免系统更新覆盖)
visudo -f /etc/sudoers.d/ops-team

基本语法

# 格式:who  where=(as_whom)  what
# 用户  主机=(切换为谁)    命令

alice  ALL=(ALL)  ALL         # alice 可以在任何主机,以任何用户,执行任何命令
%ops   ALL=(root) /bin/systemctl restart nginx  # ops 组只能以 root 重启 nginx

最小权限原则实战

# /etc/sudoers.d/web-ops
# ===========================
# Web 运维组:只能管理 Nginx 和查看日志

# 定义命令别名
Cmnd_Alias NGINX_CMDS = \
    /bin/systemctl start nginx, \
    /bin/systemctl stop nginx, \
    /bin/systemctl restart nginx, \
    /bin/systemctl reload nginx, \
    /bin/systemctl status nginx, \
    /usr/sbin/nginx -t

Cmnd_Alias LOG_CMDS = \
    /usr/bin/tail -f /var/log/nginx/*, \
    /usr/bin/less /var/log/nginx/*, \
    /usr/bin/cat /var/log/nginx/*

# 授权:web-ops 组可以无密码执行以上命令
%web-ops  ALL=(root)  NOPASSWD: NGINX_CMDS, LOG_CMDS

# 禁止:绝对禁止 shell 逃逸
# 注意:如果上面授权了 less,用户可以在 less 里按 !bash 获取 shell
# 更安全的做法是不给 less,改为只给 cat/tail
# /etc/sudoers.d/dba-team
# ===========================
# 数据库运维:只能管理 MySQL 服务,不能直接 mysql 命令行

Cmnd_Alias MYSQL_SERVICE = \
    /bin/systemctl start mysqld, \
    /bin/systemctl stop mysqld, \
    /bin/systemctl restart mysqld, \
    /bin/systemctl status mysqld

# 明确拒绝危险命令(即使 ALL 规则里包含)
Cmnd_Alias DANGEROUS = /usr/bin/mysql, /usr/bin/mysqladmin

%dba-team  ALL=(root)  NOPASSWD: MYSQL_SERVICE
%dba-team  ALL=(root)  !DANGEROUS   # ! 表示拒绝

密码要求与超时

# /etc/sudoers.d/security-policy

# 修改 sudo 密码缓存时间(默认 15 分钟)
Defaults  timestamp_timeout=5   # 5 分钟后再次要求输入密码

# 对特定用户关闭缓存(每次都要输密码)
Defaults:audit  timestamp_timeout=0

# 对高危命令始终要求密码(覆盖 NOPASSWD)
Cmnd_Alias HIGH_RISK = /usr/bin/rm -rf *, /usr/sbin/userdel, /usr/sbin/passwd

%ops  ALL=(root)  NOPASSWD: /bin/systemctl
%ops  ALL=(root)  PASSWD: HIGH_RISK   # 即使组里有 NOPASSWD,高危命令仍要密码

记录 sudo 日志

# /etc/sudoers.d/logging

# 记录所有 sudo 命令(需要 sudo 版本 >= 1.9.0)
Defaults  log_output          # 记录命令输出
Defaults  iolog_dir=/var/log/sudo-io/%{user}  # 按用户分目录
Defaults  iolog_file=%{seq}   # 文件名为序号

# 更简单:通过 syslog 记录(所有版本可用)
Defaults  syslog=auth         # 记录到 /var/log/auth.log 或 journald

# 查看 sudo 日志
grep sudo /var/log/auth.log
journalctl -u sudo

# 查看命令回放(需要 iolog)
sudoreplay /var/log/sudo-io/alice/

常见陷阱

# 陷阱 1:别名中的路径必须是绝对路径
# ❌ 错误
Cmnd_Alias BAD = systemctl restart nginx
# ✅ 正确
Cmnd_Alias GOOD = /bin/systemctl restart nginx

# 陷阱 2:带参数的命令要精确匹配
# 下面只允许 restart nginx,不允许 restart sshd
Cmnd_Alias NGINX_RESTART = /bin/systemctl restart nginx
# 但 "systemctl restart nginx --now" 不匹配!参数不同就不匹配

# 陷阱 3:shell 逃逸漏洞
# 不要授权 vim/less/more/man/find/awk 等可以执行子命令的工具
# vim 里可以 :!bash 获取 root shell

# 陷阱 4:语法错误导致 sudo 完全不可用
# 永远用 visudo,不要直接 vi /etc/sudoers
# 如果已经锁死,用单用户模式(recovery mode)修复

验证 sudo 配置

# 以某用户身份测试(不实际执行)
sudo -l -U alice
# 输出:
# User alice may run the following commands on server01:
#     (root) NOPASSWD: /bin/systemctl start nginx
#     (root) NOPASSWD: /bin/systemctl stop nginx

# 检查 sudoers 语法
visudo -c
# /etc/sudoers: parsed OK
# /etc/sudoers.d/ops-team: parsed OK

# 测试特定命令是否可以执行(不用输密码验证)
sudo -l -U bob /bin/systemctl restart nginx

三、ACL + sudo 联合使用场景

# 场景:CI/CD 系统账号(ci-bot)需要:
# 1. 读写 /data/deploy 目录(ACL 控制)
# 2. 重启应用服务(sudo 控制)
# 3. 不能做任何其他 root 操作

# Step1:ACL 设置部署目录权限
setfacl -R -m u:ci-bot:rwx /data/deploy/
setfacl -d -m u:ci-bot:rwx /data/deploy/   # 新文件也继承

# Step2:sudo 设置服务重启权限
cat > /etc/sudoers.d/ci-bot << 'EOF'
Cmnd_Alias CIBOT_CMDS = \
    /bin/systemctl restart myapp, \
    /bin/systemctl start myapp, \
    /bin/systemctl stop myapp, \
    /bin/systemctl status myapp

ci-bot  ALL=(root)  NOPASSWD: CIBOT_CMDS
EOF

# Step3:验证
sudo -l -U ci-bot
getfacl /data/deploy/

小结

ACL 和 sudo 是 Linux 权限体系中“超越 chmod 三元组”的两大工具。ACL 解决“同一目录给不同用户不同权限”的问题,关键记住 mask 的上限作用和默认 ACL 的继承机制;sudo 解决“运维人员需要部分 root 权限”的问题,关键是最小权限原则、避免 shell 逃逸、强制日志记录。两者结合使用,可以在不给任何人 root 密码的前提下,精细化控制每个用户在服务器上能做什么。