diff --git a/k8s/homepage/README.md b/k8s/homepage/README.md index f935d3b..e8616f9 100644 --- a/k8s/homepage/README.md +++ b/k8s/homepage/README.md @@ -129,13 +129,13 @@ so forward-auth can't block the widget's API calls. `homepage-secrets` is managed end-to-end by ESO + BWS as of #135 Task 8: - Values live in BWS (Bitwarden Secrets Manager) → `homelab` project → - one entry per `homepage-secrets` key (`octoprint-api-key`, - `grafana-user`, `grafana-password`). + one entry per `homepage-secrets` key (currently just + `octoprint-api-key`). - `external-secret.yaml` (Flux-owned via `clusters/gandalf/homepage.yaml`) declares the keys + their BWS secret-IDs; ESO syncs the in-cluster `homepage-secrets` Secret from BWS every `refreshInterval`. -- The Bitwarden password vault item (`Homelab OctoPrint`, - `Homelab Grafana`) stays populated as a DR mirror -- same pattern as +- The Bitwarden password vault item (`Homelab OctoPrint`) stays + populated as a DR mirror -- same pattern as `Homelab Restic Repository`. - No `kubectl create secret` step. Adding a new widget secret means: (a) put the value in BWS in the `homelab` project, then @@ -168,6 +168,23 @@ so forward-auth can't block the widget's API calls. The widget points at `http://192.168.50.118:5000` directly (Homepage pod → LAN), so it works regardless of the public Ingress. +**Grafana** is a **link tile, not a live widget** (#155). +Homepage's `grafana` widget makes a mandatory `GET /api/admin/stats` call, +and that endpoint is gated by Grafana *server admin*. +Grafana OSS can't grant server-admin to a service account: +the `isGrafanaAdmin` flag doesn't persist on a SA, +an org-`Admin` SA token still gets 403, +and the RBAC role-assignment API (`fixed:*:reader`) is Enterprise-only. +The only credential that satisfies the widget is a server-admin *user*, +which would put root-on-Grafana in the Homepage pod for a counts tile -- +below the least-privilege bar we hold every other widget to. +A Viewer-scoped SA token *can* read the underlying counts via non-admin +endpoints (`/api/search?type=dash-db`, `/api/datasources`, +`/api/alertmanager/grafana/api/v2/alerts`), +but only Homepage's one-endpoint-per-tile `customapi` widget can consume them, +which doesn't reproduce the built-in 4-stat layout. +Not worth the complexity for low-value counts, so Grafana stays a plain href. + ## External bookmarks The `config.bookmarks` block in `values.yaml` is a quick-launch dashboard for diff --git a/k8s/homepage/external-secret.yaml b/k8s/homepage/external-secret.yaml index edff3c2..291799e 100644 --- a/k8s/homepage/external-secret.yaml +++ b/k8s/homepage/external-secret.yaml @@ -29,9 +29,3 @@ spec: - secretKey: octoprint-api-key remoteRef: key: 413ae347-cc2e-43c0-8964-b45a016c6463 # gitleaks:allow - - secretKey: grafana-user - remoteRef: - key: df2f3ffd-9ae9-4f2a-b2d0-b45a016c6909 # gitleaks:allow - - secretKey: grafana-password - remoteRef: - key: 36d29c85-7184-4cd8-9041-b45a016c6da8 # gitleaks:allow diff --git a/k8s/homepage/secret.example.yaml b/k8s/homepage/secret.example.yaml index fdf0eeb..3d73782 100644 --- a/k8s/homepage/secret.example.yaml +++ b/k8s/homepage/secret.example.yaml @@ -17,10 +17,6 @@ stringData: # Generated in OctoPrint: Settings -> Application Keys (or a user API key). # Surfaced as HOMEPAGE_VAR_OCTOPRINT_API_KEY for the octoprint widget. octoprint-api-key: REPLACE_WITH_OCTOPRINT_API_KEY - - # Grafana widget basic-auth creds — a dedicated Viewer-role Grafana login - # (Homepage's grafana widget uses basic auth, not an API token). Bitwarden - # item 'Homelab Grafana', fields 'homepage-user' + 'homepage-password'. - # Surfaced as HOMEPAGE_VAR_GRAFANA_USER / HOMEPAGE_VAR_GRAFANA_PASSWORD. - grafana-user: REPLACE_WITH_GRAFANA_HOMEPAGE_USER - grafana-password: REPLACE_WITH_GRAFANA_HOMEPAGE_PASSWORD + # Grafana has no entry here: its Homepage tile is a plain link, not a live + # widget. The widget needs server-admin (GET /api/admin/stats), which Grafana + # OSS can't scope to a service account. See README "Service widgets" and #155. diff --git a/k8s/homepage/values.yaml b/k8s/homepage/values.yaml index d99ee1c..454f7c5 100644 --- a/k8s/homepage/values.yaml +++ b/k8s/homepage/values.yaml @@ -73,19 +73,6 @@ env: secretKeyRef: name: homepage-secrets key: octoprint-api-key - # Grafana widget creds — a dedicated Viewer-role Grafana login (Homepage's - # grafana widget uses basic auth, not an API token). From homepage-secrets - # (Bitwarden 'Homelab Grafana' / homepage-user + homepage-password). - - name: HOMEPAGE_VAR_GRAFANA_USER - valueFrom: - secretKeyRef: - name: homepage-secrets - key: grafana-user - - name: HOMEPAGE_VAR_GRAFANA_PASSWORD - valueFrom: - secretKeyRef: - name: homepage-secrets - key: grafana-password config: # Hand-curated initial list pointing at current *.home services. @@ -135,14 +122,14 @@ config: href: https://grafana.vigihome.net description: Dashboards & metrics icon: grafana.png - # Live widget — hits Grafana's in-cluster Service directly (not the - # Authentik-gated Ingress) using a dedicated Viewer login. Creds via - # HOMEPAGE_VAR_GRAFANA_USER/PASSWORD (homepage-secrets). See README. - widget: - type: grafana - url: http://kps-grafana.monitoring.svc.cluster.local - username: "{{HOMEPAGE_VAR_GRAFANA_USER}}" - password: "{{HOMEPAGE_VAR_GRAFANA_PASSWORD}}" + # Link tile, not a live widget. Homepage's grafana widget requires + # GET /api/admin/stats, which is gated by Grafana *server admin*. + # Grafana OSS can't grant server-admin to a service account + # (isGrafanaAdmin doesn't persist on a SA; org-Admin still 403s; the + # RBAC role-assignment API is Enterprise-only). The only credential + # that satisfies the widget is a server-admin user — handing the + # Homepage pod root-on-Grafana for a counts tile, which fails the + # least-privilege bar. So Grafana stays a plain href here. See #155. widgets: - kubernetes: diff --git a/k8s/kube-prometheus-stack/README.md b/k8s/kube-prometheus-stack/README.md index 8606ed8..cfe71ce 100644 --- a/k8s/kube-prometheus-stack/README.md +++ b/k8s/kube-prometheus-stack/README.md @@ -51,7 +51,6 @@ Chart: `prometheus-community/kube-prometheus-stack` v`85.3.3` - `oidc-client-secret` — `openssl rand -hex 64` - `admin-user` — e.g. `admin-local` (the local-admin fallback) - `admin-password` — a strong password - - `homepage-user` / `homepage-password` — added in step 7 for the widget ## One-time install @@ -137,26 +136,12 @@ prerequisites (secrets, PV, blueprint, reflected SMTP) must exist **before** minutes. If the exact Service/StatefulSet names differ by release, confirm with `kubectl -n monitoring get svc,sts` and adjust the targets below. -7. **Add the Homepage Grafana widget creds.** Create a dedicated - **Viewer**-role Grafana login (Grafana UI → Administration → Users → - add `homepage`), store it in Bitwarden vault item `Homelab Grafana` - under custom fields `homepage-user` and `homepage-password`, then - put the same two values in BWS (Secrets Manager → `homelab` project, - secret names `grafana-user` and `grafana-password`). ESO + the - Homepage Flux Kustomization handle the in-cluster `homepage-secrets` - Secret from there -- no `kubectl create secret` step. See - `../homepage/README.md` for the BWS migration script. - - Then `helm upgrade` so the new Grafana widget config in - `k8s/homepage/values.yaml` loads: - - ```sh - helm upgrade homepage jameswynn/homepage -n homepage --version 2.1.0 -f k8s/homepage/values.yaml - ``` - - Issue #155 tracks switching this Grafana login to a service-account + - bearer-token model; once that lands, `grafana-user`/`grafana-password` - in BWS get replaced with a single `grafana-token` entry. +7. **No Homepage-widget step.** Grafana appears on Homepage as a **plain + link tile**, not a live widget. The widget needs `GET /api/admin/stats`, + which Grafana *server admin* gates, and Grafana OSS can't scope that to a + service account — handing the Homepage pod a server-admin user just for a + counts tile fails the least-privilege bar. Closed in #155; rationale in + `../homepage/README.md` "Service widgets". ## Verify