标签 Podman 下的文章

在复杂的 IT 基础设施中,有许多重复性任务。成功运行这些任务并不容易。运行失败大多数是人为错误引发。在 Ansible 帮助下,你可以通过远程主机来执行所有任务,这些远程主机按照 行动手册 playbook 执行,行动手册可以根据需要重复使用多次。在本文中,你将学习如何在 Fedora Linux 上安装、配置 Ansible,以及如何使用它来管理、配置 Podman 容器。

Ansible

Ansible 是一个由红帽赞助的开源基础设施自动化工具。它可以处理大型基础设施带来的所有问题,例如安装和更新软件包、备份、确保特定服务持续运行等等。你用 YAML 写的行动手册来做这些事。Ansible 行动手册可以反复使用,使系统管理员的工作不那么复杂。行动手册减少了重复任务,并且可以轻松修改。但是我们有很多像 Ansible 一样的自动化工具,为什么要用它呢?与其他一些配置管理工具不同,Ansible 是无代理的:你不必在受管节点上安装任何东西。

Podman

Podman 是一个开源的容器引擎,用于开发、管理和运行容器镜像。但什么是容器呢?每当你创建任何新应用程序并将其部署在物理服务器、云服务器或虚拟机上时,你面临的最常见问题是可移植性和兼容性。这就是容器出现的原因。容器在操作系统级别上进行虚拟化,因此它们只包含所需的库和应用程序服务。容器的好处包括:

  • 便携性
  • 隔离性
  • 扩展性
  • 轻量级
  • 快速启动
  • 更小的磁盘和内存需求

简而言之:当你为任何应用程序构建容器镜像时,所有必需的依赖项都被打包到容器中。你现在可以在任何主机操作系统上运行该容器,没有任何可移植性和兼容性问题。

Podman 的关键亮点在于它没有守护程序,因此不需要 root 权限来运行容器。你可以借助 Dockerfile 构建容器镜像,或者从 Docker Hub、fedoraproject.orgQuay 上拉取镜像。

为什么用 Ansible 配置 Podman?

Ansible 提供了一种轻松多次运行重复任务的方法。它还为云提供商(如 AWS、GCP 和 Azure)、容器管理工具(如 Docker 和 Podman)与数据库管理提供了大量模块。Ansible 还有一个社区(Ansible Galaxy),在这里你可以找到大量 Ansible 角色 Roles ,它们由来自世界各地的贡献者创建。因为这些,Ansible 成为了 DevOps 工程师和系统管理员手中的好工具。

借助 DevOps,应用程序的开发步伐很快。开发的应用不局限于任意操作系统,这点至关重要。这就是 Podman 出现的地方。

安装 Ansible

首先,安装 Ansible:

$ sudo dnf install ansible -y

配置 Ansible

Ansible 需要在受管节点上运行 ssh,所以首先生成一个 密钥对 Key Pair

$ ssh-keygen

生成密钥后,将密钥复制到受管节点。

输入 yes,然后输入受管节点的密码。现在可以远程访问受管主机。

为了能够访问受管节点,你需要将所有主机名或 IP 地址存储在清单文件中。默认情况下,这是在 ~/etc/ansible/hosts

这是 库存 inventory 文件的样子。方括号用于将组分配给某些特定的节点。

[group1]
green.example.com
blue.example.com
[group2]
192.168.100.11
192.168.100.10

检查所有受管节点是否可以到达。

$ ansible all -m ping

你可以看到如下输出:

