# ansible-role-dex Dedicated Kubernetes/Helm role extracted from ansible-role-eom's Dex task. Deploys Dex with local **Catppuccin Mocha / Latte** styles and an **eom.dev** wordmark, retaining Dex's shipped authentication and consent templates. ## Generic defaults and eom.dev workflow Role defaults now use example domains/DNs, blank credentials, a 10Gi optional database request, no implicit Matrix client and disabled eom.dev branding. Plaintext LDAP remains the default as requested; TLS remains opt-in. Use [playbooks/deploy-eom.yml](playbooks/deploy-eom.yml) for the actual eom.dev deployment. It calls this role and explicitly preserves the existing issuer, namespace/releases, database settings/256Gi sizing, plaintext LDAP host, legacy POSIX/group filter, `uid` subject mapping, Matrix callback, and Catppuccin logo. Provide these existing variables through controller inventory/group_vars or an Ansible Vault-encrypted vars file: - `dex_admin_password`: existing Dex PostgreSQL password. - `openldap_readonly_password`: existing LDAP search-account password. - `matrix_dex_oidc_client_id` and `matrix_dex_oidc_client_secret`: existing MAS client. - `postgresql_admin_password`: needed only if deliberately enabling DB management. - `www_dex_client_secret`: matches the website's `DEX_CLIENT_SECRET`. - `gitea_dex_client_secret`, `discourse_dex_client_secret`, `peertube_dex_client_secret`, `mastodon_dex_client_secret`, and `nextcloud_dex_client_secret`: one distinct confidential-client secret per app. The site playbook now registers all seven clients explicitly in `dex_static_clients` and disables the Matrix convenience client to avoid a duplicate. None of these secrets is optional unless you deliberately remove its client from your inventory/playbook. Existing secrets must be preserved, not regenerated on each deploy. No real secrets are included in source control. ### Application configuration required after client registration All applications use issuer **https://dex.eom.dev/** and discovery document **https://dex.eom.dev/.well-known/openid-configuration**, with `openid profile email` scopes (and `groups` only when used). Configure the matching ID/secret in each service. Client creation in Dex **does not enable SSO in the application**. | Application | Client ID | Exact callback / prerequisite | | --- | --- | --- | | Matrix Auth Service | Existing `matrix_dex_oidc_client_id` | Original `https://mas.eom.dev/upstream/callback/01K96AQEZKKABW34PY3R6BVNJ4` retained | | www | `www-membership` | `https://www.eom.dev/members/oidc/callback/` | | Gitea | `gitea` | `https://gitea.eom.dev/user/oauth2/dex/callback`; add OpenID Connect authentication source named **dex** | | Discourse | `discourse` | `https://discourse.eom.dev/auth/oidc/callback`; enable discourse-openid-connect and confirm callback for your installed plugin version | | PeerTube | `peertube` | `https://peertube.eom.dev/plugins/auth-openid-connect/router/code-cb`; install/configure official auth-openid-connect plugin | | Mastodon | `mastodon` | `https://mastodon.eom.dev/auth/auth/openid_connect/callback`; retained from its configured OIDC_REDIRECT_URI; verify installed OIDC implementation supports that route | | Nextcloud | `nextcloud` | `https://nextcloud.eom.dev/index.php/apps/user_oidc/code`; install **user_oidc**, not the OIDC provider app or oidc_login | Callback paths are not interchangeable. Gitea's source name affects its URL; Nextcloud's admin screen supplies the exact callback, which can omit index.php when URL rewriting is enabled. If using **oidc_login** instead, explicitly replace the callback with its `/apps/oidc_login/oidc` route rather than enabling both by guesswork. The existing Mastodon deployment points to Google; changing it to Dex is a separate app-side migration, not another set of automatically active OIDC environment variables. Keep a tested administrator fallback and avoid automatic email-only account linking when migrating existing users. The eom.dev deployment uses one `ldap` connector for legacy POSIX accounts and website `inetOrgPerson` accounts. It retains `idAttr: uid`, so existing OIDC subjects remain stable. The website resolves the signed UID to LDAP and anchors its account link to immutable `entryUUID`. Its combined user filter admits `inetOrgPerson` entries throughout the configured search base; review that authentication scope before deployment. Each relying application must still implement its own authorization policy. Run the playbook on the controller with its kubeconfig and this role available in `ANSIBLE_ROLES_PATH`. Load your encrypted variables with Ansible's `--extra-vars` file option and `--ask-vault-pass` or your existing Vault identity configuration; do not put plaintext credentials in this playbook or pass them on command lines. This is an Ansible playbook, not a CI provider workflow, and is not automatically triggered. The old EOM compatibility entry now uses generic defaults unless you explicitly pass these site settings; prefer the site playbook for migration. ## Migration from ansible-role-eom 1. Back up the Dex PostgreSQL database and record the installed Dex/PostgreSQL Helm release versions and current values in a secure location. Values contain credentials; do not paste them into tickets, chat or unencrypted git files. 2. Install this role under `ansible-role-dex` in your controller's role search path. Install [requirements.yml](requirements.yml) and the Kubernetes Python client; Helm 3 and an authorized kubeconfig are required. Tested with Ansible Core 2.18; role metadata minimum is 2.15. 3. Use the site workflow above or invoke `ansible-role-dex` with explicit site variables in the existing playbook. The old `ansible-role-eom/tasks/dex.yaml` is now a thin `include_role` compatibility entry point. Use one entry point, not both. No global eom dependency was added: its current main task only deploys Monero and remains unchanged. 4. Keep namespace `dex`, release `dex`, issuer **https://dex.eom.dev/** (including trailing slash), database host `postgresql`, user/database `dex` and existing credentials. A role extraction must not change OIDC issuer or existing client secrets/redirect URIs, connector IDs or user-ID mappings. 5. Review the pinned Dex chart `0.26.0` (app `v2.46.0`) against your installed release **before applying**. Override `dex_chart_version` and optionally `dex_image_tag` if staging a different supported release. This role does not discover/guess the installed version or perform a live migration by itself. 6. PostgreSQL management defaults to **false**: the existing release and PVC are left untouched. Do not uninstall the old release or delete its volumes. The extracted config corrects the old `databbase` typo to `database: dex`. The site playbook maps legacy inventory variables explicitly: | Old inventory variable | New role variable | | --- | --- | | `dex_admin_password` | `dex_postgres_password` (DB password, not a Dex UI admin) | | `postgresql_admin_password` | `dex_postgres_admin_password` | | `openldap_readonly_password` | `dex_ldap_bind_password` | | `matrix_dex_oidc_client_id` / `matrix_dex_oidc_client_secret` | Existing Matrix Auth Service client | The site playbook explicitly includes the existing Matrix client. Its original redirect URI is explicit in `dex_static_clients`; override to match your actual MAS configuration. Other clients previously added directly to Helm must be declared in `dex_static_clients` before running this role. Helm values are reset to this role's complete values: undeclared manual Helm customizations are not implicitly preserved. The role's authoritative values must be reviewed. ## Main variables See [defaults/main.yml](defaults/main.yml) for all options. | Variable | Default / behavior | | --- | --- | | `dex_namespace`, `dex_release_name` | `dex`, `dex` | | `dex_chart_version` | `0.26.0` (explicit, reviewed release) | | `dex_issuer`, `dex_hostname` | `https://{{ dex_hostname }}/`, `dex.example.com` | | `dex_ingress_class`, `dex_cluster_issuer` | `nginx`, `letsencrypt-prod` | | `dex_theme_enabled`, `dex_brand_name` | false, `Dex` (site playbook enables eom.dev theme) | | `dex_static_clients` | Additional Dex static client objects; default empty | | `dex_additional_connectors` | Additional complete connector objects; default empty | | `dex_env_secret_name` | Optional existing environment Secret in Dex namespace | | `dex_ca_secret_name` | Optional existing CA Secret mounted at `/etc/dex-ca` | | `dex_postgres_manage` | false; reuse existing DB without changing its release | | `dex_postgres_ssl_mode` | `disable`, preserving internal DB behavior | | `dex_image_pull_secrets` | Kubernetes imagePullSecrets list | | `dex_extra_volumes`, `dex_extra_volume_mounts` | Optional additional mounts | ### LDAP: plaintext is the default, TLS is opt-in As requested, default LDAP behavior remains `insecureNoSSL: true` against `ldap.example.com:389` (the site playbook uses `openldap.openldap.svc.cluster.local`). This exposes both service-bind and user passwords to network observers. Restrict it to the trusted internal network with your cluster's policies; no public LDAP exposure is created by this role. The generic connector uses `inetOrgPerson` and DN-based group matching. The site playbook retains ID `ldap`, `idAttr: uid`, its POSIX/Dex Users filter branch plus website `inetOrgPerson` accounts, and `userAttr: cn` group matching. For `uniqueMember` values containing full DNs, set `dex_ldap_group_user_attribute: DN` deliberately after testing. Changing connector ID or idAttr changes OIDC subjects and can detach existing accounts. For **StartTLS**, explicitly set `dex_ldap_insecure_no_ssl: false`, `dex_ldap_start_tls: true`, and a host/port supporting StartTLS. For **LDAPS**, set both flags false and use the server's TLS hostname with port 636. Certificate verification remains enabled; no `insecureSkipVerify` bypass is offered. For a private CA set `dex_ca_secret_name` and `dex_ldap_root_ca` to `/etc/dex-ca/`. Verify certificate hostname, validity, SHA-2 signature, strong keys and CA trust independently. Additional connectors have their own transport settings and must be configured separately. ### Credentials and clients Supply database and LDAP passwords from Vault/external lookups or the supported legacy secret variables. Helm deployment and validation tasks are `no_log` and diff-suppressed. Dex configuration is stored by the official chart in a Kubernetes **Secret**, never in the theme ConfigMap. Helm stores release values, including inline credentials, in its release Secrets: protect these with RBAC, encryption at rest and encrypted backups. This role does not generate or rotate passwords. The root filesystem stays read-only; a 16 MiB memory-backed `emptyDir` mounted at `/tmp` provides Dex's required temporary config-file writes without making the container filesystem writable. Client `secretEnv` can refer to a key in `dex_env_secret_name`. LDAP connectors support `$ENV_NAME` references, such as `bindPW: $DEX_LDAP_BIND_PASSWORD`. The database password remains a protected Helm/Dex config value; it is not assumed to support the same connector substitution. Never commit actual secrets. [tests/example-vars.yml](tests/example-vars.yml) shows a www client using the single existing `ldap` connector. The Secret's `WWW_DEX_CLIENT_SECRET` must match the website's `DEX_CLIENT_SECRET`; set `DEX_CONNECTOR_ID=ldap` and the exact matching issuer. No client or additional connector is silently enabled by default. Verify the combined search filter and scope before enabling public registration. ### Theme [files/styles.css](files/styles.css) implements responsive Mocha dark mode and Latte light mode using `prefers-color-scheme`, visible focus rings, local fonts, inputs, errors, connector buttons and consent panels. [files/logo.svg](files/logo.svg) extends the website's orbit mark into an eom.dev wordmark. Only `themes/light/styles.css` and `themes/light/logo.svg` are mounted read-only under the official image's `/srv/dex/web`; frontend selects that directory and `theme/logo.svg`. All other version-matched templates, icons and static assets come from the installed Dex image. No forms, OAuth parameters, redirects or approval/security logic are replaced. This deliberately retains the shipped favicon; logo/theme are local, with no third-party fonts or scripts. A checksum of public theme assets triggers a rollout when they change (subPath mounts do not hot-reload). Setting `dex_theme_enabled: false` restores Dex's default frontend; the unused ConfigMap is retained. Existing environment Secret or CA Secret rotations require a Dex rollout; the role does not expose or hash their contents. Inspect log privacy at any upstream proxies too: ingress access logs are disabled to avoid recording authorization codes and related URLs. ### Optional PostgreSQL management For a new install or an explicitly reviewed DB upgrade, set `dex_postgres_manage: true` and **pin `dex_postgres_chart_version`** to the intended Bitnami release. Defaults retain release `postgresql`, 256Gi and `volumeName: data` from the old task. Confirm existing PV/PVC binding, storage size, chart/image availability and schema compatibility first; do not use this to switch major database versions without a backup/restore plan. Changing Helm password values does not automatically rotate an existing database password. Managed DB transport is unchanged; configure external DB TLS/mesh separately if required. ## Validation [tests/Dockerfile](tests/Dockerfile) installs the declared collection and Helm, checks Ansible syntax, renders actual values and runs the pinned chart through Helm and strict Kubernetes schemas. Fixtures verify legacy plaintext/subjects, Matrix client, additional environment client, opt-in TLS, theme mounts, protected Dex config and disabled/minimal variants. No cluster is contacted by these tests. The theme was also exercised with the official Dex v2.46.0 image using isolated SQLite/mock authentication. Verify real LDAP, client redirects and database access in staging before rollout. Chart upgrades should repeat that image/theme compatibility test. No production deployments or database changes are performed as part of this extraction. The editor's scanner flags high-severity findings in the validation runner's upstream Python base image. That image is test-only and is not deployed by this role. Review/update approved CI base-image digests with your image scanner; these findings are not represented as resolved by the functional tests.