FreeBSDjail()chroot() 系统调用有很多问题,如前所述。例如,它难以正确使用,root 用户还是可以从中脱离,而且它根本不去控制网络访问。FreeBSD 开发者决定增加一个新的系统调用来解决这些问题,这个新的系统调用叫做 jail() 。这个调用类似于 chroot() ,不过尽力更易用且更用效。在一个 jail 中,所有的请求(即使是 root 的)都被 jail 所限,进程只能与 jail 中的其他进程通信,而且系统封锁了root 用户试图从 jail 中脱离的典型途径。jail 会被分配一个特定的 IP 地址,不能使用任何其他地址作为它自己的地址。
jail() 调用是 FreeBSD 所独有的,这就限制了它的效用。不过,各个 OSS/FS 内核之间有很多交叉影响(cross-pollination)。例如,已经使用 Linux 安全框架为 Linux 开发了一个 jail 版本。而且,FreeBSD 5 已经添加了一个灵活的 MAC 框架(来自 TrustedBSD 项目),包括一个具有类似 SELinux 基本功能的模块。所有,将来看到更多这种情况不要感到奇怪。
Linux 安全模块(LSM)在 2001 年的 Linux Kernel Summit 上,Linus Torvalds 遇到了一个问题。一些不同的安全项目,包括 Security-EnhancedLinux(SELinux)项目,要求他将他们的安全方法添加到 Linux 内核中。问题是,这些不同的方法常常是不兼容的。Torvalds 没有简单的方法可以判定哪个是最好的,所以他要求那些项目为 Linux 合作创建某种通用的安全框架。那样,管理员就可以给他们特别的系统安装任意他们想要的安全方法。与 Torvalds 讨论了几次以后,Crispin Cowan 建立了一个小组来创建这个通用的安全框架。这个框架被命名为 Linux 安全模块(LSM)框架,现在是标准 Linux 内核的一部分(如 2.6 版本内核)。
概念上讲,LSM 框架特别简单。Linux 内核仍去做它常规的安全检测;例如,如果您要写一个文件,您仍需要对其有写权限。不过,不论何时如果 Linux 内核需要判定是否应该准许访问,它还要进行核对——通过一个“book”去要求一个安全模块来进行——来确定动作是否得到准许。这样,管理员可以简单地选出他想要使用的安全模块,并像其他 Linux 内核模块一样将其插入。从那时以后,那个安全模块将判定什么是允许的。
LSM 框架设计得如此灵活,它可以实现很多不同种类的安全策略。实际上,一些不同的项目进行合作以确保 LSM 框架足以胜任真正的工作。例如,当内部对象被创建或被删除时 LSM 引入一些调用——不是因为那些操作可能会中止,而是让安全模块可以保持对重要数据的追踪。使用了一些不同的分析工具来确保 LSM 框架不会遗漏其目标的任何重要异常分支。结果证明,这个项目比很多人想像的要困难,它的成功是来之不易的。
有必要理解 LSM 所做的基本的设计决定。基本上,LSM 框架故意设计为几乎所有异常分支都是受限的,而不是可信的(authoritative)。一个 可信的 异常分支做出绝对最终的决定:如果异常分支认为一个请求应该被准许,那么它就会被无条件准许。相反, 可信的 异常分支只能增加另外的限制;它不能授与新权限。理论上,如果所有 LSM 异常分支都是可信的,LSM 框架将会更加灵活。有一个名为 capable() 的异常分支是可信任的——但是只是因为它不得不支持常规的 POSIX 能力。不过,要让 所有 异常分支都可信,就要对 Linux 内核进行很多根本的改变,还说不准这种改变是不是会被接受。
有很多人担心,如果大部分异常分支都可信,即使是最小的缺陷也将成为灾难;而让异常分支受限意味着用户将不会感到意外(不管怎样,原来的 UNIX 权限仍正常工作)。所以 LSM 框架开发者有意选择了限制方法,而且它的大部分开发者自信他们可以在框架内工作。
理解 LSM 框架的其他限制也是重要的。LSM 框架设计只是用来支持访问控制,不是审计等其他安全问题。LSM 模块本身不能记录所有请求或它们的结果,因为它们不能看到全部。为什么?一个原因是,内核可能没有调用 LSM 模块就拒绝了请求;如果您想审计这个拒绝就会有问题。还有,出于性能的考虑,有一些提议的用于网络的 LSM 异常分支和数据域没有被主线内核所采用。它可以控制一些网络访问,但不足以支持“labelled”网络数据流(在这种情况下,不同的数据包有不同的由操作系统处理的安全标签)。这些都是不合适的限制,也不符合一般思想的基本原则;LSM 框架有希望终有一天得到扩展以破除这些限制。
尽管如此,即使有这些限制,LSM 框架对给特权添加限制来说仍是非常实用。Torvalds 的目标由 LSM 框架根本上实现了:“我不喜欢在不同的安全人群之间斗争。我希望是间接的,让 我跳出这场斗争,然后市场斗争就可以决定哪个策略和实现最终得到 应用。”
所以,如果您想在 Linux 上限制授与您的程序的特权,您可以创建完全您自己的安全模块。如果您利用真正外来的限制,可能会需要那样做——幸好这是可能的。不过,这很重要;不管怎样,您还是要编写内核代码。如果可能,您最好不要使用已有的 Linux 安全模块,而是尝试去编写自己安全模块。有一些可用的 LSM 模块,不过,Security-Enhanced Linux(SELinux)是最成熟的 Linux 安全模块之一,所以让我们来研究这个模块。
Security-Enhanced Linux(SELinux)的历史一个小历史将有助于帮助您理解 Security-Enhanced Linux(SELinux)——而且它本身也是段有趣的历史。美国国家安全局(National Security Agency,NSA)长时间以来就关注大部分操作系统中受限的安全能力。毕竟,他们的工作之一就是要确保美国国防部使用的计算机在面临没完没了的攻击时保持安全。NSA 发现大部分操作系统的安全机制,包括 Windows 和大部分 UNIX 和 Linux 系统,只实现了“选择性访问控制(discretionary access control)”(DAC)机制。DAC 机制只是根据运行程序的用户的身份和文件等对象的所有者来决定程序可以做什么。NSA 认为这是一个严重的问题,因为 DAC 本身对脆弱的或恶意的程序来说是一个不合格的防护者。取而代之的,NSA 长期以来一直希望操作系统同样能支持“强制访问控制(mandatory accesscontrol)”(MAC)机制。
MAC 机制使得系统管理员可以定义整个系统的安全策略,这个策略可以基于其他因素,像是用户的角色、程序的可信性及预期使用、程序将要使用的数据的类型等等,来限制程序可以做哪些事情。一个小例子,有了 MAC 后用户不能轻易地将“保密的(Secret)”数据转化为“不保密的(Unclassified)”的数据。不过,MAC 实际上可以做的比那要多得多。
NSA 已经与操作系统提供商合作了多年,但是很多占有最大市场的提供商对于将 MAC 集成进来没有兴趣。即使是那些集成了 MAC 的提供商也通常是将其做为“单独的产品”,而不是常规产品。一部分原因只是因为旧式的 MAC 不够灵活。
于是 NSA 的研究力量尽力去使 MAC 更灵活并且并容易被包含在操作系统中。他们使用 Mach 操作系统开发了他们的思想的原型,后来发起的工作扩展了“Fluke”研究操作系统。不过,难以让人们信服这些思想可以适用于“真实的”操作系统 ,因为所有这些工作都基于微型的“玩具级的”研究项目。极少可以在原型之外进行尝试以查看这些思想在真实的应用程序中工作得如何。NSA 不能说服具有所有权的提供商来添加这些思想,而且 NSA 也没有权利去修改私有的操作系统。这不是个新问题;多年前 DARPA 试图强制它的操作系统研究人员使用私有的操作系统Windows,但遇到了很多问题(参见下面的 )。
于是,NSA 偶然发现了一个回想起来似乎显而易见的想法:使用一个 不是 玩具的开放源代码操作系统,并实现他们的安全思想,以显示(1)它可以工作,(2)它具体如何工作(通过为所有人提供源代码)。他们选择了主导市场的开放源代码内核(Linux)并在其中实现了他们的思想,即“security-enhanced Linux”(SELinux)。毫无意外,使用真正的系统(Linux)让 NSA 研究人员可以处理他们在玩具中无法处理的问题。例如,在大部分基于 Linux 的系统中,几乎所有都是动态链接的,所以他们不得不做一些关于程序如何执行的深入分析(查阅他们关于“entrypoint”和“execute”权限的文档以获得更多资料)。这是一个更为成功的方法;正在使用 SELinux 的人比使用先前的原型的人多得多。
SELinux 如何工作那么,SELinux 如何工作呢?SELinux 的方法实际上非常普通。每一个重要的内核对象,比如每个文件系统对象和每个进程,都有一个关联到它们的“安全上下文(security context)”。安全上下文可以基于军事安全层级(如不保密的、保密的和高度保密的)、基于用户角色、基于应用程序(这样,一个 Web 服务器可以拥有它自己的安全上下文),或者基于很多其他内容。当它执行另一个程序时,进程的安全上下文可以改变。甚至,取决于调用它的程序,一个给定的程序可以在不同的安全上下文中运行,即使是同一个用户启动了所有程序。
然后系统管理员就可以创建一个指定哪些特权授与哪个安全上下文的“安全策略(security policy)”。当发生系统调用时,SELinux 去检查是否所有需要的特权都已经授与了——如果没有,它就拒绝那个请求。
例如,要创建一个文件,当前进程的安全上下文必须对父目录的安全上下文的“搜索(search)”和“add_name”特权,而且它需要有对于(要创建的)文件的安全上下文的“创建(create)”特权。同样,那个文件的安全上下文必须有特权与文件系统“关联(associated)”(所以,举例来说,“高度保密”的文件不能写到一个“不保密”的磁盘)。还有用于套接字、网络接口、主机和端口的网络访问控制。如果安全策略为那些全部授与了权限,那么请求就会被 SELinux 所允许。否则,就会被禁止。如果按部就班地去做所有这些检查将会较慢,不过有很多优化方案(基于多年的研究)使其变得很迅速。
这一检查完全独立于类 UNIX 系统中的通常的权限位;在 SELinux 系统中,您必须 既 有标准的类 UNIX 权限, 又 有 SELinux 权限才能去做一些事情。不过,SELinux 检查可以做很多对传统的类 UNIX 权限来说难以完成的事情。使用 SELinux,您可以方便地创建一个只能运行特定程序并且只能在特定的上下文中写文件的 Web 服务器。更有趣的是,如果一个攻击者攻入了 Web 服务器并成为 root,攻击者不会获得整个系统的控制权——如果有一个好的安全策略的话。
那就有了困难:为了使 SELinux 有效,您需要有一个好的安全策略来由 SELinux 执行。大部分用户将需要一个他们容易修改的实用的初始策略。几年前我开始体验 SELinux;那时,初始策略还不成熟,有很多问题。例如,在那些以前的日子中我发现早期的样例策略不允许系统更新硬件时钟(最后我提交了一个补丁以解决这一问题)。设计好的初始安全策略类似对产品分类,NSA 希望由商业界来做,而且看起来是要这样做。Red Hat、一些 Debian 开发者、Gentoo 以及其他人正在使用基本的 SELinux框架,并且正在创建初始安全策略,这样用户可以马上开始使用它。的确,Red Hat 计划为所有用户在他们的 Fedora 内核中都启用 SELinux,并提供简单的工具来使得非专业用户可以通过选择一些常见选项来修改他们的安全策略。Gentoo 有一个可引导的 SELinux LiveCD。这些团体将使得最小化程序特权变得更简单,而不需要大量代码。
在这里我们又回到了原处。SELinux 只有当程序执行时才允许发生安全传输,它控制进程的权限(不是一个进程的一部分)。所以,为了充分发挥 SELinux 的潜力,您需要将您的应用程序分解为独立的进程和程序,只有一些小的有特权的组件——这恰恰如同如何在没有 SELinux 的情况开发安全的程序。像 SELinux 这样的工具让您可以更好地控制授与的权限,并这样创建一个更强有力的防御,但是,您仍需要将您的程序拆分为更小的组件,以使得那些控制能发挥最大的效用。
结束语最小化特权是对各种安全问题的最重要防御。由于缺陷是不可避免的,您会希望大大降低缺陷导致安全问题的可能性。不过,至少一个安全的程序的 一些 部分必须有涉及安全的代码,所以您不能只是最小化特权而忽视所有其他。甚至在您已经最小化了那些涉及安全的部分以后,那些部分还是必须是正确的。为了是正确的,您需要避免常见的错误。 |