
AppArmor:15年历史的Linux内核安全模块如何成为关键防线
来源标题:What on Earth Is 'AppArmor'
AppArmor 是一个在 Linux 内核中强制执行强制访问控制(MAC)的安全模块,自 2.6.36 起进入主线内核,并已在 Ubuntu 和 Debian 上默认启用超过十年。它对每个进程的文件打开、网络套接字、能力检查和 exec 调用进行策略管控。
以下正文由 AI 翻译自 Hacker News 最新(原文版权归原站所有),可能存在偏差,重要内容请以原文为准。
AI 编程 安全 Linux AppArmor 到底是什么 一个 15 岁的 Linux 内核模块,如何成为我们未曾亲手编写的代码的缺失安全网 Cheikh seck 8 分钟阅读· 12 小时前
我第一次遇到“AppArmor”这个名字是在 LimeWire 时代。这个词一直留在我脑海里,因为它听起来就像给应用程序披上的一层字面意义上的铠甲——一个包裹住你代码、把坏东西挡在外面的保护壳。当时我以为它是某种杀毒软件,或者防火墙插件。这个名字很有画面感,但含义模糊。我从未深究它到底做什么。
多年后,当我开始专业地写 Go 时,这个名字又出现了——这次是在不同的语境中。我了解到 AppArmor 根本不是用户态工具。它是一个 Linux 内核安全模块,对单个进程强制执行强制访问控制(MAC)。它自 2.6.36 起就存在于主线内核中,并且十多年来一直是 Ubuntu 和 Debian 的默认配置。这里的“铠甲”不是比喻——它是一个运行在内核内部的策略引擎,会介入每一次文件打开、每一个网络套接字、每一次能力检查、每一次 exec 调用。而且它不在乎二进制文件是人写的还是 AI 生成的。
最后这一点正是本文的重点。
新的威胁面 AI 编码代理的能力非常出色。让它构建一个 REST API,它几秒钟就能产出一个可工作的 main.go。让它从外部服务获取数据,它会自动接好 net/http,不需要你交代。让它处理用户上传,它会痛快地把文件写到磁盘。
问题不在于这些代理有恶意。它们没有。问题在于它们太字面了。它们生成的代码语法正确、在功能上符合提示词——但它们没有爆炸半径的概念。代理不知道 os.ReadFile("/etc/shadow") 是一起安全事件。它不知道向外连接攻击者控制的域名就是数据外泄。它不知道在不应出现的上下文中调用 exec.Command("/bin/sh") 就是一个等着发生的 shell 逃逸。
在 LimeWire 时代,威胁是用户主动下载恶意软件。今天,威胁更隐蔽:它是我们自己大规模生成、却没有逐行阅读的代码。一个 AI 代理一分钟能产出十几个文件。你没法一分钟审计十几个文件。风险就住在生成与审查之间的这道缝隙里。
AppArmor 填补了这道缝隙。它不信任代码的出处。它不在乎是谁写的。如果二进制路径匹配某个 profile,内核就会强制执行该策略——就这样。无论代码是手写的、AI 生成的,还是两者兼有, confinement 都完全相同。
我写了一个小型 Go 程序,用来触发六类系统访问。目的是演示当严格的 profile 生效时,AppArmor 实际会拦截什么、允许什么。
二进制文件位于 /usr/local/bin/go-apparmor。profile 名为 go-apparmor。这个程序做的事情如下:
Profile 逐行解读 配套的 profile(go-apparmor.txt)精确定义了允许什么。其余一切都被内核拒绝并记录日志。结构如下:
#include <tunables/global> profile go-apparmor /usr/local/bin/go-apparmor {
--- Base abstractions for libc, dynamic loader, etc. ---
#include <abstractions/base>
--- Filesystem: read-only access to a curated set of paths ---
/etc/hostname r, /proc/self/status r, /tmp/** rw,
Deny everything else under /etc
deny /etc/** r,
--- Network: deny all outbound network ---
deny network,
--- Capabilities: deny all capabilities ---
deny capability,
--- Process execution: deny exec of arbitrary binaries ---
deny /bin/ls x, deny /usr/bin/** x, deny /bin/sh x, deny /bin/bash x, deny /bin/** x,
--- Signals, ptrace, mounts ---
deny signal, deny ptrace, deny mount, deny umount,
每一节的作用如下:
当我运行这个实验时,内核审计日志实时捕获了每一次拒绝。以下是实际发生的情况:
apparmor="DENIED" operation="open" profile="go-apparmor" name="/proc/660438/cgroup" ... apparmor="DENIED" operation="open" profile="go-apparmor" name="/run/systemd/resolve/stub-resolv.conf" ... apparmor="DENIED" operation="create" class="net" profile="go-apparmor" family="unix" sock_type="stream" ... apparmor="DENIED" operation="create" class="net" profile="go-apparmor" family="inet" sock_type="dgram" ... apparmor="DENIED" operation="exec" profile="go-apparmor" name="/usr/lib/cargo/bin/coreutils/ls" ... apparmor="DENIED" operation="mknod" profile="go-apparmor" name="/etc/apparmor-test-marker" ...
有几点值得注意:
AI 代理可以生成一个 Go 程序,去读取敏感文件、打开网络连接或启动 shell——全都是完全合法的 Go 代码,也全都是危险的。AppArmor 提供了一层纵深防御,不信任代码的出处。
免费加入 Medium,获取这位作者的更新。
设想一个场景:你让代理“构建一个从互联网获取数据并在本地保存的工具”。代理产出的代码会:
在严格的 AppArmor profile 下,如果这些操作超出了允许集合,它们都不会成功。代理的代码会在内核层面失败——不是以令人困惑的错误失败,而是以明确的拒绝失败。而且因为 AppArmor 会记录每一次拒绝,你会确切知道代码试图做什么。
这对于以提升权限运行的 AI 编码代理尤其相关。如果一个代理拥有编辑你代码库的 sudo 权限,并且它写了一个辅助二进制文件、之后又被执行,AppArmor 可以 confine 那个二进制文件,无论它是谁写的。策略绑定的是二进制路径,不是作者。
profile 就是一个文本文件。要允许更多访问,编辑它并重新加载:
sudo apparmor_parser -r /etc/apparmor.d/go-apparmor # reload
要把 profile 切换到 complain 模式(只记录、不拒绝):
sudo aa-complain go-apparmor
要恢复 enforce 模式:
sudo aa-enforce go-apparmor
(原文较长,此处译出前 6000 字,完整内容请点「打开原文」。)
正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。