- LSM是什么?
- 主、次、独占LSM模块
- SELINUX – 安全增强的Linux
- SMACK – 简化的强制访问控制
- APPARMOR
- TOMOYO
- LOADPIN
- YAMA
- SAFESETID
- LOCKDOWN LSM
- 结论
我猜,你读这篇文章,说明你已经对Linux安全模块(LSM
)有所了解。如果你使用过SELinux
或AppArmor
,其实就已经用过LSM
了。甚至,在你使用的Linux
发行版本或Android
系统之上,也使用了LSM
。
内核5.4
版本内,有8个LSM
模块:SELinux
、SMACK
、AppArmor
、TOMOYO
、Yama
、LoadPin
、SafeSetID
、Lockdown
。还有一些LSM
模块在开发中,比如SARA
和 KRSI
,也许不久就会合入Linux内核源码中。如果你是关注安全的系统或软件工程师,理解为什么有这么多的LSM
模块是非常值得的。它们有一些是解决通用问题,有一些则是解决特定问题。意识到它们的差异,才能更好地理解Linux的安全特性。
LSM
是什么?
一个LSM
模块是直接编译Linux
内核的代码,利用LSM
框架,它可以拒绝某个进程访问重要的内核对象。这些对象包括:文件、inode
、任务控制块、凭证和进程间通信对象。通过指定允许的交互,安全管理员可以让攻击者很难利用程序的缺陷从而攻击系统的其它部分。
LSM
框架的第一个,也是最流行的使用场景是强制访问控制(MAC
)策略。毫无疑问,在内核中实现MAC
策略的方法有多种。2001年,美国国家安全局的Peter Loscocco
在Linux 2.5
内核峰会上展示了首个实现。Linux Weekly News
的Jonathan Corbet
后来指出:
经过这么多年的发展,这些标准接口已经形成了LSM
框架。截止到5.4
内核,该框架已经包含224
个hook
点,这些hook
点包含一个注册函数的API和为LSM
模块保留受保护内核对象所使用的内存的API。到Linux 2.6
版本,LSM
框架和SELinux
合并到了内核主线中(使用LSM
框架,而不是直接在内核代码中修改)。尽管LSM
框架的实现,随着时间的不断推移而发生变化,但是它实现对重要内核对象的细粒度访问控制的基本目标没有改变。
目前的LSM
框架已经允许用户将多个LSM
模块编译进内核,存储在内核的堆栈空间中,并同时使用它们。下图展示了一个文件打开(open())操作的简略调用流程图(假设为3个LSM
模块注册了hook
钩子函数。
- 用户态进程调用
open()
,打开一个文件; - 调度系统调用,使用文件路径作为获取内核文件对象的参数。如果参数非法,返回错误。
- 正常的自由访问控制(
Discretionary Access Control
)文件访问权限检查。如果没有权限,系统调用终止,返回给用户错误。 - 如果满足
DAC
控制,则LSM
框架为每个使能的LSM
模块调用file_opne
钩子函数。任何一个LSM
钩子函数返回错误,则系统调用终止,并返回给用户错误。 - 如果所有的安全检查通过,则为该进程打开该文件,并返回给用户态进程一个新的文件描述符
fd
。
主、次、独占LSM
模块
对LSM
有了初认识之后,我们再来看各个LSM
模块能做什么。首先,我们先看看早期的主LSM
模块:SELinux
、SMACK
、AppArmor
和TOMOYO
,它们都是MAC
访问控制策略的实现,从用户空间加载配置策略。他们都以自己的方式解决相同的问题。
早期的LSM
框架一次只能允许加载一个LSM
模块。所有的主LSM
模块都假设自己独占内核保护对象的指针或标识符。因此,所以一次只能使用一个主LSM
模块。可以在编译内核时选择编译进镜像,如下图所示;也可以通过内核命令行参数传递。
LSM
框架不断优化,已经消除了主、次LSM
模块之间的区别。现在区分主、次LSM
模块的优选方法是使用LSM_FLAG_EXCLUSIVE
独占标志。一个用户可以配置多个LSM
,只要给其中的一个设置LSM_FLAG_EXCLUSIVE
标志即可。
次LSM
是将大部分策略直接编码到内核代码中。通常情况下,次LSM
模块只有enable/disable
选项,而不是将策略文件在系统启动时从用户空间加载。
SELINUX – 安全增强的Linux
SELinux
最早是在Linux2.6
版本合入内核的,RedHat
发布的Linux
发行版将其作为默认的MAC
强制访问策略。它以功能强大和复杂著称。
SELinux
基于属性实现,将文件的安全属性存储在文件系统的扩展文件属性中。比如,使用ls -Z /bin/bash
文件的安全属性,如下所示。我们可以看到有四个:
冒号分割的属性,分别代表user:role:type:level
。
$ ls -Z /bin/bash -rwxr-xr-x. root root system_u:object_r:shell_exec_t:s0 /bin/bash
SELinux
的常用方法是指定主体(在此也就是指user
)对某种类型的对象采取什么动作。再看上面的ls
的输出,自由访问控制(DAC
)权限表示所有的用户都允许读、执行bash
,但使用 SELinux
,安全管理员可以进一步指定允许执行或读取策略文件中的shell_exec_t
类型文件的主体。比如说,安全工程师不许web
服务器执行shell
,因为web
服务器易受远程攻击。
SELinux
使用扩展属性实现的副作用是,对于那些不支持扩展属性的文件系统中对象无法保护,比如挂载NFSv4
版本的NFS
文件系统。
由于SELinux
的复杂性和强大功能,可以参考Red Hat’s SELinux User’s and Administrator’s Guide获取更多信息。
SMACK – 简化的强制访问控制
与SELinux
一样,SMACK
也是基于文件扩展属性的MAC
实现,是开发者合并到Linux
内核中的第二个LSM
模块(2.6.24
)。但是与SELinux
不一样的是,SMACK
是专为嵌入式系统设计的,对于系统管理员来说更简单。SMACK
是车级Linux
(AGL
)和Tizen
操作系统的默认MAC
实现。
APPARMOR
AppArmor
是另一种MAC
实现,最初由Immunix
开发,2.6.36
版本合入内核。AppArmor
是基于 Debian
的系统的主要MAC
实现。
除了减少了工具数量和复杂性之外,AppArmor
和SELinux
最大的不同就是,它是基于Path
而不是基于属性。
基于Path
的实现有利有弊。积极的一面是,基于Path
的策略可以保护任何文件系统的文件,因为存储安全信息不需要扩展属性。甚至可以为不存在的文件指定安全规则,因为这种方式下,可以将Path
存储在配置文件中而无需标注任何实际的文件或目录。另一方面,最常被提及的负面影响是,因为能够创建硬链接,对于同一个物理文件可能存在多个Path
。那么,单个文件的安全策略可能会因为不同的Path
而不同,这可能会导致安全漏洞。
TOMOYO
与AppArmor
一样,TOMOYO
是另一个基于Path
的MAC
实现,Linux 2.6.30
版本首次合入。TOMOYO
是专为嵌入式系统设计的,允许安全管理员在测试时记录所有的用户进程交互,从而根据开发、测试期间互相看见的进程才能够交互。如果使用了TOMOYO
策略的系统,落入不可信的用户或敌对环境中,用户态进程仅执行那些之前允许的交互,简化了策略生成。
LOADPIN
LoadPin
,是一个次LSM
模块,Linux4.7
版本合入,用以保证加载内核的所有文件(内核模块、固件等)来自相同的文件系统,并期望这样的文件系统是由只读的设备提供。这旨在简化从只读设备启动的嵌入式系统,让其无需对内核模块进行签名或检查。
因为简单易用,LoadPin
能够简化某些类型的嵌入式系统的内核免受恶意代码攻击的过程。
YAMA
Yama
,Linux 3.4
合入内核的LSM
模块,旨在收集主内核没有处理的系统内的DAC
安全限制。目前,它支持缩小ptrace()
系统调用的范围,阻止通过已经攻击成功的用户进程作为跳板,从相同用户的其它进程中抽取敏感数据信息。
SAFESETID
SafeSetID
是在Linux 5.1
版本合入的一个LSM
模块,用来限制将UID/GID
转换成白名单中允许的那些UID/GID
。
我认为Linux
内核中对SafeSetID
使用场景的描述是非常准确的:
This can be used to allow a non-root program to transition to other untrusted uids without full blown
CAP_SETUID
capabilities. The non-root program would still needCAP_SETUID
to do any kind of transition, but the additional restrictions imposed by this LSM would mean it is a “safer” version ofCAP_SETUID
since the non-root program cannot take advantage ofCAP_SETUID
to do any unapproved actions (e.g. setuid to uid 0 or create/enter new user namespace). The higher level goal is to allow for uid-based sandboxing of system services without having to give outCAP_SETUID
all over the place just so that non-root programs can drop to even-lesser-privileged uids. This is especially relevant when one non-root daemon on the system should be allowed to spawn other processes as different uids, but its undesirable to give the daemon a basically-root-equivalent CAP_SETUID.通俗的说就是,如果非特权程序想要生成具有不同
uid
的进程时,无需赋予其CAP_SETUID
能力,而是通过SafeSetID
就可以修改uid
。— Documentation/admin-guide/LSM/SafeSetID.rst
LOCKDOWN LSM
lockdown是Linux 5.4
版本合入内核的,实现对kernel
的锁定
。启用了lockdown
功能后,就可以通过内核命令行参数锁定kernel
,以便保护其完整性和机密性。当设置lockdown
为保护完整性,它的特性就不允许用户空间修改kernel
。这些特性包括:未签名的内核模块加载,访问/dev/{mem,kmem,port}
,访问/dev/efi_test
,未签名镜像的执行,休眠,直接PCI
访问,原始IO
端口访问,原始MSR
访问,修改ACPI
表,直接PCMCIA CIS
存储,串行端口的重新配置,不安全的内核模块参数,不安全的MMIO
内存,以及debugfs
访问。当设置lockdown
为保护机密性时,所有的完整性保护都被启用,另外还要禁止的功能有:用户空间从正在运行的内核中提取潜在的机密信息,例如/proc/kcore
访问,使用kprobe
和bpf
读取内核RAM
,perf
的不安全使用以及tracefs
的使用。
lockdown
可以通过SELinux
、AppArmor
、SMACK
、或TOMOYO
策略文件实现,这种基于静态策略的独立LSM
策略文件的实现方式,意味着它可以跨平台运行,而无关于具体的MAC
实现机制。
结论
LSM
并不是专门设计用来阻止对进程攻击的工具。良好的编程实践,配置管理和内存安全的编程语言才是实现阻止攻击的工具。但是,当系统中运行的程序存在漏洞时,LSM
确实能够阻止利用漏洞攻击系统的其它组件。所以说,LSM
是Linux
系统纵深防御的重要一层,通过理解它们能够提供什么保护,可以增加对保护系统的哪些部分以及如何实现这些保护有一个更好的理解。