From 7a62489ad6aab358500783e7c2f17d7dcf3d3bb8 Mon Sep 17 00:00:00 2001 From: Nikholas Pcenicni <82239765+nikpcenicni@users.noreply.github.com> Date: Sat, 28 Mar 2026 16:38:47 -0400 Subject: [PATCH] Enhance noble_landing_urls role by adding support for generating a Headlamp ServiceAccount token with a configurable duration. Update documentation to reflect changes in the markdown output for Headlamp sign-in. Modify fetch_credentials task to include token generation alongside existing credential fetching. These updates improve the usability and security of the Headlamp integration. --- .../noble_landing_urls/defaults/main.yml | 5 ++++- .../tasks/fetch_credentials.yml | 19 ++++++++++++++++++- .../templates/noble-lab-ui-urls.md.j2 | 3 ++- clusters/noble/apps/headlamp/README.md | 17 +++++++++++++++++ 4 files changed, 41 insertions(+), 3 deletions(-) diff --git a/ansible/roles/noble_landing_urls/defaults/main.yml b/ansible/roles/noble_landing_urls/defaults/main.yml index 84c1fe8..9123f73 100644 --- a/ansible/roles/noble_landing_urls/defaults/main.yml +++ b/ansible/roles/noble_landing_urls/defaults/main.yml @@ -2,9 +2,12 @@ # Regenerated when **noble_landing_urls** runs (after platform stack). Paths match Traefik + cert-manager Ingresses. noble_landing_urls_dest: "{{ noble_repo_root }}/ansible/output/noble-lab-ui-urls.md" -# When true, run kubectl against the cluster to fill Argo CD / Grafana passwords in the markdown (requires working kubeconfig). +# When true, run kubectl to fill Argo CD / Grafana secrets and a bounded Headlamp SA token in the markdown (requires working kubeconfig). noble_landing_urls_fetch_credentials: true +# Headlamp: bounded token for UI sign-in (`kubectl create token`); cluster may cap max duration. +noble_landing_urls_headlamp_token_duration: 48h + noble_lab_ui_entries: - name: Argo CD description: GitOps UI (sync, apps, repos) diff --git a/ansible/roles/noble_landing_urls/tasks/fetch_credentials.yml b/ansible/roles/noble_landing_urls/tasks/fetch_credentials.yml index 8bfa4f4..acbdfee 100644 --- a/ansible/roles/noble_landing_urls/tasks/fetch_credentials.yml +++ b/ansible/roles/noble_landing_urls/tasks/fetch_credentials.yml @@ -1,5 +1,5 @@ --- -# Populates template variables from Secrets (no_log on kubectl to avoid leaking into Ansible stdout). +# Populates template variables from Secrets + Headlamp token (no_log on kubectl to avoid leaking into Ansible stdout). - name: Fetch Argo CD initial admin password (base64) ansible.builtin.command: argv: @@ -53,3 +53,20 @@ failed_when: false changed_when: false no_log: true + +- name: Create Headlamp ServiceAccount token (for UI sign-in) + ansible.builtin.command: + argv: + - kubectl + - -n + - headlamp + - create + - token + - headlamp + - "--duration={{ noble_landing_urls_headlamp_token_duration | default('48h') }}" + environment: + KUBECONFIG: "{{ noble_kubeconfig }}" + register: noble_fetch_headlamp_token + failed_when: false + changed_when: false + no_log: true diff --git a/ansible/roles/noble_landing_urls/templates/noble-lab-ui-urls.md.j2 b/ansible/roles/noble_landing_urls/templates/noble-lab-ui-urls.md.j2 index e7ca91f..ca22969 100644 --- a/ansible/roles/noble_landing_urls/templates/noble-lab-ui-urls.md.j2 +++ b/ansible/roles/noble_landing_urls/templates/noble-lab-ui-urls.md.j2 @@ -20,7 +20,7 @@ This file is **generated** by Ansible (`noble_landing_urls` role). Use it as a t |-----|---------------------|-------------------| | **Argo CD** | `admin` | {% if (noble_fetch_argocd_pw_b64 is defined) and (noble_fetch_argocd_pw_b64.rc | default(1) == 0) and (noble_fetch_argocd_pw_b64.stdout | default('') | length > 0) %}`{{ noble_fetch_argocd_pw_b64.stdout | b64decode }}`{% else %}*(not fetched — use commands below)*{% endif %} | | **Grafana** | {% if (noble_fetch_grafana_user_b64 is defined) and (noble_fetch_grafana_user_b64.rc | default(1) == 0) and (noble_fetch_grafana_user_b64.stdout | default('') | length > 0) %}`{{ noble_fetch_grafana_user_b64.stdout | b64decode }}`{% else %}*(from Secret — use commands below)*{% endif %} | {% if (noble_fetch_grafana_pw_b64 is defined) and (noble_fetch_grafana_pw_b64.rc | default(1) == 0) and (noble_fetch_grafana_pw_b64.stdout | default('') | length > 0) %}`{{ noble_fetch_grafana_pw_b64.stdout | b64decode }}`{% else %}*(not fetched — use commands below)*{% endif %} | -| **Headlamp** | ServiceAccount token | No fixed password. Sign in with a SA token, or configure OIDC — `clusters/noble/apps/headlamp/README.md`. | +| **Headlamp** | ServiceAccount **`headlamp`** | {% if (noble_fetch_headlamp_token is defined) and (noble_fetch_headlamp_token.rc | default(1) == 0) and (noble_fetch_headlamp_token.stdout | default('') | trim | length > 0) %}Token ({{ noble_landing_urls_headlamp_token_duration | default('48h') }}): `{{ noble_fetch_headlamp_token.stdout | trim }}`{% else %}*(not generated — use command below)*{% endif %} | | **Prometheus** | — | No auth in default install (lab). | | **Alertmanager** | — | No auth in default install (lab). | | **Longhorn** | — | No default login unless you enable access control in the UI settings. | @@ -48,3 +48,4 @@ To generate this file **without** calling kubectl, run Ansible with **`-e noble_ - **Grafana** password is random unless you set `grafana.adminPassword` in chart values. - **Vault** UI needs **unsealed** Vault; tokens come from your chosen auth method. - **Prometheus / Alertmanager** UIs are unauthenticated by default — restrict when hardening (`talos/CLUSTER-BUILD.md` Phase G). +- **Headlamp** token above expires after the configured duration; re-run Ansible or `kubectl create token` to refresh. diff --git a/clusters/noble/apps/headlamp/README.md b/clusters/noble/apps/headlamp/README.md index f5e6bea..14d42dc 100644 --- a/clusters/noble/apps/headlamp/README.md +++ b/clusters/noble/apps/headlamp/README.md @@ -16,3 +16,20 @@ helm upgrade --install headlamp headlamp/headlamp -n headlamp \ ``` Sign-in uses a **ServiceAccount token** (Headlamp docs: create a limited SA for day-to-day use). This repo binds the Headlamp workload SA to the built-in **`edit`** ClusterRole (**`clusterRoleBinding.clusterRoleName: edit`** in **`values.yaml`**) — not **`cluster-admin`**. For cluster-scoped admin work, use **`kubectl`** with your admin kubeconfig. Optional **OIDC** in **`config.oidc`** replaces token login for SSO. + +## Sign-in token (ServiceAccount `headlamp`) + +Use a short-lived token (Kubernetes **1.24+**; requires permission to create **TokenRequests**): + +```bash +export KUBECONFIG=/path/to/talos/kubeconfig # or your admin kubeconfig +kubectl -n headlamp create token headlamp --duration=48h +``` + +Paste the printed JWT into Headlamp’s token field at **`https://headlamp.apps.noble.lab.pcenicni.dev`**. + +To use another duration (cluster `spec.serviceAccount` / admission limits may cap it): + +```bash +kubectl -n headlamp create token headlamp --duration=8760h +```