Add Authentik and oauth2-proxy support to noble cluster setup, including environment variables, playbook tags, and landing URLs. Update README and kustomization.yaml to reflect new OIDC integration, enhancing security and user authentication capabilities.

This commit is contained in:
Nikholas Pcenicni
2026-05-14 00:23:48 -04:00
parent 2bf7277917
commit 78b524a044
25 changed files with 1125 additions and 7 deletions

View File

@@ -0,0 +1,217 @@
---
# **.env** is shell `KEY=value` syntax (not YAML). Source it like **noble_velero** does.
- name: Stat repository .env for Authentik
ansible.builtin.stat:
path: "{{ noble_repo_root }}/.env"
register: noble_authentik_dotenv_stat
changed_when: false
- name: Load NOBLE_AUTHENTIK_SECRET_KEY from .env when unset
ansible.builtin.shell: |
set -a
. "{{ noble_repo_root }}/.env"
set +a
printf '%s' "${NOBLE_AUTHENTIK_SECRET_KEY:-}"
register: noble_authentik_secret_key_from_env
when:
- noble_authentik_dotenv_stat.stat.exists | default(false)
- noble_authentik_secret_key | default('') | length == 0
changed_when: false
no_log: true
- name: Apply NOBLE_AUTHENTIK_SECRET_KEY from .env
ansible.builtin.set_fact:
noble_authentik_secret_key: "{{ noble_authentik_secret_key_from_env.stdout | trim }}"
when:
- noble_authentik_secret_key_from_env is defined
- (noble_authentik_secret_key_from_env.stdout | default('') | trim | length) > 0
no_log: true
- name: Load NOBLE_AUTHENTIK_POSTGRES_PASSWORD from .env when unset
ansible.builtin.shell: |
set -a
. "{{ noble_repo_root }}/.env"
set +a
printf '%s' "${NOBLE_AUTHENTIK_POSTGRES_PASSWORD:-}"
register: noble_authentik_pg_from_env
when:
- noble_authentik_dotenv_stat.stat.exists | default(false)
- noble_authentik_postgresql_password | default('') | length == 0
changed_when: false
no_log: true
- name: Apply NOBLE_AUTHENTIK_POSTGRES_PASSWORD from .env
ansible.builtin.set_fact:
noble_authentik_postgresql_password: "{{ noble_authentik_pg_from_env.stdout | trim }}"
when:
- noble_authentik_pg_from_env is defined
- (noble_authentik_pg_from_env.stdout | default('') | trim | length) > 0
no_log: true
- name: Load NOBLE_AUTHENTIK_BOOTSTRAP_TOKEN from .env when unset
ansible.builtin.shell: |
set -a
. "{{ noble_repo_root }}/.env"
set +a
printf '%s' "${NOBLE_AUTHENTIK_BOOTSTRAP_TOKEN:-}"
register: noble_authentik_bt_from_env
when:
- noble_authentik_dotenv_stat.stat.exists | default(false)
- noble_authentik_bootstrap_token | default('') | length == 0
changed_when: false
no_log: true
- name: Apply NOBLE_AUTHENTIK_BOOTSTRAP_TOKEN from .env
ansible.builtin.set_fact:
noble_authentik_bootstrap_token: "{{ noble_authentik_bt_from_env.stdout | trim }}"
when:
- noble_authentik_bt_from_env is defined
- (noble_authentik_bt_from_env.stdout | default('') | trim | length) > 0
no_log: true
- name: Load NOBLE_AUTHENTIK_BOOTSTRAP_EMAIL from .env when unset
ansible.builtin.shell: |
set -a
. "{{ noble_repo_root }}/.env"
set +a
printf '%s' "${NOBLE_AUTHENTIK_BOOTSTRAP_EMAIL:-}"
register: noble_authentik_be_from_env
when:
- noble_authentik_dotenv_stat.stat.exists | default(false)
- noble_authentik_bootstrap_email | default('') | length == 0
changed_when: false
no_log: true
- name: Apply NOBLE_AUTHENTIK_BOOTSTRAP_EMAIL from .env
ansible.builtin.set_fact:
noble_authentik_bootstrap_email: "{{ noble_authentik_be_from_env.stdout | trim }}"
when:
- noble_authentik_be_from_env is defined
- (noble_authentik_be_from_env.stdout | default('') | trim | length) > 0
no_log: true
- name: Load NOBLE_AUTHENTIK_BOOTSTRAP_PASSWORD from .env when unset
ansible.builtin.shell: |
set -a
. "{{ noble_repo_root }}/.env"
set +a
printf '%s' "${NOBLE_AUTHENTIK_BOOTSTRAP_PASSWORD:-}"
register: noble_authentik_bp_from_env
when:
- noble_authentik_dotenv_stat.stat.exists | default(false)
- noble_authentik_bootstrap_password | default('') | length == 0
changed_when: false
no_log: true
- name: Apply NOBLE_AUTHENTIK_BOOTSTRAP_PASSWORD from .env
ansible.builtin.set_fact:
noble_authentik_bootstrap_password: "{{ noble_authentik_bp_from_env.stdout | trim }}"
when:
- noble_authentik_bp_from_env is defined
- (noble_authentik_bp_from_env.stdout | default('') | trim | length) > 0
no_log: true
- name: Load NOBLE_AUTHENTIK_CLIENT_SECRET_ARGOCD from .env when unset
ansible.builtin.shell: |
set -a
. "{{ noble_repo_root }}/.env"
set +a
printf '%s' "${NOBLE_AUTHENTIK_CLIENT_SECRET_ARGOCD:-}"
register: noble_authentik_cs_argo_from_env
when:
- noble_authentik_dotenv_stat.stat.exists | default(false)
- noble_authentik_client_secret_argocd | default('') | length == 0
changed_when: false
no_log: true
- name: Apply NOBLE_AUTHENTIK_CLIENT_SECRET_ARGOCD from .env
ansible.builtin.set_fact:
noble_authentik_client_secret_argocd: "{{ noble_authentik_cs_argo_from_env.stdout | trim }}"
when:
- noble_authentik_cs_argo_from_env is defined
- (noble_authentik_cs_argo_from_env.stdout | default('') | trim | length) > 0
no_log: true
- name: Load NOBLE_AUTHENTIK_CLIENT_SECRET_GRAFANA from .env when unset
ansible.builtin.shell: |
set -a
. "{{ noble_repo_root }}/.env"
set +a
printf '%s' "${NOBLE_AUTHENTIK_CLIENT_SECRET_GRAFANA:-}"
register: noble_authentik_cs_graf_from_env
when:
- noble_authentik_dotenv_stat.stat.exists | default(false)
- noble_authentik_client_secret_grafana | default('') | length == 0
changed_when: false
no_log: true
- name: Apply NOBLE_AUTHENTIK_CLIENT_SECRET_GRAFANA from .env
ansible.builtin.set_fact:
noble_authentik_client_secret_grafana: "{{ noble_authentik_cs_graf_from_env.stdout | trim }}"
when:
- noble_authentik_cs_graf_from_env is defined
- (noble_authentik_cs_graf_from_env.stdout | default('') | trim | length) > 0
no_log: true
- name: Load NOBLE_AUTHENTIK_CLIENT_SECRET_HEADLAMP from .env when unset
ansible.builtin.shell: |
set -a
. "{{ noble_repo_root }}/.env"
set +a
printf '%s' "${NOBLE_AUTHENTIK_CLIENT_SECRET_HEADLAMP:-}"
register: noble_authentik_cs_hl_from_env
when:
- noble_authentik_dotenv_stat.stat.exists | default(false)
- noble_authentik_client_secret_headlamp | default('') | length == 0
changed_when: false
no_log: true
- name: Apply NOBLE_AUTHENTIK_CLIENT_SECRET_HEADLAMP from .env
ansible.builtin.set_fact:
noble_authentik_client_secret_headlamp: "{{ noble_authentik_cs_hl_from_env.stdout | trim }}"
when:
- noble_authentik_cs_hl_from_env is defined
- (noble_authentik_cs_hl_from_env.stdout | default('') | trim | length) > 0
no_log: true
- name: Load NOBLE_AUTHENTIK_CLIENT_SECRET_OAUTH2_PROXY from .env when unset
ansible.builtin.shell: |
set -a
. "{{ noble_repo_root }}/.env"
set +a
printf '%s' "${NOBLE_AUTHENTIK_CLIENT_SECRET_OAUTH2_PROXY:-}"
register: noble_authentik_cs_o2_from_env
when:
- noble_authentik_dotenv_stat.stat.exists | default(false)
- noble_authentik_client_secret_oauth2_proxy | default('') | length == 0
changed_when: false
no_log: true
- name: Apply NOBLE_AUTHENTIK_CLIENT_SECRET_OAUTH2_PROXY from .env
ansible.builtin.set_fact:
noble_authentik_client_secret_oauth2_proxy: "{{ noble_authentik_cs_o2_from_env.stdout | trim }}"
when:
- noble_authentik_cs_o2_from_env is defined
- (noble_authentik_cs_o2_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
. "{{ noble_repo_root }}/.env"
set +a
printf '%s' "${NOBLE_AUTHENTIK_OAUTH2_PROXY_COOKIE_SECRET:-}"
register: noble_authentik_cs_cookie_from_env
when:
- noble_authentik_dotenv_stat.stat.exists | default(false)
- noble_authentik_oauth2_proxy_cookie_secret | default('') | length == 0
changed_when: false
no_log: true
- name: Apply NOBLE_AUTHENTIK_OAUTH2_PROXY_COOKIE_SECRET from .env
ansible.builtin.set_fact:
noble_authentik_oauth2_proxy_cookie_secret: "{{ noble_authentik_cs_cookie_from_env.stdout | trim }}"
when:
- noble_authentik_cs_cookie_from_env is defined
- (noble_authentik_cs_cookie_from_env.stdout | default('') | trim | length) > 0
no_log: true

View File

@@ -0,0 +1,302 @@
---
- name: Authentik disabled (set noble_authentik_install=true and .env — see role README)
ansible.builtin.debug:
msg: "Skipping noble_authentik (noble_authentik_install is false)."
when: not (noble_authentik_install | default(false) | bool)
- name: Authentik + OIDC stack
when: noble_authentik_install | default(false) | bool
block:
- name: Include Authentik secrets from .env
ansible.builtin.include_tasks: from_env.yml
- name: Require Authentik secrets and bootstrap settings
ansible.builtin.assert:
that:
- noble_authentik_secret_key | default('') | length > 0
- noble_authentik_postgresql_password | default('') | length > 0
- noble_authentik_bootstrap_token | default('') | length > 0
- noble_authentik_bootstrap_email | default('') | length > 0
- noble_authentik_bootstrap_password | default('') | length > 0
- noble_authentik_client_secret_argocd | default('') | length > 0
- 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_oauth2_proxy_cookie_secret | default('') | length > 0
fail_msg: >-
Authentik requires secrets in .env (see ansible/roles/noble_authentik/README.md) or matching -e extra-vars.
- name: Ensure Ansible temp dir for rendered Helm values
ansible.builtin.file:
path: "{{ noble_repo_root }}/ansible/.ansible-tmp"
state: directory
mode: "0700"
- name: Render Authentik Helm extra values (secrets)
ansible.builtin.template:
src: authentik-extra-values.yaml.j2
dest: "{{ noble_repo_root }}/ansible/.ansible-tmp/authentik-extra-values.yaml"
mode: "0600"
no_log: true
- name: Apply Authentik and oauth2-proxy namespaces
ansible.builtin.command:
argv:
- kubectl
- apply
- -f
- "{{ noble_repo_root }}/clusters/noble/bootstrap/authentik/namespace.yaml"
- -f
- "{{ noble_repo_root }}/clusters/noble/bootstrap/oauth2-proxy/namespace.yaml"
environment:
KUBECONFIG: "{{ noble_kubeconfig }}"
changed_when: true
- name: Install Authentik (Helm)
ansible.builtin.command:
argv:
- helm
- upgrade
- --install
- authentik
- goauthentik/authentik
- --namespace
- authentik
- --version
- "{{ noble_authentik_chart_version }}"
- -f
- "{{ noble_repo_root }}/clusters/noble/bootstrap/authentik/values.yaml"
- -f
- "{{ noble_repo_root }}/ansible/.ansible-tmp/authentik-extra-values.yaml"
- --force-conflicts
- --wait
- --timeout
- "{{ noble_authentik_helm_wait_timeout }}"
environment:
KUBECONFIG: "{{ noble_kubeconfig }}"
changed_when: true
- name: Wait for authentik server rollout
ansible.builtin.command:
argv:
- kubectl
- rollout
- status
- deployment/authentik-server
- -n
- authentik
- --timeout=15m
environment:
KUBECONFIG: "{{ noble_kubeconfig }}"
changed_when: false
- name: Render Authentik API client descriptor (JSON)
ansible.builtin.template:
src: authentik-clients.json.j2
dest: "{{ noble_repo_root }}/ansible/.ansible-tmp/authentik-clients.json"
mode: "0600"
no_log: true
- name: Configure Authentik OAuth2/OIDC providers (API)
ansible.builtin.command:
argv:
- python3
- "{{ role_path }}/files/configure_authentik.py"
environment:
AUTHENTIK_API_BASE: "{{ noble_authentik_api_base }}"
AUTHENTIK_TOKEN: "{{ noble_authentik_bootstrap_token }}"
BOOTSTRAP_EMAIL: "{{ noble_authentik_bootstrap_email }}"
CLIENT_JSON: "{{ noble_repo_root }}/ansible/.ansible-tmp/authentik-clients.json"
when: noble_authentik_configure_idp | default(true) | bool
changed_when: true
no_log: true
- name: Create argocd namespace Secret for OIDC client (Argo CD $authentik-oidc:clientSecret)
ansible.builtin.shell: |
set -euo pipefail
kubectl -n argocd create secret generic authentik-oidc \
--from-literal=clientSecret="${ARGOCD_OIDC_SECRET}" \
--dry-run=client -o yaml | kubectl apply -f -
kubectl -n argocd label secret authentik-oidc app.kubernetes.io/part-of=argocd --overwrite
environment:
KUBECONFIG: "{{ noble_kubeconfig }}"
ARGOCD_OIDC_SECRET: "{{ noble_authentik_client_secret_argocd }}"
no_log: true
changed_when: true
- name: Create Grafana OIDC client secret (GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET)
ansible.builtin.shell: |
set -euo pipefail
kubectl -n monitoring create secret generic authentik-grafana-oauth \
--from-literal=GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET="${GRAFANA_OIDC_SECRET}" \
--dry-run=client -o yaml | kubectl apply -f -
environment:
KUBECONFIG: "{{ noble_kubeconfig }}"
GRAFANA_OIDC_SECRET: "{{ noble_authentik_client_secret_grafana }}"
no_log: true
changed_when: true
- name: Create Headlamp OIDC env secret (OIDC_* env vars)
ansible.builtin.shell: |
set -euo pipefail
kubectl -n headlamp create secret generic headlamp-oidc \
--from-literal=OIDC_CLIENT_ID="{{ noble_authentik_client_id_headlamp }}" \
--from-literal=OIDC_CLIENT_SECRET="${HEADLAMP_OIDC_SECRET}" \
--from-literal=OIDC_ISSUER_URL="{{ noble_authentik_public_url }}/application/o/headlamp/" \
--from-literal=OIDC_SCOPES="openid profile email groups offline_access" \
--from-literal=OIDC_CALLBACK_URL="https://headlamp.apps.noble.lab.pcenicni.dev/oidc-callback" \
--from-literal=OIDC_USE_PKCE="true" \
--dry-run=client -o yaml | kubectl apply -f -
environment:
KUBECONFIG: "{{ noble_kubeconfig }}"
HEADLAMP_OIDC_SECRET: "{{ noble_authentik_client_secret_headlamp }}"
no_log: true
changed_when: true
- name: Create oauth2-proxy credentials Secret (OIDC to Authentik; not BasicAuth)
ansible.builtin.shell: |
set -euo pipefail
kubectl -n oauth2-proxy create secret generic oauth2-proxy-credentials \
--from-literal=client-id="{{ noble_authentik_client_id_oauth2_proxy }}" \
--from-literal=client-secret="${O2_CLIENT_SECRET}" \
--from-literal=cookie-secret="${O2_COOKIE_SECRET}" \
--dry-run=client -o yaml | kubectl apply -f -
environment:
KUBECONFIG: "{{ noble_kubeconfig }}"
O2_CLIENT_SECRET: "{{ noble_authentik_client_secret_oauth2_proxy }}"
O2_COOKIE_SECRET: "{{ noble_authentik_oauth2_proxy_cookie_secret }}"
no_log: true
changed_when: true
- name: Install oauth2-proxy (Helm) — OIDC provider Authentik
ansible.builtin.command:
argv:
- helm
- upgrade
- --install
- oauth2-proxy
- oauth2-proxy/oauth2-proxy
- --namespace
- oauth2-proxy
- --version
- "{{ noble_authentik_oauth2_proxy_chart_version }}"
- -f
- "{{ noble_repo_root }}/clusters/noble/bootstrap/oauth2-proxy/values.yaml"
- --force-conflicts
- --wait
- --timeout
- 10m
environment:
KUBECONFIG: "{{ noble_kubeconfig }}"
changed_when: true
- name: Apply Traefik ForwardAuth Middleware (references oauth2-proxy OIDC session)
ansible.builtin.command:
argv:
- kubectl
- apply
- -f
- "{{ noble_repo_root }}/clusters/noble/bootstrap/oauth2-proxy/middleware-forwardauth.yaml"
environment:
KUBECONFIG: "{{ noble_kubeconfig }}"
changed_when: true
- name: Helm upgrade Argo CD with Authentik OIDC values
ansible.builtin.command:
argv:
- helm
- upgrade
- --install
- argocd
- argo/argo-cd
- --namespace
- argocd
- --version
- "{{ noble_authentik_argocd_chart_version }}"
- -f
- "{{ noble_repo_root }}/clusters/noble/bootstrap/argocd/values.yaml"
- -f
- "{{ noble_repo_root }}/clusters/noble/bootstrap/argocd/values-authentik-oidc.yaml"
- --force-conflicts
- --wait
- --timeout
- 15m
environment:
KUBECONFIG: "{{ noble_kubeconfig }}"
changed_when: true
- name: Helm upgrade kube-prometheus-stack (Grafana OIDC + ForwardAuth on Prom/AM)
ansible.builtin.command:
argv:
- helm
- upgrade
- --install
- kube-prometheus
- prometheus-community/kube-prometheus-stack
- -n
- monitoring
- --version
- "{{ noble_authentik_kube_prometheus_chart_version }}"
- -f
- "{{ noble_repo_root }}/clusters/noble/bootstrap/kube-prometheus-stack/values.yaml"
- -f
- "{{ noble_repo_root }}/clusters/noble/bootstrap/kube-prometheus-stack/values-authentik-oidc.yaml"
- --force-conflicts
- --wait
- --timeout
- "{{ noble_authentik_kube_prometheus_helm_wait_timeout }}"
environment:
KUBECONFIG: "{{ noble_kubeconfig }}"
changed_when: true
- name: Helm upgrade Headlamp with Authentik OIDC values
ansible.builtin.command:
argv:
- helm
- upgrade
- --install
- headlamp
- headlamp/headlamp
- --version
- "{{ noble_authentik_headlamp_chart_version }}"
- -n
- headlamp
- -f
- "{{ noble_repo_root }}/clusters/noble/bootstrap/headlamp/values.yaml"
- -f
- "{{ noble_repo_root }}/clusters/noble/bootstrap/headlamp/values-authentik-oidc.yaml"
- --force-conflicts
- --wait
- --timeout
- 10m
environment:
KUBECONFIG: "{{ noble_kubeconfig }}"
changed_when: true
- name: Helm upgrade Longhorn with ForwardAuth (oauth2-proxy OIDC)
ansible.builtin.command:
argv:
- helm
- upgrade
- --install
- longhorn
- longhorn/longhorn
- -n
- longhorn-system
- --version
- "{{ noble_authentik_longhorn_chart_version }}"
- -f
- "{{ noble_repo_root }}/clusters/noble/bootstrap/longhorn/values.yaml"
- -f
- "{{ noble_repo_root }}/clusters/noble/bootstrap/longhorn/values-authentik-forwardauth.yaml"
- --force-conflicts
- --wait
- --timeout
- "{{ noble_helm_longhorn_wait_timeout | default('20m') }}"
environment:
KUBECONFIG: "{{ noble_kubeconfig }}"
register: noble_authentik_longhorn_helm
retries: "{{ noble_helm_longhorn_retries | default(8) | int }}"
delay: "{{ noble_helm_longhorn_retry_delay | default(25) | int }}"
until: noble_authentik_longhorn_helm.rc == 0
changed_when: true