This commit is contained in:
2026-10-27 16:04:33 -04:00
parent 6de8385829
commit 9a31efc38e
6 changed files with 33 additions and 80 deletions
+16 -15
View File
@@ -59,12 +59,13 @@ 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.
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`
@@ -145,8 +146,9 @@ 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,
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.
@@ -173,13 +175,12 @@ 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.
[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
+1 -1
View File
@@ -52,7 +52,7 @@ dex_matrix_client_enabled: false
dex_matrix_redirect_uris: []
# Full objects (id, name, secret or secretEnv, redirectURIs). Use Vault for secrets.
dex_static_clients: []
# Additional connectors let www use entryUUID without changing legacy subjects.
# Optional extra connectors; the eom.dev site uses its existing LDAP connector.
dex_additional_connectors: []
# Optional environment Secret for secretEnv and $ENV connector credentials.
dex_env_secret_name: ""
+3 -28
View File
@@ -31,7 +31,8 @@
dex_postgres_password: "{{ dex_admin_password }}"
dex_postgres_admin_password: "{{ postgresql_admin_password | default('') }}"
# Explicitly preserve internal plaintext LDAP and legacy OIDC subjects.
# Keep the existing connector ID and UID subject mapping. The filter also
# admits website inetOrgPerson accounts so there is one Dex login option.
dex_ldap_host: openldap.openldap.svc.cluster.local
dex_ldap_insecure_no_ssl: true
dex_ldap_start_tls: false
@@ -40,7 +41,7 @@
dex_ldap_connector_id: ldap
dex_ldap_connector_name: eom.dev account
dex_ldap_user_base_dn: dc=eom,dc=dev
dex_ldap_user_filter: "(&(objectClass=posixAccount)(memberOf=cn=Dex Users,ou=Dex,ou=Services,dc=eom,dc=dev))"
dex_ldap_user_filter: "(|(&(objectClass=posixAccount)(memberOf=cn=Dex Users,ou=Dex,ou=Services,dc=eom,dc=dev))(objectClass=inetOrgPerson))"
dex_ldap_id_attribute: uid
dex_ldap_name_attribute: givenName
dex_ldap_group_base_dn: dc=eom,dc=dev
@@ -93,31 +94,5 @@
# Override from its admin screen if your installation rewrites index.php.
redirectURIs:
- https://nextcloud.eom.dev/index.php/apps/user_oidc/code
dex_additional_connectors:
- "{{ _dex_eom_www_connector }}"
_dex_eom_www_connector:
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: "{{ openldap_readonly_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
roles:
- ansible-role-dex
+4 -9
View File
@@ -83,12 +83,8 @@ config:
{% for connector in dex_additional_connectors %}
- {{ connector | to_json }}
{% endfor %}
volumes:
# The official entrypoint renders its config into /tmp before starting Dex.
- name: dex-tmp
emptyDir:
medium: Memory
sizeLimit: 16Mi
volumes:{% if not dex_theme_enabled and not dex_ca_secret_name and not dex_extra_volumes %} []{% endif %}
{% if dex_theme_enabled %}
- name: theme
configMap:
@@ -102,9 +98,8 @@ volumes:
{% for volume in dex_extra_volumes %}
- {{ volume | to_json }}
{% endfor %}
volumeMounts:
- name: dex-tmp
mountPath: /tmp
volumeMounts:{% if not dex_theme_enabled and not dex_ca_secret_name and not dex_extra_volume_mounts %} []{% endif %}
{% if dex_theme_enabled %}
- name: theme
mountPath: /srv/dex/web/themes/light/styles.css
+6 -26
View File
@@ -20,29 +20,9 @@ dex_static_clients:
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
# One LDAP connector admits both legacy POSIX accounts and website accounts.
# Keep its ID and idAttr unchanged so existing OIDC subjects remain stable.
dex_ldap_connector_id: ldap
dex_ldap_id_attribute: uid
dex_ldap_user_base_dn: dc=eom,dc=dev
dex_ldap_user_filter: "(|(&(objectClass=posixAccount)(memberOf=cn=Dex Users,ou=Dex,ou=Services,dc=eom,dc=dev))(objectClass=inetOrgPerson))"
+3 -1
View File
@@ -31,6 +31,8 @@ for fixture_run in (1,):
assert resolved['dex_ldap_group_user_attribute'] == 'cn'
assert resolved['dex_theme_enabled'] is True
assert resolved['dex_postgres_manage'] is False
assert resolved['dex_additional_connectors'] == []
assert resolved['dex_ldap_user_filter'] == '(|(&(objectClass=posixAccount)(memberOf=cn=Dex Users,ou=Dex,ou=Services,dc=eom,dc=dev))(objectClass=inetOrgPerson))'
clients = {client['id']: client for client in resolved['dex_static_clients']}
assert len(clients) == len(resolved['dex_static_clients']) == 7
assert resolved['dex_matrix_client_enabled'] is False
@@ -42,7 +44,6 @@ for fixture_run in (1,):
assert clients['mastodon']['redirectURIs'] == ['https://mastodon.eom.dev/auth/auth/openid_connect/callback']
assert clients['nextcloud']['redirectURIs'] == ['https://nextcloud.eom.dev/index.php/apps/user_oidc/code']
assert all(client['secret'] and len(client['redirectURIs']) == 1 for client in clients.values())
assert resolved['dex_additional_connectors'][0]['config']['userSearch']['idAttr'] == 'entryUUID'
values = yaml.safe_load(Path('/tmp/dex-values.yml').read_text())
config = values['config']
@@ -53,6 +54,7 @@ ldap = config['connectors'][0]
assert ldap['id'] == 'ldap'
assert ldap['config']['insecureNoSSL'] is True
assert ldap['config']['startTLS'] is False
assert len(config['connectors']) == 1
assert ldap['config']['userSearch']['idAttr'] == 'uid'
assert ldap['config']['groupSearch']['userMatchers'][0]['userAttr'] == 'DN'
assert config['staticClients'][0]['id'] == 'matrix-fixture'