feat(secret-rotator): netbird-key mode #334

Merged
k.renner merged 1 commits from feat/netbird-key-mode into master 2026-07-21 15:23:20 +00:00
Member

Step 1 of 2 to port the NetBird rotator. Adds the netbird-key binary mode; the manifest port follows once CI publishes the image (the CronJob pins it by digest).

What it does

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 (ephemeral, bounded expiry), so nothing is deleted.

It deliberately does not delete old keys. A past manual cleanup that filtered keys for deletion wiped two unrelated ones (a jq precedence bug); that risk has no place in an automated rotator. Old keys age out on their own.

Faithful to the python it replaces

  • auth is Authorization: Token <pat> — not Bearer
  • type/usage_limit fixed (reusable, unlimited); name, expiry, ephemeral, allow_extra_dns_labels, auto_groups from env
  • key stored verbatim (serde_json, no trailing newline)
  • assert_fields_exist guards the target path
  • an error that echoes the response redacts the key field so a value can't leak

Validated against real NetBird (throwaway key, zero blast radius)

TLS verifies with built-in webpki roots (no -k, no no-verify context needed)
POST /api/setup-keys (Token auth) -> response has 'key'
key: 36 chars, no trailing newline
settings: reusable, ephemeral, allow_extra_dns_labels, usage_limit 0, 1 auto_group
throwaway key deleted; 0 remaining

clippy -D warnings clean; 12 tests pass. In-cluster pieces (login, read_field, assert_fields_exist, patch_field) are the same code already proven live by the other modes.

🤖 Generated with Claude Code

Step 1 of 2 to port the NetBird rotator. Adds the `netbird-key` binary mode; the manifest port follows once CI publishes the image (the CronJob pins it by digest). ## What it does 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 (ephemeral, bounded expiry), so nothing is deleted. **It deliberately does not delete old keys.** A past manual cleanup that filtered keys for deletion wiped two unrelated ones (a jq precedence bug); that risk has no place in an automated rotator. Old keys age out on their own. ## Faithful to the python it replaces - auth is `Authorization: Token <pat>` — **not** Bearer - `type`/`usage_limit` fixed (reusable, unlimited); name, expiry, ephemeral, allow_extra_dns_labels, auto_groups from env - key stored verbatim (serde_json, no trailing newline) - `assert_fields_exist` guards the target path - an error that echoes the response **redacts** the `key` field so a value can't leak ## Validated against real NetBird (throwaway key, zero blast radius) ``` TLS verifies with built-in webpki roots (no -k, no no-verify context needed) POST /api/setup-keys (Token auth) -> response has 'key' key: 36 chars, no trailing newline settings: reusable, ephemeral, allow_extra_dns_labels, usage_limit 0, 1 auto_group throwaway key deleted; 0 remaining ``` `clippy -D warnings` clean; 12 tests pass. In-cluster pieces (login, read_field, assert_fields_exist, patch_field) are the same code already proven live by the other modes. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
claude-bot added 1 commit 2026-07-21 15:20:45 +00:00
feat(secret-rotator): netbird-key mode
ci/woodpecker/push/build-ci-images Pipeline was successful
2611d7bc81
Adds the mode that will replace the NetBird python rotator. 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 (keys are
ephemeral with a bounded expiry), so nothing is deleted — no destructive step,
no window. This deliberately does NOT delete old keys; a past manual cleanup
that filtered keys for deletion wiped unrelated ones (a jq precedence bug), and
that risk has no place in an automated rotator.

NetBird auth is `Authorization: Token <pat>` — NOT Bearer. The key/type/
usage_limit semantics (reusable, unlimited) are fixed; name, expiry, ephemeral,
allow_extra_dns_labels and auto_groups come from the environment. The returned
key is used verbatim (serde_json, no trailing newline). assert_fields_exist
guards the target path. An error that echoes the response redacts the `key`
field so a value can never leak into logs.

Validated against the real NetBird API on a throwaway key (created then
deleted, zero blast radius): TLS verifies with the built-in webpki roots (no
no-verify context needed), POST with Token auth returns a 36-char key with no
trailing newline and the expected settings (reusable/ephemeral/usage_limit 0/
1 auto_group). clippy -D warnings clean; 12 tests pass; the in-cluster pieces
are the same code already proven live by the other modes.

No rotator uses it yet — the manifest port follows once CI publishes the image.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
k.renner merged commit 57362a35ed into master 2026-07-21 15:23:19 +00:00
Sign in to join this conversation.