Auto-learning, not manual rules
Files a container opens and destinations it dials during a learning window are added to a per-cgroup allow-set automatically. Nothing to author, nothing to keep current, no rule language to learn.
Pahlevan is an eBPF-powered Kubernetes runtime security tool. It self-learns per-workload baselines during a learning window, then enforces them in-kernel with LSM BPF - no hand-written rules.
EPERM.Kubernetes workloads face runtime attacks that slip past image scanning and perimeter defenses. Runtime tools either watch (alert-only) or make you write the rules yourself. Pahlevan does neither.
Files a container opens and destinations it dials during a learning window are added to a per-cgroup allow-set automatically. Nothing to author, nothing to keep current, no rule language to learn.
Under enforcement, an open of an unlearned path or an egress to an unlearned
destination is denied with EPERM by an LSM BPF program - the
syscall never succeeds. Detection tools only tell you it already happened.
Events are tied to the real container via bpf_get_current_cgroup_id(),
and file paths are resolved in-kernel with bpf_d_path(). A seccomp
profile is generated from the learned syscall set.
Every program below observes during the learning window and refuses during enforcement, scoped to a single cgroup. The allow-set is whatever the workload did while it was being watched.
| Kernel program | What it sees | What it does about it |
|---|---|---|
lsm/file_open |
Every open, with the path resolved in-kernel by bpf_d_path |
EPERM on a path outside the learned set |
lsm/socket_connect |
Every outbound connect, IPv4 and IPv6, named against cluster Services | EPERM on an unlearned destination |
lsm/bprm_check_security |
Every exec: binary, argv, working directory, four levels of ancestry | EPERM, or SIGKILL, on an unlearned binary |
lsm/capable |
Every capability check, plus the task's effective, permitted and inheritable sets | EPERM on a capability never exercised |
kprobe/commit_creds |
The moment privilege actually changes - including changes no syscall asked for | SIGKILL when privilege is gained with no execve to explain it |
tracepoint/sys_enter |
Every syscall with its six arguments; escalation primitives report every occurrence | Becomes the generated seccomp profile |
uretprobe/readline |
Commands typed at an interactive prompt, builtins included | Records what someone with a shell actually did |
Pahlevan splits the per-node eBPF data plane from a leader-elected control plane. The agent owns everything that touches the kernel; the operator owns everything that touches the Kubernetes API.
Runs on every node and owns the eBPF data plane: loading and attaching CO-RE programs, building per-container baselines, and enforcing locally in the kernel.
Requires no host access and runs in a user namespace (hostUsers: false),
handling policy lifecycle, cluster-wide status aggregation, and CEL-based admission
via a ValidatingAdmissionPolicy - no webhook.
Per-cgroup allow-sets built from observed file opens and egress destinations during a learning window.
lsm/file_open denies unlearned opens with EPERM, resolving full paths via bpf_d_path.
lsm/socket_connect denies connections to destinations outside the learned allow-set.
A raw_tracepoint/sys_enter sees every syscall, deduplicated in-kernel per (cgroup, syscall).
A seccomp profile is generated from the learned syscall set - least privilege without hand-authoring.
Compile-once / run-everywhere programs, portable across kernels without per-node compilation.
A PahlevanPolicy CRD drives a selector → learn → enforce lifecycle.
Automatic policy rollback when enforcement disrupts a workload.
The operator runs with hostUsers: false and needs no host access.
The allow-set cannot tell your app running python3 from an injected command running it. processFilter constrains the parent process, uid and gid at bprm_check_security.
A denial reads prod/postgres:5432, resolved from Services and pods the agent already caches. An address the cluster has never seen is tagged external — the difference between a misconfiguration and exfiltration.
Events ship as OTLP log records to the same collector as the metrics and traces, sharing the attributes Grafana joins on. Loki, Tempo and Mimir correlate without a hand-written query.
pahlevan policy explain -f policy.yaml translates it offline and names every part the data plane cannot enforce. --strict fails a CI gate.
A PahlevanPolicy selector matches target pods; the agent identifies their cgroups via bpf_get_current_cgroup_id().
During the learning window, every file a container opens (path resolved with bpf_d_path) and every egress destination is added to that cgroup's allow-set. Syscalls are observed in parallel.
On autoTransition (or when you flip the mode), the policy moves to enforcement and a seccomp profile is generated from the learned syscalls.
An open of an unlearned path - or egress to an unlearned destination - is denied in-kernel with EPERM before the operation completes.
kubectl apply -f https://github.com/obsernetics/pahlevan/releases/latest/download/install.yaml
helm repo add pahlevan https://obsernetics.github.io/pahlevan/charts
helm repo update
helm install pahlevan pahlevan/pahlevan-operator \
-n pahlevan-system --create-namespace
kubectl get pahlevanpolicy nginx-security -w
Start in Monitoring. Flip enforcementConfig.mode to
Blocking once you trust the learned baseline to have denials enforced
in the kernel.
apiVersion: policy.pahlevan.io/v1alpha1
kind: PahlevanPolicy
metadata:
name: nginx-security
spec:
selector:
matchLabels:
app: nginx
learningConfig:
duration: 5m # observe normal behavior
autoTransition: true # then enforce automatically
enforcementConfig:
mode: Monitoring # use Blocking to deny in-kernel
selfHealing:
enabled: true # roll back if it breaks the workload
test/benchmark/run.sh drives
26 attack scenarios and
3 benign controls through
a real workload inside a kernel-isolated VM, twice: once with no agent installed at
all, then with Pahlevan learning and enforcing.
The control pass is the part that makes the rest trustworthy. Without it, a scenario that silently failed to execute is indistinguishable from one that was prevented, and a CPU figure has no idle node to subtract. Scenarios are mapped to 27 MITRE ATT&CK for Containers techniques and committed alongside the harness, so a run is reproducible rather than reported.
Read the benign controls first. Pahlevan blocks some of them, because
curl and cat are outside a learned baseline too. That is the
trade the whole design makes, not a footnote to it: a baseline narrow enough to stop
an attacker is narrow enough to stop an administrator.
The container image ships the agent, operator, and CLI in one distroless image on GHCR. Pick Helm, the single-file manifest, or pull the image directly.
helm repo add pahlevan https://obsernetics.github.io/pahlevan/charts
helm repo update
helm install pahlevan pahlevan/pahlevan-operator \
-n pahlevan-system --create-namespace
kubectl apply -f https://github.com/obsernetics/pahlevan/releases/latest/download/install.yaml
docker pull ghcr.io/obsernetics/pahlevan:latest
# or pin a version
docker pull ghcr.io/obsernetics/pahlevan:v2.1.0
One distroless image contains pahlevan-agent (privileged DaemonSet),
pahlevan-operator (leader-elected Deployment), and the pahlevan CLI.
The Helm chart defaults its image to ghcr.io/obsernetics/pahlevan.
After installing, apply a PahlevanPolicy to start the learn → enforce loop.
CAP_BPF).CONFIG_BPF_LSM and lsm=bpf for in-kernel enforcement. Monitoring-only mode works without the BPF LSM.Pahlevan ships a reproducible harness built from real attack scenarios, with a no-agent control pass to give the numbers a denominator. Because enforcement requires the BPF LSM, it runs inside an eBPF-capable VM.
Results are generated by the harness, not hand-written - no numbers are published until a run is committed.
Benchmark methodology & results ↗Learn what your workloads do, then enforce it in the kernel - without writing a single rule.