From a5ed715d810886adc0b75824df3a5963b51504bf Mon Sep 17 00:00:00 2001 From: Steve Brasier Date: Tue, 9 Dec 2025 17:06:32 +0000 Subject: [PATCH 1/2] add DEX support for ondemand --- ansible/roles/openondemand/README.md | 39 +++++++++++++++++--- ansible/roles/openondemand/defaults/main.yml | 34 ++++++++++++++++- ansible/roles/openondemand/tasks/main.yml | 21 ++++++++--- 3 files changed, 82 insertions(+), 12 deletions(-) diff --git a/ansible/roles/openondemand/README.md b/ansible/roles/openondemand/README.md index 3727d21b2..b73137945 100644 --- a/ansible/roles/openondemand/README.md +++ b/ansible/roles/openondemand/README.md @@ -10,7 +10,8 @@ This uses the [osc.ood](https://github.com/OSC/ood-ansible) Ansible role to prov - The `openondemand` node, i.e. the node which will host the Open Ondemand server/portal must: - Have the slurm packages (e.g. `sinfo` etc) installed and be able to contact the Slurm controller (e.g. add this node to the `login` group). - Have access to any cluster shared filesystems. -- Open Ondemand's authentication maps authenticated users (e.g. via OIDC) to local users on the `openondemand` node (see `openondemand_mapping_users`). You must therefore ensure that whatever is providing users for the cluster covers the `openondemand` node, e.g. if using `basic_users` role ensure the group for this includes the `openondemand` group. +- Open Ondemand's authentication maps authenticated users (e.g. via OIDC) to local users on the `openondemand` node. + Therefore whatever mechanism provides cluster users (e.g. `basic_users`, `freeipa`, `ldap` via sssd) must cover the `openondemand` node. ## Role Variables @@ -27,10 +28,8 @@ This uses the [osc.ood](https://github.com/OSC/ood-ansible) Ansible role to prov See the Open Ondemand [Authentication docs](https://osc.github.io/ood-documentation/latest/authentication/overview.html) for an overview of the authentication process. -- `openondemand_auth`: Required. Authentication method, either `'oidc'` or `'basic_pam'`. See relevant subsection below. -- `openondemand_mapping_users`: Required for `openondemand_auth=='oidc'`. A list of dicts defining mappings between remote authenticated usernames and local system usernames - see the Open Ondemand [user mapping docs](https://osc.github.io/ood-documentation/latest/authentication/overview/map-user.html). Each dict should have the following keys: - - `name`: A local (existing) user account - - `openondemand_username`: The remote authenticated username. See also `openondemand_oidc_remote_user_claim` if using OIDC authentication. +- `openondemand_auth`: Required. Authentication method, either `'oidc'`, `dex` +or `'basic_pam'`. See relevant subsection below. #### OIDC authentication @@ -45,6 +44,36 @@ The following variables are active when `openondemand_auth` is `oidc`. This role The OIDC provider should be configured to redirect to `https://{{ openondemand_servername }}/oidc` with scopes as appropriate for `openondemand_oidc_scope`. +<<<<<<< HEAD +======= +When using OIDC the remote user must be mapped to a local Linux user. The default +is to map the entire remote user claim string to the local username. See the +Open Ondemand [user mapping docs](https://osc.github.io/ood-documentation/latest/authentication/overview/map-user.html) +for more. The [osc.ood role](https://github.com/OSC/ood-ansible) variables such +as `user_map_match` may be set directly if necessary. + +#### DEX authentication +This runs DEX on the Open Ondemand host to provide an OIDC endpoint which federates +from some other identity provider. Generally no OIDC configuration is required. +Dex configuration can be provided using the `dex_settings` [osc.ood role](https://github.com/OSC/ood-ansible) +variable. + +**IMPORTANT** This takes a string of yaml, not actual yaml. E.g.: + +```yaml +dex_settings: | + dex: + connectors: + - type: ldap + id: ldap + name: LDAP + ... +``` + +See comments above for OIDC regarding remote user mapping. For LDAP the default +mapping is likely to be sufficent. + +>>>>>>> c5636843 (update ondemand role to support DEX) #### Basic/PAM authentication This option uses HTTP Basic Authentication (i.e. browser prompt) to get a username and password. This is then checked against an existing local user using PAM. Note that HTTPS is configured by default, so the password is protected in transit, although there are [other](https://security.stackexchange.com/a/990) security concerns with Basic Authentication. diff --git a/ansible/roles/openondemand/defaults/main.yml b/ansible/roles/openondemand/defaults/main.yml index ecb5a8d12..a0faa5fb6 100644 --- a/ansible/roles/openondemand/defaults/main.yml +++ b/ansible/roles/openondemand/defaults/main.yml @@ -1,8 +1,13 @@ --- # Authentication: +<<<<<<< HEAD openondemand_auth: # "oidc" or "basic_pam" openondemand_mapping_users: [] +======= +openondemand_auth: # "oidc", "dex" or "basic_pam" + +>>>>>>> c5636843 (update ondemand role to support DEX) ## Variables for `openondemand_auth=oidc` : openondemand_oidc_client_id: openondemand_oidc_client_secret: @@ -52,23 +57,46 @@ openondemand_auth_defaults: OIDCScope: "{{ openondemand_oidc_scope }}" OIDCRemoteUserClaim: "{{ openondemand_oidc_remote_user_claim }}" httpd_auth: # ood_portal.yml.j2 +<<<<<<< HEAD - "AuthType openid-connect" - "Require valid-user" - "ProxyPreserveHost On" # see under https://grafana.com/blog/2022/02/08/grafana-7.5.15-and-8.3.5-released-with-moderate-severity-security-fixes/ user_map_cmd: /opt/ood/ood_auth_map/bin/ood_auth_map.mapfile user_map_match: none +======= + - 'AuthType openid-connect' + - 'Require valid-user' + - 'ProxyPreserveHost On' # see under https://grafana.com/blog/2022/02/08/grafana-7.5.15-and-8.3.5-released-with-moderate-severity-security-fixes/ + + # Defaults for dex + dex: + oidc_uri: /oidc # has to be set separately to trigger oidc integration! + # *DON'T* set ood_auth_openidc - that will be provided automatically + httpd_auth: + - 'AuthType openid-connect' + - 'Require valid-user' + - 'ProxyPreserveHost On' # see under https://grafana.com/blog/2022/02/08/grafana-7.5.15-and-8.3.5-released-with-moderate-severity-security-fixes/ + +>>>>>>> c5636843 (update ondemand role to support DEX) # Defaults for basic/PAM auth - see https://osc.github.io/ood-documentation/latest/authentication/pam.html basic_pam: httpd_auth: # ood_portal.yml.j2 - "AuthType Basic" - 'AuthName "Open OnDemand"' +<<<<<<< HEAD - "AuthBasicProvider PAM" - "AuthPAMService ood" - "Require valid-user" - "ProxyPreserveHost On" # see under https://grafana.com/blog/2022/02/08/grafana-7.5.15-and-8.3.5-released-with-moderate-severity-security-fixes/ user_map_cmd: null user_map_match: ".*" +======= + - 'AuthBasicProvider PAM' + - 'AuthPAMService ood' + - 'Require valid-user' + - 'ProxyPreserveHost On' # see under https://grafana.com/blog/2022/02/08/grafana-7.5.15-and-8.3.5-released-with-moderate-severity-security-fixes/ +>>>>>>> c5636843 (update ondemand role to support DEX) # The below mapping is used to override osc.ood defaults. Keys are osc.ood variable names. # If you need to override *these* defaults (i.e. this role's vars are not sufficent) just set the corresponding osc.ood var as normal. @@ -92,13 +120,17 @@ openondemand_osc_ood_defaults: - SSLHonorCipherOrder On - SSLCompression off - SSLSessionTickets Off +<<<<<<< HEAD # User mapping: user_map_cmd: "{{ openondemand_auth_defaults[openondemand_auth | lower].user_map_cmd }}" user_map_match: "{{ openondemand_auth_defaults[openondemand_auth | lower].user_map_match }}" +======= + +>>>>>>> c5636843 (update ondemand role to support DEX) # Auth: - oidc_uri: "{{ openondemand_auth_defaults.oidc.oidc_uri if (openondemand_auth | lower) == 'oidc' else none }}" + oidc_uri: "{{ openondemand_auth_defaults.oidc.oidc_uri if (openondemand_auth | lower) in ['oidc', 'dex'] else none }}" ood_auth_openidc: "{{ openondemand_auth_defaults.oidc.ood_auth_openidc if (openondemand_auth | lower) == 'oidc' else none }}" httpd_auth: "{{ openondemand_auth_defaults[openondemand_auth | lower].httpd_auth }}" diff --git a/ansible/roles/openondemand/tasks/main.yml b/ansible/roles/openondemand/tasks/main.yml index 6fb6edb88..bdf5f3c2b 100644 --- a/ansible/roles/openondemand/tasks/main.yml +++ b/ansible/roles/openondemand/tasks/main.yml @@ -1,6 +1,11 @@ --- - name: Set osc.ood variables from this role's defaults if no overriding inventory var +<<<<<<< HEAD ansible.builtin.set_fact: +======= + # NB: When condition is important - some configurations require undefined vars + set_fact: +>>>>>>> c5636843 (update ondemand role to support DEX) "{{ item.key }}": "{{ lookup('vars', item.key, default=item.value) }}" loop: "{{ openondemand_osc_ood_defaults | dict2items }}" when: (item.key in hostvars[inventory_hostname]) or (item.value) @@ -98,6 +103,7 @@ dest: /etc/ood/config/pun/html/missing_home_directory.html mode: "0644" +<<<<<<< HEAD - name: Create mapping directory ansible.builtin.file: path: /etc/grid-security @@ -116,6 +122,8 @@ mode: u=rw,g=r,o= when: openondemand_mapping_users +======= +>>>>>>> c5636843 (update ondemand role to support DEX) - name: Create app directories for dashboard links ansible.builtin.file: path: /var/www/ood/apps/sys/{{ item.app_name | default(item.name) }} @@ -130,12 +138,13 @@ mode: "0644" loop: "{{ openondemand_dashboard_links }}" -# - name: Ensure ondemand-dex is running and active -# service: -# name: ondemand-dex -# enabled: yes -# state: stopped -# when: false +- name: Ensure ondemand-dex is running/enabled/restarted for config changes + # Can be dropped once on a version including https://github.com/OSC/ood-ansible/pull/272 + ansible.builtin.systemd: + name: ondemand-dex + enabled: yes + state: restarted + when: openondemand_auth | lower == "dex" # - name: Copy site images # copy: From 79ccb546bda1b42cc3066100410e26c2742ca2cd Mon Sep 17 00:00:00 2001 From: Steve Brasier Date: Thu, 11 Dec 2025 16:46:20 +0000 Subject: [PATCH 2/2] fix merge conflicts --- ansible/roles/openondemand/README.md | 3 -- ansible/roles/openondemand/defaults/main.yml | 31 -------------------- ansible/roles/openondemand/tasks/main.yml | 25 ---------------- 3 files changed, 59 deletions(-) diff --git a/ansible/roles/openondemand/README.md b/ansible/roles/openondemand/README.md index b73137945..7c379af58 100644 --- a/ansible/roles/openondemand/README.md +++ b/ansible/roles/openondemand/README.md @@ -44,8 +44,6 @@ The following variables are active when `openondemand_auth` is `oidc`. This role The OIDC provider should be configured to redirect to `https://{{ openondemand_servername }}/oidc` with scopes as appropriate for `openondemand_oidc_scope`. -<<<<<<< HEAD -======= When using OIDC the remote user must be mapped to a local Linux user. The default is to map the entire remote user claim string to the local username. See the Open Ondemand [user mapping docs](https://osc.github.io/ood-documentation/latest/authentication/overview/map-user.html) @@ -73,7 +71,6 @@ dex_settings: | See comments above for OIDC regarding remote user mapping. For LDAP the default mapping is likely to be sufficent. ->>>>>>> c5636843 (update ondemand role to support DEX) #### Basic/PAM authentication This option uses HTTP Basic Authentication (i.e. browser prompt) to get a username and password. This is then checked against an existing local user using PAM. Note that HTTPS is configured by default, so the password is protected in transit, although there are [other](https://security.stackexchange.com/a/990) security concerns with Basic Authentication. diff --git a/ansible/roles/openondemand/defaults/main.yml b/ansible/roles/openondemand/defaults/main.yml index a0faa5fb6..14a7c910c 100644 --- a/ansible/roles/openondemand/defaults/main.yml +++ b/ansible/roles/openondemand/defaults/main.yml @@ -1,13 +1,8 @@ --- # Authentication: -<<<<<<< HEAD -openondemand_auth: # "oidc" or "basic_pam" -openondemand_mapping_users: [] -======= openondemand_auth: # "oidc", "dex" or "basic_pam" ->>>>>>> c5636843 (update ondemand role to support DEX) ## Variables for `openondemand_auth=oidc` : openondemand_oidc_client_id: openondemand_oidc_client_secret: @@ -57,14 +52,6 @@ openondemand_auth_defaults: OIDCScope: "{{ openondemand_oidc_scope }}" OIDCRemoteUserClaim: "{{ openondemand_oidc_remote_user_claim }}" httpd_auth: # ood_portal.yml.j2 -<<<<<<< HEAD - - "AuthType openid-connect" - - "Require valid-user" - - "ProxyPreserveHost On" # see under https://grafana.com/blog/2022/02/08/grafana-7.5.15-and-8.3.5-released-with-moderate-severity-security-fixes/ - user_map_cmd: /opt/ood/ood_auth_map/bin/ood_auth_map.mapfile - user_map_match: none - -======= - 'AuthType openid-connect' - 'Require valid-user' - 'ProxyPreserveHost On' # see under https://grafana.com/blog/2022/02/08/grafana-7.5.15-and-8.3.5-released-with-moderate-severity-security-fixes/ @@ -78,25 +65,15 @@ openondemand_auth_defaults: - 'Require valid-user' - 'ProxyPreserveHost On' # see under https://grafana.com/blog/2022/02/08/grafana-7.5.15-and-8.3.5-released-with-moderate-severity-security-fixes/ ->>>>>>> c5636843 (update ondemand role to support DEX) # Defaults for basic/PAM auth - see https://osc.github.io/ood-documentation/latest/authentication/pam.html basic_pam: httpd_auth: # ood_portal.yml.j2 - "AuthType Basic" - 'AuthName "Open OnDemand"' -<<<<<<< HEAD - - "AuthBasicProvider PAM" - - "AuthPAMService ood" - - "Require valid-user" - - "ProxyPreserveHost On" # see under https://grafana.com/blog/2022/02/08/grafana-7.5.15-and-8.3.5-released-with-moderate-severity-security-fixes/ - user_map_cmd: null - user_map_match: ".*" -======= - 'AuthBasicProvider PAM' - 'AuthPAMService ood' - 'Require valid-user' - 'ProxyPreserveHost On' # see under https://grafana.com/blog/2022/02/08/grafana-7.5.15-and-8.3.5-released-with-moderate-severity-security-fixes/ ->>>>>>> c5636843 (update ondemand role to support DEX) # The below mapping is used to override osc.ood defaults. Keys are osc.ood variable names. # If you need to override *these* defaults (i.e. this role's vars are not sufficent) just set the corresponding osc.ood var as normal. @@ -120,15 +97,7 @@ openondemand_osc_ood_defaults: - SSLHonorCipherOrder On - SSLCompression off - SSLSessionTickets Off -<<<<<<< HEAD - - # User mapping: - user_map_cmd: "{{ openondemand_auth_defaults[openondemand_auth | lower].user_map_cmd }}" - user_map_match: "{{ openondemand_auth_defaults[openondemand_auth | lower].user_map_match }}" -======= - ->>>>>>> c5636843 (update ondemand role to support DEX) # Auth: oidc_uri: "{{ openondemand_auth_defaults.oidc.oidc_uri if (openondemand_auth | lower) in ['oidc', 'dex'] else none }}" ood_auth_openidc: "{{ openondemand_auth_defaults.oidc.ood_auth_openidc if (openondemand_auth | lower) == 'oidc' else none }}" diff --git a/ansible/roles/openondemand/tasks/main.yml b/ansible/roles/openondemand/tasks/main.yml index bdf5f3c2b..9ee9043f6 100644 --- a/ansible/roles/openondemand/tasks/main.yml +++ b/ansible/roles/openondemand/tasks/main.yml @@ -1,11 +1,7 @@ --- - name: Set osc.ood variables from this role's defaults if no overriding inventory var -<<<<<<< HEAD ansible.builtin.set_fact: -======= # NB: When condition is important - some configurations require undefined vars - set_fact: ->>>>>>> c5636843 (update ondemand role to support DEX) "{{ item.key }}": "{{ lookup('vars', item.key, default=item.value) }}" loop: "{{ openondemand_osc_ood_defaults | dict2items }}" when: (item.key in hostvars[inventory_hostname]) or (item.value) @@ -103,27 +99,6 @@ dest: /etc/ood/config/pun/html/missing_home_directory.html mode: "0644" -<<<<<<< HEAD -- name: Create mapping directory - ansible.builtin.file: - path: /etc/grid-security - state: directory - owner: root - group: apache - mode: u=rwX,g=rX,o= - when: openondemand_mapping_users - -- name: Create mapping file - ansible.builtin.template: - dest: /etc/grid-security/grid-mapfile - src: grid-mapfile.j2 - owner: root - group: apache - mode: u=rw,g=r,o= - when: openondemand_mapping_users - -======= ->>>>>>> c5636843 (update ondemand role to support DEX) - name: Create app directories for dashboard links ansible.builtin.file: path: /var/www/ood/apps/sys/{{ item.app_name | default(item.name) }}