Initial commit
This commit is contained in:
Executable
+10
@@ -0,0 +1,10 @@
|
||||
FROM python:3.13-slim-trixie
|
||||
RUN pip install --no-cache-dir 'ansible-core>=2.18,<2.19' 'kubernetes>=28,<34' PyYAML 'kubernetes-validate>=1.31,<1.35'
|
||||
ADD https://get.helm.sh/helm-v3.17.3-linux-amd64.tar.gz /tmp/helm.tar.gz
|
||||
RUN echo 'ee88b3c851ae6466a3de507f7be73fe94d54cbf2987cbaa3d1a3832ea331f2cd /tmp/helm.tar.gz' | sha256sum -c - && tar -xzf /tmp/helm.tar.gz -C /tmp && mv /tmp/linux-amd64/helm /usr/local/bin/helm && rm -rf /tmp/linux-amd64 /tmp/helm.tar.gz
|
||||
COPY requirements.yml /tmp/requirements.yml
|
||||
RUN ansible-galaxy collection install -r /tmp/requirements.yml
|
||||
WORKDIR /roles/ansible-role-dex
|
||||
COPY . ./
|
||||
ENV ANSIBLE_ROLES_PATH=/roles
|
||||
CMD ["sh", "-ec", "ansible-playbook -i localhost, tests/test.yml --syntax-check; ansible-playbook -i localhost, playbooks/deploy-eom.yml --syntax-check; ansible-playbook -i localhost, tests/render.yml; helm repo add dex https://charts.dexidp.io; helm template dex dex/dex --version 0.26.0 --namespace dex -f /tmp/dex-values.yml > /tmp/dex-manifests.yml; python tests/validate.py"]
|
||||
Executable
+48
@@ -0,0 +1,48 @@
|
||||
---
|
||||
# Passwords supplied in a Vault-encrypted inventory, never in this example.
|
||||
dex_issuer: https://dex.eom.dev/
|
||||
dex_hostname: dex.eom.dev
|
||||
dex_postgres_manage: false
|
||||
dex_postgres_host: postgresql
|
||||
dex_postgres_database: dex
|
||||
dex_postgres_user: dex
|
||||
# Legacy credential names are still supported by defaults:
|
||||
# dex_admin_password, openldap_readonly_password, matrix_dex_oidc_client_secret.
|
||||
|
||||
# Existing Secret in the Dex namespace. WWW_DEX_CLIENT_SECRET must match the
|
||||
# www membership app's DEX_CLIENT_SECRET. DEX_LDAP_BIND_PASSWORD is the search
|
||||
# account password; no real values go in this public file.
|
||||
dex_env_secret_name: dex-client-secrets
|
||||
dex_static_clients:
|
||||
- id: www-membership
|
||||
name: eom.dev Membership
|
||||
secretEnv: WWW_DEX_CLIENT_SECRET
|
||||
redirectURIs:
|
||||
- https://www.eom.dev/members/oidc/callback/
|
||||
|
||||
# Additional connector: don't alter legacy ldap/idAttr uid, which changes sub
|
||||
# identifiers for existing applications. This connector admits free web accounts.
|
||||
dex_additional_connectors:
|
||||
- type: ldap
|
||||
id: www-ldap
|
||||
name: eom.dev Membership
|
||||
config:
|
||||
host: openldap.openldap.svc.cluster.local
|
||||
insecureNoSSL: true
|
||||
bindDN: cn=readonly,dc=eom,dc=dev
|
||||
bindPW: $DEX_LDAP_BIND_PASSWORD
|
||||
userSearch:
|
||||
baseDN: ou=people,dc=eom,dc=dev
|
||||
filter: "(objectClass=inetOrgPerson)"
|
||||
username: uid
|
||||
idAttr: entryUUID
|
||||
emailAttr: mail
|
||||
nameAttr: cn
|
||||
preferredUsernameAttr: uid
|
||||
groupSearch:
|
||||
baseDN: ou=groups,dc=eom,dc=dev
|
||||
filter: "(objectClass=groupOfUniqueNames)"
|
||||
userMatchers:
|
||||
- userAttr: DN
|
||||
groupAttr: uniqueMember
|
||||
nameAttr: cn
|
||||
Executable
+52
@@ -0,0 +1,52 @@
|
||||
---
|
||||
- name: Render Dex values without a cluster or real secrets
|
||||
hosts: localhost
|
||||
connection: local
|
||||
gather_facts: false
|
||||
vars:
|
||||
role_path: /roles/ansible-role-dex
|
||||
tasks:
|
||||
- name: Load defaults
|
||||
ansible.builtin.include_vars: ../defaults/main.yml
|
||||
- name: Set fake isolated credentials
|
||||
ansible.builtin.set_fact:
|
||||
dex_postgres_password: fixture-not-a-real-password
|
||||
dex_ldap_bind_password: fixture-not-a-real-password
|
||||
dex_matrix_client_enabled: true
|
||||
matrix_dex_oidc_client_id: matrix-fixture
|
||||
matrix_dex_oidc_client_secret: fixture-not-a-real-client-secret
|
||||
dex_theme_enabled: true
|
||||
- name: Render default values
|
||||
ansible.builtin.template:
|
||||
src: ../templates/values.yml.j2
|
||||
dest: /tmp/dex-values.yml
|
||||
mode: "0600"
|
||||
- name: Set optional settings
|
||||
ansible.builtin.set_fact:
|
||||
dex_theme_enabled: false
|
||||
dex_matrix_client_enabled: false
|
||||
dex_ldap_insecure_no_ssl: false
|
||||
dex_ldap_start_tls: true
|
||||
dex_ldap_root_ca: /etc/dex-ca/ldap.pem
|
||||
dex_ca_secret_name: ldap-ca
|
||||
dex_env_secret_name: dex-client-secrets
|
||||
dex_static_clients:
|
||||
- id: www-membership
|
||||
name: Example Membership
|
||||
secretEnv: WWW_DEX_CLIENT_SECRET
|
||||
redirectURIs: [https://www.example.com/members/oidc/callback/]
|
||||
- name: Render optional values
|
||||
ansible.builtin.template:
|
||||
src: ../templates/values.yml.j2
|
||||
dest: /tmp/dex-values-optional.yml
|
||||
mode: "0600"
|
||||
- name: Set empty optional lists
|
||||
ansible.builtin.set_fact:
|
||||
dex_ca_secret_name: ""
|
||||
dex_env_secret_name: ""
|
||||
dex_static_clients: []
|
||||
- name: Render minimal values
|
||||
ansible.builtin.template:
|
||||
src: ../templates/values.yml.j2
|
||||
dest: /tmp/dex-values-minimal.yml
|
||||
mode: "0600"
|
||||
Executable
+6
@@ -0,0 +1,6 @@
|
||||
---
|
||||
- name: Check dedicated Dex role syntax
|
||||
hosts: localhost
|
||||
gather_facts: false
|
||||
roles:
|
||||
- ansible-role-dex
|
||||
Executable
+72
@@ -0,0 +1,72 @@
|
||||
"""Check real Ansible-rendered values and Helm manifests without a cluster."""
|
||||
|
||||
import base64
|
||||
from pathlib import Path
|
||||
|
||||
import yaml
|
||||
from kubernetes_validate import validate
|
||||
from ansible.parsing.dataloader import DataLoader
|
||||
from ansible.template import Templar
|
||||
|
||||
# Resolve the site's actual workflow variables with fixture credentials only;
|
||||
# verify both Matrix-only migration and opt-in website integration.
|
||||
defaults = yaml.safe_load(Path('defaults/main.yml').read_text())
|
||||
site = yaml.safe_load(Path('playbooks/deploy-eom.yml').read_text())[0]['vars']
|
||||
for website_enabled in (False, True):
|
||||
variables = {
|
||||
**defaults, **site,
|
||||
'dex_admin_password': 'fixture-db',
|
||||
'openldap_readonly_password': 'fixture-ldap',
|
||||
'matrix_dex_oidc_client_id': 'matrix-fixture',
|
||||
'matrix_dex_oidc_client_secret': 'fixture-matrix',
|
||||
}
|
||||
if website_enabled:
|
||||
variables['www_dex_client_secret'] = 'fixture-www'
|
||||
resolved = Templar(loader=DataLoader(), variables=variables).template(site)
|
||||
assert resolved['dex_issuer'] == 'https://dex.eom.dev/'
|
||||
assert resolved['dex_postgres_password'] == 'fixture-db'
|
||||
assert resolved['dex_ldap_group_user_attribute'] == 'cn'
|
||||
assert resolved['dex_theme_enabled'] is True
|
||||
assert resolved['dex_postgres_manage'] is False
|
||||
assert bool(resolved['dex_static_clients']) == website_enabled
|
||||
assert bool(resolved['dex_additional_connectors']) == website_enabled
|
||||
if website_enabled:
|
||||
assert resolved['dex_static_clients'][0]['secret'] == 'fixture-www'
|
||||
assert resolved['dex_additional_connectors'][0]['config']['userSearch']['idAttr'] == 'entryUUID'
|
||||
|
||||
values = yaml.safe_load(Path('/tmp/dex-values.yml').read_text())
|
||||
config = values['config']
|
||||
assert config['issuer'] == 'https://dex.example.com/'
|
||||
assert config['storage']['config']['database'] == 'dex'
|
||||
assert 'databbase' not in config['storage']['config']
|
||||
ldap = config['connectors'][0]
|
||||
assert ldap['id'] == 'ldap'
|
||||
assert ldap['config']['insecureNoSSL'] is True
|
||||
assert ldap['config']['startTLS'] is False
|
||||
assert ldap['config']['userSearch']['idAttr'] == 'uid'
|
||||
assert ldap['config']['groupSearch']['userMatchers'][0]['userAttr'] == 'DN'
|
||||
assert config['staticClients'][0]['id'] == 'matrix-fixture'
|
||||
assert config['frontend']['logoURL'] == 'theme/logo.svg'
|
||||
assert config['frontend']['dir'] == '/srv/dex/web'
|
||||
assert len(values['volumeMounts']) == 2
|
||||
assert all(mount['readOnly'] for mount in values['volumeMounts'])
|
||||
optional = yaml.safe_load(Path('/tmp/dex-values-optional.yml').read_text())
|
||||
assert 'frontend' not in optional['config']
|
||||
assert optional['config']['connectors'][0]['config']['startTLS'] is True
|
||||
assert optional['config']['staticClients'][0]['secretEnv'] == 'WWW_DEX_CLIENT_SECRET'
|
||||
assert optional['envFrom'][0]['secretRef']['name'] == 'dex-client-secrets'
|
||||
minimal = yaml.safe_load(Path('/tmp/dex-values-minimal.yml').read_text())
|
||||
assert minimal['config']['staticClients'] == []
|
||||
assert minimal['volumes'] == [] and minimal['volumeMounts'] == []
|
||||
objects = [obj for obj in yaml.safe_load_all(Path('/tmp/dex-manifests.yml').read_text()) if obj]
|
||||
for obj in objects:
|
||||
validate(obj, desired_version='1.31.0', strict=True)
|
||||
deployment = next(obj for obj in objects if obj['kind'] == 'Deployment')
|
||||
assert deployment['metadata']['name'] == 'dex'
|
||||
mounts = deployment['spec']['template']['spec']['containers'][0]['volumeMounts']
|
||||
assert any(mount['mountPath'].endswith('/light/styles.css') for mount in mounts)
|
||||
secret = next(obj for obj in objects if obj['kind'] == 'Secret')
|
||||
stored = yaml.safe_load(base64.b64decode(secret['data']['config.yaml']))
|
||||
assert stored['storage']['config']['password'] == 'fixture-not-a-real-password'
|
||||
assert not any(obj['kind'] in ['PersistentVolumeClaim', 'StatefulSet'] for obj in objects)
|
||||
print(f'PASS: {len(objects)} real Helm resources; legacy identity/plain LDAP preserved, optional TLS, clients, theme mounts and Secret config validated')
|
||||
Reference in New Issue
Block a user