From 71c4b33374f2038cf53f2169b3cad6245f9c8b4b Mon Sep 17 00:00:00 2001 From: Claude Bot Date: Sun, 19 Jul 2026 08:45:13 +0200 Subject: [PATCH] fix(netbird-rotation): use python:alpine (apk install fails under non-root PSA) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Testing #304 revealed the runtime 'apk add curl jq' fails ('Unable to lock database: Permission denied') because the job runs non-root (runAsUser 65534) and the openbao ns enforces restricted PSA — apk needs root. Switch to python:3.13-alpine and do the HTTP/JSON via stdlib (urllib/json), no package install, non-root-safe. Same fix must be applied to the still-open cloudflare (#305) and proxmox (#306) Class-B jobs. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_015K7gLUVVnutvDnzV2TrMRJ --- .../openbao/rotation/netbird-ingress.yaml | 52 ++++++++++--------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/kubernetes/infrastructure/openbao/rotation/netbird-ingress.yaml b/kubernetes/infrastructure/openbao/rotation/netbird-ingress.yaml index a859e33f..dfdfdc51 100644 --- a/kubernetes/infrastructure/openbao/rotation/netbird-ingress.yaml +++ b/kubernetes/infrastructure/openbao/rotation/netbird-ingress.yaml @@ -69,9 +69,11 @@ spec: type: RuntimeDefault containers: - name: rotate - # Class-B jobs need an HTTP client + JSON. alpine + runtime apk add - # (same runtime-install approach as seed/influxdb/ha-token-job.yaml). - image: alpine:3.21 + # Class-B jobs need an HTTP client + JSON. python:alpine provides both + # via stdlib (urllib/json) with NO runtime package install, so it runs + # cleanly under the non-root securityContext (apk/apt need root and fail + # here — the openbao ns enforces restricted PSA). + image: python:3.13-alpine securityContext: allowPrivilegeEscalation: false capabilities: @@ -84,32 +86,34 @@ spec: # FILL IN: comma-separated NetBird group IDs for the new key. - name: NB_AUTO_GROUPS value: "REPLACE_WITH_GROUP_IDS" - command: ["/bin/sh", "-ec"] + command: ["python3", "-c"] args: - | - apk add --no-cache curl jq ca-certificates >/dev/null - JWT="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" - # 1. OpenBao login (k8s auth) → client token - T=$(curl -sf --request POST "$BAO_ADDR/v1/auth/kubernetes/login" \ - -d "{\"role\":\"rotator-netbird\",\"jwt\":\"$JWT\"}" | jq -r .auth.client_token) - [ -n "$T" ] && [ "$T" != null ] || { echo "openbao login failed"; exit 1; } + import json, os, urllib.request as u + bao = os.environ["BAO_ADDR"]; mgmt = os.environ["NB_MANAGEMENT_URL"] + groups = [g for g in os.environ["NB_AUTO_GROUPS"].split(",") if g] + jwt = open("/var/run/secrets/kubernetes.io/serviceaccount/token").read() + def call(method, url, headers=None, body=None, ct="application/json"): + data = json.dumps(body).encode() if body is not None else None + req = u.Request(url, data=data, method=method, headers=headers or {}) + if data is not None: + req.add_header("Content-Type", ct) + with u.urlopen(req, timeout=30) as r: + return json.loads(r.read() or b"{}") + # 1. OpenBao login (k8s auth) + tok = call("POST", bao + "/v1/auth/kubernetes/login", + body={"role": "rotator-netbird", "jwt": jwt})["auth"]["client_token"] # 2. read the bootstrap NetBird PAT - PAT=$(curl -sf -H "X-Vault-Token: $T" \ - "$BAO_ADDR/v1/homelab/data/infrastructure/secret-rotators-bootstrap/netbird" \ - | jq -r .data.data.pat) - [ -n "$PAT" ] && [ "$PAT" != null ] || { echo "no bootstrap PAT"; exit 1; } + pat = call("GET", bao + "/v1/homelab/data/infrastructure/secret-rotators-bootstrap/netbird", + {"X-Vault-Token": tok})["data"]["data"]["pat"] # 3. mint a new reusable setup key via the NetBird mgmt API - GROUPS=$(echo "$NB_AUTO_GROUPS" | jq -R 'split(",")') - NEW=$(curl -sf -H "Authorization: Token $PAT" -H "Content-Type: application/json" \ - -X POST "$NB_MANAGEMENT_URL/api/setup-keys" \ - -d "{\"name\":\"ingress-rotated\",\"type\":\"reusable\",\"expires_in\":31536000,\"usage_limit\":0,\"auto_groups\":$GROUPS}" \ - | jq -r .key) - [ -n "$NEW" ] && [ "$NEW" != null ] || { echo "setup-key creation failed"; exit 1; } + key = call("POST", mgmt + "/api/setup-keys", {"Authorization": "Token " + pat}, + {"name": "ingress-rotated", "type": "reusable", "expires_in": 31536000, + "usage_limit": 0, "auto_groups": groups})["key"] # 4. patch the new key into OpenBao (KV v2 merge-patch) - curl -sf -H "X-Vault-Token: $T" -H "Content-Type: application/merge-patch+json" \ - -X PATCH "$BAO_ADDR/v1/homelab/data/infrastructure/netbird-ingress" \ - -d "{\"data\":{\"setup-key\":\"$NEW\"}}" >/dev/null - echo "rotated setup-key at homelab/infrastructure/netbird-ingress (value not logged)" + call("PATCH", bao + "/v1/homelab/data/infrastructure/netbird-ingress", + {"X-Vault-Token": tok}, {"data": {"setup-key": key}}, "application/merge-patch+json") + print("rotated setup-key at homelab/infrastructure/netbird-ingress (value not logged)") resources: requests: cpu: 25m -- 2.54.0