,

如何在容器中使用 Podman

您是否曾经想过在容器中运行 Podman:Podman 在 Podman 中、Podman 在 Docker 中,甚至 Podman 在 Kubernetes 中?

对于从事上游容器技术的人来说,最常被问到的主题之一是在容器中运行 Podman。过去,这主要与 Docker in Docker (DIND) 相关,但现在,人们也希望在 Podman 中运行 Podman (PINP) 或在 Docker 中运行 Podman (PIND)。

但是 Podman 可以通过多种方式运行,有 rootful 和 rootless 两种模式。我们最终会遇到人们想要运行 rootful 和 rootless Podman 的各种组合。

  • rootful Podman 在 rootful Podman 中
  • rootless Podman 在 rootful Podman 中
  • rootful Podman 在 rootless Podman 中
  • rootless Podman 在 rootless Podman 中

您明白了我的意思。

这篇博文将尝试涵盖每种组合,首先讨论特权。我们将在第一部分中开始讨论 PINP 场景。在该系列的第二部分中,我们将介绍类似的内容,但将在 Kubernetes 上下文 中进行。请务必阅读这两篇文章以全面了解情况。

容器引擎需要特权

为了在容器中运行像 Podman 这样的容器引擎,您首先需要了解的是,您需要相当多的特权。

  • 容器需要多个 UID。大多数容器映像需要多个 UID 才能正常工作。例如,您的映像中可能大部分文件归 root 所有,但有些归 apache 用户 (UID=60) 所有。
  • 容器引擎挂载文件系统并使用系统调用 clone 来创建用户命名空间。

注意:您可能需要更新版本的 Podman。本博文中的示例是在 Podman 3.2 中运行的。

我们的测试映像

对于本博文中的示例,我们将使用 quay.io/podman/stable 映像,该映像的构建目标是找到在容器中运行 Podman 的最佳方式。您可以查看我们在 github.com 仓库 中的 Dockerfile 和 containers.conf 映像中是如何构建该映像的。

# stable/Dockerfile
#
# Build a Podman container image from the latest
# stable version of Podman on the Fedoras Updates System.
# https://bodhi.fedoraproject.org/updates/?search=podman
# This image can be used to create a secured container
# that runs safely with privileges within the container.
#
FROM registry.fedoraproject.org/fedora:latest

# Don't include container-selinux and remove
# directories used by yum that are just taking
# up space.
RUN dnf -y update; yum -y reinstall shadow-utils; \
yum -y install podman fuse-overlayfs --exclude container-selinux; \
rm -rf /var/cache /var/log/dnf* /var/log/yum.*

RUN useradd podman; \
echo podman:10000:5000 > /etc/subuid; \
echo podman:10000:5000 > /etc/subgid;

VOLUME /var/lib/containers
VOLUME /home/podman/.local/share/containers

ADD https://raw.githubusercontent.com/containers/libpod/master/contrib/podmanimage/stable/containers.conf /etc/containers/containers.conf
ADD https://raw.githubusercontent.com/containers/libpod/master/contrib/podmanimage/stable/podman-containers.conf /home/podman/.config/containers/containers.conf

RUN chown podman:podman -R /home/podman

# chmod containers.conf and adjust storage.conf to enable Fuse storage.
RUN chmod 644 /etc/containers/containers.conf; sed -i -e 's|^#mount_program|mount_program|g' -e '/additionalimage.*/a "/var/lib/shared",' -e 's|^mountopt[[:space:]]*=.*$|mountopt = "nodev,fsync=0"|g' /etc/containers/storage.conf
RUN mkdir -p /var/lib/shared/overlay-images /var/lib/shared/overlay-layers /var/lib/shared/vfs-images /var/lib/shared/vfs-layers; touch /var/lib/shared/overlay-images/images.lock; touch /var/lib/shared/overlay-layers/layers.lock; touch /var/lib/shared/vfs-images/images.lock; touch /var/lib/shared/vfs-layers/layers.lock

ENV _CONTAINERS_USERNS_CONFIGURED=""

让我们检查一下 Dockerfile。

FROM registry.fedoraproject.org/fedora:latest

# Don't include container-selinux and remove
# directories used by yum that are just taking
# up space.
RUN dnf -y update; yum -y reinstall shadow-utils; \
yum -y install podman fuse-overlayfs --exclude container-selinux; \
rm -rf /var/cache /var/log/dnf* /var/log/yum.*

首先拉取 fedora 最新版本,然后更新到最新软件包。请注意,它重新安装了 shadow-utils,因为在 Fedora 映像的 shadow-utils 安装中存在一个已知问题,其中 newsubuidnewsubgid 上的 filecaps 未设置。重新安装 shadow-utils 可以解决此问题。接下来,安装 Podman 以及 fuse-overlayfs。我们不安装 container-selinux,因为它在容器中不需要。

RUN useradd podman; \
echo podman:10000:5000 > /etc/subuid; \
echo podman:10000:5000 > /etc/subgid;

