ルートレスのPodmanはどのように動作しますか?

Podmanがどのようにしてユーザ名前空間を利用してルートレスモードで動作するのかを説明します。

この記事は How does rootless Podman work? | opensource.com を翻訳したものです。RHEL 7.8以降およびRHEL 8.1以降ではこの記事で紹介されているユーザ名前空間を活用した rootless container がフルサポートとなっていて、利用できます。

:::11 Feb 2019 Daniel J Walsh:::

f:id:mrwk:20200407164016p:plain

Image by: Opensource.com 

前回のユーザ名前空間とPodmanについての記事では、Podmanコマンドを使って異なるユーザ名前空間で異なるコンテナを起動し、コンテナ間の分離を改善する方法について説明しました。Podmanはまた、ユーザ名前空間を利用してルートレスモードで実行できるようにしています。基本的に、非特権ユーザがPodmanを実行すると、ユーザ名前空間を設定してそこへ参加します。Podmanがユーザ名前空間内でrootになった後、Podmanは特定のファイルシステムをマウントし、コンテナをセットアップすることができます。ここでは、以下で説明するユーザが利用可能なUIDを追加する以外には、特権のエスカレーションはありません。

Podmanはどのようにしてユーザ名前空間を作成するのですか?

shadow-utils

現在のほとんどの Linux ディストリビューションには、/etc/subuid/etc/subgid ファイルを使用して、ユーザ名前空間内のユーザが利用できる UID と GID を決定する shadow-utils の実装が含まれています。

$ cat /etc/subuid
dwalsh:100000:65536
test:165536:65536
$ cat /etc/subgid
dwalsh:100000:65536
test:165536:65536

useraddプログラムは、システムに追加されたユーザごとに65536個の UIDを自動的に割り当てます。システムに既存のユーザがいる場合は、自分でUIDを割り当てる必要があります。これらのファイルの形式は、username:STARTUID:TOTALUIDSです。つまり、私の場合、dwalshにはUID 100000から165535までのUIDが割り当てられ、それに加えて私のデフォルトUIDが/etc/passwdで定義されている3265になります。これらのUID範囲を割り当てる際には、システム上の実際のUIDと重ならないように注意する必要があります。UID 100001 としてリストアップされたユーザがいた場合、私 (dwalsh) はこの UID になり、潜在的に UID が所有するファイルを読み書きしたり実行したりすることができます。

Shadow-utils はまた、2つの setuid(または setfilecap) プログラムを追加します。Fedora では、このようになります。

$ getcap /usr/bin/newuidmap
/usr/bin/newuidmap = cap_setuid+ep
$ getcap /usr/bin/newgidmap
/usr/bin/newgidmap = cap_setgid+ep

Podmanはこれらのファイルを実行してユーザ名前空間を設定します。ルートレスコンテナの中から /proc/self/uid_map と /proc/self/gid_map を調べることでマッピングを見ることができます。

$ podman run alpine cat /proc/self/uid_map /proc/self/gid_map
        0       3267         1
        1       100000         65536
        0       3267         1
        1       100000         65536

上で見たように、Podmanはコンテナ内のルートを現在のUID (3267)にマッピングするのがデフォルトで、その後、/etc/subuidと/etc/subgidに割り当てられたUID/GIDの範囲を1からマップします。 つまり、私の例では、コンテナ内のUID=1はUID 100000で、UID=2はUID 100001で、同様に65536は165535に対応するまで続きます。

ユーザ名前空間の外から、ユーザ名前空間にマッピングされていない UID や GID が所有するアイテムは、kernel.overflowuid sysctl で設定されたユーザに属しているように見えますが、これはデフォルトでは 65534 で、私の /etc/passwd ファイルでは nobody という名前が付いています。あなたのプロセスはマッピングされていない ID として実行できないので、所有者とグループのパーミッションは適用されず、「その他」のパーミッションに基づいてしかファイルにしかアクセスできません。rootはユーザ名前空間にマッピングされていませんから、これには、コンテナを実行しているシステム上で実際の root が所有するすべてのファイルが含まれます。

Buildah コマンドには buildah unshare というクールな機能があります。これにより、Podmanが実行しているのと同じユーザ名前空間に入ることができますが、コンテナのファイルシステムには入らないので、ホームディレクトリの内容を一覧表示することができます。

$ ls -ild /home/dwalsh
8193 drwx--x--x. 290 dwalsh dwalsh 20480 Jan 29 07:58 /home/dwalsh
$ buildah unshare ls -ld /home/dwalsh
drwx--x--x. 290 root root 20480 Jan 29 07:58 /home/dwalsh

ユーザ名前空間の外でホームディレクトリの属性をリストアップすると、カーネルは所有権を dwalsh として報告しますが、ユーザ名前空間内ではディレクトリを root が所有していると報告することに注意してください。これは、ホームディレクトリが3267によって所有されており、ユーザ名前空間内ではそのUIDをrootとして扱っているからです。 

ユーザ名前空間を設定した後、Podmanでは次に何が起こるのでしょうか?

Podmanはコンテナイメージを引き出すためにcontainers/storageを使用します。containers/storageは賢いので、イメージ内のrootが所有するすべてのファイルをユーザ名前空間のrootにマップし、異なるUIDが所有するその他のファイルをユーザ名前空間のUIDにマップします。デフォルトでは、この内容は ~/.local/share/containers/storage に書き込まれます。コンテナストレージは、ルートレスモードで vfs モードまたはオーバーレイで動作します。Note: Overlay は fuse-overlayfs 実行ファイルがインストールされている場合にのみサポートされます。

カーネルはユーザ名前空間のrootには特定のタイプのファイルシステムをマウントすることだけを許可しています。現時点では以下が利用できます: procfs, sysfs, tmpfs, fusefs, bind mount (ソースとデスティネーションの両方を Podman を実行しているユーザが所有している場合のみ。OverlayFSはまだサポートされていませんが、カーネルチームが許可できるように取り組んでいます。) 。

fuse-overlayfsを利用している場合、Podmanはコンテナのストレージをマウントします。ストレージドライバがvfsを使用している場合はマウントの必要はありません。しかし、vfs上でPodmanを使用すると、各コンテナは基盤となるファイルシステム全体をコピーするため、多くのスペースを必要とします。

Podmanは/procと/sysをいくつかのtmpfsと共にマウントし、コンテナ内のデバイスを作成します。

ホストネットワーク以外のネットワークを使用するために、Podmanはslirp4netnsプログラムを使用して、特権のないネットワーク名前空間用のユーザモードネットワークを設定します。slirp4netnsを使用すると、Podmanはコンテナ内のポートをホストに公開することができます。ただし、カーネルは1024以下のポートに非特権プロセスをバインドすることを許可しません。ポートへのバインドにはPodman-1.1以降が必要です。

Rootless Podmanはコンテナの分離にユーザ名前空間を使用することができますが、/etc/subuidファイルで定義されたUIDにしかアクセスできません。

結論

Podmanツールは、システムのセキュリティを犠牲にすることなくコンテナを構築して使用することを可能にします。開発者にroot権限を与えずに必要とするアクセスを可能にできます。 

また、コンテナを本番環境に投入する際には、ユーザ名前空間によって提供される追加のセキュリティを利用して、ワークロードを互いに分離しておくことができます。

 

クリエイティブ・コモンズ・ライセンス
この 記事 は クリエイティブ・コモンズ 表示 - 継承 4.0 国際 ライセンスの下に提供されています。

* 各記事は著者の見解によるものでありその所属組織を代表する公式なものではありません。その内容については非公式見解を含みます。