prefixd is a BGP FlowSpec policy daemon for automated DDoS mitigation. It receives attack signals from detectors, applies policy-driven playbooks, and announces FlowSpec rules to routers via GoBGP.
You have detectors (FastNetMon, Kentik, Prometheus alerts) that know when you're under attack. You have routers (Juniper, Arista, Cisco) that can filter traffic at line rate with FlowSpec. But there's a gap:
| Problem | Without prefixd | With prefixd |
|---|---|---|
| Detector → Router | Manual CLI or fragile scripts | Automated policy engine |
| Response time | Minutes (operator intervention) | Seconds (API-driven) |
| Safety | Easy to fat-finger, no guardrails | Quotas, safelist, /32-only, TTLs |
| Visibility | Scattered logs, no audit trail | Dashboard, metrics, audit log |
| Expiry | Forget to remove rules | Auto-expire via TTL |
Key idea: Detectors signal intent, prefixd decides policy. No detector ever speaks BGP directly.
Detector ──► prefixd ──► GoBGP ──► Routers
│
├── Policy Engine (playbooks)
├── Guardrails (quotas, safelist)
└── Reconciliation (auto-expire)
git clone https://github.com/lance0/prefixd.git
cd prefixd
# Copy example environment file
cp .env.example .env
# Edit .env to set your passwords (optional for local testing)
# POSTGRES_PASSWORD=your-secure-passworddocker compose up -dThis starts 4 services:
| Service | Purpose | Ports |
|---|---|---|
| prefixd | Policy daemon | 8080 (API), 9090 (metrics) |
| dashboard | Web UI | 3000 |
| gobgp | BGP route server | 179 (BGP), 50051 (gRPC) |
| postgres | State storage | 5432 |
# Check all services are healthy
docker compose ps
# Test the API
curl http://localhost:8080/v1/health
# {"status":"healthy","version":"0.8.0",...}docker compose exec prefixd prefixdctl operators create \
--username admin --role admin
# Enter password when promptedopen http://localhost:3000Login with the admin credentials you just created.
# View prefixd logs
docker compose logs prefixd
# View GoBGP logs
docker compose logs gobgp
# Restart everything
docker compose restartCommon issues:
- Port 5432 in use → Stop local PostgreSQL or change port in docker-compose.yml
- Port 179 in use → Another BGP daemon is running
- "connection refused" → Wait a few seconds for services to start
Once you have prefixd running locally:
Edit configs/inventory.yaml to map your IP space to customers:
customers:
- customer_id: acme
name: "ACME Corp"
prefixes:
- "203.0.113.0/24"
services:
- service_id: dns
assets:
- ip: "203.0.113.10"
allowed_ports:
udp: [53]Edit configs/playbooks.yaml to set response policies:
playbooks:
- name: udp_flood
match:
vector: udp_flood
steps:
- action: police
rate_bps: 10000000 # 10 Mbps
ttl_seconds: 120
- action: discard # Escalate if attack persists
ttl_seconds: 300Point your detector at prefixd's API:
curl -X POST http://localhost:8080/v1/events \
-H "Content-Type: application/json" \
-d '{
"source": "fastnetmon",
"victim_ip": "203.0.113.10",
"vector": "udp_flood",
"bps": 5000000000
}'See FastNetMon Integration for a complete setup guide.
Configure GoBGP neighbors in configs/gobgp.conf and set up FlowSpec import policies on your routers. See Router Setup.
| Category | What it does |
|---|---|
| Signal Ingestion | HTTP API accepts attack events from any detector |
| Policy Engine | YAML playbooks define per-vector responses |
| Guardrails | Quotas, safelist, /32-only enforcement, mandatory TTLs |
| BGP FlowSpec | Announces via GoBGP (traffic-rate, discard actions) |
| Reconciliation | Auto-expires mitigations, repairs RIB drift |
| Dashboard | Real-time web UI with WebSocket updates |
| Authentication | Three roles: admin, operator, viewer |
| Observability | Prometheus metrics, structured logs, audit trail |
- Detector sends event →
POST /v1/eventswith victim IP, vector, confidence - Inventory lookup → Find customer/service owning the IP
- Playbook match → Determine action (police/discard) based on vector
- Guardrails check → Validate quotas, safelist, prefix length
- FlowSpec announce → Send rule to GoBGP via gRPC
- Router enforcement → Traffic filtered at line rate
- Auto-expiry → Rule withdrawn when TTL expires
Fail-open design: If prefixd dies, mitigations auto-expire. No permanent rules, no stuck state.
| Topic | Link |
|---|---|
| Full deployment guide | docs/deployment.md |
| Configuration reference | docs/configuration.md |
| API reference | docs/api.md |
| CLI reference | docs/cli.md |
| Router setup | docs/deployment.md#router-configuration |
| Grafana dashboards | docs/grafana.md |
| Architecture | docs/architecture.md |
FlowSpec (RFC 5575, RFC 8955) is supported by:
| Vendor | Platform | Notes |
|---|---|---|
| Juniper | MX, PTX, SRX | Full FlowSpec support |
| Arista | 7xxx | EOS 4.20+ |
| Cisco | IOS-XR | ASR 9000, NCS |
| Nokia | SR OS | 19.x+ |
- Docker (recommended) or Rust 1.85+ for building from source
- PostgreSQL 14+ for state storage
- GoBGP is included in Docker Compose
Current version: v0.8.0
- Core functionality stable and tested
- API may change before v1.0
- See ROADMAP.md for planned features
- Issues: GitHub Issues
- Contributing: CONTRIBUTING.md
- Security: SECURITY.md
MIT - See LICENSE
