Slide 1

Slide 1 text

eBPFによる Process Lifecycle Monitoring - Tetragonの実装紹介 - Yuki Nakamura March 15, 2025 @Cloud Native Community Japan - eBPF Japan Meetup #3

Slide 2

Slide 2 text

Whoami: Yuki Nakamura 👨‍💻 Platform Engineer ex-IBM Group, Mapbox Tech Blog Container・Kubernetes関連のツール(ArgoCD, Buildkit, etc.) eBPF(Tetragon, Aya) 🐝 eBPFとの関わり Documentary: Unlocking the Kernelを見て、eBPFを活用したツール開発を志す eBPF関連のコードがLinux Kernelのコードベースにマージされていく経緯 eBPFが人気を集めていく経緯 名言: "This is like putting Javascript into the kernel." - Brendan Gregg Tetragon Tetragon ProjectにContribution Tetragon-mini: RustでTetragonをRewriteしながらeBPFを学習

Slide 3

Slide 3 text

Tetragon Demo: Process Lifecycle Monitoring https://youtu.be/P7Pork8-hp8 0:00

Slide 4

Slide 4 text

Thoughts🤔 スゴイけど、なんの役に立つ?そもそもTetragonは何のためのツール? Process Lifecycle Monitoringはどうやって実現している? どんなKernelのEvent Hookを利用している? eBPF側・UserSpace側ではどんなコードが書かれている?

Slide 5

Slide 5 text

Agenda 1. Tetragonの概要とユースケース Runtime Enforcement Security Observability 収集したProcess Lifecycleの分析 2. Process Lifecycle Monitoringの仕組み Linuxの前提知識 Tetragonのコード解説

Slide 6

Slide 6 text

Questions🙋‍♀️🙋‍♂️ TetragonというProjectを知っていますか? Tetragonを触ったことありますか? Tetragonを本番環境で利用していますか?

Slide 7

Slide 7 text

Tetragon:概要 CNCFプロジェクト, Ciliumのサブプロジェクト Ciliumと同様にC(eBPF)とGo(UserSpace)で書かれている 2023年11月にv1.0がリリース 2023-11-01: v1.0 2024-04-29: v1.1 2024-09-05: v1.2 2024-12-13: v1.3 (本日紹介するコードはv1.3を参照) Tetragonを一言で言うと… eBPFベースのSecurity Observability & Runtime Enforcement Tool

Slide 8

Slide 8 text

Tetragon:Runtime Enforcement(実装紹介対象外) あるルールに合致するSyscallが発生した際に、それをカーネル空間内で即座に制御する仕組み Tracing Policy(ルール): Traceするカーネル内のイベントや条件に一致した場合のアクションを定義 例1: /etc/passwdへの書き込みを試みる、PIDが1または0でないすべてのsys_writeをkillする 例2: 特定のbinaryファイルの実行を禁止する eBPFを活用することでユーザースペースにイベントを伝達することなく処理を完了するのが特徴。 このアプローチにより、低レイテンシかつ確実なセキュリティポリシーの適用が可能。 Kernel Event eBPF Map Syscall Event eBPF Program eBPF Program eBPF Program Kill / Override eBPF Program Set up eBPF Programs/Maps Tetragon Agent Tetra CLI Tracing Policy Process eBPF Map eBPF Map

Slide 9

Slide 9 text

Tetragon:Security Observability セキュリティ関連のKernel内のEventをリアルタイムに観測・分析できるようにすること Eventの例: File Access, TCP Connection Event, ProcessのLifeCycle(実行終了) etc. eBPF ProgramでEventを検知し、eBPF Mapを介してユーザースペースのTetragon Agentに伝達する。任意 のCollector, Storage/Analytics Toolを活用し、蓄積・分析が可能になる。 Storage/AnalyticsTool Kernel Event eBPF Map Syscall Event eBPF Program eBPF Program eBPF Program eBPF Program Set up eBPF Programs/Maps Tetragon Agent Tetra CLI Tracing Policy Process eBPF Map eBPF Map Grafana loki S3 Collector fluentd optl Athena tetragon.log

