Cerberus is a Linux-based Intrusion Prevention System (IPS) built using eBPF and BPF LSM.
Unlike traditional monitoring systems that only observe suspicious activity, Cerberus can actively participate in Linux security decisions and block unauthorized operations before they are completed.
The project currently demonstrates real-time file access monitoring and prevention using an eBPF program running inside the Linux kernel.
The goal of Cerberus is to build a lightweight, real-time Intrusion Prevention System using eBPF.
The project evolves through the following stages:
Monitoring
↓
Detection
↓
Prevention
↓
Dynamic Security Policies
↓
Complete IPS Architecture
Instead of continuously polling system activity from user space, Cerberus attaches eBPF programs directly to kernel events and security hooks.
This allows the system to inspect operations with very low overhead.
The basic architecture of Cerberus is:
USER SPACE
┌───────────────────────────────────────┐
│ │
│ Cerberus Agent (C++) │
│ │
│ • Loads eBPF program │
│ • Receives security events │
│ • Displays blocked activity │
│ • Will manage security policies │
│ │
└───────────────────▲───────────────────┘
│
│ Ring Buffer
│
┌───────────────────┴───────────────────┐
│ │
│ KERNEL SPACE │
│ │
│ Cerberus eBPF Program │
│ │
│ • Intercepts security operations │
│ • Inspects file access │
│ • Applies security rules │
│ • Allows or blocks operations │
│ │
└───────────────────▲───────────────────┘
│
│
Linux Kernel
│
│
User Process
| Technology | Purpose |
|---|---|
| C | eBPF kernel program |
| C++ | User-space Cerberus agent |
| eBPF | Safe programmable logic inside the Linux kernel |
| BPF LSM | Security enforcement and blocking |
| libbpf | Communication between user space and eBPF |
| bpftool | BPF inspection and skeleton generation |
| Clang/LLVM | Compiling C code into BPF bytecode |
| CO-RE | Kernel compatibility using BTF information |
cerberus/
│
├── build/
│ ├── cerberus_agent
│ ├── ips_core.bpf.o
│ └── ips_core.skel.h
│
├── headers/
│ └── vmlinux.h
│
├── src/
│ ├── common.h
│ ├── ips_agent.cpp
│ └── ips_core.bpf.c
│
├── Makefile
├── .gitignore
└── README.md
This file contains structures shared between:
- Kernel-space eBPF program
- User-space C++ agent
Current event structure:
struct event {
int pid;
char comm[16];
char filename[256];
};The kernel program sends security events to user space.
Both sides must understand the event data in the same format.
Kernel creates:
struct event
↓
Ring Buffer
↓
User Space receives:
struct event
File:
src/ips_core.bpf.c
This is the core security enforcement component.
The current program attaches to:
SEC("lsm/file_open")This means Cerberus is attached to the Linux Security Module file-opening hook.
The flow is:
Process attempts to open a file
↓
Linux performs security check
↓
LSM file_open hook
↓
Cerberus eBPF program runs
↓
Check security policy
↓
┌─────┴─────┐
│ │
ALLOW BLOCK
│ │
▼ ▼
Continue Return -EPERM
↓
Operation denied
The current prototype checks whether the file name begins with:
sensitive
For example:
sensitive.txt
When such a file is accessed:
cat /tmp/sensitive.txt
Cerberus intercepts the operation through the BPF LSM hook.
The eBPF program then returns:
return -EPERM;EPERM means:
Operation not permitted
Linux then denies the operation.
Example:
cat: /tmp/sensitive.txt: Operation not permitted
This proves that Cerberus is performing real prevention, not just monitoring.
Cerberus uses a BPF Ring Buffer for communication between:
Kernel Space
↓
User Space
The flow is:
Suspicious Operation
↓
eBPF detects violation
↓
Create event
↓
Reserve Ring Buffer space
↓
Store PID
Store Process Name
Store Filename
↓
Submit event
↓
User-Space Agent receives event
↓
Display alert
Example output:
[BLOCKED INTRUSION]
PID: 1234
Process: cat
Protected Target: sensitive.txt
File:
src/ips_agent.cpp
The user-space agent is responsible for:
- Loading the eBPF program
- Attaching the eBPF program
- Connecting to the Ring Buffer
- Receiving security events
- Displaying blocked activity
The agent uses the automatically generated BPF skeleton.
The flow is:
Open BPF Skeleton
↓
Load BPF Program
↓
Attach Program to LSM Hook
↓
Connect to Ring Buffer
↓
Wait for Events
↓
Display Security Alerts
The skeleton is automatically generated using:
bpftool gen skeletonIt provides an easy interface for the C++ application to interact with the eBPF program.
Instead of manually handling every BPF object and map, the agent can use functions such as:
ips_core_bpf__open_and_load();and:
ips_core_bpf__attach();Pipeline:
ips_core.bpf.c
↓
Clang
↓
ips_core.bpf.o
↓
bpftool
↓
ips_core.skel.h
↓
C++ Agent
Cerberus uses a Makefile to automate compilation.
The build process is:
1. Generate vmlinux.h
↓
2. Compile eBPF C program
↓
3. Generate BPF skeleton
↓
4. Compile C++ agent
Detailed flow:
Linux Kernel BTF
↓
bpftool
↓
headers/vmlinux.h
↓
────────────────────────────
src/ips_core.bpf.c
↓
clang -target bpf
↓
build/ips_core.bpf.o
↓
────────────────────────────
bpftool gen skeleton
↓
build/ips_core.skel.h
↓
────────────────────────────
src/ips_agent.cpp
↓
g++
↓
build/cerberus_agent
make cleanmakeExpected pipeline:
Generate vmlinux.h
↓
Compile BPF program
↓
Generate skeleton
↓
Compile Cerberus agent
Run the agent with root privileges:
sudo ./build/cerberus_agentExpected output:
Cerberus IPS Enforcer Active. Running in BLOCKING mode...
Keep this terminal running.
Open another terminal.
Create a protected test file if necessary:
touch /tmp/sensitive.txtThen attempt to access it:
cat /tmp/sensitive.txtExpected result:
cat: /tmp/sensitive.txt: Operation not permitted
This confirms that Cerberus successfully intercepted and blocked the operation.
Check active Linux Security Modules:
cat /sys/kernel/security/lsmThe output should contain:
bpf
Example:
lockdown,capability,yama,selinux,bpf,...
To verify that Cerberus is loaded:
sudo bpftool prog list | grep lsmExpected output should include something similar to:
lsm name restrict_file_open
This confirms:
Cerberus eBPF Program
↓
Successfully Loaded
↓
Attached to Linux Security Framework
Completed.
Cerberus originally used a tracepoint:
tracepoint/syscalls/sys_enter_openat
The system could:
- Detect
openatsystem calls - Capture process ID
- Capture process name
- Capture file name
- Send information to user space
Architecture:
Process
↓
openat syscall
↓
eBPF Tracepoint
↓
Collect Information
↓
Ring Buffer
↓
User Space
Tracepoints can observe events, but they cannot prevent the operation.
So Cerberus was only a monitoring system.
Completed.
The architecture was changed from:
Tracepoint
↓
Observe Only 👀
to:
BPF LSM
↓
Security Decision 🛡️
Cerberus now attaches to:
lsm/file_open
This allows the program to participate in the Linux security decision.
The program can return:
0
Meaning:
Allow Operation
or:
-EPERM
Meaning:
Deny Operation
Cerberus successfully blocked:
cat /tmp/sensitive.txtwith:
Operation not permitted
🎉 Cerberus is now functioning as an actual Intrusion Prevention System prototype.
Currently, the security rule is hardcoded:
if filename starts with "sensitive"
This means changing the policy requires:
Change C code
↓
Recompile
↓
Reload BPF program
That is not practical for a real IPS.
Introduce a BPF Hash Map.
Architecture:
User Space Agent
│
│ Add / Remove Rules
▼
┌──────────────────────┐
│ BPF Hash Map │
│ │
│ Blocked Targets │
└──────────┬───────────┘
│
▼
Cerberus eBPF
│
▼
Security Check
│
┌────┴────┐
│ │
Match No Match
│ │
▼ ▼
BLOCK ALLOW
This will allow security policies to change dynamically without recompiling the eBPF program.
Potential development stages:
Use BPF Hash Maps for runtime policy management.
Block or allow specific processes.
Example:
Unknown Process
↓
Attempts Sensitive File Access
↓
Cerberus
↓
BLOCK
Monitor and prevent suspicious network activity.
Possible targets:
connect()
send()
execve()
Detect suspicious combinations such as:
Shell Process
+
Network Connection
+
Suspicious Execution
↓
Potential Reverse Shell
Create centralized policies managed by user space.
Store intrusion events for later analysis.
Potential future architecture:
eBPF Kernel Programs
↓
Cerberus Agent
↓
Event Processing
↓
API
↓
Dashboard
PHASE 1 ✅
eBPF System Call Monitoring
│
▼
PHASE 2 ✅
BPF LSM Active Prevention
│
▼
PHASE 3 🔜
Dynamic Blocklist using BPF Maps
│
▼
PHASE 4
Process-Based Security Policies
│
▼
PHASE 5
Network Security Monitoring
│
▼
PHASE 6
Attack Detection Engine
│
▼
PHASE 7
Centralized Policy Management
│
▼
PHASE 8
Dashboard and Alerting
The current implementation is a prototype.
Known limitations include:
- Filename matching is currently hardcoded.
- The current implementation checks the filename rather than maintaining a complete dynamic policy system.
- No persistent event logging yet.
- No centralized configuration.
- No network protection yet.
- No advanced attack correlation yet.
These limitations are intentional next development areas.
Since Cerberus is a team project, contributors should avoid directly pushing experimental work to main.
Recommended workflow:
git checkout main
git pullCreate a feature branch:
git checkout -b feature-nameMake changes and test:
make clean
makeCommit changes:
git add .
git commit -m "Describe your feature"Push the branch:
git push -u origin feature-nameThen create a Pull Request for review.
Recommended structure:
main
│
├── feature/dynamic-blocklist
│
├── feature/process-rules
│
├── feature/network-monitoring
│
└── feature/logging
Cerberus demonstrates an important distinction in eBPF security:
Tracepoints
↓
Observe events
↓
Monitoring
vs
BPF LSM
↓
Participate in security decisions
↓
Prevention
The major milestone of the project was moving from:
"We can see suspicious activity."
to:
"We can stop suspicious activity."
┌─────────────────────────────────────┐
│ CERBERUS STATUS │
├─────────────────────────────────────┤
│ │
│ eBPF Build Pipeline ✅ │
│ Kernel BTF / CO-RE ✅ │
│ Tracepoint Monitoring ✅ │
│ Ring Buffer Events ✅ │
│ User-Space Agent ✅ │
│ BPF LSM Enabled ✅ │
│ File Access Blocking ✅ │
│ Dynamic Policies 🔜 │
│ Advanced Detection 🔜 │
│ Dashboard 🔜 │
│ │
└─────────────────────────────────────┘
Observe. Detect. Prevent.