Skip to content
lance0Public

About

BGP FlowSpec policy daemon for automated DDoS mitigation

Topics

Resources

Contributing

Security policy

Stars

83 stars

Watchers

1 watching

Forks

Latest commit

 

History

681 Commits

Folders and files

Repository files navigation

prefixd prefixd

prefixd

Build License

Turn DDoS detector signals into guarded BGP FlowSpec mitigations.

Detectors post attack events over HTTP. prefixd maps the target to your inventory, picks a playbook, checks guardrails, and asks a separate BGP speaker to announce a FlowSpec rule with a mandatory TTL. No detector speaks BGP.

Documentation · Quickstart · Cookbook · Releases

Preferred speaker: rustbgpd, "an API-first BGP daemon in Rust for route servers, route reflectors, and network automation." prefixd drives it over gRPC, and the rustbgpd Compose profile is the default path. GoBGP stays maintained and selectable. Why rustbgpd

Status: pre-1.0, and the API can change before 1.0. The latest release, v0.20.0, is the first with the rustbgpd backend and Compose profiles. Upgrading from v0.19.1 needs one maintenance step; follow the upgrade guide.

Choose your path

NOC with a detector

FastNetMon, Prometheus Alertmanager or any JSON webhook sends attack events. prefixd groups signals per target, weighs corroborating sources and applies the matching playbook. Controller integration tests cover these adapters; the dated profile qualification covers manual mitigation, not detector traffic.

FastNetMon · Generic webhook · Corroborating signals

Operator during an attack

Mitigate a target by hand from the dashboard or API inside the same guardrails, then withdraw it with a reason that the audit log keeps. Rehearse policy in dry-run before you let it announce.

Manual mitigation · Dry-run rollout · Troubleshooting

Automation and integration

Drive events, mitigations, the safelist and configuration through the HTTP API or prefixdctl. The controller serves its OpenAPI document at /openapi.json.

HTTP API · CLI · Authentication

prefixd stops at the speaker. Router receipt and enforcement depend on your router software, address family and import policy. Existing router receipts are scoped to GoBGP; rustbgpd has local and peer tests but no vendor router receipt yet. See vendor compatibility.

Try it locally

With Git and Docker Compose installed, start the controller, dashboard, PostgreSQL and rustbgpd from published images. Nothing compiles locally.

git clone https://github.com/lance0/prefixd.git
cd prefixd
docker compose --profile rustbgpd up -d --pull always --no-build

Create the first administrator at the hidden password prompt, then check liveness:

docker compose exec prefixd-rustbgpd prefixdctl operators create \
  --username admin --role admin
curl http://localhost/v1/health

Open http://localhost and sign in. nginx serves the dashboard and API on port 80, and rustbgpd publishes BGP on 179. Prometheus (9091) and Grafana (3001) bind host loopback only. The sample peer uses a documentation address, so it stays Idle until you configure your edge router in configs/rustbgpd.toml.

Dashboard overview

Dashboard overview

Mitigations list

Mitigations list

Synthetic data; router installation is unobserved.

For GoBGP, start a fresh deployment with docker compose --profile gobgp up -d --pull always --no-build and run controller commands in prefixd-gobgp. Run one profile at a time: switching doesn't move active rules or the database's speaker binding. The quickstart covers configuration storage, building from source and troubleshooting.

Guardrails run before the speaker sees a rule

Detector events and manual requests pass the same checks before prefixd saves intent or calls the speaker. A rejection returns 422 with its reason and sends nothing to BGP. With the shipped configs/prefixd.yaml, sign in and request a mitigation for a safelisted address:

curl -sS -c cookies.txt -X POST http://localhost/v1/auth/login \
  -H 'Content-Type: application/json' \
  -d '{"username":"admin","password":"your_password_here"}'
curl -sS -b cookies.txt -X POST http://localhost/v1/mitigations \
  -H 'Content-Type: application/json' \
  -d '{"reason":"guardrail check","victim_ip":"10.0.0.5","protocol":"udp",
       "dst_ports":[53],"action":"discard","ttl_seconds":120}'
{"error":"guardrail violation: IP 10.0.0.5/32 is safelisted"}

The other limits in the shipped configuration reject requests the same way:

Guardrail Shipped setting Example rejection
Safelist 10.0.0.0/8, 192.168.0.0/16, plus API entries IP 10.0.0.5/32 is safelisted
TTL Required, 30 to 1800 seconds TTL 7200s out of bounds (min=30, max=1800)
Ports At most 8 destination ports too many ports: 9 (max=8)
Active quotas 5 per customer, 200 per POP, 500 global quota exceeded: customer (5/5)
New mitigation rate 30 per POP in any 60 seconds; TTL extensions don't count new mitigation rate exceeded: 30 created at this POP in the last 60s (max_new_per_minute=30)
Destination IPv4 /32 and IPv6 /128 host routes only Intent is always built as a host route

Two more rules protect routing state:

  • Dry-run: mode: dry-run runs the full pipeline and records mitigations, metrics and audit entries, but never calls the speaker. Those mitigations stay simulations after a switch to enforced. The shipped config sets mode: enforced; a config that omits mode runs in dry-run.
  • Immutable withdrawal intent: an operator withdrawal, detector unban or TTL expiry saves its rule, actor and reason before the speaker call. A failed or uncertain delete stays pending and retries against the same speaker; it can't turn back into an announcement.

A 201 means prefixd saved intent and the speaker accepted the local request. It doesn't prove that a router installed the rule. The running controller enforces TTLs: if prefixd stops while the speaker stays up, rules stay announced.

Guardrail settings · Withdrawal recovery · Dry-run rollout

Install

CI publishes the controller and dashboard images to ghcr.io/lance0/prefixd and ghcr.io/lance0/prefixd-dashboard. Pin a controller and dashboard built from the same revision.

Pairs with rustbgpd

prefixd decides what to mitigate; rustbgpd owns the BGP sessions. The two meet at a narrow, tested boundary:

  • Typed gRPC calls: prefixd adds, deletes and lists FlowSpec rules through rustbgpd's injection and RIB services, on an owner-only Unix socket locally or verified mTLS remotely. Every mutation returns an explicit outcome.
  • Reconciliation restores lost rules: rustbgpd holds injected rules in its process. After a speaker restart, prefixd's database still holds the desired state, and the next reconciliation poll (every 30 seconds in the shipped config) re-adds missing rules.
  • Qualified versions: v0.76.0 is the minimum controller contract and v0.78.0 is the Compose pin. CI runs the lifecycle, TTL and restart suite against the v0.76.0, v0.77.0 and v0.78.0 immutable images.

This qualifies the controller-to-speaker boundary on Linux amd64. It says nothing about router enforcement.

rustbgpd README · rustbgpd docs · Speaker backends · ADR 023

Documentation

Start with the documentation index, or go to your task:

Task Guide
Run it for the first time Quickstart · Cookbook
Connect a detector FastNetMon · Generic webhook · Correlation
Configure, deploy or upgrade Configuration · Deployment · Upgrading
Automate HTTP API · CLI · API versioning
Choose or operate a speaker Speaker backends · Vendors · Troubleshooting
Check the evidence Profile qualification · Benchmarks (historical)
Understand or contribute Architecture · Decisions · Contributing · Roadmap
Report a problem Issues · Security · Changelog

License

MIT. See LICENSE.

About

BGP FlowSpec policy daemon for automated DDoS mitigation

Topics

Resources

Contributing

Security policy

Stars

83 stars

Watchers

1 watching

Forks

Releases

Sponsor this project

Packages

Contributors

Languages