Linux内核LSM机制详解
Linux内核LSM机制介绍
Linux 内核的 LSM (Linux Security Module) 机制是一个安全框架,它允许不同的强制访问控制 (MAC) 安全模型以可加载内核模块的形式实现,并将其钩子(hooks)集成到内核的各种关键操作中。
核心目标和解决的问题
- 提供灵活性: 在 LSM 之前,想要在内核中实现不同的安全策略(如 SELinux、AppArmor 等)需要大规模修改内核源码,难以维护且相互冲突。LSM 提供了一套标准的接口,使得不同的安全模块可以独立开发和加载,无需修改内核核心代码。
- 实现强制访问控制: 补充传统的 UNIX **自主访问控制 (DAC)**。DAC(如用户/组/文件权限)由资源所有者决定谁可以访问。MAC 则由系统管理员定义的安全策略强制决定,无论资源所有者意愿如何,提供更细粒度和更严格的安全控制。
- 标准化钩子点: 在内核执行对安全敏感的操作之前(如打开文件、创建进程、网络操作、访问 IPC 对象等),LSM 框架定义了标准的“钩子点”。安全模块可以在这些点上注册回调函数,由内核在操作执行前调用,让模块决定是否允许该操作。
LSM 的工作原理
钩子点: 内核开发者在关键的内核函数路径上放置了 LSM 钩子调用。例如:
file_open
: 在打开文件之前。inode_permission
: 在检查文件权限之后、DAC 权限之后。task_alloc
/cred_prepare
: 在创建新进程或修改进程凭证之前。socket_bind
/socket_connect
: 在绑定或连接 socket 之前。sb_mount
: 在挂载文件系统之前。bprm_check_security
: 在执行程序之前。ptrace_access_check
: 在进行 ptrace 操作之前。- 还有很多其他钩子点覆盖了内核的各个子系统(IPC, 模块加载, 任务调度, 网络等)。
安全模块注册: 一个安全模块(如
selinux
,apparmor
,tomoyo
)在初始化时,向 LSM 框架注册自己,并提供其实现的钩子函数集合(一个包含函数指针的结构体)。钩子调用: 当内核执行到一个设置了 LSM 钩子的操作时:
- 内核会先执行传统的 DAC 检查。如果 DAC 拒绝,操作直接失败。
- 如果 DAC 允许,内核会遍历所有已注册并启用的安全模块(按照编译或启动时确定的顺序),调用该操作对应的钩子函数。
- 每个被调用的钩子函数根据其模块内部的安全策略(策略规则)检查当前操作(主体 - 通常是进程/用户,客体 - 文件/socket/IPC 对象,操作 - 读/写/执行等)是否被允许。
- 如果某个钩子函数返回一个错误(非零值),表示该安全模块拒绝此次操作,内核会中止操作并返回权限错误(如
-EACCES
)。 - 如果所有被调用的钩子函数都返回 0(允许),内核则继续执行该操作。
安全域: 安全模块通常会给主体(进程)和客体(文件、socket 等)打上“安全标签”或“安全上下文”(如 SELinux 的
user:role:type:level
或 AppArmor 的路径规则)。钩子函数就是基于这些标签和操作类型来查询策略数据库做出决策。
主要特点
- 模块化: 核心特性,不同的安全模型作为独立模块存在。
- 补充 DAC: LSM 钩子在 DAC 检查之后调用。如果 DAC 拒绝,LSM 不会被调用。LSM 提供额外的、更严格的检查。
- 堆叠: 较新的内核版本(大约 4.2 之后)支持有限度的 LSM 堆叠,允许多个安全模块同时生效。内核按预定义的顺序依次调用每个模块的钩子。如果任何一个模块拒绝,操作即被拒绝。常见的堆叠模式是将一个主要的 MAC 模块(如 SELinux 或 AppArmor)与一些更小的、功能特定的模块(如
Yama
- 限制ptrace
,LoadPin
- 限制模块/固件加载路径,Lockdown
- 内核锁定)一起使用。但像 SELinux 和 AppArmor 这样的全功能 MAC 模块通常不能同时启用。 - 性能: 添加钩子调用必然带来开销。安全模块的策略查找复杂度是主要性能因素。现代内核通过优化(如通过
static_call
等技术减少间接调用开销)和模块自身优化(如 SELinux 的 AVC 缓存)来减轻影响。未启用安全模块时,LSM 框架自身的开销非常小。 - 最小权限原则: LSM 实现的 MAC 模型(如 SELinux, AppArmor)的核心目标是强制执行最小权限原则,即使进程以 root 身份运行,也只能执行策略明确允许的操作。
常见的内置 LSM 模块
- SELinux: 由 NSA 开发,使用基于类型强制(TE)、角色访问控制(RBAC)和多级安全(MLS/MCS)的复杂标签系统。策略灵活强大,但配置复杂。广泛用于企业级 Linux 发行版(RHEL, Fedora)和 Android。
- AppArmor: 由 Novell/SUSE 开发,使用基于路径名的配置文件和规则。配置相对直观(通常通过路径和程序名)。广泛用于桌面和服务器 Linux 发行版(Ubuntu, openSUSE)。
- Smack: 简单强制访问控制内核。设计目标是在嵌入式场景中保持简单性。标签相对简单(直接附加到文件 inode 和任务结构上)。
- TOMOYO: 起源于日本,采用基于路径名的策略,特点是其“学习模式”可以生成初始策略配置。
- Yama: 专注于限制
ptrace
系统调用,防止非特权用户调试敏感进程(增强沙盒逃逸防护)。常与其他模块堆叠使用。 - LoadPin: 强制要求所有内核模块和固件必须从同一个文件系统加载(通常是只读的),防止加载被篡改的模块。
- Lockdown: 随着 UEFI Secure Boot 发展而来,在内核运行时限制对某些敏感内核功能的访问(如修改运行中内核、访问原始内存、加载未签名模块等),保护内核完整性。在启用 Secure Boot 的系统中通常处于
integrity
或confidentiality
模式。 - BPF LSM: 允许使用 eBPF 程序来实现安全策略逻辑,提供了极大的灵活性来动态定义安全行为。是一个强大的新兴方向。
配置和使用
- 通常在内核编译时配置哪些 LSM 模块被编译(
CONFIG_SECURITY_XXX=y/m
)。 - 系统启动时通过内核引导参数
security=
指定启用的主要模块(如security=selinux
,security=apparmor
)。对于堆叠,顺序也由该参数或内核默认顺序决定。 - 各个模块有自己的用户态工具来管理策略(
semodule
/semanage
/setools
for SELinux,aa-genprof
/aa-enforce
/apparmor_parser
for AppArmor)。 - 可以通过
/sys/kernel/security/lsm
查看当前启用的 LSM 模块及其顺序。 - 可以通过
/proc/self/attr/current
(SELinux) 或/proc/self/attr/apparmor/current
(AppArmor) 查看进程的安全上下文。
总结
LSM 是 Linux 内核安全架构的基石。它通过提供标准化的钩子框架,使得强大的强制访问控制安全模型(如 SELinux 和 AppArmor)以及其他安全增强功能能够以模块化、可扩展的方式集成到内核中,极大地提升了 Linux 系统应对安全威胁的能力,特别是在实现最小权限原则和系统加固方面。理解 LSM 对于进行 Linux 系统安全管理和开发安全应用程序至关重要。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Helloeuler!