Podman:一种更安全的容器运行方式

Podman 使用传统的 fork/exec 模型(而非客户端/服务器模型)来运行容器。
397 位读者喜欢这篇文章。

在深入探讨本文的主题,Podman 和容器之前,我需要稍微介绍一下 Linux 审计功能的技术细节。

什么是审计?

Linux 内核有一个有趣的安全性功能,称为 审计。它允许管理员监视系统上的安全事件,并将它们记录到 audit.log 中,该日志可以存储在本地或远程的另一台机器上,以防止黑客试图掩盖其踪迹。

/etc/shadow 文件是一个常见的安全文件,需要监视,因为向其中添加记录可能会使攻击者重新获得对系统的访问权限。管理员希望知道是否有任何进程修改了该文件。您可以通过执行以下命令来做到这一点

# auditctl -w /etc/shadow

现在让我们看看如果我修改 /etc/shadow 文件会发生什么

# touch /etc/shadow

# ausearch -f /etc/shadow -i -ts recent

type=PROCTITLE msg=audit(10/10/2018 09:46:03.042:4108) : proctitle=touch /etc/shadow

type=SYSCALL msg=audit(10/10/2018 09:46:03.042:4108) : arch=x86_64 syscall=openat

success=yes exit=3 a0=0xffffff9c a1=0x7ffdb17f6704 a2=O_WRONLY|O_CREAT|O_NOCTTY|

O_NONBLOCK a3=0x1b6 items=2 ppid=2712 pid=3727 auid=dwalsh uid=root gid=root

euid=root suid=root fsuid=root egid=root sgid=root fsgid=root tty=pts1 ses=3 comm=touch

exe=/usr/bin/touch subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)

审计记录中有很多信息,但我突出显示了它记录了 root 用户修改了 /etc/shadow 文件,并且该进程的审计 UID (auid) 的所有者是 dwalsh

内核做了什么?

跟踪登录 UID

有一个字段叫做 loginuid,存储在 /proc/self/loginuid 中,它是系统上每个进程的 proc 结构的一部分。这个字段只能设置一次;设置后,内核将不允许任何进程重置它。

当我登录系统时,登录程序会为我的登录进程设置 loginuid 字段。

我的 UID,dwalsh,是 3267。

$ cat /proc/self/loginuid

3267

现在,即使我成为 root 用户,我的登录 UID 仍然保持不变。

$ sudo cat /proc/self/loginuid

3267

请注意,从初始登录进程 fork 和执行的每个进程都会自动继承 loginuid。这就是内核知道登录者是 dwalsh 的原因。

容器

现在让我们看看容器。

sudo podman run fedora cat /proc/self/loginuid

3267

即使是容器进程也保留了我的 loginuid。现在让我们用 Docker 试试。

sudo docker run fedora cat /proc/self/loginuid

4294967295

为什么会有差异?

Podman 对容器使用传统的 fork/exec 模型,因此容器进程是 Podman 进程的子进程。Docker 使用客户端/服务器模型。我执行的 docker 命令是 Docker 客户端工具,它通过客户端/服务器操作与 Docker 守护进程通信。然后 Docker 守护进程创建容器并处理 stdin/stdout 与 Docker 客户端工具的通信。

进程的默认 loginuid(在其 loginuid 设置之前)是 4294967295。由于容器是 Docker 守护进程的子进程,而 Docker 守护进程是 init 系统的子进程,我们看到 systemd、Docker 守护进程和容器进程都具有相同的 loginuid,4294967295,审计将其称为未设置的审计 UID。

cat /proc/1/loginuid

4294967295

这可能被如何滥用?

让我们看看如果 Docker 启动的容器进程修改了 /etc/shadow 文件会发生什么。

$ sudo docker run --privileged -v /:/host fedora touch /host/etc/shadow

$ sudo ausearch -f /etc/shadow -i

type=PROCTITLE msg=audit(10/10/2018 10:27:20.055:4569) : proctitle=/usr/bin/coreutils

--coreutils-prog-shebang=touch /usr/bin/touch /host/etc/shadow

