使用 SELinux 和 Smack 增强轻量级容器(5)

现在需要创建一个能够安全启动容器的脚本。这意味着它能将自己的进程标签设置为 vs1,并通过 dropmacadmin 打包容器的 /sbin/init。如下所示:

cat >> /vs1/vs1.sh << EOF

#!/bin/sh

cp /bin/dropmacadmin /vs1/rootfs.vs1/bin/

attr -S -s SMACK64 -V vs1 /vs1/rootfs.vs1/bin/dropmacadmin

echo vs1 > /proc/self/attr/current

lxc-start -n vs1 /bin/dropmacadmin /sbin/init

EOF

chmod u+x /vs1/vs1.sh

再做一件事情就可以让 vs1 在其即将装载的 tmpfs 文件系统上执行写操作:

sed -i 's/defaults/defaults,smackfsroot=vs1,smackfsdef=vs1/' \

/vs1/rootfs.vs1/etc/fstab

这导致在 /dev/shm 上装载 tmpfs 文件系统,以带上 vs1 标签,从而让 vs1 可以对它执行写操作。否则,vs1 init 脚本将不能在设置网络时创建需要使用的 /dev/shm/network 目录。类似地,如果希望使用基于 ram 的 /tmp,使用相同的选项即可。

现在,我们再次帮助 vs1 进行伪装。像创建 vs1 那样创建 vs2,在每个步骤中将 vs1 替换为 vs2。然后在 vs1 的 /mnt 下绑定装载根文件系统:

mount --bind /vs1 /vs1

mount --make-runbindable /vs1

mount --rbind / /vs1/rootfs.vs1/mnt

使用 vs1.sh 启动容器。注意,您还可以从 kvm 主机看到 vs1 和 vs2 上的 Web 页面。此外还要注意,vs1 不能通过网络访问 vs2。它也不能查看 vs2 的文件:

vs1:~# ls /mnt/

(directory listing)

vs1:~# ls /mnt/vs2/rootfs.vs2

ls:/mnt/vs2/rootfs.vs2: Permission denied

vs1:~# mkdir /cgroup

vs1:~# mount -t cgroup cgroup /cgroup

vs1:~# ls /cgroup

ls:/mnt/vs3: Permission denied

vs1:~# mknod /dev/sda1 b 8 1

mknod: `/dev/sda1': Operation not permitted

vs1:~# mount /mnt/dev/sda1 /tmp

mount: permission denied

它能查看主机文件系统。对于需要保护的任何东西,可以使用 host 标签进行标记。在 cgroup 文件系统上就采用了这种做法,这正是 ls /cgroup 失败的原因。

最后,设备白名单 cgroup 防止我们创建磁盘设备,或在它存在的情况下装载它(因为这需要通过 /mnt 来完成)。

当然,我们的设置方式让容器管理员可以删除 /mnt/dev/sda1,或用其他方法扰乱主机,因此除了用于演示外,这种绑定装载是不如人意的!

注意,在 SELinux 系统上,默认(且容易的)路由允许容器通过网络彼此进行对话,而在 Smack 中则恰好相反。目前,允许容器彼此对话还是比较困难的。不久以后,将可以在 IP 地址上设置标签,并且允许建立策略以实现容器之间的通信。

在如何建立 Smack 网络方面还有另一个问题。命令 kill -9 -1 终止系统上的每个任务。当这个操作由容器中的任务执行时,它将仅终止同一容器中的任务。这种行为已经在上游内核中得到修复,但我们使用的 Fedora 10 内核还存在该问题。因此,每个任务都会发出一个 -9 信号。

在受 SELinux 保护的容器中,SELinux 阻止该信号通过容器边界,因此 kill -9 -1 实际上是安全的。但在 Smack 中,默认情况下任务被标记为 _(就像网络一样),因此由于我们允许容器执行 _ 写操作以写到网络中,并且终止任务在 Smack 中被认为是写访问,所以允许容器管理员在整个系统上终止任何任务。

另一个缺点(SELinux 容器仍然存在该缺点)与 Unix98 伪终端有关。打开两个图形化终端。在第一个终端中,启动 vs1 并查看 /dev/pts。您将看见至少两个条目(0 和 1),它们分别属于每个终端。可以从 vs1 容器写入到与另一个终端对应的条目。

对于 Fedora 内核,有两个解决方案。可以使用设备白名单 cgroup 拒绝容器打开设备。但是这必须在容器每次启动时手动操作,以允许它访问自己的终端;或者应用 SELinux 和 Smack 标签,结果是一样的。

更新的 2.6.29 内核支持 devpts 名称空间。容器必须重新装载 /dev/pts,在这个操作之后,它将不能访问属于主机或其他容器的 devpts 条目。

结束语

本文介绍了构建受 LSM 保护的容器所需的工具,但还有很多工作需要做:

对于 Smack,必须选择需要标记为 host 的文件。

对于 SELinux,应该对其进行调优,然后将一个 container 接口放置到上游引用策略。

尽管这些工作正在进行当中,在获得更多关于受 LSM 保护的容器的经验之前,您不应该完全信赖这些机制来阻止不可信的根用户。

尽管目前在创建容器方面还没有最佳实践,但仍然有一些想法是值得考虑的。首先,记住您正试图实现两个有些矛盾的目标:最小化容器(以及主机)之间的复制,同时需要确保安全隔离。实现这些目标的方法之一是,创建一个最小的完整 rootfs,其中不运行任何容器,并且将它的类型标记为所有容器都可以读取的类型。然后使用 lxc-sshd 脚本的定制版本创建每个基于原型的实际容器,以为容器的大部分文件系统创建只读装载,同时为容器提供一个可以存储文件的私有可写位置(比如 /scratch)。由于每个容器都有一个私有的装载名称空间,所以它能够绑定装载任何私有的和/或对于其私有共享目录可写的文件或目录。例如,如果它需要一个私有 /lib,则可以执行 mount --bind /scratch/rootfs/lib /lib。同样地,管理员也能确保每个容器都在启动时执行 mount --bind /scratch/shadow /etc/shadow。

对于 SELinux 和 Smack,我演示的这个方法的一个明显缺点就是容器管理员不能在自己的容器的内部利用 LSM 控制信息流。并且为简单起见,容器中的所有任务都使用 MAC 策略统一处理。在另一篇文章中,我将探讨如何允许容器管理员指定自己的 LSM 策略,同时又能够约束它们。

致谢

Smack 的作者 Casey Schaufler 为构建受 Smack 保护的容器提供了帮助,Dan Walsh 提供了关于 SELinux 策略的反馈。我在此对他们表示衷心感谢!

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/b55a634bdb11da5774507863276a5ec3.html