Skip to content

v0.24.0

Latest

Choose a tag to compare

@github-actions github-actions released this 02 Nov 21:20
e58e29a

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_diversions table, creates no rows and changes no existing mitigation; no adoption step runs.
  • Scrubber diversion stays off unless scrubber.enabled is set; the only new configuration keys are under scrubber, and a 0.23.1 prefixd.yaml loads 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 degraded when 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_receipt is 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/scrubbers and GET /v1/diversions allow 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-cmark to 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_diversions table. See the upgrade guide before disabling profiles or downgrading with outstanding diversions.

  • Every /v1/scrubbers and /v1/diversions endpoint requires authentication; arm, preview, create and withdraw also require Admin. Unknown profiles and diversions return 404, disabled scrubbers 409, and an unconfirmed outcome 500. Withdrawal takes an optional reason, also through prefixdctl 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 active or 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.yml overlay reads configs/inventory.yaml directly 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 = false on neighbors that never come up only mattered on v0.80.x. selection_deferred remains a decoded export reason; the PrefixdBlackholeNotExported hint 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_flowspec is set, and GET /v1/speaker/routes returns 502. 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 argon2 to 0.6 and drop the unused direct password-hash 0.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. Their state is 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/events accepts bounded q, source and vector parameters 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 All remain selectable.

  • GET /v1/mitigations accepts bounded literal substring search through q, covering target, vector, customer ID and mitigation UUID before pagination.

  • rustbgpd ownership refusals now distinguish owner_mismatch from key_unowned in admission 409 bodies, retirement audit reasons and operational logs. Missing, malformed, conflicting or unrecognized rich-status details use unspecified; 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 existing not_live health result, with a typed generation_not_live diagnostic 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 previous 2001:db8:acme::/48 is not a valid prefix, and inventory load rejects it.

Artifacts

Container images, tagged 0.24.0 and 0.24:

  • ghcr.io/lance0/prefixd@sha256:a1d24fd04bd7433d507aa83858d56e622aaa3747f9f7d0a0438a425ef9f604a3
  • ghcr.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