nsenter で Linux namespace に触れる

## namespace を一覧化する

`lsns`で namespace の一覧を見ることができます。コンテナで bash を実行するコンテナを作った状態で`lsns`を実行した結果が以下です。複数の TYPE の namespace ができていることがわかります。

```bash
# lsns -o NS,TYPE,PATH,NPROCS,PID,PPID,COMMAND,UID,USER
        NS TYPE   PATH               NPROCS   PID  PPID COMMAND                       UID USER
4026531835 cgroup /proc/1/ns/cgroup     126     1     0 /sbin/init                      0 root
4026531836 pid    /proc/1/ns/pid        124     1     0 /sbin/init                      0 root
4026531837 user   /proc/1/ns/user       126     1     0 /sbin/init                      0 root
4026531838 uts    /proc/1/ns/uts        124     1     0 /sbin/init                      0 root
4026531839 ipc    /proc/1/ns/ipc        124     1     0 /sbin/init                      0 root
4026531840 mnt    /proc/1/ns/mnt        120     1     0 /sbin/init                      0 root
4026531861 mnt    /proc/19/ns/mnt         1    19     2 kdevtmpfs                       0 root
4026531993 net    /proc/1/ns/net        124     1     0 /sbin/init                      0 root
4026532157 mnt    /proc/446/ns/mnt        1   446     1 /lib/systemd/systemd-udevd      0 root
4026532167 mnt    /proc/490/ns/mnt        1   490     1 /lib/systemd/systemd-networkd 100 systemd-network
4026532168 mnt    /proc/518/ns/mnt        1   518     1 /lib/systemd/systemd-resolved 101 systemd-resolve
4026532193 mnt    /proc/27142/ns/mnt      2 27142 27104 bash                            0 root
4026532194 uts    /proc/27142/ns/uts      2 27142 27104 bash                            0 root
4026532195 ipc    /proc/27142/ns/ipc      2 27142 27104 bash                            0 root
4026532196 pid    /proc/27142/ns/pid      2 27142 27104 bash                            0 root
4026532198 net    /proc/27142/ns/net      2 27142 27104 bash                            0 root
```

コンテナ内で動いているプロセスの PID（この例では、27142） が`lsns`と一致していることがわかります。

```bash
# docker ps
CONTAINER ID   IMAGE               COMMAND   CREATED          STATUS          PORTS     NAMES
ef0425afad12   nicolaka/netshoot   "bash"    54 minutes ago   Up 54 minutes             vigilant_hopper
# docker inspect --format {{.State.Pid}} ef0425afad12
27142
```

## namespace に入る

`nsenter`コマンドを使うと namespace の中に入ってコマンドを実行することができて便利です。例えば、コンテナ内には netstat コマンドが入っていないけれど、実行したい時は以下のように、`--target PID`で対象プロセスを指定します。network namespace に入る場合は、`--net`指定します。

```bash
# nsenter --target 27142 --net netstat -ant
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State
tcp        0      0 172.17.0.2:53400        172.217.175.238:80      TIME_WAIT
```

`--mount`を指定すると mount namespace になります。`--all`で全てのタイプの namespace に入ります。

```bash
# nsenter --target 27142 --all cat /etc/os-release
NAME="Alpine Linux"
ID=alpine
VERSION_ID=3.13.2
PRETTY_NAME="Alpine Linux v3.13"
HOME_URL="https://alpinelinux.org/"
BUG_REPORT_URL="https://bugs.alpinelinux.org/"
```

## ネットワークの階層モデルと namespace

### namespace の影響受けるもの

`netstat`コマンドは namespace によって見える範囲が変わります。例えば、コンテナ内から `curl http://google.com/` を実行した直後の状態です。

```bash
# nsenter --target 27142 --all netstat -ant | grep 80
tcp        0      0 172.17.0.2:34148        216.58.197.206:80       TIME_WAIT

# netstat -ant | grep 80
#   何も表示されない
```

`arp`コマンドも同様です。

```bash
# nsenter --target 27142 --all arp
? (172.17.0.1) at 02:42:b0:b9:9b:50 [ether]  on eth0
# arp
Address                  HWtype  HWaddress           Flags Mask            Iface
172.17.0.2               ether   02:42:ac:11:00:02   C                     docker0
10.0.2.3                 ether   52:54:00:12:35:03   C                     eth0
_gateway                 ether   52:54:00:12:35:02   C                     eth0
```

### namespace の影響受けないもの

一方、`tcpdump` コマンドは namespace の影響受けません。コンテナ内から `curl http://google.com/` を実行した時の結果です。`tcpdump`はより低レイヤー（データリンク層 L2）の情報を取得するからのようです。

```bash
# コンテナ内部で tcpdump を実行した結果
13:19:41.076077 IP a8c89c57d8ef.40442 > nrt12s36-in-f14.1e100.net.80: Flags [P.], seq 0:74, ack 1, win 64240, length 74: HTTP: GET / HTTP/1.1

# コンテナ外部で tcpdump を実行した結果
13:19:41.076207 IP vagrant.40442 > nrt12s36-in-f14.1e100.net.http: Flags [P.], seq 0:74, ack 1, win 64240, length 74: HTTP: GET / HTTP/1.1
```

コンテナ外部で `iptraf-ng` を使うと、コンテナ内から張った TCP コネクションを一覧化することが出来て便利です。
![iptraf-ng.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/376039/5e23d65f-96ce-49f9-9fc7-f5b2aff9cbf0.jpeg)

## 参考

https://qiita.com/minamijoyo/items/9dd59109e9fe1a35f888
https://github.com/nicolaka/netshoot