Files
ansible-role-dex/README.md
T
2026-10-23 20:28:35 -04:00

234 lines
14 KiB
Markdown
Executable File

# 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 website's `www-ldap` connector uses entryUUID while the legacy `ldap`
connector uses uid. Those produce different subjects for the same directory
entry: do not switch existing accounts between connectors without an explicit
linking/migration plan. The legacy connector still filters for Dex Users; merely
registering an application does not bypass this restriction or grant paid room
access. Each relying application must 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/group filter,
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.
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 and separate
`www-ldap` connector using `entryUUID`, `inetOrgPerson` and DN group matching.
This avoids breaking legacy subjects while supporting new free website users.
The Secret's `WWW_DEX_CLIENT_SECRET` must match the website's `DEX_CLIENT_SECRET`;
set the website's `DEX_CONNECTOR_ID=www-ldap` and exact matching issuer. No new
client or connector is silently enabled by default. Verify that this connector's
search base includes the website account OU 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.