Repository navigation
prefixd 0.24.0 adds manual scrubber diversion through rustbgpd's guarded FlowSpec contract. It is disabled by default and needs an administrator, a configured profile and fresh readiness. The release also reports per-peer FlowSpec export on rustbgpd, searches and pages the complete mitigation and event history on the server, and, with opt-in leasing, detects a rustbgpd restart before the next lease renewal. It requires rustbgpd v0.81.0, which must be upgraded first, and migration 019 adds the scrubber diversion table on startup.
Upgrade notes
- Follow v0.23.1 → 0.24.0 in the upgrade guide: upgrade rustbgpd to v0.81.0, then replace the controller and dashboard, then copy
grafana/prometheus-alerts.yml. prefixd refuses to start against v0.80.x with "upgrade the speaker first". See rustbgpd v0.81. - Migration 019 applies on startup. It adds the
scrubber_diversionstable, creates no rows and changes no existing mitigation; no adoption step runs. - Scrubber diversion stays off unless
scrubber.enabledis set; the only new configuration keys are underscrubber, and a 0.23.1prefixd.yamlloads as it is. Enable diversion in a separate restart, and drain every diversion before disabling it or downgrading; see scrubber diversion and downgrade. - On rustbgpd, health is now
degradedwhen an active FlowSpec rule reaches no Established peer or its export read fails, and a local FlowSpec rule that prefixd cannot represent fails the whole speaker read; remove such foreign rules before upgrading. Existing password hashes keep working, and the new history search parameters leave existing API clients unaffected. The guide lists the other API changes.
Known limitations
- TTL expiry needs the running controller. Unleased FlowSpec rules and blackholes stay announced if prefixd stops while the speaker stays up; the leased modes, which bound that to one lease, are opt-in and rustbgpd-only.
- prefixd qualifies control-plane receipts only: local speaker intent, per-peer export and, in fixtures, a peer's received routes. Router forwarding and scrubber behavior are not qualified. A scrubber profile's
qualification_receiptis an operator reference that prefixd does not verify. - A diversion withdrawal that meets another owner's rule at its match stays outstanding. prefixd never deletes a foreign rule, and there is no break-glass override yet.
- The dashboard's Scrubbers page is shown only to administrators, although
GET /v1/scrubbersandGET /v1/diversionsallow any authenticated user. - prefixd now reads rustbgpd's per-peer FlowSpec export, but still uses neither its batch reads nor replay-complete signaling. Do not list prefixd in
global.gr_eor_replay_owners. - The 0.23.0 known limitations otherwise still apply.
Security
- Update the build-time Markdown parser
pulldown-cmarkto 0.13.6 in both lockfiles for RUSTSEC-2026-0414 and RUSTSEC-2026-0433. It is used by protobuf code generation; no advisory exception or runtime behavior change is introduced.
Added
-
Add disabled-by-default manual scrubber diversion through rustbgpd's guarded FlowSpec contract: immutable persisted profile/action snapshots, independent owners, authenticated bounded readiness, withdrawal latching and explicit re-arm. API/OpenAPI, CLI and dashboard controls preserve full host matches and require target confirmation. Dry-run sends no speaker mutation; pending withdrawal retains victim exclusion through the authorization barrier and final revision-checked local absence. Router forwarding requires a separate qualified profile receipt. Requires rustbgpd v0.81.0; migration 019 adds the
scrubber_diversionstable. See the upgrade guide before disabling profiles or downgrading with outstanding diversions. -
Every
/v1/scrubbersand/v1/diversionsendpoint requires authentication; arm, preview, create and withdraw also require Admin. Unknown profiles and diversions return404, disabled scrubbers409, and an unconfirmed outcome500. Withdrawal takes an optionalreason, also throughprefixdctl diversions withdraw --reason, and the dashboard confirms withdrawal with a warning that attack traffic returns to ordinary routing. Create and withdraw finish and record their outcome even if the client disconnects. -
Each scrubber lifecycle pass reads the guarded inventory once, renews each profile owner once with a window that outlives the next renewal, and bounds rotating export reads. A slow export read marks evidence historical rather than latching; steady passes write no diversion or audit rows. Draining intent admitted by another controller process waits rustbgpd's 120-second window cap. Saved withdrawals cannot return to
activeor change their first reason or requester. A profile target inside its own destinations is refused. -
Add a separate guarded FlowSpec speaker boundary for direct target-IP diversion, with exact IPv4/IPv6 action read-back, independent owner windows and revision-fenced deletion. All guarded mutations require the controller fence and use no legacy fallback. Another owner's guarded rule is an occupied key in the guarded inventory, never prefixd intent, and is never deleted. Definitive guarded refusals, such as a window that is not live or a revision mismatch, are named in logs and errors; other failures remain unconfirmed until read back.
-
CI runs the authenticated browser withdrawal smoke against disposable PostgreSQL and pinned rustbgpd, reusing the candidate controller binaries and production dashboard build. It checks pending confirmation, persisted/rendered audit and fresh local speaker intent before and after withdrawal, retains protected failure evidence, and removes its owned stack. No router enforcement qualification is implied.
-
Per-peer FlowSpec export evidence on rustbgpd, keyed by the full canonical match and shown on mitigation detail, authenticated health and the dashboard. Separate bounded FlowSpec metrics preserve the existing blackhole metrics. Both kinds share one fair, rotating 10-second observation budget. Failed and historical reads remain distinct from empty or unsupported observation; committed means the peer's Adj-RIB-Out, not router enforcement. No configuration or schema migration is required, and the existing blackhole alert stays scoped to blackholes.
-
An optional
docker-compose.inventory.ymloverlay readsconfigs/inventory.yamldirectly from the host for either speaker profile. Existing volume-managed inventories remain the default. The quickstart covers switching workflows, reloading edits and recreating stale file mounts.
Changed
-
Require and pin rustbgpd v0.81.0 for Compose, labs and fixtures, and vendor its unmodified release proto. CI qualifies v0.81.0 and checks that startup refuses the released v0.80.1 image.
-
On rustbgpd v0.81.0, replayed blackholes and FlowSpec rules are selected immediately after a speaker restart instead of waiting for Graceful Restart selection deferral. The 0.23.1 advice to set
graceful_restart = falseon neighbors that never come up only mattered on v0.80.x.selection_deferredremains a decoded export reason; thePrefixdBlackholeNotExportedhint for it and its operator message no longer describe the old hold. -
prefixd refuses to read a local FlowSpec rule it cannot represent on its rustbgpd speaker, such as one with IPv6 extended communities or an unguarded redirect-to-IP action that another tool injected. The whole read fails rather than dropping the row: reconciliation fails, startup is refused when
bgp.rustbgpd.leased_flowspecis set, andGET /v1/speaker/routesreturns502. Remove the foreign rule to recover. A guarded rule of another owner is excluded from these reads instead; a malformed guarded rule in prefixd's own scrubber owner namespace still fails them. -
Update the password hashing dependency
argon2to 0.6 and drop the unused directpassword-hash0.5 dependency. New operator hashes keep the same Argon2id parameters and PHC format; existing stored password hashes are unchanged and still authenticate. -
rustbgpd export evidence includes configured addressed peers without an Established session even when the separate peer-session read fails. These peers stay outside Established-peer disposition counts; an all-down roster still warns
no_established_peer. Theirstateis now nullable when no matching FSM observation is available; the dashboard shows that state as unobserved. Unbound interface-only neighbors still use the optional session snapshot. No speaker minimum or metric label changes. -
The event dashboard now uses 20-row server cursor navigation, literal search across IP and event UUID, and exact source/vector filters. Ordering is fixed by ingestion time. CSV export traverses every matching page and refuses incomplete, duplicate or nonadvancing results. Exports are live and retained in browser memory; large-history memory qualification remains open.
GET /v1/eventsaccepts boundedq,sourceandvectorparameters while preserving unfiltered behavior and the existing tuple cursor format. -
Dashboard CSV exports now quote carriage returns inside fields, preserving detector and audit text as one field instead of creating extra CSV records.
-
The dashboard mitigation list now uses complete server cursor navigation and server-side search, customer, POP and status filters instead of filtering only the newest 100 rows. Pages stay in newest-first order; column sorting of a bounded subset is removed. Selection is page-scoped, and CSV export walks all matching pages, refusing a partial download on failure. Exports are live traversals and retain matching rows in browser memory. Only one page is rendered at a time; standard SWR caching retains visited pages for the app lifetime, and large-history memory qualification remains open. Search drafts that exceed the UTF-8 byte limit or contain null characters retain the last valid results without submitting the invalid query. Empty filter choices are omitted and identifiers named
Allremain selectable. -
GET /v1/mitigationsaccepts bounded literal substring search throughq, covering target, vector, customer ID and mitigation UUID before pagination. -
rustbgpd ownership refusals now distinguish
owner_mismatchfromkey_unownedin admission409bodies, retirement audit reasons and operational logs. Missing, malformed, conflicting or unrecognized rich-status details useunspecified; prefixd still changes no speaker route and never falls back to an unleased add. Refused lease renewals still trigger a full leased re-add and retain the existingnot_livehealth result, with a typedgeneration_not_livediagnostic when the speaker supplies it.
Fixed
-
With opt-in rustbgpd leasing, an independent two-second liveness probe now detects a changed daemon incarnation and wakes full leased FlowSpec and blackhole recovery before the next renewal or inventory poll. Failed or unsupported probes do not change lease health, and late probe or renewal replies cannot invalidate or restore a superseded generation. Re-adds retain the existing fence and guardrails; probe cadence is not a recovery SLA or proof of router enforcement.
-
A controller running in dry-run continues to observe export for active FlowSpec rules retained from an earlier run, while excluding rows recorded as dry-run. Observation sends no announcement, withdrawal or lease renewal, and never infers an unknown legacy admission mode.
-
Export metric scrapes now exclude peer dispositions and unexported warnings from before the controller lost ownership, including during standby and after regain before the next observation pass. Read-error metrics still describe attempted reads; ownership loss alone does not count as a failed RPC.
Documentation
- FastNetMon guidance distinguishes its source-build native rustbgpd RTBH action from the prefixd notify path, and explains the exclusive speaker-target requirement and leased/unleased route conflicts.
- The IPv6 customer prefix in the configuration reference's inventory example is now
2001:db8:ac00::/48. The previous2001:db8:acme::/48is not a valid prefix, and inventory load rejects it.
Artifacts
Container images, tagged 0.24.0 and 0.24:
ghcr.io/lance0/prefixd@sha256:a1d24fd04bd7433d507aa83858d56e622aaa3747f9f7d0a0438a425ef9f604a3ghcr.io/lance0/prefixd-dashboard@sha256:91276a55f6e8ba41b18ad8d57749cc912c0deee85d2a6512f8054cad0f9b9aee
SHA256SUMS:
854d177ea79db1e55633842e91dd8477f78e5d5e76f3714eb41c72ef7b5e0029 prefixd-linux-amd64.tar.gz
cd510036272d2a3002f478567384f07697e472232cecd52e035fae44b5f0ec32 prefixd-sbom.cdx.json
Full changelog: v0.23.1...v0.24.0