Linux 分布式安全模块
传统上,电信行业一直使用集群来满足其运营商级的高可用性、可靠性和可扩展性要求,同时依赖于经济高效的硬件和软件。高效的集群安全性现在是一项基本要求,但尚未以连贯的方式解决。
为了满足电信领域 Linux 集群对高级安全功能的需求,爱立信研究(加拿大蒙特利尔)的开放系统实验室启动了一个名为分布式安全基础设施(DSI)的项目。DSI 的主要目标是设计和开发一种安全基础设施,为在运营商级 Linux 集群上运行的电信应用提供高级安全机制。DSI 的一个重要组成部分是分布式安全模块(DSM),它提供了在 Linux 集群内强制访问控制的实现。
在本文中,我们将讨论拥有分布式安全模块的目标、架构、功能、性能和实现状态。我们还将提供一个教程,解释如何安装 DSM 并进行实验。
当前实施的安全机制依赖于自主访问控制机制。然而,这些机制不足以防范当今复杂环境中可能发生的各种攻击。访问决策基于用户身份和所有权。因此,这些机制很容易被绕过,恶意应用程序很容易导致系统安全故障和漏洞。
各种研究结果表明,操作系统提供的强制安全对于整个系统的安全至关重要。此外,他们证明强制访问控制机制对于支持计算环境中不同实体之间复杂的关系是有效的。
作为 DSI 项目的一部分,我们致力于强制访问控制框架的设计和实现。我们正在将集群感知访问控制机制实现为 Linux 可加载模块。我们在这方面的工作将有助于将 Linux 定位为集群服务器的安全操作系统。
我们的工作主要基于 FLASK 架构和 Linux 安全模块(LSM)框架;然而,我们的重点是 Linux 集群服务器,而不是单个 Linux 服务器。我们解决了集群安全性的性能挑战,因为强制执行安全性可能会导致系统性能下降、管理增加以及给用户带来一些不便。
我们的 DSM 实现的一个重要方面是其分布式特性。从安全角度来看,此方面提供了集群中安全资源的位置透明性。
LSM 框架在 Linux 内核中不提供任何额外的安全性。相反,它提供了支持开发安全模块的基础设施。LSM 内核补丁将安全字段添加到内核数据结构,并在内核代码中的特殊点插入调用(称为挂钩),以执行模块特定的访问控制检查。
LSM 添加了用于注册和注销安全模块的方法,以及一个通用的安全系统调用,允许用户程序和 LSM 之间进行通信,以支持安全感知应用程序。每个 LSM 挂钩都是全局结构 security_ops 中的函数指针。由于挂钩嵌入在内核中,即使在安装安全模块之前也会被调用,因此此结构被初始化为虚拟安全模块提供的一组函数。这些函数只是占位符,用于更有效的安全机制,可以作为 Linux 模块加载。引入了 register_security 方法,允许安全模块设置自己的安全函数来覆盖虚拟函数。unregister_security 方法用于返回到虚拟函数。
LSM 方法分为两类:1) 用于处理安全字段的挂钩和 2) 用于执行访问控制的挂钩。当创建 Linux 资源时,安全标签会附加到它。这些标签用于通过安全挂钩强制执行强制访问控制。当对象被销毁时,标签将被删除。用于处理安全字段的挂钩用于标签创建和删除。task_security_ops 结构中的 alloc_security 和 free_security 就是这些挂钩的示例。使用 LSM 进行强制访问控制的过程如图 1 所示。

图 1. 使用 LSM 模块进行访问控制
假设主体(在本例中是一个进程)的安全 ID 为 SSec,并且正在尝试访问(1)安全 ID 为 TSec 的资源(在本例中是一个文件)。为了执行访问,主体发出系统调用(2)。系统调用由 Linux 内核代码(图 1 中的系统调用接口)处理。在做出访问决策之前,内核(使用安全挂钩)咨询 LSM 模块(3),其中用户特定的安全性实现为函数“f”。LSM 将计算函数“f”并将结果返回给内核。然后,内核将授予或拒绝访问目标资源(4)。
分布式安全模块是 DSI 的一部分。实现 DSM 的目的是强制执行访问控制,并使用发送进程和节点的安全属性为集群节点的 IP 消息提供标签。
我们使用 Linux 内核 2.4.17 和适用于该内核版本的安全补丁(lsm-full-2002_01_15-2.4.17.patch)开始了 DSM 的开发。DSM 的实现基于 CIPSO 和 FIPS-188 标准,这些标准指定了 IP 头部修改(向 IP 头部添加选项),以及添加到 IP 协议栈的挂钩。
由于 DSM 是 DSI 的一个组件,让我们简要地了解一下它。作为运营商级 Linux 集群的一部分,DSI 必须符合运营商级要求,例如可靠性、可扩展性和高可用性。此外,DSI 还支持以下要求:连贯的框架、进程级方法、先发制人的安全性、动态安全策略、透明的密钥管理以及对性能的最小影响。

