redeploy authentic policies as code.

This commit is contained in:
2026-03-21 14:00:37 -05:00
parent 9ad585681f
commit c087f32355
26 changed files with 1408 additions and 240 deletions

View File

@@ -0,0 +1,6 @@
---
- name: Apply common role
hosts: "{{ target | default('all') }}"
become: true
roles:
- common

View File

@@ -0,0 +1,7 @@
---
- name: Enroll host as step-ca SSH client
hosts: "{{ target | default('all') }}"
become: true
tasks:
- name: Include step-ca client enrollment
ansible.builtin.include_tasks: roles/common/tasks/step_ca_client.yml

View File

@@ -0,0 +1,15 @@
---
# ------------------------------------------------------------------------------
# FILE: playbooks/install_talosctl.yml
# DESCRIPTION: Install talosctl on city-hall for managing the fastpass cluster.
# USAGE:
# cd ansible
# ansible-playbook -i inventory.yml playbooks/install_talosctl.yml
# ------------------------------------------------------------------------------
- name: Install talosctl
hosts: talos_control
become: true
roles:
- talosctl

View File

@@ -1,20 +1,25 @@
# ------------------------------------------------------------------------------
---
# ──────────────────────────────────────────────────────────────────────────────
# FILE: roles/authentik/tasks/main.yml
# DESCRIPTION: Deploys Authentik from boilerplate-generated files in the repo.
# The compose.yaml lives in boilerplates/authentik/.
# The .env file is templated to inject secrets from Ansible vault.
# ------------------------------------------------------------------------------
# Custom blueprints are synced from boilerplates/authentik/blueprints/.
# ──────────────────────────────────────────────────────────────────────────────
- name: Create Authentik directory structure
file:
path: "{{ authentik_base_dir }}"
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: "{{ ansible_user }}"
group: docker
mode: '0775'
loop:
- "{{ authentik_base_dir }}"
- "{{ authentik_base_dir }}/blueprints"
- name: Copy compose.yaml from boilerplate
copy:
ansible.builtin.copy:
src: "{{ playbook_dir }}/../../boilerplates/authentik/compose.yaml"
dest: "{{ authentik_base_dir }}/compose.yaml"
owner: "{{ ansible_user }}"
@@ -22,8 +27,17 @@
mode: '0644'
notify: restart authentik
- name: Sync custom blueprints
ansible.builtin.copy:
src: "{{ playbook_dir }}/../../boilerplates/authentik/blueprints/"
dest: "{{ authentik_base_dir }}/blueprints/"
owner: "{{ ansible_user }}"
group: docker
mode: '0644'
notify: restart authentik
- name: Deploy .env with secrets from vault
template:
ansible.builtin.template:
src: env.j2
dest: "{{ authentik_base_dir }}/.env"
owner: "{{ ansible_user }}"

View File

@@ -2,3 +2,10 @@
AUTHENTIK_SECRET_KEY={{ authentik_secret_key }}
DATABASE_PASSWORD={{ authentik_database_password }}
AUTHENTIK_ADMIN_PASSWORD={{ authentik_admin_password }}
# OIDC client credentials for blueprints
# These are substituted into blueprint YAML via ${VAR} syntax
PROXMOX_OIDC_CLIENT_ID={{ vault_proxmox_oidc_client_id }}
PROXMOX_OIDC_CLIENT_SECRET={{ vault_proxmox_oidc_client_secret }}
STEPCA_OIDC_CLIENT_ID={{ vault_stepca_oidc_client_id }}
STEPCA_OIDC_CLIENT_SECRET={{ vault_stepca_oidc_client_secret }}

View File

@@ -10,7 +10,7 @@
name: "{{ inventory_hostname | replace('_', '-') }}"
- name: Set timezone
timezone:
community.general.timezone:
name: "{{ common_timezone }}"
- name: Update apt cache
@@ -52,3 +52,30 @@
# --- Step-CA SSH certificate enrollment ---
- name: Enroll host in step-ca SSH CA
include_tasks: step_ca_client.yml
- name: Create authorized principals directory
ansible.builtin.file:
path: /etc/ssh/auth_principals
state: directory
owner: root
group: root
mode: '0755'
- name: Create authorized principals files
ansible.builtin.copy:
content: "{{ item.principals | join('\n') }}\n"
dest: "/etc/ssh/auth_principals/{{ item.local_user }}"
owner: root
group: root
mode: '0644'
loop: "{{ step_ca_principal_mappings }}"
notify: restart sshd
- name: Configure sshd AuthorizedPrincipalsFile
ansible.builtin.lineinfile:
path: /etc/ssh/sshd_config
line: "AuthorizedPrincipalsFile /etc/ssh/auth_principals/%u"
regexp: "^#?AuthorizedPrincipalsFile"
state: present
notify: restart sshd

View File

