コンテナランタイムを自作した
コンテナの仕組みを勉強したかったため、Goでコンテナランタイムを自作した。雑実装だし未実装の機能もたくさんあるが、ある程度形になってきたため現状をまとめる。
リポジトリ
- kombu/dashi - 自作コンテナランタイム
- kombu/nimono - eBPFを利用したシステムコールロガー
- kombu/yaminabe - dashiとnimonoを利用したマルウェアサンドボックス
プロジェクト名から和の雰囲気を感じるが、これはリポジトリ名をkombu(昆布)にしたかったため、せっかくなら今回は和風で固めようと思ったから。趣があっていいんじゃないでしょうか。
dashiが自作コンテナランタイムだが、nimonoとyaminabeは実験的な要素で、セキュキャン2023でコンテナを使ったマルウェアサンドボックスを実装した経験があり、今回はその再実装を自作コンテナランタイムでやりたいなと思ったので実装している。言語としてGoを選択した理由は、Goを書いたことがなかったということと、コンテナを扱うOSSがGoで実装されている事例が多く見受けられたため。実際OCI(後述)のリファレンス実装でGoが使われており、ライブラリとしてそのまま使うことができたのでよかった。
コンテナを動かすために必要な技術
container-security.dev www.publickey1.jp
コンテナの仕組みを学ぶうえでこれらのサイトがとても役に立った。
低レベルランタイムと高レベルランタイム
コンテナランタイムは大きく分けて2種類あり、それぞれ役割分担しながらコンテナを実現している。
- 低レベルランタイム - 実際にLinuxの機能を呼び出してコンテナを起動する(例:runC、gVisor)
- 高レベルランタイム - コンテナイメージの管理などをしている(例:containerd)
Open Container Initiative (OCI)
www.opencontainers.org コンテナの技術仕様を標準化する組織。ランタイムやコンテナイメージの仕様を標準化している。Runtime Specification、Image Specification、Distribution Specificationといった仕様がある。コンテナソフトウェアといえばDockerが有名だが、DockerはこのOCIに準拠するにあたってソースコードが公開されるようになった。
コンテナイメージ
コンテナ環境をパッケージ化したもので、設定ファイル、ファイルシステムバンドルなどが含まれている。
Namespace
Linuxカーネルの機能で、Namespaceの内と外でシステムリソースを分離する。ネットワーク、ファイルシステム、PIDなどそれぞれ分離することができる。ファイルシステム、PIDを分離することによって、Namespaceの内側から外側のプロセスに干渉することができなくなる。コンテナの肝。
chroot, pivot_root
どちらもルートディレクトリ/
を別のディレクトリにすげ替える機能。各コンテナ向けに用意されたrootfsに切り替えるために用いる。chrootができる権限を持っていると簡単にコンテナエスケープできてしまうため、代わりにpivot_rootを利用したほうが安全。
cgroup
プロセスをグループ化し、そのグループに属するプロセスに対してリソースの制限を行う機能。CPU使用率、メモリ使用量など細かく制限することができる。
Capability
Linuxにおける権限の仕組みで、ファイルやプロセスに対して権限を設定する。例えば先述のchrootができる権限、ネットワーク通信ができる権限、プロセスをKillできる権限など、様々な種類がある。特権を持っていなくても、特定の権限を経由してコンテナエスケープすることができてしまうものもあるため、過剰な権限の付与は危険。
Seccomp
特定のシステムコールの実行を禁止する機能。Capabilityの制限を突破されてもSeccompによって防ぐことができる。
コンテナランタイムの実装
dashiはCLIツールとして実装したので、コマンドを入力して操作する。当初は低レベルランタイムという立ち位置で開発をしていたが、この段階ではまだ役割を分けるメリットが特にないため、1つのバイナリとして実装した。
[_____@archlinux kombu]$ ./build/dashi Usage: dashi <flags> <subcommand> <subcommand args> Subcommands: commands list all command names create create new container delete delete container download download docker image and convert to OCI runtime bundle init container's init process preinit pre initialize container start start container
downloadコマンド
指定されたDockerイメージをダウンロードし、OCIランタイムバンドル(Filesystem Bundle)に変換するコマンド。ここでは実装をサボっていて、内部でskopeoとumociを呼び出しているだけなので、これらのツールを事前にインストールする必要がある。ダウンロードと変換が完了すると、./bundles
以下にバンドルが配置される。
[_____@archlinux kombu]$ ls -l ./bundles total 8 drwx------ 3 root root 4096 Jun 27 09:58 busybox-latest drwx------ 3 root root 4096 May 16 17:12 ubuntu-latest [_____@archlinux kombu]$ sudo ls -l ./bundles/busybox-latest total 80 -rw-r--r-- 1 root root 2967 Jun 27 09:58 config.json drwxr-xr-x 11 root root 4096 May 19 2023 rootfs -rw-r--r-- 1 root root 66026 Jun 27 09:58 sha256_50aa4698fa6262977cff89181b2664b99d8a56dbca847bf62f2ef04854597cf8.mtree -rw-r--r-- 1 root root 372 Jun 27 09:58 umoci.json
このconfig.jsonに記述された設定に基づき、コンテナの設定・起動を行う。config.jsonの仕様はここで読むことができる。
start/preinit/initコマンド
createコマンドでバンドルを指定してコンテナを作成することに成功すると、./containers/<CONTAINER NAME>
以下にバンドルの中身がコピーされる。その後startコマンドでコンテナを起動することができる。
[_____@archlinux kombu]$ sudo ./build/dashi start test 2024/06/27 10:22:08 DEBU Received request from child req=get_cid 2024/06/27 10:22:08 DEBU Received request from child req=get_init_opt 2024/06/27 10:22:08 DEBU Mounted source=proc dest=./containers/test/rootfs/proc 2024/06/27 10:22:08 DEBU Mounted source=tmpfs dest=./containers/test/rootfs/dev 2024/06/27 10:22:08 DEBU Mounted source=devpts dest=./containers/test/rootfs/dev/pts 2024/06/27 10:22:08 DEBU Mounted source=shm dest=./containers/test/rootfs/dev/shm 2024/06/27 10:22:08 DEBU Mounted source=mqueue dest=./containers/test/rootfs/dev/mqueue 2024/06/27 10:22:08 DEBU Mounted source=sysfs dest=./containers/test/rootfs/sys 2024/06/27 10:22:08 DEBU Mounted source=cgroup2 dest=./containers/test/rootfs/sys/fs/cgroup 2024/06/27 10:22:08 DEBU Received request from child req=send_mount_list 2024/06/27 10:22:08 DEBU Set UID uid=0 2024/06/27 10:22:08 DEBU Set GID gid=0 2024/06/27 10:22:08 DEBU Set root path=./containers/test/rootfs 2024/06/27 10:22:08 DEBU Set cwd cwd=/ 2024/06/27 10:22:08 DEBU Set env env="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" 2024/06/27 10:22:08 DEBU Set env env="TERM=xterm" 2024/06/27 10:22:08 DEBU Set env env="HOME=/root" 2024/06/27 10:22:08 DEBU Set hostname hostname=umoci-default 2024/06/27 10:22:08 DEBU Set rlimits 2024/06/27 10:22:08 DEBU Set capabilities caps="{Effective:536871968 Permitted:536871968 Inheritable:536871968}" 2024/06/27 10:22:08 INFO Start container... args=[/bin/bash] root@umoci-default:/# uname -a Linux umoci-default 6.8.7-arch1-1 #1 SMP PREEMPT_DYNAMIC Wed, 17 Apr 2024 15:20:28 +0000 x86_64 x86_64 x86_64 GNU/Linux root@umoci-default:/# ps aux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0.0 0.0 1226956 4888 ? Sl 01:22 0:00 ./build/dashi init root 6 0.0 0.0 4588 3712 ? S 01:22 0:00 /bin/bash root 10 0.0 0.0 7888 3968 ? R+ 01:22 0:00 ps aux root@umoci-default:/# exit exit 2024/06/27 10:29:57 DEBU Received request from child req=unmount 2024/06/27 10:29:57 DEBU Unmounted dest=./containers/test/rootfs/sys/fs/cgroup 2024/06/27 10:29:57 DEBU Unmounted dest=./containers/test/rootfs/dev/pts 2024/06/27 10:29:57 DEBU Unmounted dest=./containers/test/rootfs/dev/shm 2024/06/27 10:29:57 DEBU Unmounted dest=./containers/test/rootfs/dev/mqueue 2024/06/27 10:29:57 DEBU Unmounted dest=./containers/test/rootfs/proc 2024/06/27 10:29:57 DEBU Unmounted dest=./containers/test/rootfs/dev 2024/06/27 10:29:57 DEBU Unmounted dest=./containers/test/rootfs/sys 2024/06/27 10:29:57 DEBU Received request from child req=close_con [_____@archlinux kombu]$
Ubuntuのイメージを使った例だと、デフォルトでconfig.jsonに記述されているbashが呼び出される。psコマンドで確認するとしっかりPID分離が行われており、コンテナの内側からホストのプロセスに触れなくなっていることがわかる。
startコマンド実行からの処理の流れを図にした。
startコマンドを呼び出したあと、preinitコマンドをバックグラウンドで呼び出し、ソケット通信を行いながらコンテナの初期化処理を進めている。preinitコマンドを呼び出す段階でNamespace分離を行っている。GoではCloneflagsを設定するだけで分離することができるため便利。
preinitコマンドを呼び出すコード(dashi/cmd/start.go
)
cmd := exec.Command(os.Args[0], "preinit") cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.SysProcAttr = c.SpecSysProcAttr() cmd.ExtraFiles = []*os.File{cSock.F} if err := cmd.start(); err != nil { log.Error("Error occured", "err", err) return subcommands.ExitFailure }
c.SpecSysProcAttr()
でconfig.jsonのNamespaceの設定(linux.namespaces)を読み取り、CloneFlagsにパースしている。
preinitコマンド(dashi/cmd/preinit.go
)
... if err := c.Init(&opt); err != nil { log.Error("Failed to initialize container", "err", err) return subcommands.ExitFailure } log.Info("Starting container...", "args", opt.Args) if err := syscall.Exec("/proc/self/exe", []string{"/proc/self/exe", "init"}, os.Environ()); err != nil { log.Error("Failed to exec init", "err", err) return subcommands.ExitFailure } // unreachable here return subcommands.ExitSuccess
c.Init()
で現在のプロセスに対してconfig.jsonの各種設定を反映する。設定完了後にinitコマンドを呼び出しているが、preinitコマンドの呼び出し時と違い、execシステムコールを行い、プロセスを上書きしている。また、chrootを実行したあとであるため、os.Args[0]の代わりに/proc/self/exeを利用して自分自身を呼び出している。
dashi/cmd/init.go
... // call program cmd := exec.Command(opt.Args[0], opt.Args[1:]...) cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr // socket connection check go func() { pingReqBytes, _ := internal.RequestToBytes("ping") for range ticker.C { cSock.Write(pingReqBytes) } }() if err := cmd.Run(); err != nil { log.Warn("Exit status was not 0", "err", err) } return subcommands.ExitSuccess
initコマンドでは逆にpreinitコマンドの呼び出し時と同じようにexec.Command
で対象のプログラムを呼び出している。対象のプログラムの実行中、バックグラウンドでソケット通信による10秒間隔の疎通確認を行っている。initコマンドが終了する前に疎通が途切れると、ゾンビプロセスになったと判断して対象のプログラムのプロセスをKillする(未実装)。
特権コンテナ/非特権コンテナ
sudoをつけて実行すると特権コンテナが起動し、そうでなければ非特権コンテナが起動する。非特権コンテナでは権限の関係でコンテナの初期化の設定方法が変わるため、config.jsonの設定を非特権用に書き換える処理が追加されている。この処理はruncのコードを参考にした(というかほぼパクリ)。
まだ未実装の機能
- cgroup
- Seccomp
- pivot_root
- ゾンビプロセスの自動Kill
- PIDがNamespaceによって分離されているため、プロセスの特定ができない
- どうしようか考え中
- コンテナ内でのネットワーク通信
Capability、設定が剥がれてる疑惑
root@umoci-default:/# ps aux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0.4 0.0 1227208 4628 ? Sl 01:30 0:00 /proc/self/exe init root 9 0.0 0.0 4588 3840 ? S 01:30 0:00 /bin/bash root 12 50.0 0.0 7888 3968 ? R+ 01:30 0:00 ps aux root@umoci-default:/# cat /proc/1/status | grep Cap CapInh: 0000000020000420 CapPrm: 000001ffffffffff CapEff: 000001ffffffffff CapBnd: 000001ffffffffff CapAmb: 0000000000000000 root@umoci-default:/# cat /proc/9/status | grep Cap CapInh: 0000000020000420 CapPrm: 000001ffffffffff CapEff: 000001ffffffffff CapBnd: 000001ffffffffff CapAmb: 0000000000000000
本来ならばCapPrm
とCapEff
もCapInh
と同じ値になっている必要があるが000001ffffffffff
、つまり全許可になってしまっている。こういう表記になっているが、非特権コンテナではホストユーザーと同じ権限で動いている(mknod /dev/null
ができなかった)?要検証。
マルウェアサンドボックスの実装
現状自作コンテナランタイムをサンドボックス環境として利用するにはあまりにもセキュリティがお粗末過ぎると思っているが、せっかくなので紹介する。
このマルウェアサンドボックスは指定したプログラムを自作コンテナランタイムで動いているコンテナ内で実行し、実行中に呼ばれたシステムコールのログを収集する。ログを解析し、あらかじめ定義しておいた検知ルールに違反していることがわかったら、その違反しているルールを出力する。振る舞い検知だけでマルウェアかどうか判断することはできないため、あくまで「違反している」という体にしている。
システムコールロガーについては前回の記事で書いたので、それを参考にしてほしい。 zebian.hatenablog.com
[_____@archlinux kombu]$ python3 ./task.py task_run ... ./build/yaminabe -t ./target_programs/remove_root/target/debug/remove_root -d ./detection_rules [2024-07-04T02:23:40Z INFO yaminabe::sandbox] Created mount directory [2024-07-04T02:23:42Z INFO yaminabe::sandbox] Execute runner script... [2024-07-04T02:23:42Z INFO yaminabe::wrapper] Running command in the container: ["sh", "/mnt/runner.sh"] 2024/07/04 11:23:45 DEBU Mounted source=proc dest=./containers/22042023-b8d0-48e1-836e-381c56384f3d/rootfs/proc 2024/07/04 11:23:45 DEBU Mounted source=tmpfs dest=./containers/22042023-b8d0-48e1-836e-381c56384f3d/rootfs/dev 2024/07/04 11:23:45 DEBU Mounted source=devpts dest=./containers/22042023-b8d0-48e1-836e-381c56384f3d/rootfs/dev/pts 2024/07/04 11:23:45 DEBU Mounted source=shm dest=./containers/22042023-b8d0-48e1-836e-381c56384f3d/rootfs/dev/shm 2024/07/04 11:23:45 DEBU Mounted source=mqueue dest=./containers/22042023-b8d0-48e1-836e-381c56384f3d/rootfs/dev/mqueue 2024/07/04 11:23:45 DEBU Mounted source=sysfs dest=./containers/22042023-b8d0-48e1-836e-381c56384f3d/rootfs/sys 2024/07/04 11:23:45 DEBU Mounted source=cgroup2 dest=./containers/22042023-b8d0-48e1-836e-381c56384f3d/rootfs/sys/fs/cgroup 2024/07/04 11:23:45 DEBU Mounted source=mount-22042023-b8d0-48e1-836e-381c56384f3d dest=./containers/22042023-b8d0-48e1-836e-381c56384f3d/rootfs/mnt 2024/07/04 11:23:45 DEBU Set UID uid=0 2024/07/04 11:23:45 DEBU Set GID gid=0 2024/07/04 11:23:45 DEBU Set root path=./containers/22042023-b8d0-48e1-836e-381c56384f3d/rootfs 2024/07/04 11:23:45 DEBU Set cwd cwd=/ 2024/07/04 11:23:45 DEBU Set env env="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" 2024/07/04 11:23:45 DEBU Set env env="TERM=xterm" 2024/07/04 11:23:45 DEBU Set env env="HOME=/root" 2024/07/04 11:23:45 DEBU Set hostname hostname=umoci-default 2024/07/04 11:23:45 DEBU Set rlimits 2024/07/04 11:23:45 DEBU Set capabilities caps="{Effective:536871968 Permitted:536871968 Inheritable:536871968}" 2024/07/04 11:23:45 INFO Start container... args="[sh /mnt/runner.sh]" 2024/07/04 02:23:45 Loaded BPF binary 2024/07/04 02:23:45 Loaded BPF object 2024/07/04 02:23:45 Attached hook function removed successfully! 2024/07/04 02:23:46 Successed to export log 2024/07/04 11:23:46 DEBU Unmounted dest=./containers/22042023-b8d0-48e1-836e-381c56384f3d/rootfs/sys/fs/cgroup 2024/07/04 11:23:46 DEBU Unmounted dest=./containers/22042023-b8d0-48e1-836e-381c56384f3d/rootfs/dev/pts 2024/07/04 11:23:46 DEBU Unmounted dest=./containers/22042023-b8d0-48e1-836e-381c56384f3d/rootfs/dev/shm 2024/07/04 11:23:46 DEBU Unmounted dest=./containers/22042023-b8d0-48e1-836e-381c56384f3d/rootfs/dev/mqueue 2024/07/04 11:23:46 DEBU Unmounted dest=./containers/22042023-b8d0-48e1-836e-381c56384f3d/rootfs/proc 2024/07/04 11:23:46 DEBU Unmounted dest=./containers/22042023-b8d0-48e1-836e-381c56384f3d/rootfs/dev 2024/07/04 11:23:46 DEBU Unmounted dest=./containers/22042023-b8d0-48e1-836e-381c56384f3d/rootfs/sys 2024/07/04 11:23:46 DEBU Unmounted dest=./containers/22042023-b8d0-48e1-836e-381c56384f3d/rootfs/mnt 2024/07/04 11:23:46 INFO Exited container [2024-07-04T02:23:46Z INFO yaminabe] violation detected! [2024-07-04T02:23:46Z INFO yaminabe] violated detection rule: DetectionRule { meta: Meta { name: "Sample rule", desc: "This is the sample rule" }, syscall: Some(Syscall { blacklist_numbers: [], frequent: [SyscallFrequent { threshold_count: 10, number: 59 }, SyscallFrequent { threshold_count: 4, number: 263 }], consecutive: [SyscallConsecutive { threshold_count: 4, number: 1 }] }), timestamp: Some(Timestamp { check_timetravel: true }) } [2024-07-04T02:23:46Z INFO yaminabe] message: "Frequent syscall detected: 263" [2024-07-04T02:23:46Z INFO yaminabe::sandbox] Removed mount directory
この例では、検体プログラムであるremove_rootが検知ルール「Sample rule」に違反しており、理由が「システムコール263番(renameat)が4回以上呼ばれた」ということがわかる。
detection_rules/sample.toml
[meta] name = "Sample rule" desc = "This is the sample rule" [syscall] # 指定のシステムコールが実行されていたら検知 #blacklist_numbers = [0, 1, 2, 3] # read, write, open, close blacklist_numbers = [] # 頻繁に実行されるシステムコールを監視 [[syscall.frequent]] # 検体の実行中にexecveの実行が10回を超えたら検知 threshold_count = 10 number = 59 # execve [[syscall.frequent]] threshold_count = 4 number = 263 # renameat # 連続で実行されるシステムコールを監視 [[syscall.consecutive]] # 連続でwriteが4回実行されたら検知 threshold_count = 4 number = 1 [timestamp] # タイムスタンプが過去や未来に飛んだら(改ざんされたら)検知 check_timetravel = true
他にもいくつか検知項目を用意している。現在の実装ではOR条件になっているが、AND条件にする機能もあったほうがいいかもしれない。
システムコールロガーの問題点
システムコールロガーをコンテナ内で動かすことにおいて色々問題が発生したのでまとめる。
- 非特権コンテナでBPFを実行できない
2024/05/30 18:36:45 Loaded BPF binary 2024/05/30 18:36:45 Failed to load BPF object: field HookX64SysCall: program hook_x64_sys_call: load BTF: BTF not supported (requires >= v4.18)
非特権環境で実行しようとするとと、このようにBTF not supportedと出力されて実行ができない。 docs.redhat.com このドキュメントによると、そもそも非特権環境では権限不足で動かないっぽい?
- NamespaceでPID分離しているにもかかわらずホストのログが出力される
先述の通り非特権コンテナでは動かせないため、仕方なく特権コンテナで動かすことにした。
uptime: 0d 2:40:20:733.963.236, syscall: Some(Write), pid: 528, ppid: 483, comm: "node" uptime: 0d 2:40:20:734.122.348, syscall: Some(Signalfd), pid: 528, ppid: 483, comm: "node" uptime: 0d 2:40:20:734.173.862, syscall: Some(Signalfd), pid: 528, ppid: 483, comm: "node" uptime: 0d 2:40:20:734.174.852, syscall: Some(Signalfd), pid: 528, ppid: 483, comm: "node" uptime: 0d 2:40:20:734.185.193, syscall: Some(Shutdown), pid: 483, ppid: 479, comm: "node" uptime: 0d 2:40:20:734.303.354, syscall: Some(Signalfd), pid: 483, ppid: 479, comm: "node" uptime: 0d 2:40:20:734.304.845, syscall: Some(Signalfd), pid: 483, ppid: 479, comm: "node" uptime: 0d 2:40:20:735.832.16, syscall: Some(Write), pid: 483, ppid: 479, comm: "node" uptime: 0d 2:40:20:735.921.956, syscall: Some(Signalfd), pid: 483, ppid: 479, comm: "node" uptime: 0d 2:40:20:735.923.555, syscall: Some(Signalfd), pid: 483, ppid: 479, comm: "node" uptime: 0d 2:40:20:735.996.357, syscall: Some(Sendmsg), pid: 4754, ppid: 4735, comm: "tokio-runtime-w" uptime: 0d 2:40:20:736.17.280, syscall: Some(SchedSetaffinity), pid: 4754, ppid: 4735, comm: "tokio-runtime-w" uptime: 0d 2:40:20:736.22.692, syscall: Some(Signalfd), pid: 4754, ppid: 4735, comm: "tokio-runtime-w" uptime: 0d 2:40:20:736.34.108, syscall: Some(Recvfrom), pid: 4754, ppid: 4735, comm: "code-5437499feb" uptime: 0d 2:40:20:736.56.631, syscall: Some(SchedSetaffinity), pid: 4754, ppid: 4735, comm: "code-5437499feb" uptime: 0d 2:40:20:736.64.799, syscall: Some(RtSigprocmask), pid: 4734, ppid: 4730, comm: "sshd" uptime: 0d 2:40:20:736.69.315, syscall: Some(Read), pid: 4734, ppid: 4730, comm: "sshd" uptime: 0d 2:40:20:736.79.392, syscall: Some(MemfdCreate), pid: 4734, ppid: 4730, comm: "sshd" uptime: 0d 2:40:20:736.84.872, syscall: Some(RtSigprocmask), pid: 4734, ppid: 4730, comm: "sshd" uptime: 0d 2:40:20:736.85.999, syscall: Some(Unshare), pid: 4734, ppid: 4730, comm: "sshd" uptime: 0d 2:40:20:736.87.25, syscall: Some(RtSigprocmask), pid: 4734, ppid: 4730, comm: "sshd" uptime: 0d 2:40:20:736.88.445, syscall: Some(Write), pid: 4734, ppid: 4730, comm: "sshd" uptime: 0d 2:40:20:736.234.531, syscall: Some(RtSigprocmask), pid: 4734, ppid: 4730, comm: "sshd" uptime: 0d 2:40:20:736.236.305, syscall: Some(Unshare), pid: 4734, ppid: 4730, comm: "sshd" uptime: 0d 2:40:20:736.747.483, syscall: Some(Signalfd), pid: 9010, ppid: 9009, comm: "dashi" uptime: 0d 2:40:20:736.749.506, syscall: Some(Getitimer), pid: 9010, ppid: 9009, comm: "dashi" ... uptime: 0d 2:40:21:484.391.457, syscall: Some(Mmap), pid: 9038, ppid: 9024, comm: "target" uptime: 0d 2:40:21:484.399.799, syscall: Some(Pipe), pid: 9038, ppid: 9024, comm: "target" uptime: 0d 2:40:21:484.405.923, syscall: Some(Mkdirat), pid: 9038, ppid: 9024, comm: "target" uptime: 0d 2:40:21:484.409.664, syscall: Some(Fstat), pid: 9038, ppid: 9024, comm: "target" uptime: 0d 2:40:21:484.410.670, syscall: Some(Mmap), pid: 9038, ppid: 9024, comm: "target" uptime: 0d 2:40:21:484.413.295, syscall: Some(Close), pid: 9038, ppid: 9024, comm: "target" uptime: 0d 2:40:21:484.415.789, syscall: Some(Mkdirat), pid: 9038, ppid: 9024, comm: "target" uptime: 0d 2:40:21:484.418.95, syscall: Some(Read), pid: 9038, ppid: 9024, comm: "target" uptime: 0d 2:40:21:484.423.978, syscall: Some(Fstat), pid: 9038, ppid: 9024, comm: "target" uptime: 0d 2:40:21:484.424.722, syscall: Some(Mmap), pid: 9038, ppid: 9024, comm: "target" uptime: 0d 2:40:21:484.426.179, syscall: Some(Mmap), pid: 9038, ppid: 9024, comm: "target" uptime: 0d 2:40:21:484.430.605, syscall: Some(Mmap), pid: 9038, ppid: 9024, comm: "target" uptime: 0d 2:40:21:484.432.719, syscall: Some(Mmap), pid: 9038, ppid: 9024, comm: "target" ...
収集されたログを見れば分かる通り、コンテナ内には存在しないプロセスのログが存在するし、pid、ppidもホストから見たものになっている。この問題は前にも遭遇したことがあるため予想はしていたが、BPFプログラムはカーネルレベルで動いているため、これはおそらく仕様。本当は良くないが、仕方ないのでプロセス名「target」でフィルターをかけた。BPF、結構便利だけど若干融通が効かない場面がある。