OpenShift上のコンテナのパケットキャプチャファイルを取得する

## はじめに

OpenShift上のコンテナのトラフィックを解析するにあたり、色々と模索したので備忘録として残します。
ここでは以下の2つの方法でパケットキャプチャファイルを取得し、Wiresharkでの解析を行いました。

- サイドカーコンテナー(対象のコンテナと同じPodに存在)
- ksniff

まだまだ勉強中であるため、間違い等あるかと思いますが、ご了承ください。

## 参考情報

- [Packet capture inside Pod using community ksniff with OpenShift 4](https://access.redhat.com/articles/5436111)
- [Network Capture on OpenShift 4 with tcpdump and ksniff](https://two-oes.medium.com/network-capture-on-openshift-4-with-tcpdump-and-ksniff-5ed8ae296b5b)

## 環境情報

OpenShift Container Platform 4.7

## パケットキャプチャファイル取得方法1: サイドカーコンテナ

同じPod内であれば、コンテナはネットワーク的に分離されておらず、IPアドレスを共有しています。

そのため、パケットキャプチャの取得対象のコンテナに対して、パケットをサイドカーコンテナからキャプチャすることができます。

サイドカーコンテナを使用してパケットキャプチャファイルを取得するためには、それ用にイメージを自作する、もしくは、既存のパブリックなコンテナイメージを使用します。

今回は、`tcpdump`コマンドなどネットワークデバッグに必要なコマンドがインストールされている[nicolaka/netshoot](https://hub.docker.com/r/nicolaka/netshoot)というパブリックのコンテナイメージを使用しています。

### ログ

- 全文は省略しますが、yamlファイルでキャプチャしたいコンテナと合わせてサイドカーコンテナを定義します。
  - ここでは、debugという名前でサイドカーコンテナを作成します。

```yaml
spec:
  template:
    spec:
      containers:
        # MQ Container
        - image: 'ibmcom/mq:9.2.2.0-r1'
          name: qmgr
              :
        # Sidecar Container for tcpdump
        - image: nicolaka/netshoot
          name: debug
          command:
            - /bin/sh
            - -c
            - sleep 3600
:
```

- yamlファイルの作成など必要な準備が完了したら、Podをデプロイします。

```s
oc process -f ./xxx.yml | oc apply -f -
```

- debugコンテナに`tcpdump`コマンドが存在することを確認します。

```s
$ oc exec -it oqm1-0 -c debug -- tcpdump -h
tcpdump version 4.99.1
libpcap version 1.10.1 (with TPACKET_V3)
OpenSSL 1.1.1k  25 Mar 2021
Usage: tcpdump [-AbdDefhHIJKlLnNOpqStuUvxX#] [ -B size ] [ -c count ] [--count]
		[ -C file_size ] [ -E algo:secret ] [ -F file ] [ -G seconds ]
		[ -i interface ] [ --immediate-mode ] [ -j tstamptype ]
		[ -M secret ] [ --number ] [ --print ] [ -Q in|out|inout ]
		[ -r file ] [ -s snaplen ] [ -T type ] [ --version ]
		[ -V file ] [ -w file ] [ -W filecount ] [ -y datalinktype ]
		[ --time-stamp-precision precision ] [ --micro ] [ --nano ]
		[ -z postrotate-command ] [ -Z user ] [ expression ]
```

- サービスアカウントの作成とSCCの付与します。
  - debugコンテナはrootで動作するため
  - ここでは`anyuid`のsccを持つサービスアカウント`debug`を作成する。

```s
$ oc adm policy add-scc-to-user anyuid -z default
clusterrole.rbac.authorization.k8s.io/system:openshift:scc:anyuid added: "default"
```

=> Podを再起動し、適用します。

- `tcpdump`コマンドを実行してみます。

```s
$ oc exec -it oqm1-0 -c debug -- tcpdump
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
^C
0 packets captured
0 packets received by filter
0 packets dropped by kernel
```

=>  これで、パケットをキャプチャできることが確認できました。


- パケットキャプチャの取得
  - 対象のコンテナを動作させ、その間のパケットをキャプチャし、パケットキャプチャファイルを生成します。

```s
$ oc exec -it oqm1-0 -c debug -- tcpdump -w /tmp/tcpdump.hb60.cap
tcpdump: listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes

:(パケットキャプチャ中)

// 終了する場合には「ctl+c」
$ oc exec -it oqm1-0 -c debug -- tcpdump -w /tmp/tcpdump.hb60.cap
tcpdump: listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
^C
39 packets captured
39 packets received by filter
0 packets dropped by kernel
```

- debugコンテナにパケットキャプチャファイルが作成されていることを確認します。

```s
$ oc exec -it oqm1-0 -c debug -- ls -l /tmp/
total 12
-rw-r--r--    1 root     root         11292 Aug 25 12:24 tcpdump.hb60.cap
```

- 今回は、ローカル環境のWiresharkで解析したいため、OpenShiftからローカル環境(~/Desktop)にパケットキャプチャファイルをコピーします。

```s
$ oc cp -c debug oqm1-0:/tmp/tcpdump.hb60.cap Desktop/tcpdump.hb60.cap
tar: removing leading '/' from member names
```

- ローカル環境にコピーできました。

```s
$ ls -l Desktop | grep tcp
-rw-r--r--@  1 xxxxxxxxx  staff   11292  8 25 21:26 tcpdump.hb60.cap
```

=> これで、Wiresharkに取り込んで、解析することができます。


## パケットキャプチャファイル取得方法2: oc sniff

yamlが変更できない(サイドカーコンテナを起動できない)場合でも、パケットキャプチャをしたかったので、サイドカーコンテナ以外の方法がないかも検討しました。

[Packet capture inside Pod using community ksniff with OpenShift 4](https://access.redhat.com/articles/5436111)で紹介されているksniffを使用すれば、サイドカーコンテナがなくてもパケットキャプチャができそうなので試してみました。

ただし、「ksniffはコミュニティプロジェクトであるため、Red Hatが直接メンテナンスしているわけではありません。何か問題が発生した場合や使い勝手について質問がある場合は、GitHubでissueを作成」とのことです。

### ログ

参考情報を元にインストールしていきます。

- まず、ksniffのzipファイルをダウンロードします。

```s
$ wget https://github.com/eldadru/ksniff/releases/download/v1.6.0/ksniff.zip
--2021-08-31 20:31:08--  https://github.com/eldadru/ksniff/releases/download/v1.6.0/ksniff.zip
github.com (github.com) をDNSに問いあわせています... 52.69.186.44
github.com (github.com)|52.69.186.44|:443 に接続しています... 接続しました。
HTTP による接続要求を送信しました、応答を待っています... 302 Found
場所: https://github-releases.githubusercontent.com/137678747/1b61f880-821d-11eb-8933-e9e362793cd9?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIWNJYAX4CSVEH53A%2F20210831%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20210831T113109Z&X-Amz-Expires=300&X-Amz-Signature=e8f12167ab7cbc40d6db9f412a8176c317dec818bd4db5ff3c3b221db6a0fb06&X-Amz-SignedHeaders=host&actor_id=0&key_id=0&repo_id=137678747&response-content-disposition=attachment%3B%20filename%3Dksniff.zip&response-content-type=application%2Foctet-stream [続く]
--2021-08-31 20:31:08--  https://github-releases.githubusercontent.com/137678747/1b61f880-821d-11eb-8933-e9e362793cd9?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIWNJYAX4CSVEH53A%2F20210831%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20210831T113109Z&X-Amz-Expires=300&X-Amz-Signature=e8f12167ab7cbc40d6db9f412a8176c317dec818bd4db5ff3c3b221db6a0fb06&X-Amz-SignedHeaders=host&actor_id=0&key_id=0&repo_id=137678747&response-content-disposition=attachment%3B%20filename%3Dksniff.zip&response-content-type=application%2Foctet-stream
github-releases.githubusercontent.com (github-releases.githubusercontent.com) をDNSに問いあわせています... 2606:50c0:8001::154, 2606:50c0:8002::154, 2606:50c0:8003::154, ...
github-releases.githubusercontent.com (github-releases.githubusercontent.com)|2606:50c0:8001::154|:443 に接続しています... 接続しました。
HTTP による接続要求を送信しました、応答を待っています... 200 OK
長さ: 53497723 (51M) [application/octet-stream]
`ksniff.zip' に保存中

ksniff.zip                           100%[=====================================================================>]  51.02M  7.51MB/s 時間 6.3s

2021-08-31 20:31:15 (8.09 MB/s) - `ksniff.zip' へ保存完了 [53497723/53497723]
```

- ダウンロードしたzipファイルを`unzip`します。

```s
$ unzip ksniff.zip
Archive:  ksniff.zip
  inflating: kubectl-sniff
  inflating: kubectl-sniff-windows
  inflating: kubectl-sniff-darwin
  inflating: static-tcpdump
  inflating: Makefile
  inflating: plugin.yaml
  inflating: LICENSE

$ mv kubectl-sniff /usr/local/bin/

$ oc plugin list
The following compatible plugins are available:

/usr/local/bin/kubectl-sniff
```

- `make install`コマンドでインストールします。

```s
$ make install
mkdir -p /usr/local/bin
cp kubectl-sniff-darwin /usr/local/bin/kubectl-sniff
cp plugin.yaml /usr/local/bin
cp static-tcpdump /usr/local/bin
```

- これで、`oc sniff`コマンドが使用可能になりました。

```s
$ oc sniff
Usage:
  sniff pod [-n namespace] [-c container] [-f filter] [-o output-file] [-l local-tcpdump-path] [-r remote-tcpdump-path] [flags]

Examples:
kubectl sniff hello-minikube-7c77b68cff-qbvsd -c hello-minikube

Flags:
  -c, --container string                container (optional)
  -x, --context string                  kubectl context to work on (optional)
  -f, --filter string                   tcpdump filter (optional)
  -h, --help                            help for sniff
      --image string                    the privileged container image (optional)
  -i, --interface string                pod interface to packet capture (optional) (default "any")
  -l, --local-tcpdump-path string       local static tcpdump binary path (optional)
  -n, --namespace string                namespace (optional)
  -o, --output-file string              output file path, tcpdump output will be redirect to this file instead of wireshark (optional) ('-' stdout)
      --pod-creation-timeout duration   the length of time to wait for privileged pod to be created (e.g. 20s, 2m, 1h). A value of zero means the creation never times out. (default 1m0s)
  -p, --privileged                      if specified, ksniff will deploy another pod that have privileges to attach target pod network namespace
  -r, --remote-tcpdump-path string      remote static tcpdump binary path (optional) (default "/tmp/static-tcpdump")
      --socket string                   the container runtime socket path (optional)
  -v, --verbose                         if specified, ksniff output will include debug information (optional)
```

- パケットキャプチャファイルを取得します。

```s
$ oc sniff -p oqm1-0 -n xxxxxxxx -o test.pcap
INFO[0000] no container specified, taking first container we found in pod.
INFO[0000] selected container: 'qmgr'
INFO[0000] sniffing method: privileged pod
INFO[0000] sniffing on pod: 'oqm1-0' [namespace: 'xxxxxxxx', container: 'qmgr', filter: '', interface: 'any']
INFO[0000] creating privileged pod on node: '10.244.128.7'
INFO[0000] pod created: &Pod{ObjectMeta:{ksniff-bzp6p ksniff- xxxxxxxx /api/v1/namespaces/xxxxxxxx/pods/ksniff-bzp6p 14c7fcae-4095-401d-9d34-b42652b407aa 42044091 

:

INFO[0000] waiting for pod successful startup
INFO[0005] pod: 'ksniff-bzp6p' created successfully on node: '10.244.128.7'
INFO[0005] executing command: '[chroot /host crictl inspect --output json f809f4e48680c26f38853f15bc5af7d2ae21c126706ae8c4c6a16de3c41cf2c8]' on container: 'ksniff-privileged', pod: 'ksniff-bzp6p', namespace: 'xxxxxxxx'
INFO[0006] command: '[chroot /host crictl inspect --output json f809f4e48680c26f38853f15bc5af7d2ae21c126706ae8c4c6a16de3c41cf2c8]' executing successfully exitCode: '0', stdErr :''
INFO[0006] output file option specified, storing output in: 'test.pcap'
INFO[0006] starting remote sniffing using privileged pod
INFO[0006] executing command: '[nsenter -n -t 193742 -- tcpdump -i any -U -w - ]' on container: 'ksniff-privileged', pod: 'ksniff-bzp6p', namespace: 'xxxxxxxx'
^C
```

- `oc sniff`コマンドを実行すると、専用のPodが自動作成されます。

```s
$ oc get pods -o wide

NAME            READY   STATUS    RESTARTS   AGE     IP              NODE           NOMINATED NODE   READINESS GATES
ksniff-bzp6p    1/1     Running   0          21m     172.17.18.132   10.244.128.7   <none>           <none>
oqm1-0          2/2     Running   156        6d11h   172.17.56.181   10.244.128.5   <none>           <none>
```

- `oc sniff`コマンドではパケットキャプチャファイルをローカル環境に直接作成できるので、少し便利です。