From 07bd94c55a29b30d64433617ecd69dfc87f66547 Mon Sep 17 00:00:00 2001 From: Ben Vincent Date: Sun, 9 Aug 2026 12:33:30 +1000 Subject: [PATCH] Wire API_TOKEN_PEPPERS into NetBox config (#346) ## Why NetBox 4.6.5 refuses to save v2 API tokens without `API_TOKEN_PEPPERS` ("Unable to save v2 tokens: API_TOKEN_PEPPERS is not defined"), which blocks creating the superuser token the NetBox Vault engine needs (it defaults to v2 tokens). The chart only auto-generates a pepper when it creates the config secret itself; it does not do that while `existingSecret` (`netbox-secret-key`) is set, so the config secret carries no `api_token_peppers` key. ## Changes - Document `api_token_peppers` on the `netbox-secret-key` VaultStaticSecret: a JSON pepper map `{"1": ""}` seeded once into Vault alongside `secret_key`. VSO syncs every key at the path into the config secret, which the chart already mounts as an optional file into `API_TOKEN_PEPPERS`. - Add a reloader annotation via `commonAnnotations` so the `netbox` and `netbox-worker` Deployments roll when `netbox-secret-key` changes, picking up the seeded pepper (and any rotated `secret_key`) without a manual restart. ## Follow-up (out of band) seed the pepper once (rotating it invalidates existing v2 tokens): ``` PEP=$(openssl rand -base64 48 | tr -d '\n') vault kv patch kv/kubernetes/namespace/netbox/default/netbox-secret-key \ api_token_peppers="{\"1\": \"$PEP\"}" ``` --------- Co-authored-by: Ben Vincent Reviewed-on: https://git.unkin.net/unkin/argocd-apps/pulls/346 Co-authored-by: Ben Vincent Co-committed-by: Ben Vincent --- apps/base/netbox/vaultstaticsecret.yaml | 8 +++++++- apps/overlays/au-syd1/netbox/values.yaml | 13 ++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/apps/base/netbox/vaultstaticsecret.yaml b/apps/base/netbox/vaultstaticsecret.yaml index 59833d3..fc909c9 100644 --- a/apps/base/netbox/vaultstaticsecret.yaml +++ b/apps/base/netbox/vaultstaticsecret.yaml @@ -19,7 +19,13 @@ spec: type: kv-v2 vaultAuthRef: default --- -# Django SECRET_KEY (key: secret_key). One-time Vault seed. +# Config secret. Keys: +# secret_key : Django SECRET_KEY (50+ random chars). One-time Vault seed. +# api_token_peppers : JSON object {"1": "<50+ char random>"} used to HMAC-hash +# v2 API tokens. One-time Vault seed — rotating a pepper +# invalidates existing v2 tokens, so set it once. +# VSO syncs every key at the path into the destination Secret, and the NetBox +# chart mounts both keys from it (existingSecret) — no explicit key mapping needed. apiVersion: secrets.hashicorp.com/v1beta1 kind: VaultStaticSecret metadata: diff --git a/apps/overlays/au-syd1/netbox/values.yaml b/apps/overlays/au-syd1/netbox/values.yaml index 967cde9..174ebce 100644 --- a/apps/overlays/au-syd1/netbox/values.yaml +++ b/apps/overlays/au-syd1/netbox/values.yaml @@ -13,13 +13,24 @@ image: # Two web replicas for zero-downtime rollouts (media is RWX, see below). replicaCount: 2 -# Django SECRET_KEY — existingSecret must contain key: secret_key. +# Config secret. Must contain key: secret_key (Django SECRET_KEY). Also carries +# api_token_peppers — a JSON object {"1": "<50+ char random>"} the chart mounts +# (optional file) into API_TOKEN_PEPPERS; NetBox 4.6 refuses to save v2 tokens +# without it. The chart only auto-generates a pepper when it creates this secret +# itself, which it does NOT do while existingSecret is set — so the pepper is +# seeded into Vault alongside secret_key (see base/vaultstaticsecret.yaml). existingSecret: netbox-secret-key # Bootstrap superuser — existingSecret keys: username, password, email, api_token. superuser: existingSecret: netbox-superuser +# Roll NetBox (web + worker) when the config secret changes, so a Vault-seeded +# api_token_peppers (or a rotated secret_key) is picked up without a manual +# restart. commonAnnotations lands on Deployment metadata, where reloader reads it. +commonAnnotations: + secret.reloader.stakater.com/reload: netbox-secret-key + # Disable the bundled Bitnami subcharts; we bring our own Postgres and Valkey. postgresql: enabled: false