Introduction: eBPF — The Double-Edged Sword Inside Your Kernel
If you've been keeping an eye on Linux kernel security lately, you've almost certainly run into eBPF (extended Berkeley Packet Filter). What started as a humble packet filtering mechanism has quietly evolved into a full-blown in-kernel virtual machine — one that lets you run sandboxed programs right at the kernel level without touching kernel source code or dealing with kernel modules. It's become the backbone of modern observability and networking, and increasingly, it sits at the heart of both security defense and security threats.
Here's what keeps me up at night: eBPF has become one of the most potent attack vectors on Linux systems. FortiGuard Labs detected 151 new samples of the BPFDoor backdoor and three new Symbiote variants in 2025 alone. The LinkPro rootkit, discovered in October 2025 targeting AWS-hosted Kubernetes clusters, showed how eBPF can hide processes, intercept network traffic, and exfiltrate data — all while flying completely under the radar of traditional EDR solutions. Most Linux antivirus tools simply can't monitor in-kernel eBPF activity because their telemetry doesn't reach that deep.
But here's the twist — eBPF is also the most effective defense against these very threats. Tools like Cilium Tetragon, Falco, and Tracee use eBPF to monitor system calls, catch anomalous behavior, and enforce security policies directly in the kernel with minimal performance overhead. The best way to fight an eBPF rootkit? Better eBPF.
This guide covers both sides of the coin. You'll learn how eBPF works at a security-relevant level, how attackers are weaponizing it, how to detect and defend against eBPF-based threats, and how to deploy eBPF-powered security tools for runtime protection. Whether you're a sysadmin, a DevSecOps engineer, or a security researcher, you'll walk away with practical knowledge to both harden your systems and leverage eBPF for real-time defense.
How eBPF Works: A Security-Focused Overview
The eBPF Architecture
eBPF programs are small, event-driven programs that run inside the Linux kernel's eBPF virtual machine. Unlike kernel modules (which can be terrifying to deploy in production), eBPF programs go through a strict verification process before loading. The eBPF verifier checks for memory safety, makes sure programs actually terminate — no infinite loops allowed — and validates all memory accesses. Once verified, programs get JIT-compiled to native machine code for near-native execution speed.
The key architectural components you need to understand from a security perspective:
- Program Types — eBPF supports a bunch of program types, each attached to different kernel hook points: kprobes (kernel function entry/exit), tracepoints, network hooks (XDP, TC), LSM hooks, and more. The type determines what a program can do and what data it can access.
- Maps — Shared data structures (hash tables, arrays, ring buffers) that let eBPF programs talk to each other and to user-space applications. Maps persist across eBPF program invocations and can store state, statistics, or configuration.
- Helper Functions — A defined set of kernel functions that eBPF programs can call. These provide capabilities like reading process info, modifying packets, sending signals, or overriding return values.
- BTF (BPF Type Format) — Type information embedded in the kernel that makes eBPF programs portable across kernel versions. BTF is critical for tools like Tetragon and Falco that need to work across diverse environments without recompilation.
Why eBPF is Security-Relevant
From a security standpoint, eBPF's power boils down to its position: it runs inside the kernel with direct access to kernel data structures. An eBPF program attached to a kprobe on tcp_connect sees every TCP connection attempt before it reaches user-space. A program on the sys_enter_execve tracepoint sees every single process execution. That deep visibility is exactly what defenders need — and exactly what attackers want.
The critical security properties worth understanding:
- Kernel-level execution — Programs run in kernel context, giving them access to raw system events before any user-space filtering can occur.
- Stealth — eBPF programs don't show up in process listings, don't leave traditional filesystem footprints, and are invisible to most security tools that focus on user-space activity. That alone should make you nervous.
- Network interception — XDP and TC programs can inspect, modify, or drop network packets at the driver level, before the kernel's networking stack even processes them.
- Persistence — eBPF programs can be pinned to the BPF filesystem (
/sys/fs/bpf/), surviving the termination of the loader process.
The eBPF Threat Landscape: How Attackers Weaponize the Kernel
BPFDoor: The Pioneer of eBPF-Based Backdoors
BPFDoor was among the first widely documented eBPF-based backdoors, and it exposed a fundamental problem: BPF packet filters get processed by the kernel before firewall rules. So an attacker can craft a "magic packet" that triggers the backdoor even when the packet would be blocked by iptables or nftables. The malware attaches a classic BPF (cBPF) filter to a raw socket, watches for packets containing a specific byte sequence, and opens a reverse shell when it detects the trigger — all without opening any listening ports.
Honestly, the elegance of this approach is unsettling.
In 2025, FortiGuard Labs discovered new BPFDoor variants with enhanced capabilities: IPv6 support, UDP-based traffic, and dynamic port hopping for covert C2 communication. These variants are significantly harder to detect with traditional network monitoring because they blend in with legitimate traffic patterns.
Symbiote: The Parasitic eBPF Rootkit
Symbiote takes eBPF abuse to another level. Unlike traditional malware that runs as a separate process, Symbiote injects itself into every running process through LD_PRELOAD and uses eBPF programs to hide its network activity. The eBPF component filters packets at the socket level, ensuring that tools like tcpdump, ss, and lsof never see the malicious connections. Even an admin running tcpdump with root privileges sees a sanitized view of network traffic — because the eBPF filter runs before the capture tool receives the packets.
Think about that for a second. You're root, running a packet capture, and you still can't see what's happening on your own machine.
LinkPro: eBPF Rootkits Hit the Cloud
Discovered in October 2025, LinkPro targeted AWS-hosted Kubernetes clusters and represented a big step forward for eBPF-based threats. LinkPro used eBPF programs attached to tracepoints and kprobes to:
- Hide specific processes from
/procenumeration - Intercept and modify DNS responses to redirect traffic
- Exfiltrate container secrets and Kubernetes service account tokens
- Persist across pod restarts by pinning programs to the BPF filesystem on the host node
LinkPro was activated via "magic" TCP packets — similar to BPFDoor — but used more sophisticated eBPF techniques including XDP programs for network interception, making it nearly invisible to standard security monitoring.
The Core Detection Problem
Traditional security tools face a fundamental challenge with eBPF-based threats: they operate in user-space, while the malicious eBPF programs run in kernel-space. An EDR agent that monitors system calls only sees what the kernel chooses to expose. If an eBPF program is modifying kernel data structures or filtering syscall results before they reach user-space, the EDR is essentially working with tampered data.
This is the "visibility gap" that's made eBPF-based malware so effective — and so scary.
Hardening Linux Against eBPF Abuse
Before you deploy any fancy defensive eBPF tools, you need to lock down the eBPF subsystem itself. These hardening steps reduce the attack surface and make it meaningfully harder for adversaries to load malicious eBPF programs.
Step 1: Disable Unprivileged BPF Access
This is the single most important hardening step. On many distributions, unprivileged users can create BPF maps and load certain program types by default. You'll want to disable this immediately:
# Check current setting
sysctl kernel.unprivileged_bpf_disabled
# Disable unprivileged BPF access (cannot be re-enabled without reboot)
sudo sysctl -w kernel.unprivileged_bpf_disabled=1
# Make persistent across reboots
echo "kernel.unprivileged_bpf_disabled=1" | sudo tee /etc/sysctl.d/99-ebpf-hardening.conf
sudo sysctl --system
Once set to 1, this value can't be changed back to 0 without a reboot — that's intentional, by the way. It prevents attackers from re-enabling unprivileged BPF access after gaining limited privileges. Starting with kernel 5.16, you can also set this to 2, which disables unprivileged BPF but lets root re-enable it if needed.
Step 2: Restrict BPF-Related Capabilities
Even for privileged users, you should use Linux capabilities to limit which processes can interact with the BPF subsystem. Here are the relevant capabilities:
- CAP_BPF (since Linux 5.8) — Required to create BPF maps, load BPF programs, and access BPF objects.
- CAP_PERFMON — Required to attach BPF programs to tracepoints, kprobes, and perf events.
- CAP_NET_ADMIN — Required for network-related BPF programs (XDP, TC, socket filters).
- CAP_SYS_ADMIN — The legacy catch-all capability that grants full BPF access. You really want to avoid handing this out.
For containers, make sure you drop these capabilities unless they're specifically needed:
# Docker: Run container without BPF capabilities
docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE myapp:latest
# Kubernetes: PodSecurityContext to restrict BPF
apiVersion: v1
kind: Pod
metadata:
name: hardened-app
spec:
containers:
- name: app
image: myapp:latest
securityContext:
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
Step 3: Enable BPF JIT Hardening
The BPF JIT compiler translates verified BPF bytecode into native machine code. While this is great for performance, it also opens the door to JIT spray attacks. Enable JIT hardening to mitigate this:
# Enable BPF JIT hardening
sudo sysctl -w net.core.bpf_jit_harden=2
# Make persistent
echo "net.core.bpf_jit_harden=2" | sudo tee -a /etc/sysctl.d/99-ebpf-hardening.conf
sudo sysctl --system
Setting the value to 2 enables hardening for all users, including root. This adds constant blinding to JIT-compiled code, making it resistant to JIT spray attacks at a small performance cost. Value 1 only hardens for unprivileged users — not enough if you're serious about defense.
Step 4: Monitor the BPF Filesystem and Loaded Programs
You should regularly audit what eBPF programs are loaded on your systems. The bpftool utility is your go-to inspection tool here:
# List all loaded BPF programs
sudo bpftool prog list
# List all BPF maps
sudo bpftool map list
# Show detailed info about a specific program (by ID)
sudo bpftool prog show id 42
# Dump the BPF program's bytecode instructions
sudo bpftool prog dump xlated id 42
# Check for pinned programs on the BPF filesystem
sudo find /sys/fs/bpf/ -type f 2>/dev/null
# List all attached kprobes (potential hooks)
sudo cat /sys/kernel/debug/kprobes/list 2>/dev/null
# Export as JSON for automated processing
sudo bpftool prog list --json | python3 -m json.tool
Create a baseline of expected BPF programs and automate periodic comparison. Here's a straightforward monitoring script:
#!/bin/bash
# ebpf-audit.sh — Periodic eBPF program auditing
BASELINE="/etc/ebpf-baseline.json"
CURRENT="/tmp/ebpf-current.json"
ALERT_LOG="/var/log/ebpf-audit.log"
# Capture current state
sudo bpftool prog list --json > "$CURRENT" 2>/dev/null
# Compare with baseline
if [ -f "$BASELINE" ]; then
DIFF=$(diff <(jq -S '.[] | {type, tag, name}' "$BASELINE") \
<(jq -S '.[] | {type, tag, name}' "$CURRENT"))
if [ -n "$DIFF" ]; then
echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] ALERT: eBPF program change detected" >> "$ALERT_LOG"
echo "$DIFF" >> "$ALERT_LOG"
# Send alert via your preferred notification channel
logger -p security.alert "eBPF program inventory changed — review $ALERT_LOG"
fi
else
echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] INFO: Creating initial eBPF baseline" >> "$ALERT_LOG"
cp "$CURRENT" "$BASELINE"
fi
Step 5: Implement Signed BPF Programs (Kernel 6.x+)
For organizations with strict security requirements, newer kernels support eBPF program signing. This ensures that only programs cryptographically signed by a trusted authority can be loaded into the kernel. Adoption is still growing in 2026, but this is clearly where kernel security is headed:
# Check if your kernel supports BPF token-based delegation
# (available in kernel 6.9+)
grep -i bpf_token /boot/config-$(uname -r)
# Lockdown mode also restricts BPF loading
# Check current lockdown status
cat /sys/kernel/security/lockdown
On systems that support it, Kernel Lockdown Mode (set to integrity or confidentiality) restricts BPF program loading, preventing even root from loading arbitrary eBPF programs unless the system is booted with specific parameters. This is particularly useful for production servers where the set of required eBPF programs is known and fixed.
Defensive eBPF: Runtime Security with Tetragon
Cilium Tetragon is a CNCF project that provides eBPF-based security observability and runtime enforcement. What sets it apart from detection-only tools is that Tetragon can both monitor and block malicious activity directly in the kernel. That's a big deal.
Installing Tetragon on Linux
For standalone Linux (non-Kubernetes) deployments:
# Install Tetragon using the official repository (Debian/Ubuntu)
curl -sL https://github.com/cilium/tetragon/releases/latest/download/tetragon-linux-amd64.tar.gz \
-o tetragon.tar.gz
tar xzf tetragon.tar.gz
sudo install -o root -g root -m 0755 tetragon /usr/local/bin/tetragon
sudo install -o root -g root -m 0755 tetra /usr/local/bin/tetra
# Create configuration directory
sudo mkdir -p /etc/tetragon/tetragon.conf.d/
sudo mkdir -p /etc/tetragon/tetragon.tp.d/
# Start Tetragon with default settings
sudo tetragon --bpf-lib /usr/local/lib/tetragon/bpf/ &
For Kubernetes environments, Helm is the way to go:
# Add the Cilium Helm repository
helm repo add cilium https://helm.cilium.io
helm repo update
# Install Tetragon
helm install tetragon cilium/tetragon -n kube-system
# Verify pods are running
kubectl get pods -n kube-system -l app.kubernetes.io/name=tetragon
Writing Tetragon Tracing Policies
Tetragon's real power comes from its TracingPolicy custom resource. Let me walk you through some practical policies for common security scenarios.
Detect unauthorized file access:
# /etc/tetragon/tetragon.tp.d/sensitive-file-access.yaml
apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
name: sensitive-file-access
spec:
kprobes:
- call: "security_file_open"
syscall: false
args:
- index: 0
type: "file"
selectors:
- matchArgs:
- index: 0
operator: "Prefix"
values:
- "/etc/shadow"
- "/etc/passwd"
- "/etc/sudoers"
- "/root/.ssh/"
- "/etc/ssh/sshd_config"
matchActions:
- action: Post
rateLimit: "1m"
Monitor and block reverse shell attempts:
# /etc/tetragon/tetragon.tp.d/block-reverse-shells.yaml
apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
name: block-reverse-shells
spec:
tracepoints:
- subsystem: "syscalls"
event: "sys_enter_connect"
args:
- index: 1
type: "sockaddr"
selectors:
- matchArgs:
- index: 1
operator: "NotDAddr"
values:
- "127.0.0.1"
- "10.0.0.0/8"
- "172.16.0.0/12"
- "192.168.0.0/16"
matchBinaries:
- operator: "In"
values:
- "/bin/bash"
- "/bin/sh"
- "/usr/bin/bash"
- "/usr/bin/python3"
- "/usr/bin/perl"
- "/usr/bin/nc"
- "/usr/bin/ncat"
matchActions:
- action: Sigkill
Fair warning: this policy is pretty aggressive — it kills any shell or scripting interpreter that tries to connect to an external IP address. In production, you'd probably want to refine the binary list and destination ranges. But for sensitive systems, this catches a wide range of reverse shell techniques and that trade-off might be worth it.
Detect privilege escalation via SUID binaries:
# /etc/tetragon/tetragon.tp.d/suid-execution.yaml
apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
name: suid-execution-monitor
spec:
kprobes:
- call: "security_bprm_creds_from_file"
syscall: false
args:
- index: 0
type: "linux_binprm"
- index: 1
type: "file"
selectors:
- matchActions:
- action: Post
Using Tetra CLI for Real-Time Monitoring
The tetra CLI gives you real-time access to Tetragon events:
# Stream all process execution events
sudo tetra getevents -o compact
# Filter for specific event types
sudo tetra getevents -o compact --process-filter exec
# JSON output for SIEM integration
sudo tetra getevents -o json | jq '.process_exec | select(.process.binary | contains("sh"))'
# Example output for a process execution:
# 🚀 process default/nginx /bin/sh -c "whoami"
# 💥 exit default/nginx /bin/sh -c "whoami" 0
Defensive eBPF: Runtime Detection with Falco
While Tetragon excels at enforcement, Falco (a CNCF graduated project) is really the gold standard for runtime threat detection. Falco uses eBPF to monitor system calls and generates alerts based on a rich rules language. I've found it pairs well with Tetragon — they complement each other nicely.
Installing Falco with eBPF Driver
# Add Falco's repository (Debian/Ubuntu)
curl -fsSL https://falco.org/repo/falcosecurity-packages.asc | \
sudo gpg --dearmor -o /usr/share/keyrings/falco-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/falco-archive-keyring.gpg] \
https://download.falco.org/packages/deb stable main" | \
sudo tee /etc/apt/sources.list.d/falcosecurity.list
sudo apt-get update
sudo apt-get install -y falco
# Configure Falco to use the modern eBPF driver
sudo falcoctl driver install --type modern_ebpf
# Start Falco
sudo systemctl enable --now falco-modern-bpf.service
Writing Custom Falco Rules for eBPF Threat Detection
Here are custom rules specifically designed to catch eBPF-based threats:
# /etc/falco/rules.d/ebpf-threats.yaml
# Detect BPF system call usage by non-standard processes
- rule: Unexpected BPF System Call
desc: >
Detects processes making bpf() system calls that are not part of
the expected set of security and monitoring tools.
condition: >
evt.type = bpf and not proc.name in (tetragon, falco, cilium-agent,
systemd, bpftool, node_exporter)
output: >
Unexpected BPF syscall (user=%user.name command=%proc.cmdline
pid=%proc.pid parent=%proc.pname container=%container.id
evt_type=%evt.arg.cmd)
priority: WARNING
tags: [ebpf, threat_detection]
# Detect access to raw sockets (used by BPFDoor)
- rule: Raw Socket Creation
desc: >
Detects creation of raw sockets, commonly used by BPFDoor-style
backdoors to sniff for magic packets.
condition: >
evt.type in (socket) and evt.arg.type contains RAW
and not proc.name in (ping, traceroute, tcpdump, nmap)
output: >
Raw socket created (user=%user.name command=%proc.cmdline
pid=%proc.pid parent=%proc.pname)
priority: CRITICAL
tags: [ebpf, bpfdoor, network]
# Detect writes to the BPF filesystem
- rule: BPF Filesystem Write
desc: Detect programs being pinned to the BPF filesystem for persistence.
condition: >
(evt.type in (open, openat, openat2) and evt.arg.flags contains O_WRONLY
and fd.name startswith /sys/fs/bpf/)
or (evt.type in (rename, renameat, renameat2) and fd.name startswith /sys/fs/bpf/)
output: >
Write to BPF filesystem detected (user=%user.name command=%proc.cmdline
file=%fd.name pid=%proc.pid)
priority: CRITICAL
tags: [ebpf, persistence]
Integrating Falco Alerts with Your SIEM
Falco supports multiple output channels. For production setups, you'll want to forward alerts to your SIEM or log aggregation platform:
# /etc/falco/falco.yaml (relevant sections)
# Output to syslog for SIEM collection
syslog_output:
enabled: true
# Output to a file for log shipping
file_output:
enabled: true
keep_alive: true
filename: /var/log/falco/events.log
# HTTP webhook for real-time alerting (e.g., Slack, PagerDuty)
http_output:
enabled: true
url: "https://your-webhook-endpoint.example.com/falco"
user_agent: "falco/0.x"
# JSON output format for easier parsing
json_output: true
json_include_output_property: true
json_include_tags_property: true
Detecting eBPF-Based Rootkits with Tracee
Aqua Security's Tracee is an open-source runtime security tool that uses eBPF to detect suspicious behavioral patterns — including eBPF abuse itself. What I particularly like about Tracee is its approach to detecting syscall hooking, which is especially effective against eBPF rootkits.
Deploying Tracee for eBPF Threat Detection
# Run Tracee as a container (simplest deployment)
docker run --name tracee -it --rm \
--pid=host --cgroupns=host \
--privileged \
-v /etc/os-release:/etc/os-release-host:ro \
-v /boot/config-$(uname -r):/boot/config-$(uname -r):ro \
aquasec/tracee:latest \
--filter event=bpf,security_bpf,security_bpf_prog \
--output json
# Example: detect BPF program loading events
docker run --name tracee -it --rm \
--pid=host --cgroupns=host \
--privileged \
-v /etc/os-release:/etc/os-release-host:ro \
aquasec/tracee:latest \
--filter event=bpf \
--filter 'args.cmd=BPF_PROG_LOAD' \
--output json
Tracee monitors the bpf() system call directly and can detect when new eBPF programs are loaded, what type they are, and which process loaded them. This provides visibility into eBPF activity that traditional tools completely miss.
Building a Comprehensive eBPF Security Monitoring Pipeline
No single tool gives you complete eBPF security coverage. Here's how to layer them together for real defense-in-depth:
Architecture Overview
┌─────────────────────────────────────────────────┐
│ Linux Kernel │
│ ┌───────────┐ ┌───────────┐ ┌───────────────┐ │
│ │ Tetragon │ │ Falco │ │ Tracee │ │
│ │ (enforce) │ │ (detect) │ │ (forensics) │ │
│ └─────┬─────┘ └─────┬─────┘ └──────┬────────┘ │
│ │ │ │ │
└────────┼──────────────┼──────────────┼────────────┘
│ │ │
┌────▼────┐ ┌────▼────┐ ┌────▼────┐
│ Enforce │ │ Alert │ │ Analyze │
│ Policy │ │ to SIEM │ │ Behavior│
└─────────┘ └─────────┘ └─────────┘
Each tool has a distinct role in this stack:
- Tetragon — Your active defense layer. Kill processes, block connections, deny file access based on policy. This is where you stop attacks in real-time.
- Falco — Broad detection coverage with extensive rule libraries. Catches suspicious patterns and generates high-fidelity alerts for your security operations team.
- Tracee — Deep forensic analysis and behavioral detection. Particularly valuable when you're investigating eBPF-based threats or trying to understand attacker techniques post-incident.
- bpftool audit script — Baseline comparison for detecting unauthorized eBPF program loading (the script we built earlier in this guide).
Automated Response Pipeline
Here's a practical example of tying these tools into an automated response pipeline using a systemd service and shell scripting:
#!/bin/bash
# ebpf-response.sh — Automated eBPF threat response
# Triggered by Falco alert webhook or log watcher
EVENT_TYPE="$1"
PROCESS_PID="$2"
PROCESS_NAME="$3"
log_event() {
logger -p security.crit "eBPF-RESPONSE: $*"
}
case "$EVENT_TYPE" in
"unexpected_bpf_load")
log_event "Unexpected BPF load by $PROCESS_NAME (PID: $PROCESS_PID)"
# Capture forensic data before taking action
sudo bpftool prog list --json > "/var/log/ebpf-forensics/progs-$(date +%s).json"
sudo bpftool map list --json > "/var/log/ebpf-forensics/maps-$(date +%s).json"
# Capture process details
cat /proc/$PROCESS_PID/cmdline > "/var/log/ebpf-forensics/cmdline-${PROCESS_PID}-$(date +%s).txt" 2>/dev/null
cat /proc/$PROCESS_PID/maps > "/var/log/ebpf-forensics/maps-${PROCESS_PID}-$(date +%s).txt" 2>/dev/null
;;
"raw_socket_creation")
log_event "Raw socket created by $PROCESS_NAME (PID: $PROCESS_PID) — potential BPFDoor"
# Capture network state
ss -tlnp > "/var/log/ebpf-forensics/sockets-$(date +%s).txt"
;;
"bpf_filesystem_write")
log_event "BPF filesystem write by $PROCESS_NAME (PID: $PROCESS_PID) — potential persistence"
# Document what was pinned
find /sys/fs/bpf/ -type f -newer /var/log/ebpf-forensics/.last-check \
> "/var/log/ebpf-forensics/new-pins-$(date +%s).txt" 2>/dev/null
touch /var/log/ebpf-forensics/.last-check
;;
esac
Performance Considerations and Best Practices
Running multiple eBPF security tools simultaneously is totally feasible — eBPF's inherently low overhead makes this possible. But you should still keep an eye on resource usage, especially on busy production systems.
Measuring eBPF Overhead
# Check CPU usage of eBPF programs
sudo bpftool prog profile id <PROG_ID> duration 10
# Monitor overall BPF subsystem statistics
sudo cat /proc/stat | grep bpf
# Track memory usage of BPF maps
sudo bpftool map list | grep -i bytes
# Check BPF program run counts and timing
sudo bpftool prog show id <PROG_ID> | grep run_cnt
Best Practices for Production Deployments
- Start with monitoring, then add enforcement — Deploy Falco and Tracee in detection-only mode first. Understand your baseline before enabling Tetragon's enforcement actions. Trust me, killing legitimate processes in production is far worse than missing an alert.
- Use rate limiting on alerts — High-frequency events like file accesses and network connections can generate enormous volumes of alerts. Configure rate limits in your tracing policies to prevent alert fatigue and resource exhaustion.
- Pin critical BPF programs — Your defensive eBPF programs should be pinned to
/sys/fs/bpf/so they survive process restarts. Configure your security tools to automatically re-pin on startup. - Monitor the monitors — Use systemd watchdog functionality or a separate health-check mechanism to make sure your eBPF security tools are actually running. An attacker's first move might be to disable your monitoring.
- Keep kernels updated — eBPF verifier bugs get discovered regularly and can be exploited to load malicious programs that bypass safety checks. Kernel updates aren't optional here; they're critical for eBPF security.
- Implement least-privilege BPF access — Use
CAP_BPFandCAP_PERFMONinstead ofCAP_SYS_ADMINwhere possible. Grant BPF capabilities only to the specific service accounts that actually need them.
Quick Reference: Complete eBPF Hardening Checklist
Use this checklist to systematically harden your Linux systems against eBPF-based threats:
## Kernel Parameters
☐ kernel.unprivileged_bpf_disabled = 1
☐ net.core.bpf_jit_harden = 2
☐ Kernel lockdown mode enabled (if feasible)
## Capability Management
☐ CAP_BPF, CAP_PERFMON, CAP_NET_ADMIN, CAP_SYS_ADMIN dropped from all containers
☐ Only authorized security tools granted BPF capabilities
☐ allowPrivilegeEscalation: false in all pod security contexts
## Monitoring and Detection
☐ bpftool baseline created and periodic comparison automated
☐ /sys/fs/bpf/ monitored for unexpected changes
☐ /sys/kernel/debug/kprobes/list audited regularly
☐ Falco deployed with eBPF-specific detection rules
☐ Tracee deployed for BPF syscall monitoring
## Runtime Enforcement
☐ Tetragon deployed with TracingPolicies for critical assets
☐ Reverse shell prevention policies active
☐ Sensitive file access policies configured
☐ Alert pipeline connected to SIEM/SOC
## Operational
☐ eBPF security tool health monitored (watchdog/heartbeat)
☐ Kernel regularly updated (eBPF verifier bug patches)
☐ Incident response playbook includes eBPF forensics steps
☐ BPF program inventory documented and maintained
Conclusion: Embracing the eBPF Security Paradigm
eBPF represents a fundamental shift in Linux security. The same technology behind the stealthiest rootkits we've seen — BPFDoor, Symbiote, LinkPro — also powers the most effective defenses against them. That duality isn't going away. If anything, it's accelerating as eBPF capabilities expand with each kernel release.
The practical steps in this guide — hardening the BPF subsystem, deploying Tetragon for enforcement, Falco for detection, and Tracee for forensics — give you a layered defense strategy that addresses the eBPF threat landscape as it stands today. But things move fast. The 151 new BPFDoor samples and the emergence of cloud-native eBPF rootkits like LinkPro in 2025 make it clear that attackers are investing heavily in this space.
So your defense needs to keep pace. Start with the hardening checklist, deploy at least one eBPF monitoring tool, and build from there. The kernel is both the battlefield and the watchtower — and eBPF is the technology that determines who controls it.