From 032ffee86605a7d2ad95bd3baad090c77ee283c1 Mon Sep 17 00:00:00 2001 From: Nikholas Pcenicni <82239765+nikpcenicni@users.noreply.github.com> Date: Thu, 14 May 2026 19:58:56 -0400 Subject: [PATCH] Enable Authentik installation and add support for extra public hostnames in the configuration. Updated README and values files to reflect changes for improved deployment flexibility and documentation clarity. --- ansible/inventory/group_vars/all.yml | 5 +++- ansible/roles/noble_authentik/README.md | 12 ++++++++ .../roles/noble_authentik/defaults/main.yml | 5 ++++ .../templates/authentik-extra-values.yaml.j2 | 18 ++++++++++++ .../noble/bootstrap/authentik/values.yaml | 28 ++++++++++++++++++- clusters/noble/bootstrap/newt/README.md | 18 ++++++++++++ 6 files changed, 84 insertions(+), 2 deletions(-) diff --git a/ansible/inventory/group_vars/all.yml b/ansible/inventory/group_vars/all.yml index 4e55bc6..6a8d9d1 100644 --- a/ansible/inventory/group_vars/all.yml +++ b/ansible/inventory/group_vars/all.yml @@ -14,7 +14,7 @@ noble_k8s_api_server_fallback: "https://192.168.50.20:6443" noble_skip_k8s_health_check: false # Pangolin / Newt — set true only after newt-pangolin-auth Secret exists (SOPS: clusters/noble/secrets/ or imperative — see clusters/noble/bootstrap/newt/README.md) -noble_newt_install: false +noble_newt_install: true # cert-manager needs Secret cloudflare-dns-api-token in cert-manager namespace before ClusterIssuers work noble_cert_manager_require_cloudflare_secret: true @@ -27,3 +27,6 @@ noble_argocd_apply_bootstrap_root_application: true # Authentik (OIDC IdP) + oauth2-proxy ForwardAuth — set **true** after **.env** has NOBLE_AUTHENTIK_* (see ansible/roles/noble_authentik/README.md). noble_authentik_install: true +# Optional: public (or extra) Authentik hostnames on the same IdP — list of FQDNs. Pangolin: CNAME + resource → Newt → Traefik (see noble_authentik README). +noble_authentik_ingress_extra_hosts: + - auth.nikflix.ca diff --git a/ansible/roles/noble_authentik/README.md b/ansible/roles/noble_authentik/README.md index f8e32e3..ebda5be 100644 --- a/ansible/roles/noble_authentik/README.md +++ b/ansible/roles/noble_authentik/README.md @@ -14,6 +14,18 @@ Installs **Authentik** (Helm `goauthentik/authentik`) as the cluster IdP, **oaut See **`defaults/main.yml`**. Hostnames default to **`auth.apps.noble.lab.pcenicni.dev`** and **`oauth2.apps.noble.lab.pcenicni.dev`**. **`noble_authentik_ensure_admin_ui_access`** (default **true**) re-applies **authentik Admins** superuser membership via the worker on each **`--tags authentik`** run so the admin UI keeps working under **2026+** RBAC. +### Extra public hostname (Pangolin + Newt, same Authentik) + +To expose the **same** Authentik instance on an **internet-facing** FQDN (while keeping the lab name on Traefik), set **`noble_authentik_ingress_extra_hosts`** in **`ansible/inventory/group_vars/all.yml`** (or **`-e`**) to a list of extra FQDNs, for example **`auth.example.com`**. Re-run **`ansible-playbook playbooks/noble.yml --tags authentik`**. Ansible extends **`server.ingress.hosts`** and **`tls[0].hosts`** so **cert-manager** issues one certificate with SANs for the primary **`noble_authentik_host`** plus those names (DNS must resolve for your issuer — often **Cloudflare** for public names, split horizon for lab). + +Then in **Pangolin**: link the domain, create an **HTTP** resource for that hostname, and set the **target** to your **Newt** site with **`ip:port`** pointing at the cluster **Traefik** HTTPS entry (same pattern as **`clusters/noble/bootstrap/newt/README.md`** — typically the MetalLB / LAN VIP and **443**). One Newt tunnel can front many hostnames. + +In **Authentik**, add a **Brand** (or equivalent) for the new hostname if you want different titles/favicon; OAuth **redirect URIs** for each app must include issuer URLs that match what browsers use (often you keep **internal** issuer URLs in cluster apps and use the public URL only for human login, or align all apps to the public issuer — pick one strategy to avoid mixed **`iss`** / callback mismatches). + +### “Secondary tenant” (separate PostgreSQL schema — alpha) + +Authentik **tenancy** (multiple isolated tenants in one deployment, **`AUTHENTIK_TENANTS__ENABLED`**) is **alpha**, requires **per-tenant Enterprise licensing**, **`AUTHENTIK_TENANTS__API_KEY`**, and **`AUTHENTIK_OUTPOSTS__DISABLE_EMBEDDED_OUTPOST=true`** (embedded outposts are unsupported with tenancy). It is **not** wired in this repo by default. See [Tenancy](https://docs.goauthentik.io/sys-mgmt/tenancy). For most homelabs, **one tenant** plus **`noble_authentik_ingress_extra_hosts`** is the right split. + ## IdP configuration When **`noble_authentik_configure_idp`** is true, Ansible creates/updates OAuth2 providers and applications for **argocd**, **grafana**, **headlamp**, and **oauth2-proxy** using either the **worker ORM path** (default **`noble_authentik_oidc_provision_via: worker`**: **`kubectl exec`** + **`ak shell`** + **`files/worker_upsert_oauth_oidc.py`**, which avoids **2026+** REST **403** on **`GET …/providers/oauth2/**`) or the **REST-only path** (**`noble_authentik_oidc_provision_via: rest`**: **`files/configure_authentik.py`** needs a token that can list/patch OAuth2 providers). With the worker path and a bootstrap email, it also runs **`files/worker_add_bootstrap_user_groups.py`** so **`User.groups.add`** does not depend on **`GET …/core/users/**`. It then runs **`configure_authentik.py`** with **`AUTHENTIK_SKIP_OIDC_REST`** / **`AUTHENTIK_SKIP_USER_GROUP_REST`** when those worker steps ran, so the script only calls **`ensure_group`** over the API (skipped when **`AUTHENTIK_NOBLE_*_GROUP_PK`** are set). diff --git a/ansible/roles/noble_authentik/defaults/main.yml b/ansible/roles/noble_authentik/defaults/main.yml index 2762406..e012969 100644 --- a/ansible/roles/noble_authentik/defaults/main.yml +++ b/ansible/roles/noble_authentik/defaults/main.yml @@ -21,6 +21,11 @@ noble_authentik_host: auth.apps.noble.lab.pcenicni.dev noble_authentik_public_url: "https://{{ noble_authentik_host }}" noble_authentik_api_base: "{{ noble_authentik_public_url }}/api/v3" +# Optional extra Ingress hostnames (FQDN strings) for the **same** Authentik release — e.g. a **public** name +# (Pangolin HTTP resource → Newt site → Traefik) while **`noble_authentik_host`** stays the in-lab name. +# Ansible merges these into **server.ingress.hosts** / **tls** (one cert Secret with multiple SANs). +noble_authentik_ingress_extra_hosts: [] + noble_authentik_oauth2_proxy_host: oauth2.apps.noble.lab.pcenicni.dev # OIDC client ids (must match Authentik providers created by configure script) diff --git a/ansible/roles/noble_authentik/templates/authentik-extra-values.yaml.j2 b/ansible/roles/noble_authentik/templates/authentik-extra-values.yaml.j2 index 9318791..c6c12f1 100644 --- a/ansible/roles/noble_authentik/templates/authentik-extra-values.yaml.j2 +++ b/ansible/roles/noble_authentik/templates/authentik-extra-values.yaml.j2 @@ -14,3 +14,21 @@ global: postgresql: auth: password: "{{ noble_authentik_postgresql_password }}" +{% if noble_authentik_ingress_extra_hosts | default([]) | length > 0 %} +# Extra SANs on the same Authentik server (e.g. public FQDN behind Pangolin → Newt → Traefik). Helm’s last -f +# replaces **server.ingress.hosts** / **tls[0].hosts**; primary lab host stays first. +server: + ingress: + hosts: + - {{ noble_authentik_host }} +{% for h in noble_authentik_ingress_extra_hosts %} + - {{ h }} +{% endfor %} + tls: + - secretName: authentik-apps-noble-tls + hosts: + - {{ noble_authentik_host }} +{% for h in noble_authentik_ingress_extra_hosts %} + - {{ h }} +{% endfor %} +{% endif %} diff --git a/clusters/noble/bootstrap/authentik/values.yaml b/clusters/noble/bootstrap/authentik/values.yaml index 5926f2b..5493ede 100644 --- a/clusters/noble/bootstrap/authentik/values.yaml +++ b/clusters/noble/bootstrap/authentik/values.yaml @@ -3,12 +3,38 @@ # Secrets (secret_key, postgres password, bootstrap) are supplied at install time by Ansible # (-f authentik-extra-values.yaml from noble_authentik role). Do not commit real secrets here. # -# DNS: auth.apps.noble.lab.pcenicni.dev → Traefik LB (see traefik/values.yaml). +# DNS: auth.apps.noble.lab.pcenicni.dev → Traefik LB (see traefik/values.yaml). Optional **extra** Ingress hostnames +# (e.g. a public Pangolin FQDN) are merged by Ansible — **`noble_authentik_ingress_extra_hosts`** in **group_vars** (see **noble_authentik** README). # # helm repo add goauthentik https://charts.goauthentik.io && helm repo update # kubectl apply -f clusters/noble/bootstrap/authentik/namespace.yaml # helm upgrade --install authentik goauthentik/authentik -n authentik --create-namespace \ # --version 2026.2.3 -f clusters/noble/bootstrap/authentik/values.yaml -f /path/to/extra.yaml --wait +# +# **Media / uploads:** server + worker mount **PVC `authentik-data`** at **`/data`** (Authentik stores media under **`/data/media`**). Chart **`additionalObjects`** creates the PVC (**Longhorn**, RWO). Increase **storage** or use another **storageClassName** if needed. + +global: + volumes: + - name: authentik-data + persistentVolumeClaim: + claimName: authentik-data + volumeMounts: + - name: authentik-data + mountPath: /data + +additionalObjects: + - apiVersion: v1 + kind: PersistentVolumeClaim + metadata: + name: authentik-data + namespace: "{{ .Release.Namespace }}" + spec: + accessModes: + - ReadWriteOnce + storageClassName: longhorn + resources: + requests: + storage: 10Gi postgresql: enabled: true diff --git a/clusters/noble/bootstrap/newt/README.md b/clusters/noble/bootstrap/newt/README.md index b4cc011..db5e5c3 100644 --- a/clusters/noble/bootstrap/newt/README.md +++ b/clusters/noble/bootstrap/newt/README.md @@ -88,6 +88,24 @@ curl -sS -X PUT -H "Authorization: Bearer ${TOKEN}" -H 'Content-Type: applicatio Exact JSON fields and IDs differ by domain type (**ns** vs **cname** vs **wildcard**); see [Common API routes](https://docs.pangolin.net/manage/common-api-routes) and Swagger. +### Authentik on a public name + +Use **`noble_authentik_ingress_extra_hosts`** (see **`ansible/roles/noble_authentik/README.md`**) so the Authentik Ingress (and **cert-manager** SANs) include your public FQDN, then create the Pangolin **HTTP** resource + **target** to the same Traefik **:443** endpoint as other apps. One Newt site can carry many hostnames. + +### What to put in Pangolin (resource + target) + +1. **Public hostname** — the FQDN users type in the browser (must match **`noble_authentik_ingress_extra_hosts`** and your **CNAME** at the DNS host Pangolin documents for that domain). +2. **Site** — the Pangolin **site** that owns your **Newt** pair (same **`NEWT_ID`** / **`NEWT_SECRET`** as the cluster Secret). In the UI: **Sites** → pick the site connected to this cluster. +3. **Target `ip`** — an address **reachable from inside the tunnel** to **Traefik HTTPS**. On noble this is usually the Traefik **LoadBalancer** IP (repo pins **`192.168.50.211`** in **`clusters/noble/bootstrap/traefik/values.yaml`**). Confirm live: + + `kubectl -n traefik get svc -l app.kubernetes.io/name=traefik -o wide` + + Use **`EXTERNAL-IP`** (or **`LOAD_BALANCER_IP`** from the Service status) as **`ip`**. If Newt runs **in** the cluster, that MetalLB/LAN VIP is correct; if you run Newt elsewhere, use whatever L3 path reaches Traefik from that host. +4. **Target `port`** — **`443`** (TLS to Traefik; SNI carries the public hostname). +5. **Target `method`** — **`http`** in the Integration API examples above (TLS is still terminated at Traefik; Pangolin’s field names follow their docs). + +Discovery in Pangolin’s UI: **Domains** (see required CNAME) → **Resources** → **Add** HTTP resource for the subdomain/FQDN → **Targets** / **Backends** → attach **site** + **ip:port**. Official flow: [Domains](https://docs.pangolin.net/manage/common-api-routes#list-domains), [Integration API](https://docs.pangolin.net/manage/integration-api), and your deployment’s **Swagger** at **`https:///v1/docs`** when enabled. + ## LAN vs internet - **LAN / VPN:** point **`*.apps.noble.lab.pcenicni.dev`** at the Traefik **LoadBalancer** (**`192.168.50.211`**) with local or split-horizon DNS if you want direct in-lab access.