Enhance Authentik and Newt configurations to support Open WebUI integration. Add necessary environment variables and secrets management for Open WebUI in .env.sample and Ansible tasks. Update README to clarify setup steps for automating HTTP resources with Pangolin, ensuring consistency with new branding and deployment practices.
This commit is contained in:
@@ -30,4 +30,6 @@ 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
|
||||
# Open WebUI — public Ingress host (Pangolin → Newt → Traefik). Must match **clusters/noble/apps/open-webui/values.yaml** **ingress.host** and Authentik redirect URI (Ansible uses this var).
|
||||
noble_open_webui_public_host: webui.nikflix.ca
|
||||
noble_authentik_blueprints_enabled: true
|
||||
@@ -225,7 +225,7 @@
|
||||
- role: noble_cert_manager
|
||||
tags: [cert_manager, certs]
|
||||
- role: noble_newt
|
||||
tags: [newt]
|
||||
tags: [newt, pangolin]
|
||||
- role: noble_argocd
|
||||
tags: [argocd, gitops]
|
||||
- role: noble_platform
|
||||
|
||||
@@ -28,6 +28,8 @@ To expose the **same** Authentik instance on an **internet-facing** FQDN (while
|
||||
|
||||
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.
|
||||
|
||||
**Open WebUI (public hostname only):** Set **`noble_open_webui_public_host`** in **`group_vars`** to the same FQDN as **`clusters/noble/apps/open-webui/values.yaml`** **`ingress.host`** (for example **`webui.example.com`**). Add a **Pangolin** HTTP resource for that hostname to the same Newt target. Point **`OPENID_PROVIDER_URL`** in **`values.yaml`** at your **public** Authentik host (usually the first **`noble_authentik_ingress_extra_hosts`** entry, e.g. **`https://auth.example.com/application/o/open-webui/.well-known/openid-configuration`**), and set **`OPENID_REDIRECT_URI`** / **`WEBUI_URL`** to **`https://<noble_open_webui_public_host>/…`**. After changing **`noble_open_webui_public_host`**, re-run **`ansible-playbook playbooks/noble.yml --tags authentik`** so Authentik’s OAuth2 redirect URI matches.
|
||||
|
||||
### Split routing, two Brands, and optional blueprints
|
||||
|
||||
This role supports a **single Authentik deployment** with **two hostnames** (lab + public) and **different Brands** per **`Host`**, without Authentik’s separate-database **Tenancy** feature (see [Tenancy](https://docs.goauthentik.io/sys-mgmt/tenancy) — alpha / licensing). [Brands](https://docs.goauthentik.io/brands/) choose default **authentication** (and related) **flows** and branding for each FQDN.
|
||||
@@ -110,7 +112,7 @@ Authentik **tenancy** (multiple isolated tenants in one deployment, **`AUTHENTIK
|
||||
|
||||
## 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).
|
||||
When **`noble_authentik_configure_idp`** is true, Ansible creates/updates OAuth2 providers and applications for **argocd**, **grafana**, **headlamp**, **open-webui**, 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).
|
||||
|
||||
## RBAC notes
|
||||
|
||||
@@ -136,7 +138,7 @@ When **`noble_authentik_configure_idp`** is true, Ansible creates/updates OAuth2
|
||||
- **Headlamp OIDC authorize fails / `invalid_scope`**: Authentik often has no separate **`groups`** ScopeMapping (groups live under **`profile`**). Default **`noble_authentik_headlamp_oidc_scopes`** omits **`groups`**; add a **`groups`** mapping to the provider in Authentik and set **`noble_authentik_headlamp_oidc_scopes`** to include **`groups`** if you need that scope by name.
|
||||
- **Headlamp OIDC: Authentik flashes then back at login / page refresh**: Headlamp **does** support normal browser OAuth (redirect to Authentik and return to **`/oidc-callback`**). If the callback fails, the UI looks like it “drops” auth. Common causes: **`X-Forwarded-Proto`** not reaching Headlamp (callback built as **`http`** — see [Headlamp OIDC docs](https://headlamp.dev/docs/latest/installation/in-cluster/oidc)); **Traefik ForwardAuth** on the same Ingress (do not combine with native OIDC); **PKCE** state issues — this role defaults **`noble_authentik_headlamp_oidc_use_pkce: false`** for Authentik confidential clients (set **`true`** in **`group_vars`** if you need PKCE).
|
||||
- **Headlamp UI: `/me` works but `/clusters/main/version` (and other K8s calls) return 401**: Headlamp forwards your **OIDC id_token** to **kube-apiserver**. The API server must be configured with **OIDC** flags for the same **issuer** and **`oidc-client-id`** as Headlamp (see **`talos/talconfig.yaml`** patch and [Kubernetes OIDC authentication](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#openid-connect-tokens)). Apply regenerated Talos configs to control plane nodes, then **`kubectl apply -k clusters/noble/bootstrap/headlamp`** (or **`--tags authentik`**) for **`oidc-noble-admins-clusterrolebinding.yaml`**. Ensure your user is in Authentik group **`noble-admins`** and the id_token includes a **`groups`** claim if you rely on that binding.
|
||||
- **Headlamp + Traefik ForwardAuth (`oauth2-proxy-forward-auth`)**: Do **not** put ForwardAuth on the **Headlamp** Ingress while using **native Headlamp OIDC**. Auth runs on **`/oidc-callback`** before Headlamp can finish the code exchange; ForwardAuth returns **401** and breaks login. Use **either** native OIDC (this repo’s **`values-authentik-oidc.yaml`**) **or** terminate auth at oauth2-proxy only (no **`config.oidc`**), not both.
|
||||
- **Headlamp + Traefik ForwardAuth (`oauth2-proxy-forward-auth`)**: Do **not** put ForwardAuth on the **Headlamp** Ingress while using **native Headlamp OIDC**. Auth runs on **`/oidc-callback`** before Headlamp can finish the code exchange; ForwardAuth returns **401** and breaks login. Use **either** native OIDC (this repo’s **`values-authentik-oidc.yaml`**) **or** terminate auth at oauth2-proxy only (no **`config.oidc`**), not both. The same applies to **Open WebUI** native OIDC (**`/oauth/oidc/callback`** in **`clusters/noble/apps/open-webui/values.yaml`**).
|
||||
|
||||
### Fix admin access manually (worker shell, no Ansible)
|
||||
|
||||
|
||||
@@ -122,6 +122,7 @@ noble_authentik_client_id_argocd: argocd
|
||||
noble_authentik_client_id_grafana: grafana
|
||||
noble_authentik_client_id_headlamp: headlamp
|
||||
noble_authentik_client_id_oauth2_proxy: oauth2-proxy
|
||||
noble_authentik_client_id_open_webui: open-webui
|
||||
|
||||
# Headlamp **OIDC_SCOPES** for Secret **headlamp-oidc**. Omit **groups** unless the Authentik OAuth2 provider
|
||||
# includes a separate **groups** ScopeMapping (2026.x defaults often embed groups in **profile** only; requesting
|
||||
@@ -143,8 +144,15 @@ noble_authentik_client_secret_argocd: ""
|
||||
noble_authentik_client_secret_grafana: ""
|
||||
noble_authentik_client_secret_headlamp: ""
|
||||
noble_authentik_client_secret_oauth2_proxy: ""
|
||||
noble_authentik_client_secret_open_webui: ""
|
||||
noble_authentik_oauth2_proxy_cookie_secret: ""
|
||||
|
||||
# **open-webui** namespace — Secret **open-webui-secrets** (Ansible **--tags authentik**). See **clusters/noble/apps/open-webui/values.yaml**.
|
||||
noble_open_webui_openai_api_key: ""
|
||||
noble_open_webui_webui_secret_key: ""
|
||||
# Public FQDN for Open WebUI (Ingress + OIDC **redirect_uri**). Set in **group_vars** (e.g. **webui.example.com**); must match GitOps **values.yaml** **ingress.host** and **OPENID_REDIRECT_URI** / **WEBUI_URL**.
|
||||
noble_open_webui_public_host: ""
|
||||
|
||||
# Optional: OAuth2 provider flow PKs (UUID strings). When **both** are set, **configure_authentik.py**
|
||||
# skips **GET /flows/instances/** (avoids 403 if the API token cannot view flows). If unset, the role
|
||||
# tries **kubectl exec** into **authentik-worker** + **ak shell** to read the same slugs from the DB.
|
||||
|
||||
@@ -195,6 +195,69 @@
|
||||
- (noble_authentik_cs_o2_from_env.stdout | default('') | trim | length) > 0
|
||||
no_log: true
|
||||
|
||||
- name: Load NOBLE_AUTHENTIK_CLIENT_SECRET_OPEN_WEBUI from .env when unset
|
||||
ansible.builtin.shell: |
|
||||
set -a
|
||||
. "{{ noble_repo_root }}/.env"
|
||||
set +a
|
||||
printf '%s' "${NOBLE_AUTHENTIK_CLIENT_SECRET_OPEN_WEBUI:-}"
|
||||
register: noble_authentik_cs_ow_from_env
|
||||
when:
|
||||
- noble_authentik_dotenv_stat.stat.exists | default(false)
|
||||
- noble_authentik_client_secret_open_webui | default('') | length == 0
|
||||
changed_when: false
|
||||
no_log: true
|
||||
|
||||
- name: Apply NOBLE_AUTHENTIK_CLIENT_SECRET_OPEN_WEBUI from .env
|
||||
ansible.builtin.set_fact:
|
||||
noble_authentik_client_secret_open_webui: "{{ noble_authentik_cs_ow_from_env.stdout | trim }}"
|
||||
when:
|
||||
- noble_authentik_cs_ow_from_env is defined
|
||||
- (noble_authentik_cs_ow_from_env.stdout | default('') | trim | length) > 0
|
||||
no_log: true
|
||||
|
||||
- name: Load NOBLE_OPEN_WEBUI_OPENAI_API_KEY from .env when unset
|
||||
ansible.builtin.shell: |
|
||||
set -a
|
||||
. "{{ noble_repo_root }}/.env"
|
||||
set +a
|
||||
printf '%s' "${NOBLE_OPEN_WEBUI_OPENAI_API_KEY:-}"
|
||||
register: noble_open_webui_openai_from_env
|
||||
when:
|
||||
- noble_authentik_dotenv_stat.stat.exists | default(false)
|
||||
- noble_open_webui_openai_api_key | default('') | length == 0
|
||||
changed_when: false
|
||||
no_log: true
|
||||
|
||||
- name: Apply NOBLE_OPEN_WEBUI_OPENAI_API_KEY from .env
|
||||
ansible.builtin.set_fact:
|
||||
noble_open_webui_openai_api_key: "{{ noble_open_webui_openai_from_env.stdout | trim }}"
|
||||
when:
|
||||
- noble_open_webui_openai_from_env is defined
|
||||
- (noble_open_webui_openai_from_env.stdout | default('') | trim | length) > 0
|
||||
no_log: true
|
||||
|
||||
- name: Load NOBLE_OPEN_WEBUI_WEBUI_SECRET_KEY from .env when unset
|
||||
ansible.builtin.shell: |
|
||||
set -a
|
||||
. "{{ noble_repo_root }}/.env"
|
||||
set +a
|
||||
printf '%s' "${NOBLE_OPEN_WEBUI_WEBUI_SECRET_KEY:-}"
|
||||
register: noble_open_webui_webui_secret_from_env
|
||||
when:
|
||||
- noble_authentik_dotenv_stat.stat.exists | default(false)
|
||||
- noble_open_webui_webui_secret_key | default('') | length == 0
|
||||
changed_when: false
|
||||
no_log: true
|
||||
|
||||
- name: Apply NOBLE_OPEN_WEBUI_WEBUI_SECRET_KEY from .env
|
||||
ansible.builtin.set_fact:
|
||||
noble_open_webui_webui_secret_key: "{{ noble_open_webui_webui_secret_from_env.stdout | trim }}"
|
||||
when:
|
||||
- noble_open_webui_webui_secret_from_env is defined
|
||||
- (noble_open_webui_webui_secret_from_env.stdout | default('') | trim | length) > 0
|
||||
no_log: true
|
||||
|
||||
- name: Load NOBLE_AUTHENTIK_OAUTH2_PROXY_COOKIE_SECRET from .env when unset
|
||||
ansible.builtin.shell: |
|
||||
set -a
|
||||
|
||||
@@ -22,9 +22,16 @@
|
||||
- noble_authentik_client_secret_grafana | default('') | length > 0
|
||||
- noble_authentik_client_secret_headlamp | default('') | length > 0
|
||||
- noble_authentik_client_secret_oauth2_proxy | default('') | length > 0
|
||||
- noble_authentik_client_secret_open_webui | default('') | length > 0
|
||||
- noble_authentik_oauth2_proxy_cookie_secret | default('') | length > 0
|
||||
- noble_open_webui_openai_api_key | default('') | length > 0
|
||||
- noble_open_webui_webui_secret_key | default('') | length > 0
|
||||
- noble_open_webui_public_host | default('') | trim | length > 0
|
||||
fail_msg: >-
|
||||
Authentik requires secrets in .env (see ansible/roles/noble_authentik/README.md) or matching -e extra-vars.
|
||||
Includes Open WebUI: NOBLE_AUTHENTIK_CLIENT_SECRET_OPEN_WEBUI, NOBLE_OPEN_WEBUI_OPENAI_API_KEY,
|
||||
NOBLE_OPEN_WEBUI_WEBUI_SECRET_KEY (see .env.sample). Set **noble_open_webui_public_host** (must match
|
||||
**clusters/noble/apps/open-webui/values.yaml** ingress host; see README Pangolin section).
|
||||
|
||||
- name: Require Authentik S3 media settings (same endpoint/keys as Velero; dedicated bucket)
|
||||
ansible.builtin.assert:
|
||||
@@ -566,6 +573,32 @@
|
||||
no_log: true
|
||||
changed_when: true
|
||||
|
||||
- name: Ensure open-webui namespace exists (Secret before Argo first sync)
|
||||
ansible.builtin.shell: |
|
||||
set -euo pipefail
|
||||
kubectl create namespace open-webui --dry-run=client -o yaml | kubectl apply -f -
|
||||
environment:
|
||||
KUBECONFIG: "{{ noble_kubeconfig }}"
|
||||
when: noble_authentik_configure_idp | default(true) | bool
|
||||
changed_when: true
|
||||
|
||||
- name: Create Open WebUI secrets (OpenAI + WEBUI + OIDC client secret)
|
||||
ansible.builtin.shell: |
|
||||
set -euo pipefail
|
||||
kubectl -n open-webui create secret generic open-webui-secrets \
|
||||
--from-literal=OPENAI_API_KEY="${OPENAI_API_KEY}" \
|
||||
--from-literal=WEBUI_SECRET_KEY="${WEBUI_SECRET_KEY}" \
|
||||
--from-literal=OAUTH_CLIENT_SECRET="${OAUTH_CLIENT_SECRET}" \
|
||||
--dry-run=client -o yaml | kubectl apply -f -
|
||||
environment:
|
||||
KUBECONFIG: "{{ noble_kubeconfig }}"
|
||||
OPENAI_API_KEY: "{{ noble_open_webui_openai_api_key }}"
|
||||
WEBUI_SECRET_KEY: "{{ noble_open_webui_webui_secret_key }}"
|
||||
OAUTH_CLIENT_SECRET: "{{ noble_authentik_client_secret_open_webui }}"
|
||||
no_log: true
|
||||
when: noble_authentik_configure_idp | default(true) | bool
|
||||
changed_when: true
|
||||
|
||||
- name: Create oauth2-proxy credentials Secret (OIDC to Authentik; not BasicAuth)
|
||||
ansible.builtin.shell: |
|
||||
set -euo pipefail
|
||||
|
||||
@@ -22,5 +22,11 @@
|
||||
"client_id": {{ noble_authentik_client_id_oauth2_proxy | to_json }},
|
||||
"client_secret": {{ noble_authentik_client_secret_oauth2_proxy | to_json }},
|
||||
"redirect_uri": "https://{{ noble_authentik_oauth2_proxy_host }}/oauth2/callback"
|
||||
},
|
||||
"open-webui": {
|
||||
"name": "Open WebUI",
|
||||
"client_id": {{ noble_authentik_client_id_open_webui | to_json }},
|
||||
"client_secret": {{ noble_authentik_client_secret_open_webui | to_json }},
|
||||
"redirect_uri": "https://{{ noble_open_webui_public_host | trim }}/oauth/oidc/callback"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,12 @@
|
||||
"client_id": {{ noble_authentik_client_id_oauth2_proxy | to_json }},
|
||||
"client_secret": {{ noble_authentik_client_secret_oauth2_proxy | to_json }},
|
||||
"redirect_uri": "https://{{ noble_authentik_oauth2_proxy_host }}/oauth2/callback"
|
||||
},
|
||||
"open-webui": {
|
||||
"name": "Open WebUI",
|
||||
"client_id": {{ noble_authentik_client_id_open_webui | to_json }},
|
||||
"client_secret": {{ noble_authentik_client_secret_open_webui | to_json }},
|
||||
"redirect_uri": "https://{{ noble_open_webui_public_host | trim }}/oauth/oidc/callback"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
---
|
||||
# Set true after creating the newt-pangolin-auth Secret (see role / cluster docs).
|
||||
noble_newt_install: true
|
||||
|
||||
# Pangolin Integration API — idempotent HTTP resources + Traefik targets (see clusters/noble/bootstrap/newt/README.md §4).
|
||||
noble_pangolin_sync_http_resources: false
|
||||
# Extra FQDNs to sync (in addition to **noble_authentik_ingress_extra_hosts** + **noble_open_webui_public_host** when set).
|
||||
noble_pangolin_http_fqdns_extra: []
|
||||
# Traefik HTTPS backend for Pangolin targets (MetalLB / LAN VIP). Empty → **kubectl** discovers the Traefik Service.
|
||||
noble_pangolin_traefik_target_ip: ""
|
||||
noble_pangolin_traefik_target_port: 443
|
||||
|
||||
@@ -3,40 +3,46 @@
|
||||
ansible.builtin.debug:
|
||||
msg: "noble_newt_install is false — set PANGOLIN_ENDPOINT, NEWT_ID, NEWT_SECRET in repo .env (or create the Secret manually) and set noble_newt_install=true to deploy Newt."
|
||||
when: not (noble_newt_install | bool)
|
||||
tags: [newt]
|
||||
|
||||
- name: Create Newt namespace
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- kubectl
|
||||
- apply
|
||||
- -f
|
||||
- "{{ noble_repo_root }}/clusters/noble/bootstrap/newt/namespace.yaml"
|
||||
environment:
|
||||
KUBECONFIG: "{{ noble_kubeconfig }}"
|
||||
- name: Deploy Newt (Pangolin tunnel) and optional Pangolin HTTP resource sync
|
||||
when: noble_newt_install | bool
|
||||
changed_when: true
|
||||
tags: [newt, pangolin]
|
||||
block:
|
||||
- name: Create Newt namespace
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- kubectl
|
||||
- apply
|
||||
- -f
|
||||
- "{{ noble_repo_root }}/clusters/noble/bootstrap/newt/namespace.yaml"
|
||||
environment:
|
||||
KUBECONFIG: "{{ noble_kubeconfig }}"
|
||||
changed_when: true
|
||||
|
||||
- name: Apply Newt Pangolin auth Secret from repository .env (optional)
|
||||
ansible.builtin.include_tasks: from_env.yml
|
||||
when: noble_newt_install | bool
|
||||
- name: Apply Newt Pangolin auth Secret from repository .env (optional)
|
||||
ansible.builtin.include_tasks: from_env.yml
|
||||
|
||||
- name: Install Newt chart
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- helm
|
||||
- upgrade
|
||||
- --install
|
||||
- newt
|
||||
- fossorial/newt
|
||||
- --namespace
|
||||
- newt
|
||||
- --version
|
||||
- "1.5.0"
|
||||
- -f
|
||||
- "{{ noble_repo_root }}/clusters/noble/bootstrap/newt/values.yaml"
|
||||
- --force-conflicts
|
||||
- --wait
|
||||
environment:
|
||||
KUBECONFIG: "{{ noble_kubeconfig }}"
|
||||
when: noble_newt_install | bool
|
||||
changed_when: true
|
||||
- name: Install Newt chart
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- helm
|
||||
- upgrade
|
||||
- --install
|
||||
- newt
|
||||
- fossorial/newt
|
||||
- --namespace
|
||||
- newt
|
||||
- --version
|
||||
- "1.5.0"
|
||||
- -f
|
||||
- "{{ noble_repo_root }}/clusters/noble/bootstrap/newt/values.yaml"
|
||||
- --force-conflicts
|
||||
- --wait
|
||||
environment:
|
||||
KUBECONFIG: "{{ noble_kubeconfig }}"
|
||||
changed_when: true
|
||||
|
||||
- name: Optional Pangolin Integration API (HTTP resources + Traefik targets)
|
||||
ansible.builtin.include_tasks: pangolin_sync.yml
|
||||
when: noble_pangolin_sync_http_resources | default(false) | bool
|
||||
|
||||
95
ansible/roles/noble_newt/tasks/pangolin_sync.yml
Normal file
95
ansible/roles/noble_newt/tasks/pangolin_sync.yml
Normal file
@@ -0,0 +1,95 @@
|
||||
---
|
||||
# Pangolin Integration API — public HTTP resources → Newt site → Traefik (see clusters/noble/bootstrap/newt/README.md §4).
|
||||
# Included only when **noble_pangolin_sync_http_resources** is true.
|
||||
- name: Build Pangolin HTTP FQDN list
|
||||
ansible.builtin.set_fact:
|
||||
noble_pangolin_http_fqdns_effective: >-
|
||||
{{
|
||||
(
|
||||
noble_pangolin_http_fqdns_extra | default([])
|
||||
+ (noble_authentik_ingress_extra_hosts | default([]))
|
||||
+ ([noble_open_webui_public_host | trim] if (noble_open_webui_public_host | default('') | trim | length) > 0 else [])
|
||||
) | unique | list
|
||||
}}
|
||||
|
||||
- name: Discover Traefik LoadBalancer IP for Pangolin targets (when not set explicitly)
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- kubectl
|
||||
- get
|
||||
- svc
|
||||
- -n
|
||||
- traefik
|
||||
- -l
|
||||
- app.kubernetes.io/name=traefik
|
||||
- -o
|
||||
- jsonpath={.items[0].status.loadBalancer.ingress[0].ip}
|
||||
environment:
|
||||
KUBECONFIG: "{{ noble_kubeconfig }}"
|
||||
register: noble_pangolin_traefik_lb_ip
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
when:
|
||||
- noble_pangolin_http_fqdns_effective | length > 0
|
||||
- noble_pangolin_traefik_target_ip | default('') | trim | length == 0
|
||||
|
||||
- name: Resolve Traefik IP for Pangolin sync
|
||||
ansible.builtin.set_fact:
|
||||
noble_pangolin_traefik_ip_resolved: >-
|
||||
{{
|
||||
(noble_pangolin_traefik_target_ip | default('') | trim)
|
||||
if (noble_pangolin_traefik_target_ip | default('') | trim | length > 0)
|
||||
else (noble_pangolin_traefik_lb_ip.stdout | default('') | trim)
|
||||
}}
|
||||
when: noble_pangolin_http_fqdns_effective | length > 0
|
||||
|
||||
- name: Require Traefik IP for Pangolin sync
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- noble_pangolin_traefik_ip_resolved | length > 0
|
||||
fail_msg: >-
|
||||
Set **noble_pangolin_traefik_target_ip** in inventory (Traefik Service LoadBalancer / MetalLB IP), or ensure
|
||||
**kubectl** can read **traefik** Services (see **clusters/noble/bootstrap/traefik/**).
|
||||
when: noble_pangolin_http_fqdns_effective | length > 0
|
||||
|
||||
- name: Stat repository .env for Pangolin API credentials
|
||||
ansible.builtin.stat:
|
||||
path: "{{ noble_repo_root }}/.env"
|
||||
register: noble_pangolin_env_file
|
||||
changed_when: false
|
||||
when: noble_pangolin_http_fqdns_effective | length > 0
|
||||
|
||||
- name: Require .env for Pangolin Integration API secrets
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- noble_pangolin_env_file.stat.exists | default(false)
|
||||
fail_msg: >-
|
||||
Pangolin sync needs **.env** at the repo root with **NOBLE_PANGOLIN_*** variables (see **.env.sample**).
|
||||
when: noble_pangolin_http_fqdns_effective | length > 0
|
||||
|
||||
- name: Sync Pangolin public HTTP resources (Integration API)
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- python3
|
||||
- "{{ noble_repo_root }}/clusters/noble/bootstrap/newt/scripts/sync_pangolin_http_resources.py"
|
||||
- "--env-file"
|
||||
- "{{ noble_repo_root }}/.env"
|
||||
- "--fqdns"
|
||||
- "{{ noble_pangolin_http_fqdns_effective | join(',') }}"
|
||||
- "--traefik-ip"
|
||||
- "{{ noble_pangolin_traefik_ip_resolved }}"
|
||||
- "--traefik-port"
|
||||
- "{{ noble_pangolin_traefik_target_port | int | string }}"
|
||||
register: noble_pangolin_sync_cmd
|
||||
changed_when: >-
|
||||
'[create]' in (noble_pangolin_sync_cmd.stdout | default(''))
|
||||
or '[target]' in (noble_pangolin_sync_cmd.stdout | default(''))
|
||||
or 'target created' in (noble_pangolin_sync_cmd.stdout | default(''))
|
||||
when: noble_pangolin_http_fqdns_effective | length > 0
|
||||
|
||||
- name: Skip Pangolin sync (no public FQDNs configured)
|
||||
ansible.builtin.debug:
|
||||
msg: >-
|
||||
noble_pangolin_sync_http_resources is true but the FQDN list is empty
|
||||
(set **noble_authentik_ingress_extra_hosts**, **noble_open_webui_public_host**, and/or **noble_pangolin_http_fqdns_extra**).
|
||||
when: noble_pangolin_http_fqdns_effective | length == 0
|
||||
Reference in New Issue
Block a user