在本系列中,我们介绍了各种安全层,这些安全层不仅可以将容器与主机上的其他进程隔离开来,还可以将容器与其底层主机隔离开来。在这篇文章中,我们将讨论 AppArmor 和 SELinux 如何在我们之前讨论过的其他隔离层之外提供额外的限制。
强制访问控制系统
AppArmor 和 SELinux 是强制访问控制 (MAC) 系统的示例。这些系统与其他安全控制(通常称为自主访问控制(DAC)系统)的不同之处在于,用户通常无法更改其操作。
文件权限是 DAC 系统的一个示例。文件的所有者可以调整其权限,以允许主机上的任何人修改它。使用 MAC 系统时,用户可能无法修改对他们拥有的资源施加的约束。这些限制甚至包括 root 用户,尽管系统上的 root 用户只需禁用整个 MAC 系统即可绕过此限制。Linux 中的 MAC 系统允许你限制对各种系统资源的访问,以便即使是其他特权进程也无法访问它们。
虽然可以在任何 Linux 主机上同时使用 AppArmor 或 SELinux,但通常只启用其中一个MAC策略 ,这因不同发行版而异。默认情况下,Debian 衍生系统使用 AppArmor,而基于 Red Hat 的系统则使用 SELinux。
AppArmor
AppArmor 通过定义可应用于主机上运行的进程的不同配置文件来实现其控制能力。这些配置文件可以限制对许多资源的访问,包括文件、网络流量和 Linux capabilities。
在安装了 AppArmor 的系统上,我们可以开始探索如何使用 sudo aa-status 命令来使其发挥作用。这将显示有关 AppArmor 配置和状态的信息。
从这个输出中,我们可以看到几个有趣的信息:
- AppArmor 已加载并正常工作。
- 系统上定义了 34 个配置文件。
- 目前,没有进程具有启用的 AppArmor 配置文件。
从容器化的角度来看,配置文件列表中最有趣的部分是 docker-default 。默认情况下,此配置文件在 Docker 中默认用于提供一些保护,且不会影响应用程序兼容性。但是,这意味着它并没有像它可能的那样被锁定,因此有必要为需要额外保护的应用程序创建更严格的配置文件。
为了演示当进程获取活动 AppArmor 配置文件时会发生什么,我们可以通过 docker run -d nginx 启动一个新的 Docker 容器,然后运行 aa-status 命令。可以看到,我们现在有五个进程(由我们的容器启动)定义了一个配置文件,还有五个进程处于强制模式,这意味着 AppArmor 将根据为每个进程定义的配置文件限制它们的操作。
现在我们已经了解了 AppArmor 的基础知识,让我们看看可以使用自定义 AppArmor 配置文件执行哪些操作,以及如何将其应用于 Docker 容器。
使用 AppArmor 的自定义配置文件
AppArmor 允许你控制许多 Linux 资源,包括网络和文件访问。为了通过一个简单的示例来演示这一点,我们可以创建一个配置文件来阻止对容器内/etc目录的写入访问,即使运行容器的用户是 root 。
首先,我们将创建一个最小的配置文件来实现我们的目标。
代码语言:c复制#include <tunables/global>
profile docker-block-bin flags=(attach_disconnected, mediate_deleted) {
#include <abstractions/base>
file,
deny /etc/** wl,
}
这里的关键行是 deny /etc/** wl,它阻止对/etc和任何子目录的写入访问。我们将此配置文件写入 /etc/apparmor.d/containers/docker-block-etc ,然后使用命令 sudo apparmor_parser -r /etc/apparmor.d/containers/docker-block-etc 将其加载到内核中。一旦准备就绪,我们就可以用 Docker 进行测试了。
我们可以创建一个新容器并使用 --security-opt 标志将我们的 etc 阻止配置文件应用于它:
代码语言:bash复制docker run --rm -it --name block-bin --security-opt "apparmor=docker-block-etc" ubuntu:22.04 /bin/bash
然后,我们可以从容器内部尝试向/etc写入文件.输出证明,尽管我们是 root 用户,但我们无无法写入该目录。
如果您需要为 Docker 容器开发更复杂的配置文件,有一些工具可以帮助简化该过程,例如 Bane。Bane 的优点是自动为所有 Docker 容器添加基本限制。它还为配置文件规范提供了简化的语法。
SELinux
SELinux 在 Linux 方面有着悠久的历史。美国国家安全局最初在 2000 年将其作为 Linux 内核的一系列补丁实施。从那时起,Linux 生态系统中的开发仍在继续,如今,SELinux 默认用于基于 Red Hat 的发行版等。
与 AppArmor 相比,SELinux 采用了完全不同的安全方法。SELinux 没有将离散配置文件应用于进程,而是标记 Linux 资源(例如文件和端口),并根据每个资源的标签和尝试访问资源的进程的属性来限制对它们的访问。
在安装了 SELinux 的系统上,我们可以使用 sestatus 命令来查看它的配置方式。
此命令返回有关如何在此主机上配置 SELinux 的关键信息。
第一行表示已启用 SELinux。Loaded policy name 告诉我们我们在 targeted 模式下运行(这意味着 SELinux 将应用于主机上的分发提供商(例如 Red Hat)选择的特定进程),而不是 mls 模式,后者更严格,并且对每个进程施加限制。通常,mls 模式不适用于通用系统,因为管理所有进程的标记和权限很复杂。
下一行是:Current mode: enforcing.在这里,可能的选项是:enforcing、 permissive 和 disabled。permissive模式对于设置 SELinux 很有用,因为它不会阻止操作。相反,它将记录在系统处于enforcing模式时可能发生的任何拒绝。
现在我们已经注意到在此主机上启用了 SELinux,我们可以探索有关当前配置的更多详细信息。运行 sudo semanage login -l 将向我们展示如何配置 SELinux 以处理标准用户进程。
从这个输出中,我们可以看到 SELinux 认为普通用户(用__default__表示)和 root 用户是不受限制的,这意味着它不会对他们施加限制。
您可以使用标准系统工具和-Z开关来查看 SELinux 使用的标签。例如,pf -efZ 将显示有关应用于不同进程的标签的信息。在下面的示例中,我们可以看到 dockerd 和 containerd 进程的标签应用了 container_runtime_t 类型,并且标准用户使用的 bash 和 ssh 进程具有 unconfined_t 类型。
我们还可以使用类似 ls -alZ .
容器 SELinux 策略
在 Fedora 或 Red Hat 等 Linux 发行版下运行 Docker 时,通用的 SELinux 策略将应用于所有新容器。与 Docker 的默认 AppArmor 配置文件一样,此常规配置文件必须在提供的保护中做出权衡,因为它将相同的策略应用于每个容器。
要查看此策略的效果,我们可以运行类似 docker run --rm -it --name home_container -v /home/rorym:/hosthome fedora /bin/bash 启动名为“home_container”的新容器,该容器将我们的主目录挂载到容器中。如果我们尝试在容器内的 /hosthome 目录中创建一个文件,即使我们以 root 用户身份运行,我们也会被阻止。
为了确认 SELinux 是否阻止了访问,我们可以运行同一个容器并将 --security-opt label:disable 添加到命令中,这实际上禁用了该容器的 SELinux。如果我们随后尝试在 /hosthome 目录中创建一个文件,我们可以看到它是成功的。
如果我们想创建一个自定义的 SELinux 策略来允许我们访问我们的主目录,一种选择是使用 udica 之类的工具。此工具分析有关正在运行的容器的数据,以创建 SELinux 策略,然后可以加载和使用该策略。
首先,让我们检查我们的容器,并通过运行命令 docker inspect home_container | sudo udica home_container 将结果传递给 udica。完成此操作后,udica 将指示我们加载新的 SELinux 模块(它已创建),然后在指定新策略的同时重新启动我们的容器。使用此策略启动容器后,我们可以看到可以根据需要写入主目录。
结论
强制访问控制系统可以为容器提供额外的保护层。但是,它还需要努力学习如何有效地使用它们,并且自定义它们以大规模使用容器是一项艰巨的任务。因此,组织通常需要评估其风险状况,以确定使用它们是否有意义。在本系列的下一部分中,我们将介绍使用 seccomp 配置文件进行低级容器强化的另一种选择。