Slide 10

Slide 10 text

Processの分析1: 強い権限を持つプロセスの検索 CAP_SYS_ADMIN(幅広い管理権限を持つCapability)が付与されているプロセスを検索 。 親プロセスのBinaryやKubernetesのNamespace名、Pod名などのContextも同時に取得可能。 1. ProcessEventのログファイルtetragon.log(JSONL)をDuckDBを利用して検索した ↩︎ [1]

Slide 11

Slide 11 text

Processの分析2: 不審なShell実行の検知 Shellから実行されたプロセスを検索。 Shellの親プロセスを再帰的に検索することでアクセス経路も把握可能。

Slide 12

Slide 12 text

Agenda 1. Tetragonの概要とユースケース Runtime Enforcement Security Observability 収集したProcess Lifecycleの分析 2. Process Lifecycle Monitoringの仕組み Linuxの前提知識 プロセスのデータ構造 TGIDとPID Process Management Syscall Tetragonのコード解説

Slide 13

Slide 13 text

Linux基礎: task_struct task_struct とは、Linuxカーネルにおいて各プロセス(またはスレッド)を管理するデータ構造 Linux: include/linux/sched.h Tetragonはこのtask_structからプロセスの情報を収集している eBPF helper関数 bpf_get_current_task() : 現在のプロセス(スレッド)に対応する task_struct のポ インタを取得 struct task_struct { pid_t pid; pid_t tgid; char comm[TASK_COMM_LEN]; // プロセスのコマンド名 struct nsproxy *nsproxy; // Namespace struct mm_struct *mm; // プロセスが使用するユーザ空間のメモリ管理情報へのポインタ ... struct task_struct *task = bpf_get_current_task(); get_namespaces(&event->ns, task); // task_struct からnamespace の情報を取得。get_namespace はTetragon 内で定義された関数。

Slide 14

Slide 14 text

Linux: TGIDとPID TGIDはプロセスの識別子。PIDはスレッドの識別子。 Multi Thread task_struct tgid 200 pid 200 comm binary_2 task_struct tgid 200 pid 201 comm binary_2 task_struct tgid 200 pid 202 comm binary_2 Single Thread task_struct tgid 100 pid 100 comm binary_1 ⚠️PIDはプロセスの識別子ではない⚠️ Tetragonはプロセス単位でイベントを監視。プロセス内のThread(Multithreading)の作成・削除は対象外。 eBPF helper関数 bpf_get_current_pid_tgid() : pidとtgidを取得 u64 pid_tgid = bpf_get_current_pid_tgid(); u32 tgid = pid_tgid >> 32; // 上位32 ビットからTGID を取得 u32 pid = pid_tgid & 0xFFFFFFFF; // 下位32 ビットからPID を取得

Slide 15

Slide 15 text

Linux: Process Management Syscall プロセスは以下の手順で、作成・実行・終了される。 1. 親プロセスが fork() やclone() や類似のSyscallなどを呼び出して、子プロセスを作成する。 2. 子プロセスは execve() や類似のSyscallを呼び出して、プログラムを実行する。 3. 子プロセスは実行が終了すると、 exit() や類似のSyscallを呼び出して、プロセスを終了する。 Parent Child fork() execve() exit() wait()

Slide 16

Slide 16 text

具体例: lsコマンド実行時のSyscall bashがlsコマンドを実行するときのSyscallをTraceした結果は以下の通りである。 Terminal1 (bash, PID=23167) Terminal2 以下のSyscallが呼ばれていた。 1. 作成: clone() 2. 実行: execve() 3. 終了: exit_group() ls -la strace -fp 23167 2>&1 | grep -e clone -e execve -e exit clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLDstrace: Process 23895 attached [pid 23895] execve("/usr/bin/ls", ["ls", "--color=auto", "-la"], 0xbb5be6b20cc0 * 24 vars /) = 0 [pid 23895] exit_group(0) = ? [pid 23895] +++ exited with 0 +++

Slide 17

Slide 17 text

Agenda 1. Tetragonの概要とユースケース Runtime Enforcement Security Observability 収集したProcess Lifecycleの分析 2. Process Lifecycle Monitoringの仕組み Linuxの前提知識 プロセスのデータ構造 TGIDとPID Process Management Syscall Tetragonの実装紹介 Fork Execve Exit

Slide 18

Slide 18 text

(おさらい) Process Lifecycle Monitoring

Slide 19

Slide 19 text

Tetragon: Process Lifecycle Monitoring - Overview 各Process Management SyscallのHookにProcess Lifecycle Eventを作成するeBPF Programをアタッチする。 eBPF Programが作成したEventをeBPF Mapを介してユーザースペースに伝達する。 User Space Kernel Space Exit-related Syscall eBPF Map Fork-related Syscall eBPF Program for creating Clone Event Execve-related Syscall eBPF Program for creating Exit Event eBPF Program for creating Execve Event Tetragon Agent

Slide 20

Slide 20 text

Fork: eBPF ProgramとHook Point section名: kprobe/wake_up_new_task のeBPF Programを kprobe の wake_up_new_task にアタッチ Tetragon UserSpace: base.go User Space Kernel Space Tetragon Agent fork-related syscalls event_wake_up_new_task kprobe wake_up_new_task perf_event_array tcpmon_map 47 Fork = program.Builder( 48 "bpf_fork.o", // the name of the BPF object file 49 "wake_up_new_task", // the hook point 50 "kprobe/wake_up_new_task", // the program section name 51 "kprobe_pid_clear", // the name of pin 52 "kprobe", // the type of BPF program 53 ).SetPolicy(basePolicy)

Slide 21

Slide 21 text

Fork: Hook Point (wake_up_new_task) wake_up_new_task はforkのmain routineである kernel_clone() の中で呼ばれる関数 Linux: kernel/fork.c -> kernel_clone() kernel_clone()はmainのfork-routine。 pid_t kernel_clone(struct kernel_clone_args *args) { struct task_struct *p; wake_up_new_task(p); ...

Slide 22

Slide 22 text

Fork: eBPF Program 主な処理: Clone Eventを組み立て、eBPF Map: tcpmon_mapに書き込む Tetragon eBPF: bpf_fork.c プロセス内でのThreadの作成を無視する仕組み: wake_up_new_taskはThread作成時にも呼ばれるため、同じTGIDでCloneEventがすでに作成されてい るかを確認し、作成されていない場合のみ新たに作成する。 curr = execve_map_get(tgid); if (curr->key.ktime != 0) // Check whether the event for the tgid has already been created. return 0; ``` --> 23 __attribute__((section("kprobe/wake_up_new_task"), used)) int 24 BPF_KPROBE(event_wake_up_new_task, struct task_struct *task) 25 { 26 struct msg_clone_event msg; 27 ... 28 perf_event_output_metric(ctx, MSG_OP_CLONE, &tcpmon_map, 29 BPF_F_CURRENT_CPU, &msg, msg_size); // Write msg_clone_event to tcpmon_map

Slide 23

Slide 23 text

Execve User Space Kernel Space Exit-related Syscall eBPF Map Fork-related Syscall eBPF Program for creating Clone Event Execve-related Syscall eBPF Program for creating Exit Event eBPF Program for creating Execve Event Tetragon Agent

Slide 24

Slide 24 text

Execve: eBPF ProgramとHook Point section名: tracepoint/sys_execve のeBPF Programを tracepoint の sched/sched_process_exec にアタ ッチ Tetragon UserSpace: base.go Kernel Space Tail Call event_execve execve_send Tail Call execve_rate execve-related syscalls trecepoint sched_process_exec User Space Tetragon Agent perf_event_array tcpmon_map 23 Exit = program.Builder( 24 config.ExecObj(), // the name of the BPF object file 25 "sched/sched_process_exec", // the hook point 26 "tracepoint/sys_execve", // the program section name 27 "event_execve", // the name of pin 28 "execve", // the type of BPF program 29 ).SetPolicy(basePolicy)

Slide 25

Slide 25 text

Execve: Hook Point (sched/sched_process_exec) tracepoint: sched/sched_process_exec は新しいプロセスが実行される際にTriggerされる プロセス内でのThreadの作成を無視する仕組み: プロセス内でのThread作成時には、 sched/sched_process_exec はトリガーされない。 <参考>TracepointのeBPF Programを書く際は、そのTracepointで利用可能なデータのフォーマットを確認 することが大切。以下のコマンドで確認可能。 cat /sys/kernel/debug/tracing/events/sched/sched_process_exec/format name: sched_process_exec ID: 267 format: field:unsigned short common_type; offset:0; size:2; signed:0; field:unsigned char common_flags; offset:2; size:1; signed:0; field:unsigned char common_preempt_count; offset:3; size:1; signed:0; field:int common_pid; offset:4; size:4; signed:1; field:__data_loc char[] filename; offset:8; size:4; signed:0; field:pid_t pid; offset:12; size:4; signed:1; field:pid_t old_pid; offset:16; size:4; signed:1; print fmt: "filename=%s pid=%d old_pid=%d", __get_str(filename), REC->pid, REC->old_pid

Slide 26

Slide 26 text

Execve: eBPF Program 主な処理: Execve Event (msg_exit)を組み立て、eBPF Map: tcpmon_mapに書き込む Tetragon eBPF: bpf_execve_event.c event_execve execve_send __attribute__((section("tracepoint/sys_execve"), used)) int event_execve(struct trace_event_raw_sched_process_exec *ctx) { struct task_struct *task = (struct task_struct *)get_current_task(); char *filename = (char *)ctx + (_(ctx->__data_loc_filename) & 0xFFFF); // Use __data_loc_filename in ctx struct msg_execve_event *event; __attribute__((section("tracepoint"), used)) int execve_send(void *ctx __arg_ctx) { // Write msg_execve_event to tcpmon_map perf_event_output_metric(ctx, MSG_OP_EXECVE, &tcpmon_map, BPF_F_CURRENT_CPU, event, size);

Slide 27

Slide 27 text

Execve: Tail Call Execve Eventの処理は3つのeBPF Programに分かれており、Tail Callによって順に実行される 1. event_execve: Execve Eventの組み立て 2. execve_rate: cgroup単位で大量のEventが発生した際の監視を抑制(Event throttling) 3. execve_send: Execve EventをeBPF Mapへの書き込み Kernel Space Tail Call event_execve execve_send Tail Call execve_rate execve-related syscalls trecepoint sched_process_exec User Space Tetragon Agent perf_event_array tcpmon_map Tail Call導入のメリット: ロジックの分離 eBPF Verifierのプログラムサイズ制限到達の回避 スタック使用量の削減(最大512バイト) 1. v5.2までは、命令数の上限が4k、複雑さの上限が128k。その後、これらの上限は1Mに引き上げられた。↩︎ [1]

Slide 28

Slide 28 text

Tips: Tail Call間でのデータ共有 Tail Callの呼び出し時にデータを渡すことはできない。 その解決策として、eBPF Mapを利用して、eBPF Program間でEventデータを共有している。 Tetragon eBPF: process.h Kernel Space Tail Call read/write event_execve read/write execve_send Tail Call read/write execve_rate execve-related syscalls trecepoint sched_process_exec User Space Tetragon Agent perf_event_array tcpmon_map Storage for sharing states PerCpuArray CPU:1 CPU:n PerCpuArray ... 360 struct { 361 __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); 362 __uint(max_entries, 1); 363 __type(key, __u32); 364 __type(value, struct msg_execve_event); 365 } execve_msg_heap_map SEC(".maps");

Slide 29

Slide 29 text

Exit User Space Kernel Space Exit-related Syscall eBPF Map Fork-related Syscall eBPF Program for creating Clone Event Execve-related Syscall eBPF Program for creating Exit Event eBPF Program for creating Execve Event Tetragon Agent

Slide 30

Slide 30 text

Exit: eBPF ProgramとHook Point section名: kprobe/acct_process のeBPF Programを kprobe の acct_process にアタッチ Tetragon UserSpace: base.go User Space Kernel Space Tetragon Agent exit-related syscalls event_exit_acct_process kprobe acct_process perf_event_array tcpmon_map 39 Exit = program.Builder( 40 "bpf_exit.o", // the name of the BPF object file 41 "acct_process", // the hook point 42 "kprobe/acct_process", // the program section name 43 "event_exit", // the name of pin 44 "kprobe", // the type of BPF program 45 ).SetPolicy(basePolicy)

Slide 31

Slide 31 text

Exit: Hook Point (acct_process) acct_process はdo_exit()の中でThread Groupがなくなるときに呼ばれる関数 Linux: kernel/exit.c -> do_exit() プロセス内でのThreadの削除を無視する仕組み: acct_processはThread Groupがなくなるときに呼ばれるため、プロセス終了時に1回のみ呼ばれる。 なお、acct_processがないKernelの場合は、 disassociate_ctty が利用される。 <参考>以前はtracepointの sched/sched_process_exit やkprobeの kprobe/__put_task_struct を利用 していたが、Thread Groupの終了を簡単に検知できるacct_processに変更された。 tetragon: Switch exit tracepoint to __put_task_struct kprobe #558 tetragon: Hook exit sensor on acct_process #1509 void __noreturn do_exit(long code) { if (group_dead) acct_process();

Slide 32

Slide 32 text

Exit: eBPF Program 主な処理: Exit Event (msg_exit)を組み立て、eBPF Map: tcpmon_mapに書き込む Tetragon eBPF: bpf_exit.cの kprobe/acct_process section Tetragon eBPF: bpf_exit.h 47 __attribute__((section("kprobe/acct_process"), used)) int 48 event_exit_acct_process(struct pt_regs *ctx) 49 { 50 __u64 pid_tgid = get_current_pid_tgid(); 51 52 event_exit_send(ctx, pid_tgid >> 32); 53 return 0; 54 } FUNC_INLINE void event_exit_send(void *ctx, __u32 tgid) { struct msg_exit *exit; exit->info.tid = tgid; ... perf_event_output_metric(ctx, MSG_OP_EXIT, &tcpmon_map, BPF_F_CURRENT_CPU, exit, size); // Write msg_exit to tcpmon_map

Slide 33

Slide 33 text

Process Lifecycle Monitoring - 詳細 3つのHook Point、5つのeBPF Program、複数のeBPF Mapを利用して、Process Lifecycle Monitoringを実現 User Space Kernel Space exit-related syscalls Tail Call event_execve perf_event_array tcpmon_map Tetragon Agent Tetra CLI fork-related syscalls event_exit_acct_process execve_send Tail Call execve_rate event_wake_up_new_task execve-related syscalls kprobe wake_up_new_task trecepoint sched_process_exec kprobe acct_process

Slide 34

Slide 34 text

まとめ 1. TetragonのRuntime Enforcement、Security Observabilityについて説明した 2. Linuxの基礎知識(task_struct、TGIDとPID、Process関連のSyscall)について説明した 3. Tetragon/Kernelのコードを一部紹介した(Fork/Execve/ExitのHook PointとeBPF Program) 4. eBPFのTips(TracepointのData Format、Tail Call、Per-CPU Mapを利用したデータ共有)を紹介した