Files
home-server/ansible/roles/noble_authentik/tasks/main.yml

728 lines
36 KiB
YAML

---
- 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: Require Authentik S3 media settings (same endpoint/keys as Velero; dedicated bucket)
ansible.builtin.assert:
that:
- noble_authentik_media_s3_bucket | default('') | length > 0
- noble_authentik_s3_endpoint | default('') | length > 0
- noble_authentik_s3_access_key | default('') | length > 0
- noble_authentik_s3_secret_key | default('') | length > 0
fail_msg: >-
Set NOBLE_AUTHENTIK_MEDIA_S3_BUCKET (dedicated bucket for media, not the Velero backup bucket).
For S3 URL and keys, set NOBLE_AUTHENTIK_S3_URL / NOBLE_AUTHENTIK_S3_ACCESS_KEY / NOBLE_AUTHENTIK_S3_SECRET_KEY,
or reuse Velero's NOBLE_VELERO_S3_URL and NOBLE_VELERO_AWS_ACCESS_KEY_ID / NOBLE_VELERO_AWS_SECRET_ACCESS_KEY
in .env (see .env.sample and clusters/noble/bootstrap/velero/README.md).
- name: Require Authentik SMTP From when SMTP host is set
ansible.builtin.assert:
that:
- noble_authentik_smtp_from | default('') | trim | length > 0
fail_msg: >-
When NOBLE_AUTHENTIK_SMTP_HOST is set, set NOBLE_AUTHENTIK_SMTP_FROM (sender address).
See repository .env.sample and https://docs.goauthentik.io/install-config/configuration/#email-settings
when: noble_authentik_smtp_host | default('') | trim | length > 0
- 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: Ensure dir for rendered Authentik blueprints
ansible.builtin.file:
path: "{{ noble_repo_root }}/ansible/.ansible-tmp/authentik-blueprints"
state: directory
mode: "0700"
when: noble_authentik_blueprints_enabled | default(false) | bool
- name: Assert noble Authentik blueprint variables (when blueprints enabled)
ansible.builtin.assert:
that:
- >-
((noble_authentik_blueprint_public_groups | default([])) | length
+ (noble_authentik_blueprint_extra_directory_groups | default([])) | length
+ (noble_authentik_blueprint_nikflix_groups | default([])) | length) > 0
- noble_authentik_blueprint_lab_operator_groups | default([]) | length > 0
- noble_authentik_blueprint_lab_flow_slug | default('') | trim | length > 0
- noble_authentik_blueprint_public_auth_flow_slug | default('') | trim | length > 0
- (noble_authentik_blueprint_lab_mfa_not_configured_action | default('configure') | trim | lower)
in ['skip', 'deny', 'configure']
- noble_authentik_blueprint_public_invitation_enrollment_flow_slug | default('') | trim | length > 0
- noble_authentik_blueprint_lab_invitation_enrollment_flow_slug | default('') | trim | length > 0
- noble_authentik_blueprint_public_invitation_user_group | default('') | trim | length > 0
- noble_authentik_blueprint_lab_invitee_group_name | default('') | trim | length > 0
- (noble_authentik_blueprint_public_invitation_user_type | default('external') | trim | lower) in ['external', 'internal']
- (noble_authentik_blueprint_lab_invitation_user_type | default('internal') | trim | lower) in ['external', 'internal']
fail_msg: >-
When noble_authentik_blueprints_enabled is true, set at least one entry across
noble_authentik_blueprint_public_groups, noble_authentik_blueprint_extra_directory_groups,
and/or noble_authentik_blueprint_nikflix_groups,
plus noble_authentik_blueprint_lab_operator_groups (non-empty), noble_authentik_blueprint_lab_flow_slug,
noble_authentik_blueprint_public_auth_flow_slug, noble_authentik_blueprint_lab_mfa_not_configured_action
(skip, deny, or configure), invitation enrollment flow slugs, noble_authentik_blueprint_public_invitation_user_group,
noble_authentik_blueprint_lab_invitee_group_name, and invitation user_type values (external or internal).
See ansible/roles/noble_authentik/defaults/main.yml and README.
when: noble_authentik_blueprints_enabled | default(false) | bool
- name: Validate noble Authentik blueprint directory group entries (when blueprints enabled)
ansible.builtin.assert:
that:
- (item is string and (item | trim | length) > 0) or (item is mapping and (item.name | default('') | trim | length) > 0)
fail_msg: >-
Each noble_authentik_blueprint_*_groups entry must be a non-empty string or a dict with key **name** (string).
Invalid entry: {{ item }}
loop: "{{ (noble_authentik_blueprint_public_groups | default([])) + (noble_authentik_blueprint_extra_directory_groups | default([])) + (noble_authentik_blueprint_nikflix_groups | default([])) }}"
loop_control:
label: "{{ item if item is string else item.name }}"
when: noble_authentik_blueprints_enabled | default(false) | bool
- name: Render Authentik noble blueprint YAML files
ansible.builtin.template:
src: "blueprints/{{ item }}.j2"
dest: "{{ noble_repo_root }}/ansible/.ansible-tmp/authentik-blueprints/{{ item }}"
mode: "0600"
loop:
- 10-noble-public-groups.yaml
- 20-noble-lab-operator-authentication-flow.yaml
- 21-noble-public-authentication-flow.yaml
- 22-noble-invitation-enrollment-flows.yaml
- 30-noble-brands-domain-split.yaml
when: noble_authentik_blueprints_enabled | default(false) | bool
- name: Apply Authentik noble blueprints ConfigMap (worker mounts under /blueprints/mounted/cm-*)
ansible.builtin.shell: |
set -euo pipefail
kubectl -n "{{ noble_authentik_namespace }}" create configmap "{{ noble_authentik_blueprints_configmap_name }}" \
--from-file="{{ noble_repo_root }}/ansible/.ansible-tmp/authentik-blueprints" \
--dry-run=client -o yaml | kubectl apply -f -
environment:
KUBECONFIG: "{{ noble_kubeconfig }}"
when: noble_authentik_blueprints_enabled | default(false) | bool
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: Verify authentik-worker mounts noble blueprints volume (Helm blueprints.configMaps)
ansible.builtin.shell: |
set -euo pipefail
WANT="blueprints-cm-{{ noble_authentik_blueprints_configmap_name }}"
D="$(kubectl get deploy -n "{{ noble_authentik_namespace }}" \
-l app.kubernetes.io/name=authentik,app.kubernetes.io/component=worker \
-o jsonpath='{.items[0].metadata.name}')"
MOUNTS="$(kubectl get deploy -n "{{ noble_authentik_namespace }}" "$D" \
-o jsonpath='{.spec.template.spec.containers[0].volumeMounts[*].name}')"
if ! echo "$MOUNTS" | tr ' ' '\n' | grep -Fxq "$WANT"; then
echo "Expected volumeMount ${WANT} on ${D}; got: ${MOUNTS}" >&2
exit 1
fi
environment:
KUBECONFIG: "{{ noble_kubeconfig }}"
when: noble_authentik_blueprints_enabled | default(false) | bool
changed_when: false
- 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: Wait for authentik worker rollout
ansible.builtin.command:
argv:
- kubectl
- rollout
- status
- deployment/authentik-worker
- -n
- authentik
- --timeout=15m
environment:
KUBECONFIG: "{{ noble_kubeconfig }}"
changed_when: false
when: noble_authentik_configure_idp | default(true) | bool
- name: Wait until Authentik API accepts bootstrap token (worker finished bootstrap)
ansible.builtin.uri:
url: "{{ noble_authentik_api_base }}/core/applications/?page_size=1"
method: GET
headers:
Authorization: "Bearer {{ noble_authentik_bootstrap_token }}"
Accept: application/json
status_code: [200, 401, 403, 500, 502, 503]
timeout: 30
register: noble_authentik_api_bootstrap_ready
until: noble_authentik_api_bootstrap_ready.status == 200
retries: "{{ noble_authentik_bootstrap_api_wait_retries }}"
delay: "{{ noble_authentik_bootstrap_api_wait_delay }}"
when: noble_authentik_configure_idp | default(true) | bool
changed_when: false
no_log: true
- 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
# Authentik 2026+ RBAC: bootstrap tokens often cannot **GET /flows/instances/** (403). Resolve UUIDs
# from the worker DB via **ak shell** when inventory PKs are unset (same slugs as configure_authentik.py).
- name: Resolve OAuth provider flow UUIDs from authentik-worker (DB; bypasses flows API RBAC)
ansible.builtin.shell: |
set -euo pipefail
NS="{{ noble_authentik_namespace }}"
POD="$(kubectl get pods -n "$NS" \
-l "app.kubernetes.io/name=authentik,app.kubernetes.io/component=worker" \
-o jsonpath='{.items[0].metadata.name}')"
REM=/tmp/ansible_resolve_oauth_flow_pks.py
kubectl cp "{{ role_path }}/files/resolve_oauth_flow_pks.py" "${NS}/${POD}:${REM}"
kubectl exec -n "$NS" "$POD" -- ak shell -c "exec(compile(open('${REM}').read(), 'ansible_resolve_oauth_flow_pks.py', 'exec'))"
kubectl exec -n "$NS" "$POD" -- rm -f "$REM" || true
environment:
KUBECONFIG: "{{ noble_kubeconfig }}"
register: noble_authentik_oauth_flow_pk_resolve
changed_when: false
when:
- noble_authentik_configure_idp | default(true) | bool
- (noble_authentik_oauth_authorization_flow_pk | default('') | trim | length) == 0
- (noble_authentik_oauth_invalidation_flow_pk | default('') | trim | length) == 0
- name: Apply OAuth flow PKs from worker resolution for configure_authentik.py
ansible.builtin.set_fact:
noble_authentik_oauth_authorization_flow_pk: "{{ (noble_authentik_oauth_flow_pk_resolve.stdout_lines | select('match', '^[0-9a-fA-F-]{36}$') | list)[0] }}"
noble_authentik_oauth_invalidation_flow_pk: "{{ (noble_authentik_oauth_flow_pk_resolve.stdout_lines | select('match', '^[0-9a-fA-F-]{36}$') | list)[1] }}"
when:
- noble_authentik_oauth_flow_pk_resolve is defined
- not (noble_authentik_oauth_flow_pk_resolve.skipped | default(false))
- (noble_authentik_oauth_flow_pk_resolve.rc | default(-1)) == 0
- (noble_authentik_oauth_flow_pk_resolve.stdout_lines | default([]) | select('match', '^[0-9a-fA-F-]{36}$') | list | length) >= 2
# Bootstrap tokens often cannot list **/crypto/certificatekeypairs/** (403).
- name: Resolve OAuth signing key UUID from authentik-worker (DB; bypasses crypto API RBAC)
ansible.builtin.shell: |
set -euo pipefail
NS="{{ noble_authentik_namespace }}"
POD="$(kubectl get pods -n "$NS" \
-l "app.kubernetes.io/name=authentik,app.kubernetes.io/component=worker" \
-o jsonpath='{.items[0].metadata.name}')"
REM=/tmp/ansible_resolve_oauth_signing_key_pk.py
kubectl cp "{{ role_path }}/files/resolve_oauth_signing_key_pk.py" "${NS}/${POD}:${REM}"
kubectl exec -n "$NS" "$POD" -- ak shell -c "exec(compile(open('${REM}').read(), 'ansible_resolve_oauth_signing_key_pk.py', 'exec'))"
kubectl exec -n "$NS" "$POD" -- rm -f "$REM" || true
environment:
KUBECONFIG: "{{ noble_kubeconfig }}"
register: noble_authentik_oauth_signing_key_resolve
changed_when: false
when:
- noble_authentik_configure_idp | default(true) | bool
- (noble_authentik_oauth_signing_key_pk | default('') | trim | length) == 0
- name: Apply OAuth signing key PK from worker resolution for configure_authentik.py
ansible.builtin.set_fact:
noble_authentik_oauth_signing_key_pk: "{{ (noble_authentik_oauth_signing_key_resolve.stdout_lines | select('match', '^[0-9a-fA-F-]{36}$') | list)[0] }}"
when:
- noble_authentik_oauth_signing_key_resolve is defined
- not (noble_authentik_oauth_signing_key_resolve.skipped | default(false))
- (noble_authentik_oauth_signing_key_resolve.rc | default(-1)) == 0
- (noble_authentik_oauth_signing_key_resolve.stdout_lines | default([]) | select('match', '^[0-9a-fA-F-]{36}$') | list | length) >= 1
# Bootstrap tokens often cannot list **/propertymappings/provider/scope/** (403).
- name: Resolve OAuth scope mapping UUIDs from authentik-worker (DB; bypasses propertymappings API RBAC)
ansible.builtin.shell: |
set -euo pipefail
NS="{{ noble_authentik_namespace }}"
POD="$(kubectl get pods -n "$NS" \
-l "app.kubernetes.io/name=authentik,app.kubernetes.io/component=worker" \
-o jsonpath='{.items[0].metadata.name}')"
REM=/tmp/ansible_resolve_oauth_scope_mapping_pks.py
kubectl cp "{{ role_path }}/files/resolve_oauth_scope_mapping_pks.py" "${NS}/${POD}:${REM}"
kubectl exec -n "$NS" "$POD" -- ak shell -c "exec(compile(open('${REM}').read(), 'ansible_resolve_oauth_scope_mapping_pks.py', 'exec'))"
kubectl exec -n "$NS" "$POD" -- rm -f "$REM" || true
environment:
KUBECONFIG: "{{ noble_kubeconfig }}"
register: noble_authentik_oauth_scope_mapping_resolve
changed_when: false
when:
- noble_authentik_configure_idp | default(true) | bool
- (noble_authentik_oauth_scope_mapping_pks | default('') | trim | length) == 0
- name: Apply OAuth scope mapping PKs from worker resolution for configure_authentik.py
ansible.builtin.set_fact:
noble_authentik_oauth_scope_mapping_pks: "{{ (noble_authentik_oauth_scope_mapping_resolve.stdout_lines | default([]) | select('match', '^[0-9a-fA-F-]{36}$') | list) | join(',') }}"
when:
- noble_authentik_oauth_scope_mapping_resolve is defined
- not (noble_authentik_oauth_scope_mapping_resolve.skipped | default(false))
- (noble_authentik_oauth_scope_mapping_resolve.rc | default(-1)) == 0
- (noble_authentik_oauth_scope_mapping_resolve.stdout_lines | default([]) | select('match', '^[0-9a-fA-F-]{36}$') | list | length) >= 4
# Bootstrap tokens often cannot **GET /core/groups/** (403). Worker **get_or_create** ensures groups exist.
- name: Resolve noble-admins / noble-editors group UUIDs from authentik-worker (DB; bypasses groups API RBAC)
ansible.builtin.shell: |
set -euo pipefail
NS="{{ noble_authentik_namespace }}"
POD="$(kubectl get pods -n "$NS" \
-l "app.kubernetes.io/name=authentik,app.kubernetes.io/component=worker" \
-o jsonpath='{.items[0].metadata.name}')"
REM=/tmp/ansible_resolve_noble_group_pks.py
kubectl cp "{{ role_path }}/files/resolve_noble_group_pks.py" "${NS}/${POD}:${REM}"
kubectl exec -n "$NS" "$POD" -- ak shell -c "exec(compile(open('${REM}').read(), 'ansible_resolve_noble_group_pks.py', 'exec'))"
kubectl exec -n "$NS" "$POD" -- rm -f "$REM" || true
environment:
KUBECONFIG: "{{ noble_kubeconfig }}"
register: noble_authentik_noble_group_resolve
changed_when: false
when:
- noble_authentik_configure_idp | default(true) | bool
- (noble_authentik_group_pk_noble_admins | default('') | trim | length) == 0
- (noble_authentik_group_pk_noble_editors | default('') | trim | length) == 0
- name: Apply noble group PKs from worker resolution for configure_authentik.py
ansible.builtin.set_fact:
noble_authentik_group_pk_noble_admins: "{{ (noble_authentik_noble_group_resolve.stdout_lines | default([]) | select('match', '^[0-9a-fA-F-]{36}$') | list)[0] }}"
noble_authentik_group_pk_noble_editors: "{{ (noble_authentik_noble_group_resolve.stdout_lines | default([]) | select('match', '^[0-9a-fA-F-]{36}$') | list)[1] }}"
when:
- noble_authentik_noble_group_resolve is defined
- not (noble_authentik_noble_group_resolve.skipped | default(false))
- (noble_authentik_noble_group_resolve.rc | default(-1)) == 0
- (noble_authentik_noble_group_resolve.stdout_lines | default([]) | select('match', '^[0-9a-fA-F-]{36}$') | list | length) >= 2
- name: Render Authentik worker admin-access spec (JSON for ak shell)
ansible.builtin.template:
src: authentik-worker-admin-access-spec.json.j2
dest: "{{ noble_repo_root }}/ansible/.ansible-tmp/authentik-worker-admin-access-spec.json"
mode: "0600"
no_log: true
when:
- noble_authentik_configure_idp | default(true) | bool
- noble_authentik_ensure_admin_ui_access | default(true) | bool
- name: Ensure authentik Admins + superuser group flag (worker ORM; restores admin UI access)
ansible.builtin.shell: |
set -euo pipefail
NS="{{ noble_authentik_namespace }}"
POD="$(kubectl get pods -n "$NS" \
-l "app.kubernetes.io/name=authentik,app.kubernetes.io/component=worker" \
-o jsonpath='{.items[0].metadata.name}')"
SPEC=/tmp/ansible_authentik_worker_admin_access_spec.json
REM=/tmp/ansible_worker_ensure_authentik_admin_access.py
kubectl cp "{{ noble_repo_root }}/ansible/.ansible-tmp/authentik-worker-admin-access-spec.json" "${NS}/${POD}:${SPEC}"
kubectl cp "{{ role_path }}/files/worker_ensure_authentik_admin_access.py" "${NS}/${POD}:${REM}"
kubectl exec -n "$NS" "$POD" -- env AUTHENTIK_WORKER_ADMIN_ACCESS_SPEC="${SPEC}" ak shell -c "exec(compile(open('${REM}').read(), 'ansible_worker_ensure_authentik_admin_access.py', 'exec'))"
kubectl exec -n "$NS" "$POD" -- rm -f "$SPEC" "$REM" || true
environment:
KUBECONFIG: "{{ noble_kubeconfig }}"
register: noble_authentik_worker_admin_access
changed_when: >-
"worker:" in (noble_authentik_worker_admin_access.stdout | default(""))
and "authentik Admins" in (noble_authentik_worker_admin_access.stdout | default(""))
failed_when: >-
(noble_authentik_worker_admin_access.rc | default(-1)) != 0
or (
"worker:" not in (noble_authentik_worker_admin_access.stdout | default(""))
or "authentik Admins" not in (noble_authentik_worker_admin_access.stdout | default(""))
)
when:
- noble_authentik_configure_idp | default(true) | bool
- noble_authentik_ensure_admin_ui_access | default(true) | bool
- name: Require OAuth PKs for worker OIDC upsert (ORM path)
ansible.builtin.assert:
that:
- (noble_authentik_oauth_authorization_flow_pk | default('') | trim | length) > 0
- (noble_authentik_oauth_invalidation_flow_pk | default('') | trim | length) > 0
- (noble_authentik_oauth_signing_key_pk | default('') | trim | length) > 0
- (noble_authentik_oauth_scope_mapping_pks | default('') | trim | length) > 0
fail_msg: >-
Worker OIDC provisioning needs flow UUIDs, signing key UUID, and comma-separated scope-mapping UUIDs.
Ensure worker resolution tasks ran, or set noble_authentik_oauth_* inventory vars (see role README).
when:
- noble_authentik_configure_idp | default(true) | bool
- (noble_authentik_oidc_provision_via | default('worker') | lower) == 'worker'
- name: Render Authentik worker OIDC spec (JSON for ak shell upsert)
ansible.builtin.template:
src: authentik-worker-oidc-spec.json.j2
dest: "{{ noble_repo_root }}/ansible/.ansible-tmp/authentik-worker-oidc-spec.json"
mode: "0600"
no_log: true
when:
- noble_authentik_configure_idp | default(true) | bool
- (noble_authentik_oidc_provision_via | default('worker') | lower) == 'worker'
- name: Upsert OAuth2 providers + applications in authentik-worker (ORM; bypasses provider REST RBAC)
ansible.builtin.shell: |
set -euo pipefail
NS="{{ noble_authentik_namespace }}"
POD="$(kubectl get pods -n "$NS" \
-l "app.kubernetes.io/name=authentik,app.kubernetes.io/component=worker" \
-o jsonpath='{.items[0].metadata.name}')"
SPEC=/tmp/ansible_authentik_worker_oidc_spec.json
REM=/tmp/ansible_worker_upsert_oauth_oidc.py
kubectl cp "{{ noble_repo_root }}/ansible/.ansible-tmp/authentik-worker-oidc-spec.json" "${NS}/${POD}:${SPEC}"
kubectl cp "{{ role_path }}/files/worker_upsert_oauth_oidc.py" "${NS}/${POD}:${REM}"
kubectl exec -n "$NS" "$POD" -- env AUTHENTIK_WORKER_OIDC_SPEC="${SPEC}" ak shell -c "exec(compile(open('${REM}').read(), 'ansible_worker_upsert_oauth_oidc.py', 'exec'))"
kubectl exec -n "$NS" "$POD" -- rm -f "$SPEC" "$REM" || true
environment:
KUBECONFIG: "{{ noble_kubeconfig }}"
register: noble_authentik_worker_oidc_upsert
changed_when: >-
"worker: OAuth2 providers + applications upserted"
in (noble_authentik_worker_oidc_upsert.stdout | default(""))
failed_when: >-
(noble_authentik_worker_oidc_upsert.rc | default(-1)) != 0
or (
"worker: OAuth2 providers + applications upserted"
not in (noble_authentik_worker_oidc_upsert.stdout | default(""))
)
when:
- noble_authentik_configure_idp | default(true) | bool
- (noble_authentik_oidc_provision_via | default('worker') | lower) == 'worker'
- name: Require noble group PKs for worker bootstrap group membership
ansible.builtin.assert:
that:
- (noble_authentik_group_pk_noble_admins | default('') | trim | length) > 0
- (noble_authentik_group_pk_noble_editors | default('') | trim | length) > 0
fail_msg: >-
Worker bootstrap group membership needs noble-admins / noble-editors UUIDs (worker DB resolve or inventory).
See noble_authentik_group_pk_noble_* in defaults/README.
when:
- noble_authentik_configure_idp | default(true) | bool
- (noble_authentik_oidc_provision_via | default('worker') | lower) == 'worker'
- (noble_authentik_bootstrap_email | default('') | trim | length) > 0
- name: Render Authentik worker user-groups spec (JSON for ak shell)
ansible.builtin.template:
src: authentik-worker-user-groups-spec.json.j2
dest: "{{ noble_repo_root }}/ansible/.ansible-tmp/authentik-worker-user-groups-spec.json"
mode: "0600"
no_log: true
when:
- noble_authentik_configure_idp | default(true) | bool
- (noble_authentik_oidc_provision_via | default('worker') | lower) == 'worker'
- (noble_authentik_bootstrap_email | default('') | trim | length) > 0
- name: Add bootstrap user to noble groups in authentik-worker (ORM; bypasses users API RBAC)
ansible.builtin.shell: |
set -euo pipefail
NS="{{ noble_authentik_namespace }}"
POD="$(kubectl get pods -n "$NS" \
-l "app.kubernetes.io/name=authentik,app.kubernetes.io/component=worker" \
-o jsonpath='{.items[0].metadata.name}')"
SPEC=/tmp/ansible_authentik_worker_user_groups_spec.json
REM=/tmp/ansible_worker_add_bootstrap_user_groups.py
kubectl cp "{{ noble_repo_root }}/ansible/.ansible-tmp/authentik-worker-user-groups-spec.json" "${NS}/${POD}:${SPEC}"
kubectl cp "{{ role_path }}/files/worker_add_bootstrap_user_groups.py" "${NS}/${POD}:${REM}"
kubectl exec -n "$NS" "$POD" -- env AUTHENTIK_WORKER_USER_GROUPS_SPEC="${SPEC}" ak shell -c "exec(compile(open('${REM}').read(), 'ansible_worker_add_bootstrap_user_groups.py', 'exec'))"
kubectl exec -n "$NS" "$POD" -- rm -f "$SPEC" "$REM" || true
environment:
KUBECONFIG: "{{ noble_kubeconfig }}"
register: noble_authentik_worker_user_groups
changed_when: >-
"worker: bootstrap user group membership updated"
in (noble_authentik_worker_user_groups.stdout | default(""))
failed_when: (noble_authentik_worker_user_groups.rc | default(-1)) != 0
when:
- noble_authentik_configure_idp | default(true) | bool
- (noble_authentik_oidc_provision_via | default('worker') | lower) == 'worker'
- (noble_authentik_bootstrap_email | default('') | trim | length) > 0
- 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"
AUTHENTIK_OAUTH_AUTHORIZATION_FLOW_PK: "{{ noble_authentik_oauth_authorization_flow_pk | default('') }}"
AUTHENTIK_OAUTH_INVALIDATION_FLOW_PK: "{{ noble_authentik_oauth_invalidation_flow_pk | default('') }}"
AUTHENTIK_OAUTH_SIGNING_KEY_PK: "{{ noble_authentik_oauth_signing_key_pk | default('') }}"
AUTHENTIK_OAUTH_SCOPE_MAPPING_PKS: "{{ noble_authentik_oauth_scope_mapping_pks | default('') }}"
AUTHENTIK_NOBLE_ADMINS_GROUP_PK: "{{ noble_authentik_group_pk_noble_admins | default('') }}"
AUTHENTIK_NOBLE_EDITORS_GROUP_PK: "{{ noble_authentik_group_pk_noble_editors | default('') }}"
AUTHENTIK_SKIP_OIDC_REST: "{{ '1' if (noble_authentik_oidc_provision_via | default('worker') | lower) == 'worker' else '' }}"
AUTHENTIK_SKIP_USER_GROUP_REST: "{{ '1' if ((noble_authentik_oidc_provision_via | default('worker') | lower) == 'worker' and (noble_authentik_bootstrap_email | default('') | trim | length) > 0) else '' }}"
when: noble_authentik_configure_idp | default(true) | bool
changed_when: 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="{{ noble_authentik_headlamp_oidc_scopes }}" \
--from-literal=OIDC_CALLBACK_URL="https://headlamp.apps.noble.lab.pcenicni.dev/oidc-callback" \
--from-literal=OIDC_USE_PKCE="{{ 'true' if (noble_authentik_headlamp_oidc_use_pkce | bool) else 'false' }}" \
--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
- "{{ noble_authentik_oauth2_proxy_helm_wait_timeout }}"
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"
- --set=config.oidc.usePKCE={{ 'true' if (noble_authentik_headlamp_oidc_use_pkce | bool) else 'false' }}
- --force-conflicts
- --wait
- --timeout
- 10m
environment:
KUBECONFIG: "{{ noble_kubeconfig }}"
changed_when: true
- name: Apply Headlamp static manifests (metrics RBAC + OIDC group binding)
ansible.builtin.command:
argv:
- kubectl
- apply
- -k
- "{{ noble_repo_root }}/clusters/noble/bootstrap/headlamp"
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