跳转至

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 临时绕过(仅测试使用)。

实践任务

  1. aa-status 检查当前 enforce 和 complain 模式的策略数量
  2. 将 sshd 策略临时切换到 complain 模式,重启 ssh 服务,观察日志差异,再切回 enforce
  3. 创建上述 /opt/myapp/run.sh 示例策略,加载并 enforce,测试脚本访问 /etc/shadow 是否被阻断
  4. 查看 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/syslogjournalctl | 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 服务、容器章节中反复用到。