文書の過去の版を表示しています。
名前空間の操作,その 6: ユーザ名前空間について更にもう少し
この記事では,ユーザ名前空間の先の議論を続ける.特に,ユーザ名前空間の相互作用とケーパビリティ,他の名前空間とユーザ名前空間を一緒に使う場合についてもう少し詳細に見ていく.当面は少なくとも,この記事は名前空間に関するこのシリーズのまとめとなるだろう.
ユーザ名前空間とケーパビリティ
それぞれのプロセスは特定のユーザ名前空間と結びついている.fork() もしくは CLONE_NEWUSER フラグのない clone() の呼び出しによって生成したプロセスは,親プロセスと同じユーザ名前空間内に存在する.もし,変更先の名前空間で CAP_SYS_ADMIN を持っている場合は,プロセスは setns() を使って存在するユーザ名前空間を変更することができる.この場合,プロセスはターゲットとなる名前空間上でフルセットのケーパビリティを得る.
一方で clone(CLONE_NEWUSER) の呼び出しは新しいユーザ名前空間を作成し,子プロセスをその名前空間に置く.この呼び出しは二つの名前空間の間に親子関係も確立する.つまり,ユーザ名前空間は (初期の名前空間以外は) (clone(CLONE_NEWUSER) を使って名前空間を作成したプロセスのユーザ名前空間である) 親を持つ.ユーザ名前空間の間の親子関係は,プロセスが unshare(CLONE_NEWUSER) を呼び出したときも確立される.clone と unshare の違いは,unshare() は呼び出し元を新しいユーザ名前空間に配置し,その名前空間の親は呼び出し元が元に属していた名前空間となることである.以下を見るとすぐわかるように,ユーザ名前空間の間の親子関係は,プロセスが子供の名前空間で持つであろうケーパビリティを定義するという意味で重要である.
それぞれのプロセスは 3 つの関連するケーパビリティセットも持つ: Permitted, Inheritable, Effective である.capabilities(7)に,これらの 3 つについて少し詳細に説明がある.この記事では,我々の興味の対象である effective ケーパビリティについて主に述べる.このセットはプロセスが特権オペレーションを実行する時の能力を決める.
ユーザ名前空間は (effective) ケーパビリティが解釈される方法を変える.最初に,特定のユーザ名前空間内でケーパビリティを持っていることは,プロセスがその名前空間が管理しているリソース上の操作を実行するときのみ有効となる.後ほど,ユーザ名前空間と他の名前空間の相互作用について話す時に,もう少しこの点について話をする.さらに,プロセスが特定のユーザ名前空間内でケーパビリティを持っているか否かは,その名前空間のメンバーであることと,ユーザ名前空間間の親子関係によって決まる.このルールはいかのようなものである.
- プロセスがその名前空間のメンバーであり,とあるケーパビリティが effective ケーパビリティセットに存在してる場合,プロセスはユーザ名前空間内でそのケーパビリティを持つ.プロセスはいくつかの方法で effective セット内にケーパビリティを得る可能性がある.その最も一般的な理由は,ケーパビリティを与えられたプログラム (set-user-ID プログラムやファイルケーパビリティを与えられたプログラムなど) を実行することか,自動的にフルセットのケーパビリティを得る clone(CLONE_NEWUSER) で呼ばれた子プロセスであることである.
- プロセスがユーザ名前空間内でケーパビリティを持つ場合,そのプロセスは全ての子供 (と更に削除された子孫) の名前空間内でもそのケーパビリティを持つ.言い換えれば,新しいユーザ名前空間を作成することは,親の名前空間内の特権プロセスの効果から,その(新しく作成した)名前空間のメンバーを隔離することはない.
- ユーザ名前空間が作成されたとき,カーネルは作成しているプロセスの実効ユーザ ID をその名前空間の「オーナー」として記録する.前のルールの効果により,これらのケーパビリティは全ての子孫の名前空間に同様に伝播する.これは,新しいユーザ名前空間の作成後,親の名前空間内の同じユーザが所有する他のプロセスは,その新しい名前空間内で全てのケーパビリティを持つということである.
3 つ目のルールを小さなプログラム userns_setns_test.c の助けを借りてデモすることができる.このプログラムはコマンドライン引数: /proc/PID/ns/user のパス名を 1 つ取る.このプログラムは新しいユーザ名前空間内に子プロセスを作成し,親 (親はプログラムを起動したシェルと同じ名前空間に留まっている) と子は setns() を使ってコマンドライン上で指定された名前空間に入ろうとする.先に注意したとおり,setns() は呼び出し元が CAP_SYS_ADMIN ケーパビリティをターゲットとなる名前空間で持っている必要がある.
デモのために,このシリーズの先の記事で作成した userns_child_exec.c と共にこのプログラムを使う.まず,このプログラムを使って新しいユーザ名前空間で実行されるシェルを起動する (ここではプロセスの名前を区別して作成するために ksh を使う).
$ id -u 1000 $ readlink /proc/$$/ns/user # Obtain ID for initial namespace user:[4026531837] $ ./userns_child_exec -U -M '0 1000 1' -G '0 1000 1' ksh ksh$ echo $$ # Obtain PID of shell 528 ksh$ readlink /proc/$$/ns/user # This shell is in a new namespace user:[4026532318]
ここで,最初の名前空間で実行しているシェルに移動するために別のターミナルウィンドウに移動して,テストプログラムを実行する.
$ readlink /proc/$$/ns/user # Verify that we are in parent namespace user:[4026531837] $ ./userns_setns_test /proc/528/ns/user parent: readlink("/proc/self/ns/user") ==> user:[4026531837] parent: setns() succeeded child: readlink("/proc/self/ns/user") ==> user:[4026532319] child: setns() failed: Operation not permitted
後のプログラムは,以下のように作られた色々なプロセスと名前空間の間の親子関係を示している (黒の矢印)