Clients
This commit is contained in:
@@ -22,10 +22,49 @@ Ansible Vault-encrypted vars file:
|
|||||||
- `openldap_readonly_password`: existing LDAP search-account password.
|
- `openldap_readonly_password`: existing LDAP search-account password.
|
||||||
- `matrix_dex_oidc_client_id` and `matrix_dex_oidc_client_secret`: existing MAS client.
|
- `matrix_dex_oidc_client_id` and `matrix_dex_oidc_client_secret`: existing MAS client.
|
||||||
- `postgresql_admin_password`: needed only if deliberately enabling DB management.
|
- `postgresql_admin_password`: needed only if deliberately enabling DB management.
|
||||||
- Optional `www_dex_client_secret`: enables the website client and its separate
|
- `www_dex_client_secret`: matches the website's `DEX_CLIENT_SECRET`.
|
||||||
`www-ldap`/`entryUUID` connector, without changing legacy identities. It must
|
- `gitea_dex_client_secret`, `discourse_dex_client_secret`,
|
||||||
match the website's `DEX_CLIENT_SECRET`. Leave absent to preserve Matrix-only
|
`peertube_dex_client_secret`, `mastodon_dex_client_secret`, and
|
||||||
behavior. Review and declare any other deployed clients before rollout.
|
`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
|
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`
|
||||||
@@ -70,8 +109,8 @@ The site playbook maps legacy inventory variables explicitly:
|
|||||||
| `openldap_readonly_password` | `dex_ldap_bind_password` |
|
| `openldap_readonly_password` | `dex_ldap_bind_password` |
|
||||||
| `matrix_dex_oidc_client_id` / `matrix_dex_oidc_client_secret` | Existing Matrix Auth Service client |
|
| `matrix_dex_oidc_client_id` / `matrix_dex_oidc_client_secret` | Existing Matrix Auth Service client |
|
||||||
|
|
||||||
The site playbook enables the existing Matrix client. Its original
|
The site playbook explicitly includes the existing Matrix client. Its original
|
||||||
redirect URI is explicit in `dex_matrix_redirect_uris`; override to match your
|
redirect URI is explicit in `dex_static_clients`; override to match your
|
||||||
actual MAS configuration. Other clients previously added directly to Helm must
|
actual MAS configuration. Other clients previously added directly to Helm must
|
||||||
be declared in `dex_static_clients` before running this role. Helm values are
|
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
|
reset to this role's complete values: undeclared manual Helm customizations are
|
||||||
|
|||||||
@@ -46,14 +46,54 @@
|
|||||||
dex_ldap_group_user_attribute: cn
|
dex_ldap_group_user_attribute: cn
|
||||||
dex_ldap_group_filter: "(objectClass=groupOfUniqueNames)"
|
dex_ldap_group_filter: "(objectClass=groupOfUniqueNames)"
|
||||||
|
|
||||||
dex_matrix_client_enabled: true
|
# All clients are declared once below; don't also emit the convenience client.
|
||||||
dex_matrix_redirect_uris:
|
dex_matrix_client_enabled: false
|
||||||
|
# Credentials are separate Vault/inventory variables, shared only with the
|
||||||
|
# corresponding application. No secret is generated or rotated by this play.
|
||||||
|
dex_static_clients:
|
||||||
|
- id: "{{ matrix_dex_oidc_client_id }}"
|
||||||
|
name: Matrix Auth Service
|
||||||
|
secret: "{{ matrix_dex_oidc_client_secret }}"
|
||||||
|
redirectURIs:
|
||||||
- https://mas.eom.dev/upstream/callback/01K96AQEZKKABW34PY3R6BVNJ4
|
- https://mas.eom.dev/upstream/callback/01K96AQEZKKABW34PY3R6BVNJ4
|
||||||
|
- id: www-membership
|
||||||
# Optional website integration. Set www_dex_client_secret in Vault to enable;
|
name: eom.dev Membership
|
||||||
# it must equal the membership application's DEX_CLIENT_SECRET.
|
secret: "{{ www_dex_client_secret }}"
|
||||||
dex_static_clients: "{{ [{'id': 'www-membership', 'name': 'eom.dev Membership', 'secret': www_dex_client_secret, 'redirectURIs': ['https://www.eom.dev/members/oidc/callback/']}] if (www_dex_client_secret | default('') | length > 0) else [] }}"
|
redirectURIs:
|
||||||
dex_additional_connectors: "{{ [_dex_eom_www_connector] if (www_dex_client_secret | default('') | length > 0) else [] }}"
|
- https://www.eom.dev/members/oidc/callback/
|
||||||
|
- id: gitea
|
||||||
|
name: Gitea
|
||||||
|
secret: "{{ gitea_dex_client_secret }}"
|
||||||
|
# Authentication source in Gitea must be named exactly "dex".
|
||||||
|
redirectURIs:
|
||||||
|
- https://gitea.eom.dev/user/oauth2/dex/callback
|
||||||
|
- id: discourse
|
||||||
|
name: Discourse
|
||||||
|
secret: "{{ discourse_dex_client_secret }}"
|
||||||
|
# discourse-openid-connect strategy name is oidc.
|
||||||
|
redirectURIs:
|
||||||
|
- https://discourse.eom.dev/auth/oidc/callback
|
||||||
|
- id: peertube
|
||||||
|
name: PeerTube
|
||||||
|
secret: "{{ peertube_dex_client_secret }}"
|
||||||
|
# Requires peertube-plugin-auth-openid-connect.
|
||||||
|
redirectURIs:
|
||||||
|
- https://peertube.eom.dev/plugins/auth-openid-connect/router/code-cb
|
||||||
|
- id: mastodon
|
||||||
|
name: Mastodon
|
||||||
|
secret: "{{ mastodon_dex_client_secret }}"
|
||||||
|
# Matches OIDC_REDIRECT_URI in the existing Mastodon deployment.
|
||||||
|
redirectURIs:
|
||||||
|
- https://mastodon.eom.dev/auth/auth/openid_connect/callback
|
||||||
|
- id: nextcloud
|
||||||
|
name: Nextcloud
|
||||||
|
secret: "{{ nextcloud_dex_client_secret }}"
|
||||||
|
# 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.
|
||||||
|
redirectURIs:
|
||||||
|
- https://nextcloud.eom.dev/index.php/apps/user_oidc/code
|
||||||
|
dex_additional_connectors:
|
||||||
|
- "{{ _dex_eom_www_connector }}"
|
||||||
_dex_eom_www_connector:
|
_dex_eom_www_connector:
|
||||||
type: ldap
|
type: ldap
|
||||||
id: www-ldap
|
id: www-ldap
|
||||||
|
|||||||
@@ -13,6 +13,23 @@
|
|||||||
fail_msg: Configure database/LDAP credentials, client secrets and explicit database chart version if managing PostgreSQL.
|
fail_msg: Configure database/LDAP credentials, client secrets and explicit database chart version if managing PostgreSQL.
|
||||||
no_log: true
|
no_log: true
|
||||||
|
|
||||||
|
- name: Validate explicit OIDC clients
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- item.id | default('') | length > 0
|
||||||
|
- item.name | default('') | length > 0
|
||||||
|
- item.redirectURIs | default([]) | length > 0
|
||||||
|
- item.public | default(false) or ((item.secret | default('') | length > 0) != (item.secretEnv | default('') | length > 0))
|
||||||
|
fail_msg: Every confidential client needs an ID, name, exact callback URI and either a secret or secretEnv, but not both.
|
||||||
|
loop: "{{ dex_static_clients }}"
|
||||||
|
no_log: true
|
||||||
|
|
||||||
|
- name: Reject duplicate explicit client IDs
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- dex_static_clients | map(attribute='id') | unique | length == dex_static_clients | length
|
||||||
|
no_log: true
|
||||||
|
|
||||||
- name: Ensure Dex namespace
|
- name: Ensure Dex namespace
|
||||||
kubernetes.core.k8s:
|
kubernetes.core.k8s:
|
||||||
state: present
|
state: present
|
||||||
|
|||||||
+19
-9
@@ -8,30 +8,40 @@ from kubernetes_validate import validate
|
|||||||
from ansible.parsing.dataloader import DataLoader
|
from ansible.parsing.dataloader import DataLoader
|
||||||
from ansible.template import Templar
|
from ansible.template import Templar
|
||||||
|
|
||||||
# Resolve the site's actual workflow variables with fixture credentials only;
|
# Resolve the site's actual workflow with fixture credentials only.
|
||||||
# verify both Matrix-only migration and opt-in website integration.
|
|
||||||
defaults = yaml.safe_load(Path('defaults/main.yml').read_text())
|
defaults = yaml.safe_load(Path('defaults/main.yml').read_text())
|
||||||
site = yaml.safe_load(Path('playbooks/deploy-eom.yml').read_text())[0]['vars']
|
site = yaml.safe_load(Path('playbooks/deploy-eom.yml').read_text())[0]['vars']
|
||||||
for website_enabled in (False, True):
|
for fixture_run in (1,):
|
||||||
variables = {
|
variables = {
|
||||||
**defaults, **site,
|
**defaults, **site,
|
||||||
'dex_admin_password': 'fixture-db',
|
'dex_admin_password': 'fixture-db',
|
||||||
'openldap_readonly_password': 'fixture-ldap',
|
'openldap_readonly_password': 'fixture-ldap',
|
||||||
'matrix_dex_oidc_client_id': 'matrix-fixture',
|
'matrix_dex_oidc_client_id': 'matrix-fixture',
|
||||||
'matrix_dex_oidc_client_secret': 'fixture-matrix',
|
'matrix_dex_oidc_client_secret': 'fixture-matrix',
|
||||||
|
'www_dex_client_secret': 'fixture-www',
|
||||||
|
'gitea_dex_client_secret': 'fixture-gitea',
|
||||||
|
'discourse_dex_client_secret': 'fixture-discourse',
|
||||||
|
'peertube_dex_client_secret': 'fixture-peertube',
|
||||||
|
'mastodon_dex_client_secret': 'fixture-mastodon',
|
||||||
|
'nextcloud_dex_client_secret': 'fixture-nextcloud',
|
||||||
}
|
}
|
||||||
if website_enabled:
|
|
||||||
variables['www_dex_client_secret'] = 'fixture-www'
|
|
||||||
resolved = Templar(loader=DataLoader(), variables=variables).template(site)
|
resolved = Templar(loader=DataLoader(), variables=variables).template(site)
|
||||||
assert resolved['dex_issuer'] == 'https://dex.eom.dev/'
|
assert resolved['dex_issuer'] == 'https://dex.eom.dev/'
|
||||||
assert resolved['dex_postgres_password'] == 'fixture-db'
|
assert resolved['dex_postgres_password'] == 'fixture-db'
|
||||||
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 bool(resolved['dex_static_clients']) == website_enabled
|
clients = {client['id']: client for client in resolved['dex_static_clients']}
|
||||||
assert bool(resolved['dex_additional_connectors']) == website_enabled
|
assert len(clients) == len(resolved['dex_static_clients']) == 7
|
||||||
if website_enabled:
|
assert resolved['dex_matrix_client_enabled'] is False
|
||||||
assert resolved['dex_static_clients'][0]['secret'] == 'fixture-www'
|
assert clients['matrix-fixture']['redirectURIs'] == ['https://mas.eom.dev/upstream/callback/01K96AQEZKKABW34PY3R6BVNJ4']
|
||||||
|
assert clients['www-membership']['secret'] == 'fixture-www'
|
||||||
|
assert clients['gitea']['redirectURIs'] == ['https://gitea.eom.dev/user/oauth2/dex/callback']
|
||||||
|
assert clients['discourse']['redirectURIs'] == ['https://discourse.eom.dev/auth/oidc/callback']
|
||||||
|
assert clients['peertube']['redirectURIs'] == ['https://peertube.eom.dev/plugins/auth-openid-connect/router/code-cb']
|
||||||
|
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'
|
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())
|
||||||
|
|||||||
Reference in New Issue
Block a user