[mahesh@fedora new] $ ansible all -m ping
fedora.example.com I SUCCESS {
    "ansibe_facts": {
       "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}
[mahesh@fedora new] $

现在创建你的第一个 行动手册 playbook ,它将在受管节点上安装 Podman。首先用 .yml 拓展名创建一个任意名称的文件。

$ vim name_of_playbook.yml

行动手册应该如下所示。第一个字段是行动手册的名称。主机字段(hosts)用于提及清单中提到的主机名或组名。become: yes 表示升级权限,以及任务(tasks)包含所要执行的任务,这里的名称(name)指定任务(tasks)名称,yum 是安装软件包的模块,下面在名称字段(name)指定软件包名称,在状态字段(state)指定安装或删除软件包。

---
 - name: First playbook
   hosts: fedora.example.com
   become: yes
   tasks:
     - name: Installing podman.
       yum:
         name: podman
         state: present

检查文件中是否有语法错误:

$ ansible-playbook filename --syntax-check

现在运行行动手册:

$ ansible-playbook filename

你可以看到如下输出:

[mahesh@fedora new] $ ansible-playbook podman_installation.yml
PLAY [First playbook] *************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************
0k: [fedora.example.com]

TASK [Installing podman] ************************************************************************************************
changed: [fedora.example.com]

PLAY RECAP *************************************************************************************************
fedora.example.com    : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
[mahesh@fedora new] $

现在创建一个新的行动手册,从 Docker Hub 中拉取一个镜像。你将使用 podman_image 模块从 Docker Hub 中提取版本号为 2-alpine 的 httpd 镜像。

---
 - name: Playbook for podman.
    hosts: fedora.example.com
    tasks:
     - name: Pull httpd:2-alpine image from dockerhub.
       podman_image:
         name: docker.io/httpd
         tag: 2-alpine

现在检查已拉取的镜像:

[mahesh@fedora new] $ podman images
REPOSITORY                           TAG                  IMAGE ID           CREATED             SIZE
docker.io/library/httpd       2-alpine         fa848876521a    11 days ago        57 MB

[mahesh@fedora new] $

创建一个新的行动手册来运行 httpd 镜像。更多信息请查看 podman\_container 模块文档。

---
 - name: Playbook for podman.
   hosts: fedora.example.com
   tasks:
     - name: Running httpd image.
       containers.podman.podman_container:
         name: my-first-container
         image:  docker.io/httpd:2-alpine
         state: started

检查容器运行状态。

[mahesh@fedora new] $ podman ps
CONTAINER ID        IMAGE    COMMAND   CREATED      STATUS         PORTS         NAMES
45d966eOe207     docker.io/library/httpd:2-alpine    httpd-foreground    13 seconds ago    Up 13 seconds ago       my-first-container

[mahesh@fedora new] $

现在停止已运行的容器,改变状态,由 started 变为 absent

- name: Stopping httpd container.
  containers.podman.podman_container:
    name: my-first-container
    image: docker.io/httpd:2-alpine
    state: absent

当你执行 podman ps 命令时,你看不到任何运行的容器。

[mahesh@fedora new] $ podman ps
CONTAINER ID    IMAGE    COMMAND    CREATED    STATUS    PORTS    NAMES

[mahesh@fedora new] $

podman_container 可以做很多事情,例如重新创建容器、重新启动容器、检查容器是否正在运行等等。有关执行这些操作的信息,请参考 文档


via: https://fedoramagazine.org/using-ansible-to-configure-podman-containers/

作者:mahesh1b 选题:lujun9972 译者:DCOLIVERSUN 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

容器很棒,让你可以将你的应用连同其依赖项一起打包,并在任何地方运行。从 2013 年的 Docker 开始,容器已经让软件开发者的生活变得更加轻松。

Docker 的一个缺点是它有一个中央守护进程,它以 root 用户的身份运行,这对安全有影响。但这正是 Podman 的用武之地。Podman 是一个 无守护进程容器引擎,用于开发、管理和在你的 Linux 系统上以 root 或无 root 模式运行 OCI 容器。

下面这些文章可以用来了解更多关于 Podman 的信息:

如果你使用过 Docker,你很可能也知道 Docker Compose,它是一个用于编排多个可能相互依赖的容器的工具。要了解更多关于 Docker Compose 的信息,请看它的文档

什么是 Podman Compose?

Podman Compose 项目的目标是作为 Docker Compose 的替代品,而不需要对 docker-compose.yaml 文件进行任何修改。由于 Podman Compose 使用 吊舱 pod 工作,所以最好看下“吊舱”的最新定义。

一个“ 吊舱 pod ”(如一群鲸鱼或豌豆荚)是由一个或多个容器组成的组,具有共享的存储/网络资源,以及如何运行容器的规范。

Pods - Kubernetes 文档

(LCTT 译注:容器技术领域大量使用了航海比喻,pod 一词,意为“豆荚”,在航海领域指“吊舱” —— 均指盛装多个物品的容器。常不翻译,考虑前后文,可译做“吊舱”。)

Podman Compose 的基本思想是,它选中 docker-compose.yaml 文件里面定义的服务,为每个服务创建一个容器。Docker Compose 和 Podman Compose 的一个主要区别是,Podman Compose 将整个项目的容器添加到一个单一的吊舱中,而且所有的容器共享同一个网络。如你在例子中看到的,在创建容器时使用 --add-host 标志,它甚至用和 Docker Compose 一样的方式命名容器。

安装

Podman Compose 的完整安装说明可以在项目页面上找到,它有几种方法。要安装最新的开发版本,使用以下命令:

pip3 install https://github.com/containers/podman-compose/archive/devel.tar.gz

确保你也安装了 Podman,因为你也需要它。在 Fedora 上,使用下面的命令来安装Podman:

sudo dnf install podman

例子:用 Podman Compose 启动一个 WordPress 网站

想象一下,你的 docker-compose.yaml 文件在一个叫 wpsite 的文件夹里。一个典型的 WordPress 网站的 docker-compose.yaml (或 docker-compose.yml) 文件是这样的:

version: "3.8"
services:
  web:
    image: wordpress
    restart: always
    volumes:
      - wordpress:/var/www/html
    ports:
      - 8080:80
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: magazine
      WORDPRESS_DB_NAME: magazine
      WORDPRESS_DB_PASSWORD: 1maGazine!
      WORDPRESS_TABLE_PREFIX: cz
      WORDPRESS_DEBUG: 0
    depends_on:
      - db
    networks:
      - wpnet
  db:
    image: mariadb:10.5
    restart: always
    ports:
      - 6603:3306

    volumes:
      - wpdbvol:/var/lib/mysql

    environment:
      MYSQL_DATABASE: magazine
      MYSQL_USER: magazine
      MYSQL_PASSWORD: 1maGazine!
      MYSQL_ROOT_PASSWORD: 1maGazine!
    networks:
      - wpnet
volumes:
  wordpress: {}
  wpdbvol: {}

networks:
  wpnet: {}

如果你用过 Docker,你就会知道你可运行 docker-compose up 来启动这些服务。Docker Compose 会创建两个名为 wpsite_web_1wpsite_db_1 的容器,并将它们连接到一个名为 wpsite_wpnet 的网络。

现在,看看当你在项目目录下运行 podman-compose up 时会发生什么。首先,一个以执行命令的目录命名的吊舱被创建。接下来,它寻找 YAML 文件中定义的任何名称的卷,如果它们不存在,就创建卷。然后,在 YAML 文件的 services 部分列出的每个服务都会创建一个容器,并添加到吊舱中。

容器的命名与 Docker Compose 类似。例如,为你的 web 服务创建一个名为 wpsite_web_1 的容器。Podman Compose 还为每个命名的容器添加了 localhost 别名。之后,容器仍然可以通过名字互相解析,尽管它们并不像 Docker 那样在一个桥接网络上。要做到这一点,使用选项 -add-host。例如,-add-host web:localhost

请注意,docker-compose.yaml 包含了一个从主机 8080 端口到容器 80 端口的 Web 服务的端口转发。现在你应该可以通过浏览器访问新 WordPress 实例,地址为 http://localhost:8080

WordPress Dashboard

控制 pod 和容器

要查看正在运行的容器,使用 podman ps,它可以显示 web 和数据库容器以及吊舱中的基础设施容器。

CONTAINER ID  IMAGE                               COMMAND               CREATED      STATUS          PORTS                                         NAMES
a364a8d7cec7  docker.io/library/wordpress:latest  apache2-foregroun...  2 hours ago  Up 2 hours ago  0.0.0.0:8080->80/tcp, 0.0.0.0:6603->3306/tcp  wpsite_web_1
c447024aa104  docker.io/library/mariadb:10.5      mysqld                2 hours ago  Up 2 hours ago  0.0.0.0:8080->80/tcp, 0.0.0.0:6603->3306/tcp  wpsite_db_1
12b1e3418e3e  k8s.gcr.io/pause:3.2

你也可以验证 Podman 已经为这个项目创建了一个吊舱,以你执行命令的文件夹命名。

POD ID        NAME             STATUS    CREATED      INFRA ID      # OF CONTAINERS
8a08a3a7773e  wpsite           Degraded  2 hours ago  12b1e3418e3e  3

要停止容器,在另一个命令窗口中输入以下命令:

podman-compose down

你也可以通过停止和删除吊舱来实现。这实质上是停止并移除所有的容器,然后再删除包含的吊舱。所以,同样的事情也可以通过这些命令来实现:

podman pod stop podname
podman pod rm podname

请注意,这不会删除你在 docker-compose.yaml 中定义的卷。所以,你的 WordPress 网站的状态被保存下来了,你可以通过运行这个命令来恢复它。

podman-compose up

总之,如果你是一个 Podman 粉丝,并且用 Podman 做容器工作,你可以使用 Podman Compose 来管理你的开发和生产中的容器。


via: https://fedoramagazine.org/manage-containers-with-podman-compose/

作者:Mehdi Haghgoo 选题:lujun9972 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

容器化是一项蓬勃发展的技术。在不久的将来,多达百分之七十五的全球组织可能会运行某种类型的容器化技术。由于广泛使用的技术更容易成为黑客攻击的目标,因此保护容器的安全就显得尤为重要。本文将演示如何使用 POSIX 权能 Capability 来保护 Podman 容器的安全。Podman 是 RHEL8 中默认的容器管理工具。

确定 Podman 容器的权限模式

容器以特权模式或无特权模式运行。在特权模式下,容器的 uid 0 被映射到宿主机的 uid 0。对于某些情况,无特权的容器缺乏对宿主机资源的充分访问能力。但不管其操作模式如何,包括 强制访问控制 Mandatory Access Control (MAC:如 apparmor、SELinux 等)、seccomp 过滤器、删除 权能 Capability 、命名空间等在内的技术有助于确保容器的安全。

要从容器外部确定特权模式:

$ podman inspect --format="{{.HostConfig.Privileged}}" <container id>

如果上面的命令返回 true,那么容器在特权模式下运行。如果返回 false,那么容器在非特权模式下运行。

要从容器内部确定特权模式:

$ ip link add dummy0 type dummy

如果该命令允许你创建一个接口,那么你运行的是一个特权容器,否则你运行的是一个非特权容器。

权能

命名空间隔离了容器的进程,使其无法任意访问宿主机的资源,也无法访问在同一宿主机上运行的其他容器的资源。然而,在特权容器内的进程仍然可以做一些事情,如改变 IP 路由表、跟踪任意进程和加载内核模块。 权能 Capability 允许人们对容器内的进程可以访问或更改的资源施加更细微的限制,即使容器在特权模式下运行也一样。权能还允许人们为无特权的容器分配它本来不会拥有的特权。

例如,如果要将 NET_ADMIN 功能添加到一个无特权的容器中,以便在容器内部创建一个网络接口,你可以用下面的参数运行 podman

[root@vm1 ~]# podman run -it --cap-add=NET_ADMIN centos
[root@b27fea33ccf1 /]# ip link add dummy0 type dummy
[root@b27fea33ccf1 /]# ip link

上面的命令演示了在一个无特权的容器中创建一个 dummy0 接口。如果没有 NET_ADMIN 权能,非特权容器将无法创建接口。上面的命令演示了如何将一个权能授予一个无特权的容器。

目前,大约有 39 种权能可以被授予或拒绝。特权容器默认会被授予许多权能。建议从特权容器中删除不需要的权能,以使其更加安全。

要从容器中删除所有权能:

$ podman run -it -d --name mycontainer --cap-drop=all centos

列出一个容器的权能:

$ podman exec -it 48f11d9fa512 capsh --print

上述命令显示没有向容器授予任何权能。

请参考 capabilities 手册页以获取完整的权能列表:

$ man capabilities

可以使用 capsh 命令来列出目前拥有的权能:

$ capsh --print

作为另一个例子,下面的命令演示了如何从容器中删除 NET_RAW 权能。如果没有 NET_RAW 权能,就不能从容器中 ping 互联网上的服务器。

$ podman run -it --name mycontainer1 --cap-drop=net_raw centos
>>> ping google.com (will output error, operation not permitted)

最后一个例子,如果你的容器只需要 SETUIDSETGID 权能,你可以删除所有权能,然后只重新添加这两个权能来实现这样的权限设置。

$ podman run -d --cap-drop=all --cap-add=setuid --cap-add=setgid fedora sleep 5 > /dev/null; pscap | grep sleep

上面的 pscap 命令会显示容器被授予的权能。

我希望你喜欢这个关于如何使用权能来保护 Podman 容器的简短探索。

谢谢!


via: https://fedoramagazine.org/podman-with-capabilities-on-fedora/

作者:shiwanibiradar 选题:lujun9972 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

了解如何使用 Podman 在单独的用户空间运行容器。

Podmanlibpod 库的一部分,使用户能够管理 pod、容器和容器镜像。在我的上一篇文章中,我写过将 Podman 作为一种更安全的运行容器的方式。在这里,我将解释如何使用 Podman 在单独的用户命名空间中运行容器。

作为分离容器的一个很棒的功能,我一直在思考 用户命名空间 user namespace ,它主要是由 Red Hat 的 Eric Biederman 开发的。用户命名空间允许你指定用于运行容器的用户标识符(UID)和组标识符(GID)映射。这意味着你可以在容器内以 UID 0 运行,在容器外以 UID 100000 运行。如果容器进程逃逸出了容器,内核会将它们视为以 UID 100000 运行。不仅如此,任何未映射到用户命名空间的 UID 所拥有的文件对象都将被视为 nobody 所拥有(UID 是 65534, 由 kernel.overflowuid 指定),并且不允许容器进程访问,除非该对象可由“其他人”访问(即世界可读/可写)。

如果你拥有一个权限为 660 的属主为“真实” root 的文件,而当用户命名空间中的容器进程尝试读取它时,会阻止它们访问它,并且会将该文件视为 nobody 所拥有。

示例

以下是它是如何工作的。首先,我在 root 拥有的系统中创建一个文件。

$ sudo bash -c "echo Test > /tmp/test"
$ sudo chmod 600 /tmp/test
$ sudo ls -l /tmp/test
-rw-------. 1 root root 5 Dec 17 16:40 /tmp/test

接下来,我将该文件卷挂载到一个使用用户命名空间映射 0:100000:5000 运行的容器中。

$ sudo podman run -ti -v /tmp/test:/tmp/test:Z --uidmap 0:100000:5000 fedora sh
# id
uid=0(root) gid=0(root) groups=0(root)
# ls -l /tmp/test
-rw-rw----. 1 nobody nobody 8 Nov 30 12:40 /tmp/test
# cat /tmp/test
cat: /tmp/test: Permission denied

上面的 --uidmap 设置告诉 Podman 在容器内映射一系列的 5000 个 UID,从容器外的 UID 100000 开始的范围(100000-104999)映射到容器内 UID 0 开始的范围(0-4999)。在容器内部,如果我的进程以 UID 1 运行,则它在主机上为 100001。

由于实际的 UID=0 未映射到容器中,因此 root 拥有的任何文件都将被视为 nobody 所拥有。即使容器内的进程具有 CAP_DAC_OVERRIDE 能力,也无法覆盖此种保护。DAC_OVERRIDE 能力使得 root 的进程能够读/写系统上的任何文件,即使进程不是 root 用户拥有的,也不是全局可读或可写的。

用户命名空间的功能与宿主机上的功能不同。它们是命名空间的功能。这意味着我的容器的 root 只在容器内具有功能 —— 实际上只有该范围内的 UID 映射到内用户命名空间。如果容器进程逃逸出了容器,则它将没有任何非映射到用户命名空间的 UID 之外的功能,这包括 UID=0。即使进程可能以某种方式进入另一个容器,如果容器使用不同范围的 UID,它们也不具备这些功能。

请注意,SELinux 和其他技术还限制了容器进程破开容器时会发生的情况。

使用 podman top 来显示用户名字空间

我们在 podman top 中添加了一些功能,允许你检查容器内运行的进程的用户名,并标识它们在宿主机上的真实 UID。

让我们首先使用我们的 UID 映射运行一个 sleep 容器。

$ sudo podman run --uidmap 0:100000:5000 -d fedora sleep 1000

现在运行 podman top

$ sudo podman top --latest user huser
USER   HUSER
root   100000

$ ps -ef | grep sleep
100000   21821 21809  0 08:04 ?         00:00:00 /usr/bin/coreutils --coreutils-prog-shebang=sleep /usr/bin/sleep 1000

注意 podman top 报告用户进程在容器内以 root 身份运行,但在宿主机(HUSER)上以 UID 100000 运行。此外,ps 命令确认 sleep 过程以 UID 100000 运行。

现在让我们运行第二个容器,但这次我们将选择一个单独的 UID 映射,从 200000 开始。

$ sudo podman run --uidmap 0:200000:5000 -d fedora sleep 1000
$ sudo podman top --latest user huser
USER   HUSER
root   200000

$ ps -ef | grep sleep
100000   21821 21809  0 08:04 ?         00:00:00 /usr/bin/coreutils --coreutils-prog-shebang=sleep /usr/bin/sleep 1000
200000   23644 23632  1 08:08 ?         00:00:00 /usr/bin/coreutils --coreutils-prog-shebang=sleep /usr/bin/sleep 1000

请注意,podman top 报告第二个容器在容器内以 root 身份运行,但在宿主机上是 UID=200000。

另请参阅 ps 命令,它显示两个 sleep 进程都在运行:一个为 100000,另一个为 200000。

这意味着在单独的用户命名空间内运行容器可以在进程之间进行传统的 UID 分离,而这从一开始就是 Linux/Unix 的标准安全工具。

用户名字空间的问题

几年来,我一直主张用户命名空间应该作为每个人应该有的安全工具,但几乎没有人使用过。原因是没有任何文件系统支持,也没有一个 移动文件系统 shifting file system

在容器中,你希望在许多容器之间共享基本镜像。上面的每个示例中使用了 Fedora 基本镜像。Fedora 镜像中的大多数文件都由真实的 UID=0 拥有。如果我在此镜像上使用用户名称空间 0:100000:5000 运行容器,默认情况下它会将所有这些文件视为 nobody 所拥有,因此我们需要移动所有这些 UID 以匹配用户名称空间。多年来,我想要一个挂载选项来告诉内核重新映射这些文件 UID 以匹配用户命名空间。上游内核存储开发人员还在继续研究,在此功能上已经取得一些进展,但这是一个难题。

由于由 Nalin Dahyabhai 领导的团队开发的自动 chown 内置于容器/存储中,Podman 可以在同一镜像上使用不同的用户名称空间。当 Podman 使用容器/存储,并且 Podman 在新的用户命名空间中首次使用一个容器镜像时,容器/存储会 “chown”(如,更改所有权)镜像中的所有文件到用户命名空间中映射的 UID 并创建一个新镜像。可以把它想象成一个 fedora:0:100000:5000 镜像。

当 Podman 在具有相同 UID 映射的镜像上运行另一个容器时,它使用“预先 chown”的镜像。当我在0:200000:5000 上运行第二个容器时,容器/存储会创建第二个镜像,我们称之为 fedora:0:200000:5000

请注意,如果你正在执行 podman buildpodman commit 并将新创建的镜像推送到容器注册库,Podman 将使用容器/存储来反转该移动,并将推送所有文件属主变回真实 UID=0 的镜像。

这可能会导致在新的 UID 映射中创建容器时出现真正的减速,因为 chown 可能会很慢,具体取决于镜像中的文件数。此外,在普通的 OverlayFS 上,镜像中的每个文件都会被复制。普通的 Fedora 镜像最多可能需要 30 秒才能完成 chown 并启动容器。

幸运的是,Red Hat 内核存储团队(主要是 Vivek Goyal 和 Miklos Szeredi)在内核 4.19 中为 OverlayFS 添加了一项新功能。该功能称为“仅复制元数据”。如果使用 metacopy=on 选项来挂载层叠文件系统,则在更改文件属性时,它不会复制较低层的内容;内核会创建新的 inode,其中包含引用指向较低级别数据的属性。如果内容发生变化,它仍会复制内容。如果你想试用它,可以在 Red Hat Enterprise Linux 8 Beta 中使用此功能。

这意味着容器 chown 可能在两秒钟内发生,并且你不会倍增每个容器的存储空间。

这使得像 Podman 这样的工具在不同的用户命名空间中运行容器是可行的,大大提高了系统的安全性。

前瞻

我想向 Podman 添加一个新选项,比如 --userns=auto,它会为你运行的每个容器自动选择一个唯一的用户命名空间。这类似于 SELinux 与单独的多类别安全(MCS)标签一起使用的方式。如果设置环境变量 PODMAN_USERNS=auto,则甚至不需要设置该选项。

Podman 最终允许用户在不同的用户名称空间中运行容器。像 BuildahCRI-O 这样的工具也可以利用用户命名空间。但是,对于 CRI-O,Kubernetes 需要了解哪个用户命名空间将运行容器引擎,上游正在开发这个功能。

在我的下一篇文章中,我将解释如何在用户命名空间中将 Podman 作为非 root 用户运行。


via: https://opensource.com/article/18/12/podman-and-user-namespaces

作者:Daniel J Walsh 选题:lujun9972 译者:wxy 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

Podman 使用传统的 fork/exec 模型(相对于客户端/服务器模型)来运行容器。

在进入本文的主要主题 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

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

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

我(dwalsh)的 UID 是 3267。

$ cat /proc/self/loginuid
3267

现在,即使我变成了 root,我的登录 UID 仍将保持不变。

$ sudo cat /proc/self/loginuid
3267

请注意,从初始登录过程 fork 并 exec 的每个进程都会自动继承 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(LCTT 译注:2 32 - 1)。由于容器是 Docker 守护程序的后代,而 Docker 守护程序是 init 系统的子代,所以,我们看到 systemd、Docker 守护程序和容器进程全部具有相同的 loginuid4294967295,审计系统视其为未设置审计 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)

由于它使用传统的 fork/exec 方式,因此 Podman 正确记录了所有内容。

这只是观察 /etc/shadow 文件的一个简单示例,但审计系统对于观察系统上的进程非常有用。使用 fork/exec 容器运行时(而不是客户端/服务器容器运行时)来启动容器允许你通过审计日志记录保持更好的安全性。

最后的想法

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

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

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


via: https://opensource.com/article/18/10/podman-more-secure-way-run-containers

作者:Daniel J Walsh 选题:lujun9972 译者:wxy 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出