カーネル2.6.24以降、Linuxは6種類の名前空間をサポートしています。名前空間は、完全な低レベルの仮想化テクノロジを使用しなくても、システムの他の部分からより分離されたプロセスを作成するのに役立ちます。
- CLONE_NEWIPC:IPC名前空間:SystemVIPCとPOSIXメッセージキューを分離できます。
- CLONE_NEWPID:PID名前空間:PIDは分離されています。つまり、名前空間内の仮想PIDは、名前空間外のPIDと競合する可能性があります。名前空間内のPIDは、名前空間外の他のPIDにマップされます。名前空間内の最初のPIDは「1」になり、名前空間外ではinitに割り当てられます
- CLONE_NEWNET:ネットワーク名前空間:ネットワーク(/ proc / net、IP、インターフェース、およびルート)は分離されています。サービスは名前空間内の同じポートで実行でき、「重複した」仮想インターフェイスを作成できます。
- CLONE_NEWNS:名前空間をマウントします。プロセスに表示されるマウントポイントを分離する機能があります。マウント名前空間を使用すると、chroot()と同様の機能を実現できますが、セキュリティが向上します。
- CLONE_NEWUTS:UTS名前空間。この名前空間の主な目的は、ホスト名とNIS名を分離することです。
- CLONE_NEWUSER:ユーザー名前空間。ここで、ユーザーIDとグループIDは名前空間の内部と外部で異なり、複製できます。
まず、プロセスの名前空間を示すために必要なCプログラムの構造を見てみましょう。以下はDebian6および7でテストされています。最初に、スタックにメモリのページを割り当て、そのメモリページの最後にポインタを設定する必要があります。ヒープにメモリを割り当てるmallocではなく、allocaを使用してスタックメモリを割り当てます。
void *mem = alloca(sysconf(_SC_PAGESIZE)) + sysconf(_SC_PAGESIZE);
次に、cloneを使用して子プロセスを作成し、子スタック'mem'の場所と、新しい名前空間を指定するために必要なフラグを渡します。子スペース内で実行する関数として「callee」を指定します:
mypid = clone(callee, mem, SIGCHLD | CLONE_NEWIPC | CLONE_NEWPID | CLONE_NEWNS | CLONE_FILES, NULL);
cloneを呼び出した後、子プロセスが終了するのを待ってから、親を終了します。そうでない場合、親の実行フローは続行され、直後に終了し、子をクリアします:
while (waitpid(mypid, &r, 0) < 0 && errno == EINTR) { continue; }
最後に、子の終了コードを使用してシェルに戻ります。
if (WIFEXITED(r)) { return WEXITSTATUS(r); } return EXIT_FAILURE;
それでは、呼び出し先関数を見てみましょう:
static int callee() { int ret; mount("proc", "/proc", "proc", 0, ""); setgid(u); setgroups(0, NULL); setuid(u); ret = execl("/bin/bash", "/bin/bash", NULL); return ret; }
ここでは、/ procファイルシステムをマウントし、uid(ユーザーID)とgid(グループID)を'u'の値に設定してから、/ bin/bashシェルを生成します。 LXCは、リソースの分離にcgroupと名前空間を利用するOSレベルの仮想化ツールです。 「u」を65534に設定して、すべてをまとめましょう。これは、Debianのユーザー「nobody」とグループ「nogroup」です。
#define _GNU_SOURCE #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/wait.h> #include <sys/mount.h> #include <grp.h> #include <alloca.h> #include <errno.h> #include <sched.h> static int callee(); const int u = 65534; int main(int argc, char *argv[]) { int r; pid_t mypid; void *mem = alloca(sysconf(_SC_PAGESIZE)) + sysconf(_SC_PAGESIZE); mypid = clone(callee, mem, SIGCHLD | CLONE_NEWIPC | CLONE_NEWPID | CLONE_NEWNS | CLONE_FILES, NULL); while (waitpid(mypid, &r, 0) < 0 && errno == EINTR) { continue; } if (WIFEXITED(r)) { return WEXITSTATUS(r); } return EXIT_FAILURE; } static int callee() { int ret; mount("proc", "/proc", "proc", 0, ""); setgid(u); setgroups(0, NULL); setuid(u); ret = execl("/bin/bash", "/bin/bash", NULL); return ret; }
コードを実行すると、次のようになります。
[email protected]:~/pen/tmp# gcc -O -o ns.c -Wall -Werror -ansi -c89 ns.c [email protected]:~/pen/tmp# ./ns [email protected]:~/pen/tmp$ id uid=65534(nobody) gid=65534(nogroup) [email protected]:~/pen/tmp$ ps auxw USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND nobody 1 0.0 0.0 4620 1816 pts/1 S 21:21 0:00 /bin/bash nobody 5 0.0 0.0 2784 1064 pts/1 R+ 21:21 0:00 ps auxw [email protected]:~/pen/tmp$
UIDとGIDがnobodyとnogroupのものに設定されていることに注意してください。特に、完全なps出力には、実行中のプロセスが2つだけ表示され、それらのPIDはそれぞれ1と5であることに注意してください。それでは、ipnetnsを使用してネットワーク名前空間を操作することに移りましょう。まず、現在名前空間が存在しないことを確認しましょう:
[email protected]:~# ip netns list Object "netns" is unknown, try "ip help".
この場合、ipがアップグレードを必要とするか、カーネルがアップグレードを必要とします。 2.6.24より新しいカーネルを使用していると仮定すると、おそらくipです。アップグレード後、ipnetnslistはデフォルトで何も返さないはずです。 'ns1'という名前の新しい名前空間を追加しましょう:
[email protected]:~# ip netns add ns1 [email protected]:~# ip netns list ns1
まず、現在のインターフェースをリストしましょう:
[email protected]:~# ip link list 1: lo:mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: eth0: mtu 1500 qdisc pfifo_fast state UNKNOWN mode DEFAULT qlen 1000 link/ether 00:0c:29:65:25:9e brd ff:ff:ff:ff:ff:ff
次に、新しい仮想インターフェイスを作成し、それを新しい名前空間に追加します。仮想インターフェイスはペアで作成され、相互にリンクされています。仮想クロスケーブルを想像してみてください。
[email protected]:~# ip link add veth0 type veth peer name veth1 [email protected]:~# ip link list 1: lo:ifconfig -aは、veth0とveth1の両方の追加も表示します。mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: eth0: mtu 1500 qdisc pfifo_fast state UNKNOWN mode DEFAULT qlen 1000 link/ether 00:0c:29:65:25:9e brd ff:ff:ff:ff:ff:ff 3: veth1: mtu 1500 qdisc noop state DOWN mode DEFAULT qlen 1000 link/ether d2:e9:52:18:19:ab brd ff:ff:ff:ff:ff:ff 4: veth0: mtu 1500 qdisc noop state DOWN mode DEFAULT qlen 1000 link/ether f2:f7:5e:e2:22:ac brd ff:ff:ff:ff:ff:ff
これで、新しいインターフェイスを名前空間に割り当てることができます。名前空間内でコマンドを実行するには、ipnetnsexecが使用されることに注意してください。
[email protected]:~# ip link set veth1 netns ns1 [email protected]:~# ip netns exec ns1 ip link list 1: lo:veth1はns1名前空間にあるため、ifconfig-aはveth0のみを表示するようになりました。mtu 65536 qdisc noop state DOWN mode DEFAULT link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 3: veth1: mtu 1500 qdisc noop state DOWN mode DEFAULT qlen 1000 link/ether d2:e9:52:18:19:ab brd ff:ff:ff:ff:ff:ff
veth0 / veth1:
を削除する必要がありますip netns exec ns1 ip link del veth1
これで、ホストのveth0にIPアドレス192.168.5.5/24を割り当てることができます:
ifconfig veth0 192.168.5.5/24
そして、ns1内にveth1 192.168.5.10/24を割り当てます。
ip netns exec ns1 ifconfig veth1 192.168.5.10/24 up
ホストと名前空間の両方でipaddrlistを実行するには:
[email protected]:~# ip addr list 1: lo:mtu 65536 qdisc noqueue state UNKNOWN link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: eth0: mtu 1500 qdisc pfifo_fast state UNKNOWN qlen 1000 link/ether 00:0c:29:65:25:9e brd ff:ff:ff:ff:ff:ff inet 192.168.3.122/24 brd 192.168.3.255 scope global eth0 inet6 fe80::20c:29ff:fe65:259e/64 scope link valid_lft forever preferred_lft forever 6: veth0: mtu 1500 qdisc pfifo_fast state UP qlen 1000 link/ether 86:b2:c7:bd:c9:11 brd ff:ff:ff:ff:ff:ff inet 192.168.5.5/24 brd 192.168.5.255 scope global veth0 inet6 fe80::84b2:c7ff:febd:c911/64 scope link valid_lft forever preferred_lft forever [email protected]:~# ip netns exec ns1 ip addr list 1: lo: mtu 65536 qdisc noop state DOWN link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 5: veth1: mtu 1500 qdisc pfifo_fast state UP qlen 1000 link/ether 12:bd:b6:76:a6:eb brd ff:ff:ff:ff:ff:ff inet 192.168.5.10/24 brd 192.168.5.255 scope global veth1 inet6 fe80::10bd:b6ff:fe76:a6eb/64 scope link valid_lft forever preferred_lft forever
名前空間の内外のルーティングテーブルを表示するには:
[email protected]:~# ip route list default via 192.168.3.1 dev eth0 proto static 192.168.3.0/24 dev eth0 proto kernel scope link src 192.168.3.122 192.168.5.0/24 dev veth0 proto kernel scope link src 192.168.5.5 [email protected]:~# ip netns exec ns1 ip route list 192.168.5.0/24 dev veth1 proto kernel scope link src 192.168.5.10
最後に、物理インターフェイスと仮想インターフェイスを接続するには、ブリッジが必要です。ホストでeth0とveth0をブリッジしてから、DHCPを使用してns1名前空間内のIPを取得しましょう。
[email protected]:~# brctl addbr br0 [email protected]:~# brctl addif br0 eth0 [email protected]:~# brctl addif br0 veth0 [email protected]:~# ifconfig eth0 0.0.0.0 [email protected]:~# ifconfig veth0 0.0.0.0 [email protected]:~# dhclient br0 [email protected]:~# ip addr list br0 7: br0:mtu 1500 qdisc noqueue state UP link/ether 00:0c:29:65:25:9e brd ff:ff:ff:ff:ff:ff inet 192.168.3.122/24 brd 192.168.3.255 scope global br0 inet6 fe80::20c:29ff:fe65:259e/64 scope link valid_lft forever preferred_lft forever
br0には192.168.3.122/24のIPが割り当てられています。名前空間について:
[email protected]:~# ip netns exec ns1 dhclient veth1 [email protected]:~# ip netns exec ns1 ip addr list 1: lo:mtu 65536 qdisc noop state DOWN link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 5: veth1: mtu 1500 qdisc pfifo_fast state UP qlen 1000 link/ether 12:bd:b6:76:a6:eb brd ff:ff:ff:ff:ff:ff inet 192.168.3.248/24 brd 192.168.3.255 scope global veth1 inet6 fe80::10bd:b6ff:fe76:a6eb/64 scope link valid_lft forever preferred_lft forever
素晴らしい! veth1には192.168.3.248/24が割り当てられています
IO Digital Sec
Linuxコンサルタント