type=SYSCALL msg=audit(10/10/2018 10:27:20.055:4569) : arch=x86_64 syscall=openat

success=yes exit=3 a0=0xffffff9c a1=0x7ffdb6973f50 a2=O_WRONLY|O_CREAT|O_NOCTTY|

O_NONBLOCK a3=0x1b6 items=2 ppid=11863 pid=11882 auid=unset uid=root gid=root

euid=root suid=root fsuid=root egid=root sgid=root fsgid=root tty=(none) ses=unset

comm=touch exe=/usr/bin/coreutils subj=system_u:system_r:spc_t:s0 key=(null)

在 Docker 的情况下,auid 是未设置的 (4294967295);这意味着安全官可能知道某个进程修改了 /etc/shadow 文件,但身份丢失了。

如果攻击者随后删除了 Docker 容器,则系统上将不会留下任何关于谁修改了 /etc/shadow 文件的痕迹。

现在让我们看看 Podman 的完全相同的情况。

$ sudo podman run --privileged -v /:/host fedora touch /host/etc/shadow

$ sudo ausearch -f /etc/shadow -i

type=PROCTITLE msg=audit(10/10/2018 10:23:41.659:4530) : proctitle=/usr/bin/coreutils

--coreutils-prog-shebang=touch /usr/bin/touch /host/etc/shadow

type=SYSCALL msg=audit(10/10/2018 10:23:41.659:4530) : arch=x86_64 syscall=openat

success=yes exit=3 a0=0xffffff9c a1=0x7fffdffd0f34 a2=O_WRONLY|O_CREAT|O_NOCTTY|

O_NONBLOCK a3=0x1b6 items=2 ppid=11671 pid=11683 auid=dwalsh uid=root gid=root

euid=root suid=root fsuid=root egid=root sgid=root fsgid=root tty=(none) ses=3 comm=touch

exe=/usr/bin/coreutils subj=unconfined_u:system_r:spc_t:s0 key=(null)

由于 Podman 使用传统的 fork/exec,一切都正确记录下来了。

这只是一个监视 /etc/shadow 文件的简单示例,但审计系统在监视进程在系统上执行的操作方面非常强大。使用 fork/exec 容器运行时来启动容器(而不是客户端/服务器容器运行时)允许您通过审计日志记录来维护更好的安全性。

最终想法

与客户端/服务器模型相比,fork/exec 模型在启动容器时还有许多其他优点。例如,systemd 功能包括

  • SD_NOTIFY: 如果您将 Podman 命令放入 systemd 单元文件中,容器进程可以通过 Podman 返回通知到堆栈,表明该服务已准备好接收任务。这是在客户端/服务器模式下无法完成的。
  • 套接字激活: 您可以将连接的套接字从 systemd 传递到 Podman,再传递到容器进程以供使用。这在客户端/服务器模型中是不可能的。

在我看来,最好的功能是以非 root 用户身份运行 Podman 和容器。这意味着您永远不必授予用户主机上的 root 权限,而在客户端/服务器模型(如 Docker 所采用的)中,您必须打开一个套接字连接到以 root 身份运行的特权守护进程才能启动容器。您在那里受制于守护进程中实施的安全机制,而不是主机操作系统中实施的安全机制——这是一个危险的主张。

User profile image.
Daniel Walsh 在计算机安全领域工作了近 30 年。Dan 于 2001 年 8 月加入红帽。

4 条评论

感谢 Dan 发布这篇文章。Podman 在 selinux 与 docker 方面也提供了优势吗?似乎 fork/exec 可以为容器更直接地进入 selinux 域。

嗯,我们可以更好地处理 Docker 守护进程的一些转换规则。但由于我在 Docker/Moby 中添加并维护了 SELinux 工作,我也希望它们保持一流水平。

回复 作者 david c (未验证)

感谢这篇文章。很好的解释,简单易懂 Podman 和 Docker 之间的区别。

有史以来最好的帖子

知识共享许可协议本作品根据知识共享署名-相同方式共享 4.0 国际许可协议获得许可。
© . All rights reserved.