
Jailer ist ein auf eBPF basierendes Prozess-Jailing-System, das eine verbindliche Zugriffskontrolle (MAC) für Linux bereitstellt. Es verfolgt Prozesse mithilfe von BPF-task_storage-Maps und setzt rollenbasierte Richtlinien für Dateizugriff, Netzwerkoperationen und Prozessausführung durch.
Warnung: Dieses Projekt befindet sich in intensiver Entwicklung und ist NICHT bereit für den Produktionseinsatz. APIs, Richtlinienformate und Verhalten können sich ohne Vorankündigung ändern. Nur zu Test- und Experimentierzwecken verwenden.
Hinweis: Dies ist eine unabhängige Implementierung und nicht dasselbe Projekt wie die Lösung von Meta. Obwohl BpfJailer funktional ähnlich und vom ursprünglichen Konzept und Design von Liam Wisehart, Justin Nga, Carl El Khoury, Mansee Chadha bei Meta inspiriert ist, handelt es sich um eine separate, unabhängig entwickelte Codebasis. Wir danken für die Vision und die grundlegenden Konzepte, die diese Arbeit inspiriert haben.
Jailer ist ein eBPF-basiertes Prozess-Isolierungssystem, das eine obligatorische Zugriffskontrolle (MAC) für Linux bereitstellt. Es verfolgt Prozesse mithilfe von BPF-task_storage-Maps und setzt rollenbasierte Richtlinien für Dateizugriff, Netzwerkoperationen und Prozessausführung durch.

