Docker コンテナの uid と gid の詳細な理解

Docker コンテナの uid と gid の詳細な理解

デフォルトでは、コンテナ内のプロセスは root ユーザー権限で実行され、この root ユーザーはホスト マシンの root ユーザーと同じユーザーです。恐ろしいことに聞こえますが、これはコンテナ内のプロセスに適切な機会が与えられると、ホスト マシン上のすべてを制御できることを意味するからです。この記事では、ユーザー名、グループ名、ユーザー ID (uid)、グループ ID (gid) がコンテナー内のプロセスとホスト システム間でどのようにマッピングされるかを理解します。これはシステムのセキュリティにとって非常に重要です。注: この記事のデモ環境は Ubuntu 16.04 です (下の画像はインターネットから引用したものです)。

まずuidとgidを理解しましょう

Linux カーネルは uid と gid を管理し、カーネルレベルのシステム コールを使用して、要求に権限を付与するかどうかを決定します。たとえば、プロセスがファイルに書き込もうとすると、カーネルは作成プロセスの uid と gid をチェックして、ファイルを変更するのに十分な権限があるかどうかを判断します。カーネルはユーザー名とグループ名ではなく、uid と gid を使用することに注意してください。

簡潔にするために、この記事の残りの部分では uid のみを例として使用します。システムは、基本的に gid を uid と同じように扱います。

多くの学生は、Docker コンテナを単に軽量の仮想マシンとして理解しています。これにより、コンテナ技術の理解の難しさが軽減されますが、多くの誤解を招く可能性もあります。実際、仮想マシン技術とは異なり、同じホスト上で実行されるすべてのコンテナは同じカーネル (ホストのカーネル) を共有します。コンテナ化によってもたらされる大きな価値は、これらすべての独立したコンテナ (実際にはプロセス) がカーネルを共有できることです。つまり、Docker ホスト上で数百または数千のコンテナが実行されている場合でも、カーネルによって制御される uid と gid のセットは 1 つだけになります。したがって、同じ uid は、ホストとコンテナ内の同じユーザーを表します (異なる場所に異なるユーザー名が表示される場合でも)。

ユーザー名を表示するための通常の Linux ツール (id などのコマンドなど) はカーネルの一部ではないため、同じ uid が異なるコンテナーで異なるユーザー名として表示される場合があることに注意してください。ただし、異なるコンテナーであっても、同じ uid に対して異なる権限を持つことはできません。

Linux ユーザー名前空間テクノロジーをすでに知っている場合は、「Linux 名前空間: ユーザー」を参照してください。これまでのところ、Docker はデフォルトでユーザー名前空間を有効にしていないことに注意してください。これは、この記事で説明されているケースでもあります。次の記事では、ユーザー名前空間を有効にするために Docker を設定する方法を紹介します。

コンテナ内のデフォルト ユーザーは root です。

関連する設定が行われていない場合、コンテナ内のプロセスはデフォルトで root ユーザー権限で開始されます。次のデモでは、ubuntu イメージを使用して sleep プログラムを実行します。

$ docker run -d --name sleepme ubuntu スリープ無限

上記のコマンドでは sudo が使用されていないことに注意してください。ホスト マシン上の著者のログイン ユーザーは nick で、uid は 1000 です。

ホスト内のスリープ プロセスの情報を表示します。

$ ps aux | grep スリープ

スリープ プロセスの有効なユーザー名は root です。つまり、スリープ プロセスには root 権限があります。

次にコンテナに入り、状況が以前と同じであることを確認します。スリープ プロセスにもルート権限があります。

では、コンテナ内の root ユーザーはホスト上の root ユーザーと同じですか?

答えは「はい、同じ UID に対応します」です。理由は前に説明しました。システム全体が同じカーネルを共有し、カーネルは uid と gid のセットを 1 つだけ管理します。

実際、上記の結論はデータ量を通じて簡単に検証できます。ホスト マシン上に、root ユーザーだけが読み書きできるファイルを作成します。

次に、それをコンテナにマウントします。

