AppArmor
目录
- 概述:什么是 AppArmor
- 历史背景与设计哲学
- 核心架构与工作原理
- 配置文件(Profile)语法详解
- 两种运行模式:Enforce vs Complain
- 工具链与实战
- AppArmor 与 Docker 容器安全
- AppArmor 与 Kubernetes
- AppArmor vs SELinux:选型指南
- 安全加固最佳实践
- 常见问题排查
- 总结
1. 概述:什么是 AppArmor
AppArmor(Application Armor,应用程序盔甲)是 Linux 内核的强制访问控制(MAC,Mandatory Access Control)安全模块。它允许系统管理员为每个应用程序定义安全配置文件(Profile),精确控制程序能访问哪些文件、使用哪些 Linux Capabilities、建立哪些网络连接等。
核心特性
| 特性 | 说明 |
|---|---|
| 基于路径 | 策略基于文件路径而非 inode/标签,直观易懂 |
| 以程序为中心 | 每个可执行文件绑定一个 Profile,非白名单程序不受限制 |
| 默认放行 | 未配置 Profile 的程序不受任何限制(与 SELinux 的默认拒绝相反) |
| 渐进式部署 | 支持 Complain(仅记录)和 Enforce(强制拦截)两种模式 |
| 内核级执行 | 策略在内核中执行,被限制的应用无法绕过或修改策略 |
| 与 LSM 集成 | 作为 Linux Security Module 框架的一部分,与内核深度整合 |
AppArmor 在安全架构中的位置
┌─────────────────────────────────────────────────┐
│ 用户空间 │
│ ┌──────────┐ ┌──────────┐ ┌───────────────┐ │
│ │ 应用程序 │ │aa-genprof│ │apparmor_parser│ │
│ │ (受限) │ │(策略生成) │ │ (策略加载) │ │
│ └────┬─────┘ └──────────┘ └───────┬───────┘ │
│ │ │ │
├───────┼──────────────────────────────┼──────────┤
│ │ 内核空间 │ │
│ ▼ ▼ │
│ ┌─────────────────────────────────────────┐ │
│ │ LSM Hooks (安全钩子) │ │
│ │ ┌─────────────────────────────────┐ │ │
│ │ │ AppArmor LSM Module │ │ │
│ │ │ ┌──────────┐ ┌───────────────┐ │ │ │
│ │ │ │ 策略缓存 │ │ DFA 匹配引擎 │ │ │ │
│ │ │ │ (Policy │ │ (确定性有限 │ │ │ │
│ │ │ │ Cache) │ │ 自动机) │ │ │ │
│ │ │ └──────────┘ └───────────────┘ │ │ │
│ │ └─────────────────────────────────┘ │ │
│ └─────────────────────────────────────────┘ │
│ │
│ 文件系统 │ 网络栈 │ Capabilities │ IPC │
└─────────────────────────────────────────────────┘
2. 历史背景与设计哲学
发展时间线
| 时间 | 里程碑 |
|---|---|
| 1998 | Immunix 公司开始开发 AppArmor 的前身 SubDomain |
| 2005 | Novell 收购 Immunix,将 SubDomain 重命名为 AppArmor |
| 2006 | AppArmor 首次以补丁形式发布(Linux 2.6.15+) |
| 2007 | Novell 将 AppArmor 开源(GPL) |
| 2009 | AppArmor 2.3 发布,网络规则、DBus 规则引入 |
| 2010 | Linux 2.6.36:AppArmor 合入主线内核 |
| 2014 | AppArmor 3.0:DFA 策略编译引擎、细粒度网络控制 |
| 2019+ | 成为 Ubuntu、SUSE 的默认 MAC 系统;深度集成 Docker/K8s |
| 2024+ | 策略缓存优化、用户空间通知增强、io_uring 控制支持 |
设计哲学:与 SELinux 的根本差异
AppArmor 的设计遵循 KISS(Keep It Simple, Stupid) 原则:
| 设计维度 | AppArmor | SELinux |
|---|---|---|
| 标识方式 | 文件路径(/usr/bin/nginx) |
安全标签(system_u:object_r:httpd_exec_t:s0) |
| 默认行为 | 未配置 = 不受限 | 未配置 = 全部拒绝 |
| 策略粒度 | 程序级别 | 进程/文件/端口/用户全维度 |
| 学习曲线 | 低(数小时即可上手) | 高(需要系统学习) |
| 适用场景 | 单应用隔离、容器安全 | 多级安全(MLS)、军事/政府系统 |
哲学核心:AppArmor 认为,安全策略应该与用户日常使用的应用程序名称和路径直接对应,而非依赖抽象的安全标签系统。
3. 核心架构与工作原理
3.1 LSM 框架集成
AppArmor 作为 Linux Security Module(LSM)框架的一部分,通过在内核关键路径上注册 钩子函数(Hooks) 实现访问控制。
内核编译配置:
# 启用 AppArmor
CONFIG_SECURITY=y
CONFIG_SECURITY_APPARMOR=y
# 设置为默认 LSM(可与其他 LSM 共存)
CONFIG_LSM="apparmor,selinux"
# 内核启动参数
# 禁用 AppArmor:apparmor=0
# 指定 LSM 顺序:lsm=apparmor,selinux
主要 LSM 钩子位置:
系统调用入口
│
▼
open("/etc/shadow", O_RDONLY)
│
▼
security_file_open() ← AppArmor LSM Hook
│
├─ 检查当前任务的 Profile
├─ DFA 匹配:/etc/shadow 是否在允许列表中?
│
├─ 允许 → 继续执行
└─ 拒绝 → 返回 -EACCES + 审计日志
3.2 基于路径的访问控制
AppArmor 的核心特征是基于路径(Path-Based)的访问控制,而非 SELinux 的基于标签(Label-Based)。
路径解析过程:
// 内核中 AppArmor 的路径解析(简化版)
// 源码:security/apparmor/file.c
当进程尝试访问 /var/www/html/index.html 时:
1. 获取当前任务的 Profile(如 /usr/sbin/nginx)
2. 将目标路径规范化为绝对路径
3. 在 Profile 的规则集中匹配:
- /var/www/html/index.html
- /var/www/html/**
- /var/www/**
- /var/**
- /**
4. 找到第一条匹配规则,应用其权限
优点:
- 策略直观,直接使用文件路径,无需理解标签系统
- 文件移动/重命名后策略自动适用(如果通配符规则正确)
- 排查方便:
dmesg中直接显示被拒绝的文件路径
局限:
- 硬链接可能绕过路径检查(因为 AppArmor 基于路径而非 inode)
- 需要确保文件系统路径不被篡改
3.3 策略编译与 DFA 引擎
AppArmor 策略在内核中使用 确定性有限自动机(DFA,Deterministic Finite Automaton) 进行高效匹配。
策略加载流程:
文本 Profile ──► apparmor_parser ──► 二进制策略 ──► 内核 DFA 引擎
(/etc/ (用户空间) (编译后的 (内核空间
apparmor.d/) 策略缓存) 高效匹配)
# 编译并加载 Profile
sudo apparmor_parser -r /etc/apparmor.d/usr.bin.nginx
# 查看已加载的策略
sudo aa-status
# 预编译策略(加速启动)
sudo apparmor_parser -Q /etc/apparmor.d/usr.bin.nginx
4. 配置文件(Profile)语法详解
4.1 配置文件结构
AppArmor Profile 文件存储在 /etc/apparmor.d/,文件命名通常将路径中的 / 替换为 .:
/etc/apparmor.d/
├── abstractions/ # 抽象层(可复用的规则片段)
│ ├── base
│ ├── nameservice
│ └── python
├── tunables/ # 全局变量
│ ├── global
│ └── home
├── local/ # 本地自定义
├── usr.bin.nginx # Nginx 的 Profile
├── usr.sbin.mysqld # MySQL 的 Profile
└── usr.bin.evince # Evince 文档查看器的 Profile
Profile 基本结构:
# 头部:定义 Profile 名称和标志
#include <tunables/global>
profile myapp /usr/bin/myapp flags=(complain) {
# 包含抽象层
#include <abstractions/base>
# 能力规则
capability net_bind_service,
# 网络规则
network inet tcp,
# 文件规则
/usr/bin/myapp mr,
/etc/myapp/** r,
/var/log/myapp.log rw,
/tmp/myapp-* rwl,
# 拒绝规则(在允许规则之后)
deny /etc/shadow r,
}
4.2 文件路径规则与权限
权限字母
| 权限 | 含义 | 说明 |
|---|---|---|
| r | Read(读) | 读取文件内容 |
| w | Write(写) | 写入/截断/创建文件 |
| a | Append(追加) | 仅追加模式写入 |
| m | Memory map(内存映射) | mmap() 可执行映射 |
| l | Link(链接) | 创建硬链接 |
| k | Lock(锁定) | 文件锁 flock() |
| x | Execute(执行) | 执行程序(需配合执行模式) |
通配符语法
| 通配符 | 含义 | 示例 |
|---|---|---|
* |
匹配任意字符(不含 /) |
/tmp/* → /tmp/a, /tmp/abc |
** |
匹配任意字符(含 /) |
/var/** → /var/a/b/c |
? |
匹配单个字符 | /tmp/file? → /tmp/file1 |
[abc] |
匹配字符集中一个 | /tmp/file[123] → /tmp/file1 |
{a,b} |
匹配备选项之一 | /var/{log,run}/** |
规则示例
# 只读访问配置文件目录
/etc/nginx/** r,
# 读写访问日志文件
/var/log/nginx/*.log rw,
# 可读写并可锁定
/var/lib/nginx/** rwk,
# 允许执行脚本(仅读取+映射执行)
/usr/share/nginx/** mr,
# 允许读写 + 创建链接
/tmp/nginx-* rwl,
# 所有者只写(@{OWNER} 变量匹配文件所有者)
@{HOME}/.nginx-tmp rw,
# 明确拒绝(deny 规则优先级高于 allow)
deny /etc/shadow r,
deny /root/** rwx,
4.3 执行模式控制
AppArmor 对子进程的执行提供了精细的模式控制:
| 模式 | 语法 | 行为 |
|---|---|---|
| ix(inherit) | /bin/sh ix, |
子进程继承当前 Profile |
| Px(profile) | /bin/sh Px, |
子进程切换到自己的 Profile |
| Cx(child) | /bin/sh Cx, |
切换到子 Profile(Hat) |
| Ux(unconfined) | /bin/sh Ux, |
子进程不受限制 |
| px(profile-clean) | /bin/sh px, |
切换 Profile 并清理环境 |
| Pix(混合) | /bin/sh Pix, |
优先 Px,Profile 不存在则 ix |
# 示例:Apache 的 Profile
/usr/sbin/apache2 {
# Apache 主进程
/usr/sbin/apache2 mr,
# CGI 脚本:切换到各自的 Profile(如果存在)
/usr/lib/cgi-bin/** Px,
# 系统 shell:受限制的子 Profile
/bin/dash Cx -> apache2-dash,
# 辅助工具:继承 Apache 的限制
/usr/bin/rotatelogs ix,
}
# 子 Profile(Hat)
profile apache2-dash {
#include <abstractions/base>
/bin/dash mr,
/usr/bin/id ix,
/bin/ls ix,
}
4.4 能力(Capability)规则
AppArmor 可以精确控制进程能使用哪些 Linux Capabilities:
# 允许绑定低端口(<1024)
capability net_bind_service,
# 允许修改系统时间
capability sys_time,
# 允许发送信号给其他进程
capability kill,
# 允许使用 chroot
capability sys_chroot,
# 允许修改文件所有者
capability chown,
# 允许绕过文件权限检查
capability dac_override,
# 允许设置 UID/GID
capability setuid,
capability setgid,
# 拒绝特定能力
deny capability sys_admin,
deny capability sys_module,
常用 Capabilities 速查:
| Capability | 用途 |
|---|---|
net_bind_service |
绑定 1024 以下的端口 |
net_raw |
使用 RAW 和 PACKET 套接字 |
sys_admin |
系统管理操作(mount 等) |
sys_ptrace |
跟踪其他进程 |
sys_module |
加载/卸载内核模块 |
sys_time |
修改系统时钟 |
4.5 网络规则
# 网络域类型
network inet, # IPv4
network inet6, # IPv6
network netlink, # Netlink 套接字
# 协议类型
network tcp, # TCP
network udp, # UDP
network icmp, # ICMP
network raw, # RAW 套接字
# 组合使用
network inet tcp, # IPv4 TCP
network inet6 udp, # IPv6 UDP
network inet stream, # IPv4 TCP 流套接字
network inet dgram, # IPv4 UDP 数据报套接字
# 创建套接字
network create inet,
# 拒绝
deny network raw,
deny network packet,
4.6 其他资源控制
# 挂载控制
mount, # 允许所有挂载
mount /dev/sda1 -> /mnt/data/, # 允许特定挂载
deny mount /dev/sda1 -> /mnt/secret/, # 拒绝特定挂载
umount, # 允许卸载
# 信号控制
signal (send) peer=nginx, # 允许向 nginx 发送信号
signal (receive) peer=unconfined, # 允许接收非受限进程的信号
# Ptrace 控制
ptrace peer=@{profile_name}, # 允许跟踪同 Profile 的进程
deny ptrace, # 拒绝跟踪
# D-Bus 控制
dbus bind bus=session, # 绑定 Session Bus
dbus (send) bus=system path=/org/freedesktop/*,
# Unix 域套接字
unix (create),
unix (send receive) type=stream,
4.7 变量、别名与抽象层
变量定义
# 在 Profile 文件开头定义
@{APP_HOME}=/opt/myapp
@{LOG_DIR}=/var/log/myapp
profile myapp /opt/myapp/bin/myapp {
@{APP_HOME}/bin/** mr,
@{APP_HOME}/etc/** r,
@{LOG_DIR}/** rw,
}
# 预定义变量(在 tunables/global 中)
@{HOME} # 用户主目录
@{HOSTNAME} # 主机名
@{pid} # 进程 PID
@{PROC} # /proc
别名
# 为路径定义别名
alias /usr/sbin/nginx -> /usr/bin/nginx,
抽象层(Abstractions)
抽象层是可复用的规则片段,减少重复代码:
#include <abstractions/base> # 基础系统访问(推荐始终包含)
#include <abstractions/nameservice> # DNS 解析
#include <abstractions/python> # Python 运行时
#include <abstractions/perl> # Perl 运行时
#include <abstractions/ssl_certs> # SSL 证书访问
#include <abstractions/private-files> # 排除用户私有文件
常用的 abstractions/base 包含内容:
# 基础文件系统访问
/proc/*/mounts r,
/sys/kernel/security/apparmor/** r,
# 动态链接器
/lib/ld-*.so* mr,
/lib{,32,64}/ld-*.so mr,
# 基本库
/lib/**.so* mr,
/usr/lib/**.so* mr,
4.8 子配置文件与 Hat
Hat(子配置文件)允许为同一程序的不同执行上下文定义不同的安全域:
/usr/sbin/httpd {
# Apache 主进程规则
/usr/sbin/httpd mr,
# 定义 Hat
^handlers {
# CGI 处理器的安全上下文
/usr/lib/cgi-bin/** Px,
}
^default {
# 默认上下文
/var/www/** r,
}
# 切换到 Hat
change_profile -> **,
}
5. 两种运行模式:Enforce vs Complain
Enforce 模式(强制模式)
- 行为:违规操作被阻止并记录日志
- 用途:生产环境的安全执行
- 特点:这是默认模式
# 设置为 enforce 模式
sudo aa-enforce /etc/apparmor.d/usr.bin.nginx
# 在 Profile 中声明
profile myapp /usr/bin/myapp flags=(enforce) { ... }
Complain 模式(投诉/学习模式)
- 行为:违规操作被允许,但记录到日志
- 用途:开发新 Profile、调试现有策略
- 特点:不影响应用正常运行
# 设置为 complain 模式
sudo aa-complain /etc/apparmor.d/usr.bin.nginx
# 在 Profile 中声明
profile myapp /usr/bin/myapp flags=(complain) { ... }
模式切换流程图
新应用需要 AppArmor 保护
│
▼
┌──────────────────────────┐
│ 1. 设为 complain 模式 │
│ 运行应用,收集日志 │
└──────────┬───────────────┘
│
▼
┌──────────────────────────┐
│ 2. aa-logprof 分析日志 │
│ 生成/更新规则 │
└──────────┬───────────────┘
│
▼
┌──────────────────────────┐
│ 3. 测试规则是否完整 │
│ (保持 complain) │
└──────────┬───────────────┘
│ 无异常
▼
┌──────────────────────────┐
│ 4. 切换到 enforce 模式 │
│ (生产就绪) │
└──────────────────────────┘
6. 工具链与实战
6.1 命令行工具速查
| 命令 | 功能 |
|---|---|
aa-status |
查看 AppArmor 状态和已加载的 Profile |
aa-enforce <profile> |
将 Profile 设为强制模式 |
aa-complain <profile> |
将 Profile 设为投诉模式 |
aa-genprof <program> |
交互式生成新 Profile |
aa-logprof |
从日志中更新已有 Profile |
aa-autodep <program> |
创建最小骨架 Profile |
aa-unconfined |
列出不受 AppArmor 限制的进程 |
apparmor_parser -r <file> |
加载/替换 Profile |
apparmor_parser -R <file> |
卸载 Profile |
apparmor_parser -Q <file> |
预编译 Profile(不加载) |
apparmor_status |
查看 AppArmor 状态(同 aa-status) |
6.2 手动编写配置文件
为 Nginx 编写自定义 Profile
# 1. 创建 Profile 文件
sudo vim /etc/apparmor.d/usr.sbin.nginx
#include <tunables/global>
/usr/sbin/nginx {
#include <abstractions/base>
#include <abstractions/nameservice>
#include <abstractions/ssl_certs>
# 能力控制
capability net_bind_service,
capability setuid,
capability setgid,
capability dac_override,
deny capability sys_admin,
deny capability sys_module,
# 网络控制
network inet tcp,
network inet6 tcp,
deny network raw,
# 二进制文件
/usr/sbin/nginx mr,
# 配置文件(只读)
/etc/nginx/** r,
/etc/ssl/** r,
# 日志文件(读写)
/var/log/nginx/*.log rw,
# 网站内容(只读)
/var/www/** r,
# PID 文件
/var/run/nginx.pid rw,
# 临时文件
/tmp/nginx-* rw,
# 明确拒绝敏感路径
deny /etc/shadow r,
deny /root/** rwx,
deny /etc/ssh/** r,
}
# 2. 加载 Profile
sudo apparmor_parser -r /etc/apparmor.d/usr.sbin.nginx
# 3. 设为 enforce 模式
sudo aa-enforce /usr/sbin/nginx
# 4. 重启 Nginx 验证
sudo systemctl restart nginx
6.3 使用 aa-genprof 交互式生成配置
aa-genprof 是最常用的 Profile 生成工具,通过分析应用行为日志自动生成规则:
# 1. 启动交互式生成
sudo aa-genprof /usr/bin/myapp
# 输出示例:
# Updating AppArmor profiles in /etc/apparmor.d.
# Writing updated profile for /usr/bin/myapp.
# Setting /usr/bin/myapp to complain mode.
#
# Before you begin, you may wish to check if a
# default profile exists for the application.
#
# Please start the application to be profiled in
# another window and exercise its functionality now.
#
# Once completed, select the "Scan" option below.
# 2. 在另一个终端运行应用
sudo /usr/bin/myapp --do-stuff
# 3. 回到 aa-genprof 终端,按 S 扫描日志
# Profile: /usr/bin/myapp
# Path: /etc/myapp/config.yml
# New Mode: owner r
# Severity: 4
#
# [1 - /etc/myapp/config.yml]
# (A)llow / [(D)eny] / (I)gnore / Audi(t) / Abo(r)t / (F)inish
# 4. 为每个事件选择操作
# A - 允许
# D - 拒绝
# I - 忽略(跳过此规则)
# F - 完成
# 5. 保存:按 S 保存 Profile
6.4 使用 aa-logprof 更新已有配置
当应用行为变化时,使用 aa-logprof 更新已有 Profile:
# 分析审计日志,更新 Profile
sudo aa-logprof
# 交互式选择:
# Profile: /usr/bin/myapp
# Path: /var/log/myapp/new-feature.log
# New Mode: owner rw
# Severity: 4
#
# (A)llow / (D)eny / (I)gnore / (G)lob / Glob w/Ext / Abo(r)t / (F)inish
#
# G - 使用通配符(如 /var/log/myapp/*.log)
# Glob w/Ext - 保留扩展名的通配符
6.5 调试与排错
查看拒绝日志
# 实时查看 AppArmor 审计日志
sudo dmesg -w | grep -i apparmor
# 输出示例:
# audit: type=1400 audit(1453830992.845:37): apparmor="DENIED"
# operation="open" profile="/usr/bin/myapp" name="/etc/shadow"
# pid=1234 comm="myapp" requested_mask="r" denied_mask="r"
# 使用 journalctl(systemd 系统)
sudo journalctl -k | grep -i apparmor
sudo journalctl -f -k | grep -i apparmor
分析拒绝日志
# 日志关键字段解读
# operation="open" ← 操作类型
# profile="/usr/bin/myapp" ← 受限的 Profile
# name="/etc/shadow" ← 被拒绝访问的资源
# requested_mask="r" ← 请求的权限
# denied_mask="r" ← 被拒绝的权限
# comm="myapp" ← 进程命令名
# pid=1234 ← 进程 PID
常见调试流程
# 1. 将 Profile 设为 complain 模式
sudo aa-complain /usr/bin/myapp
# 2. 重新运行应用(执行所有功能)
sudo /usr/bin/myapp
# 3. 用 aa-logprof 从日志生成规则
sudo aa-logprof
# 4. 检查生成的规则是否合理
cat /etc/apparmor.d/usr.bin.myapp
# 5. 切换回 enforce 模式
sudo aa-enforce /usr/bin/myapp
# 6. 验证无异常
sudo dmesg -c > /dev/null # 清空日志
sudo /usr/bin/myapp
sudo dmesg | grep -i apparmor
7. AppArmor 与 Docker 容器安全
7.1 Docker 默认 AppArmor 配置
Docker 自动为每个容器生成并加载名为 docker-default 的 Profile:
# 查看 Docker 是否使用了 AppArmor
docker info | grep -i apparmor
# 查看容器的安全选项
docker inspect <container> | jq '.[0].HostConfig.SecurityOpt'
# 查看容器进程的 AppArmor 状态
cat /proc/$(docker inspect -f '{{.State.Pid}}' <container>)/attr/current
# 输出:docker-default (enforce)
docker-default 模板源码位置:
https://github.com/moby/moby/blob/master/profiles/apparmor/template.go
7.2 为容器编写自定义配置
# 1. 创建自定义 Profile
sudo vim /etc/apparmor.d/containers.d/docker-nginx
#include <tunables/global>
profile docker-nginx flags=(attach_disconnected,mediate_deleted) {
#include <abstractions/base>
# 网络
network inet tcp,
network inet udp,
network inet icmp,
deny network raw,
file,
umount,
# 拒绝写入系统关键目录
deny /bin/** wl,
deny /boot/** wl,
deny /dev/** wl,
deny /etc/** wl,
deny /lib/** wl,
deny /lib64/** wl,
deny /proc/** wl,
deny /root/** wl,
deny /sbin/** wl,
deny /sys/** wl,
deny /usr/** wl,
# 审计写操作
audit /** w,
# Nginx 特定
/var/run/nginx.pid w,
/usr/sbin/nginx ix,
/var/www/** r,
# 禁止执行 shell
deny /bin/dash mrwklx,
deny /bin/sh mrwklx,
# 必要能力
capability chown,
capability dac_override,
capability setuid,
capability setgid,
capability net_bind_service,
deny mount,
}
# 2. 加载 Profile
sudo apparmor_parser -r -W /etc/apparmor.d/containers.d/docker-nginx
# 3. 使用自定义 Profile 运行容器
docker run --security-opt "apparmor=docker-nginx" \
-p 80:80 -d --name secure-nginx nginx
# 4. 验证
docker exec secure-nginx cat /proc/1/attr/current
# 输出:docker-nginx (enforce)
7.3 使用 bane 简化容器配置生成
bane 是一个用 TOML 语法生成 AppArmor Profile 的工具,专为 Docker 容器设计:
# 安装
sudo apt install bane
# 编写简化的 TOML 配置
cat > nginx.toml << 'EOF'
Name = "docker-nginx"
[Filesystem]
# 只读路径
ReadOnlyPaths = [
"/bin/**", "/boot/**", "/dev/**", "/etc/**",
"/lib/**", "/lib64/**", "/proc/**",
"/sbin/**", "/sys/**", "/usr/**"
]
# 允许执行
AllowExec = ["/usr/sbin/nginx"]
# 允许写入
AllowWrite = ["/var/run/nginx.pid", "/var/log/nginx/**"]
[Network]
# 允许的 TCP 端口
TCP = ["80", "443"]
[Capability]
# 允许的能力
Allow = ["chown", "dac_override", "setuid", "setgid", "net_bind_service"]
EOF
# 生成 AppArmor Profile
bane nginx.toml > /etc/apparmor.d/containers.d/docker-nginx
8. AppArmor 与 Kubernetes
Kubernetes 从 v1.4 开始支持 AppArmor(Beta),v1.30 起达到 GA:
apiVersion: v1
kind: Pod
metadata:
name: apparmor-pod
annotations:
# 为容器指定 AppArmor Profile
container.apparmor.security.beta.kubernetes.io/nginx: localhost/docker-nginx
spec:
containers:
- name: nginx
image: nginx:latest
nodeSelector:
# AppArmor 仅在特定节点上可用
kubernetes.io/os: linux
Kubernetes AppArmor 注解格式:
container.apparmor.security.beta.kubernetes.io/<container_name>:
<profile_ref>
| Profile 引用 | 含义 |
|---|---|
runtime/default |
使用容器运行时的默认 Profile |
localhost/<name> |
使用节点上预加载的 Profile |
unconfined |
不使用 AppArmor |
9. AppArmor vs SELinux:选型指南
| 对比维度 | AppArmor | SELinux |
|---|---|---|
| 设计理念 | 基于路径,程序为中心 | 基于标签,全局策略 |
| 默认发行版 | Ubuntu, SUSE, Debian | RHEL, CentOS, Fedora |
| 策略语法 | 简洁(类似配置文件) | 复杂(需要理解 TE/RBAC/MLS) |
| 学习曲线 | 1-3 天可上手 | 1-3 周基础掌握 |
| 默认行为 | 未配置 = 不受限 | 未配置 = 全部拒绝 |
| 文件系统 | 路径匹配(可能被硬链接绕过) | inode 标签(不受路径变化影响) |
| 多级安全(MLS) | 不支持 | 完整支持 |
| 容器生态 | Docker/Podman 原生支持 | 需要配置和标签管理 |
| 策略调试 | complain 模式 + aa-logprof | permissive 域 + audit2allow |
| 适用场景 | 中小型部署、单应用隔离、容器 | 大型企业、政府/军事、多级安全 |
选型建议
需要多级安全(MLS)?
├─ 是 → SELinux
└─ 否 → 继续
使用 RHEL/CentOS 生态?
├─ 是 → SELinux(系统默认)
└─ 否 → 继续
使用 Ubuntu/SUSE/Debian 生态?
├─ 是 → AppArmor(系统默认)
└─ 否 → 继续
团队安全经验有限,需要快速部署?
├─ 是 → AppArmor(简单易用)
└─ 否 → 继续
需要容器级别的安全隔离?
├─ 是 → AppArmor(Docker/K8s 原生集成更好)
└─ 否 → 根据发行版选择
需要最高安全级别和精细控制?
├─ 是 → SELinux
└─ 否 → AppArmor
10. 安全加固最佳实践
10.1 Profile 编写原则
# ✅ 好:最小权限原则
profile secure-app /opt/app/bin/secure-app {
#include <abstractions/base>
# 只允许必需的路径
/opt/app/bin/secure-app mr,
/opt/app/etc/config.yml r,
/opt/app/logs/*.log rw,
# 只允许必需的 capability
capability net_bind_service,
# 明确拒绝敏感操作
deny capability sys_admin,
deny capability sys_module,
deny /etc/shadow r,
deny /root/** rwx,
}
# ❌ 坏:过度宽松
profile loose-app /opt/app/bin/loose-app {
#include <abstractions/base>
capability, # 允许所有 capability!
/** rw, # 允许读写整个文件系统!
network, # 允许所有网络!
}
10.2 生产环境 Checklist
| 项目 | 状态 | 说明 |
|---|---|---|
| 所有关键服务有 Profile | ⬜ | Nginx/Apache/MySQL/Redis 等 |
| Profile 处于 enforce 模式 | ⬜ | aa-status 确认 |
| 使用最小权限原则 | ⬜ | 仅允许必需的路径和能力 |
| 明确 deny 敏感路径 | ⬜ | /etc/shadow, /root/** 等 |
| 定期审计日志 | ⬜ | dmesg 检查 DENIED 记录 |
| 容器使用自定义 Profile | ⬜ | 而非 docker-default |
| CI/CD 集成 Profile 验证 | ⬜ | 部署前测试 Profile |
| 备份 Profile 文件 | ⬜ | /etc/apparmor.d/ 纳入版本控制 |
10.3 多层防护组合
# 完整的容器安全配置
docker run -d \
--name ultra-secure-app \
--security-opt apparmor=custom-profile \ # AppArmor 文件访问控制
--security-opt seccomp=strict.json \ # Seccomp 系统调用过滤
--security-opt no-new-privileges:true \ # 禁止提权
--cap-drop=ALL \ # 移除所有 Capabilities
--cap-add=NET_BIND_SERVICE \ # 只加回必需的
--read-only \ # 根文件系统只读
--tmpfs /tmp:noexec,nosuid \ # tmpfs 限制
myapp:latest
11. 常见问题排查
问题 1:应用无法启动,日志显示 Permission Denied
# 诊断步骤
# 1. 检查是否是 AppArmor 导致
sudo dmesg | grep -i "apparmor.*DENIED"
# 2. 临时切换到 complain 模式确认
sudo aa-complain /usr/bin/myapp
sudo /usr/bin/myapp # 如果能启动,说明是 AppArmor 拦截
# 3. 用 aa-logprof 更新 Profile
sudo aa-logprof
# 4. 切换回 enforce
sudo aa-enforce /usr/bin/myapp
问题 2:容器退出,无任何日志
# 可能是 AppArmor 阻止了关键操作
docker run --security-opt apparmor=unconfined <image> # 临时绕过
# 如果容器能启动,说明需要调整 Profile
问题 3:aa-genprof 生成规则不全
# 确保覆盖所有功能路径
# 在 complain 模式下完整运行应用的所有功能
# 包括:启动、正常操作、异常处理、停止
# 重新扫描日志
sudo aa-logprof
问题 4:AppArmor 未启用
# 检查内核支持
grep -i apparmor /boot/config-$(uname -r)
# 应有:CONFIG_SECURITY_APPARMOR=y
# 检查是否加载
sudo aa-status
# 或
cat /sys/module/apparmor/parameters/enabled
# 输出 Y 表示已启用
# 如果未加载
sudo systemctl enable apparmor
sudo systemctl start apparmor
问题 5:Profile 更新后未生效
# 重新加载 Profile
sudo apparmor_parser -r /etc/apparmor.d/usr.bin.myapp
# 或重新启动 apparmor 服务
sudo systemctl reload apparmor
# 验证
sudo aa-status | grep myapp
12. 总结
核心要点
| 要点 | 说明 |
|---|---|
| MAC 安全模块 | AppArmor 是 LSM 框架下的强制访问控制系统 |
| 基于路径 | 策略使用文件路径而非标签,直观易用 |
| 以程序为中心 | 每个可执行文件一个 Profile,未配置的程序不受影响 |
| 两种模式 | Complain(学习/调试)和 Enforce(强制执行) |
| 渐进式部署 | 从 complain 开始 → aa-logprof 完善 → enforce 上线 |
| 容器集成 | Docker/K8s 原生支持,docker-default 自动保护 |
| 内核执行 | 策略在内核中强制,应用无法绕过 |
推荐资源
- AppArmor 官方文档
- AppArmor GitLab 仓库
- Docker AppArmor 安全配置
- Ubuntu AppArmor Wiki
- Arch Linux AppArmor Wiki
- AppArmor Profile 语法手册
免责声明:本文内容基于 Linux 内核文档和 AppArmor 官方资料编写,适用于 Linux 2.6.36+ 内核系列。具体 API 行为和发行版配置可能有所差异,请以实际运行环境为准。