Paranoid Penguin - SELinux 简介
SELinux 是美国国家安全局 (NSA) 针对 Linux 强制访问控制的强大实现,它看起来像是一项令人生畏的技术。它有很多活动部件,这些部件都标有(双关语)神秘的、首字母缩略词密集的术语,在 Linux 已经抽象的架构之上增加了一些非常密集的抽象层。更糟糕的是,SELinux 的许多文档似乎是由安全极客为安全极客编写的。
嗯,人们对 LDAP 也说了类似甚至更糟糕的话,但与 LDAP(我们在本专栏的 2003 年 7 月、8 月和 9 月的《LJ》杂志中介绍过)一样,如果您学习一些基本概念,熟悉一个适度大小的术语列表并研究一些有代表性的策略文件,您就可以让 SELinux 做您需要它做的事情。
在本月的专栏中,我们将讨论 SELinux 的基础知识。我们从 SELinux 的总体设计目标开始;介绍 SELinux 主体、权限和对象的概念,以及它们如何融入安全上下文;并将这些想法结合起来讨论类型强制。
相信我,这些内容足够您入门了!我们将把实际的 SELinux 配置留到后续的专栏中。但是,如果您迫切需要在启用 SELinux 的系统上让某些东西工作,请参阅本文的在线资源。
那么,我们究竟要用 SELinux 解决什么问题呢?不亚于整个安全补丁的无休止的竞争!
正如我之前在这个专栏中说过的,Linux 安全通常似乎归结为研究人员和攻击者发现 Linux 应用程序和内核中的新安全漏洞的循环;供应商和开发人员争先恐后地发布补丁,与此同时,攻击者对未打补丁的系统造成严重破坏;以及不幸的系统管理员最终应用本周或本月的补丁,然后很快又重复整个痛苦的过程。这就是安全补丁的无休止的竞争,它是无法取胜的。永远都会存在零日(尚未打补丁)漏洞。
这就是为什么多年来我花费了这么多笔墨来赞扬诸如虚拟化服务器、创建 chroot 监狱、以非特权用户身份运行进程以及使用强制访问控制等技术,所有这些技术都限制了零日漏洞的影响。SELinux 就像 Novell AppArmor 一样,是一种强制访问控制实现,它不能阻止零日攻击,但它专门设计用于遏制其影响。
为什么补丁的无休止的竞争是无法取胜的?因为在 Linux 的默认自主访问控制 (DAC) 模型中,每个进程都以启动(或有时拥有)它的用户的权限运行——也就是说,该用户所有的权限。如果攻击者攻陷了任何以 root 身份运行的进程,或将受损进程提升到 root 权限,则攻击者可以执行 root 可以执行的任何操作,即使该操作与进程的预期功能完全无关。
例如,假设我有一个名为 blinkend 的守护进程,它以用户 someguy 的身份运行,并且这个守护进程被攻击者劫持。blinkend 的唯一功能是在键盘 LED 上用莫尔斯电码闪烁笑话,所以您可能会认为,嗯,攻击者能做的最坏的事情就是闪烁一些侮辱,对吧?错了。攻击者可以做 someguy 帐户可以做的任何事情,其中可能包括从执行 Bash shell 到挂载 CD-ROM 的所有操作。
然而,在 SELinux 下,blinkend 进程将在一个狭义定义的活动域中运行,该域将允许它执行其工作(闪烁 LED,可能从特定的文本文件中读取笑话等等)。换句话说,blinkend 的权限将不会根据其用户/所有者确定;相反,它们将由更狭窄的标准确定。如果 blinkend 的域定义得足够严格,即使对 blinkend 进程的成功攻击,充其量也只会导致顽皮的莫尔斯电码闪烁。
简而言之,这就是 SELinux 旨在解决的问题。
我假设您了解自主访问控制(又名普通的旧文件系统权限)在 Linux 中是如何工作的。如果您不了解,我在 2004 年 10 月和 11 月的《Linux Journal》杂志的两部分系列文章“Linux 文件系统基础知识”(请参阅资源)中介绍了这个主题。
可以说,即使在 SELinux 下,Linux DAC 仍然适用。如果给定文件上的普通 Linux 权限阻止了特定操作(例如,用户 A 尝试写入文件 B),则该操作仍将被阻止,并且 SELinux 不会费心评估该操作。但是,如果普通 Linux 权限允许该操作,SELinux 将在允许其发生之前根据其自身的安全策略评估该操作。
那么,SELinux 是如何做到这一点的呢?SELinux 的起点似乎与 DAC 范例相似:它评估主体尝试对对象执行的操作。
在 SELinux 中,主体始终是进程。这可能看起来违反直觉。主体有时不是最终用户吗?不完全是——用户执行命令(进程)。SELinux 自然非常关注谁或什么执行给定的进程,但进程本身,而不是执行它的个人,被认为是主体。
在 SELinux 中,我们将操作称为权限,就像我们在 Linux DAC 中所做的那样。然而,被操作的对象是不同的。在 Linux DAC 模型中,对象始终是文件或目录,而在 SELinux 中,对象不仅包括文件和目录,还包括其他进程以及内核空间和用户空间中的各种系统资源。
SELinux 区分各种各样的对象类(类别)——实际上有几十个。您可以在网站“对象类和权限概述”(请参阅资源)上阅读完整列表。毫不奇怪,文件是最常用的对象类。其他重要的对象类包括以下内容
dir
socket
tcp_socket
unix_stream_socket
filesystem
node
xserver
cursor
每个对象类都有一组特定的可能权限(操作)。这是有道理的。例如,您可以对目录执行某些操作,这些操作根本不适用于 X 服务器。每个对象类都可能具有其他类共有的继承权限(例如,读取),以及仅适用于它的唯一权限。以下只是与 dir 类关联的一些唯一权限
search
rmdir
getattr
remove_name
reparent
不要因为我没有解释这些类名或操作而感到沮丧;在这一点上,您不需要为了它们本身而理解它们。我只是在说明 SELinux 比 Linux DAC 的简单用户、组、文件、目录和读/写/执行权限模型走得更远。
您可能会猜到,如果您必须为每个可能的主体对每个可能的对象执行的每个可能的操作创建单独的规则,SELinux 将无法使用。SELinux 通过两种方式绕过了这一点:1) 采取“凡是未明确许可的,都将被拒绝”的立场,以及 2) 以各种方式对主体、权限和对象进行分组。这两点都有积极和消极的影响。
“默认拒绝”立场允许您只创建描述您期望和想要的行为的规则/策略,而不是所有可能的行为。这也是任何访问控制技术可以拥有的最安全的设计原则。但是,它也要求您预测系统上每个守护进程和命令的所有可能的允许行为(以及它们之间的交互)。
这就是为什么 Red Hat Enterprise Linux 4 和 Fedora Core 3 中的“目标”SELinux 策略实际上实现了相当于“仅限制这些特定服务”的策略,让策略中未明确涵盖的所有进程自由运行。不,这不是使用 SELinux 最安全的方式,甚至不是 SELinux 最初设计的使用方式。但正如我们将看到的,这对于通用系统来说是一个合理的折衷方案。
SELinux 的各种分组(角色、类型/域、上下文等等)的优点显然是,与始终需要指定单个主体、权限和对象相比,效率更高。缺点是更多的术语和抽象层。唉,力量伴随着复杂性。
那么,SELinux 如何对主体、权限和对象进行分组呢?
SELinux 控制的每个单独的主体和对象都受安全上下文的管辖,每个安全上下文都由用户、角色和域(也称为类型)组成。
用户是您期望的:个人用户,无论是人类还是守护进程。但是,SELinux 维护着自己的用户列表,与 Linux DAC 系统分开。在主体的安全上下文中,用户标签指示主体(再次强调,必须是进程)正在运行哪个 SELinux 用户帐户的权限。在对象的安全上下文中,用户标签指示哪个 SELinux 用户帐户拥有该对象。
角色有点像 Linux DAC 系统中的组,因为角色可以由许多预授权用户承担,每个用户都可以在不同的时间被授权承担不同的角色。不同之处在于,在 SELinux 中,用户一次只能承担一个角色,并且只有在被授权的情况下才能切换角色。安全上下文中指定的角色指示指定用户在该特定上下文中正在操作的角色。
最后,域有点像沙箱:主体和对象的组合,它们可以相互交互。域也称为类型,尽管域和类型在 Flask 安全模型(NSA 在此基础上构建了 SELinux)中是两个不同的事物,但在 SELinux 中,域和类型是同义词。
这种模型,其中每个进程(主体)都被分配到一个域,在该域中只允许某些操作,称为类型强制 (TE),它是 SELinux 的核心。类型强制也构成了 Fedora 和 Red Hat Enterprise Linux 中 SELinux 实现的大部分。
还有更多内容,但在我进一步深入之前,我想使用一个示例场景来说明安全上下文。
假设我们正在使用 SELinux 保护我的 LED 闪烁守护进程 blinkend。正如您所回忆的那样,它以帐户 someguy 的权限运行,并且它从一个文本文件(我们将其称为 /home/someguy/messages.txt)中读取它闪烁的消息。
在 SELinux 下,我们将需要一个名为 someguy 的 SELinux 用户(请记住,这是对底层 Linux DAC 的 someguy 帐户的补充——即 /etc/passwd 中的帐户)。我们还需要 someguy 在此上下文中承担的角色;我们可以将其称为 blink_r(按照惯例,SELinux 角色名称以 _r 结尾)。
blinkend 安全上下文的核心将是其域,我们将其称为 blinkend_t(按照惯例,SELinux 域名以 _t 结尾——t 是类型的缩写)。blinkend_t 将指定规则,允许 blinkend 进程读取文件 /home/someguy/messages.txt,然后将数据写入,例如 /dev/numlockled。
文件 /home/someguy/messages.txt 和特殊文件 /dev/numlockled 将需要它们自己的安全上下文。这两个上下文可能都可以使用 blinkend_t 域,但由于它们描述的是对象而不是主体,因此它们将指定通用的角色 object_r。对象,顾名思义,本质上是被动的(对它们执行操作,而不是反过来),通常不承担有意义的角色,但每个安全上下文都必须包含一个角色。
SELinux 必须就主体、域和对象做出两种类型的决策:访问决策和转换决策。访问决策涉及主体对已经存在的对象执行操作,或创建保留在预期域中的新对象。访问决策很容易理解。在我们的示例中,“blinkend 可以读取 /home/someguy/messages.txt 吗?”就是一个这样的决策。
然而,转换决策有点微妙。它们涉及在与主体进程运行的域不同的域中调用进程,或在与父目录类型不同的类型中创建对象。(注意:即使域和类型在 SELinux 中是同义词,但按照惯例,我们在谈论进程时通常使用域,而在讨论文件时使用类型。)
也就是说,通常,如果一个进程执行另一个进程,则第二个进程默认情况下将在相同的 SELinux 域中运行。例如,如果 blinkend 衍生一个子进程,则子进程将在 blinkend_t 域中运行,与其父进程相同。但是,如果 blinkend 尝试将进程衍生到其他域中,SELinux 将需要做出域转换决策,以确定是否允许这样做。像其他所有事情一样,转换必须在 SELinux 策略中明确授权。这是防止权限提升攻击的重要检查。
文件转换的工作方式类似。如果主体在某个目录中创建一个文件(并且如果主体的域中允许创建此文件),则新文件通常将继承父目录的安全上下文(用户、角色和域)。例如,如果 blinkend 的安全上下文允许它在 /home/someguy/ 中写入一个新文件,例如 /home/someguy/error.log,则 error.log 将继承 /home/someguy/ 的安全上下文(用户、角色和类型)。如果由于某种原因,blinkend 尝试使用不同的安全上下文标记 error.log,SELinux 将需要做出类型转换决策。
明白了吗?转换决策是必要的,因为同一个文件或资源可能在多个域/类型中使用;进程和文件转换是系统操作的正常部分。但是,如果域可以任意更改,攻击者将更容易进行恶意活动。
除了类型强制之外,SELinux 还包括第二个模型,称为基于角色的访问控制 (RBAC)。虽然我现在没有空间了,但 RBAC 构建在我们已经讨论过的概念之上,提供了在涉及真实人类用户(而不是守护进程和其他自动化进程)时特别有用的控制。
下次,我将详细描述 RBAC,并开始深入探讨如何实际使用 SELinux,从 Fedora 和 Red Hat 的“目标”策略开始。在那之前,请注意安全!
本文资源: /article/9510。
Mick Bauer (darth.elmo@wiremonkeys.org) 是美国最大银行之一的网络安全架构师。他是 O'Reilly 图书 Linux 服务器安全 第二版(以前称为 使用 Linux 构建安全服务器)的作者,信息安全会议的偶尔演讲者,以及“网络工程波尔卡舞曲”的作曲家。