BPF_MAP_TYPE_TASK_STORAGE-Unterstützung)# Aktuelle Kernel-Konfiguration prüfen
zcat /proc/config.gz 2>/dev/null || cat /boot/config-$(uname -r)
Erforderliche Optionen:
CONFIG_BPF=y
CONFIG_BPF_SYSCALL=y
CONFIG_BPF_LSM=y
CONFIG_DEBUG_INFO_BTF=y
BPF LSM muss in den Kernel-Boot-Parametern aktiviert sein:
# Prüfen, ob BPF LSM aktiv ist
cat /sys/kernel/security/lsm
# Sollte "bpf" in der Liste enthalten
# Falls nicht, zu den Kernel-Boot-Parametern hinzufügen:
# /etc/default/grub bearbeiten und zu GRUB_CMDLINE_LINUX hinzufügen:
# lsm=lockdown,capability,landlock,yama,apparmor,bpf
# Dann grub aktualisieren und neu starten:
sudo update-grub
sudo reboot
Für Ubuntu/Debian-Systeme kann auch Folgendes verwendet werden:
# Ein Skript erstellen, um BPF LSM zu aktivieren
cat > /tmp/enable_bpf_lsm.sh << 'EOF'
#!/bin/bash
GRUB_FILE="/etc/default/grub"
if grep -q "lsm=" "$GRUB_FILE"; then
sudo sed -i 's/lsm=[^""]*/lsm=lockdown,capability,landlock,yama,apparmor,bpf/' "$GRUB_FILE"
else
sudo sed -i 's/GRUB_CMDLINE_LINUX="\(.*\)"/GRUB_CMDLINE_LINUX="\1 lsm=lockdown,capability,landlock,yama,apparmor,bpf"/' "$GRUB_FILE"
fi
sudo update-grub
echo "BPF LSM aktiviert. Bitte neustarten."
EOF
chmod +x /tmp/enable_bpf_lsm.sh
sudo /tmp/enable_bpf_lsm.sh
# Rust installieren
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Build-Abhängigkeiten installieren (Ubuntu/Debian)
sudo apt-get install -y clang llvm libelf-dev linux-headers-$(uname -r)
# BPF-Ziel für Rust installieren
rustup target add bpfel-unknown-none
cd bpfjail
# BPF-Programme bauen
cd bpfjailer-bpf && cargo build --release && cd ..
# Daemon und Client bauen
cargo build --release
# Als root ausführen (lädt config/policy.json, falls vorhanden)
sudo RUST_LOG=info ./target/release/bpfjailer-daemon
Erwartete Ausgabe:
[INFO] BpfJailer daemon starting...
[INFO] Loading BpfJailer eBPF programs with libbpf-rs...
[INFO] ✓ pending_enrollments map available for enrollment
[INFO] ✓ network_rules map available for port/protocol filtering
[INFO] ✓ task_storage map created successfully
[INFO] ✓ Program task_alloc attached
[INFO] ✓ Program file_open attached
[INFO] ✓ Program socket_bind attached
[INFO] ✓ Program socket_connect attached
[INFO] ✓ Program bprm_check_security attached
[INFO] Initialized with default roles: restricted (1), permissive (2)
[INFO] Loaded policy from config/policy.json
[INFO] Loaded 5 roles
[INFO] Enrollment server listening on /run/bpfjailer/enrollment.sock
# Sicherheitslücken-Tests OHNE Jail ausführen (zeigt erfolgreiche Angriffe)
sudo python3 tests/vulnerable_apps/run_tests.py
# Sicherheitslücken-Tests MIT eingeschränkter Rolle ausführen (zeigt blockierte Angriffe)
sudo python3 tests/vulnerable_apps/run_tests.py --role 1
# Bestimmten Test ausführen
sudo python3 tests/vulnerable_apps/run_tests.py --role 1 --test path
# Verfügbare Tests und Rollen auflisten
sudo python3 tests/vulnerable_apps/run_tests.py --list
Beispielausgabe mit eingeschränkter Rolle:
============================================================
TEST: Path Traversal / Arbitrary File Read
============================================================
Attempting to read /etc/passwd via path traversal...
BLOCKED - Permission denied (BpfJailer blocked file access)
============================================================
TEST: Command Injection
============================================================
Attempting command injection...
BLOCKED - Permission denied (BpfJailer blocked exec)
============================================================
TEST: Reverse Shell / Data Exfiltration
============================================================
Test 1: Reverse shell connection to 127.0.0.1:4444
BLOCKED - Permission denied (BpfJailer blocked connect)
#!/usr/bin/env python3
import socket
import json
import os
# Verbindung zum Daemon herstellen
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.connect("/run/bpfjailer/enrollment.sock")
# Mit eingeschränkter Rolle anmelden (ID 1)
request = {"Enroll": {"pod_id": 1, "role_id": 1}}
sock.send((json.dumps(request) + "\n").encode())
response = sock.recv(4096).decode()
print(f"Anmeldung: {response}")
sock.close()
# Versuchen, eine Datei zu lesen (sollte blockiert werden)
try:
open("/etc/passwd").read()
print("Dateizugriff: ERLAUBT")
except PermissionError:
print("Dateizugriff: BLOCKIERT")
BpfJailer lädt Rollen aus einer JSON-Richtliniendatei. Der Daemon sucht in dieser Reihenfolge nach der Richtliniendatei:
$BPFJAILER_POLICY/etc/bpfjailer/policy.jsonconfig/policy.json (relativ zum Arbeitsverzeichnis){
"roles": {
"restricted": {
"id": 1,
"name": "restricted",
"flags": {
"allow_file_access": false,
"allow_network": false,
"allow_exec": false
},
"network_rules": []
},
"webserver": {
"id": 3,
"name": "webserver",
"flags": {
"allow_file_access": true,
"allow_network": true,
"allow_exec": false
},
"network_rules": [
{"protocol": "tcp", "port": 80, "allow": true},
{"protocol": "tcp", "port": 443, "allow": true}
]
}
},
"pods": []
}
BpfJailer unterstützt feingranulare Netzwerkkontrolle mit Pro-Port-TCP/UDP-Regeln.
network_rules map:
Schlüssel: { role_id, port, protocol, direction }
Wert: allowed (1) oder denied (0)
// Im Daemon-Code:
use bpfjailer_daemon::process_tracker::{PROTO_TCP, DIR_CONNECT};
// Nur TCP-Verbindungen zu den Ports 80 und 443 erlauben
process_tracker.add_network_rule(RoleId(1), 80, PROTO_TCP, DIR_CONNECT, true)?;
process_tracker.add_network_rule(RoleId(1), 443, PROTO_TCP, DIR_CONNECT, true)?;
Portbereiche können in policy.json mit port_start und port_end angegeben werden:
{
"network_rules": [
{"protocol": "tcp", "port": 443, "allow": true},
{"protocol": "tcp", "port_start": 8000, "port_end": 8100, "allow": true}
]
}
| Feld | Beschreibung |
|---|---|
port | Einzelner Port (z.B. 80) |
port_start + port_end | Portbereich (z.B. 8000-8100) |
Hinweis: Große Bereiche (>1000 Ports) verbrauchen viele BPF-Map-Einträge. Bei Bereichen über 1000 Ports wird eine Warnung protokolliert.
┌─────────────────────────────────────────────────────────────┐
│ User Space │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌──────────────────┐ │
│ │ Client │───▶│ bpfjailer-daemon │ │
│ │ (enroll) │ │ │ │
│ └─────────────┘ │ - PolicyManager │ │
│ │ - ProcessTracker│ │
│ │ - EnrollmentSvr │ │
│ └────────┬─────────┘ │
│ │ writes to │
│ ▼ │
├─────────────────────────────────────────────────────────────┤
│ BPF Maps │
│ ┌──────────────────┐ ┌─────────────┐ ┌──────────────┐ │
│ │pending_enrollments│ │ role_flags │ │ task_storage │ │
│ │ (PID → info) │ │ (role→flags)│ │(task→info) │ │
│ └──────────────────┘ └─────────────┘ └──────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ BPF LSM Hooks │
│ ┌────────────┐ ┌───────────┐ ┌─────────────┐ ┌──────────┐ │
│ │ task_alloc │ │ file_open │ │socket_bind/ │ │bprm_check│ │
│ │(inheritance│ │(migration │ │ connect │ │_security │ │
│ │ + init) │ │ + check) │ │ (check) │ │ (check) │ │
│ └────────────┘ └───────────┘ └─────────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────────┘
/run/bpfjailer/enrollment.sock{"Enroll": {"pod_id": N, "role_id": M}}pending_enrollments[PID] und role_flags[role_id]task_storagetask_storage + role_flags auf Durchsetzungtask_alloc-HookBpfJailer unterstützt zwei Installationsmodi:
Verwendet einen laufenden Daemon für Anmeldung und Richtlinienverwaltung:
# systemd-Dienst installieren
sudo cp config/bpfjailer-daemon.service /etc/systemd/system/
sudo cp target/release/bpfjailer-daemon /usr/sbin/
sudo mkdir -p /etc/bpfjailer
sudo cp config/policy.json /etc/bpfjailer/
# Aktivieren und starten
sudo systemctl daemon-reload
sudo systemctl enable bpfjailer-daemon
sudo systemctl start bpfjailer-daemon
Funktionen:
Lädt BPF-Programme beim frühen Booten und beendet sich. Die Programme bleiben bis zum Neustart aktiv:
# Bootstrap-Dienst installieren
sudo cp config/bpfjailer-bootstrap.service /etc/systemd/system/
sudo cp target/release/bpfjailer-bootstrap /usr/sbin/
sudo mkdir -p /etc/bpfjailer
sudo cp config/policy.json /etc/bpfjailer/
# Aktivieren (wird beim nächsten Booten ausgeführt)
sudo systemctl daemon-reload
sudo systemctl enable bpfjailer-bootstrap
# Oder jetzt manuell ausführen
sudo bpfjailer-bootstrap
Funktionen:
/sys/fs/bpf/bpfjailer/ verankertVerankerte Programme prüfen:
ls -la /sys/fs/bpf/bpfjailer/
ls -la /sys/fs/bpf/bpfjailer/maps/
ls -la /sys/fs/bpf/bpfjailer/progs/
Audit-Ereignisse anzeigen:
# Ereignisse werden in den Perf-Puffer gesendet, von journald aufgenommen
journalctl -f | grep bpfjailer
# Kernel-Version prüfen (benötigt 5.11+)
uname -r
# Prüfen, ob BPF LSM aktiv ist
cat /sys/kernel/security/lsm | grep bpf
# Prüfen, ob BTF verfügbar ist
ls -la /sys/kernel/btf/vmlinux
# Prüfen, ob BPF-Programme geladen werden können
sudo bpftool prog list
# Berechtigungen prüfen
sudo capsh --print | grep cap_bpf
# Sicherstellen, dass der Daemon als root läuft
ps aux | grep bpfjailer
# Socket-Berechtigungen prüfen
ls -la /run/bpfjailer/enrollment.sock
Neben der Unix-Socket-Anmeldung unterstützt BpfJailer die automatische Anmeldung basierend auf:
Alle Prozesse, die eine bestimmte Binärdatei ausführen, automatisch anmelden:
{
"exec_enrollments": [
{
"executable_path": "/usr/bin/nginx",
"pod_id": 1000,
"role": "webserver"
}
]
}
Wenn ein Prozess /usr/bin/nginx ausführt, wird er automatisch mit der Rolle webserver angemeldet. Dies wird durch den Inode der ausführbaren Datei abgeglichen, sodass Symlinks und Hardlinks korrekt behandelt werden.
Alle Prozesse in einer bestimmten Cgroup automatisch anmelden:
{
"cgroup_enrollments": [
{
"cgroup_path": "/sys/fs/cgroup/bpfjailer/sandbox",
"pod_id": 2000,
"role": "sandbox"
}
]
}
Cgroup erstellen und Prozesse hineinverschieben:
# Cgroup erstellen
sudo mkdir -p /sys/fs/cgroup/bpfjailer/sandbox
# Prozess in die Cgroup verschieben
echo $$ | sudo tee /sys/fs/cgroup/bpfjailer/sandbox/cgroup.procs
# Der Prozess ist jetzt automatisch mit der Rolle sandbox angemeldet
Erweiterte Attribute auf ausführbaren Dateien für Anmeldeinformationen setzen:
# Anmelde-Xattr setzen
sudo setfattr -n user.bpfjailer.pod_id -v $(printf '\x01\x00\x00\x00\x00\x00\x00\x00') /path/to/binary
sudo setfattr -n user.bpfjailer.role_id -v $(printf '\x03\x00\x00\x00') /path/to/binary
# Xattr prüfen
getfattr -d /path/to/binary
Zum Ausführungszeitpunkt (bprm_check_security-LSM-Hook):
exec_enrollment-Map istcgroup_enrollment-Map isttask_storage automatisch mit pod_id und role_id setzenDie Anmeldung bleibt über Fork/Exec dank des task_alloc-Hooks bestehen
Richtlinienregeln (Netzwerk, Pfad, Ausführung) werden basierend auf role_id angewendet
GPL-2.0 (erforderlich für BPF-Programme)
| Merkmal | Status | Beschreibung |
|---|
| Prozessverfolgung | ✅ Funktionsfähig | Verfolgt Prozesse mithilfe der task_storage-BPF-Map |
| Socket-Anmeldung | ✅ Funktionsfähig | Prozesse melden sich über die Unix-Socket-API an |
| Rollenbasierte Richtlinien | ✅ Funktionsfähig | Eingeschränkte und freizügige Rollen |
| Dateizugriffskontrolle | ✅ Funktionsfähig | Datei-Öffnen-Operationen blockieren/erlauben |
| Jail-Vererbung | ✅ Funktionsfähig | Kindprozesse erben das Jail des Elternprozesses |
| Netzwerkkontrolle | ✅ Funktionsfähig | Socket-Bind/Connect blockieren/erlauben |
| Port/Protokoll-Filterung | ✅ Funktionsfähig | Pro-Port-TCP/UDP-Erlauben/Verbieten-Regeln |
| Ausführungskontrolle | ✅ Funktionsfähig | Prozessausführung blockieren/erlauben |
| Pfadabgleich | ✅ Funktionsfähig | Dentry-Walking mit Cache-Invalidierung |
| Signierte Binärdateien | 🚧 Ansatz | Binärsignatur-Validierung (nicht implementiert) |
| Alternative Anmeldung | ✅ Funktionsfähig | Automatische Anmeldung per ausführbare Datei, Cgroup oder Xattr |
| Daemonloser Modus | ✅ Funktionsfähig | Bootstrap-Binärdatei verankert Programme beim frühen Booten |
| Audit-Ereignisse | ✅ Funktionsfähig | Perf-Puffer für systemd-journald-Integration |
| Test | Schwachstelle | Abgemildert durch |
|---|
path_traversal | Beliebiges Dateilesen via ../ | restricted, isolated |
command_injection | Shell-Befehlsausführung | restricted, webserver, isolated |
reverse_shell | Ausgehende Verbindungen zum Angreifer | restricted, isolated |
ssrf | Zugriff auf interne Dienste/Cloud-Metadaten | restricted, isolated |
arbitrary_write | Schreiben in sensible Pfade | restricted |
crypto_miner | Herunterladen + Ausführen + Verbinden zum Pool | restricted, webserver |
privilege_escalation | shadow lesen, sudoers schreiben | restricted |
| Flag | Beschreibung |
|---|
allow_file_access | Datei-Öffnen-Operationen erlauben |
allow_network | Socket-Bind/Connect erlauben |
allow_exec | Prozessausführung erlauben |
allow_setuid | Setuid-Operationen erlauben |
allow_ptrace | Ptrace-Operationen erlauben |
| Rollen-ID | Name | Dateizugriff | Netzwerk | Ausführung |
|---|
| 1 | restricted | Gesperrt | Gesperrt | Gesperrt |
| 2 | permissive | Erlaubt | Erlaubt | Erlaubt |
| 3 | webserver | Erlaubt | Ports 80, 443, 8080 | Gesperrt |
| 4 | database | Erlaubt | Ports 5432, 6379 | Gesperrt |
| 5 | isolated | Erlaubt | Gesperrt | Gesperrt |
| 6 | web_with_db | Erlaubt | Ports 80, 443, 5432, 3306, 6379 | Gesperrt |
| 7 | worker | Erlaubt | Ports 443, 5432, 6379, 5672 | Erlaubt |
| Konstante | Wert | Beschreibung |
|---|
PROTO_TCP | 6 | TCP-Protokoll |
PROTO_UDP | 17 | UDP-Protokoll |
DIR_BIND | 0 | socket bind() |
DIR_CONNECT | 1 | socket connect() |
| Aspekt | Daemon-Modus | Daemonloser Modus |
|---|
| Angriffsfläche | Laufender Daemon | Kein laufender Prozess |
| Anmeldung | Unix-Socket + Alternativen | Nur Alternativen |
| Richtlinienaktualisierungen | Heißes Neuladen | Neustart erforderlich |
| Audit-Protokollierung | Daemon liest Ringpuffer | journald via Perf-Puffer |
| Programm-Entfernung | Daemon stoppen | Nur Neustart |
| Boot-Reihenfolge | Nach network.target | Vor basic.target |