@@ -95,3 +95,29 @@
community.docker.docker_compose_v2:
project_src: "{{ stepca_base_dir }}"
state: present
- name: Template OIDC provisioner patch script
ansible.builtin.template:
src: patch_oidc_provisioner.py.j2
dest: "{{ stepca_base_dir }}/patch_oidc_provisioner.py"
owner: root
group: root
mode: "0700"
no_log: true
- name: Patch OIDC provisioner in ca.json
ansible.builtin.command:
cmd: python3 {{ stepca_base_dir }}/patch_oidc_provisioner.py
register: oidc_patch_result
changed_when: "'CHANGED' in oidc_patch_result.stdout"
- name: Restart step-ca if OIDC provisioner changed
community.docker.docker_compose_v2:
project_src: "{{ stepca_base_dir }}"
state: restarted
when: oidc_patch_result is changed
- name: Clean up patch script
ansible.builtin.file:
path: "{{ stepca_base_dir }}/patch_oidc_provisioner.py"
state: absent

View File

@@ -0,0 +1,57 @@
#!/usr/bin/env python3
# Managed by Ansible — do not edit manually
# Patches the OIDC provisioner in step-ca's ca.json with values from Ansible vars
import json
import sys
ca_json_path = "{{ stepca_data_dir }}/config/ca.json"
provisioner_name = "{{ stepca_oidc_provisioner_name }}"
desired_domains = {{ stepca_oidc_domains | to_json }}
desired_client_id = "{{ stepca_oidc_client_id }}"
desired_client_secret = "{{ stepca_oidc_client_secret }}"
desired_config_endpoint = "{{ stepca_oidc_configuration_endpoint }}"
desired_listen_address = "{{ stepca_oidc_listen_address }}"
with open(ca_json_path) as f:
cfg = json.load(f)
changed = False
found = False
for p in cfg["authority"]["provisioners"]:
if p.get("name") == provisioner_name and p.get("type") == "OIDC":
found = True
updates = {
"domains": desired_domains,
"clientID": desired_client_id,
"clientSecret": desired_client_secret,
"configurationEndpoint": desired_config_endpoint,
"listenAddress": desired_listen_address,
}
for key, value in updates.items():
if p.get(key) != value:
p[key] = value
changed = True
break
if not found:
# Add the OIDC provisioner if it doesn't exist
cfg["authority"]["provisioners"].append({
"type": "OIDC",
"name": provisioner_name,
"clientID": desired_client_id,
"clientSecret": desired_client_secret,
"configurationEndpoint": desired_config_endpoint,
"domains": desired_domains,
"listenAddress": desired_listen_address,
"claims": {"enableSSHCA": True},
"options": {"x509": {}, "ssh": {}},
})
changed = True
if changed:
with open(ca_json_path, "w") as f:
json.dump(cfg, f, indent=8)
print("CHANGED")
else:
print("OK")

View File

@@ -0,0 +1,15 @@
---
# ------------------------------------------------------------------------------
# FILE: roles/talosctl/defaults/main.yml
# DESCRIPTION: Default variables for talosctl installation.
# ------------------------------------------------------------------------------
# Pin to a specific version for reproducibility.
# Update this when upgrading the fastpass cluster.
talosctl_version: "v1.12.4"
# Architecture of the target host (amd64 or arm64)
talosctl_arch: "amd64"
# Where to install the binary
talosctl_install_dir: "/usr/local/bin"

View File

@@ -0,0 +1,51 @@
---
# ------------------------------------------------------------------------------
# FILE: roles/talosctl/tasks/main.yml
# DESCRIPTION: Install talosctl binary from GitHub releases.
# Downloads a pinned version, installs to /usr/local/bin,
# and verifies the installation.
# ------------------------------------------------------------------------------
- name: Check if talosctl is already installed
stat:
path: "{{ talosctl_install_dir }}/talosctl"
register: talosctl_binary
- name: Get installed talosctl version
command: "{{ talosctl_install_dir }}/talosctl version --client --short"
register: talosctl_installed_version
changed_when: false
failed_when: false
when: talosctl_binary.stat.exists
- name: Install talosctl
when: >
not talosctl_binary.stat.exists or
talosctl_version not in (talosctl_installed_version.stdout | default(''))
block:
- name: Download talosctl {{ talosctl_version }}
get_url:
url: "https://github.com/siderolabs/talos/releases/download/{{ talosctl_version }}/talosctl-linux-{{ talosctl_arch }}"
dest: "/tmp/talosctl"
mode: "0755"
- name: Install talosctl to {{ talosctl_install_dir }}
copy:
src: "/tmp/talosctl"
dest: "{{ talosctl_install_dir }}/talosctl"
mode: "0755"
remote_src: true
- name: Clean up downloaded binary
file:
path: "/tmp/talosctl"
state: absent
- name: Verify talosctl installation
command: "{{ talosctl_install_dir }}/talosctl version --client --short"
register: talosctl_verify
changed_when: false
- name: Display installed talosctl version
debug:
msg: "talosctl installed: {{ talosctl_verify.stdout }}"

View File

@@ -0,0 +1,17 @@
---
# ------------------------------------------------------------------------------
# FILE: roles/talosctl/meta/main.yml
# ------------------------------------------------------------------------------
galaxy_info:
author: rblundon
description: Install talosctl CLI for managing TalosOS clusters
license: MIT
min_ansible_version: "2.14"
platforms:
- name: Ubuntu
versions:
- noble
dependencies:
- role: common