Files
ansible-role-dex/README.md
T
2026-10-27 16:42:21 -04:00

238 lines
15 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 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/<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](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.