fix: Remove unnecessary container registry step
This commit is contained in:
2
TODO.txt
2
TODO.txt
@@ -1 +1 @@
|
|||||||
- Replace alvaroaleman.freeipa-client with https://galaxy.ansible.com/freeipa/ansible_freeipa
|
- Setup Grafana to use Keycloak
|
||||||
63
ansible-navigator.yml.old
Normal file
63
ansible-navigator.yml.old
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
# cspell:ignore cmdline, workdir
|
||||||
|
---
|
||||||
|
ansible-navigator:
|
||||||
|
ansible:
|
||||||
|
config:
|
||||||
|
help: false
|
||||||
|
# Inventory is set in ansible.cfg. Override at runtime with -i if needed.
|
||||||
|
|
||||||
|
execution-environment:
|
||||||
|
container-engine: podman
|
||||||
|
enabled: true
|
||||||
|
image: aap.toal.ca/ee-demo:latest
|
||||||
|
pull:
|
||||||
|
policy: missing
|
||||||
|
|
||||||
|
environment-variables:
|
||||||
|
pass:
|
||||||
|
- OP_SERVICE_ACCOUNT_TOKEN # 1Password service account (vault)
|
||||||
|
- OP_CONNECT_HOST # 1Password Connect server (alternative)
|
||||||
|
- OP_CONNECT_TOKEN
|
||||||
|
- CONTROLLER_HOST # AAP / AWX controller
|
||||||
|
- CONTROLLER_OAUTH_TOKEN
|
||||||
|
- CONTROLLER_USERNAME
|
||||||
|
- CONTROLLER_PASSWORD
|
||||||
|
- AAP_HOSTNAME # Newer AAP naming (same controller)
|
||||||
|
- AAP_USERNAME
|
||||||
|
- AAP_PASSWORD
|
||||||
|
- SATELLITE_SERVER_URL
|
||||||
|
- SATELLITE_USERNAME
|
||||||
|
- SATELLITE_PASSWORD
|
||||||
|
- SATELLITE_VALIDATE_CERTS
|
||||||
|
- NETBOX_API
|
||||||
|
- NETBOX_API_TOKEN
|
||||||
|
- NETBOX_TOKEN
|
||||||
|
|
||||||
|
# Volume mounts are not merged across config files - all required mounts
|
||||||
|
# must be listed here when a project config is present.
|
||||||
|
volume-mounts:
|
||||||
|
# 1Password SSH agent socket (required for vault-id-from-op-client.sh)
|
||||||
|
- src: "/home/ptoal/.1password/agent.sock"
|
||||||
|
dest: "/root/.1password/agent.sock"
|
||||||
|
options: "Z"
|
||||||
|
# Ansible utilities
|
||||||
|
- src: "/home/ptoal/.ansible/utils/"
|
||||||
|
dest: "/root/.ansible/utils"
|
||||||
|
options: "Z"
|
||||||
|
# Project-local collections (toallab.infra and others not in the EE image)
|
||||||
|
- src: "collections"
|
||||||
|
dest: "/runner/project/collections"
|
||||||
|
options: "Z"
|
||||||
|
- src: "~/.kube/config"
|
||||||
|
dest: "/root/.kube/config"
|
||||||
|
options: "ro"
|
||||||
|
|
||||||
|
|
||||||
|
logging:
|
||||||
|
level: warning
|
||||||
|
file: /tmp/ansible-navigator.log
|
||||||
|
|
||||||
|
mode: stdout
|
||||||
|
|
||||||
|
playbook-artifact:
|
||||||
|
enable: false
|
||||||
@@ -51,8 +51,12 @@
|
|||||||
# e.g. platform 'aap' in namespace 'aap' → aap-aap.apps.openshift.toal.ca
|
# e.g. platform 'aap' in namespace 'aap' → aap-aap.apps.openshift.toal.ca
|
||||||
__aap_platform_name: "{{ aap_operator_platform_name | default('aap') }}"
|
__aap_platform_name: "{{ aap_operator_platform_name | default('aap') }}"
|
||||||
__aap_namespace: "{{ aap_operator_namespace | default('aap') }}"
|
__aap_namespace: "{{ aap_operator_namespace | default('aap') }}"
|
||||||
|
# Use custom gateway hostname if set, otherwise fall back to auto-generated route
|
||||||
|
__aap_gateway_host: >-
|
||||||
|
{{ aap_operator_gateway_route_host
|
||||||
|
| default(__aap_platform_name + '-' + __aap_namespace + '.apps.' + ocp_cluster_name + '.' + ocp_base_domain) }}
|
||||||
__aap_oidc_redirect_uris:
|
__aap_oidc_redirect_uris:
|
||||||
- "https://{{ __aap_platform_name }}-{{ __aap_namespace }}.apps.{{ ocp_cluster_name }}.{{ ocp_base_domain }}/accounts/profile/callback/"
|
- "https://{{ __aap_gateway_host }}/accounts/profile/callback/"
|
||||||
|
|
||||||
module_defaults:
|
module_defaults:
|
||||||
middleware_automation.keycloak.keycloak_client:
|
middleware_automation.keycloak.keycloak_client:
|
||||||
@@ -119,7 +123,7 @@
|
|||||||
- " Redirect : {{ __aap_oidc_redirect_uris | join(', ') }}"
|
- " Redirect : {{ __aap_oidc_redirect_uris | join(', ') }}"
|
||||||
- ""
|
- ""
|
||||||
- "Set in host_vars for the aap host:"
|
- "Set in host_vars for the aap host:"
|
||||||
- " aap_gateway_url: https://{{ __aap_platform_name }}-{{ __aap_namespace }}.apps.{{ ocp_cluster_name }}.{{ ocp_base_domain }}"
|
- " aap_gateway_url: https://{{ __aap_gateway_host }}"
|
||||||
- " aap_oidc_issuer: {{ __aap_keycloak_api_url }}/realms/{{ keycloak_realm }}"
|
- " aap_oidc_issuer: {{ __aap_keycloak_api_url }}/realms/{{ keycloak_realm }}"
|
||||||
- ""
|
- ""
|
||||||
- "Then run: --tags aap_configure_oidc to register the authenticator in AAP."
|
- "Then run: --tags aap_configure_oidc to register the authenticator in AAP."
|
||||||
|
|||||||
151
playbooks/deploy_vault.yml
Normal file
151
playbooks/deploy_vault.yml
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
---
|
||||||
|
# Deploy and configure HashiCorp Vault CE on TrueNAS Scale.
|
||||||
|
#
|
||||||
|
# Vault is deployed as a TrueNAS custom app (Docker Compose).
|
||||||
|
# This playbook handles post-deploy configuration only — it does NOT install Vault.
|
||||||
|
# See: docs/ for the TrueNAS compose YAML and vault.hcl required before running.
|
||||||
|
#
|
||||||
|
# Prerequisites:
|
||||||
|
# - Vault running on TrueNAS and accessible at vault_url
|
||||||
|
# - vault host/group in inventory with vault_url and vault_oidc_issuer set
|
||||||
|
#
|
||||||
|
# Keycloak OIDC prerequisites (--tags vault_configure_keycloak,vault_configure_oidc):
|
||||||
|
# - Keycloak realm exists (configured via deploy_openshift.yml)
|
||||||
|
# - vault_vault_oidc_client_secret in 1Password (or it will be generated and displayed)
|
||||||
|
# - In host_vars for the vault host:
|
||||||
|
# vault_url: "http://nas.lan.toal.ca:8200"
|
||||||
|
# vault_oidc_issuer: "https://keycloak.apps.<cluster>.<domain>/realms/<realm>"
|
||||||
|
#
|
||||||
|
# Play order:
|
||||||
|
# Play 0: vault_configure_keycloak — Create Keycloak OIDC client for Vault
|
||||||
|
# Play 1: vault_init — Initialize Vault, display keys for 1Password
|
||||||
|
# Play 2: (default) — Unseal + configure OIDC authentication
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ansible-navigator run playbooks/deploy_vault.yml --tags vault_configure_keycloak
|
||||||
|
# ansible-navigator run playbooks/deploy_vault.yml --tags vault_init
|
||||||
|
# ansible-navigator run playbooks/deploy_vault.yml
|
||||||
|
# ansible-navigator run playbooks/deploy_vault.yml --tags vault_configure_keycloak,vault_init
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Play 0: Create Keycloak OIDC client for Vault (optional)
|
||||||
|
# Runs on openshift hosts to access keycloak_url/keycloak_realm host vars.
|
||||||
|
# Creates the OIDC client in Keycloak with the correct Vault callback URIs.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
- name: Configure Keycloak OIDC client for Vault
|
||||||
|
hosts: openshift
|
||||||
|
gather_facts: false
|
||||||
|
connection: local
|
||||||
|
|
||||||
|
tags:
|
||||||
|
- never
|
||||||
|
- vault_configure_keycloak
|
||||||
|
|
||||||
|
vars:
|
||||||
|
__vault_keycloak_api_url: "{{ keycloak_url }}{{ keycloak_context | default('') }}"
|
||||||
|
__vault_oidc_client_id: "{{ vault_oidc_client_id | default('vault') }}"
|
||||||
|
__vault_url: "{{ hostvars[groups['vault'][0]]['vault_url'] | default('http://nas.lan.toal.ca:8200') }}"
|
||||||
|
|
||||||
|
module_defaults:
|
||||||
|
middleware_automation.keycloak.keycloak_client:
|
||||||
|
auth_client_id: admin-cli
|
||||||
|
auth_keycloak_url: "{{ __vault_keycloak_api_url }}"
|
||||||
|
auth_realm: master
|
||||||
|
auth_username: "{{ keycloak_admin_user }}"
|
||||||
|
auth_password: "{{ vault_keycloak_admin_password }}"
|
||||||
|
validate_certs: "{{ keycloak_validate_certs | default(true) }}"
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Set Vault OIDC client secret (vault value or generated)
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
__vault_oidc_client_secret: "{{ vault_vault_oidc_client_secret | default(lookup('community.general.random_string', length=32, special=false)) }}"
|
||||||
|
__vault_oidc_secret_generated: "{{ vault_vault_oidc_client_secret is not defined }}"
|
||||||
|
no_log: true
|
||||||
|
|
||||||
|
- name: Create Vault OIDC client in Keycloak
|
||||||
|
middleware_automation.keycloak.keycloak_client:
|
||||||
|
realm: "{{ keycloak_realm }}"
|
||||||
|
client_id: "{{ __vault_oidc_client_id }}"
|
||||||
|
name: "HashiCorp Vault"
|
||||||
|
description: "OIDC client for Vault on TrueNAS"
|
||||||
|
enabled: true
|
||||||
|
protocol: openid-connect
|
||||||
|
public_client: false
|
||||||
|
standard_flow_enabled: true
|
||||||
|
implicit_flow_enabled: false
|
||||||
|
direct_access_grants_enabled: false
|
||||||
|
service_accounts_enabled: false
|
||||||
|
secret: "{{ __vault_oidc_client_secret }}"
|
||||||
|
redirect_uris:
|
||||||
|
- "{{ __vault_url }}/ui/vault/auth/oidc/oidc/callback"
|
||||||
|
- "http://localhost:8250/oidc/callback"
|
||||||
|
web_origins:
|
||||||
|
- "+"
|
||||||
|
protocol_mappers:
|
||||||
|
- name: groups
|
||||||
|
protocol: openid-connect
|
||||||
|
protocolMapper: oidc-group-membership-mapper
|
||||||
|
config:
|
||||||
|
full.path: "false"
|
||||||
|
id.token.claim: "true"
|
||||||
|
access.token.claim: "true"
|
||||||
|
userinfo.token.claim: "true"
|
||||||
|
claim.name: groups
|
||||||
|
state: present
|
||||||
|
no_log: "{{ keycloak_no_log | default(true) }}"
|
||||||
|
|
||||||
|
- name: Display generated client secret (save this to vault!)
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg:
|
||||||
|
- "*** GENERATED VAULT OIDC CLIENT SECRET — SAVE THIS TO 1PASSWORD ***"
|
||||||
|
- "vault_vault_oidc_client_secret: {{ __vault_oidc_client_secret }}"
|
||||||
|
- ""
|
||||||
|
- "Save to 1Password and reference as vault_vault_oidc_client_secret."
|
||||||
|
when: __vault_oidc_secret_generated | bool
|
||||||
|
|
||||||
|
- name: Display Keycloak Vault OIDC configuration summary
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg:
|
||||||
|
- "Keycloak Vault OIDC client configured:"
|
||||||
|
- " Realm : {{ keycloak_realm }}"
|
||||||
|
- " Client : {{ __vault_oidc_client_id }}"
|
||||||
|
- " Issuer : {{ __vault_keycloak_api_url }}/realms/{{ keycloak_realm }}"
|
||||||
|
- ""
|
||||||
|
- "Set in host_vars for the vault host:"
|
||||||
|
- " vault_oidc_issuer: {{ __vault_keycloak_api_url }}/realms/{{ keycloak_realm }}"
|
||||||
|
- ""
|
||||||
|
- "Then run: --tags vault_init (if not done) then the default play."
|
||||||
|
verbosity: 1
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Play 1: Initialize Vault (optional, one-time)
|
||||||
|
# Initializes Vault and displays root token + unseal keys for saving to 1Password.
|
||||||
|
# Fails after init intentionally — save credentials then run the default play.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
- name: Initialize Vault
|
||||||
|
hosts: vault
|
||||||
|
gather_facts: false
|
||||||
|
connection: local
|
||||||
|
|
||||||
|
tags:
|
||||||
|
- never
|
||||||
|
- vault_init
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Run Vault init tasks
|
||||||
|
ansible.builtin.include_role:
|
||||||
|
name: vault_setup
|
||||||
|
tasks_from: init.yml
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Play 2: Unseal and configure Vault OIDC authentication (default)
|
||||||
|
# Requires vault_vault_root_token and vault_vault_oidc_client_secret in 1Password.
|
||||||
|
# Optionally unseals if vault_unseal_keys is provided and Vault is sealed.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
- name: Configure Vault OIDC authentication
|
||||||
|
hosts: vault
|
||||||
|
gather_facts: false
|
||||||
|
connection: local
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- role: vault_setup
|
||||||
@@ -25,5 +25,8 @@ aap_operator_hub_file_storage_size: 10Gi
|
|||||||
aap_operator_admin_user: admin
|
aap_operator_admin_user: admin
|
||||||
|
|
||||||
# --- Routing (optional) ---
|
# --- Routing (optional) ---
|
||||||
|
# Set to a custom hostname to override the auto-generated Gateway route (primary UI/API entry point)
|
||||||
|
# aap_operator_gateway_route_host: aap.example.com
|
||||||
# Set to a custom hostname to override the auto-generated Controller route
|
# Set to a custom hostname to override the auto-generated Controller route
|
||||||
# aap_operator_controller_route_host: aap.example.com
|
# aap_operator_controller_route_host: controller.example.com
|
||||||
|
|
||||||
|
|||||||
@@ -58,10 +58,18 @@ argument_specs:
|
|||||||
description: Admin username for the platform.
|
description: Admin username for the platform.
|
||||||
type: str
|
type: str
|
||||||
default: admin
|
default: admin
|
||||||
aap_operator_controller_route_host:
|
aap_operator_gateway_route_host:
|
||||||
description: >
|
description: >
|
||||||
Custom hostname for the Automation Controller Route.
|
Custom hostname for the AAP Gateway Route (primary UI/API entry point in AAP 2.5+).
|
||||||
When set, overrides the auto-generated route hostname (e.g. aap.example.com).
|
When set, overrides the auto-generated gateway route (e.g. aap.example.com).
|
||||||
Leave unset to use the default apps subdomain route.
|
Leave unset to use the default apps subdomain route.
|
||||||
type: str
|
type: str
|
||||||
required: false
|
required: false
|
||||||
|
aap_operator_controller_route_host:
|
||||||
|
description: >
|
||||||
|
Custom hostname for the Automation Controller Route.
|
||||||
|
When set, overrides the auto-generated controller route hostname.
|
||||||
|
Leave unset to use the default apps subdomain route.
|
||||||
|
type: str
|
||||||
|
required: false
|
||||||
|
|
||||||
|
|||||||
@@ -116,6 +116,9 @@
|
|||||||
# PostgreSQL storage for all components (RWO)
|
# PostgreSQL storage for all components (RWO)
|
||||||
database:
|
database:
|
||||||
postgres_storage_class: "{{ aap_operator_storage_class }}"
|
postgres_storage_class: "{{ aap_operator_storage_class }}"
|
||||||
|
# Gateway is the primary UI/API entry point in AAP 2.5+
|
||||||
|
gateway:
|
||||||
|
route_host: "{{ aap_operator_gateway_route_host | default(omit) }}"
|
||||||
# Component toggles and per-component config
|
# Component toggles and per-component config
|
||||||
controller:
|
controller:
|
||||||
disabled: "{{ aap_operator_controller_disabled | bool }}"
|
disabled: "{{ aap_operator_controller_disabled | bool }}"
|
||||||
@@ -129,6 +132,48 @@
|
|||||||
eda:
|
eda:
|
||||||
disabled: "{{ aap_operator_eda_disabled | bool }}"
|
disabled: "{{ aap_operator_eda_disabled | bool }}"
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Step 3a: Clear controller route_host if not explicitly set
|
||||||
|
# strategic-merge-patch (state: present) does not remove existing fields,
|
||||||
|
# so we must explicitly remove /spec/controller/route_host if the user
|
||||||
|
# hasn't set aap_operator_controller_route_host (e.g. after switching to
|
||||||
|
# gateway-based routing).
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# The platform operator propagates route_host to the child AutomationController CR.
|
||||||
|
# Both must be cleared or the controller operator will continue to set the old hostname.
|
||||||
|
- name: Remove controller route_host from platform CR (use auto-generated route)
|
||||||
|
kubernetes.core.k8s_json_patch:
|
||||||
|
api_version: aap.ansible.com/v1alpha1
|
||||||
|
kind: AnsibleAutomationPlatform
|
||||||
|
namespace: "{{ aap_operator_namespace }}"
|
||||||
|
name: "{{ aap_operator_platform_name }}"
|
||||||
|
patch:
|
||||||
|
- op: remove
|
||||||
|
path: /spec/controller/route_host
|
||||||
|
when: aap_operator_controller_route_host is not defined
|
||||||
|
failed_when: false # no-op if field doesn't exist
|
||||||
|
|
||||||
|
- name: Remove controller route_host from child AutomationController CR
|
||||||
|
kubernetes.core.k8s_json_patch:
|
||||||
|
api_version: automationcontroller.ansible.com/v1beta1
|
||||||
|
kind: AutomationController
|
||||||
|
namespace: "{{ aap_operator_namespace }}"
|
||||||
|
name: "{{ aap_operator_platform_name }}-controller"
|
||||||
|
patch:
|
||||||
|
- op: remove
|
||||||
|
path: /spec/route_host
|
||||||
|
when: aap_operator_controller_route_host is not defined
|
||||||
|
failed_when: false # no-op if field doesn't exist or CR not yet created
|
||||||
|
|
||||||
|
- name: Delete aap-controller Route so operator recreates with correct hostname
|
||||||
|
kubernetes.core.k8s:
|
||||||
|
api_version: route.openshift.io/v1
|
||||||
|
kind: Route
|
||||||
|
namespace: "{{ aap_operator_namespace }}"
|
||||||
|
name: "{{ aap_operator_platform_name }}-controller"
|
||||||
|
state: absent
|
||||||
|
when: aap_operator_controller_route_host is not defined
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
# Step 4: Wait for platform to be ready
|
# Step 4: Wait for platform to be ready
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
|
|||||||
22
roles/vault_setup/defaults/main.yml
Normal file
22
roles/vault_setup/defaults/main.yml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
# --- Vault API ---
|
||||||
|
vault_url: "http://nas.lan.toal.ca:8200"
|
||||||
|
vault_validate_certs: false
|
||||||
|
|
||||||
|
# --- Init ---
|
||||||
|
vault_init_key_shares: 5
|
||||||
|
vault_init_key_threshold: 3
|
||||||
|
|
||||||
|
# --- OIDC ---
|
||||||
|
vault_oidc_client_id: vault
|
||||||
|
vault_oidc_admin_group: vault-admins
|
||||||
|
vault_oidc_default_ttl: 1h
|
||||||
|
vault_oidc_max_ttl: 8h
|
||||||
|
|
||||||
|
# --- Unseal ---
|
||||||
|
# vault_unseal_keys: [] # list of 3+ unseal key strings (from 1Password)
|
||||||
|
|
||||||
|
# --- Secrets (required, set via vault or host_vars) ---
|
||||||
|
# vault_vault_root_token: # root token from 1Password (required for Play 2)
|
||||||
|
# vault_vault_oidc_client_secret: # OIDC client secret from Keycloak (required for Play 2)
|
||||||
|
# vault_oidc_issuer: # e.g. https://keycloak.apps.openshift.toal.ca/realms/toallab
|
||||||
61
roles/vault_setup/meta/argument_specs.yml
Normal file
61
roles/vault_setup/meta/argument_specs.yml
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
---
|
||||||
|
argument_specs:
|
||||||
|
main:
|
||||||
|
short_description: Configure a running HashiCorp Vault instance
|
||||||
|
description:
|
||||||
|
- Unseals Vault if sealed and unseal keys are provided.
|
||||||
|
- Enables and configures OIDC authentication using Keycloak.
|
||||||
|
- Creates an admin policy and maps a Keycloak group to it.
|
||||||
|
- Requires Vault to already be initialized (use vault_init tag first).
|
||||||
|
options:
|
||||||
|
vault_url:
|
||||||
|
description: Base URL of the Vault API.
|
||||||
|
type: str
|
||||||
|
default: "http://nas.lan.toal.ca:8200"
|
||||||
|
vault_validate_certs:
|
||||||
|
description: Whether to validate TLS certificates for Vault API calls.
|
||||||
|
type: bool
|
||||||
|
default: false
|
||||||
|
vault_vault_root_token:
|
||||||
|
description: Vault root token for API authentication. Required.
|
||||||
|
type: str
|
||||||
|
required: true
|
||||||
|
vault_oidc_issuer:
|
||||||
|
description: OIDC discovery URL base (Keycloak realm URL). Required.
|
||||||
|
type: str
|
||||||
|
required: true
|
||||||
|
vault_vault_oidc_client_secret:
|
||||||
|
description: OIDC client secret from Keycloak. Required.
|
||||||
|
type: str
|
||||||
|
required: true
|
||||||
|
vault_oidc_client_id:
|
||||||
|
description: OIDC client ID registered in Keycloak.
|
||||||
|
type: str
|
||||||
|
default: vault
|
||||||
|
vault_oidc_admin_group:
|
||||||
|
description: Keycloak group name to map to the Vault admin policy.
|
||||||
|
type: str
|
||||||
|
default: vault-admins
|
||||||
|
vault_oidc_default_ttl:
|
||||||
|
description: Default token TTL for OIDC-authenticated tokens.
|
||||||
|
type: str
|
||||||
|
default: 1h
|
||||||
|
vault_oidc_max_ttl:
|
||||||
|
description: Maximum token TTL for OIDC-authenticated tokens.
|
||||||
|
type: str
|
||||||
|
default: 8h
|
||||||
|
vault_unseal_keys:
|
||||||
|
description: >-
|
||||||
|
List of unseal key strings. If provided and Vault is sealed,
|
||||||
|
the role will attempt to unseal using these keys.
|
||||||
|
type: list
|
||||||
|
elements: str
|
||||||
|
default: []
|
||||||
|
vault_init_key_shares:
|
||||||
|
description: Number of key shares for vault operator init.
|
||||||
|
type: int
|
||||||
|
default: 5
|
||||||
|
vault_init_key_threshold:
|
||||||
|
description: Number of key shares required to unseal.
|
||||||
|
type: int
|
||||||
|
default: 3
|
||||||
136
roles/vault_setup/tasks/configure_oidc.yml
Normal file
136
roles/vault_setup/tasks/configure_oidc.yml
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
---
|
||||||
|
# Configure Keycloak OIDC authentication in Vault.
|
||||||
|
#
|
||||||
|
# Creates:
|
||||||
|
# - OIDC auth method (auth/oidc)
|
||||||
|
# - OIDC config pointing to Keycloak realm
|
||||||
|
# - Default OIDC role with groups claim
|
||||||
|
# - Admin ACL policy
|
||||||
|
# - External identity group mapped to vault_oidc_admin_group Keycloak group
|
||||||
|
|
||||||
|
- name: Set Vault API auth headers
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
__vault_headers:
|
||||||
|
X-Vault-Token: "{{ vault_vault_root_token }}"
|
||||||
|
|
||||||
|
- name: Enable OIDC auth method
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "{{ vault_url }}/v1/sys/auth/oidc"
|
||||||
|
method: POST
|
||||||
|
headers: "{{ __vault_headers }}"
|
||||||
|
validate_certs: "{{ vault_validate_certs }}"
|
||||||
|
body_format: json
|
||||||
|
body:
|
||||||
|
type: oidc
|
||||||
|
description: Keycloak OIDC
|
||||||
|
status_code: [200, 204, 400]
|
||||||
|
register: __vault_enable_oidc
|
||||||
|
no_log: true
|
||||||
|
changed_when: __vault_enable_oidc.status in [200, 204]
|
||||||
|
|
||||||
|
- name: Configure OIDC provider (Keycloak)
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "{{ vault_url }}/v1/auth/oidc/config"
|
||||||
|
method: POST
|
||||||
|
headers: "{{ __vault_headers }}"
|
||||||
|
validate_certs: "{{ vault_validate_certs }}"
|
||||||
|
body_format: json
|
||||||
|
body:
|
||||||
|
oidc_discovery_url: "{{ vault_oidc_issuer }}"
|
||||||
|
oidc_client_id: "{{ vault_oidc_client_id }}"
|
||||||
|
oidc_client_secret: "{{ vault_vault_oidc_client_secret }}"
|
||||||
|
default_role: default
|
||||||
|
status_code: [200, 204]
|
||||||
|
no_log: true
|
||||||
|
|
||||||
|
- name: Create default OIDC role
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "{{ vault_url }}/v1/auth/oidc/role/default"
|
||||||
|
method: POST
|
||||||
|
headers: "{{ __vault_headers }}"
|
||||||
|
validate_certs: "{{ vault_validate_certs }}"
|
||||||
|
body_format: json
|
||||||
|
body:
|
||||||
|
bound_audiences:
|
||||||
|
- "{{ vault_oidc_client_id }}"
|
||||||
|
allowed_redirect_uris:
|
||||||
|
- "{{ vault_url }}/ui/vault/auth/oidc/oidc/callback"
|
||||||
|
- "http://localhost:8250/oidc/callback"
|
||||||
|
user_claim: preferred_username
|
||||||
|
groups_claim: groups
|
||||||
|
token_policies:
|
||||||
|
- default
|
||||||
|
token_ttl: "{{ vault_oidc_default_ttl }}"
|
||||||
|
token_max_ttl: "{{ vault_oidc_max_ttl }}"
|
||||||
|
status_code: [200, 204]
|
||||||
|
no_log: true
|
||||||
|
|
||||||
|
- name: Create admin ACL policy
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "{{ vault_url }}/v1/sys/policies/acl/admin"
|
||||||
|
method: POST
|
||||||
|
headers: "{{ __vault_headers }}"
|
||||||
|
validate_certs: "{{ vault_validate_certs }}"
|
||||||
|
body_format: json
|
||||||
|
body:
|
||||||
|
policy: |
|
||||||
|
path "*" {
|
||||||
|
capabilities = ["create", "read", "update", "delete", "list", "sudo"]
|
||||||
|
}
|
||||||
|
status_code: [200, 204]
|
||||||
|
no_log: true
|
||||||
|
|
||||||
|
- name: Create external identity group for admin
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "{{ vault_url }}/v1/identity/group"
|
||||||
|
method: POST
|
||||||
|
headers: "{{ __vault_headers }}"
|
||||||
|
validate_certs: "{{ vault_validate_certs }}"
|
||||||
|
body_format: json
|
||||||
|
body:
|
||||||
|
name: "{{ vault_oidc_admin_group }}"
|
||||||
|
type: external
|
||||||
|
policies:
|
||||||
|
- admin
|
||||||
|
status_code: [200, 204]
|
||||||
|
register: __vault_admin_group
|
||||||
|
no_log: true
|
||||||
|
|
||||||
|
- name: Get OIDC auth method accessor
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "{{ vault_url }}/v1/sys/auth"
|
||||||
|
method: GET
|
||||||
|
headers: "{{ __vault_headers }}"
|
||||||
|
validate_certs: "{{ vault_validate_certs }}"
|
||||||
|
status_code: 200
|
||||||
|
register: __vault_auth_list
|
||||||
|
no_log: true
|
||||||
|
|
||||||
|
- name: Set OIDC accessor fact
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
__vault_oidc_accessor: "{{ __vault_auth_list.json['oidc/'].accessor }}"
|
||||||
|
|
||||||
|
- name: Create group alias mapping Keycloak group to Vault admin group
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "{{ vault_url }}/v1/identity/group-alias"
|
||||||
|
method: POST
|
||||||
|
headers: "{{ __vault_headers }}"
|
||||||
|
validate_certs: "{{ vault_validate_certs }}"
|
||||||
|
body_format: json
|
||||||
|
body:
|
||||||
|
name: "{{ vault_oidc_admin_group }}"
|
||||||
|
mount_accessor: "{{ __vault_oidc_accessor }}"
|
||||||
|
canonical_id: "{{ __vault_admin_group.json.data.id }}"
|
||||||
|
status_code: [200, 204]
|
||||||
|
no_log: true
|
||||||
|
|
||||||
|
- name: Display OIDC configuration summary
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg:
|
||||||
|
- "Vault OIDC configured:"
|
||||||
|
- " Provider : {{ vault_oidc_issuer }}"
|
||||||
|
- " Client : {{ vault_oidc_client_id }}"
|
||||||
|
- " Admin group: {{ vault_oidc_admin_group }}"
|
||||||
|
- ""
|
||||||
|
- "Login at: {{ vault_url }}/ui"
|
||||||
|
- "Select: OIDC → Sign in with Keycloak"
|
||||||
54
roles/vault_setup/tasks/init.yml
Normal file
54
roles/vault_setup/tasks/init.yml
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
---
|
||||||
|
# Initialize Vault. Idempotent: skips if already initialized.
|
||||||
|
# On success, displays root token and unseal keys for manual saving to 1Password.
|
||||||
|
# After saving, rerun the playbook (default play) to complete configuration.
|
||||||
|
|
||||||
|
- name: Check Vault initialization status
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "{{ vault_url }}/v1/sys/init"
|
||||||
|
method: GET
|
||||||
|
validate_certs: "{{ vault_validate_certs }}"
|
||||||
|
register: __vault_init_status
|
||||||
|
|
||||||
|
- name: Skip init (already initialized)
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: "Vault is already initialized. Skipping init."
|
||||||
|
when: __vault_init_status.json.initialized | bool
|
||||||
|
|
||||||
|
- name: Initialize Vault
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "{{ vault_url }}/v1/sys/init"
|
||||||
|
method: POST
|
||||||
|
validate_certs: "{{ vault_validate_certs }}"
|
||||||
|
body_format: json
|
||||||
|
body:
|
||||||
|
secret_shares: "{{ vault_init_key_shares }}"
|
||||||
|
secret_threshold: "{{ vault_init_key_threshold }}"
|
||||||
|
status_code: 200
|
||||||
|
register: __vault_init_result
|
||||||
|
no_log: true
|
||||||
|
when: not __vault_init_status.json.initialized | bool
|
||||||
|
|
||||||
|
- name: Display init output — SAVE TO 1PASSWORD NOW
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg:
|
||||||
|
- "*** VAULT INITIALIZED — SAVE THE FOLLOWING TO 1PASSWORD IMMEDIATELY ***"
|
||||||
|
- ""
|
||||||
|
- "Root Token:"
|
||||||
|
- " vault_vault_root_token: {{ __vault_init_result.json.root_token }}"
|
||||||
|
- ""
|
||||||
|
- "Unseal Keys (need {{ vault_init_key_threshold }} of {{ vault_init_key_shares }}):"
|
||||||
|
- "{% for key in __vault_init_result.json.keys_base64 %} unseal_key_{{ loop.index }}: {{ key }}{% endfor %}"
|
||||||
|
- ""
|
||||||
|
- "Save vault_unseal_keys as a list of {{ vault_init_key_threshold }} key strings in 1Password."
|
||||||
|
- "Save vault_vault_root_token to 1Password."
|
||||||
|
when: not __vault_init_status.json.initialized | bool
|
||||||
|
|
||||||
|
- name: Fail after init — save credentials before continuing
|
||||||
|
ansible.builtin.fail:
|
||||||
|
msg: >-
|
||||||
|
Vault initialization complete.
|
||||||
|
SAVE the root token and unseal keys to 1Password before continuing.
|
||||||
|
Then run the default play to unseal and configure OIDC:
|
||||||
|
ansible-navigator run playbooks/deploy_vault.yml
|
||||||
|
when: not __vault_init_status.json.initialized | bool
|
||||||
51
roles/vault_setup/tasks/main.yml
Normal file
51
roles/vault_setup/tasks/main.yml
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
---
|
||||||
|
# Configures a running, initialized HashiCorp Vault instance.
|
||||||
|
#
|
||||||
|
# Expects Vault to already be initialized (run --tags vault_init first).
|
||||||
|
# Unseals if sealed and vault_unseal_keys is defined.
|
||||||
|
# Then configures OIDC authentication with Keycloak.
|
||||||
|
|
||||||
|
- name: Validate required variables
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- vault_url | length > 0
|
||||||
|
- vault_vault_root_token | default('') | length > 0
|
||||||
|
- vault_oidc_issuer | default('') | length > 0
|
||||||
|
- vault_vault_oidc_client_secret | default('') | length > 0
|
||||||
|
fail_msg: >-
|
||||||
|
vault_vault_root_token, vault_oidc_issuer, and vault_vault_oidc_client_secret
|
||||||
|
are required. Run --tags vault_init first, save credentials to 1Password,
|
||||||
|
then run --tags vault_configure_keycloak,vault_configure_oidc or default play.
|
||||||
|
|
||||||
|
- name: Check Vault status
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "{{ vault_url }}/v1/sys/health"
|
||||||
|
method: GET
|
||||||
|
validate_certs: "{{ vault_validate_certs }}"
|
||||||
|
status_code: [200, 429, 472, 473, 501, 503]
|
||||||
|
register: __vault_health
|
||||||
|
|
||||||
|
- name: Assert Vault is initialized
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- __vault_health.json.initialized | bool
|
||||||
|
fail_msg: >-
|
||||||
|
Vault is not initialized. Run:
|
||||||
|
ansible-navigator run playbooks/deploy_vault.yml --tags vault_init
|
||||||
|
|
||||||
|
- name: Unseal Vault if sealed
|
||||||
|
ansible.builtin.include_tasks: unseal.yml
|
||||||
|
when:
|
||||||
|
- __vault_health.json.sealed | bool
|
||||||
|
- vault_unseal_keys | default([]) | length > 0
|
||||||
|
|
||||||
|
- name: Assert Vault is unsealed
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- not __vault_health.json.sealed | bool or __vault_unsealed | default(false) | bool
|
||||||
|
fail_msg: >-
|
||||||
|
Vault is sealed. Provide vault_unseal_keys (list of unseal key strings) or
|
||||||
|
unseal manually via the Vault UI, then rerun.
|
||||||
|
|
||||||
|
- name: Configure OIDC authentication
|
||||||
|
ansible.builtin.include_tasks: configure_oidc.yml
|
||||||
37
roles/vault_setup/tasks/unseal.yml
Normal file
37
roles/vault_setup/tasks/unseal.yml
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
---
|
||||||
|
# Unseal Vault using keys from vault_unseal_keys list.
|
||||||
|
# Submits keys one at a time until Vault reports unsealed.
|
||||||
|
# Requires vault_init_key_threshold keys in vault_unseal_keys.
|
||||||
|
|
||||||
|
- name: Submit unseal keys
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "{{ vault_url }}/v1/sys/unseal"
|
||||||
|
method: POST
|
||||||
|
validate_certs: "{{ vault_validate_certs }}"
|
||||||
|
body_format: json
|
||||||
|
body:
|
||||||
|
key: "{{ item }}"
|
||||||
|
status_code: 200
|
||||||
|
loop: "{{ vault_unseal_keys[:vault_init_key_threshold] }}"
|
||||||
|
register: __vault_unseal_result
|
||||||
|
no_log: true
|
||||||
|
|
||||||
|
- name: Check unseal status
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "{{ vault_url }}/v1/sys/health"
|
||||||
|
method: GET
|
||||||
|
validate_certs: "{{ vault_validate_certs }}"
|
||||||
|
status_code: [200, 429]
|
||||||
|
register: __vault_health
|
||||||
|
|
||||||
|
- name: Assert Vault unsealed successfully
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- not __vault_health.json.sealed | bool
|
||||||
|
fail_msg: >-
|
||||||
|
Vault is still sealed after submitting {{ vault_init_key_threshold }} keys.
|
||||||
|
Check that vault_unseal_keys contains the correct keys and try again.
|
||||||
|
|
||||||
|
- name: Register unseal success
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
__vault_unsealed: true
|
||||||
Reference in New Issue
Block a user