ericandClaude Opus 5.5 fa17989fed Dex: send LDAP groups, accept membership accounts, gate Mastodon
- Match groups by DN so the groups claim is populated
- Use cn for names; membership-created accounts have no givenName
- Add a mastodon connector that only admits Mastodon Users, and pin
  every client to its connector via allowedConnectors
- Register Nextcloud's user_oidc callback without index.php

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 17:54:59 -05:00
2026-10-27 16:04:33 -04:00
2026-10-23 18:15:49 -04:00
2026-10-23 18:15:49 -04:00
2026-10-23 20:28:35 -04:00
2026-10-27 16:42:21 -04:00
2026-10-27 16:04:33 -04:00
2026-10-23 18:15:49 -04:00
2026-10-23 18:15:49 -04:00
2026-10-23 18:15:49 -04:00
2026-10-27 16:42:21 -04:00
2026-10-23 18:15:49 -04:00

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 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 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 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/<secret-key>. 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 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 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 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 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.

S
Description
Ansible role to deploy Dex to Kubernetes
Readme
96 KiB
Languages
Python 38.2%
Jinja 30.5%
CSS 24.2%
Dockerfile 7.1%