为什么会有 Linux 安全模块
在 Linux 上谈安全,SELinux 和 AppArmor 是两个绕不开的名字。第一次部署 CentOS 的人,多半被 SELinux 的 denied 日志折腾过;Ubuntu 用户接触 Docker 时,也大概率遇到过 AppArmor 相关的报错。而 Landlock 是这几年逐渐走近视野的另一个 LSM 实现,很多人把它挂在嘴边,却不太清楚它到底解决什么问题。理解这三者,关键不是背概念,而是先把 Linux 安全模块这套框架在系统里扮演的角色弄清楚。

传统 Linux 权限模型只回答“操作者是谁”,也就是 UID、GID 和文件 mode bit。这种模型的短板很明显:进程一旦拿到 root 权限,基本就不受约束。LSM 的出现在于把这些安全决策从内核业务逻辑里抽出来,变成一组可插拔的钩子。无论是打开文件、创建进程还是监听端口,内核在执行操作前都会询问安全模块:这次访问应该被允许吗?SELinux、AppArmor 和 Landlock,正是这个问题的三种不同回答方式。
三个模块,三种不同的安全观
SELinux:全系统打标签的强制访问控制
SELinux 由美国国家安全局主导贡献,是 Linux 世界里最老牌的强制访问控制实现。它的核心是标签:系统中的每个主体(进程)和客体(文件、端口、目录等)都有安全上下文,就像文件被贴上了分类标签。安全策略通过一组 TE(Type Enforcement)规则定义,哪些类型的进程可以访问哪些类型的客体,以及可以执行何种操作。默认情况下,凡是策略里没有显式允许的操作都会被拒绝。
这套机制能提供很高的隔离粒度,非常适合多租户或合规要求严格的场景,它甚至能限制进程之间的信号发送,防止一个应用访问另一个应用的临时文件。但代价同样清晰:策略语言复杂,排查问题需要读 audit.log 里的 AVC 记录。很多初上手的团队会经历“服务莫名起不来,库里查不到原因”的挫败感,最后选择 setenforce 0 或者干脆在内核参数里把它关掉。这等于把一个重要防线交给了经验不足的维护者。
AppArmor:用路径描述进程权限
AppArmor 走了另一条路线:它直接以可执行文件的路径作为安全域标识,每个程序可以绑定一个 profile,profile 里列出了这个程序可以使用的文件路径、网络协议、capability 等权限。相比 SELinux 的标签体系,这种模型对系统管理员友好得多,配置文本清晰可读,几行就能覆盖一个守护进程的默认行为。
它的另一大优势在于“作案工具”随手可得:complain 模式下只记录违规行为而不拦截,管理员可以观察真实业务流量,再逐步收紧策略。Ubuntu/Debian 的发行版默认集成了部分 AppArmor profile,Docker 也会默认加载自己的 Docker profile 来限制容器进程。
但基于路径的模型也有自己的死角。如果被限制的程序创建了指向系统文件的符号链接,某类路径解析场景下规则可能被绕过;另外 profile 的继承关系比较复杂,父子进程切换可执行文件时,到底继承哪份 profile 需要小心设计。这些问题不是 AppArmor 的“原罪”,但说明它并非一个可以无脑开启就高枕无忧的开关。
Landlock:把沙箱能力交给进程自己
Landlock 的定位和前面两个模块不太一样。它面向的不是系统管理员,而是应用开发者——一个普通进程可以通过特定的系统调用,给自己创建一个受限的执行环境。这种“自我设限”的能力,比容器和 seccomp 更直接:一个解析 PDF 或解压文件的进程,启动时先创建 Landlock 规则集,只允许访问自己工作目录和显式声明的临时目录,之后所有文件系统操作都会被内核拒绝在权限集合之外。它不要求 root,不要求先写 policy 文件,内核会通过 landlock_create_ruleset、landlock_add_rule 和 landlock_restrict_self 这几个系统调用完成逐一限制。
Landlock 从内核 4.13 开始进入主线,后续几个大版本持续补齐文件系统权限和部分网络权限,现在已经能覆盖不少真实沙箱需求。更重要的是,Landlock 被设计成可以和其他 LSM 堆叠使用。系统启动时仍然可以加载 SELinux 或 AppArmor,Landlock 只对被主动调用的进程产生额外约束,不会干扰全局策略。
同一套 LSM 框架,差异比想象中大
既然都是走 LSM 钩子,真正拉开差距的是安全模型的决策方式。下面这个表格,基本能梳理出三个模块在实践中的差异:
| 比较项 | SELinux | AppArmor | Landlock |
|---|---|---|---|
| 安全模型 | 全系统标签 + 类型强制 | 进程路径 + 权限集 | 进程内规则集 |
| 默认拦截 | 未允许即拒绝 | 只限制已配置 profile 的进程 | 只限制主动设置沙箱的进程 |
| 配置方式 | 策略包 / policy 文件 | AppArmor profile 文本 | 系统调用创建 ruleset |
| 是否需要特权 | 需要 | 需要 | 不需要 |
| 典型控制对象 | 文件、端口、进程、网络对象等 | 文件、网络、mount、信号等 | 文件系统层级和部分网络端口 |
| 学习成本 | 高 | 低到中 | 中 |
| 典型场景 | 高安全服务器、多租户隔离 | Ubuntu/Debian 默认防护、容器默认 profile | 浏览器渲染进程、解析器内置沙箱 |
从工程角度看,这三种方案并不是要彼此替代。SELinux 适合拿整个系统作为信任边界来管理,AppArmor 适合快速把单个服务的行为卡住,Landlock 则把安全能力下沉到应用自身,形成纵深防御里很微妙的一环。
最容易踩的几个误区
- SELinux 一定比 AppArmor 更安全。安全水平取决于策略质量和持续维护。一个策略没有覆盖新版服务的 SELinux enforce 系统,未必比一个由运维团队定期 review 的 AppArmor profile 更可靠。
- 开启强制模式就等于安全到位。如果策略写得太宽,比如给某个进程放行了整个 /home,强制模式也只是名义上的强制。而拿 audit2allow 把 denied 记录全部自动转成 allow 的做法,会让策略逐渐形同虚设。
- AppArmor 只是简单的路径匹配。它也有 link 规则、挂载限制和 profile 继承,真正的漏洞往往来自 profile 未包含嵌套可执行文件,或者符号链接场景没约束好,而不是方案本身不具备能力。
- 有了 Landlock 就不需要容器和 seccomp。Landlock 只是给进程一个主动加锁的机制,它不解决进程隔离、资源限制和系统调用面的全局收紧,适合作为应用内建沙箱,而不是容器安全的替代品。
到了工程上,怎么选,怎么落地
一个很常见的小团队场景是这样:服务在 Ubuntu 上跑,Docker 默认开启了 AppArmor,于是大家默认“安全已经开着”。但真的去查日志会发现,Docker 的默认 profile 非常宽松,它主要挡住了一些容器逃逸常见路径,但不会根据业务定义进程该访问哪些目录。要真正管住应用,你仍然得为每个容器内的入口进程写一份自定义 AppArmor profile。
另一个场景是金融或政务系统,安全审计要求隔部门、隔应用。这种场景下 SELinux 的全系统标签能力很合适,但前提是你已经准备好一套策略发布流程。每次发布新版本、引入新的二进制,都可能需要更新策略包。没有这个流程,强制模式很快就会成为误伤源头,而不是保护层。
如果你开发的是基础工具,比如一个接受用户输入的压缩包解析器,那 Landlock 是目前很划算的选择。在 main 函数入口处调用 landlock 系统调用,把进程的文件系统访问范围限定在工作目录下,即使第三方解压库出了漏洞,攻击者能影响的文件也极其有限。下面是一段 AppArmor profile 示例,虽然和 Landlock 的实现机制不同,但能帮助理解“把权限收窄”这件事的粒度差异:
# /etc/apparmor.d/usr.local.bin.import-tool
#include <tunables/global>
/usr/local/bin/import-tool {
/etc/import-tool.conf r,
/data/** rw,
/tmp/import-*.log w,
/usr/bin/unpack ix,
network inet stream,
}
这个 profile 限定 import-tool 只能读取指定配置文件,在 /data 目录里读写文件,写 /tmp 下的以 import- 开头的日志,执行 /usr/bin/unpack 并继承当前 profile,同时允许 TCP 网络。如果用 SELinux 写同等约束,需要为两三个进程、若干文件和端口配置类型标签;而 Landlock 则要求应用内部通过 API 把这些权限逐一写进规则集。三者对策略的“爆炸半径”控制,显然站在不同的抽象层次上。
落到实际操作,无论选择哪一个,建议都从审计模式开始。SELinux 的 permissive、AppArmor 的 complain,都是让系统先记录违规但不拦截。跑上几周,你才能真正看到业务程序的访问模式,再决定哪些规则需要正式 enforce。如果一上来就开启强制模式,很容易陷入“安全团队在收紧,运维团队在绕开”的对抗循环。
最后说点什么
安全没有银弹,LSM 只是把选择权交到一个更底层的位置。SELinux 的精细、AppArmor 的直观、Landlock 的轻量和可叠加,分别回应不同阶段、不同规模的问题。真正重要的不是给系统贴上一张“启用了某安全模块”的标签,而是持续维护一套和业务匹配、会随着版本迭代而更新的策略。能解释清楚为什么这么限制、允许了谁、拒绝了什么,永远比一个照搬教程后强行 enforce 的系统更值得信任。
原创文章,作者:fudengji,如若转载,请注明出处:https://fudengji.cn/article/693/