$ docker run --rm -it -w=/testv -v $(pwd)/testv:/testv ubuntu

コンテナ内でファイルを読み書きできます。

コンテナ内のプロセスのユーザー ID は、Dockerfile の USER コマンドまたは docker run コマンドの --user パラメータを使用して指定できます。これら 2 つの状況を別々に検討してみましょう。

Dockerfile でユーザー ID を指定する

Dockerfile にユーザー appuser を追加し、USER コマンドを使用して、プログラムをこのユーザーとして実行するように指定できます。Dockerfile の内容は次のとおりです。

Ubuntuから
useradd -r -u 1000 -g appuserを実行します
ユーザー appuser
ENTRYPOINT ["sleep", "infinity"]

test という名前のイメージにコンパイルします。

docker build -t テストを実行します。 

テストイメージを使用してコンテナを起動します。

$ docker run -d --name sleepme テスト

ホスト内のスリープ プロセスの情報を表示します。

今回表示される有効なユーザーは nick です。これは、ホスト マシンで、uid 1000 を持つユーザーの名前が nick であるためです。次にコンテナに入り、確認してみましょう。

$ docker exec -it sleepme bash 

コンテナ内の現在のユーザーは、設定した appuser です。コンテナ内の /etc/passwd ファイルを確認すると、appuser の uid が 1000 であることがわかります。これは、ホスト マシンのユーザー nick の uid と同じです。

ユーザー nick だけが読み書きできる別のファイルを作成しましょう。

また、それをデータ ボリュームとしてコンテナーにマウントします。

$ docker run -d --name sleepme -w=/testv -v $(pwd)/testv:/testv テスト

コンテナでは、testfile の所有者は appuser になり、もちろん appuser にはファイルの読み取りと書き込みの権限があります。

ここで一体何が起こったのでしょうか?これは何を示しているのでしょうか?

まず、ホスト システムには、uid 1000 のユーザー ニックネームが存在します。次に、コンテナ内のプログラムは appuser として実行されます。これは、Dockerfile プログラムで USER appuser コマンドを使用して指定されます。

実際、システム カーネルによって管理される uid 1000 は 1 つだけです。ホスト マシンではユーザー nick と見なされ、コンテナーではユーザー appuser と見なされます。
したがって、明確にしておく必要があることが 1 つあります。コンテナー内では、ユーザー appuser はコンテナー外のユーザー nick の権利と権限を取得できるということです。ホスト上のユーザー nick または uid 1000 に付与された権限は、コンテナー内の appuser にも付与されます。

コマンドライン引数からユーザーIDをカスタマイズする

docker run コマンドの --user パラメータを使用して、コンテナ内のプロセスのユーザー ID を指定することもできます。たとえば、次のコマンドを実行します。

$ docker run -d --user 1000 --name sleepme ubuntu スリープ無限

コマンドラインでパラメータ --user 1000 を指定したので、スリープ プロセスの実効ユーザーは nick として表示されます。コンテナに入って見てみましょう:

$ docker exec -it sleepme bash 

何が起こっているのか?ユーザー名が「名前がありません!」と表示される! /etc/passwd ファイルを確認すると、確かに uid 1000 のユーザーは存在しません。ユーザー名がなくても、ユーザー ID の権限にはまったく影響しません。ニックネームを持つユーザーだけが読み書きできるファイルの読み書きは引き続き可能で、ユーザー情報はユーザー名ではなく uid に置き換えられます。

コンテナの作成時に docker run --user で指定されたユーザー ID は、Dockerfile で指定された値を上書きすることに注意してください。
テスト イメージを使用して 2 つのコンテナを再度実行します。

$ docker run -d テスト

スリープ プロセス情報を表示します。

$ docker run --user 0 -d テスト

スリープ プロセス情報を再度確認します。

--urser 0 パラメータを指定するプロセスは、有効なユーザーが root であることを示しており、コマンドラインパラメータ --user 0 が Dockerfile 内の USER コマンドの設定をオーバーライドすることを示しています。

