LSM 与访问控制

本页目录

Unix 权限允许,服务仍可能被策略拒绝;拥有一个 capability,也不能推断所有相关操作都会通过。访问控制需要把主体、对象、操作及策略组合一起解释。

DAC 包括 owner/group/other 与 ACL,通常允许对象所有者参与安排;强制策略可进一步约束服务域。

查阅目标阅读位置
LSM 与标签/profile第 1~3 节
capability 与 BPF 策略第 4~5 节
keyring、完整性与运行状态第 6~7 节

1. LSM 提供检查位置,模块提供策略

一次文件访问要满足多个独立条件 调用与路径检查 对象与操作是否有效 基础权限检查 DAC / ACL / capability 已启用 LSM 的检查 标签、profile 等策略 执行文件操作 仍可能发生 I/O 等错误 图示表达约束关系;实际内核检查分布在多个路径中 某一层允许,不会自动绕过其他层;LSM 也不只在 syscall 入口运行。

LSM hook 位于文件、凭据、进程、网络等内核对象的操作路径中;一次系统调用可能触发多个检查,内核内部操作也可能经过这些位置。hook 既包括返回决策的函数,也包括分配、释放或更新安全状态的通知,不能统一说“所有 hook 返回错误就拒绝”。

例如 security_file_open() 使用具有返回值的 file_open hook;将它写成 call_void_hook() 再声称检查错误是不成立的。读取目标版本的 security/security.c 与 include/linux/lsm_hook_defs.h,比复制旧版 hook 链表结构更可靠。

对常见的拒绝型权限 hook,已启用模块按定义的组合规则参与检查;一项拒绝不能靠另一模块的允许覆盖。不能画成 SELinux 和 AppArmor 在所有系统上固定并行运行:实际启用组合与顺序取决于内核版本、配置和启动参数。securityfs 可见时,/sys/kernel/security/lsm 列出当前活动模块。

LSM 的名字也不意味着它们是可以随意 insmod/rmmod 的普通模块。框架概念见 LSM 文档,其中历史结构体说明应结合当前源码阅读。

2. SELinux:主体域与对象类型

SELinux 安全上下文通常包含 user、role、type 和级别/范围。例如文件标签中的 httpd_sys_content_t 是对象类型;服务进程则运行在某个域中。

读一条 type enforcement 拒绝记录时,对齐四项:

字段要确认的问题
source context哪个主体域发起访问?
target context对象实际是什么类型?
object class文件、目录还是其他对象?
permission被拒绝的是 read、search 或哪项操作?

只看路径容易漏掉标签、域转换或目录 search 权限。规则表达的是源域对目标类型某类对象的操作许可。

AVC 缓存访问决策以降低查询成本。标签来源包括文件系统扩展属性和策略为特定对象生成的标签;procfs、tmpfs 等的标记方式不能统一说成都由 genfscon 生成。

Enforcing 执行策略拒绝;permissive 通常记录本应拒绝的访问而不施加该 SELinux 拒绝。其他 DAC、LSM 和系统错误仍可使操作失败,所以 permissive 不等于所有操作都会成功。审计还受策略与日志配置影响,不能假设每次判断都有一条新日志。

3. AppArmor 与 Smack:不同的策略表达

AppArmor 以程序 profile 为核心,文件访问规则常按路径表达,并可限制 capability、网络等操作。相同 Unix 用户下的不同程序因此可以受到不同约束。

路径策略需要理解进程的挂载视图、别名、重命名和链接规则,但不能直接下结论“创建 hardlink 或 bind mount 就能绕过 AppArmor”。链接和挂载本身也受检查,profile、策略 namespace 及具体规则决定结果。下面仅是规则语法片段,不是可直接部署的完整服务 profile:

