Kernel-level security enforcement that follows your containers everywhere.
Write policy once, compile to WASM, evaluate it the same way on every platform.
Warmor (WebAssembly + Armor) is a runtime security enforcer. It hooks the kernel with eBPF and LSM-BPF and evaluates every event against a policy running in a WebAssembly sandbox. It targets the policy portability problem: write security rules once in YAML or Rust, compile them to WASM, and evaluate them with the same engine on Linux, Windows and macOS. Linux is the mature platform; Windows and macOS are experimental.
How enforcement works today (two tiers):
- In the kernel (Linux,
--lsm-enforce). LSM hooks return-EPERMfor operations that already have a deny entry in the kernel policy map. Onmain, those entries are only written for exec (exact binary path) and connect (remote address and port). They are written after the WASM policy has seen that operation once. - In userspace (everything else). A map miss is allowed by the kernel and sent to the daemon. The WASM policy evaluates it, and on DENY the daemon kills the process with
SIGKILL. The first occurrence of a denied operation, and every file, bind, listen, ptrace or mount deny, is therefore detected and then killed, not prevented.
Kernel default-deny (--default-deny, opt-in) closes that first-occurrence gap for exec and connect: when the policy is an allowlist (default_action: deny with exact eq rules on the path / endpoint), the daemon preloads every allow rule into the kernel policy map at startup and marks the domain, so the hooks refuse (EPERM) anything not on the allowlist — no seeing-it-once first. Policies that are not eligible (globs, field-dependent rules, default_action: allow) simply keep the miss-allowed behaviour. See the design for the exact eligibility rules and safety ordering.
See Feature Status for exactly what ships on main.
Requirements: Linux with BTF (/sys/kernel/btf/vmlinux), Go 1.26+, and Rust with the wasm32-unknown-unknown target (rustup target add wasm32-unknown-unknown) for compiling YAML policies. Kernel blocking also needs CONFIG_BPF_LSM=y with bpf in /sys/kernel/security/lsm.
git clone https://github.com/yasindce1998/warmor.git && cd warmor
go build -o bin/ ./cmd/... # prebuilt BPF objects are checked in
# Compile a YAML policy to WASM (-o must come before the input file)
./bin/warmor-compile -o crypto.wasm examples/policies/block-crypto-miners.yaml
# Start in audit mode first: denies are logged, nothing is killed
sudo ./bin/warmor-daemon --policy crypto.wasm --audit --event-sink stdout
# Then enforce: LSM blocking for cached exec/connect denies, SIGKILL for the rest
sudo ./bin/warmor-daemon --policy crypto.wasm --lsm-enforceStart with
--audit. Without it, denied processes are SIGKILLed even when--lsm-enforceis off. The daemon's default--policy(policies/example/policy.wasm, built withmake build-policy) is a starter example, not a production policy.
The daemon prints a Security posture: line at startup that tells you whether LSM blocking is active, audit-only, or unavailable (tracepoint observe mode).
First time? See the Getting Started Guide for detailed setup instructions, dependency installation, and your first policy walkthrough.
┌─────────────────────────────────────────────────────────────────────┐
│ YAML Policy ──→ warmor-compile ──→ WASM Module ──→ Daemon │
│ │ │
│ ┌──────────┐ ┌──────────────┐ ┌──────────────────┐ │ │
│ │ Kernel │───→│ Ring Buffer │───→│ WASM Evaluator │─┘ │
│ │ LSM/eBPF │←───│ Policy Map │←───│ Decision Cache │ │
│ │ Hooks │ └──────────────┘ └──────────────────┘ │
│ └──────────┘ │
│ LSM: exec, file_open, connect, bind, listen, ptrace, sb_mount │
│ tracepoints: execve, openat, connect │
└─────────────────────────────────────────────────────────────────────┘
- Kernel hooks see operations (LSM-BPF and tracepoints on Linux; ETW, plus experimental eBPF-for-Windows, on Windows; ESF on macOS)
- BPF policy map answers operations that already have an entry, in the kernel. A miss is allowed and reported to userspace.
- WASM sandbox evaluates each new event in an isolated module, capped at 16 MiB of memory and a 50 ms evaluation deadline (
--eval-timeout); exceeding either fails closed (DENY). - Decisions feed back into the kernel map, for exec and connect only on
main. A userspace DENY kills the offending process.
| Platform | Technology | Status on main |
|---|---|---|
| Linux | eBPF tracepoints + LSM-BPF (7 hooks) | Most mature. Kernel blocking for cached exec/connect denies, and detect-then-SIGKILL for everything else. LSM attach is tested in a VM in CI. |
| Windows | ETW, plus eBPF-for-Windows | Experimental beta. The ETW path (detect, then TerminateProcess) is what is tested in CI. The eBPF-for-Windows programs have never been built or run in CI, so their kernel blocking is unverified. |
| macOS | Endpoint Security Framework | Experimental, detection only. Every AUTH event is answered ALLOW. The cgo ESF code is not compiled in CI, and release binaries are built without cgo. |
Measured on one otherwise idle laptop (WSL2, Core Ultra 5 135U) with the harness
in docs/PERFORMANCE.md, which has the methodology,
the caveats and the commands (make bench, make bench-kernel). The kernel
numbers come from a nested KVM VM, not bare metal. "Declared" means the policy
exports its fields-read declaration, which every compiled YAML policy does and
no Rust crate under policies/ does yet.
| Metric | Measured | Design target |
|---|---|---|
| Decision latency, cache hit | P95 1.2–1.3 µs | <100 µs P95 |
| Decision latency, cache miss (WASM) | P95 32 µs (YAML policy), 55 µs (Rust advanced crate) |
<100 µs P95 |
| Decision cache hit rate, synthetic host workload | 95.4% declared; 0% undeclared (PID in every key) | >90% |
| Throughput, daemon event loop | 175k events/s declared; 24k–41k undeclared | 100k events/s |
| Memory (RSS, in-process harness) | 60–104 MB declared; 105–128 MB undeclared | <50 MB (not met) |
| CPU at 10k events/s | 11% of one core declared; 83% undeclared (fixture); the Rust crate fell short of 10k (9.2k achieved) | <5% |
| Kernel fast path (BPF program, policy_map hit) | 0.11 µs connect, 0.34 µs exec | <1 µs |
Kernel, event sent to userspace (every miss, every open()) |
0.4–1.6 µs per event; delivered in batches, up to 5 ms later | none set |
- YAML DSL — Declarative rules with glob matching, variables and conditions, compiled by
warmor-compile(needscargoand thewasm32-unknown-unknowntarget). Unknown keys are silently ignored, so check rules with--rust-onlyif in doubt. - Rust WASM — Full Rust policies compiled to
wasm32-unknown-unknownfor complex logic - Hot-Reload — SIGHUP swaps the WASM module without a restart. Kernel map entries from the old policy are not cleared yet.
- Two-Tier Cache — A userspace decision cache, plus the kernel policy map for exec and connect decisions
- Policy Signing — Ed25519 signatures on WASM policies: signed OCI bundles (
warmor-policy-bundle --push/--pull), and detached<policy>.sigfiles thatwarmor-daemon --verify-keychecks at startup, on reload and on every policy server download, failing closed (see Policy Toolchain)
Warmor goes beyond static policy enforcement. On main, learning and simulation ship as CLI tools. The other features in this section are experimental Go libraries with unit tests that are not yet wired into the daemon or server: no flag or binary reaches them. See Feature Status.
Record events with the daemon's file sink, then have warmor-learn synthesize an allowlist policy from them.
sudo warmor-daemon --policy crypto.wasm --audit --event-sink file:/var/log/warmor/events.ndjson
warmor-learn --events /var/log/warmor/events.ndjson --follow --duration 30m -o learned-policy.yamlReplay recorded events against a candidate policy before deployment to see what would newly be denied. The simulator reads events-*.ndjson files from a directory. The daemon has no dedicated event-store sink yet, so name the file sink accordingly.
sudo warmor-daemon --policy current.wasm --audit --event-sink file:/var/lib/warmor/events/events-current.ndjson
warmor-simulate --policy candidate.yaml --data /var/lib/warmor/events --since 7d --format json -o report.jsonRefuse to execute any binary whose content isn't in an allowlist of known-good SHA-256 digests, wherever it lives. A sleepable LSM program hashes each exec with IMA. Requires CONFIG_IMA with SHA-256 as IMA's hash (the daemon checks at startup). Tested in VMs on IMA-enabled 6.1 and 6.18 kernels; start with audit.
warmor-integrity-scan --rootfs /path --all -o integrity-db.json
sudo warmor-daemon --policy policy.wasm --integrity-db integrity-db.json --integrity-mode audit # then enforce--escape-detect=alert|deny matches events from container cgroups against breakout techniques: nsenter, block-device and cgroup v1 mounts, cross-cgroup ptrace, container runtime socket connects, /proc/*/ns/* access, and escape tools. Matches are logged, counted and labelled; deny also terminates the process. See Escape Detection.
Maps events to MITRE ATT&CK techniques and builds per-container kill-chain graphs (internal/attackgraph).
Container relationship graph (network connections, shared volumes, shared namespaces) with BFS reachability queries (internal/blastradius).
Z-score outlier detection over behavioural fingerprints of containers running the same image (internal/drift).
warmor-server supports percentage-based rollouts with consistent hashing: create, ramp, complete and abort through the admin API (warmorctl's Rollouts tab is view-only). The deny-rate CanaryAnalyzer that would trigger automatic rollback exists but is not connected. Nothing feeds it decisions, so there is no automatic rollback on main.
A rule's temporal: block (max_container_age, min_container_age, UTC time_of_day with midnight wrap, days_of_week) is compiled into the WASM policy: the rule acts only when its conditions match and the event is outside the permitted window. The daemon sends the event's time and its container's age, dated from the oldest process in the container's cgroup (Linux, hostPID). Unknown age fails closed, so age rules also act on host processes. Event types with temporal rules are evaluated every time, never cached or synced to the kernel map. See Temporal Policies.
- name: no-curl-after-startup
event: network
conditions:
all:
- comm: { eq: "curl" }
action: deny
temporal:
max_container_age: "5m"| Tool | Purpose |
|---|---|
warmor-daemon |
Enforcement daemon: tracepoints, LSM-BPF hooks, WASM evaluation, event sinks, policy-server client |
warmor-server |
Central policy server: agent registration and heartbeats, policy versions, percentage rollouts, mTLS/JWT |
warmor-compile |
YAML to WASM compiler (needs cargo and the wasm32-unknown-unknown target) |
warmorctl |
Interactive TUI (dashboard, agents, policies, rollouts, certs). Interactive only; there are no subcommands. |
warmor-learn |
Synthesize a policy from recorded daemon events |
warmor-simulate |
Replay recorded events against candidate policies |
warmor-integrity-scan |
Build and verify SHA-256 binary hash databases (the daemon's --integrity-db allowlist) |
warmor-policy-gen |
Generate policies from audit logs |
warmor-sbom-policy |
Generate policies from SBOM manifests |
warmor-policy-diff |
Compare two policies and show what changes |
warmor-policy-merge |
Merge multiple policies with conflict resolution |
warmor-policy-bundle |
Package policies into signed OCI bundles; sign/verify detached <policy>.sig |
warmor-oci-hook |
Experimental. OCI runtime hook that reports container start/stop to warmor-server. It has no auth or TLS, so it only works against warmor-server --insecure, and the server does not use the bindings yet. |
There is no warmor-dashboard binary on main. The SSE dashboard in internal/dashboard is library-only.
10 example Rust policy crates covering threat scenarios.
All 10 crates build for wasm32-unknown-unknown and use the JSON ABI (evaluate_syscall). CI builds every crate and runs each through the real WASM host (TestPolicyCrates): benign events must be allowed and the crate's own attack examples denied. The host does not send args, parent_comm or cgroup_id, so rules on those fields never fire; time context is sent only to modules declaring ABI version 3 (temporal-access).
| Policy | Threat Domain | Pattern |
|---|---|---|
advanced |
Process/file enforcement | evaluate_syscall |
cross-platform |
Platform-aware security | Flat struct |
multi |
Multi-event dispatch | Tagged enum |
container-escape |
Container breakout (12 techniques) | Tagged enum |
supply-chain |
Runtime integrity (9 controls) | Tagged enum |
temporal-access |
Time-based access control (8 rules) | Flat struct |
zero-trust-net |
Network microsegmentation (10 controls) | Flat struct |
lateral-movement |
Lateral movement detection (10 techniques) | Tagged enum |
crypto-mining |
Cryptojacking detection (12 indicators) | Tagged enum |
example |
Starter template | evaluate_syscall |
Build any policy:
rustup target add wasm32-unknown-unknown
cd policies/container-escape
cargo build --release --target wasm32-unknown-unknown
# Output: target/wasm32-unknown-unknown/release/warmor_policy_container_escape.wasm
# Or build every crate at once:
./scripts/build-rust-policies.shSee Rust Policy Examples for full documentation and authoring guide.
The Helm chart does not work on
mainand is being fixed. Do not install it yet.
- The chart has no value that enables
--lsm-enforce, and--set config.*values are ignored.- The policy ConfigMap ships YAML, which the daemon can only compile with
cargo, and the distroless image does not include it.- The
ValidatingWebhookConfigurationhas no handler behind it.- The
WarmorPolicyCRD has no controller.See Feature Status.
The chart (deploy/helm/warmor) contains a DaemonSet with BPF capabilities, RBAC, a ServiceMonitor, Grafana dashboards and alert rules.
internal/ (status on main)
enforcer/ — Core event loop, WASM evaluation, SIGKILL actions Shipped
(NetFilter, sandbox, learning mode inside) Library-only
ebpf/ — Tracepoint + LSM-BPF loaders, policy map, ring buf Shipped (see STATUS for map gaps)
wasm/ — Wazero runtime, JSON and binary ABIs Shipped
cache/ — Userspace decision cache Shipped
streaming/ — Pipeline: enrichers → stdout/file/webhook sinks Shipped (syslog sink library-only)
policyserver/ — Policy server, percentage rollouts Shipped (canary analyzer library-only)
lineage/ — Process tree tracking Partial (no PPID in events)
learner/ — Learning recorder + policy synthesizer Shipped (warmor-learn)
simulator/ — Event replay engine Shipped (warmor-simulate)
integrity/ — SHA-256 binary hash scan/verify Shipped (kernel side experimental)
compiler/ — YAML → Rust → WASM compilation pipeline Shipped
platform/ — Linux eBPF, Windows ETW / eBPF-for-Windows, macOS Linux shipped; Windows/macOS experimental
crypto/ — mTLS (Ed25519), JWT (HMAC+EdDSA), bundle signing Shipped
metrics/ — Prometheus collector and exporter Shipped
logging/ — Structured JSON (zerolog) Shipped
policygen/ sbompolicy/ policydiff/ policymerge/ policybundle/ Shipped (CLI tools)
container/ — Cgroup→container resolver, container start times Shipped (escape, temporal); containerd monitor library-only
escape/ — Container escape pattern correlator Shipped (--escape-detect)
drift/ — Behavioral fingerprint + z-score detection Library-only
attackgraph/ — MITRE ATT&CK correlation + kill-chain DAG Library-only
blastradius/ — Container relationship graph + BFS reachability Library-only
temporal/ — Temporal constraint types + reference semantics Shipped (compiled into YAML policies)
patterns/ — Glob/regex matcher helpers Library-only
dashboard/ — SSE dashboard (ring buffer, embedded HTML) Library-only
See Architecture Deep Dive for data flow diagrams.
| Category | Documents |
|---|---|
| Getting Started | Getting Started • Build Guide |
| Architecture | System Design • Security Posture • BPF Compatibility |
| Platforms | Linux • Windows • macOS |
| Policies | Authoring Guide • Rust Examples • YAML DSL |
| Toolchain | Policy Toolchain — generate (SBOM/audit), merge, diff, simulate, sign & distribute |
| Intelligence | Learning Mode • Escape Detection • Supply Chain |
| Fleet | Canary Rollout • Experimental Features • Temporal |
| Kubernetes | CRD Usage • Helm Chart |
| Project | Feature Status • PRD, Phase Tracking & Roadmap |
We welcome contributions! Open an issue or pull request.
High-impact areas:
- Wiring the library-only features into the daemon (see Feature Status)
- Windows eBPF kernel enforcement (build and verify the eBPF-for-Windows programs)
- macOS ESF blocking (AUTH responses currently always ALLOW)
- New Rust WASM policy crates (threat detection scenarios)
- Performance benchmarks and optimization
- Integration tests on additional kernel versions
MIT License. See LICENSE.
- cilium/ebpf — eBPF library for Go
- tetratelabs/wazero — Pure Go WebAssembly runtime (zero dependencies)
- Rust — Policy implementation language
- MITRE ATT&CK — Threat framework for attack graph correlation
Warmor — pre-stable. See Feature Status for what ships today.
Issues • Discussions