要約する

この記事の例から、コンテナ内で実行されているプロセスにもホスト リソースへのアクセス権があることがわかります (Docker はデフォルトでユーザーを分離しません)。もちろん、一般的にコンテナ テクノロジでは、コンテナ内のプロセスの可視リソースがコンテナ内でブロックされます。ただし、データ ボリューム内のファイルに対する操作で示したように、コンテナー内のプロセスがホスト マシンのリソースにアクセスする機会を得ると、その権限はホスト マシン上のユーザーの権限と同じになることがわかります。したがって、より安全な方法は、デフォルトのルート ユーザーを使用するのではなく、コンテナー内のプロセスに適切な権限を持つユーザーを指定することです。もちろん、Linux のユーザー名前空間テクノロジーを使用してユーザーを分離するという、より良い解決策があります。次の記事では、ユーザー名前空間のサポートを有効にするために Docker を構成する方法を紹介します。

参照:

Docker コンテナでの uid と gid の動作を理解する
Docker エンジンのユーザー名前空間の紹介
ユーザー名前空間でコンテナを分離する

以上がこの記事の全内容です。皆様の勉強のお役に立てれば幸いです。また、123WORDPRESS.COM を応援していただければ幸いです。

<<:  MySQLの日付文字列タイムスタンプ変換の詳細な説明

>>:  redhat7 に yum 経由で mysql5.7.17 をインストールするチュートリアル

推薦する

MySQL バックアップ スクリプトの書き方

序文:データベースのバックアップの重要性は、特にデータの損失が深刻な結果を招く可能性がある実稼働環境...

MySql データベース クエリの特殊コマンド

まずMySQLのインストールMySQLソフトウェアをダウンロードし、インストールパスを変更しますMy...

MySQL 8.0.13 のダウンロードとインストールのチュートリアル(画像とテキスト付き)

MySQL は最もよく使用されるデータベースです。詳しく知るには、コンピュータにインストールする必...

MySQL 5.7 共通データ型

——「MySQL in Simple Terms (第 2 版)」からのメモ数値型整数型バイト最小最...

Dockerコマンドの自動補完の実装

序文この友人がどれくらいDockerを使っていなかったのかは分かりませんが、突然Dockerコマンド...

MySQL 実践演習 シンプルなライブラリ管理システム

目次1. ソート機能2. データベースを準備する3. データベースに関連するエンティティクラスの構築...

Linux環境でよく使われるMySQLコマンドの紹介

mysql コマンドを入力します: mysql -u+(ユーザー名) -p+(パスワード) mysq...

Linux で unzip コマンドを使用して複数のファイルを解凍する方法

Linuxにunzipコマンドがない問題の解決策unzipコマンドを使用して.zipファイルを解凍す...

Apache ポートに基づいて仮想ホストを作成する例

apache: ポートに基づいて仮想ホストを作成する仮想ホスト(a、b、c)の作成を例に挙げます1)...

Web ベースの電子メール コンテンツの HTML フォーマット標準の概要

1. ページ要件1) 標準のヘッダーとフッターを使用するXML/HTML コードコンテンツをクリップ...

jQueryブリージングカルーセルの制作原理を詳しく解説

この記事では、jQueryブリージングカルーセル制作原理の具体的なプロセスを参考までに紹介します。具...

Docker+DockerCompose を使用して Web アプリケーションをカプセル化する方法

目次テクノロジースタックバックエンドビルドAPIフロントエンドウェブ構築ゲートウェイ建設ゲートウェイ...

WeChatアプレットはシンプルな計算機を実装する

WeChatアプレットの簡単な計算機は参考用です。具体的な内容は次のとおりです。 1. はじめに1....

MySQL ページングの制限パラメータの簡単な例

Mysqlページングの2つのパラメータ ユーザー制限 1,2 から * を選択 1 は検索する最初の...

MySQL で '%' を含むフィールドをクエリする方法の詳細な説明 (ESCAPE の使用法)

SQLのlike文では、例えば SELECT * FROM user WHERE username...