本文通过“Web服务应该能访问哪些文件”的例子,浅谈 AppArmor。

为便于理解,下面只讨论普通文件访问。使用 AppArmor 的场景中,假设目标进程已经受到对应策略的约束,并运行在 enforce(强制执行)模式下,不展开安装步骤和完整策略配置。

What

先把最容易混淆的一点说清楚:

1
2
3
Linux 文件权限允许访问
≠
AppArmor 也一定允许访问

AppArmor 是 Linux 中的一种强制访问控制机制,可以通过 profile(安全策略),限制进程能够访问哪些资源、执行哪些操作。

可以把 profile 理解为给程序制定的一份“允许清单”:

1
2
3
4
这个程序:
可以读取哪些文件?
可以写入哪些文件?
可以执行哪些程序?

它还可以约束网络访问等行为,本文只用文件读写来说明。

所以:

不仅看“运行程序的用户有没有权限”,还要看“这个程序的安全策略是否允许”。

Why

一个程序正常工作,通常不需要用到运行用户的全部访问权限。

例如,Web 服务需要读取网页、写入日志,但不一定需要读取同一台机器上的内部报表。

如果仅按用户身份控制权限,而这个用户恰好也能读取报表,那么 Web 服务就可能拥有这项并不需要的访问能力。

一旦程序出现漏洞,这些多余的权限也可能被利用。

AppArmor 的做法是:

1
2
3
用户原本能够访问的资源
↓
再根据程序的实际用途,限制它的访问范围

因此,它的目的可以概括为:

让程序只接触完成工作所需的资源,减少程序出问题后可能造成的影响。

它不是把程序中的漏洞修好了,而是在程序外面增加一道由内核执行的限制。

基础 Example

假设一个名为 demo-web 的 Web 服务,面对下面三个文件:

1
2
3
/srv/site/index.html             网页文件
/var/log/demo-web/access.log 日志文件
/srv/reports/internal.csv 与网站无关的内部报表

为突出 AppArmor 的作用,假设原有 Linux 文件权限允许该进程读取网页和报表,也允许读写日志;没有其他机制阻止这些访问。

下面只比较它对这三个文件的访问。

没有 AppArmor 限制

1
2
3
4
demo-web
├── 读取网页 → 允许
├── 读写日志 → 允许
└── 读取内部报表 → 允许

程序不需要报表,不代表操作系统会自动禁止它读取报表。

使用 AppArmor 限制

为它设置策略:允许读取网页、读写日志,但不允许读取内部报表。

1
2
3
4
demo-web + AppArmor profile
├── 读取网页 → 允许
├── 读写日志 → 允许
└── 读取内部报表 → 拒绝

即使有人利用 Web 服务的漏洞,让这个进程尝试读取报表,AppArmor 仍会按照策略拒绝这次访问。

区别就在于:

用户拥有的权限,不再全部成为这个程序可以使用的权限。

这里没有删除或隐藏报表,也没有修改报表原有的文件权限;只是额外限制了这个进程对它的访问。

How

只看三个关键点。

1. 用 profile 描述允许的访问

AppArmor 的文件访问规则主要通过路径和操作权限来描述。

例如,下面两行规则分别允许读取网页、读写日志:

1
2
/srv/site/index.html r,
/var/log/demo-web/access.log rw,

其中:

1
2
3
r:允许读取

rw:允许读取和写入

在本例的 enforce 策略中,如果没有任何规则允许读取 internal.csv,这次读取就会被拒绝。

不需要把所有不允许读取的文件,逐个列成一份黑名单。

这里只展示规则片段,不是可以直接部署的完整 profile;程序启动和正常运行所需的其他访问规则已经省略。

2. 将策略交给内核执行

用户态工具负责将 profile 加载到内核,目标进程需要在对应 profile 的约束下运行。

AppArmor 通过 LSM(Linux Security Modules) 框架参与内核的安全检查。在打开文件等操作中,内核会根据进程的策略,检查它是否有权进行这次访问。

1
2
3
4
5
程序尝试打开一个文件
↓
内核检查当前进程的 AppArmor 策略
↓
结合文件路径和请求的操作,决定是否放行

不是要求程序自己遵守约定,而是由内核执行限制。

需要注意,仅安装或启用 AppArmor,不等于所有进程都自动获得这些保护;还需要加载合适的策略,并让目标进程受到约束。

3. 与原有文件权限共同生效

AppArmor 不会替代原有的 Linux 文件权限,也不会让本来无权读取文件的用户,因为 profile 写了 r 就获得读取权限。

只看这两层检查,可以这样理解:

1
2
3
文件权限允许 + AppArmor 拒绝 → 仍然拒绝

文件权限拒绝 + AppArmor 允许 → 仍然拒绝

两边都允许,才算通过这两层访问检查。

所以:

AppArmor 是进一步收紧访问范围,不是绕过原有权限。

记忆图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
        demo-web 发起文件访问
│
▼
原有 Linux 权限检查
│
│ 通过
▼
AppArmor 策略检查
│
┌────────────┴────────────┐
▼ ▼
读取网页、读写日志 读取内部报表
│ │
策略允许 策略不允许
│ │
▼ ▼
继续访问 拒绝访问

上图只展示本文关注的两层权限检查。

可以简单记成:

1
2
3
4
5
用户权限:这个用户能访问什么?

程序策略:这个程序还被允许访问什么?

内核执行:不符合策略的访问,直接拒绝。

一句话概括:

在用户权限之外,再给程序划定访问范围:该读的可以读,不该碰的由内核拦住。


参考资料:

  1. Linux Kernel Documentation: AppArmor
  2. Ubuntu Server Documentation: AppArmor
  3. AppArmor Documentation: Profiles Basics
  4. AppArmor Documentation: Profiles Quick Reference
  5. AppArmor Documentation: Where Do LSMs Fit? A Linux Security Primer