12 - AppArmor 配置与策略编写
前提条件
- 已完成前置章节:SSH 安全加固、fail2ban、内核参数调优
- 以 root 身份登录,或 ops 用户执行
sudo -i切换至 root 环境 - 系统运行在 Debian 12 Bookworm(AppArmor 默认启用)
- 虚拟机环境:VMware Workstation Pro
全局参数
主机名 linuxdc,IP 192.168.1.100/24,SSH 端口 2222,运维用户 ops
AppArmor 与 SELinux 的定位区别
AppArmor 是 Debian/Ubuntu 系默认的强制访问控制(MAC)机制,基于路径的策略模型,上手难度低于 SELinux。RHEL 系使用 SELinux(基于标签模型)。两者目标相同:限制进程只能访问其策略明确允许的资源,即使进程被攻击者控制也无法超出策略边界。
详细步骤
1. 检查 AppArmor 当前状态
AppArmor 在 Debian 12 中默认启用并处于 enforce 模式。
aa-status
输出示例:
apparmor module is loaded.
20 profiles are loaded.
18 profiles are in enforce mode.
2 profiles are in complain mode.
0 processes have profiles defined.
确认内核模块已加载:
cat /sys/module/apparmor/parameters/enabled # 应输出 Y
aa-enabled # 应输出 Yes
检查点
aa-status 正常输出且 enforce mode 数量大于 0,表示 AppArmor 正常运行。
2. 常用 AppArmor 命令
aa-status --verbose # 详细状态(含每个策略的进程)
aa-complain /usr/sbin/sshd # 切换到 complain 模式(只记录不阻止)
aa-enforce /usr/sbin/sshd # 恢复 enforce 模式
apparmor_parser -r /etc/apparmor.d/usr.sbin.sshd # 重新加载单个策略(不重启服务)
apparmor_parser -R /etc/apparmor.d/opt.myapp.run # 卸载单个策略
systemctl reload apparmor # 重载所有策略
systemctl restart apparmor # 重启 AppArmor 服务
3. 查看 AppArmor 拒绝日志
AppArmor 拒绝事件记录在系统日志中,关键字为 DENIED:
journalctl -f | grep -i apparmor # 实时跟踪 AppArmor 日志
journalctl --since "today" | grep DENIED # 今天所有拒绝事件
grep "apparmor" /var/log/syslog | grep DENIED # 从 syslog 中查找
典型 DENIED 日志示例:
kernel: audit: type=1400 audit(1234567890.123:456): apparmor="DENIED"
operation="open" profile="/usr/sbin/sshd" name="/etc/shadow"
pid=1234 comm="sshd" requested_mask="r" denied_mask="r"
4. 创建自定义 AppArmor 策略
示例:为 /opt/myapp/run.sh 创建最小权限策略,防止它访问敏感路径。
先创建测试脚本(用于验证策略效果):
mkdir -p /opt/myapp
cat > /opt/myapp/run.sh << 'EOF'
#!/bin/bash
echo "Script running..."
cat /etc/passwd && echo "passwd: accessible" || echo "passwd: DENIED"
cat /etc/shadow && echo "shadow: accessible" || echo "shadow: DENIED"
echo "Done"
EOF
chmod +x /opt/myapp/run.sh
创建 AppArmor 策略文件:
vim /etc/apparmor.d/opt.myapp.run
策略内容:
#include <tunables/global>
profile opt.myapp.run /opt/myapp/run.sh {
#include <abstractions/base>
#include <abstractions/bash>
# 允许执行自身
/opt/myapp/run.sh r,
/bin/bash ix,
/bin/cat ix,
/usr/bin/env ix,
# 允许读写自己的工作目录和日志
/opt/myapp/** rw,
/var/log/myapp.log rw,
# 显式拒绝敏感文件(deny 规则优先于允许规则)
deny /etc/shadow r,
deny /root/** rw,
deny /home/** rw,
# 允许读取 /etc/passwd(某些程序需要查用户名)
# /etc/passwd r,
}
加载策略并切换到 enforce 模式:
apparmor_parser -r /etc/apparmor.d/opt.myapp.run
aa-enforce /opt/myapp/run.sh
aa-status | grep myapp
运行测试脚本,观察拒绝效果:
/opt/myapp/run.sh
journalctl --since "1 min ago" | grep DENIED
检查点
测试脚本输出中 /etc/shadow 访问显示 DENIED,且 journalctl 中出现对应的 AppArmor DENIED 记录。
5. 使用 complain 模式排查问题
当服务被 AppArmor 意外阻断时,先切换到 complain 模式收集所需权限:
aa-complain /usr/sbin/nginx # 切换 nginx 策略到 complain 模式
# 正常使用服务,触发所有功能路径
journalctl --since "5 min ago" | grep "apparmor.*nginx"
# 根据日志补充策略规则
aa-enforce /usr/sbin/nginx # 补充完毕后切回 enforce
策略开发最佳实践
开发新策略时,先用 aa-genprof <程序> 自动生成初始策略(需要 apparmor-utils 包),在 complain 模式下运行程序的所有功能,再用 aa-logprof 根据日志自动更新策略,最后手动审查并切换到 enforce。
6. 常用生产策略调整
MySQL/MariaDB 自定义数据目录:
vim /etc/apparmor.d/usr.sbin.mysqld
# 在策略末尾 } 之前添加
/data/mysql/ r,
/data/mysql/** rwk,
apparmor_parser -r /etc/apparmor.d/usr.sbin.mysqld
Docker 容器:
AppArmor 默认对容器应用 docker-default 策略,可通过 --security-opt apparmor=unconfined 临时绕过(仅测试使用)。
实践任务
- 用
aa-status检查当前 enforce 和 complain 模式的策略数量 - 将 sshd 策略临时切换到 complain 模式,重启 ssh 服务,观察日志差异,再切回 enforce
- 创建上述
/opt/myapp/run.sh示例策略,加载并 enforce,测试脚本访问/etc/shadow是否被阻断 - 查看 AppArmor 拒绝日志,找到至少一条 DENIED 记录并分析是哪个操作触发了拒绝
自测问题
1. AppArmor 的 enforce 模式和 complain 模式的主要区别是什么?
enforce 模式下,AppArmor 策略真正生效:违反策略的操作会被内核直接拒绝,进程收到 Permission denied 错误,同时记录 DENIED 日志。complain 模式下,AppArmor 策略仅记录不阻止:所有违规操作被放行,但同样记录到系统日志。complain 模式用于两个场景:开发新策略时收集程序实际需要的权限,以及排查"服务被 AppArmor 阻断"类问题时,先临时放行并观察日志再补充规则。
2. 如何重新加载单个 AppArmor 策略而不重启整个服务?
apparmor_parser -r /etc/apparmor.d/usr.sbin.nginx
-r 参数(replace)会替换内核中已加载的同名策略,无需重启 apparmor 服务也无需重启被限制的目标服务(nginx 等),策略变更立即对所有相关进程生效。这对生产环境非常重要,避免了服务中断。
3. AppArmor 拒绝事件主要记录在哪个日志文件或 journal 中?
AppArmor 拒绝事件通过内核 audit 子系统记录,在 Debian 12 中主要出现在两个位置:/var/log/syslog(syslog 捕获内核审计消息)和 journalctl(systemd journal)。若安装了 auditd,也会记录在 /var/log/audit/audit.log 中,且格式更结构化,便于分析。查找命令:grep "apparmor.*DENIED" /var/log/syslog 或 journalctl | grep "apparmor.*DENIED"。
4. 生产环境中遇到服务启动失败但日志显示 AppArmor DENIED 时,第一步推荐操作是什么?
第一步:将该服务策略切换到 complain 模式,而不是直接禁用 AppArmor:
aa-complain /usr/sbin/<服务名>
systemctl restart <服务名>
服务正常启动后,执行所有业务功能路径,收集完整的 DENIED 日志,然后用 aa-logprof 或手动分析日志补充策略中缺失的权限规则,最后切回 enforce 模式验证。这样既解决了眼前的服务中断问题,又保留了安全边界,避免了直接 aa-disable 完全关闭保护的错误做法。
总结
AppArmor 是 Debian 默认的强制访问控制机制,轻量且易用。通过 enforce/complain 模式切换 + 自定义策略编写,可以显著提升单个进程的安全边界。本章建立的策略开发流程(complain 收集 → 补充规则 → enforce 验证),将在后续数据库、Web 服务、容器章节中反复用到。