图 2. DSI 架构
安全服务器是管理的中心点。它是系统中所有其他安全组件的中央安全机构,负责分布式安全策略。它还通过向所有安全管理器广播分布式策略中的更改来定义整个集群的动态安全环境。
安全管理器在集群的每个节点上本地强制执行安全性。它们负责强制执行安全环境中的更改。安全管理器仅与安全服务器交换安全信息。有关 DSI 的详细描述,请参阅资源。
设计集群强制访问控制的有效解决方案是一项复杂的任务。定义访问权限涉及许多因素,因为主体和资源可能位于集群中的不同节点上。为了简化关系,我们可以处理两个级别的访问控制。在本地级别,主体和资源位于同一节点上,而在远程级别,主体和资源位于不同的节点上。
对于本地访问控制,访问权限是主体安全 ID(SSID)和资源安全 ID(TSID)的函数,参见图 1。此等式基于 FLASK 架构:访问 = 函数(SSID,TSID)。
FLASK 架构可以用作单节点处理的解决方案。当节点呈现为集群时,安全解决方案变得更加复杂。在这种情况下,我们将 FLASK 架构扩展到集群远程访问模型(图 3)。
新参数之一是安全节点 ID(SnID),顾名思义,它定义了安全方面的节点。访问权限不仅是主体和目标安全 ID 的函数,而且也是安全节点 ID 的函数。
分布式访问控制的架构如图 3 所示。该架构的等式可以描述为:访问 = 函数(SnID2,SID2,SnID1,SID1)。
分布式系统的一个重要部分是网络,它跨越集群的节点。为了在集群中应用访问控制功能,必须有一种方法可以在节点之间透明地传递安全参数。我们在 Linux 内核中进行的分布式强制访问控制原型实现将得到实践,这将为我们提供安全透明性和更好的性能。

图 3. 分布式访问控制架构
在图 3 中,我们说明了分布式访问控制如何工作的示例。安全服务器负责将安全策略传递给安全模块。它还负责向集群的每个节点提供安全节点 ID(1)。假设节点 SnID2 中的主体 2 尝试访问节点 SnID1 上的资源(文件)(2)。在这种情况下,主体 2 必须首先获得对本地通信资源的访问权限(3),然后获得一对标识符(SnID2,SID2),然后必须将其传递到远程节点(4)。这些标识符将在远程节点 SnID1 上进行验证。当访问被授予时,消息可以传递到进程 1(5)。现在进程 1 将代表进程 2 执行访问(6)。
本节更详细地解释了集群的单个节点上发生的事情。集群任何节点上的访问控制都包含两个部分(图 4)
内核空间:负责将访问控制的强制执行和决策制定任务作为单独的职责来实现。内核空间维护安全策略,其决策基于该策略。安全策略由安全服务器提供,并存储在本地内存中以实现快速访问(哈希表)。
用户空间:其许多职责(图 4)包括从分布式安全策略(1)和安全上下文存储库中获取信息,将它们组合在一起,并以易于使用的形式(2、3 和 4)将其馈送到内核空间部分。它将来自内核空间的警报传播回安全管理器,安全管理器将把它们馈送到审计和日志服务,并在必要时通过安全通信通道将它们传播到安全服务器(参见图 2)。

图 4. 节点上的访问控制
内核空间和用户空间都由每个节点上的本地安全管理器(SM)启动和监视。SM 还会将它们引入到 DSI 的其他服务和子系统中,它们需要与之交互。当用户进程尝试访问系统资源(5)时,系统调用将被转发到 DSM(6),在 DSM 中,根据 DSP 内部表示(7)做出决策。
所有主体和资源都必须被标记。由于安全模块可以在运行时加载,因此我们区分两种主体标记模式
在加载模块之前,没有任何标签附加到系统中的任何主体或资源。在模块初始化时,将扫描所有正在运行的任务,并将标签附加到它们。
当在加载安全模块后创建新进程时,安全挂钩会进行标记。
由于 Linux 将进程描述符和内核模式进程堆栈存储在单个 8KB 内存区域中,我们可以使用此事实并避免为标记主体分配内存(图 5)。

