Tetragon-mini by Rust: eBPF-based process monitoring

TL;DR;

I created eBPF-based software in Rust that can monitor the Process lifecycle.πŸ¦€πŸ

πŸš€ process      101708: root: /usr/sbin/iptables
πŸ’₯ exit         101708: root: /usr/sbin/iptables
πŸš€ process      101705: yukinakamura: /usr/bin/cat
πŸ’₯ exit         101705: yukinakamura: /usr/bin/cat
πŸš€ process      101758: yukinakamura: /usr/bin/sed
πŸ’₯ exit         101758: yukinakamura: /usr/bin/sed
  1. Motivation
  2. Scope
  3. Architecture Overview
  4. How to Run
    1. Prerequisites
    2. Clone the Repository
    3. Generate Struct codes
    4. Build and Run eBPF Programs and Agent
    5. Build and Run client
    6. Check client’s output
  5. What I Learned
    1. Easy Data Sharing between kernel space and user space
    2. Diving into the Linux Kernel
    3. The Joy of Getting eBPF Up and Running
    4. Tackling Tetragon’s eBPF Code
  6. Wrap up

Motivation

Tetragon-mini is a project where I’m trying to see if I can rewrite Tetragon using the Rust aya frameworkπŸ¦€. The original Tetragon is written in Go and C.

I was really impressed by Tetragon, and while digging into how it works, I thought, “Hey, I want to make something like this too!” Since I already use Rust for my job, I figured it was the perfect chance to kick off this project.🐝

Why “mini”?

  • Compared to Tetragon, it has a smaller binary size (because it’s written in Rust)
  • It has fewer features compared to Tetragon.

Scope

Tetragon monitors kernel function calls and can be used for the following use cases:

  • Process lifecycle
  • Filename access
  • Network observability
  • Linux process credentials
  • Host System Changes
  • Security Profiles

It can also monitor activities of containers running on Docker and Kubernetes.🐳 β˜ΈοΈ 

In Tetragon-mini, the first step was to implement process monitoring, which is a key use case of Tetragon. Support for container environments will be added in the future.

Architecture Overview

The architecture is the same as Tetragon.

Tetragon-mini Architecture
  • eBPF Programs: Detect the process lifecycle by monitoring arguments from kernel function calls and store this information in the eBPF Map.
  • Agent: Communicates the process information stored in the eBPF Map to the Client.
  • Client: Communicates with the Agent using gRPC and visualizes the received data for the user.

Data is exchanged between the eBPF and the Agent through the eBPF Map. Communication between Agent and Client is done via gRPC.

Main Rust Crates

  • aya: an eBPF library for the Rust programming language, built with a focus on developer experience and observability.
  • tokio: an event-driven, non-blocking I/O platform for writing asynchronous application
  • tonic: a gRPC over HTTP/2 implementation focused on high performance, interoperability, and flexibility

How to Run

Prerequisites

(Optional) Set up lima VM on MacOS

If you’re using MacOS, you can quickly set it up with lima and my template.

  • Install lima
brew install lima
  • Download the template
wget https://raw.githubusercontent.com/yukinakanaka/tetragon-mini/refs/heads/main/lima/tetragon-mini.yaml
  • Edit cpu and memory configuration in tetragon-mini.yaml. Default values are:
cpus: 8
memory: "16GiB"
  • Create a VM
limactl start lima-vm/aya-lab.yaml

Clone the Repository

Get all the codes from my repository.

git clone https://github.com/yukinakanaka/tetragon-mini.git
cd tetragon-mini

Generate Struct codes

Run the next command to generate the necessary Struct codes:

cargo xtask codegen

Build and Run eBPF Programs and Agent

cargo xtask run

Build and Run client

cargo run --bin tetra

Check client’s output

You can see process’s PID, user and executed binary!

πŸš€ process      101708: root: /usr/sbin/iptables
πŸ’₯ exit         101708: root: /usr/sbin/iptables
πŸš€ process      101705: yukinakamura: /usr/bin/cat
πŸ’₯ exit         101705: yukinakamura: /usr/bin/cat
πŸš€ process      101758: yukinakamura: /usr/bin/sed
πŸ’₯ exit         101758: yukinakamura: /usr/bin/sed

What I Learned

Easy Data Sharing between kernel space and user space

Passing data from eBPF to the user side turned out to be way easier than I expected. A big reason for that is aya. With aya, I can easily share the data model code between the eBPF side and the user side, making everything a lot smoother!

Diving into the Linux Kernel

I picked up a ton about the Linux Kernel, like how process management works, data structures, and kernel functions. Exploring the lower layers of the system was actually really fun!

The Joy of Getting eBPF Up and Running

There’s a real sense of accomplishment in just getting eBPF to work! After dealing with compiler errors, I often bumped into issues with the eBPF Verifier. Figuring out what’s wrong based on those error messages can be pretty tricky. I took it step-by-step, making small changes and running the code after writing just a line at a time. Plus, the Discord community was super helpful along the way!

Tackling Tetragon’s eBPF Code

Understanding the eBPF code of Tetragon was not easy. The code got a bit complicated to handle different kernel versions. But thanks to some good comments in the right spots, I was able to get it implemented (even if it’s not perfect).

Wrap up

I wrote about the process monitoring tool of Tetragon in Rust and the experiences I’ve had.

2024 was a great year for me to learn about eBPF-related technologies and welcome the birth of my daughterπŸ‘Ό. I hope to make 2025 an even more wonderful year. Wishing everyone a happy new year!