ルートレスPodmanについてよく聞かれる質問の1つは、コンテナにマウントされたボリュームの問題をデバッグする方法です。この質問は一見難しいです。多くの点で、 rootなしでPodmanを実行します rootとして実行するのとほぼ同じです 。残念ながら、これは常に正しいとは限りません。ボリュームは、最も重要な違いがある領域の1つです。ここでは、これらの違いとは何か、それらが引き起こす可能性のあるエラーの種類、およびそれらを回避する方法について詳しく説明します。まず、ルートレスPodmanの最も基本的な機能の1つであるユーザー名前空間から始めて、ルートレスコンテナがどのように機能するかについての背景情報が必要です。 。
ユーザー名前空間
コンテナの基本的なセキュリティ機能の1つは、Linuxカーネル名前空間です。名前空間は、プロセス(またはプロセスのグループ)をシステムの他の部分から分離する方法であり、表示できるものを制限します。多くの異なる名前空間があり、それぞれが異なる効果を持っています。たとえば、PID名前空間は、プロセスが表示できるPIDを制限します。名前空間内のPIDのみが表示され、ホストから独立して番号が付けられます。プロセスにはまだホスト上にPIDがあるため、ホスト上のPID2000は名前空間内のPID1である可能性があります。この記事の執筆時点では、カーネルが提供する名前空間は、Mount、PID、Network、IPC、UTS、User、cgroup、およびtimeであり、それぞれがシステムの異なる側面を分離しています。このブログで私たちが最も気にかけているのは、ユーザーの名前空間です。
ユーザー名前空間は、コンテナーで使用可能なユーザーとグループを、ホストシステムで使用可能なユーザーとグループから分離します。ユーザー名前空間は、コンテナー内のユーザーをホスト上のユーザーにマッピングすることで機能します。たとえば、コンテナ内のユーザー0から1000をホスト上のユーザー100000から101000にマップできます(グループも同じ方法でマップされますが、簡単にするためにユーザーに焦点を当てます)。このマッピングは、上記で説明したPID名前空間と非常によく似ていますが、ユーザーを使用します。
ホストから、ルートからのすべてのアクセス コンテナ内(UID 0)はUID 100000からのものであるように見えます。コンテナ内では、ホスト上のユーザー100000が所有するファイルは、UID 0( root )が所有しているように見えます。 )。興味深い質問は、コンテナーにマップされていないユーザーはどうなるかということです。ユーザー1001が所有するボリュームをホスト上で、説明したユーザー名前空間を使用してコンテナーにマウントするとどうなりますか?この場合、カーネルは、名前空間で無効なUIDまたはGIDを、 nobodyと呼ばれるユーザーおよびグループであるUID/GID65534にマップします。 、これは通常のグループではありません。
誰もが所有していないファイルを操作することは引き続き可能です。 権限が許可されている場合(たとえば、誰もが所有していないファイルを読み取ることができます これは誰でも読み取り可能であり、誰でも書き込み可能な場合はそのファイルに書き込みます)が、所有権を変更することはできません。ユーザー名前空間は、通常は rootでのみ利用できる特定の機能の限定バージョンも付与します -典型的な例は、ユーザーの名前空間がtmpfsなどの特定の種類のファイルシステムをマウントできることです。
ユーザー名前空間は、ルートとして機能できるため、非常に便利です。 実際にルートになることなくコンテナ内 システム上。ユーザー名前空間を使用して、マルチテナントシステム上のさまざまなユーザーからコンテナーを分離できます。1つのユーザーのコンテナーはUID 10000〜10999として実行され、別のユーザーのコンテナーは11000〜11999として実行されます。各コンテナ内では、アプリケーションがルートであるかのように見えます 、および付与された制限された機能により、最も一般的な操作(たとえば、パッケージのインストール)を実行できます。
ただし、アプリケーションがなんとかコンテナから抜け出したとします。その場合、 rootとして実行されていません システム上で、他のユーザーのコンテナと同じUIDおよびGIDとして実行されていないため、システムのさまざまな部分を攻撃する機能は非常に制限されています。
ただし、それらの採用は技術的な制限によって制限されていました。特に、ごく最近まで、ファイルシステムレベルでUIDとGIDを再マッピングする方法がなかったという事実がありました。 UID 10000〜10999を使用するコンテナを作成するには、そのイメージのコピーを作成してから、 chown
を作成する必要がありました。 既存のUIDに10000を追加することにより、上記のイメージ内のすべてのUID。このchown
非常に遅くなる可能性があり、(多くのファイルシステムでは)必要なスペースの量が劇的に増加します。
[コンテナを使い始めますか?この無料コースをチェックしてください。コンテナ化されたアプリケーションのデプロイ:技術的な概要。 ]
ルートレスコンテナ
ユーザーの名前空間が非常に便利で人気のあるものになっているのは、ルートレスコンテナです。 Linuxのroot以外のユーザーは、1つのUIDとGID(自分自身)にのみアクセスできます。ただし、コンテナは複数のユーザーとグループにアクセスすることを想定しています。コンテナイメージ内の多くのファイルは、 rootによって所有されていません。 、およびアプリケーションは、多くの場合、コンテナ内で非rootユーザーとして実行され、コンテナ内で特権の分離を強制します。
一部の環境(ハイパフォーマンスコンピューティング、HPC、注目すべき環境)では、コンテナー内に1人のユーザーとグループのみが含まれることが許容されます(望ましい場合もあります)。他のほとんどの環境では、1人のユーザーとグループがコンテナの有用性に対する厳しい制限です。ユーザー名前空間を使用して、通常のコンテナーのように動作する必要があるこれらの追加のユーザーとグループを取得できます。
ただし、これらの追加のユーザーとグループを実現するには、昇格された特権が必要です。これがnewuidmap
です およびnewgidmap
実行可能ファイル(および / etc / subuid
および/etc / subgid
読み取った構成ファイル)は、ユーザーとグループのブロックへのアクセスを許可します。これらのファイルは、ルートレスコンテナーが使用できるようにユーザー名前空間にマップされます。ファイルシステムのサポートに関するユーザーの名前空間の制限はある程度適用されますが、ユーザーごとに使用するUIDとGIDのセットを1つだけにすることで緩和されます。
また、カーネルが自動的に chown
を処理するという事実も注目に値します。 ユーザー名前空間内で画像を解凍した場合の操作。名前空間をユーザーがシフトすることで、コンテナーランタイムが手動で設定するのではなく、作成時に正しいUIDが割り当てられるようになります。
ユーザー名前空間の追加機能は、ルートレスコンテナーが必要とするものの一部にも不可欠です。FUSEおよびtmpfsファイルシステムをマウントする機能がないと、ルートレスコンテナーははるかに制限されます(ほとんど使用できなくなるまで)。
Podmanのユーザー名前空間
ユーザー名前空間が一般的にどのように機能するかを理解したので、ルートレスPodmanでそれらがどのように実装されるかについて説明しましょう。
ユーザーが複数のUIDとGIDを使用できない場合でも、すべてのルートレスPodmanコンテナーはユーザー名前空間で実行されます。すべてのユーザーのコンテナは、ルートレス一時停止プロセスによって開かれたままの単一のユーザー名前空間を共有します。名前空間は通常、プロセスが存在しなくなるとカーネルによってプルーニングされるため、スリープするだけで終了しないプロセスを維持することで、ユーザーの名前空間を存続させることができます。
ルートレスPodmanプロセスが最初に行うことは、ルートレスユーザー名前空間に参加することです(または、新しい名前空間を作成し、まだ存在しない場合はプロセスを一時停止します)。ユーザー名前空間の作成の一環として、Podmanは newuidmap
を実行します およびnewgidmap
ユーザーが/etc / subuid
に割り当てられている追加のUIDとGIDを付与するための実行可能ファイル および/etc / subgid
(ユーザー作成時に付与されるデフォルトの金額は、それぞれ65536です)。 podman info
で利用可能なユーザーマッピングを表示できます idMappingsのコマンド フィールド:
mheon@podman-rhel8-test $ podman info
idMappings:
gidmap:
- container_id: 0
host_id: 1000
size: 1
- container_id: 1
host_id: 100000
size: 65536
uidmap:
- container_id: 0
host_id: 1000
size: 1
- container_id: 1
host_id: 100000
size: 65536
/ etc / subuid
の場合、ルートレスユーザー名前空間はデフォルトで再作成されないことに注意してください。 および/etc / subgid
ファイルが変更されます。これは、 podman systemmigrate
を実行することで実行されます 指図。これらのファイルを編集していて、Podmanが変更を認識していないように見える場合は、このコマンドを実行してください。
すべてのrootlessPodmanコマンドは、正しいユーザーマッピングと特権を確保するために作成されたrootlessユーザー名前空間内で実行されます。 podman info
のような単純な情報コマンドでさえ 、ルートレスユーザー名前空間が必要です。そのため、機能しないユーザー名前空間は、ルートレスPodmanの目玉です。幸いなことに、これは頻繁には発生しません。その場合、通常、 newuidmap
のファイル権限が不十分であることが原因であることがわかります。 および/またはnewgidmap
システム上のバイナリ(通常、ファイル機能がありません)。それらを含むパッケージを再インストールします( shadow-utils と呼ばれます) RHEL、CentOS、Fedoraで)通常これを解決します。
ルートレスユーザー名前空間が作成されたら、コンテナーの実行を開始できます。これらのコンテナでボリュームを使用することは、ほとんどのユーザーがルートとルートレスのPodmanの実際的な違いに遭遇する最初のポイントです。ユーザーは、ボリュームがマウントされたコンテナーを実行すると、すべてが正しく設定されているように見えても、コンテナーがボリューム内のファイルにアクセスできないことをすぐに発見します。
たとえば、簡単なPodmanコマンドを調べてみましょう。
podman run --user 1000:1000 -v /home/mheon/data:/data:Z ubi8 sh
このコマンドは私のユーザーmheonによって実行されています 、UIDとGIDが1000に設定されている(コンテナーが使用するように指示されたのと同じユーザー)。私のユーザーには、 / etc / subuid
を介してUID/GID100000から始まる65536のUIDとGIDが割り当てられています。 および/etc / subgid
。 SELinuxコンテキストが設定されているため、コンテナは:Z
を介してディレクトリにアクセスできます。 ボリュームマウントのオプション。
ただし、 / data
へのアクセス コンテナ内のフォルダは拒否され、システムが返す唯一のエラーメッセージは一般的な許可が拒否されました カーネルから。ユーザーの名前空間が何であるかを知らなかったユーザーは、何が間違っているのかわかりません。
ただし、わかったので、原因は明らかです。ユーザーマッピングは、コンテナー内のUID1000が実際にはホスト上のUID1000ではないことを意味します。 podman top
を使用して、コンテナ内のユーザーとホスト上の実際のユーザーを表示できます。 コマンド:
mheon@podman-rhel8-test $ podman top -l user,huser
USER HUSER
1000 100999
ここで、ユーザー はコンテナ内のユーザーであり、 HUSER ホスト上のユーザーです。
それでも、なぜ何かが起こっているのかを知っているからといって、それを修正する方法を知っているわけではありません。それでも、特定のボリュームがマウントされたルートレスPodmanコンテナを実行したいと考えています。どうすればいいですか?幸い、これを修正する方法はたくさんありますが、これについては以下で説明します。
最初の解決策
1つ目は単純です:-user
コンテナコマンドをrootとして実行すると、オプションをコンテナから省略できます。 。上記のように、デフォルトでは、Podmanはコンテナを実行しているユーザーをルートにマップします コンテナ内— root であるにもかかわらず、ホスト上でUID /GID1000としてボリュームにアクセスします。 コンテナ内。 ルートとして実行 ルートフルコンテナ内は、システムのルートとして実行しているため、潜在的なセキュリティの問題です。 ユーザー-攻撃者がコンテナから侵入した場合、攻撃者はルートとして機能することができます。 システム上。ルートレスコンテナの要点は、これが決して真実ではないということです。セキュリティの問題は主に非要因です。
残念ながら、コンテナを rootとして実行するソリューション root以外のユーザーを使用するようにイメージが特別に作成されている場合、フラットになります。一部のコンテナイメージは、簡単に変更できないアクセス許可を削除するための複雑なエントリポイントスクリプトを備えています。これらには代替ソリューションが必要になります。
2番目の解決策
2番目のオプションは、コンテナーで実行しているユーザーに、ホストからマウントされたフォルダーの読み取りと書き込みのアクセス許可を付与することです。 Podman v3.1.0以降、これは:U
を介して自動的に実行できます。 -v
のボリュームオプション フラグ(例: -v / home / mheon / data:/ data:Z、U
。
次に、 podman unshare chown 1000:1000 / home / mheon / data
と入力します 。このボリュームオプションはディレクトリの所有権を自動的に調整するため、コンテナで実行されているユーザー(コンテナ内のユーザーUID 1000がホスト上でマップされているもの)がディレクトリを所有します。このフラグのないバージョンでは、 podman unshare
コマンドを使用して、ルートレスユーザーの名前空間を入力してから chown
を入力できます コンテナを実行しているユーザーが所有するディレクトリ。
この場合、 podman unshare chown 1000:1000 / home / mheon / data
ホスト上のディレクトリの所有権を、ユーザー名前空間のUID /GID1000にマップするユーザーとグループに変更します。所有権が変更された場合、ホスト上のすべての親ディレクトリにもすべてのユーザーの実行権限が必要になることに注意してください( chmod a+x…
)問題のディレクトリにアクセスできるようにする権限。
残念ながら、 chown
アプローチには、独自の欠点があります。 / home / mheon / data
ディレクトリはユーザーのホームディレクトリにありますが、ユーザーが所有していません(この場合、ユーザーとグループが所有しています 100999 )。ルートレスユーザー名前空間では、 mheon ユーザーはルートとして機能できます そのユーザーが所有するファイルの読み取り、書き込み、および変更。しかし、それ以外ではこれらのことはできません。 Podmanは、ルートレスユーザー名前空間内にシェルを入力するコマンドを提供します( podman unshare
)このようなファイルを変更または削除するために使用できますが、それ以外の方法でこれらのファイルを管理できないことは不便です。
3番目の解決策
3番目のオプションは、-userns =keep-id
を使用することです。 podman run
のオプション 。このフラグは、Podmanに2つのことを行うように指示します。1つは、Podmanを実行したユーザーのUIDとGIDに関してコンテナーが実行するユーザーを設定することです(-user
によって明示的にオーバーライドされない限り) フラグ)、次に、Podmanを実行したユーザーが root ではなく独自のUIDとGIDにマップされるように、コンテナーにマップされたユーザーを並べ替えます。 (これは、このコンテナー専用に作成された、ルートレスユーザー名前空間内にネストされた2番目のユーザー名前空間を介して行われます)。コンテナが実行されているユーザー名前空間のユーザーはルートではありません ただし、Podman( mheon )を実行しているユーザーにマップされます )ホスト上にあるため、例の / home / mheon / data
にマウントされているディレクトリにアクセスできます。 。
ただし、これですべてのアクセスエラーが解決されるわけではありません。もう1つの一般的な問題は、Podmanを実行しているユーザーがアクセスできるファイルまたはデバイスにマウントしようとすることですが、補足グループによってのみです。たとえば、私のユーザーである mheon 、は kvmの一部です / dev / kvm
を所有するグループ 、そして私は / dev / kvm
をマウントすることを選択します podman run -t -i -v / dev / kvm:/ dev / kvm fedora bash
を使用してコンテナに挿入します 。コンテナは/dev / kvm
にアクセスできなくなります 、ホスト上でユーザーとして実行されているにもかかわらず(アクセス権が必要です)。
その理由は、セキュリティ上の理由から、コンテナは(デフォルトで)作成の一部としてすべての追加グループを削除するためです。この動作は無効にできますが、 crun
を使用する場合のみです 特別なアノテーション(-annotation run.oci.keep_original_groups =1
)を渡すことによるOCIランタイム(これは、RHELを除くすべてのディストリビューションでPodman 3.0の時点でデフォルトになっているはずです) 。
今後のPodmanv3.2.0では、これは group-add
への特別な引数を介して利用できるようになります。 フラグ(-group-add keep-groups
)。これらのグループへのアクセスは保持できますが、ルートレスユーザー名前空間に追加する権限はありません。これは、 / etc /subuidで割り当てられたユーザーとグループに対してのみ行うことができます。コード> および
/etc / subgid
。 ls -al
/ dev / kvm
に コンテナ内では、 nobody:nobodyが所有していることがわかります (実際の所有者およびグループとして、 root:kvm 、コンテナにマップされていません。
ただし、 kvm であるにもかかわらず、アクセスできます。 グループはユーザー名前空間の一部ではなく、カーネルプロセスから見てコンテナプロセスはグループの一部です。これは、これらの補足グループの1つとして(誰もとして)ファイルを明示的に作成できないという点で、いくらか制限があります。 対話できる実際のグループではありません)が、そうでなければ到達できないホスト上のコンテンツへのアクセスをコンテナに与えるだけで十分であり、補足グループが所有するSUIDビットを持つディレクトリは引き続き正しい所有者を設定します。
高UIDユーザーが所有するファイルまたはフォルダーを含むイメージをプルしているときに、別のタイプの一般的なエラーが発生します。 UIDまたはGIDが所有するファイルまたはフォルダーが大きすぎてユーザーの名前空間に含めることができない場合は、エラーが発生します。私は以前、これと考えられる解決策についてブログを書きました。これはここにあります。
結論
Podmanの最も強力な機能の1つは、ルートレスコンテナに対する強力なサポートであり、人々が興奮している理由を理解するのは難しいことではありません。ルートレスコンテナはセットアップが簡単で、ルートよりも安全です コンテナであり、 rootとして実行されるコンテナのほとんどすべてを実行できます。 できる。もちろん、キーワードはほぼ —rootとrootlessの全体的なエクスペリエンスは非常に似ているため、違いは混乱を招く可能性があり、説明するのは簡単ではありません。このブログを読んだ後は、これらの最大の違いの1つと、Podmanを使用してコンテナーを希望どおりに実行する方法をしっかりと理解する必要があります。