图 5. 任务安全标签分配
其他标签在运行时附加到资源,这意味着模块检查标签是否存在。如果标签未附加,则将创建一个新标签。
由于可以在集群中从一个节点上的主体访问另一个节点上的资源(图 3),因此还需要控制此类访问。
当一个节点上的进程访问另一个节点上的资源时,首先检查对通信资源(套接字、网络接口)的本地访问。当本地访问被授予时,消息可以发送到远程位置。
为了识别发送主体,安全节点 ID(安全节点标识符)和主体安全 ID(安全主体标识符)被添加到 IP 数据包中。对于此实现,我们使用 IP 协议进行安全信息传输。在 IP 头部之后添加了一个基于 IP 协议栈中挂钩的新选项。在接收端,此信息(安全节点 ID 和安全 SID)被提取(基于 IP 协议栈中的挂钩),并用于构建网络安全 ID(NSID)。构建等式为:NSID = 函数(SnID,SID)。
此函数可以由安全服务器以转换表的形式指定(对于当前实现,使用简单的数学函数)。接收端通过查找表并指定 SnID 和 SID 来提取安全网络 ID。现在,安全网络 ID 可以用作所有访问控制的本地标签。
您需要遵循几个步骤来编译、加载和实验 DSM。为了说明目的,我们假设您的机器运行 Red Hat 7.2,并使用 Linux 内核 2.4.17(来自 kernel.org)。
以下是涉及的主要步骤(在以下部分中详细解释):
为内核 2.4.17 应用 LSM 补丁。
修改内核选项并使用新选项重建内核。
更新 /etc/lilo.conf 中的引导选项。
使用新内核重新启动机器。
编译并加载安全模块。
执行一些测试以验证模块是否正常工作。
分布式安全模块基于 LSM 基础设施,这是一组添加到内核的挂钩,用于安装来自 LSM 网站的安全补丁。该站点包含许多不同的补丁;您需要将补丁与相应的内核版本匹配,在本例中为内核 2.4.17。
请按照以下步骤使用 LSM 修补内核。首先,将 lsm-full-2002_01_15-2.4.17.patch.gz 下载到 /usr/src 中。然后,解压缩补丁
% gunzip lsm-full-2002_01_15-2.4.17.patch.gz
接下来,转到 Linux 源代码树并应用补丁
% cd /usr/src/linux % patch -p1 < /usr/src/lsm-full-2002_01_15-2.4.17.patch应用适当的补丁后,重新配置内核选项以支持分布式安全模块。修改以下选项
代码成熟度级别:开发和/或未完成代码/驱动程序的提示必须设置为“y”。
可加载模块支持:所有模块符号的版本信息必须设置为“n”,以避免版本控制问题。
网络选项:网络数据包过滤必须设置为“y”,以启用用于 IP 数据包修改的网络过滤挂钩。内核 httpd 加速(实验性)必须设置为“m”。此选项将在内核中包含 tcp_sync_mss。
安全选项:功能支持应设置为“m”,IP 网络支持设置为“n”,NSA SELinux 支持设置为“m”,NSA SELinux 开发模块设置为“y”,NSA SELinux MLS 策略(实验性)设置为“n”,LSM 端口 Openwall(实验性)设置为“n”,以及域和类型强制执行(实验性)设置为“n”。
启用对这些选项的支持后,只需构建内核和模块,安装它们并正常运行 LILO 即可。
由于 DSI 的所有组件目前尚未实现,我们创建了一些测试程序来模拟这些部分,例如加载安全策略和警报接收器。在模块可以使用之前,必须由 root 加载到内核中
% /sbin/insmod lsm.o
接下来,必须向安全模块提供策略。对于此练习,安全文件是一个普通的 ASCII 文件,包含四个字段:源安全 ID;目标安全 ID;类(目前仅实现了三个类:fork、socket 和 network);以及权限。
策略文件的摘录如下所示
1 1 1 0x01 1 1 2 0x07 1 1 3 0x01
可以使用我们的测试程序 UpdatePolicy 加载策略
% UpdatePolicy policy_file警报可以由我们的测试程序 CheckAlarm 接收。该程序使用 % CheckAlarm 启动。进程的默认标签可以通过更改进程映像 ELF 格式的填充字段的第一个字节(即文件中的第八个字节)来覆盖。
我们执行了三种类型的测试,以开发安全模块的初步性能评估。测试包括使用 fork 进行进程创建、UDP 本地访问和 UDP 远程访问。UDP 测试是在启用和禁用 IP 数据包修改的情况下执行的,以便查看在 IP 数据包修改期间损失了多少性能。
这些测试在配备 256MB RAM 的 Pentium III 650MHz 机器上执行。以下是我们的测试方法
进程创建:测量进程 fork 一个立即退出的子进程所需的时间。父进程循环 100,000 次,执行 fork 和 wait 调用。
UDP 本地访问:测量进程发送 UDP 消息所需的时间。它在一个循环中发送 500,000 个 UDP 消息。发送进程不检查消息是否已发送到节点外部,也不等待确认。在这种情况下,服务器是否安装了 DSM 并不重要。
UDP 远程访问测试:测量进程发送 UDP 消息并从服务器接收 UDP 响应所需的时间。此测试在一个循环中发送和接收 100,000 个 UDP 消息。客户端进程将在收到服务器的确认后发送新消息。在这种情况下,服务器运行 DSM 软件非常重要,以便在接收端检查权限。对于我们的测试,第二个服务器是一台配备 128MB RAM 的 Pentium II 300MHz 桌面电脑。
根据执行的测试,我们在表 1 和表 2 中列出了结果。所有数字均以微秒为单位。
进程创建结果:我们有 1.2% 的开销增加,因为系统必须对 fork 操作执行权限检查,并花费额外的时间标记子进程。
UDP 本地访问结果:启用 DSM 模块的设置相对于禁用 DSM 模块的设置的平均开销为 20%。此开销包括对套接字执行权限检查、发送消息和为每个消息附加 sk_buff 标签,以及标记 IP 消息。当禁用 IP 数据包修改时(表 2),开销降至 4.2%。
UDP 远程访问结果:启用 DSM 模块的设置相对于禁用 DSM 模块的设置的平均开销为 30%。开销包括以下内容:在发送套接字端执行权限检查、将标签附加到 sk_buff、将安全信息附加到 IP 消息、在接收端检索安全信息、将网络安全 ID 附加到 sk_buff、对 sk_buff 执行权限检查、对套接字执行安全检查,以及在返回消息上重复上述所有操作。当禁用 IP 数据包修改时(表 2),开销降至 5.4%。
在两个 UDP 测试案例中,大部分开销与 IP 数据包修改有关。只有一小部分开销是由安全模块引起的。
我们正在进行的工作和 DSM 的未来计划包括
完全实现分布式访问控制框架。
优化代码以获得更好的性能。
为服务器资源提供额外的功能,以代表客户端进行访问。
提供安全信息保护。
在协议栈的较低层提供安全信息传输。
测试集群安全性以抵御不同类型的攻击。
如前所述,DSM 是 DSI 架构的一个组件。到目前为止,我们已经有了 DSM 的基本实现。我们提供的性能结果必须被视为上限,因为没有对任何单个安全操作进行优化。
我们已经完成了一些优化 IP 数据包修改的工作。初步结果显示出显著的改进。启用 IP 数据包修改的 UDP 本地访问(表 1)已从 20% 的开销降至 8%。同样,UDP 远程访问(表 1)已从 30% 的开销降至 14%。这些结果令人鼓舞,我们看到了更多优化并达到更低开销的机会。然而,结果表明了开发高效分布式安全性所面临的挑战。
就真实环境中的测试而言,我们使用缓冲区溢出攻击测试了该框架。事实证明,我们当前的解决方案可以防御这些类型的攻击。
最后,我们尽力在有限的空间内描述 DSM。但是,如果您想了解更多详细信息,请随时与我们联系。我们希望您试用 DSM 的源代码和配套测试程序,并向我们发送您的反馈。所有源代码都可以在 ftp.linuxjournal.com/pub/lj/listings/issue102/6215.tgz 下载。
Miroslaw Zakrzewski (Miroslaw.Zakrzewski@Ericsson.ca) 是爱立信研究开放系统实验室的研究员。他是分布式安全模块的首席开发人员。
Ibrahim Haddad (Ibrahim.Haddad@Ericsson.ca) 是爱立信公司研究部门在加拿大蒙特利尔的研究员。