186 lines
7.8 KiB
YAML
186 lines
7.8 KiB
YAML
---
|
|
# playbooks/docker/deploy_authentik.yml
|
|
#
|
|
# Purpose:
|
|
# Deploy Authentik as a Swarm stack pinned to swarm-manager-1 with persistent
|
|
# bind mounts under /mnt/homelab/apps/authentik.
|
|
#
|
|
# Data protection:
|
|
# This playbook validates all required Authentik data paths before deploy.
|
|
# If paths are missing, deployment fails early to avoid creating empty data
|
|
# roots that could mask or diverge from an existing Authentik installation.
|
|
#
|
|
# Usage:
|
|
# ansible-playbook -i inventory/hosts.ini playbooks/docker/deploy_authentik.yml
|
|
#
|
|
# ansible-playbook -i inventory/hosts.ini playbooks/docker/deploy_authentik.yml \
|
|
# -e "stack_validate_only=true"
|
|
#
|
|
# ansible-playbook -i inventory/hosts.ini playbooks/docker/deploy_authentik.yml \
|
|
# -e "authentik_deploy_state=absent"
|
|
|
|
- name: Deploy Authentik Swarm stack
|
|
hosts: swarm_managers
|
|
become: true
|
|
gather_facts: false
|
|
vars_files:
|
|
- ../../group_vars/all.yml
|
|
vars:
|
|
authentik_deploy_target: "{{ edge_routing.swarm.stack_deploy_target | default(groups['swarm_managers'][0]) }}"
|
|
|
|
tasks:
|
|
# --------------------------------------------------
|
|
# STEP 0: Assert required secrets are present
|
|
# --------------------------------------------------
|
|
|
|
- name: Assert vault_authentik_secret_key is defined and non-empty
|
|
ansible.builtin.assert:
|
|
that:
|
|
- vault_authentik_secret_key is defined
|
|
- vault_authentik_secret_key | trim | length > 0
|
|
fail_msg: >-
|
|
vault_authentik_secret_key is not defined or is empty.
|
|
Encrypt and store it in group_vars/vault/all.yml with:
|
|
ansible-vault encrypt_string 'your-random-secret' --name 'vault_authentik_secret_key'
|
|
when: inventory_hostname == authentik_deploy_target
|
|
|
|
- name: Assert vault_authentik_postgres_password is defined and non-empty
|
|
ansible.builtin.assert:
|
|
that:
|
|
- vault_authentik_postgres_password is defined
|
|
- vault_authentik_postgres_password | trim | length > 0
|
|
fail_msg: >-
|
|
vault_authentik_postgres_password is not defined or is empty.
|
|
Encrypt and store it in group_vars/vault/all.yml with:
|
|
ansible-vault encrypt_string 'your-db-password' --name 'vault_authentik_postgres_password'
|
|
when: inventory_hostname == authentik_deploy_target
|
|
|
|
- name: Assert Authentik secrets are not placeholders
|
|
ansible.builtin.assert:
|
|
that:
|
|
- vault_authentik_secret_key not in ['change-me', 'changeme', 'your-random-secret']
|
|
- vault_authentik_postgres_password not in ['change-me', 'changeme', 'your-db-password']
|
|
fail_msg: "Authentik secrets still appear to be placeholders. Set real vault values before deploy."
|
|
when: inventory_hostname == authentik_deploy_target
|
|
|
|
# --------------------------------------------------
|
|
# STEP 1: Assert Swarm manager is active
|
|
# --------------------------------------------------
|
|
|
|
- name: Collect Swarm manager state
|
|
ansible.builtin.command: >
|
|
docker info --format '{{ "{{" }}.Swarm.LocalNodeState{{ "}}" }}|{{ "{{" }}.Swarm.ControlAvailable{{ "}}" }}'
|
|
register: _swarm_info
|
|
changed_when: false
|
|
when: inventory_hostname == authentik_deploy_target
|
|
|
|
- name: Assert target is an active Swarm manager
|
|
ansible.builtin.assert:
|
|
that:
|
|
- _swarm_info.stdout is search('active')
|
|
- _swarm_info.stdout is search('true')
|
|
fail_msg: >-
|
|
{{ inventory_hostname }} must be an active Swarm manager.
|
|
Current state: {{ _swarm_info.stdout | default('unknown') }}
|
|
when: inventory_hostname == authentik_deploy_target
|
|
|
|
# --------------------------------------------------
|
|
# STEP 2: Validate pre-existing persistent data paths
|
|
# --------------------------------------------------
|
|
|
|
- name: Stat required Authentik bind-mount paths
|
|
ansible.builtin.stat:
|
|
path: "{{ item }}"
|
|
register: _authentik_path_stat
|
|
loop:
|
|
- /mnt/homelab/apps/authentik
|
|
- /mnt/homelab/apps/authentik/data
|
|
- /mnt/homelab/apps/authentik/data/database
|
|
- /mnt/homelab/apps/authentik/data/redis
|
|
- /mnt/homelab/apps/authentik/data/media
|
|
- /mnt/homelab/apps/authentik/data/config
|
|
- /mnt/homelab/apps/authentik/data/blueprints
|
|
when: inventory_hostname == authentik_deploy_target
|
|
|
|
- name: Assert required Authentik paths exist before deploy
|
|
ansible.builtin.assert:
|
|
that:
|
|
- item.stat.exists
|
|
- item.stat.isdir
|
|
fail_msg: >-
|
|
Required Authentik path '{{ item.item }}' is missing on {{ inventory_hostname }}.
|
|
Create/restore this directory first to avoid accidental fresh bootstrap over existing data.
|
|
loop: "{{ _authentik_path_stat.results }}"
|
|
when: inventory_hostname == authentik_deploy_target
|
|
|
|
# --------------------------------------------------
|
|
# STEP 3: Deploy Authentik stack
|
|
# --------------------------------------------------
|
|
|
|
- name: Deploy Authentik stack
|
|
ansible.builtin.include_role:
|
|
name: swarm_stack_deploy
|
|
vars:
|
|
stack_name: "authentik"
|
|
stack_compose_src: "{{ playbook_dir }}/../../templates/stacks/authentik.stack.yml"
|
|
# authentik_placement_node resolved from group_vars (swarm-manager-2)
|
|
# Use service-specific state var to avoid self-reference recursion.
|
|
stack_state: "{{ authentik_deploy_state | default('present') }}"
|
|
stack_required_external_networks:
|
|
- proxy-net
|
|
stack_required_directories:
|
|
- /mnt/homelab/apps/authentik
|
|
- /mnt/homelab/apps/authentik/data
|
|
- /mnt/homelab/apps/authentik/data/database
|
|
- /mnt/homelab/apps/authentik/data/redis
|
|
- /mnt/homelab/apps/authentik/data/media
|
|
- /mnt/homelab/apps/authentik/data/config
|
|
- /mnt/homelab/apps/authentik/data/blueprints
|
|
when: inventory_hostname == authentik_deploy_target
|
|
|
|
# --------------------------------------------------
|
|
# STEP 4: Wait for service convergence
|
|
# --------------------------------------------------
|
|
|
|
- name: Wait for Authentik server service to converge
|
|
ansible.builtin.command: >
|
|
docker service ls --filter name=authentik_authentik-server --format '{{ "{{" }}.Replicas{{ "}}" }}'
|
|
register: _authentik_server_replicas
|
|
retries: 18
|
|
delay: 10
|
|
until: _authentik_server_replicas.stdout is search('1/1')
|
|
changed_when: false
|
|
when:
|
|
- inventory_hostname == authentik_deploy_target
|
|
- authentik_deploy_state | default('present') == 'present'
|
|
- not ansible_check_mode
|
|
tags: [verify]
|
|
|
|
- name: Wait for Authentik worker service to converge
|
|
ansible.builtin.command: >
|
|
docker service ls --filter name=authentik_authentik-worker --format '{{ "{{" }}.Replicas{{ "}}" }}'
|
|
register: _authentik_worker_replicas
|
|
retries: 18
|
|
delay: 10
|
|
until: _authentik_worker_replicas.stdout is search('1/1')
|
|
changed_when: false
|
|
when:
|
|
- inventory_hostname == authentik_deploy_target
|
|
- authentik_deploy_state | default('present') == 'present'
|
|
- not ansible_check_mode
|
|
tags: [verify]
|
|
|
|
- name: Report Authentik deployment result
|
|
ansible.builtin.debug:
|
|
msg:
|
|
- "================================================"
|
|
- "Authentik deployment complete."
|
|
- "================================================"
|
|
- "Stack : authentik"
|
|
- "Manager : {{ inventory_hostname }} ({{ ansible_host | default('') }})"
|
|
- "URL : https://sso.castaldifamily.com"
|
|
- "Data root : /mnt/homelab/apps/authentik"
|
|
- "Services : authentik-postgres, authentik-redis, authentik-server, authentik-worker"
|
|
- "================================================"
|
|
when: inventory_hostname == authentik_deploy_target
|
|
tags: [always] |