Linux内核分析之安全模块-02
This language version is unavailable; showing the other language.
54.1 AppArmor 配置文件模型
AppArmor 的策略单元是 profile:绑定到可执行路径(或通过 change_profile 显式进入),内部是一组"路径 glob + 权限字母"的允许规则,外加能力清单与网络/信号约束。两种模式(enforce/complain)决定违规的后果。本节解剖 profile 的语法与生命周期。
54.1.1 profile 语法:路径 glob + 权限字母
一个真实风格的 profile (/etc/apparmor.d/usr.sbin.nginx):
/usr/sbin/nginx flags=(attach_disconnected) {
#include <abstractions/base> ← 通用规则集复用
#include <abstractions/nameservice>
capability net_bind_service, ← 能力白名单 (55 章)
capability setuid,
network inet stream, ← 网络语义约束
network inet dgram,
/etc/nginx/** r, ← 路径 glob + 权限字母
/var/log/nginx/* rw,
/var/lib/nginx/** rwk, ← k = 文件锁 (33.5 节)
/usr/sbin/nginx mr, ← m=映射执行
/{,usr/}lib{,32,64}/** mr,
/proc/*/environ r,
deny /etc/shadow rwkl, ← 显式拒绝 (优先审计)
audit /etc/passwd r, ← 放行但记审计
}
权限字母表:
r 读 w 写 a 追加 k 锁 l 链接 m 映射执行
x 执行 (变体: ix=本profile内执行 / px=独立profile
/ ux=无约束执行 / cx=<子profile>)
路径 glob:
* 单级任意 ** 多级任意 ? 单字符
{} 括号选择 [] 字符类
54.1.2 模式、附件与生命周期
两种模式 (aa_profile 的 mode 字段, policy.h:258 区):
enforce 违规 = 拒绝 + 审计 (真正的 MAC)
complain 违规 = 只审计不拒 (策略学习期:
aa-genprof 工具"跑应用→收集日志→生成规则")
profile 的三种绑定:
[1] 路径附着: profile 名 = 可执行路径
(exec 该文件 → 自动进入, 与 53.3.2 节
SELinux 域迁移对照: 前提是"路径匹配"而非
"四条策略规则")
[2] 名称附着 (name=): 需显式 change_profile
(PAM/容器运行时用)
[3] hat / 子 profile: ^child 语法 — 主 profile
内 exec 切入的子域 (邮件过滤管道模式)
生命周期:
apparmorfs 加载 profile (54.2 节) → 缓存哈希
exec 触发 aa_replace_current_profile (domain.c:1226
区) → 任务 ctx 指向新 profile (cred blob)
卸载: 引用计数归零后释放 (RCU)
与 SELinux 的模型对照(本卷核心对照):
| 维度 | SELinux | AppArmor |
|---|---|---|
| 标识 | 类型(语义标签) | 可执行路径 |
| 策略库 | 全系统统一库 | 每应用独立 profile |
| 新应用成本 | 需策略建模(域+类型+迁移) | 无 profile=不受限;写 profile 增量 |
| 匹配对象 | 类型对类型 | 路径 glob |
| 默认 | 全拒 | 无 profile 不受限 |
| 学习曲线 | 陡(全局建模) | 平(按应用增量) |
小结
AppArmor 的 profile 以"路径 glob + 权限字母 + 能力清单"表达最小权限,attach 到可执行路径实现自动进入,enforce/complain 两模式支撑从学习到强制的渐进部署;与 SELinux 的本质分野是"路径名 vs 类型"与"增量 vs 全局"——前者易部署、后者体系严密,两者都在 52 章框架下共存(堆叠)。下一节看内核如何执行这套 profile。
54.2 AppArmor 内核实现
AppArmor 的内核实现三件套:每任务的 aa_task_ctx(当前 profile 引用)、profile 内的 DFA 匹配器(路径 glob 编译成确定性自动机)、以及 apparmorfs 控制接口。本节解剖判定路径、DFA 与卸载语义。
54.2.1 任务上下文与判定路径
// security/apparmor/include/task.h:13
static inline struct aa_task_ctx *task_ctx(struct task_struct *task)
/* ctx: current_profile + previous (onexec/回退栈) */
// security/apparmor/domain.c:921/1226/1398(域切换三处调用点)
struct aa_task_ctx *ctx;
判定的完整路径 (open 示例):
apparmor_file_open (52.1 节 security_file_open 的消费端)
├─ task_ctx(current) → current_profile
│ (NULL → 无 profile, 直接放行 — 54.1.2 节)
├─ 取路径: 该文件的绝对路径 (namespace 内相对)
│ (54.1.1 节 attach_disconnected 处理匿名挂载)
├─ DFA 匹配 (match.c): 路径串过自动机
│ → 命中规则的权限位掩码
├─ 与请求权限 (MAY_READ 等) 求交
└─ 缺权限 → enforce: -EACCES + audit
complain: 只 audit
域切换 (aa_replace_current_profile, domain.c:1226 区):
exec 时按 54.1.2 节绑定匹配 → ctx 换 profile
(cred blob 的 AppArmor 柜位更新, 52.3.3 节)
change_profile: 显式切换 (容器运行时进入容器
profile 的通道)
54.2.2 DFA 匹配器与卸载
DFA (确定性有限自动机) 匹配器:
加载期: 用户态把 profile 的全部路径 glob 编译
成 DFA 转移表 (match.c 的 table 格式)
运行期: 路径字符串逐字符过自动机
→ 终态 = 命中规则的权限位集
优点: glob 语义一次编译, 运行期 O(路径长度)
无回溯 — 比"逐条 glob 匹配"快且抗退化
(SELinux 的对应物是 avtab 查询 — 53.2 节;
两者都把"策略"预编译成高速结构)
卸载/替换 (policy.c):
profile 哈希表 RCU 替换 (53.2 节同款)
旧 profile 引用计数 → 归零释放
仍引用旧 profile 的任务: 继续按旧跑 (引用
保护) 或按策略标记迁移
控制接口 (apparmorfs, /sys/kernel/security/apparmor):
.access / profiles profile 枚举与原始数据
/proc/<pid>/attr/current 查/改任务当前 profile
(与 SELinux 的 attr 接口同形!)
aa-status 全局统计 (enforce/complain 计数)
加载: 用户态 apparmor_parser 编译 → 写入
.load (policy.bin — 与 53.2.2 节 SELinux 装载
接口同思想: 内核收二进制, 不解析源语法)
小结
AppArmor 内核实现的三件套:aa_task_ctx 持当前 profile 引用(无 profile 即放行)、glob 语义预编译为 DFA 自动机(O(路径长) 判定、无回溯)、apparmorfs 以二进制装载与 attr 接口对齐 SELinux 的管理形态;域切换落在 exec 钩子与 change_profile,卸载走 RCU 替换与引用回收。它与 SELinux 共享 52 章框架,而以"路径、增量、易用"作为工程取舍。安全对比的完整图景在 52-54 章间闭合。下一章回到 DAC 的精细化——Capability 与 cred。