TenForward

技術ブログ。はてなダイアリーから移転しました

Linux 3.8 の User Namespace 機能 (1)

コンテナを実現するのにカーネルに必要な機能としては大きく分けて

があります.

リソース制限は cgroup として実装されています (ここではこの話は置いときます).

名前空間は,その対象となる空間と他の空間を分ける機能を持っています.コンテナを作る場合,それぞれの空間で独立した uid/gid やネットワークインターフェースが存在しなければいけませんので,その機能を実現しています.既に Linux Kernel には色々な名前空間が実装されており,未実装でコンテナを安全に使うために必須と言われていたのが User Namespace (ユーザ名前空間) です.この実装は徐々に実装されてきていましたが,影響範囲が大きく,実装も難しいため実現していませんでしたが,ようやく kernel 3.8 で実装が完了するようです.

今までもコンテナごとに /etc/passwd などを置いて,それぞれでユーザ管理を行うことは可能でした.でもコンテナで UID=0 のユーザがいたとすると,ホストOS上や他のコンテナでもそのユーザは UID=0 でしたし,コンテナ内で root (UID=0) の権限で実行しているプロセスは,ホストOSや他のコンテナでも root (UID=0) の権限で実行している事になっていました.このため,コンテナ内で root 権限を持っている場合,ホスト OS 上の root 権限を持っている事になり,コンテナからホスト OS に対してなんでも出来る状態でした (例えばホストをリブートするとか).

これを解決するために新たにカーネル内での特権のチェックを行う専用の UID/GID が作られました.kernel UID/GID というもので,以下のように定義されています.

typedef struct {
	uid_t val;
} kuid_t;


typedef struct {
	gid_t val;
} kgid_t;

この kernel UID/GID名前空間内・コンテナ内での UID/GID を対応付ける表を名前空間内に保存しておき,コンテナ内であるユーザの権限で処理を行おうとした時は,カーネル内ではそれに対応する kernel UID/GID で処理が行われるため,コンテナ内とホスト OS 上で別の権限を持たせることが可能になります.このマッピングは /proc/PID/uid_map とか /proc/PID/gid_map というファイルに保存され,以下のような形式になっています.

         0     100000      10000

これが uid のマッピングとすると,コンテナ内では UID=0 〜 10000 までのユーザが,ホスト OS 上では UID=100000 〜 110000 にマッピングされるという意味になります.コンテナ内で UID=10000 より大きな UID のユーザを作成すると,ホスト OS 上では存在しないユーザとなります.この時に kernel UID/GID として返る値は /proc/sys/kernel/overflowuid,/proc/sys/kernel/overflowgid の値になります.

(Linux 3.8 の User Namespace 機能 (2) - TenForwardの日記へつづく)