接下来,我创建一个用户 podman 并设置 /etc/subuid/etc/subgid 文件以使用 5000 个 UID。这用于在容器中设置用户命名空间。5000 是一个任意数字,可能太小了。我们选择这个数字是因为它小于分配给 rootless 用户的 65k。如果您只以 root 身份运行容器,65k 会是一个更好的数字。

VOLUME /var/lib/containers
VOLUME /home/podman/.local/share/containers

由于我们可以使用此映像运行 rootful 和 rootless 容器,因此我们创建了两个卷。rootful Podman 使用 /var/lib/containers 作为其容器存储,rootless 使用 /home/podman/.local/share/containers。内核通常不允许叠加在叠加上,因此这将创建将在容器中使用的非叠加卷。

ADD https://raw.githubusercontent.com/containers/libpod/master/contrib/podmanimage/stable/containers.conf /etc/containers/containers.conf
ADD https://raw.githubusercontent.com/containers/libpod/master/contrib/podmanimage/stable/podman-containers.conf /home/podman/.config/containers/containers.conf

我已经预先配置了两个 containers.conf 文件,以确保容器在每种模式下都能更轻松地运行。

该映像设置为默认情况下使用 fuse-overlayfs 运行。在某些情况下,您可以为 rootful 模式运行内核的叠加文件系统,并且您很快就能在 rootless 模式下执行此操作。但是,目前,我们使用 fuse-overlayfs 作为容器在容器中的存储。其他人使用过 VFS 存储驱动程序,但效率并不高。

–privileged 标志

在容器中运行 Podman 的最简单方法是使用 --privileged 标志。

rootful Podman 在 rootful Podman 中使用 –privileged

# podman run --privileged quay.io/podman/stable podman run ubi8 echo hello
Resolved "ubi8-minimal" as an alias (/etc/containers/registries.conf.d/shortnames.conf)
Trying to pull registry.access.redhat.com/ubi8:latest...
Getting image source signatures
Copying blob sha256:a591faa84ab05242a17131e396a336da172b0e1ec66d921c9f130b7c4c24586d
Copying blob sha256:76b9354adec626b01ffb0faae4a217cebd616661fd90c4b54ba4415f53392fb8
Copying config sha256:dc080723f596f2407300cca2c19a17accad89edcf39f7b8b33e6472dd41e30f1
Writing manifest to image destination
Storing signatures
hello

为了节省时间,由于我将进行大量实验,因此我在主机上创建了一个目录 ./mycontainers,我将该目录挂载到容器中以供使用,并且不必每次都拉取映像。

# podman run --privileged -v ./mycontainers:/var/lib/containers quay.io/podman/stable podman run ubi8 echo hello
hello

rootless Podman 在 rootful Podman 中使用 –privileged

quay.io/podman/stable 映像设置了 podman 用户,您可以使用该用户来运行 rootless 容器。

# podman run --user podman --privileged quay.io/podman/stable podman run ubi8 echo hello
Resolved "ubi8" as an alias (/etc/containers/registries.conf.d/shortnames.conf)
...
hello

请注意,在这种情况下,在容器中运行的 Podman 正在以用户 podman 身份运行。这是因为容器化的 Podman 使用用户命名空间在特权容器中创建一个受限的容器。

在 Docker 中运行 rootless Podman 使用 –privileged

与 rootful Podman 类似,您还可以使用 --privileged 选项在 Docker 中运行 rootless Podman。

# docker run --privileged quay.io/podman/stable podman run ubi8 echo hello

rootless Podman 与 Docker

# docker run --user podman --privileged quay.io/podman/stable podman run ubi8 echo hello
Resolved "ubi8" as an alias (/etc/containers/registries.conf.d/shortnames.conf)
...
hello

我们能更安全地做到这一点吗?

请注意,即使我们在上面运行外部容器时使用了 --privileged,但内部容器正在受限模式下运行。在容器中运行的 rootless Podman 实际上是受限的,并且很难逃逸。鉴于此,我不喜欢使用 --privileged 标志。我认为我们可以在安全方面做得更好。

在没有 –privileged 标志的情况下运行

让我们看看如何删除 --privileged 标志以提高安全性。

rootful Podman 在 rootful Podman 中没有 –privileged

# podman run --cap-add=sys_admin,mknod --device=/dev/fuse --security-opt label=disable quay.io/podman/stable podman run ubi8-minimal echo hello
hello

我们可以从 rootful Podman 中删除 --privileged 标志,但仍然必须禁用一些安全功能才能使容器中的 rootful Podman 工作。

  1. 功能:--cap-add=sys_admin,mknod 我们需要添加两个 Linux 功能。
    1. CAP_SYS_ADMIN 是容器中以 root 身份运行的 Podman 挂载所需文件系统所必需的。
    2. CAP_MKNOD 是容器中以 root 身份运行的 Podman 创建 /dev 中的设备所必需的。(请注意,Docker 默认情况下允许这样做)。
  2. 设备:--device /dev/fuse 标志必须在容器中使用 fuse-overlayfs。此选项告诉主机上的 Podman 将 /dev/fuse 添加到容器中,以便容器化的 Podman 可以使用它。
  3. 禁用 SELinux:--security-opt label=disable 选项告诉主机的 Podman 为容器禁用 SElinux 分离。SELinux 不允许容器化进程挂载运行容器所需的所有文件系统。

