eBPF で UNIXドメインソケットの通信内容をダンプ

UNIXドメインソケットの通信内容をダンプする方法はないかと調べていたところ eBPF（extended Berkley Packet Filter）を用いた [sockdump](https://github.com/mechpen/sockdump) を見つけたので eBPF の勉強も兼ねて試しました。ダンプ結果が見易くカスタマイズもしやすかったので紹介します。

- **sockdump**

https://github.com/mechpen/sockdump 

# eBPF について
次の記事が分かり易かったです。

https://caddi.tech/archives/3880

> eBPF（extended Berkley Packet Filter）は、Linuxカーネル内でのイベント発生時に動作する処理を、安全・手軽に組み込むための仕組みで、現在ではLinuxカーネルの機能として提供されています。



# 実行環境
```shell_session
$ uname -a
Linux debian11 5.10.0-21-amd64 #1 SMP Debian 5.10.162-1 (2023-01-21) x86_64 GNU/Linux

$ docker --version
Docker version 23.0.4, build f480fb1
```

# 実行
Docker の UNIXドメインソケット `/run/docker.sock` をダンプします。

sockdump プロジェクトを取得。とてもシンプルなプロジェクトです。

```shell-session
$ git clone https://github.com/mechpen/sockdump.git

$ cd sockdump/

$ tree .
.
├── LICENSE
├── README.md
├── sockdump.py
└── wireshark
    ├── dummy.lua
    └── wireshark.jpg
```

`sockdump.py` を実行しダンプを開始します。実行には [BCC ツールキット](https://github.com/iovisor/bcc)が必要ですが、環境によってはインストールがすんなりいかず手間取ることがあったので、ここでは BCC コンテナ [`quay.io/iovisor/bcc`](https://quay.io/repository/iovisor/bcc) を用います。

```shell-session
$ docker run -d --rm --name sockdump --privileged \
  -v /sys/kernel/debug:/sys/kernel/debug:rw \
  -v /lib/modules/:/lib/modules:ro \
  -v /usr/src:/usr/src:ro \
  -v $(pwd):/app/ \
  quay.io/iovisor/bcc \
  python3 /app/sockdump.py --format string --output /app/dump.log /run/docker.sock
```

上記は、ダンプ結果をファイル `dump.log` に保存します。標準出力に出力するとその出力自体が UNIXドメインソケット `/run/docker.sock` を経由しダンプされる再帰的ループに陥るためです。

:::note info
次のエラーメッセージが表示される場合は多分、Linuxカーネルヘッダが存在しないことが原因です。カーネルヘッダをインストールしてください。
```
...
modprobe: FATAL: Module kheaders not found in directory /lib/modules/5.10.0-21-amd64
Unable to find kernel headers. Try rebuilding kernel with CONFIG_IKHEADERS=m (module) or installing the kernel development package for your running kernel version.
...
```

Linux カーネルヘッダをインストール
```shell_session
$ apt update
$ apt install -y linux-headers-$(uname -r)
```
※ インストール方法は、ご利用の環境により異なります。
:::

:::note info
Linux カーネルヘッダの有無を確認
```shell_session
$ ls -d /usr/src/*$(uname -r)
/usr/src/linux-headers-5.10.0-21-amd64
```
:::


ダンプ結果ファイル `dump.log` への追記を`tail` コマンドで監視
```shell_session
$ tail -f dump.log
```

別のターミナルで Docker コマンドを実行
```shell_session
$ docker images
REPOSITORY            TAG       IMAGE ID       CREATED         SIZE
quay.io/iovisor/bcc   latest    80f2833ca697   14 months ago   596MB
```

ダンプ結果
```shell_session
06:29:35.824 >>> process docker [27806 -> 1] path /run/docker.sock len 80(80)
HEAD /_ping HTTP/1.1
Host: docker
User-Agent: Docker-Client/23.0.4 (linux)

06:29:35.825 >>> process dockerd [2434 -> 27806] path /run/docker.sock len 316(316)
HTTP/1.1 200 OK
Api-Version: 1.42
Builder-Version: 2
Cache-Control: no-cache, no-store, must-revalidate
Content-Length: 0
Content-Type: text/plain; charset=utf-8
Docker-Experimental: false
Ostype: linux
Pragma: no-cache
Server: Docker/23.0.4 (linux)
Swarm: inactive
Date: Wed, 26 Apr 2023 06:29:35 GMT

06:29:35.825 >>> process docker [27806 -> 1] path /run/docker.sock len 91(91)
GET /v1.42/images/json HTTP/1.1
Host: docker
User-Agent: Docker-Client/23.0.4 (linux)

06:29:35.832 >>> process dockerd [2434 -> 27806] path /run/docker.sock len 559(559)
HTTP/1.1 200 OK
Api-Version: 1.42
Content-Type: application/json
Docker-Experimental: false
Ostype: linux
Server: Docker/23.0.4 (linux)
Date: Wed, 26 Apr 2023 06:29:35 GMT
Content-Length: 357

[{"Containers":-1,"Created":1645387381,"Id":"sha256:80f2833ca6972eadfb85ea2decf1151f4004ea03d239e5137f18a53a454a7862","Labels":null,"ParentId":"","RepoDigests":["quay.io/iovisor/bcc@sha256:63f8262abfa9e8fc531f23c960b27736c75d1f13fff20d9c34d3387391232dd9"],"RepoTags":["quay.io/iovisor/bcc:latest"],"SharedSize":-1,"Size":595954813,"VirtualSize":595954813}]
```

UNIXドメインソケット通信内容の HTTPリクエストとレスポンスを確認できました。

試しに最後の HTTPリクエスト `GET /v1.42/images/json` を Curlコマンドで UNIXドメインソケットへ送信すると、同じレスポンスが返ってきました。

```shell_session
$ curl --unix-socket /run/docker.sock http://docker/v1.42/images/json
[{"Containers":-1,"Created":1645387381,"Id":"sha256:80f2833ca6972eadfb85ea2decf1151f4004ea03d239e5137f18a53a454a7862","Labels":null,"ParentId":"","RepoDigests":["quay.io/iovisor/bcc@sha256:63f8262abfa9e8fc531f23c960b27736c75d1f13fff20d9c34d3387391232dd9"],"RepoTags":["quay.io/iovisor/bcc:latest"],"SharedSize":-1,"Size":595954813,"VirtualSize":595954813}]
```

レスポンスボディの JSON をフォーマット
```json
[
    {
        "Containers": -1,
        "Created": 1645387381,
        "Id": "sha256:80f2833ca6972eadfb85ea2decf1151f4004ea03d239e5137f18a53a454a7862",
        "Labels": null,
        "ParentId": "",
        "RepoDigests": [
            "quay.io/iovisor/bcc@sha256:63f8262abfa9e8fc531f23c960b27736c75d1f13fff20d9c34d3387391232dd9"
        ],
        "RepoTags": [
            "quay.io/iovisor/bcc:latest"
        ],
        "SharedSize": -1,
        "Size": 595954813,
        "VirtualSize": 595954813
    }
]
```

ダンプを終了
```
$ docker rm -f sockdump
```


# 参考文献


https://qiita.com/sg-matsumoto/items/8194320db32d4d8f7a16#1-bpf_trace_printk

https://mmi.hatenablog.com/entry/2020/12/02/031534

- BCC ツールキットのインストール手順

https://github.com/iovisor/bcc/blob/master/INSTALL.md