Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fa17989fed
|
||
|
|
642c2ed818
|
||
|
|
0e62fb0962
|
||
|
|
9a31efc38e
|
+9
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"python-envs.pythonProjects": [
|
||||||
|
{
|
||||||
|
"path": ".",
|
||||||
|
"envManager": "ms-python.python:system",
|
||||||
|
"packageManager": "ms-python.python:pip"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -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
|
environment variables. Keep a tested administrator fallback and avoid automatic
|
||||||
email-only account linking when migrating existing users.
|
email-only account linking when migrating existing users.
|
||||||
|
|
||||||
The website's `www-ldap` connector uses entryUUID while the legacy `ldap`
|
The eom.dev deployment uses one `ldap` connector for legacy POSIX accounts and
|
||||||
connector uses uid. Those produce different subjects for the same directory
|
website `inetOrgPerson` accounts. It retains `idAttr: uid`, so existing OIDC
|
||||||
entry: do not switch existing accounts between connectors without an explicit
|
subjects remain stable. The website resolves the signed UID to LDAP and anchors
|
||||||
linking/migration plan. The legacy connector still filters for Dex Users; merely
|
its account link to immutable `entryUUID`. Its combined user filter admits
|
||||||
registering an application does not bypass this restriction or grant paid room
|
`inetOrgPerson` entries throughout the configured search base; review that
|
||||||
access. Each relying application must implement its own authorization policy.
|
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
|
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`
|
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.
|
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
|
The generic connector uses `inetOrgPerson` and DN-based group matching. The site
|
||||||
playbook retains ID `ldap`, `idAttr: uid`, its POSIX/group filter,
|
playbook retains ID `ldap`, `idAttr: uid`, its POSIX/Dex Users filter branch plus
|
||||||
and `userAttr: cn` group matching. For `uniqueMember` values containing full DNs,
|
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
|
set `dex_ldap_group_user_attribute: DN` deliberately after testing. Changing
|
||||||
connector ID or idAttr changes OIDC subjects and can detach existing accounts.
|
connector ID or idAttr changes OIDC subjects and can detach existing accounts.
|
||||||
|
|
||||||
@@ -167,19 +169,21 @@ diff-suppressed. Dex configuration is stored by the official chart in a Kubernet
|
|||||||
**Secret**, never in the theme ConfigMap. Helm stores release values, including
|
**Secret**, never in the theme ConfigMap. Helm stores release values, including
|
||||||
inline credentials, in its release Secrets: protect these with RBAC, encryption
|
inline credentials, in its release Secrets: protect these with RBAC, encryption
|
||||||
at rest and encrypted backups. This role does not generate or rotate passwords.
|
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
|
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
|
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
|
database password remains a protected Helm/Dex config value; it is not assumed
|
||||||
to support the same connector substitution. Never commit actual secrets.
|
to support the same connector substitution. Never commit actual secrets.
|
||||||
|
|
||||||
[tests/example-vars.yml](tests/example-vars.yml) shows a www client and separate
|
[tests/example-vars.yml](tests/example-vars.yml) shows a www client using the
|
||||||
`www-ldap` connector using `entryUUID`, `inetOrgPerson` and DN group matching.
|
single existing `ldap` connector. The Secret's `WWW_DEX_CLIENT_SECRET` must match
|
||||||
This avoids breaking legacy subjects while supporting new free website users.
|
the website's `DEX_CLIENT_SECRET`; set `DEX_CONNECTOR_ID=ldap` and the exact
|
||||||
The Secret's `WWW_DEX_CLIENT_SECRET` must match the website's `DEX_CLIENT_SECRET`;
|
matching issuer. No client or additional connector is silently enabled by
|
||||||
set the website's `DEX_CONNECTOR_ID=www-ldap` and exact matching issuer. No new
|
default. Verify the combined search filter and scope before enabling public
|
||||||
client or connector is silently enabled by default. Verify that this connector's
|
registration.
|
||||||
search base includes the website account OU before enabling public registration.
|
|
||||||
|
|
||||||
### Theme
|
### Theme
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -52,7 +52,7 @@ dex_matrix_client_enabled: false
|
|||||||
dex_matrix_redirect_uris: []
|
dex_matrix_redirect_uris: []
|
||||||
# Full objects (id, name, secret or secretEnv, redirectURIs). Use Vault for secrets.
|
# Full objects (id, name, secret or secretEnv, redirectURIs). Use Vault for secrets.
|
||||||
dex_static_clients: []
|
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: []
|
dex_additional_connectors: []
|
||||||
# Optional environment Secret for secretEnv and $ENV connector credentials.
|
# Optional environment Secret for secretEnv and $ENV connector credentials.
|
||||||
dex_env_secret_name: ""
|
dex_env_secret_name: ""
|
||||||
|
|||||||
+27
-24
@@ -4,7 +4,7 @@
|
|||||||
- name: Deploy eom.dev Dex with Catppuccin branding
|
- name: Deploy eom.dev Dex with Catppuccin branding
|
||||||
hosts: alpha-control-plane
|
hosts: alpha-control-plane
|
||||||
vars_files:
|
vars_files:
|
||||||
../secrets.yaml
|
~/Desktop/secrets.yaml
|
||||||
become: true
|
become: true
|
||||||
vars:
|
vars:
|
||||||
dex_namespace: dex
|
dex_namespace: dex
|
||||||
@@ -31,7 +31,8 @@
|
|||||||
dex_postgres_password: "{{ dex_admin_password }}"
|
dex_postgres_password: "{{ dex_admin_password }}"
|
||||||
dex_postgres_admin_password: "{{ postgresql_admin_password | default('') }}"
|
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_host: openldap.openldap.svc.cluster.local
|
||||||
dex_ldap_insecure_no_ssl: true
|
dex_ldap_insecure_no_ssl: true
|
||||||
dex_ldap_start_tls: false
|
dex_ldap_start_tls: false
|
||||||
@@ -40,11 +41,11 @@
|
|||||||
dex_ldap_connector_id: ldap
|
dex_ldap_connector_id: ldap
|
||||||
dex_ldap_connector_name: eom.dev account
|
dex_ldap_connector_name: eom.dev account
|
||||||
dex_ldap_user_base_dn: dc=eom,dc=dev
|
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_id_attribute: uid
|
||||||
dex_ldap_name_attribute: givenName
|
dex_ldap_name_attribute: cn
|
||||||
dex_ldap_group_base_dn: dc=eom,dc=dev
|
dex_ldap_group_base_dn: dc=eom,dc=dev
|
||||||
dex_ldap_group_user_attribute: cn
|
dex_ldap_group_user_attribute: DN
|
||||||
dex_ldap_group_filter: "(objectClass=groupOfUniqueNames)"
|
dex_ldap_group_filter: "(objectClass=groupOfUniqueNames)"
|
||||||
|
|
||||||
# All clients are declared once below; don't also emit the convenience client.
|
# All clients are declared once below; don't also emit the convenience client.
|
||||||
@@ -55,34 +56,41 @@
|
|||||||
- id: "{{ matrix_dex_oidc_client_id }}"
|
- id: "{{ matrix_dex_oidc_client_id }}"
|
||||||
name: Matrix Auth Service
|
name: Matrix Auth Service
|
||||||
secret: "{{ matrix_dex_oidc_client_secret }}"
|
secret: "{{ matrix_dex_oidc_client_secret }}"
|
||||||
|
allowedConnectors: [ldap]
|
||||||
redirectURIs:
|
redirectURIs:
|
||||||
- https://mas.eom.dev/upstream/callback/01K96AQEZKKABW34PY3R6BVNJ4
|
- https://mas.eom.dev/upstream/callback/01K96AQEZKKABW34PY3R6BVNJ4
|
||||||
- id: www-membership
|
- id: www-membership
|
||||||
name: eom.dev Membership
|
name: eom.dev Membership
|
||||||
secret: "{{ www_dex_client_secret }}"
|
secret: "{{ www_dex_client_secret }}"
|
||||||
|
allowedConnectors: [ldap]
|
||||||
redirectURIs:
|
redirectURIs:
|
||||||
- https://www.eom.dev/members/oidc/callback/
|
- https://www.eom.dev/members/oidc/callback/
|
||||||
- id: gitea
|
- id: gitea
|
||||||
name: Gitea
|
name: Gitea
|
||||||
secret: "{{ gitea_dex_client_secret }}"
|
secret: "{{ gitea_dex_client_secret }}"
|
||||||
# Authentication source in Gitea must be named exactly "dex".
|
# Authentication source in Gitea must be named exactly "Dex".
|
||||||
|
allowedConnectors: [ldap]
|
||||||
redirectURIs:
|
redirectURIs:
|
||||||
- https://gitea.eom.dev/user/oauth2/dex/callback
|
- https://gitea.eom.dev/user/oauth2/Dex/callback
|
||||||
- id: discourse
|
- id: discourse
|
||||||
name: Discourse
|
name: Discourse
|
||||||
secret: "{{ discourse_dex_client_secret }}"
|
secret: "{{ discourse_dex_client_secret }}"
|
||||||
# discourse-openid-connect strategy name is oidc.
|
# discourse-openid-connect strategy name is oidc.
|
||||||
|
allowedConnectors: [ldap]
|
||||||
redirectURIs:
|
redirectURIs:
|
||||||
- https://discourse.eom.dev/auth/oidc/callback
|
- https://discourse.eom.dev/auth/oidc/callback
|
||||||
- id: peertube
|
- id: peertube
|
||||||
name: PeerTube
|
name: PeerTube
|
||||||
secret: "{{ peertube_dex_client_secret }}"
|
secret: "{{ peertube_dex_client_secret }}"
|
||||||
# Requires peertube-plugin-auth-openid-connect.
|
# Requires peertube-plugin-auth-openid-connect.
|
||||||
|
allowedConnectors: [ldap]
|
||||||
redirectURIs:
|
redirectURIs:
|
||||||
- https://peertube.eom.dev/plugins/auth-openid-connect/router/code-cb
|
- https://peertube.eom.dev/plugins/oidc-cnr/router/code-cb
|
||||||
- id: mastodon
|
- id: mastodon
|
||||||
name: Mastodon
|
name: Mastodon
|
||||||
secret: "{{ mastodon_dex_client_secret }}"
|
secret: "{{ mastodon_dex_client_secret }}"
|
||||||
|
# Tier gate: only the connector that admits Mastodon Users.
|
||||||
|
allowedConnectors: [mastodon]
|
||||||
# Matches OIDC_REDIRECT_URI in the existing Mastodon deployment.
|
# Matches OIDC_REDIRECT_URI in the existing Mastodon deployment.
|
||||||
redirectURIs:
|
redirectURIs:
|
||||||
- https://mastodon.eom.dev/auth/auth/openid_connect/callback
|
- https://mastodon.eom.dev/auth/auth/openid_connect/callback
|
||||||
@@ -91,33 +99,28 @@
|
|||||||
secret: "{{ nextcloud_dex_client_secret }}"
|
secret: "{{ nextcloud_dex_client_secret }}"
|
||||||
# This chooses the user_oidc app, not oidc_login or the OIDC provider app.
|
# This chooses the user_oidc app, not oidc_login or the OIDC provider app.
|
||||||
# Override from its admin screen if your installation rewrites index.php.
|
# Override from its admin screen if your installation rewrites index.php.
|
||||||
|
allowedConnectors: [ldap]
|
||||||
redirectURIs:
|
redirectURIs:
|
||||||
|
- https://nextcloud.eom.dev/apps/user_oidc/code
|
||||||
- https://nextcloud.eom.dev/index.php/apps/user_oidc/code
|
- https://nextcloud.eom.dev/index.php/apps/user_oidc/code
|
||||||
|
# Per-service gate for clients that cannot check group claims themselves.
|
||||||
|
# Same idAttr as the main connector; subjects differ per connector ID.
|
||||||
dex_additional_connectors:
|
dex_additional_connectors:
|
||||||
- "{{ _dex_eom_www_connector }}"
|
- type: ldap
|
||||||
_dex_eom_www_connector:
|
id: mastodon
|
||||||
type: ldap
|
name: eom.dev account
|
||||||
id: www-ldap
|
|
||||||
name: eom.dev Membership
|
|
||||||
config:
|
config:
|
||||||
host: openldap.openldap.svc.cluster.local
|
host: openldap.openldap.svc.cluster.local
|
||||||
insecureNoSSL: true
|
insecureNoSSL: true
|
||||||
bindDN: cn=readonly,dc=eom,dc=dev
|
bindDN: cn=readonly,dc=eom,dc=dev
|
||||||
bindPW: "{{ openldap_readonly_password }}"
|
bindPW: "{{ openldap_readonly_password }}"
|
||||||
userSearch:
|
userSearch:
|
||||||
baseDN: ou=people,dc=eom,dc=dev
|
baseDN: dc=eom,dc=dev
|
||||||
filter: "(objectClass=inetOrgPerson)"
|
filter: "(&(objectClass=inetOrgPerson)(memberOf=cn=Mastodon Users,ou=Mastodon,ou=Services,dc=eom,dc=dev))"
|
||||||
username: uid
|
username: uid
|
||||||
idAttr: entryUUID
|
idAttr: uid
|
||||||
emailAttr: mail
|
emailAttr: mail
|
||||||
nameAttr: cn
|
nameAttr: cn
|
||||||
preferredUsernameAttr: uid
|
preferredUsernameAttr: uid
|
||||||
groupSearch:
|
|
||||||
baseDN: ou=groups,dc=eom,dc=dev
|
|
||||||
filter: "(objectClass=groupOfUniqueNames)"
|
|
||||||
userMatchers:
|
|
||||||
- userAttr: DN
|
|
||||||
groupAttr: uniqueMember
|
|
||||||
nameAttr: cn
|
|
||||||
roles:
|
roles:
|
||||||
- ansible-role-dex
|
- roles/ericomeehan.dex
|
||||||
|
|||||||
@@ -84,11 +84,11 @@ config:
|
|||||||
- {{ connector | to_json }}
|
- {{ connector | to_json }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
volumes:
|
volumes:
|
||||||
# The official entrypoint renders its config into /tmp before starting Dex.
|
|
||||||
- name: dex-tmp
|
- name: dex-tmp
|
||||||
emptyDir:
|
emptyDir:
|
||||||
medium: Memory
|
medium: Memory
|
||||||
sizeLimit: 16Mi
|
sizeLimit: 16Mi
|
||||||
|
|
||||||
{% if dex_theme_enabled %}
|
{% if dex_theme_enabled %}
|
||||||
- name: theme
|
- name: theme
|
||||||
configMap:
|
configMap:
|
||||||
@@ -105,6 +105,7 @@ volumes:
|
|||||||
volumeMounts:
|
volumeMounts:
|
||||||
- name: dex-tmp
|
- name: dex-tmp
|
||||||
mountPath: /tmp
|
mountPath: /tmp
|
||||||
|
|
||||||
{% if dex_theme_enabled %}
|
{% if dex_theme_enabled %}
|
||||||
- name: theme
|
- name: theme
|
||||||
mountPath: /srv/dex/web/themes/light/styles.css
|
mountPath: /srv/dex/web/themes/light/styles.css
|
||||||
|
|||||||
+6
-26
@@ -20,29 +20,9 @@ dex_static_clients:
|
|||||||
redirectURIs:
|
redirectURIs:
|
||||||
- https://www.eom.dev/members/oidc/callback/
|
- https://www.eom.dev/members/oidc/callback/
|
||||||
|
|
||||||
# Additional connector: don't alter legacy ldap/idAttr uid, which changes sub
|
# One LDAP connector admits both legacy POSIX accounts and website accounts.
|
||||||
# identifiers for existing applications. This connector admits free web accounts.
|
# Keep its ID and idAttr unchanged so existing OIDC subjects remain stable.
|
||||||
dex_additional_connectors:
|
dex_ldap_connector_id: ldap
|
||||||
- type: ldap
|
dex_ldap_id_attribute: uid
|
||||||
id: www-ldap
|
dex_ldap_user_base_dn: dc=eom,dc=dev
|
||||||
name: eom.dev Membership
|
dex_ldap_user_filter: "(|(&(objectClass=posixAccount)(memberOf=cn=Dex Users,ou=Dex,ou=Services,dc=eom,dc=dev))(objectClass=inetOrgPerson))"
|
||||||
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
|
|
||||||
|
|||||||
+3
-1
@@ -31,6 +31,8 @@ for fixture_run in (1,):
|
|||||||
assert resolved['dex_ldap_group_user_attribute'] == 'cn'
|
assert resolved['dex_ldap_group_user_attribute'] == 'cn'
|
||||||
assert resolved['dex_theme_enabled'] is True
|
assert resolved['dex_theme_enabled'] is True
|
||||||
assert resolved['dex_postgres_manage'] is False
|
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']}
|
clients = {client['id']: client for client in resolved['dex_static_clients']}
|
||||||
assert len(clients) == len(resolved['dex_static_clients']) == 7
|
assert len(clients) == len(resolved['dex_static_clients']) == 7
|
||||||
assert resolved['dex_matrix_client_enabled'] is False
|
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['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 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 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())
|
values = yaml.safe_load(Path('/tmp/dex-values.yml').read_text())
|
||||||
config = values['config']
|
config = values['config']
|
||||||
@@ -53,6 +54,7 @@ ldap = config['connectors'][0]
|
|||||||
assert ldap['id'] == 'ldap'
|
assert ldap['id'] == 'ldap'
|
||||||
assert ldap['config']['insecureNoSSL'] is True
|
assert ldap['config']['insecureNoSSL'] is True
|
||||||
assert ldap['config']['startTLS'] is False
|
assert ldap['config']['startTLS'] is False
|
||||||
|
assert len(config['connectors']) == 1
|
||||||
assert ldap['config']['userSearch']['idAttr'] == 'uid'
|
assert ldap['config']['userSearch']['idAttr'] == 'uid'
|
||||||
assert ldap['config']['groupSearch']['userMatchers'][0]['userAttr'] == 'DN'
|
assert ldap['config']['groupSearch']['userMatchers'][0]['userAttr'] == 'DN'
|
||||||
assert config['staticClients'][0]['id'] == 'matrix-fixture'
|
assert config['staticClients'][0]['id'] == 'matrix-fixture'
|
||||||
|
|||||||
Reference in New Issue
Block a user