在 Linux 集群中设置虚拟安全区域
越来越多的项目使用 Linux 和其他开源软件作为构建集群的基本模块。 示例包括用于执行电影视觉效果的大规模计算的集群,以及用作下一代电信服务器的集群。
越来越频繁地,包括经济性、管理和灵活性在内的各种问题,要求应用程序在同一物理集群上运行。 电信领域中这种情况的一个例子是运营商之间共享的运营商级集群服务器。 运营商共享集群的全局基础设施,并向其客户提供不同的服务,但希望保持其二进制文件和数据的私密性。 在这种情况下,集群管理员无权访问这些应用程序的源代码,并且无法在源代码级别强制执行安全机制。 因此,需要一个安全基础设施来确保给定应用程序的资源不会被集群上的其他实体篡改或使用。
分布式安全基础设施 (DSI) 为这种情况提供了一种解决方案。 它试图通过将集群划分为几个虚拟子集群,为运营商级 Linux 集群构建一个连贯的安全框架,从而保证它们之间受控/受限的连接。 即使该项目仅处于设计和开发的第二年,我们相信 DSI 对于集群管理员来说是一个有用的工具。 本文介绍了如何使用 DSI 在 Linux 集群内设置虚拟安全区域。
在本节中,我们将简要介绍 DSI 的架构。 DSI 由一个安全服务器 (SS) 和多个安全管理器 (SM) 组成,每个节点一个(图 1)。 SS 集中管理集群:它收集 SM 发送的警报和警告,并在集群上传播唯一的安全策略。 另一方面,SM 负责在其自己的节点上强制执行安全。 此外,SS 和 SM 之间的消息通过加密和身份验证的通道进行交换,使用基于 CORBA 事件通道的 SSL/TLS。
DSI 中的安全机制在进程级别实现,以检查进程对资源的访问权限。 每个进程都由其安全上下文标识符 (ScID) 和它运行所在的节点标识 (SnID) 标识。
SnID 使用 DSI SetNodeID 工具分配。 共享相同安全上下文的所有进程都被分配相同的 ScID。 ScID 可以由系统根据 DSP 规则自动分配(见下文),或者可以使用 DSI SetSID 工具专门分配给给定的二进制文件。 这允许根据安全上下文对二进制文件进行分组。
在 DSI 中,为集群编写安全策略包括授予或拒绝给定 SnID 和 ScID 对的权限。 这些规则对整个集群有效。 所有规则都集中在 SS 上的 XML 文件中,以简化管理。
DSI 提供了一种更新和透明地自动强制执行整个集群安全性的唯一同构视图的方法。 一旦管理员修改了现有规则或向分布式安全策略 (DSP) 添加了新规则,就必须使用 dsiUpdatePolicy 工具将 DSP 加载到 SS 上。 然后,dsiUpdatePolicy 根据我们的 DSP XML 模式检查 DSP 文件(语法检查)。 如果 DSP 得到验证,SS 将新规则传播到集群的所有节点,使用安全通信通道。 最后,每个 SM 在内核级别调用分布式安全模块(DSM,见图 2)来强制执行规则。 DSM 基于 LSM 内核补丁。 其详细描述超出了本文的范围; 有关更多信息的链接,请参阅在线资源部分。
控制对本地资源的访问相当简单:DSM 模块检索请求进程的本地 ScID 和 SnID,并在安全规则中检查相应的权限。 实际上,DSI 的独创性在于分布式访问控制。 目前,仅实现了套接字通信。 为了说明这一点,我们详细介绍了当进程尝试访问位于另一个节点上的资源时的访问控制机制(图 3)
访问请求首先被本地 DSM 拦截,本地 DSM 检查进程是否具有本地调用与套接字相关的系统调用的权限。
然后,DSM 将请求进程的 ScID 和 SnID 添加到发送到远程节点的每个 IP 数据包中。
在接收节点上,远程 DSM 使用从 IP 数据包中提取的请求进程的 ScID 和 SnID,来检查其与目标套接字以及目标套接字所属进程通信的权限。
最后,远程 DSM 在本地检查目标套接字所属的进程是否可以接收来自请求进程的信息。
在本节中,我们将介绍一个简单的场景,该场景提出了一个问题,并解释了 DSI 如何帮助解决该问题。 假设我们想在两个电信运营商 PhoneMania 和 RingBell 之间共享一个包含两个节点的集群(我们从小处开始),每个运营商都在集群节点上运行自己的应用程序。 两者都提供电话报价服务:最终用户呼叫入口点服务器(使用 TelecomClient)并请求给定公司的报价。 入口点服务器(PhoneManiaEP 和 RingBellEP)将请求转发到其后端服务器(PhoneManiaBE 和 RingBellBE),后端服务器检索报价并将其发回给最终用户。
从集群运营的角度来看,问题如下:我们如何防止 PhoneMania 应用程序将其请求转发到 RingBell 的后端服务器? 在没有任何特定安全基础设施的情况下,当 PhoneMania 的后端服务器过载时,或者仅仅当它没有请求的信息时,PhoneMania 可能会这样做——更不用说更严重的订户数据盗窃或针对竞争对手的蓄意损害等情况。 为了说明这种情况,我们将所有参与者都实现为简单的 UDP 客户端和服务器应用程序(图 4)。
以下是欺诈场景的步骤
PhoneMania 和 RingBell 在名为 munster 的节点上启动其后端服务器
[munster demo]$ ./RingBellBE -h 10.1.1.2 -p 9001 RINGBELL: bind on 10.1.1.2:9001 .. [munster demo]$ ./PhoneManiaBE -h 10.1.1.2 -p 8801 PHONEMANIA: bind on 10.1.1.2:8801
然后,由于 PhoneMania 过载,他决定使用 RingBell 的资源。 因此,在节点 colby 上,PhoneMania 的入口点服务器(端口 8800)将其所有客户的请求转发到 RingBell 的后端服务器(端口 9001)
[colby demo]$ ./PhoneManiaEP -h 10.1.1.1 -p 8800 -b 10.1.1.2 -r 9001 PHONEMANIA: bind on 10.1.1.1:8800 PHONEMANIA: connect on 10.1.1.2:9001 ..
当客户端在 PhoneMania 的入口点(端口 8800)请求报价时,PhoneMania 实际上使用 RingBell 的后端服务器来回答(端口 9001)。 简而言之,PhoneMania 通过使用 RingBell 的资源获得报酬
[colby demo]$ ./TelecomClient -h 10.1.1.1 -p 8800 Connecting to : 10.1.1.1:8800 Requesting quotation for Ericsson Quote Ericsson .. [munster demo]$ .. RINGBELL backend : processing quotation request for Ericsson RINGBELL backend : quotation for Ericsson is 83 Quote Ericsson
为了防止这种情况,我们建议使用 DSI 将共享集群安全地划分为不相交的区域。 接下来,我们将逐步展示如何使用 DSI 来做到这一点。
首先,我们需要在集群的所有节点上安装 DSI。 从 SourceForge(请参阅资源)下载最新的 DSI tarball 后,DSI 应该可以在您的机器上编译,因为它使用了标准的 configure 和 make 策略。 我们在 SourceForge 站点上的 DSI 文档中详细介绍了如何构建和安装 DSI。
您应该在每个节点上运行安全管理器。 对于我们的双节点集群,这意味着它在 colby 和 munster 上运行
[colby]$ cd ~/dsi [colby]$ source dsi_setup.sh [colby]$ ~/dsi/bin/dsiSecManager
为简化起见,colby 也充当安全服务器
[colby]$ cd ~/dsi [colby]$ source dsi_setup.sh [colby]$ ~/dsi/bin/dsiSecServer
SS 和 SM 彼此之间使用 CORBA 事件通道进行通信。
我们在每个节点上加载 DSI 内核模块 DSM,以在内核级别强制执行安全
$ cd ~/dsi/lsm $ su root Password: # ./load # /sbin/lsmod Module Size Used by Not tainted dsm 36332 0 (unused) ...
然后,我们通过定义每个节点上用于安全和非安全通信的不同 IP 地址来配置 DSI。 为此,我们编写了一个名为 DciInit 的工具; 有关 dci_policy.conf 文件的格式以及如何使用 DciInit 的更多详细信息,请参阅 SourceForge 站点上的 DSI 文档
$ cd ~/dsi/user/tools $ ./DciInit ~/dsi/etc/dci_policy.conf
基本上,要创建不相交的虚拟子集群,您需要为 PhoneMania 的资源(在我们的示例中,ScID=10)和 RingBell 的资源(ScID=20)分配不同的 ScID。 然后,向 DSP 添加新规则,以限制从 ScID 10 定义的区域到 ScID 20 定义的区域以及反之亦然的任何连接。 通过将每个运营商的资源组织在单独的组中,并且它们之间没有任何可能的连接,我们实际上实现了集群的虚拟细分。 此外,管理员可以创建另一个由 ScID 30 定义的区域,该区域具有访问两个子集群以进行管理目的的权限。
首先,让我们分配每个节点上每个二进制文件的 ScID(使用 DSI SetSID 工具)
$ ~/dsi/user/tools/SetSID PhoneManiaEP 10 Changing from SID 0 to SID 10 $ ~/dsi/user/tools/SetSID PhoneManiaBE 10 Changing from SID 0 to SID 10 $ ~/dsi/user/tools/SetSID RingBellEP 20 Changing from SID 0 to SID 20 $ ~/dsi/user/tools/SetSID RingBellBE 20 Changing from SID 0 to SID 20 $ ~/dsi/user/tools/ls_dsi . PERMISSION USER GROUP BSID FILE -rwxr-xr-x lmcaxpr install 10 PhoneManiaBE -rwxr-xr-x lmcaxpr install 20 RingBellBE -rwxr-xr-x lmcaxpr install 10 PhoneManiaEP -rwxr-xr-x lmcaxpr install 20 RingBellEP
加载 DSM 后,它会强制执行默认的宽松安全规则。 为了实现集群细分,我们必须编辑 DSP 文件 ~/dsi/etc/SampleDSP.xml,并将所有现有的安全规则替换为我们自己的规则。
PhoneMania 的套接字被分配了 ScID=10,RingBell 使用 ScID=20。 以下规则将 ScID=10 分配给 PhoneMania 的入口点 UDP 套接字(端口 8800)
<class_SOCKET_INIT_rule> <protocol>UDP</protocol> <port>8800</port> <SnID>ALL</SnID> <ScID>10</ScID> </class_SOCKET_INIT_rule>
我们需要另外三个类似的规则:一个用于 PhoneMania 的后端服务器,另外两个用于 RingBell 的 ScID=20。
然后,允许 PhoneMania 的进程(源 ScID=10)在其拥有的套接字上(即,目标 ScID=10)创建、发送或接收消息
<class_SOCKET_rule> <sScID>10</sScID> <sSnID>ALL</sSnID> <tScID>10</tScID> <tSnID>ALL</tSnID> <allow>CREATE CONNECT LISTEN RECEIVE SEND</allow> </class_SOCKET_rule>
为 RingBell 的进程创建类似的规则。
当然,必须拒绝 ScID=10 和 20 之间的通信。 这只需在这些 ScID 之间设置任何套接字权限即可完成
<class_SOCKET_rule> <sScID>10</sScID> <sSnID>ALL</sSnID> <tScID>20</tScID> <tSnID>ALL</tSnID> <allow></allow> </class_SOCKET_rule>
在源 ScID=20 和目标 ScID=10 之间创建类似的规则。
给定运营商的后端服务器和入口点服务器可能位于集群的不同节点上; 请记住,我们正在共享一个集群,而不是将一个节点专用于 RingBell,另一个节点专用于 PhoneMania。 因此,PhoneMania 的进程(源 ScID=10)必须能够通过网络与其他 PhoneMania 进程(目标 ScID=10)通信。 RingBell 的情况也是如此
<class_NETWORK_rule> <sScID>10</sScID> <sSnID>ALL</sSnID> <tScID>10</tScID> <tSnID>ALL</tSnID> <deny>NETWORK_RECEIVE</deny> </class_NETWORK_rule>
最后,PhoneMania (ScID=10) 和 RingBell (ScID=10) 进程通常从 shell(默认 ScID=2)启动。 因此,基本上,我们需要允许 shell 创建一个新进程。 这是通过转换规则完成的
<class_TRANSITION_rule> <parent_ScID> 2 </parent_ScID> <SnID>ALL</SnID> <binary_ScID>10</binary_ScID> <new_ScID>10</new_ScID> </class_TRANSITION_rule>
binary_ScID 是显式分配给二进制文件的 ScID。 请记住,我们使用 SetSID 将 ScID 分配给 PhoneManiaBE 或 PhoneManiaEP。 new_ScID 是分配给新创建的进程的 ScID。 由于只有 ScID=10 才能访问套接字 8800 和 8801,因此对于 PhoneMania,新进程应分配 ScID=10。 应该为 RingBell 创建类似的规则。
这就是我们在 DSP 中需要的所有内容——12 个安全规则。 然后,我们通过向安全服务器发送更新事件来更新整个集群中的安全策略
[colby]$ cd ~/dsi/SS/test/demoSecOM [colby]$ ./dsiUpdatePolicy ~/dsi/etc/DSP.xml
安全服务器读取更新后的 DSP 文件(位于 ~/dsi/etc/DSP.xml 中),并在存在语法错误时显示警告。 最后,它自动将更新发送到每个安全管理器; 无需登录到每台机器手动更新安全策略或开发您自己的基于 Perl 的系统管理软件版本。 当您拥有数百个物理分布在世界各地的节点集群时(想想网格计算),此功能可能是一种恩赐。
现在,是时候再次尝试 PhoneMania 将请求转发到 RingBell 后端服务器的情况了
[colby demo]$ ./TelecomClient -h 10.1.1.1 -p 8800 Requesting quotation for Ericsson Quote Ericsson ... [colby demo]$ ./PhoneManiaEP -h 10.1.1.1 -p 8800 -b 10.1.1.2 -r 9001 PHONEMANIA: bind/connect on 10.1.1.1:8800 = 0 PHONEMANIA: bind/connect on 10.1.1.2:9001 = 0 Quote Ericsson Quotation request received ... [munster demo]$ ./RingBellBE -h 10.1.1.2 -p 9001 RINGBELL: bind on 10.1.1.2:9001 ...
在另一个节点 (munster) 上,我们注意到 RingBell 的后端服务器不再处理 PhoneMania 的请求,尽管 PhoneMania 非法地将它们重定向到 RingBell。 您可以使用 DSI 生成的日志(位于 /var/log/messages 中)来跟踪非法请求
May 6 07:47:31 munster kernel: DSI-LSM MODULE - dsi_sock_rcv_skb check permission sscid 10 ssnid 1 tscid 20 May 6 07:47:31 munster kernel: DSI-LSM MODULE Error - dsi_sock_rcv_skb - No Permission
我们展示了一种在不同用户的不同应用程序之间安全共享集群的实用解决方案。 DSI 项目允许用户轻松地为集群中的每个应用程序创建不相交的安全区域。 一旦 DSI 安装在集群上,为新应用程序创建新安全区域所需的工作量将缩减为为二进制文件设置适当的 ScID,并在 DSP 文件中包含相应的规则。 不需要修改源代码,而且无论如何可能都不可能。
本文的资源: /article/7688。
Makan Pourzandi (makan.pourzandi@ericsson.ca) 在爱立信加拿大研究部的开放系统研究部门工作。 他的研究领域是安全、集群计算和分布式编程的基于组件的方法。 他于 1995 年在法国里昂大学获得并行计算博士学位。
Axelle Apvrille (axelle.apvrille@ericsson.ca) 目前在爱立信加拿大研究部的开放系统研究部门工作。 她的研究兴趣是密码学、安全协议和分布式安全。 她于 1996 年在法国波尔多 ENSEIRB 获得计算机科学工程学位。