Step 2 of 2. Ports the NetBird rotator from inline python to the binary's netbird-key mode (merged in #334). Net -30 lines.
Tested live end-to-end (via the real binary image)
job Complete: minted 'ingress-rotated', OpenBao rolled 8a0aa8d4 -> 77f1be0c
NetBird: 2 'ingress-rotated' keys now (new added, old KEPT — create-new)
ESO synced -> Reloader restarted the DaemonSet
rolling update (maxUnavailable 1) -> 3/3 ready, updated=3, 0 errors
new key: used_times=3, last_used just now (all 3 pods enrolled)
pods actively handshaking peers (WireGuard/relay connected)
old key: still valid, untouched (expires naturally)
Real re-enrollment with no ingress outage — maxUnavailable=1 restarts one pod at a time, and create-new keeps the old key valid throughout, so there's no window.
Faithful port
Same POST /api/setup-keys body (reusable, ephemeral, 1yr expiry, auto_groups), same Token auth, same bootstrap PAT + target path. The binary stores the key verbatim (no trailing newline) and never deletes old keys.
Flux strips the old python command on apply (verified on the earlier ports); a manual kubectl apply would not — port via Flux.
Image digest sha256:49bfddd8…. After this merges, only pve-exporter remains on inline python.
Step 2 of 2. Ports the NetBird rotator from inline python to the binary's `netbird-key` mode (merged in #334). **Net -30 lines.**
## Tested live end-to-end (via the real binary image)
```
job Complete: minted 'ingress-rotated', OpenBao rolled 8a0aa8d4 -> 77f1be0c
NetBird: 2 'ingress-rotated' keys now (new added, old KEPT — create-new)
ESO synced -> Reloader restarted the DaemonSet
rolling update (maxUnavailable 1) -> 3/3 ready, updated=3, 0 errors
new key: used_times=3, last_used just now (all 3 pods enrolled)
pods actively handshaking peers (WireGuard/relay connected)
old key: still valid, untouched (expires naturally)
```
Real re-enrollment with no ingress outage — `maxUnavailable=1` restarts one pod at a time, and create-new keeps the old key valid throughout, so there's no window.
## Faithful port
Same POST /api/setup-keys body (reusable, ephemeral, 1yr expiry, `auto_groups`), same `Token` auth, same bootstrap PAT + target path. The binary stores the key verbatim (no trailing newline) and never deletes old keys.
Flux strips the old python `command` on apply (verified on the earlier ports); a manual `kubectl apply` would not — port via Flux.
Image digest `sha256:49bfddd8…`. After this merges, **only pve-exporter remains** on inline python.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Replaces the inline python NetBird rotator with the secret-rotator binary's
netbird-key mode. Net -30 lines; the login+POST+parse python and the
python:3.13-alpine image go away.
Mints a fresh reusable setup key via POST /api/setup-keys and patches it into
OpenBao. Create-new, not roll-in-place: the old key is left to expire, so
there is no window — it stays valid while the netbird-ingress DaemonSet
re-enrolls its pods on the new one. Nothing is deleted (deliberately — a past
manual delete-filter bug wiped unrelated keys).
Tested live end-to-end via the real binary image:
- job Complete: minted 'ingress-rotated', OpenBao rolled 8a0aa8d4 -> 77f1be0c
- NetBird now has 2 'ingress-rotated' keys (new added, old kept — create-new)
- ESO synced; Reloader restarted the DaemonSet; rolling update (maxUnavailable
1) completed 3/3 with 0 errors
- all 3 pods re-enrolled: the new key shows used_times=3, last_used just now;
pods actively handshaking peers (WireGuard/relay connected)
- old key still valid, untouched
Flux server-side apply strips the old python `command` (verified on the other
ports); a manual `kubectl apply` would not, so port via Flux.
Image digest sha256:49bfddd8…
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
🤖Automated review — model claude-sonnet-5 (complexity: normal), commit 92b59f7c8894354ce679e06dcf4cde132529d5b7.
Advisory review by the review-bot pipeline. It never approves; a human merges. Push new commits to get a fresh review.
Summary
PR #335 ports the netbird-ingress rotation CronJob from an inline Python script (embedded as a shell-style heredoc) to the secret-rotator binary's new netbird-key mode, following the pattern already established for the other rotators (crowdsec-bouncer-key.yaml, cloudflare.yaml, etc.). It's a manifest-only change (no Rust code touched here — netbird-key mode itself was added in the already-merged #334). I traced every env var the new manifest sets against secret-rotator/src/main.rs::rotate_netbird_key, netbird.rs, and bao.rs in the checkout, and the behavior is a faithful, correct port: same OpenBao role (rotator-netbird), same bootstrap/target KV paths, same default bootstrap field (pat) and target field (setup-key), same setup-key request body semantics (reusable, 1yr expiry, ephemeral, allow_extra_dns_labels, same auto_groups). No blocking issues found.
🟡 Suggestions
kubernetes/infrastructure/openbao/rotation/netbird-ingress.yaml:34 — the old inline # tested + enabled 2026-07-19 comment on suspend: false was dropped, and no equivalent note was added elsewhere in the file for this specific rollout. Not a defect, but the sibling files (e.g. crowdsec-bouncer-key.yaml's ON-DEMAND: kubectl ... create job ... line) keep an operational breadcrumb; consider whether this file should too, for consistency with the rest of the fleet.
The image digest (sha256:49bfddd8…) differs from every other rotator in this directory (which pin either 159e5888… for random/ha-token modes or 978fe53c… for cloudflare-roll). That's expected here since this is the first consumer of netbird-key mode, but worth flagging only because it means this job is now running a binary version no other CronJob has exercised in production yet — the live test in the PR description covers that risk directly, so this is just a note, not a concern.
🟢 Nits
netbird-ingress.yaml:52 — suspend: false is the default; the field is arguably redundant now that its explanatory comment is gone, but it was already present before this PR and isn't part of what changed here.
Tests
No unit tests accompany this change, but none are expected for a manifest-only diff — the actual rotation logic (rotate_netbird_key, create_setup_key, Bao::patch_field/assert_fields_exist) lives in secret-rotator and was presumably covered when netbird-key mode was introduced in #334. The PR body documents a live end-to-end test against the real cluster (key minted, ESO synced, DaemonSet rolled, new key handshaking, old key untouched) which is the appropriate validation for this kind of infra change and is more convincing than a unit test would be here.
Recommendation
Looks reasonable — no blocking issues; comments above are optional.
🤖 **Automated review** — model `claude-sonnet-5` (complexity: normal), commit `92b59f7c8894354ce679e06dcf4cde132529d5b7`.
> Advisory review by the `review-bot` pipeline. It never approves; a human merges. Push new commits to get a fresh review.
---
## Summary
PR #335 ports the `netbird-ingress` rotation CronJob from an inline Python script (embedded as a shell-style heredoc) to the `secret-rotator` binary's new `netbird-key` mode, following the pattern already established for the other rotators (`crowdsec-bouncer-key.yaml`, `cloudflare.yaml`, etc.). It's a manifest-only change (no Rust code touched here — `netbird-key` mode itself was added in the already-merged #334). I traced every env var the new manifest sets against `secret-rotator/src/main.rs::rotate_netbird_key`, `netbird.rs`, and `bao.rs` in the checkout, and the behavior is a faithful, correct port: same OpenBao role (`rotator-netbird`), same bootstrap/target KV paths, same default bootstrap field (`pat`) and target field (`setup-key`), same setup-key request body semantics (reusable, 1yr expiry, ephemeral, `allow_extra_dns_labels`, same `auto_groups`). No blocking issues found.
## 🟡 Suggestions
- `kubernetes/infrastructure/openbao/rotation/netbird-ingress.yaml:34` — the old inline `# tested + enabled 2026-07-19` comment on `suspend: false` was dropped, and no equivalent note was added elsewhere in the file for this specific rollout. Not a defect, but the sibling files (e.g. `crowdsec-bouncer-key.yaml`'s `ON-DEMAND: kubectl ... create job ...` line) keep an operational breadcrumb; consider whether this file should too, for consistency with the rest of the fleet.
- The image digest (`sha256:49bfddd8…`) differs from every other rotator in this directory (which pin either `159e5888…` for `random`/`ha-token` modes or `978fe53c…` for `cloudflare-roll`). That's expected here since this is the first consumer of `netbird-key` mode, but worth flagging only because it means this job is now running a binary version no other CronJob has exercised in production yet — the live test in the PR description covers that risk directly, so this is just a note, not a concern.
## 🟢 Nits
- `netbird-ingress.yaml:52` — `suspend: false` is the default; the field is arguably redundant now that its explanatory comment is gone, but it was already present before this PR and isn't part of what changed here.
## Tests
No unit tests accompany this change, but none are expected for a manifest-only diff — the actual rotation logic (`rotate_netbird_key`, `create_setup_key`, `Bao::patch_field`/`assert_fields_exist`) lives in `secret-rotator` and was presumably covered when `netbird-key` mode was introduced in #334. The PR body documents a live end-to-end test against the real cluster (key minted, ESO synced, DaemonSet rolled, new key handshaking, old key untouched) which is the appropriate validation for this kind of infra change and is more convincing than a unit test would be here.
## Recommendation
Looks reasonable — no blocking issues; comments above are optional.
<!-- pr-review:sha=92b59f7c8894354ce679e06dcf4cde132529d5b7 -->
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Step 2 of 2. Ports the NetBird rotator from inline python to the binary's
netbird-keymode (merged in #334). Net -30 lines.Tested live end-to-end (via the real binary image)
Real re-enrollment with no ingress outage —
maxUnavailable=1restarts one pod at a time, and create-new keeps the old key valid throughout, so there's no window.Faithful port
Same POST /api/setup-keys body (reusable, ephemeral, 1yr expiry,
auto_groups), sameTokenauth, same bootstrap PAT + target path. The binary stores the key verbatim (no trailing newline) and never deletes old keys.Flux strips the old python
commandon apply (verified on the earlier ports); a manualkubectl applywould not — port via Flux.Image digest
sha256:49bfddd8…. After this merges, only pve-exporter remains on inline python.🤖 Generated with Claude Code
Replaces the inline python NetBird rotator with the secret-rotator binary's netbird-key mode. Net -30 lines; the login+POST+parse python and the python:3.13-alpine image go away. Mints a fresh reusable setup key via POST /api/setup-keys and patches it into OpenBao. Create-new, not roll-in-place: the old key is left to expire, so there is no window — it stays valid while the netbird-ingress DaemonSet re-enrolls its pods on the new one. Nothing is deleted (deliberately — a past manual delete-filter bug wiped unrelated keys). Tested live end-to-end via the real binary image: - job Complete: minted 'ingress-rotated', OpenBao rolled 8a0aa8d4 -> 77f1be0c - NetBird now has 2 'ingress-rotated' keys (new added, old kept — create-new) - ESO synced; Reloader restarted the DaemonSet; rolling update (maxUnavailable 1) completed 3/3 with 0 errors - all 3 pods re-enrolled: the new key shows used_times=3, last_used just now; pods actively handshaking peers (WireGuard/relay connected) - old key still valid, untouched Flux server-side apply strips the old python `command` (verified on the other ports); a manual `kubectl apply` would not, so port via Flux. Image digest sha256:49bfddd8… Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>🤖 Automated review — model
claude-sonnet-5(complexity: normal), commit92b59f7c8894354ce679e06dcf4cde132529d5b7.Summary
PR #335 ports the
netbird-ingressrotation CronJob from an inline Python script (embedded as a shell-style heredoc) to thesecret-rotatorbinary's newnetbird-keymode, following the pattern already established for the other rotators (crowdsec-bouncer-key.yaml,cloudflare.yaml, etc.). It's a manifest-only change (no Rust code touched here —netbird-keymode itself was added in the already-merged #334). I traced every env var the new manifest sets againstsecret-rotator/src/main.rs::rotate_netbird_key,netbird.rs, andbao.rsin the checkout, and the behavior is a faithful, correct port: same OpenBao role (rotator-netbird), same bootstrap/target KV paths, same default bootstrap field (pat) and target field (setup-key), same setup-key request body semantics (reusable, 1yr expiry, ephemeral,allow_extra_dns_labels, sameauto_groups). No blocking issues found.🟡 Suggestions
kubernetes/infrastructure/openbao/rotation/netbird-ingress.yaml:34— the old inline# tested + enabled 2026-07-19comment onsuspend: falsewas dropped, and no equivalent note was added elsewhere in the file for this specific rollout. Not a defect, but the sibling files (e.g.crowdsec-bouncer-key.yaml'sON-DEMAND: kubectl ... create job ...line) keep an operational breadcrumb; consider whether this file should too, for consistency with the rest of the fleet.sha256:49bfddd8…) differs from every other rotator in this directory (which pin either159e5888…forrandom/ha-tokenmodes or978fe53c…forcloudflare-roll). That's expected here since this is the first consumer ofnetbird-keymode, but worth flagging only because it means this job is now running a binary version no other CronJob has exercised in production yet — the live test in the PR description covers that risk directly, so this is just a note, not a concern.🟢 Nits
netbird-ingress.yaml:52—suspend: falseis the default; the field is arguably redundant now that its explanatory comment is gone, but it was already present before this PR and isn't part of what changed here.Tests
No unit tests accompany this change, but none are expected for a manifest-only diff — the actual rotation logic (
rotate_netbird_key,create_setup_key,Bao::patch_field/assert_fields_exist) lives insecret-rotatorand was presumably covered whennetbird-keymode was introduced in #334. The PR body documents a live end-to-end test against the real cluster (key minted, ESO synced, DaemonSet rolled, new key handshaking, old key untouched) which is the appropriate validation for this kind of infra change and is more convincing than a unit test would be here.Recommendation
Looks reasonable — no blocking issues; comments above are optional.