diff --git a/kubernetes/apps/monitoring/grafana-influxdb-token.sops.yaml b/kubernetes/apps/monitoring/grafana-influxdb-token.sops.yaml deleted file mode 100644 index 269668f9..00000000 --- a/kubernetes/apps/monitoring/grafana-influxdb-token.sops.yaml +++ /dev/null @@ -1,23 +0,0 @@ -apiVersion: ENC[AES256_GCM,data:UJo=,iv:dWPZY9IPOwP/wz7NNHWgNLqcemZ7SF5vTLGkLAIVtMw=,tag:/mUiJ+1bSvM1Q+1Z2MAoAQ==,type:str] -kind: ENC[AES256_GCM,data:2evJBSao,iv:GV5uMMGXkj4unsrX6eSDSnXztVJvO3PH7KysfZVZf9Y=,tag:jL1ouzE3T6utlUgWe5xRGA==,type:str] -metadata: - name: ENC[AES256_GCM,data:yBHEPANSWoXEFtd5/hJaUhPOtGNtUg==,iv:IJVco8HWDAEpkS0R7Lw1YDI374cfG+5FIcr81jg6JUA=,tag:cmz68dU++5sqoPoHsTfBJQ==,type:str] - namespace: ENC[AES256_GCM,data:a31jcEaz5JIKSg==,iv:qiyyEy3CNgEbTIrjMIt5wurQWNsXp2Y32TIZ13pNFWM=,tag:hqtHHq+vdxOs8evHkZs2Fw==,type:str] -type: ENC[AES256_GCM,data:VE7pZ34m,iv:I22j9ZxbieeHiQwrXiS9xpHNzp49Yu2g+kbLk8CM5HU=,tag:WyaAjY6fz3RJK+ScAlkS8A==,type:str] -stringData: - INFLUXDB_TOKEN: ENC[AES256_GCM,data:MhpHqSIk3Nea7msIxzQmvEuFjChNIDfnRpHge4WNVVV3YWeecslMk7cMvRSUBSb0zQ9YYyCKWlnoM7Z+45mfpDDiYB/cnOx1DGXDPGNA8nb6wqJZkbRciA==,iv:xdzC52H9ajkCC2kyXsrlRoBBIYfZ9qD41CrPCntxPTA=,tag:e1Kdix1oX90vVhAp0eq7yQ==,type:str] -sops: - age: - - recipient: age1yqjvqkwwwxdj7mr546zfs5ckzuwndp69ya2n2drd4vypwns8w4ys04qfy3 - enc: | - -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBQZXBHaVpLUVJtOWJCaTJG - cDg3SUR1QUhDb1BhaGtIWG9tdGlFRit4L3hRCi9LNHdNdS95RHFmQTNNYk43OEJV - WGtNd1RuL0N6QTFvZVMvdHAzbmx1b2cKLS0tIHpEeStVWVI5MFlENDE5VXV4RE80 - Y2hNRHpzeTVEZmhPUjhzbklvdVR4UGcKBfngE1xQY14LM03nu1q905JrLkG3SVrX - qyGIEe5xjxTLY4WCu5jfKAEkLmjTwFTGJLg+0F0J+S2MmXyyigBNiQ== - -----END AGE ENCRYPTED FILE----- - lastmodified: "2026-04-23T12:57:29Z" - mac: ENC[AES256_GCM,data:LsMf8qJh519Ylg62jmNmMLt98db8b5S6tvJnxZgpLo9es8jux5g0f75loWTIqzsXL6T7nglWuTp2ouwX4YCgiqTjHPSjJ94ACLpeiFEmjY23gGHtZP1PY1dXZ69OwRJNNmJbEYtopw/rxO/J54MhYi28IL1MDRuwdU/ZttSlqH8=,iv:tgRMH6F4HCHctmAa8UAPnlGUojl+GN3vbDflPbhwxuI=,tag:Ol9YUiEbXSJL+49Klhh/lg==,type:str] - unencrypted_suffix: _unencrypted - version: 3.10.2 diff --git a/kubernetes/apps/monitoring/helmrelease-prometheus.yaml b/kubernetes/apps/monitoring/helmrelease-prometheus.yaml index 88f9416c..e449bdb6 100644 --- a/kubernetes/apps/monitoring/helmrelease-prometheus.yaml +++ b/kubernetes/apps/monitoring/helmrelease-prometheus.yaml @@ -183,14 +183,16 @@ spec: isDefault: false jsonData: httpMethod: POST - # InfluxDB on VM 108 — Home Assistant is the sole writer. K8s - # Grafana consumes the live VM 108 endpoint until HA migrates - # (writer cut-over + datasource flip to cluster-internal deferred - # to the HA migration plan). + # In-cluster InfluxDB (chart `influxdb-influxdb2`). Both HA (write) + # and Grafana (read) hit this directly via Service DNS. The + # `grafana-influxdb-token` Secret is created by the seed Job + # `influxdb-grafana-token` (kubernetes/seed/influxdb/grafana- + # token-job.yaml), which mints a read-only auth on the + # suedstr28 bucket every fresh deploy. - name: InfluxDB uid: influxdb type: influxdb - url: http://influxdb.srv.renner.uno:8086 + url: http://influxdb-influxdb2.influxdb.svc.cluster.local:8086 access: proxy isDefault: false jsonData: diff --git a/kubernetes/apps/monitoring/kustomization.yaml b/kubernetes/apps/monitoring/kustomization.yaml index 338602bc..5776afcb 100644 --- a/kubernetes/apps/monitoring/kustomization.yaml +++ b/kubernetes/apps/monitoring/kustomization.yaml @@ -2,7 +2,6 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - grafana-oauth.sops.yaml - - grafana-influxdb-token.sops.yaml - garage-metrics-token.sops.yaml - alertmanager-smtp.sops.yaml - pve-exporter-config.sops.yaml diff --git a/kubernetes/seed/influxdb/grafana-token-job.yaml b/kubernetes/seed/influxdb/grafana-token-job.yaml new file mode 100644 index 00000000..4be94612 --- /dev/null +++ b/kubernetes/seed/influxdb/grafana-token-job.yaml @@ -0,0 +1,145 @@ +--- +# Mirror of ha-token-job.yaml but creates a read-only auth for K8s +# Grafana on the suedstr28 bucket, and stashes the token as the +# `grafana-influxdb-token` Secret in the monitoring namespace. Replaces +# the previously SOPS-stored token that targeted the (now-retired) VM +# 108 InfluxDB and no longer matches the in-cluster server's bolt store. +apiVersion: v1 +kind: ServiceAccount +metadata: + name: influxdb-grafana-token + namespace: influxdb +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: grafana-influxdb-token-writer + namespace: monitoring +rules: + - apiGroups: [""] + resources: ["secrets"] + verbs: ["create"] + - apiGroups: [""] + resources: ["secrets"] + resourceNames: ["grafana-influxdb-token"] + verbs: ["get", "update", "patch"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: grafana-influxdb-token-writer + namespace: monitoring +subjects: + - kind: ServiceAccount + name: influxdb-grafana-token + namespace: influxdb +roleRef: + kind: Role + name: grafana-influxdb-token-writer + apiGroup: rbac.authorization.k8s.io +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: influxdb-grafana-token + namespace: influxdb + annotations: + kustomize.toolkit.fluxcd.io/force: enabled +spec: + backoffLimit: 6 + ttlSecondsAfterFinished: 86400 + template: + spec: + restartPolicy: OnFailure + serviceAccountName: influxdb-grafana-token + securityContext: + runAsUser: 0 + runAsNonRoot: false + initContainers: + - name: wait-for-influxd + image: influxdb:2.7.4 + env: + - { name: INFLUX_HOST, value: http://influxdb-influxdb2.influxdb.svc.cluster.local:8086 } + - { name: INFLUX_ORG, value: renner } + - name: INFLUX_TOKEN + valueFrom: + secretKeyRef: { name: influxdb-bootstrap, key: admin-token } + command: + - sh + - -c + - | + set -eu + until influx ping >/dev/null 2>&1; do + echo "[$(date -u +%H:%M:%S)] waiting for $INFLUX_HOST..." + sleep 5 + done + until influx bucket list >/dev/null 2>&1; do + echo "[$(date -u +%H:%M:%S)] API up; waiting for bootstrap admin token..." + sleep 5 + done + echo "[$(date -u +%H:%M:%S)] $INFLUX_HOST ready and authenticated" + containers: + - name: create + image: influxdb:2.7.4 + env: + - { name: INFLUX_HOST, value: http://influxdb-influxdb2.influxdb.svc.cluster.local:8086 } + - { name: INFLUX_ORG, value: renner } + - { name: INFLUX_BUCKET, value: suedstr28 } + - { name: TOKEN_DESC, value: grafana read } + - { name: TARGET_NS, value: monitoring } + - { name: TARGET_SECRET, value: grafana-influxdb-token } + - name: INFLUX_TOKEN + valueFrom: + secretKeyRef: { name: influxdb-bootstrap, key: admin-token } + command: + - bash + - -c + - | + set -euo pipefail + log() { printf '[%s] %s\n' "$(date -u +%H:%M:%S)" "$*"; } + + apt-get update -qq + apt-get install -y --no-install-recommends curl jq ca-certificates >/dev/null + KV=$(curl -fsSL https://dl.k8s.io/release/stable.txt) + curl -fsSL "https://dl.k8s.io/release/$KV/bin/linux/amd64/kubectl" -o /usr/local/bin/kubectl + chmod +x /usr/local/bin/kubectl + + BUCKET_ID=$(influx bucket list --name "$INFLUX_BUCKET" --json | jq -r '.[0].id') + [ -n "$BUCKET_ID" ] && [ "$BUCKET_ID" != "null" ] || { + log "ERROR: bucket $INFLUX_BUCKET not found" + exit 1 + } + log "bucket $INFLUX_BUCKET id=$BUCKET_ID" + + EXISTING=$(influx auth list --json \ + | jq -r --arg d "$TOKEN_DESC" '.[] | select(.description == $d) | .token' \ + | head -1) + if [ -n "$EXISTING" ]; then + TOKEN="$EXISTING" + log "reusing existing auth ($TOKEN_DESC)" + else + # Read-only on the bucket. Grafana queries via Flux which + # only reads. + TOKEN=$(influx auth create \ + --org "$INFLUX_ORG" \ + --description "$TOKEN_DESC" \ + --read-bucket "$BUCKET_ID" \ + --json | jq -r '.token') + [ -n "$TOKEN" ] && [ "$TOKEN" != "null" ] || { + log "ERROR: influx auth create produced empty token" + exit 1 + } + log "created new auth ($TOKEN_DESC)" + fi + + # K8s Grafana's envFromSecret reads the key as + # INFLUXDB_TOKEN (set in the chart values); use the same + # key name so a single Secret-rotate refreshes the + # template var. + log "writing $TARGET_NS/$TARGET_SECRET via apply" + kubectl -n "$TARGET_NS" create secret generic "$TARGET_SECRET" \ + --from-literal=INFLUXDB_TOKEN="$TOKEN" \ + --dry-run=client -o yaml \ + | kubectl apply -f - + + log "Done." diff --git a/kubernetes/seed/influxdb/kustomization.yaml b/kubernetes/seed/influxdb/kustomization.yaml index 758e2d45..e78daa63 100644 --- a/kubernetes/seed/influxdb/kustomization.yaml +++ b/kubernetes/seed/influxdb/kustomization.yaml @@ -4,3 +4,4 @@ resources: - bootstrap-secret.sops.yaml - helmrelease.yaml - ha-token-job.yaml + - grafana-token-job.yaml