介绍

LXC 容器可以分为两种类型

  • 特权容器
  • 非特权容器

前者可以被认为是旧式容器,它们完全不安全,只应该在非特权容器不可用且您信任容器用户对主机的 root 访问权限的环境中使用。

后者是在 LXC 1.0(2014 年 2 月)中引入的,需要一个相当新的内核(3.13 或更高)。好处是,我们认为这些容器是 root 安全的,因此,只要您关注内核安全问题,这些容器就是安全的。

由于特权容器被认为是不安全的,我们通常不会将新的容器逃逸漏洞视为值得 CVE 和快速修复的安全问题。但是,我们会尝试缓解这些问题,以防止意外损坏主机。

特权容器

特权容器被定义为任何容器,其中容器 uid 0 映射到主机的 uid 0。在这样的容器中,对主机的保护和逃逸的预防完全通过强制访问控制(apparmor、selinux)、seccomp 过滤器、删除功能和命名空间来完成。

这些技术的组合通常会防止任何意外损坏主机,其中损坏被定义为重新配置主机硬件、重新配置主机内核或访问主机文件系统等行为。

LXC 上游的立场是,这些容器不是并且不能是 root 安全的。

它们在您运行受信任的工作负载或没有不受信任的任务在容器中以 root 身份运行的环境中仍然很有价值。

我们知道一些漏洞可以让您逃离此类容器并在主机上获得完全的 root 权限。其中一些漏洞可以很容易地被阻止,因此我们在了解后会更新我们的不同策略。其他一些则无法阻止,因为它们需要阻止如此多的核心功能,以至于普通容器将变得完全不可用。

非特权容器

非特权容器在设计上是安全的。容器 uid 0 映射到容器外部的非特权用户,并且只有对其自身拥有的资源的额外权限。

使用此类容器,SELinux、AppArmor、Seccomp 和功能对于安全性来说是不必要的。LXC 仍然会使用它们来添加额外的安全层,这在发生内核安全问题时可能会有用,但安全模型不是由它们执行的。

为了使非特权容器正常工作,LXC 与 3 个 setuid 代码片段交互

  • lxc-user-nic(setuid 助手,用于创建 veth 对并在主机上桥接它)
  • newuidmap(来自 shadow 包,设置 uid 映射)
  • newgidmap(来自 shadow 包,设置 gid 映射)

所有其他操作都以您自己的用户身份或您用户拥有的 uid 身份运行。

因此,这些容器中的大多数安全问题(容器逃逸、资源滥用等)同样适用于随机的非特权用户,因此将是通用的内核安全漏洞,而不是 LXC 问题。

LXC 上游很乐意帮助跟踪此类安全问题,并与 Linux 内核社区取得联系,以便尽快解决它们。

潜在的 DoS 攻击

LXC 默认情况下并不假装防止 DoS 攻击。在运行多个不受信任的容器或允许不受信任的用户运行容器时,应该记住以下几点并相应地更新配置。

Cgroup 限制

LXC 从其父进程继承 cgroup 限制,在我的 Linux 发行版中,没有设置任何实际的限制。因此,容器中的用户可以相当容易地通过运行 fork 炸弹、使用系统的所有内存或创建网络接口直到内核内存不足来对主机进行 DoS 攻击。

这可以通过设置相关的 lxc.cgroup 配置项(内存、cpu 和 pids)或确保父用户在登录时被放置在配置适当的 cgroups 中来缓解。

用户限制

与 cgroups 一样,父进程的限制会被继承,因此非特权容器不能将 ulimit 设置为高于其父进程的值。

但是,有一点值得注意,ulimit 正如其名称所暗示的那样,与内核级别的 uid 相绑定。这是全局内核 uid,而不是用户命名空间内的 uid。

这意味着,如果两个容器通过相同的或重叠的 id 映射共享一个共同的内核 uid,那么它们也共享限制,这意味着第一个容器中的用户可以有效地对另一个容器中的相同用户进行 DoS 攻击。

为了防止这种情况,不受信任的用户或容器应该拥有完全独立的 id 映射(理想情况下分别为 65536 个 uid 和 gid)。

共享网络桥接

LXC 为其容器设置基本的 2 级连接。为了方便起见,它还在系统上提供了一个默认桥接。

由于连接到桥接的容器可以传输任何它想要的 2 级流量,因此它可以在桥接上有效地执行 MAC 或 IP 欺骗。

在运行不受信任的容器或允许不受信任的用户运行容器时,理想情况下应该为每个用户或每个不受信任的容器组创建一个桥接,并将 /etc/lxc/lxc-usernet 配置为用户只能使用分配给他们的桥接。

保护 IPv6 路由器通告接受

此外,还必须注意考虑容器通过 IPv6 路由器通告修改 LXC 主机 IPv6 路由表 的可能性。这是因为默认的 LXC 桥接只配置了 IPv4 地址。这意味着 /proc/sys/net/ipv6/conf/default/accept_ra 的值将应用于 lxcbr0 接口。如果它是一个大于 0 的值,那么 LXC 主机将接受来自连接到桥接的容器的(可能是恶意的)路由器通告。

为了避免这种情况,您可以通过在 /etc/default/lxc-net 中设置 LXC_IPV6_* 变量来配置默认桥接上的 IPv6 地址(这将启用 /proc/sys/net/ipv6/conf/lxcbr0/forwarding,如果该值为 1,则会导致 /proc/sys/net/ipv6/conf/lxcbr0/accept_ra 被有效地禁用。有关更多信息,请参见 https://linuxkernel.org.cn/doc/Documentation/networking/ip-sysctl.txt),或者您可以将 /proc/sys/net/ipv6/conf/default/accept_ra 设置为 0,这样在创建 lxcbr0 时,它的 accept_ra 将被禁用。但是,如果您在 LXC 主机上使用 IPv6 并且依赖于来自外部网络的路由器通告,那么您应该确保为外部接口启用了 accept_ra,以避免失去连接。

报告安全问题

为了确保安全问题能够尽快在所有 Linux 发行版中得到修复,应通过以下方式报告问题

然后,我们将确认安全问题,针对所有受支持的版本制定修复方案,为您提供这些补丁进行测试,然后分配一个 CVE 以及您和 Linux 发行版社区的协调发布日期。