Kapkandocs
GitHub

Safety model

Blackholing a route is a blunt instrument: a wrong or runaway announcement can take your own infrastructure offline faster than any attack. Kapkan is built so that cannot happen by accident. The six rules below are enforced in code and covered by tests — they are not configuration options you can forget to set, and they are non-negotiable.

Every one of them applies to automatic detection-driven bans and to manual bans requested through the API alike. There is no path that skips them.

1. Dry-run by default

Kapkan announces a blackhole route only when the config explicitly sets dry_run: false. An absent dry_run key is treated as true. In dry-run, every would-be announcement is logged and exposed through the API and metrics — but never sent to your routers. You can run Kapkan against live production telemetry, watch detection fire, and inspect every route it would announce before it can touch a single prefix.

!You opt in to mitigation, never out

Because a missing dry_run key means dry-run, the only way to start announcing routes is to deliberately write dry_run: false and reload. A typo, a truncated file, or a forgotten key all fail safe. See Going live for the validation checklist before you flip it.

2. No permanent bans

Every announcement carries a TTL (ban.ttl_seconds) and is automatically withdrawn when it expires — even if the attack is still ongoing. There is no code path that produces a route without an expiry. A stuck process, a lost notification, or an operator who walks away can never leave a host blackholed forever; the worst case is that traffic is dropped for one TTL window and then re-evaluated.

3. Unban hysteresis

A ban is withdrawn only after the target's traffic has stayed below the threshold for ban.unban_hysteresis_seconds, not the instant a single window dips. This prevents the announce/withdraw flapping that would otherwise happen at the edge of an attack, where traffic oscillates just above and below the limit and would churn BGP on every window.

4. Hard ban cap

ban.max_active_bans is an absolute ceiling on the number of simultaneous bans. Once it is reached, new bans are refused and the rejection is alerted loudly rather than announced. This is the circuit breaker against a misconfiguration or a detection storm: Kapkan will never blackhole half your network because a threshold was set too low or an attacker spread across thousands of destinations. A manual ban that would exceed the cap is also refused (see below).

5. Whitelist is absolute

Addresses listed in protected_whitelist — your routers, name servers, and other critical infrastructure — are never announced, regardless of how much traffic they attract and regardless of who asks. The whitelist overrides detection and it overrides manual ban requests. There is no flag that bypasses it.

6. Scoped detection

Kapkan acts only on destinations inside the configured networks prefixes. Traffic to addresses outside your protected ranges is still counted in metrics, but it can never trigger a ban. You cannot accidentally blackhole a prefix you do not own, even if a flood happens to traverse your network on the way somewhere else.

How manual bans inherit every rule

The POST /api/v1/ban endpoint is not an escape hatch. A manual ban request is checked against the same guards as automatic detection, and a request that violates one is rejected with HTTP 409 rather than silently announced:

Manual ban requestResult
Target is in protected_whitelist409 — never announced
Target is outside the configured networks scope409 — out of scope
Banning the target would exceed max_active_bans409 — cap reached

A manual ban that passes all three still respects the TTL and hysteresis rules: it auto-expires like any other announcement. The only rule manual bans bypass is the detection threshold itself — that is the entire point of a manual ban — but never the safety guards around the announcement.