rootful Podman 在 Docker 中没有 –privileged

# docker run --cap-add=sys_admin --cap-add mknod --device=/dev/fuse --security-opt seccomp=unconfined --security-opt label=disable quay.io/podman/stable podman run ubi8-minimal echo hello
hello
  1. 请注意,Docker 不支持用逗号分隔的 --cap-add 命令,因此我不得不分别添加 sys_adminmknod
  2. 仍然需要 --device /dev/fuse,因为容器默认使用 /dev/fuse
  3. Docker 始终将内置卷创建为 root:root 所有,因此我们需要创建一个卷来挂载到容器中的 Podman,以便它可以用于存储。
  4. 与往常一样,我需要禁用 SELinux 分离。
  5. 还需要禁用 seccomp,因为 Docker 的 seccomp 策略比 Podman 严格一些。您可以只使用 Podman 安全策略,方法是使用--seccomp=/usr/share/containers/seccomp.json
# docker run --cap-add=sys_admin --cap-add mknod --device=/dev/fuse --security-opt seccomp=/usr/share/containers/seccomp.json --security-opt label=disable quay.io/podman/stable podman run ubi8-minimal echo hello
hello

rootless Podman 在 rootful Podman 中没有 –privileged

使用用户命名空间,以非 root 用户身份运行特权容器,其中包含 Podman。

# podman run --user podman --security-opt label=disable --security-opt unmask=ALL --device /dev/fuse -ti quay.io/podman/stable podman run -ti docker.io/busybox echo hello
hello
  1. 请注意,与之前的 rooful 在 rooful 中的情况不同,我们不必添加危险的安全功能 sys_adminmknod
  2. 在这种情况下,我正在使用 --user podman 运行,这会自动导致容器中的 Podman 在用户命名空间中运行。
  3. 仍然禁用 SELinux,因为它会阻止挂载。
  4. 仍然需要 --device /dev/fuse 以在容器中使用 fuse-overlayfs

Podman-remote 在 rootful Podman 中,主机上泄漏了 Podman 套接字

# podman run -v /run:/run --security-opt label=disable quay.io/podman/stable podman --remote run busybox echo hi
hi

在这种情况下,我们将主机的 /run 目录泄漏到容器中。这允许 podman --remote 与主机上的 Podman 套接字通信,并在主机操作系统上启动容器。这通常是人们执行 Docker In Docker 的方式,尤其是 Docker 构建。您也可以通过这种方式执行 Podman 构建,并利用之前拉取到系统的映像。

但是,请注意,这极其不安全。容器中的进程可以完全接管主机。

  1. 您仍然需要禁用 SELinux 分离,因为 SELinux 会阻止容器进程使用 /run 中泄漏的套接字。
  2. podman --remote 标志用于告诉 Podman 在远程模式下工作。请注意,您也可以将 podman-remote 可执行文件安装到容器中并使用它。

[ 容器入门?查看此免费课程。 部署容器化应用程序:技术概述。 ]

Podman-remote 在 Docker 中,主机上泄漏了 Podman 套接字

# docker run -v /run:/run --security-opt label=disable quay.io/podman/stable podman --remote run busybox echo hi
hi

相同的示例适用于 Docker 容器。

此示例显示了一个完全受限的容器(除了 SELinux 被禁用之外),其中 Podman 套接字被泄漏到容器中。SELinux 会阻止此访问,因为它应该这样做。

# /bin/podman run --security-opt=label=disable -v /run/podman:/run/podman quay.io/podman/stable podman --remote run alpine echo hi
hi

rootless Podman 与容器化的 rootful Podman

$ podman run --privileged quay.io/podman/stable podman run ubi8 echo hello
Resolved "ubi8" as an alias (/etc/containers/registries.conf.d/shortnames.conf)
..
hello

rootless Podman 运行 rootless Podman

$ podman run --security-opt label=disable --user podman --device /dev/fuse quay.io/podman/stable podman run alpine echo hello

最后的想法

现在您对 Podman 在 Podman 中的选项有一些了解,包括使用 rootful 和 rootless 模式,以各种组合。您还更好地了解了必要的特权以及围绕 --privileged 标志的注意事项。

本系列的第二部分探讨了Podman 和 Kubernetes的用法。本文涵盖了类似的领域,但是在 Kubernetes 的背景下。

图片来自 Luisella Planeta LeoniPixabay

发表评论

订阅

使用您的电子邮件地址注册,以便通过电子邮件接收来自本网站的更新。


类别


搜索