/var/www/** r,
/var/log/example/*.log w,
capability net_bind_service,

它缺少程序加载、动态库、目录访问和完整运行依赖。理论上理解一条规则,与拥有可用且足够收敛的策略,是两项不同工作。见 AppArmor 文档。

Smack 也使用主体/对象标签和访问规则,但有自己的规则及特殊标签语义。它不是“SELinux 删除固定百分比功能”的版本;选择机制应比较实际表达能力与部署环境。见 Smack 文档。

4. Capabilities:拆分特权,并不生成完整沙箱

内核 capability 语义以线程凭据为基础,包括 permitted、effective、inheritable,以及 bounding 和 ambient 集合。常规权限检查关心 effective 集中的相关能力;exec 时如何重新计算还取决于文件 capability、set-ID、securebits 等条件。

  • CAP_NET_RAW 涉及 raw/packet socket 等权限。
  • CAP_SYS_PTRACE 可以参与绕过部分 ptrace 凭据限制,但仍有其他检查。
  • CAP_NET_BIND_SERVICE 涉及受保护低端口;网络 namespace 的端口阈值设置也会影响是否需要它。
  • CAP_SYS_ADMIN 覆盖范围很广,不宜把名字理解成一项狭窄操作。

user namespace 内的 capability 只在适用的 namespace 和对象关系中有效,容器内 root 不自动拥有宿主 root 的全部能力。反过来,移除 capability 也不消除普通用户本来就能使用的所有接口。详见 capabilities(7)。

将 DAC、capability 的作用域、LSM 与只读文件系统条件分别打开。下面呈现的是这些约束的组合,不把它们伪装成所有系统都固定遵循的一条函数调用顺序。

正在呈现知识画面
一项允许,能否覆盖另一项拒绝?

适用的 capability 可以满足特定 DAC 绕过条件,但不能把 LSM 拒绝或只读文件系统改成允许。user namespace 内的 root 也不是宿主所有对象的通行证。

5. BPF LSM:受验证的程序仍可能写错策略

BPF LSM 允许通过受权限控制的 BPF 程序扩展支持的 hook。libbpf 可处理加载和链接,CO-RE/BTF 帮助适配内核类型。

对于系统范围的 MAC hook,多个 BPF 程序串接时,程序应按该接口保留此前的拒绝结果,不能把传入的非零 ret 无条件改为 0。不同 attach 类型有不同返回语义,不能把一个例子的返回值规则直接套到所有 BPF LSM 程序。

verifier 检查可接受的内存访问、控制流等条件,降低程序破坏内核的风险;它不证明业务授权逻辑正确,也不承诺内核/verifier 永无漏洞。部署错误策略仍可能阻断正常工作。见 BPF LSM 文档。

6. Keyring、IMA 与 EVM

内核 keyring 管理带类型、权限和寿命的 key 对象,并支持将 key 链接到 keyring。它不是一个让所有用户都能读取所有密钥的全局字典,也不保证每种 key payload 都由硬件保护;trusted、encrypted 等 key 类型有各自的封装与根信任方式。

测量记录与使用时拒绝是不同目标

IMA measurement 根据策略测量文件等对象,保留测量日志,并可扩展 TPM PCR。它不是每读取一个字节就重新计算整个文件,也不是把每份文件 hash 都单独存进 TPM。远程验证需要同时理解日志、PCR、可信证明与预期状态。

IMA appraisal 则依据策略验证文件完整性/真实性并决定是否拒绝使用。EVM 保护选定安全扩展属性及相关元数据的完整性,可采用 HMAC 或签名。测量、拒绝执行与磁盘内容加密是三个不同目标;只有测量日志不自动阻止篡改后的程序运行。

7. 只读观察当前环境

from pathlib import Path

for name in ('/sys/kernel/security/lsm', '/sys/fs/selinux/enforce',
             '/proc/self/attr/current'):
    try:
        print(name, Path(name).read_text().strip())
    except OSError as exc:
        print(name, type(exc).__name__)
for line in Path('/proc/self/status').read_text().splitlines():
    if line.startswith(('Cap', 'NoNewPrivs:', 'Seccomp:')):
        print(line)

文件不可见可能来自未挂载 securityfs、配置、权限或容器视图,不能单凭读取失败断言系统没有任何访问控制。实际授权问题应结合服务进程的凭据、命名空间、标签/profile 与拒绝日志,而不是只检查当前 shell。

参考:内核 key 管理、完整性机制源码。