Claude assisted cleanup
This commit is contained in:
321
playbooks/configure_sno_oidc.yml
Normal file
321
playbooks/configure_sno_oidc.yml
Normal file
@@ -0,0 +1,321 @@
|
||||
---
|
||||
# Configure OIDC authentication on SNO OpenShift using Keycloak as identity provider.
|
||||
#
|
||||
# This playbook creates a Keycloak client for OpenShift and configures the
|
||||
# cluster's OAuth resource to use Keycloak as an OpenID Connect identity provider.
|
||||
#
|
||||
# Prerequisites:
|
||||
# - SNO cluster is installed and accessible (see deploy_openshift.yml)
|
||||
# - Keycloak server is running and accessible
|
||||
# - oc binary available in the EE PATH (override with oc_binary if running outside EE)
|
||||
# - middleware_automation.keycloak collection installed (see collections/requirements.yml)
|
||||
#
|
||||
# Inventory requirements:
|
||||
# openshift group (e.g. sno.openshift.toal.ca)
|
||||
# host_vars: ocp_cluster_name, ocp_base_domain, sno_install_dir
|
||||
# secrets: vault_keycloak_admin_password
|
||||
# vault_oidc_client_secret (optional — auto-generated if omitted; save the output!)
|
||||
#
|
||||
# Key variables:
|
||||
# keycloak_url - Keycloak base URL (e.g. https://keycloak.toal.ca)
|
||||
# keycloak_realm - Keycloak realm name (e.g. toallab)
|
||||
# keycloak_admin_user - Keycloak admin username (default: admin)
|
||||
# keycloak_context - URL prefix for Keycloak API:
|
||||
# "" for Quarkus/modern Keycloak (default)
|
||||
# "/auth" for legacy JBoss/WildFly Keycloak
|
||||
# vault_keycloak_admin_password - Keycloak admin password (vaulted)
|
||||
# vault_oidc_client_secret - OIDC client secret (vaulted, optional — auto-generated if omitted)
|
||||
# oidc_provider_name - IdP name shown in OpenShift login (default: keycloak)
|
||||
# oidc_client_id - Client ID in Keycloak (default: openshift)
|
||||
#
|
||||
# Optional variables:
|
||||
# oidc_admin_groups - List of Keycloak groups to grant cluster-admin (default: [])
|
||||
# oidc_ca_cert_file - Local path to CA cert PEM for Keycloak TLS (private CA only)
|
||||
# oc_binary - Path to oc binary (default: oc from EE PATH)
|
||||
# oc_kubeconfig - Path to kubeconfig (default: sno_install_dir/auth/kubeconfig)
|
||||
#
|
||||
# Usage:
|
||||
# op run --env-file=~/.ansible.zshenv -- ansible-navigator run playbooks/configure_sno_oidc.yml
|
||||
# op run --env-file=~/.ansible.zshenv -- ansible-navigator run playbooks/configure_sno_oidc.yml --tags keycloak
|
||||
# op run --env-file=~/.ansible.zshenv -- ansible-navigator run playbooks/configure_sno_oidc.yml --tags openshift
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Play 1: Configure Keycloak OIDC client for OpenShift
|
||||
# ---------------------------------------------------------------------------
|
||||
- name: Configure Keycloak OIDC client for OpenShift
|
||||
hosts: openshift
|
||||
gather_facts: false
|
||||
connection: local
|
||||
|
||||
tags: keycloak
|
||||
|
||||
vars:
|
||||
# Set to "/auth" for legacy JBoss/WildFly-based Keycloak; leave empty for Quarkus (v17+)
|
||||
keycloak_context: ""
|
||||
oidc_provider_name: keycloak
|
||||
oidc_client_id: openshift
|
||||
oidc_redirect_uri: "https://oauth-openshift.apps.{{ ocp_cluster_name }}.{{ ocp_base_domain }}/oauth2callback/{{ oidc_provider_name }}"
|
||||
__oidc_keycloak_api_url: "{{ keycloak_url }}{{ keycloak_context }}"
|
||||
|
||||
module_defaults:
|
||||
middleware_automation.keycloak.keycloak_realm:
|
||||
auth_client_id: admin-cli
|
||||
auth_keycloak_url: "{{ __oidc_keycloak_api_url }}"
|
||||
auth_realm: master
|
||||
auth_username: "{{ keycloak_admin_user }}"
|
||||
auth_password: "{{ vault_keycloak_admin_password }}"
|
||||
validate_certs: "{{ keycloak_validate_certs | default(true) }}"
|
||||
middleware_automation.keycloak.keycloak_client:
|
||||
auth_client_id: admin-cli
|
||||
auth_keycloak_url: "{{ __oidc_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:
|
||||
|
||||
# Generate a random 32-char alphanumeric secret if vault_oidc_client_secret is not supplied.
|
||||
# The generated value is stored in __oidc_client_secret and displayed after the play so it
|
||||
# can be vaulted and re-used on subsequent runs.
|
||||
- name: Set OIDC client secret (use vault value or generate random)
|
||||
ansible.builtin.set_fact:
|
||||
__oidc_client_secret: "{{ vault_oidc_client_secret | default(lookup('community.general.random_string', length=32, special=false)) }}"
|
||||
__oidc_secret_generated: "{{ vault_oidc_client_secret is not defined }}"
|
||||
no_log: true
|
||||
|
||||
- name: Ensure Keycloak realm exists
|
||||
middleware_automation.keycloak.keycloak_realm:
|
||||
realm: "{{ keycloak_realm }}"
|
||||
id: "{{ keycloak_realm }}"
|
||||
display_name: "{{ keycloak_realm_display_name | default(keycloak_realm | title) }}"
|
||||
enabled: true
|
||||
state: present
|
||||
no_log: "{{ keycloak_no_log | default(true) }}"
|
||||
|
||||
- name: Create OpenShift OIDC client in Keycloak
|
||||
middleware_automation.keycloak.keycloak_client:
|
||||
realm: "{{ keycloak_realm }}"
|
||||
client_id: "{{ oidc_client_id }}"
|
||||
name: "OpenShift - {{ ocp_cluster_name }}"
|
||||
description: "OIDC client for OpenShift cluster {{ ocp_cluster_name }}.{{ ocp_base_domain }}"
|
||||
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: "{{ __oidc_client_secret }}"
|
||||
redirect_uris:
|
||||
- "{{ oidc_redirect_uri }}"
|
||||
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 OIDC CLIENT SECRET — SAVE THIS TO VAULT ***"
|
||||
- "vault_oidc_client_secret: {{ __oidc_client_secret }}"
|
||||
- ""
|
||||
- "Set this in host_vars or pass as --extra-vars on future runs."
|
||||
when: __oidc_secret_generated | bool
|
||||
|
||||
- name: Display Keycloak configuration summary
|
||||
ansible.builtin.debug:
|
||||
msg:
|
||||
- "Keycloak OIDC client configured:"
|
||||
- " Realm : {{ keycloak_realm }}"
|
||||
- " Client : {{ oidc_client_id }}"
|
||||
- " Issuer : {{ keycloak_url }}{{ keycloak_context }}/realms/{{ keycloak_realm }}"
|
||||
- " Redirect: {{ oidc_redirect_uri }}"
|
||||
verbosity: 1
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Play 2: Configure OpenShift OAuth with Keycloak OIDC
|
||||
# ---------------------------------------------------------------------------
|
||||
- name: Configure OpenShift OAuth with Keycloak OIDC
|
||||
hosts: sno.openshift.toal.ca
|
||||
gather_facts: false
|
||||
connection: local
|
||||
|
||||
tags: openshift
|
||||
|
||||
vars:
|
||||
# Set to "/auth" for legacy JBoss/WildFly-based Keycloak; leave empty for Quarkus (v17+)
|
||||
keycloak_context: ""
|
||||
oidc_provider_name: keycloak
|
||||
oidc_client_id: openshift
|
||||
oidc_admin_groups: []
|
||||
__oidc_secret_name: keycloak-oidc-client-secret
|
||||
__oidc_ca_configmap_name: keycloak-oidc-ca-bundle
|
||||
__oidc_oc: "{{ oc_binary | default('oc') }}"
|
||||
# Prefer the fact set by Play 1; fall back to vault var when running --tags openshift alone
|
||||
__oidc_client_secret_value: "{{ hostvars[inventory_hostname]['__oidc_client_secret'] | default(vault_oidc_client_secret) }}"
|
||||
|
||||
tasks:
|
||||
|
||||
- name: Create temp directory for manifests
|
||||
ansible.builtin.tempfile:
|
||||
state: directory
|
||||
suffix: .oidc
|
||||
register: __oidc_tmpdir
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Secret: Keycloak client secret in openshift-config namespace
|
||||
# ------------------------------------------------------------------
|
||||
- name: Write Keycloak client secret manifest
|
||||
ansible.builtin.copy:
|
||||
dest: "{{ __oidc_tmpdir.path }}/keycloak-secret.yaml"
|
||||
mode: "0600"
|
||||
content: |
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: {{ __oidc_secret_name }}
|
||||
namespace: openshift-config
|
||||
type: Opaque
|
||||
stringData:
|
||||
clientSecret: {{ __oidc_client_secret_value }}
|
||||
no_log: true
|
||||
|
||||
- name: Apply Keycloak client secret
|
||||
ansible.builtin.command:
|
||||
cmd: "{{ __oidc_oc }} apply -f {{ __oidc_tmpdir.path }}/keycloak-secret.yaml"
|
||||
register: __oidc_secret_apply
|
||||
changed_when: "'configured' in __oidc_secret_apply.stdout or 'created' in __oidc_secret_apply.stdout"
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# CA bundle: only needed when Keycloak uses a private/internal CA
|
||||
# ------------------------------------------------------------------
|
||||
- name: Configure CA bundle ConfigMap for Keycloak TLS
|
||||
when: oidc_ca_cert_file is defined
|
||||
block:
|
||||
|
||||
- name: Write CA bundle ConfigMap manifest
|
||||
ansible.builtin.copy:
|
||||
dest: "{{ __oidc_tmpdir.path }}/keycloak-ca.yaml"
|
||||
mode: "0644"
|
||||
content: |
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: {{ __oidc_ca_configmap_name }}
|
||||
namespace: openshift-config
|
||||
data:
|
||||
ca.crt: |
|
||||
{{ lookup('ansible.builtin.file', oidc_ca_cert_file) | indent(4) }}
|
||||
|
||||
- name: Apply CA bundle ConfigMap
|
||||
ansible.builtin.command:
|
||||
cmd: "{{ __oidc_oc }} apply -f {{ __oidc_tmpdir.path }}/keycloak-ca.yaml"
|
||||
register: __oidc_ca_apply
|
||||
changed_when: "'configured' in __oidc_ca_apply.stdout or 'created' in __oidc_ca_apply.stdout"
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# OAuth cluster resource: add/replace Keycloak IdP entry
|
||||
# Reads the current config, replaces any existing entry with the same
|
||||
# name, and applies the merged result — preserving other IdPs.
|
||||
# ------------------------------------------------------------------
|
||||
- name: Get current OAuth cluster configuration
|
||||
ansible.builtin.command:
|
||||
cmd: "{{ __oidc_oc }} get oauth cluster -o json"
|
||||
register: __oidc_current_oauth
|
||||
changed_when: false
|
||||
|
||||
- name: Parse current OAuth configuration
|
||||
ansible.builtin.set_fact:
|
||||
__oidc__oidc_current_oauth_obj: "{{ __oidc_current_oauth.stdout | from_json }}"
|
||||
|
||||
- name: Build Keycloak OIDC identity provider definition
|
||||
ansible.builtin.set_fact:
|
||||
__oidc_new_idp: >-
|
||||
{{
|
||||
{
|
||||
'name': oidc_provider_name,
|
||||
'mappingMethod': 'claim',
|
||||
'type': 'OpenID',
|
||||
'openID': (
|
||||
{
|
||||
'clientID': oidc_client_id,
|
||||
'clientSecret': {'name': __oidc_secret_name},
|
||||
'issuer': keycloak_url ~ keycloak_context ~ '/realms/' ~ keycloak_realm,
|
||||
'claims': {
|
||||
'preferredUsername': ['preferred_username'],
|
||||
'name': ['name'],
|
||||
'email': ['email'],
|
||||
'groups': ['groups']
|
||||
}
|
||||
} | combine(
|
||||
oidc_ca_cert_file is defined | ternary(
|
||||
{'ca': {'name': __oidc_ca_configmap_name}}, {}
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}}
|
||||
|
||||
- name: Build updated identity providers list
|
||||
ansible.builtin.set_fact:
|
||||
__oidc_updated_idps: >-
|
||||
{{
|
||||
(__oidc__oidc_current_oauth_obj.spec.identityProviders | default([])
|
||||
| selectattr('name', '!=', oidc_provider_name) | list)
|
||||
+ [__oidc_new_idp]
|
||||
}}
|
||||
|
||||
- name: Write updated OAuth cluster manifest
|
||||
ansible.builtin.copy:
|
||||
dest: "{{ __oidc_tmpdir.path }}/oauth-cluster.yaml"
|
||||
mode: "0644"
|
||||
content: "{{ __oidc__oidc_current_oauth_obj | combine({'spec': {'identityProviders': __oidc_updated_idps}}, recursive=true) | to_nice_yaml }}"
|
||||
|
||||
- name: Apply updated OAuth cluster configuration
|
||||
ansible.builtin.command:
|
||||
cmd: "{{ __oidc_oc }} apply -f {{ __oidc_tmpdir.path }}/oauth-cluster.yaml"
|
||||
register: __oidc_oauth_apply
|
||||
changed_when: "'configured' in __oidc_oauth_apply.stdout or 'created' in __oidc_oauth_apply.stdout"
|
||||
|
||||
- name: Wait for OAuth deployment to roll out
|
||||
ansible.builtin.command:
|
||||
cmd: "{{ __oidc_oc }} rollout status deployment/oauth-openshift -n openshift-authentication --timeout=300s"
|
||||
changed_when: false
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Optional: grant cluster-admin to specified Keycloak groups
|
||||
# ------------------------------------------------------------------
|
||||
- name: Grant cluster-admin to OIDC admin groups
|
||||
ansible.builtin.command:
|
||||
cmd: "{{ __oidc_oc }} adm policy add-cluster-role-to-group cluster-admin {{ item }}"
|
||||
loop: "{{ oidc_admin_groups }}"
|
||||
changed_when: true
|
||||
when: oidc_admin_groups | length > 0
|
||||
|
||||
- name: Remove temp directory
|
||||
ansible.builtin.file:
|
||||
path: "{{ __oidc_tmpdir.path }}"
|
||||
state: absent
|
||||
|
||||
- name: Display post-configuration summary
|
||||
ansible.builtin.debug:
|
||||
msg:
|
||||
- "OpenShift OIDC configuration complete!"
|
||||
- " Provider : {{ oidc_provider_name }}"
|
||||
- " Issuer : {{ keycloak_url }}{{ keycloak_context }}/realms/{{ keycloak_realm }}"
|
||||
- " Console : https://console-openshift-console.apps.{{ ocp_cluster_name }}.{{ ocp_base_domain }}"
|
||||
- " Login : https://oauth-openshift.apps.{{ ocp_cluster_name }}.{{ ocp_base_domain }}"
|
||||
- ""
|
||||
- "Note: OAuth pods are restarting — login may be unavailable for ~2 minutes."
|
||||
verbosity: 1
|
||||
Reference in New Issue
Block a user