提到 Linux 下的强制访问控制(MAC),很多人第一反应是红帽系的 SELinux。但在 Debian 及其衍生发行版上,默认启用的却是另一套方案——AppArmor。这两者都基于 Linux 内核的 LSM(Linux Security Modules)框架实现,但设计哲学完全不同:SELinux 走的是全面而复杂的标签化路线,AppArmor 则选择了基于路径名的策略模型,简单直接,容易上手。本文就来聊聊为什么 Debian 选择 AppArmor,以及在实际系统中该如何使用和配置它。

一、SELinux 与 AppArmor 的核心区别
要理解 Debian 的选择,先得弄清楚这两套系统的本质差异。SELinux 采用的是基于标签的访问控制模型,系统中的每个文件、进程、端口、设备节点都会被赋予一个安全上下文标签,策略引擎根据主体标签和客体标签的匹配关系来决定是否放行。这种方式粒度极细,理论上可以对整个系统做完整的访问控制闭环,但代价是策略极其复杂——一个完整的目标策略动辄几万条规则,普通管理员很难手写,排错更是噩梦。相信用过 CentOS 的人都有体会,某个服务莫名起不来,最后发现是 SELinux 策略拦了某个端口或目录。
AppArmor 则完全不同,它以程序为中心,策略直接绑定到可执行文件的路径上。比如 /usr/sbin/mysqld 这个路径对应一份配置文件,里面规定了该程序可以读写哪些文件、可以访问哪些网络能力。规则写起来接近自然语言,比如允许读写 /var/lib/mysql/ 目录下的所有文件,一条规则就能表达清楚。这种基于路径名的设计让管理员读策略像读配置文档一样直观,学习和维护成本远低于 SELinux。
两者的日志和排错体验差距也很明显。AppArmor 的拒绝日志直接给出被拒绝的操作和涉及的路径,管理员看一眼就知道该在策略里加什么;SELinux 的 avc denied 日志虽然也能借助 audit2allow 生成规则,但生成的规则往往需要人工审视,滥用会导致策略形同虚设。另外从性能角度看,AppArmor 的策略匹配开销通常更小,SELinux 在开启完整策略时会有一定的系统调用开销,虽然在现代硬件上已经不太明显,但在嵌入式和容器场景中 AppArmor 的轻量化优势依然存在。
二、Debian 中 AppArmor 的安装与状态管理
自 Debian 10 Buster 起,AppArmor 已经默认安装并启用。可以通过 aa-status 命令查看当前状态:
# 查看 AppArmor 整体状态 sudo aa-status # 输出示例片段: # apparmor module is loaded. # 42 profiles are loaded. # 42 profiles are in enforce mode. # 0 profiles are in complain mode. # 确认内核已启用 AppArmor cat /sys/module/apparmor/parameters/enabled # 输出 Y 表示启用
如果使用的是更老的 Debian 版本,需要手动安装相关的软件包:
sudo apt update sudo apt install apparmor apparmor-utils # 如果需要通过内核启动参数强制启用 # 编辑 /etc/default/grub,在 GRUB_CMDLINE_LINUX 中追加: # security=apparmor sudo update-grub sudo reboot
需要强调的一点是,AppArmor 的保护效果取决于软件包是否自带策略。Debian 官方仓库中的很多服务软件(如 mysqld、named、tcpdump 等)都附带了 AppArmor 配置文件,安装后自动进入 enforce 模式。而你自己编译部署的程序默认没有任何策略,处于未受限状态,这就引出了下一节的内容——如何为自定义程序生成策略。
三、为自定义程序生成并调整策略
AppArmor 的一大亮点是提供了工具链帮助管理员半自动地生成策略。最常用的是 aa-genprof,它会为目标程序创建一份初始配置文件,并进入监听模式,此时你去正常使用这个程序,AppArmor 会记录所有被拒绝的操作,最后由你逐条决定允许与否。
# 为一个自定义脚本或程序生成策略 sudo aa-genprof /opt/myapp/server # 在另一个终端中正常运行该程序,模拟真实工作负载 # 回到 aa-genprof 的交互界面,按 s 扫描日志 # 对每条记录选择 (A)llow / (D)eny / (I)gnore # 完成后按 (F)inish 保存
如果程序已经在运行,后来才发现需要补充规则,可以用 aa-logprof 分析日志后增量调整。策略文件保存在 /etc/apparmor.d/ 目录下,文件名按路径转换,比如 /usr/sbin/mysqld 对应 etc.apparmor.d.sbin.mysqld(实际文件名为 usr.sbin.mysqld,点号替代斜杠)。手写规则也很简单,常见语法如下:
# /etc/apparmor.d/usr.sbin.mysqld 中的典型规则片段 /etc/mysql/*.pem r, /var/lib/mysql/ rwk, /var/lib/mysql/** rwk, /var/log/mysql/ rw, /var/log/mysql/*.log rw, network tcp, capability setgid, capability setuid,
策略文件头部还可以用 abi 和 include 引入公共规则集,比如 include <tunables/global>。日常运维中最常用的两个操作是模式切换:aa-complain 把某个策略切到仅记录不拦截的投诉模式,方便观察;aa-enforce 则切回强制模式,违规操作会被真正拦截并记录到日志。排查问题时,先看 /var/log/syslog 或通过 journalctl -k | grep -i apparmor 过滤 DENIED 关键字,日志里会明确写出哪个 profile 拒绝了什么操作,按图索骥补规则即可。
四、Debian 为什么不主推 SELinux
严格来说,Debian 并非完全抛弃了 SELinux,官方仓库里同样提供 selinux-basics、selinux-policy-default 等软件包,有需要的用户可以自行安装并切换。但 Debian 默认选择 AppArmor,更多是社区权衡的结果。首先是易用性:Debian 的用户群体非常广泛,从桌面用户到运维人员都有,一套不需要深入学习策略语言就能用起来的方案显然更符合多数人的需求。其次是生态配合:Ubuntu 从很早就将 AppArmor 作为默认方案并投入了大量策略开发工作,Debian 直接受益于这套共享的策略生态,主流软件包开箱即受保护。
从实际部署角度看,如果你的服务器运行的都是仓库里的标准软件,AppArmor 的默认覆盖已经够用,几乎不需要额外配置;如果你的场景要求多级安全、需要对信息流做细粒度标签隔离(比如政府或军事环境),那么 SELinux 的 MLS/MCS 能力是 AppArmor 不具备的,此时再考虑切换到 SELinux 也不迟。另外值得注意的是,容器领域两者都有应用,Docker 默认生成 AppArmor 策略,而不少 Kubernetes 发行版在 SELinux 主机上也能良好运行,选型时结合团队的技术储备比单纯比较功能强弱更有意义。
总结一下:Debian 上没有默认启用 SELinux,不是因为功能缺失,而是 AppArmor 的路径策略模型更贴合 Debian 的定位——够用、好懂、好维护。对绝大多数 Debian 用户来说,确认 AppArmor 处于启用状态、了解 aa-genprof 和 aa-logprof 的用法、知道怎么查 DENIED 日志,就已经把这套安全机制用到了实处。需要更强隔离能力时,再评估是否引入 SELinux,这才是务实的做法。