Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Linux Namespace

13,194 views

Published on

Namespace feature

Published in: Software

Linux Namespace

  1. 1. Linux Namespace @masami256
  2. 2. Table of Contents • Namespace overview • System calls • kernel implementation • Namespace Example
  3. 3. namespace overview
  4. 4. Namespace? • リソース • 所謂コンテナ型仮想化を実現する上で重要な機 能の一つ
  5. 5. Resource • Namespaceにおけるリソース • cpuやmemoryと言った物理的な計算資源で はない • ホスト名、ネットワーク設定、pidなどのカーネ ルが扱うデータ
  6. 6. Feature • プロセス間でカーネルのリソースを共有 • fork(2)の実行時は親プロセスとリソースを共有 • namespace毎に独立したリソース • 名前空間の状態を変えるような処理を行っても 別の名前空間に属するプロセスには影響は及ば ない
  7. 7. Namespace representation • 名前空間はファイルとしてユーザー空間から見え る • setns(2)で利用 masami@miko:~$ ls -l /proc/self/ns total 0 dr-x--x--x 2 masami masami 0 Aug 31 00:15 . dr-xr-xr-x 8 masami masami 0 Aug 31 00:15 .. lrwxrwxrwx 1 masami masami 0 Aug 31 00:15 ipc -> ipc:[4026531839] lrwxrwxrwx 1 masami masami 0 Aug 31 00:15 mnt -> mnt:[4026531840] lrwxrwxrwx 1 masami masami 0 Aug 31 00:15 net -> net:[4026531957] lrwxrwxrwx 1 masami masami 0 Aug 31 00:15 pid -> pid:[4026531836] lrwxrwxrwx 1 masami masami 0 Aug 31 00:15 user -> user:[4026531837] lrwxrwxrwx 1 masami masami 0 Aug 31 00:15 uts -> uts:[4026531838]
  8. 8. Namespaces • uts • net • pid • mnt • ipc • user
  9. 9. uts namespace • ホスト名、ドメイン名などのデータ • カーネルバージョン等もあるが変更不可 • ゲストが自ホスト名を変えてもホストOS側には 影響はでない
  10. 10. net namespace • ネットワーク関連のリソース • Network device • IP address • Routing table • Filtering table • Port number • /proc/net • etc…
  11. 11. pid namespace • 親プロセスとは別のpidを利用可能に • namespace Aのpid:1000とnamespace Bの pid:1000は別の存在 • procfsを適切に分ければ他のnamespceのプロ セスを参照できなくなる
  12. 12. mnt namespace • マウントしているファイルシステムを表す • 名前空間分離時は親プロセスのmnt namespace をコピー • 分離後に親プロセスがusb stickなどをマウン トしてもゲスト側からは見えない
  13. 13. ipc namespace • System V IPCで使用するリソースを分離 • 共有メモリ、セマフォ、メッセージキュー
  14. 14. user namespace • ホストとは別のuid/gid体系を持てる • ホストのuid/gidとゲストのuid/gidマッピングが必要 • 設定しないと65534が設定される • groupsは65534番のgroup入りする • 他のnamespaceと違い、独立していない • 他のnamespace空間は個々にuser namespaceを持っている • 各namespaceのコピー処理関数はuser nsを受け取るので get_user_ns()で参照カウントを増やしている
  15. 15. uid/gid mapping • マッピングを行うシステムコールは無い • 以下のファイルを用いてマッピングを実施 • /proc/<pid>/uid_map • /proc/<pid>/gid_map
  16. 16. uid/gid mapping • ゲストのuid0をホストのuid1000にマッピング • gidも同様に masami@miko:~$ ./a.out -U -M '0 1000 1' -G '0 1000 1' bash root@miko:~# id uid=0(root) gid=0(root) groups=0(root),65534 root@miko:~# cat /proc/self/uid_map /proc/self/gid_map 0 1000 1 0 1000 1 root@miko:~# touch test.txt root@miko:~# ls -la test.txt -rw-r--r-- 1 root root 0 Aug 31 12:00 test.txt root@miko:~# """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" masami@miko:~$ ls -la test.txt -rw-r--r-- 1 masami masami 0 Aug 31 12:00 test.txt masami@miko:~$
  17. 17. In an user namespace •マッピングなしでuser namespaceをunshare I have no name!@miko:/proc/640$ ls -la /usr/bin/sudo -rwsr-xr-x 1 65534 65534 142792 May 9 15:58 /usr/bin/sudo ! •通常の状態 I have no name!@miko:/proc/640$ exit logout masami@miko:~$ ls -la /usr/bin/sudo -rwsr-xr-x 1 root root 142792 May 9 15:58 /usr/bin/sudo
  18. 18. how to uid/gid mapping 1. clone(2)を呼ぶ 1. CLONE_NEWUSERをflagsにセット 2. exec()系のシステムコールを呼ぶ前にマッピングを行う 1. /proc/<child process pid>/uid_map 2. /proc/<child process pid>/gid_map 3. exec()系の関数を呼んで新たなプログラムを実行
  19. 19. system calls
  20. 20. system calls • clone(2) • unshare(2) • setns(2)
  21. 21. clone(2) • clone(2)でpid namespaceを分離した場 • 子プロセスから見ると自分のpidは1として見える • 親プロセスからは親プロセスのpid namespaceでの pidが振られたように見える • clone(2)の戻り値を使ってwaitpid(2)で待つこと ができる
  22. 22. clone(2) clone(2)で使用するフラグはsched.hをincludeして使用できるが、 CLONE_NEWUSERは_GNU_SOURCEをdefineする必要あり CLONE_NEWUTS uts namespace CLONE_NEWPID pid namecpace CLONE_NEWNS mnt namespace CLONE_NEWNET net namespace CLONE_NEWIPC ipc namespace CLONE_NEWUSER user namespace
  23. 23. unshare(2) • 自分を親プロセスの名前空間から分離させる • pid namespaceは分離できない • 一時期サポートされていたが2013/03/06から対象外になった • pidns: Don't have unshare(CLONE_NEWPID) imply CLONE_THREAD • https://github.com/torvalds/linux/commit/ 6e556ce209b09528dbf1931cbfd5d323e1345926 • unshare(1)の場合エラーにならないが何もおきない
  24. 24. setns(2) • 既存の名前空間に自身を参加させる • clone(2)、unshare(2)は親の名前空間から分離して新規の 名前空間を持つようになる • 名前空間への参加には対象の名前空間のファイルディスクリプ タを使用する • 別のpid namespaceに参加する場合、プロセス自身のpidは 変わらない • 子プロセスからpidが変わる
  25. 25. kernel implementation
  26. 26. kernel/nsproxy.c • namespace共通の処理を行う • namespaceの作成、コピーなど • 個々のnamespaceについてはそれぞれが実施
  27. 27. kernel/nsproxy.c • 前スライドでnamespace共通と説明したけど • user namespaceは扱っていない • user namespaceはcred.cや個々の namespaceが管理
  28. 28. struct nsproxy • 各namespaceのデータを保持する構造体 ! struct nsproxy { atomic_t count; struct uts_namespace *uts_ns; struct ipc_namespace *ipc_ns; struct mnt_namespace *mnt_ns; struct pid_namespace *pid_ns_for_children; struct net *net_ns; }; •user namespaceはstruct credにて管理 •struct task_structのreal_creadからuser namespaceを参照
  29. 29. common namespace structure • 各namespaceは以下の変数を必ず持つ • 参照カウンタ • procfsのinode番号 • user namespace • user namespaceの場合は親プロセスへのポインタ
  30. 30. init_nsproxy • 最初のプロセスに設定されるnsproxy構造体 • これ以降はfork系関数の呼び出し時にこの構 造体の参照カウンタをインクリメント • namespaceを分けない場合:)
  31. 31. struct nsproxy • mount namespace以外はビルド時に設定 struct nsproxy init_nsproxy = { .count = ATOMIC_INIT(1), .uts_ns = &init_uts_ns, #if defined(CONFIG_POSIX_MQUEUE) || defined(CONFIG_SYSVIPC) .ipc_ns = &init_ipc_ns, #endif .mnt_ns = NULL, .pid_ns_for_children = &init_pid_ns, #ifdef CONFIG_NET .net_ns = &init_net, #endif };
  32. 32. Initialize mnt_ns •init_mount_tree()からが実際の処理 start_kernel() @init/main.c --> vfs_caches_init() @fs/dcache.c --> mnt_init() @fs/namespace.c --> init_mount_tree() @fs/namespace.c --> create_mnt_ns() @fs/namespace.c
  33. 33. Initialize user_ns • user_nsはstruct credのデータ初期化時に設定 struct cred init_cred = { .usage = ATOMIC_INIT(4), ~略~ .cap_bset = CAP_FULL_SET, .user = INIT_USER, .user_ns = &init_user_ns,
  34. 34. copy_namespace() • int copy_namespaces(unsigned long flags, struct task_struct *tsk) • copy_process()より呼ばれる • flagにCLONE_NEWXXXがセットされていなければカ レントプロセスのnsproxy構造体の参照カウンタを+1 • そうでなければcreate_new_namespace()で該当する namespaceを作成する
  35. 35. switch_task_namespace() • void switch_task_namespaces(struct task_struct *tsk, struct nsproxy *new) • カレントプロセスの名前空間切り替えを行う • unshare(2)で使用 • プロセスのexit()時にも使用 • 切り替え先のnamespaceにNULLを設定
  36. 36. switch_task_namespace • namespaceを切り替えた結果、切り替え前の namespaceを参照するプロセスがいなくなった 場合 • free_nsproxy()を呼んでnamespaceを解放
  37. 37. free_nsproxy() • void free_nsproxy(struct nsproxy *ns) • namespaceの解放 • 各namespaceの参照カウンタをデクリメント • nsproxy構造体のインスタンス解放 • 通常はプロセスのexit時に実行される
  38. 38. unshare_nsproxy_namespace() • int unshare_nsproxy_namespaces(unsigned long unshare_flags, struct nsproxy **new_nsp, struct cred *new_cred, struct fs_struct *new_fs) • unshare(2)の実行時に呼ばれる • create_new_namespace()で名前空間を新規に 作成
  39. 39. procfs operations • namaspaceはprocfsで表現されるのでこれらを操作する関数 を登録 • setns(2)が使うのがこれら • 各namespace毎に登録 struct proc_ns_operations { const char *name; int type; void *(*get)(struct task_struct *task); void (*put)(void *ns); int (*install)(struct nsproxy *nsproxy, void *ns); unsigned int (*inum)(void *ns); };
  40. 40. procfs operations • get() • 対象namespaceの参照カウンタを+1 • put() • 対象namespaceの参照カウンタを-1 • install() • 現在のnamespaceの参照カウンタを-1し、新しいnamespaceをnsproxy構 造体にセット • inum() • namespaceのinode番号を返す
  41. 41. clone() • namespaceの操作と言ってもclone()固有で何かがあるわ けでは無い • copy_process()から以下の関数を呼ぶ • copy_creds() • user_nsのコピー/新規作成 • copy_namespace() • 既存の各namespaceのコピー/新規作成
  42. 42. setns() 1.入りたいnamespaceのfile構造体からinodeを取得し、該当 namespaceのproc_ns_operations構造体取得 2.create_new_namespace()でnsproxyの作成 •  flagsには0を渡すので既存のnamespaceの参照カウンタ が増えるだけ 3.proc_ns_operations構造体のinstall()を呼んでnsproxyに対 象のnamespaceを設定 4.switch_task_namespaces()でnamespaceの切り替えを実施
  43. 43. unshare() 1.unshareするnamespaceの取得 2.ファイルシステムのunshare 3. currentタスクのfs_struct構造体がコピーされる 4.ファイルディスクリプタのコピー • 開いているファイルディスクリプタをdup_fd()でコピー 5.user namespaceの分離 • CLONE_NEWUSERがセットされていなければ何もしない
  44. 44. unshare() ! 6.unshare_nsproxy_namespaces()でその他namespaceの分離 • 実際の処理はcreate_new_namespaces()で実施 7.上記までの操作で何かしら実行が行われた場合は以下の処理を実施 • namepace(nsproxy)の変更があった場合はswitch_task_namespaces()で切 り替え • fs_structをコピーした場合はcurrentタスクのfs_struct構造体切り替え 8. ファイルディスクリプタをコピーした場合はcurrentタスクのファイルディスクリ プタ切り替え 9. user_nsのunshareをした場合はstruct credの切り替え
  45. 45. Namespace Example • Namespace機能でどのように変わったか
  46. 46. getpid() - 2.4.37 • task_struct構造体のメンバ変数(pid)をそのまま 返却可能 #define getpid() (current->pid)
  47. 47. getpid() - 3.16 •プロセスが所属しているpid namespaceのpidを 返す必要がある getpid() -> task_tgid_vnr() -> task_tgid() -> pid_vnr() -> task_active_pid_ns() -> task_pid() -> ns_of_pid() -> pid_nr_ns()
  48. 48. chown() - 2.4.37 static int chown_common(struct dentry * dentry, uid_t user, gid_t group) { ~中略~ if (user == (uid_t) -1) user = inode->i_uid; if (group == (gid_t) -1) group = inode->i_gid; newattrs.ia_mode = inode->i_mode; newattrs.ia_uid = user; newattrs.ia_gid = group;
  49. 49. chown() - 3.16 static int chown_common(struct path *path, uid_t user, gid_t group) { ~中略~ uid = make_kuid(current_user_ns(), user); gid = make_kgid(current_user_ns(), group); ! newattrs.ia_valid = ATTR_CTIME; if (user != (uid_t) -1) { if (!uid_valid(uid)) return -EINVAL; newattrs.ia_valid |= ATTR_UID; newattrs.ia_uid = uid; } if (group != (gid_t) -1) { if (!gid_valid(gid)) return -EINVAL; newattrs.ia_valid |= ATTR_GID; newattrs.ia_gid = gid; }
  50. 50. Reference • LXCで学ぶコンテナ入門 -軽量仮想化環境を実現する技術 • http://gihyo.jp/admin/serial/01/linux_containers • Namespaces in Operation series • http://lwn.net/Articles/531114/#series_index • Professional Linux Kernel Architecture • http://www.amazon.co.jp/dp/B004T6ICZ6

×