Más contenido relacionado La actualidad más candente (20) Similar a Linux Namespace (20) Más de Masami Ichikawa (7) Linux Namespace2. Table of Contents
• Namespace overview
• System calls
• kernel implementation
• Namespace Example
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]
9. uts namespace
• ホスト名、ドメイン名などのデータ
• カーネルバージョン等もあるが変更不可
• ゲストが自ホスト名を変えてもホストOS側には
影響はでない
10. net namespace
• ネットワーク関連のリソース
• Network device
• IP address
• Routing table
• Filtering table
• Port number
• /proc/net
• etc…
11. pid namespace
• 親プロセスとは別のpidを利用可能に
• namespace Aのpid:1000とnamespace Bの
pid:1000は別の存在
• procfsを適切に分ければ他のnamespceのプロ
セスを参照できなくなる
12. mnt namespace
• マウントしているファイルシステムを表す
• 名前空間分離時は親プロセスのmnt namespace
をコピー
• 分離後に親プロセスがusb stickなどをマウン
トしてもゲスト側からは見えない
14. user namespace
• ホストとは別のuid/gid体系を持てる
• ホストのuid/gidとゲストのuid/gidマッピングが必要
• 設定しないと65534が設定される
• groupsは65534番のgroup入りする
• 他のnamespaceと違い、独立していない
• 他のnamespace空間は個々にuser namespaceを持っている
• 各namespaceのコピー処理関数はuser nsを受け取るので
get_user_ns()で参照カウントを増やしている
15. uid/gid mapping
• マッピングを行うシステムコールは無い
• 以下のファイルを用いてマッピングを実施
• /proc/<pid>/uid_map
• /proc/<pid>/gid_map
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. 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. 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()系の関数を呼んで新たなプログラムを実行
21. clone(2)
• clone(2)でpid namespaceを分離した場
• 子プロセスから見ると自分のpidは1として見える
• 親プロセスからは親プロセスのpid namespaceでの
pidが振られたように見える
• clone(2)の戻り値を使ってwaitpid(2)で待つこと
ができる
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. setns(2)
• 既存の名前空間に自身を参加させる
• clone(2)、unshare(2)は親の名前空間から分離して新規の
名前空間を持つようになる
• 名前空間への参加には対象の名前空間のファイルディスクリプ
タを使用する
• 別のpid namespaceに参加する場合、プロセス自身のpidは
変わらない
• 子プロセスからpidが変わる
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を参照
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
};
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. copy_namespace()
• int copy_namespaces(unsigned long flags, struct
task_struct *tsk)
• copy_process()より呼ばれる
• flagにCLONE_NEWXXXがセットされていなければカ
レントプロセスのnsproxy構造体の参照カウンタを+1
• そうでなければcreate_new_namespace()で該当する
namespaceを作成する
35. switch_task_namespace()
• void switch_task_namespaces(struct
task_struct *tsk, struct nsproxy *new)
• カレントプロセスの名前空間切り替えを行う
• unshare(2)で使用
• プロセスのexit()時にも使用
• 切り替え先のnamespaceにNULLを設定
37. free_nsproxy()
• void free_nsproxy(struct nsproxy *ns)
• namespaceの解放
• 各namespaceの参照カウンタをデクリメント
• nsproxy構造体のインスタンス解放
• 通常はプロセスのexit時に実行される
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. 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. procfs operations
• get()
• 対象namespaceの参照カウンタを+1
• put()
• 対象namespaceの参照カウンタを-1
• install()
• 現在のnamespaceの参照カウンタを-1し、新しいnamespaceをnsproxy構
造体にセット
• inum()
• namespaceのinode番号を返す
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の切り替え
46. getpid() - 2.4.37
• task_struct構造体のメンバ変数(pid)をそのまま
返却可能
#define getpid() (current->pid)
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. 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. 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. 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