OpenScrub
OpenScrub is the DDoS mitigation platform in the opensecstack (SIN) ecosystem. It delivers line-rate L3/L4 packet filtering through an XDP/eBPF kernel data plane and wraps every block decision in CITADEL-attested, WORM-anchored evidence — so NIS2 auditors can reconstruct what was blocked, why, and when without trusting a vendor log.
OpenScrub ships at v1.0.0 (2026-05-09) under the Apache 2.0 licence.
Overview
Most organisations either absorb DDoS traffic in userspace (slow) or pay for an opaque commercial scrubbing service (expensive and unauditable). OpenScrub closes both gaps:
- Sub-microsecond drop latency. The eBPF/C program attaches at NIC ingress via XDP, before the kernel allocates an
skb. A blocklist hit costs roughly 50 ns on commodity hardware — no userspace copy on the drop path. - Audit-grade evidence. Every distinct mitigation event emits a signed
openscrub.mitigationevent to the CITADEL WORM ledger, satisfying NIS2 Article 21(2)(c) incident-handling documentation requirements. - Automatic IOC enforcement. A built-in ThreatFlow puller pulls malicious-IP IOCs on a 15-minute cadence and reconciles them directly into the BPF blocklist map — no copy-paste, no human-in-the-loop.
Key features
| Module | Description |
|---|---|
| XDP loader (Rust + Aya) | Loads the compiled eBPF object at NIC ingress, pins BPF maps under /sys/fs/bpf/openscrub/, manages program lifecycle with CAP_BPF + CAP_NET_ADMIN + CAP_SYS_RESOURCE |
| eBPF/C data plane | Per-packet XDP_DROP / XDP_PASS decisions against an LPM-trie blocklist map and a per-CIDR rate-limit map; SYN-cookie, UDP-flood, and ICMP-flood handlers in separate programs dispatched from openscrub_kern.c |
| BGP blackhole (GoBGP) | Embedded GoBGP speaker for Remotely Triggered Black Hole (RTBH); announces /32 or /128 host routes with RFC 7999 community 65535:666 to upstream transit providers; auto-withdraws after configurable cool-down |
| Go control-plane API | REST API on :8087 — rules CRUD, live mitigations, JSON metrics snapshot, Prometheus exposition; communicates with the loader over a Unix socket to maintain a privilege boundary |
| ThreatFlow IOC puller | Goroutine polling the ThreatFlow malicious-IP feed every 15 minutes; diffs against existing source='threatflow' rules and inserts or withdraws automatically |
| CITADEL evidence emitter | Async fire-and-retry emission of openscrub.mitigation events, HMAC-SHA256 signed, to the CITADEL WORM append-only ledger |
| Prometheus metrics | pps_dropped, pps_passed, rules_active, ioc_pull_latency_ms — aggregated from per-CPU BPF stats maps every second |
| React + Vite dashboard | Operator UI on :3087; rules CRUD, live mitigation table, metrics charts; bilingual (Albanian + English) |
Architecture
OpenScrub separates privilege using a Unix socket. The API process (Go, replicated) holds no kernel capabilities; it writes rules to PostgreSQL and sends RULE_INSERT / RULE_DELETE messages on /run/openscrub/dataplane.sock. The loader process (Rust + Aya, one per node) holds CAP_BPF + CAP_NET_ADMIN, owns the BPF maps, and never accepts network connections.
The block-decision data flow:
- An operator (or the ThreatFlow puller) creates a rule via
POST /api/v1/rules. - The API validates, persists the row in PostgreSQL, and messages the loader.
- The loader writes the CIDR into the LPM-trie
blocklistBPF map (or theratelimitmap for rate-limit rules). - On every incoming packet the XDP program does an O(log W) LPM lookup — hit →
XDP_DROP; miss →XDP_PASS. - Per-CPU
statscounters are aggregated every second; a mitigation row is written and a CITADEL event is emitted asynchronously.
For volumetric attacks that exceed NIC capacity, the BGP module announces a blackhole route via GoBGP to upstream transit peers using the RTBH technique, shifting the drop point to the provider edge. The cool-down window (default 300 s) is configurable under mitigation.bgp.withdraw_delay_seconds.
privileged: false with an explicit capability set and drop: ALL as the base seccomp policy. A third-party kernel-attack-surface audit is pending scheduling.Ports & endpoints
| Port | Service |
|---|---|
8087 | OpenScrub REST API (Go) |
9091 | Prometheus metrics endpoint |
3087 | React operator dashboard (nginx) |
| Method | Path | Purpose |
|---|---|---|
GET | /api/v1/health | Liveness — status, version, DB ping, dataplane attached |
POST | /api/v1/auth/login | JWT issuance |
GET | /api/v1/rules | List rules (paginated; filter by type) |
POST | /api/v1/rules | Create rule — type ∈ blocklist / ratelimit / syncookie |
GET | /api/v1/rules/{id} | Rule detail |
DELETE | /api/v1/rules/{id} | Withdraw rule and clear BPF map entry |
GET | /api/v1/mitigations | Mitigation rows (filter by since, rule_id) |
GET | /api/v1/metrics/snapshot | JSON metrics snapshot for the dashboard |
GET | /api/v1/metrics | Prometheus text exposition (JWT-gated) |
Integration
OpenScrub authenticates dashboard users via sinauth SSO using the OAuth 2.0 authorization_code + PKCE (S256) flow. The API validates RS256-signed tokens against the sinauth JWKS endpoint at https://auth.sin.to/.well-known/jwks.json.
Minimum required environment variables:
OPENSCRUB_DB_URL=postgres://openscrub:openscrub@postgres:5432/openscrub OPENSCRUB_DATAPLANE_SOCKET=/run/openscrub/dataplane.sock OPENSCRUB_JWT_SECRET=<32+ random bytes> OPENSCRUB_CITADEL_API_URL=https://citadel.internal OPENSCRUB_CITADEL_HMAC_SECRET=<hmac secret> OPENSCRUB_THREATFLOW_API_URL=https://threatflow.internal OPENSCRUB_THREATFLOW_TOKEN=<bearer token> OPENSCRUB_IFACE=eth0
Quick start with Docker Compose:
git clone https://github.com/opensecstack/opensecstack cd opensecstack/openscrub cp .env.example .env docker compose -f deploy/docker-compose.yml up -d "cm"># Health check curl http://localhost:8087/api/v1/health "cm"># List active rules (requires Bearer token) curl -H "Authorization: Bearer $TOKEN" \ http://localhost:8087/api/v1/rules
SecureLab polls GET /api/v1/alerts?technique={id}&since={ts} (read-only, HMAC-signed) to validate that OpenScrub detections fire as expected during attack simulations. ThreatFlow pushes malicious-IP IOCs inbound; CITADEL receives outbound mitigation evidence.
Full documentation
The complete OpenScrub reference — deployment guide, XDP program guide, BGP blackhole setup, API reference, CITADEL integration, and threat model — is in the repository:
github.com/opensecstack/opensecstack/tree/main/openscrub/docs