RSS Amplifier

How Tech - Systems Programming · Aug 22, 2026

Issue 02 — Linux Agent: Process Events via eBPF

0
Sign in to vote or save

Systems · How Tech - Systems Programming

This issue ships the first executable agent: a Linux binary that observes process launches with eBPF and emits OCSF-shaped process_activity JSON. Fixture replay covers CI and non-Linux hosts. Live kernel attach belongs on a Linux Reader VM.

eBPF programs run in the kernel under a verifier. They attach to hooks and move small records to userspace through maps. For process creation telemetry, two attachment styles appear in tutorials:

HookStabilityWhat you getsched_process_exec (tracepoint)Stable ABI across kernels that expose the eventFired after a successful exec; filename and task identity are available without reconstructing execve argv from user pagesexecve / execveat (kprobe)Fragile across kernel buildsEarly argv access at the cost of page-fault handling and symbol churn

This course prefers sched_process_exec for production-shaped exec telemetry. Use an execve kprobe only as a teaching contrast: argv capture looks attractive until page faults and symbol drift show up in the field.

capCaption: Kernel tracepoint → ring buffer → Aya userspace loader → OCSF-shaped JSON.tion...

Ring buffers (BPF_MAP_TYPE_RINGBUF) replace older perf-event arrays for high-frequency events: one shared queue, reserve/submit in the program, poll in userspace. Aya (Rust) loads the bytecode, attaches the tracepoint, and reads the map. Check current Aya crate versions on crates.io at build time; this issue was verified against aya 0.14.x / aya-ebpf 0.2.x (July 2026).

Operating systems reuse PIDs. Lineage keyed only on pid collapses under wraparound and long-lived hosts. Issue 01 reserved process.uid; this agent populates it as {boot_id}:{pid}:{start_time_ns} (string). Parent events carry parent_process.uid with the same scheme. Tree reconstruction in userspace indexes by process.uid, not pid alone.

captiCaption: Parent–child edges use durable process.uid; PID reuse alone cannot reconstruct lineage.on...

Classification fields stay top-level on each event: class_uid 1007, activity_id 1 (Launch), type_uid 100701, severity_id 1. actor.process is the launching parent. Empty process.uid while claiming OCSF alignment breaks Module 7 fixtures and Module 9 joins later.

An unfiltered exec stream includes the agent binary, shells spawning helpers, and package managers. Lab 3 adds a denylist of basenames and self-pid suppression. This is the first contact with alert fatigue: volume without selection is not detection.

Prevention products that deny exec use eBPF LSM hooks (bpf_lsm), not observation-only tracepoints. This issue wires a log-only LSM stub behind a compile-time feature. Enforce mode needs CONFIG_BPF_LSM and bpf on the active LSM list (often a GRUB change and reboot on Ubuntu). Do not treat “flip a toggle” as a one-liner; Module 10 depth labs revisit enforce.

The Linux agent is layer 1 of the data plane. Events leave as JSON lines shaped for later protobuf/gRPC ingest. Ingestion, storage, and detection are still stubs.

Caption: Issue 02 activates the Linux agent; downstream layers remain placeholders until Phase B.aption

linux-agent streams OCSF-shaped process-creation events (stdout JSON). Completion criteria:

  1. cargo test -p linux-agent passes on any host (fixture-replay path).

  2. On a Linux Reader VM with privileges, --mode live attaches to sched_process_exec and prints Launch events with non-empty process.uid and type_uid 100701.

  3. You can explain why the agent uses sched_process_exec instead of an execve kprobe for the default path.

https://github.com/sysdr/production-xdr-edr/tree/main/v02-linux-agent-ebpf/agent-linux/linux-agent-common/src

Workspace layout under agent-linux/:

  • linux-agent-common — shared ExecEvent wire struct (#[repr(C)]) for eBPF and userspace

  • linux-agent-ebpf — tracepoint program + optional LSM stub (Linux bpf target)

  • linux-agent — loader, OCSF encoder, filter, tree, --mode replay|live

Replay mode reads fixtures/exec-events.jsonl and runs the same encoder/filter/tree path as live mode. Sandbox and macOS hosts use replay; live attach is the Reader VM column.

Encoder helpers (verbatim from agent-linux/linux-agent/src/ocsf.rs):

Full files and build commands: docs/implementation-guides/02-linux-agent-ebpf.md.

Limitation: sched_process_exec does not deliver a complete argv vector the way a carefully written execve kprobe might. Command lines in this issue come from /proc/<pid>/cmdline in userspace when the process still exists, with a short race window for short-lived binaries. Module 6 revisits richer capture if needed.

  1. Run linux-agent --mode replay and confirm stdout JSON includes type_uid 100701 and non-empty process.uid.

  2. Use --print-tree on the fixture stream and verify parent–child edges key on process.uid.

  3. Add a basename to the noise denylist; confirm matching fixture lines are dropped while others remain.

  4. On a Linux VM, build with --features lsm-stub, confirm the stub loads (or fails with a clear LSM-list message), and leave enforce off.

Issue 03 builds the Windows peer with ETW. Command lines do not come from Microsoft-Windows-Kernel-Process alone — plan for the NT Kernel Logger / system logger path before mirroring this OCSF shape on Windows.

Read the original on howtech.substack.com

Comments

Nothing yet. Say the first thing.

    Sign in to join the conversation.