linux:kernel:namespace:user_namespace

差分

このページの2つのバージョン間の差分を表示します。

この比較画面へのリンク

両方とも前のリビジョン 前のリビジョン
次のリビジョン
前のリビジョン
linux:kernel:namespace:user_namespace [2013/08/31 16:53] – [ユーザとグループ ID のマッピング] tenforwardlinux:kernel:namespace:user_namespace [2013/09/03 16:46] (現在) – [ユーザとグループの ID のマッピングを眺める] tenforward
行 92: 行 92:
     * uid_map (gid_map) に書くデータは,書き込むプロセスの親の名前空間での実効ユーザ ID (グループ ID) だけをユーザ名前空間のユーザ ID(グループ ID) にマッピングする単一行により構成される.このルールにより,ユーザ名前空間内の最初のプロセス (つまり clone() で作られた子供) が自身のユーザ ID (グループ ID) に対するマッピングを書けるようになる.     * uid_map (gid_map) に書くデータは,書き込むプロセスの親の名前空間での実効ユーザ ID (グループ ID) だけをユーザ名前空間のユーザ ID(グループ ID) にマッピングする単一行により構成される.このルールにより,ユーザ名前空間内の最初のプロセス (つまり clone() で作られた子供) が自身のユーザ ID (グループ ID) に対するマッピングを書けるようになる.
     * プロセスは CAP_SETUID (gid_map は CAP_SETGID) ケーパビリティを親のユーザ名前空間に対して持つ.このようなプロセスは,親のユーザ名前空間内で任意のユーザ ID (グループ ID) のマッピングを定義できる.先に述べたように,新しいユーザ名前空間の最初のプロセスは親の名前空間に対してはケーパビリティを持たない.従って,親の名前空間内のプロセスだけが,親のユーザ名前空間内の任意の ID をマップするマッピングを書く事ができる.     * プロセスは CAP_SETUID (gid_map は CAP_SETGID) ケーパビリティを親のユーザ名前空間に対して持つ.このようなプロセスは,親のユーザ名前空間内で任意のユーザ ID (グループ ID) のマッピングを定義できる.先に述べたように,新しいユーザ名前空間の最初のプロセスは親の名前空間に対してはケーパビリティを持たない.従って,親の名前空間内のプロセスだけが,親のユーザ名前空間内の任意の ID をマップするマッピングを書く事ができる.
 +
 +===== ケーパビリティ,execve(),user ID 0 =====
 +
 +このシリーズの先の記事で,ns_child_exec というプログラムを開発した.このプログラムは clone() を使い,コマンドラインオプションで指定した新しい名前空間で子プロセスを作成する.そして子プロセス内でシェルプロセスを実行する.
 +
 +このプログラムを,新しいユーザ名前空間でシェルを実行する事に使用する.そしてそれから,そのシェル内でその新しいユーザ名前空間用のユーザ ID のマッピングを定義する.そうして,問題へ突入しよう.
 +
 +<code>
 +    $ ./ns_child_exec -U  bash
 +    $ echo '0 1000 1' > /proc/$$/uid_map       # $$ is the PID of the shell
 +    bash: echo: write error: Operation not permitted
 +</code>
 +
 +このエラーは,以下のコマンドで見るように,シェルが新しいユーザ名前空間内でケーパビリティを持たないから起こっている.
 +
 +<code>
 +    $ id -u         # Verify that user ID and group ID are not mapped
 +    65534
 +    $ id -g
 +    65534
 +    $ cat /proc/$$/status | egrep 'Cap(Inh|Prm|Eff)'
 +    CapInh: 0000000000000000
 +    CapPrm: 0000000000000000
 +    CapEff: 0000000000000000
 +</code>
 +
 +問題は bash シェルを実行する execve() の呼び出しで行っている.0 でないユーザ ID のプロセスが execve() を実行したとき,プロセスのケーパビリティセットがクリアされる (capabilities(7) man ページが execve() 呼び出しのケーパビリティの扱いについて詳述している).
 +
 +この問題を避けるため,execve() を実行する前にユーザ名前空間内でユーザ ID のマッピングを作成する必要がある.これは ns_child_exec プログラムでは不可能である.これを可能にする少し機能強化されたバージョンが必要である.
 +
 +[[http://lwn.net/Articles/539940/|userns_child_exec.c]] プログラムが ns_child_exec と同じコマンドラインインターフェースを持ち,同じ働きを行う.ただし,追加のコマンドラインオプションの -M と -G だけは違う.このオプションは文字列引数を指定でき,この文字列引数によりユーザとグループ ID のマッピングを新しいユーザ名前空間内で定義する.
 +
 +<code>
 +    $ ./userns_child_exec -U -M '0 1000 1' -G '0 1000 1' bash
 +</code>
 +
 +今回は,マッピングファイルの更新が成功し,シェルが期待するユーザ ID,グループ ID,ケーパビリティを持っているのがわかる.
 +
 +<code>
 +    $ id -u
 +    0
 +    $ id -g
 +    0
 +    $ cat /proc/$$/status | egrep 'Cap(Inh|Prm|Eff)'
 +    CapInh: 0000000000000000
 +    CapPrm: 0000001fffffffff
 +    CapEff: 0000001fffffffff
 +</code>
 +
 +userns_child_exec プログラムの実装には微妙な点が少し存在する.まず,親プロセス (すなわち clone() を呼び出した側) も新しい子プロセスも,新しいユーザ名前空間の uid/gid のマッピングを更新可能であることである.しかし,先に述べたルールにより,子プロセスは自身の実効ユーザ ID だけをマップするマッピングを定義できる.もし,任意の子供内でのユーザ ID,グループ ID のマッピングを定義したい場合,定義は親プロセス側で行わなければならない.その上,親プロセスは適切なケーパビリティを持っていなければならない.具体的には CAP_SETUID, CAP_SETGID,  (そして親プロセスはマッピングファイルを開くのに必要なパーミッションを持っていることを保証するために) CAP_DAC_OVERRIDE である.
 +
 +その上に,親は子供が execve() を呼ぶ前にマッピングファイルを確実に更新する必要がある (でなければ,子供が execve() の間にケーパビリティを失うという,まさに前述のような問題が生じる).確実に実行するために,2 つのプロセスは必要な同期を取るためパイプを採用している.プログラムのソースコードのコメントに詳細な説明がある.
 +
 +===== ユーザとグループの ID のマッピングを眺める =====
 +
 +今までの所の例で /proc///PID///uid_map と /proc///PID///gid ファイルをマッピングを定義するために使う事を示した.これらのファイルはプロセスが使用しているマッピングを見るのに使う事もできる.これらのファイルに書き込む時と同様に,2 つ目の値 (ID-outside-ns) は親のユーザ名前空間に関する定義がなされている.もしファイルを開いているプロセスが異なるユーザ名前空間にいる場合,ID-outside-ns がファイルを開いているユーザ名前空間に関する定義となる.
 +
 +シェルを実行する 2 つのユーザ名前空間を作成し,名前空間内のプロセスの uid_map ファイルを調べる事で,これを示す事ができる.シェルを実行する新しいユーザ名前空間を作成して,始めよう.
 +
 +<code>
 +    $ id -u            # Display effective user ID
 +    1000
 +    $ ./userns_child_exec -U -M '0 1000 1' -G '0 1000 1' bash
 +    $ echo $$          # Show shell's PID for later reference
 +    2465
 +    $ cat /proc/2465/uid_map
 +                   1000          1
 +    $ id -u            # Mapping gives this process an effective user ID of 0
 +    0
 +</code>
 +
 +ここで別のターミナルウィンドウに移り,異なるユーザとグループのマッピングを持つ兄弟となるユーザ名前空間を作る.
 +
 +<code>
 +    $ ./userns_child_exec -U -M '200 1000 1' -G '200 1000 1' bash
 +    $ cat /proc/self/uid_map
 +           200       1000          1
 +    $ id -u            # Mapping gives this process an effective user ID of 200
 +    200
 +    $ echo $$          # Show shell's PID for later reference
 +    2535
 +</code>
 +
 +2 つ目のユーザ名前空間のシェルを実行している 2 つ目のターミナルウィンドウで,他のユーザ名前空間内のプロセスのユーザ ID のマッピングを見よう.
 +
 +<code>
 +    $ cat /proc/2465/uid_map
 +                    200          1
 +</code>
 +
 +このコマンドの出力は,他のユーザ名前空間内でのユーザ ID 0 が,自分の名前空間内ではユーザ ID 200 にマッピングされていることを示している.同じコマンドが他のユーザ名前空間内で実行されると,異なる出力を生成していることに注意すること.これは,ファイルから読み込みを行っているプロセスの属するユーザ名前空間に従って,カーネルが ID-outside-ns の値を生成しているからである.
 +
 +もし,最初のターミナルウィンドウに戻って,2 つ目のユーザ名前空間内のプロセスに対するマッピングファイルを表示したとすると,以下のような出力が見られるだろう.
 +
 +<code>
 +    $ cat /proc/2535/uid_map
 +           200          0          1
 +</code>
 +
 +再び,ここでの出力は 2 つ目のユーザ名前空間で実行した時の同じコマンドと異なる.これは ID-outside-ns の値はファイルから読み込みを行っているプロセスのユーザ名前空間に従って生成されているからである.もちろん,初期の名前空間では,最初のユーザ名前空間のユーザ ID 0 と,2 つ目の名前空間のユーザ ID 200 は,両方ともユーザ ID 1000 にマッピングされている.これを,初期の名前空間で実行する 3 つ目のシェルウィンドウで以下のコマンドを実行して調べてみよう.
 +
 +<code>
 +    $ cat /proc/2465/uid_map
 +                   1000          1
 +    $ cat /proc/2535/uid_map
 +           200       1000          1
 +</code>
 +
 +===== 最後に =====
 +
 +この記事では,ユーザ名前空間の基礎を見てきた.ユーザ名前空間の作成,ユーザとグループ ID のマッピングファイルの使用,ユーザ名前空間とケーパビリティの関係である.
 +
 +[[http://lwn.net/Articles/528078/|先の記事]]で述べたように,ユーザ名前空間実装の動機の一つは,元は root ユーザに限られていた機能へのアクセスを非 root アプリケーションに与える事である.伝統的な UNIX システムでは,様々な機能は,非特権ユーザが特権プログラムの実行環境の操作するのを防ぐために root ユーザに制限されていた.期待しない,望まない方法でこれらのプログラムの動きが影響を受ける可能性があったためである.
 +
 +ユーザ名前空間は,(名前空間外では特権を持たない) プロセスが,その名前空間に特権の範囲を制限すると同時に root 特権を持てるようにする.その結果,プロセスはシステム全体の特権プログラムの実行環境の操作はできない.これらの root 特権を有意義に使うために,他のタイプの名前空間と同時にユーザ名前空間を同時に使う必要がある.このトピックはこのシリーズの次の記事の主題を形作る.
  
  • linux/kernel/namespace/user_namespace.1377968007.txt.gz
  • 最終更新: 2013/08/31 16:53
  • by tenforward