Configure multi-realm federated authentication
Your users can log in to the Dashboard and select from multiple federated external Identity Providers (IdPs) by using OpenID Connect (OIDC).
Enable multiple federated IdP authentication Copy linkLink copied!
Configure Red Hat OpenStack Services on OpenShift (RHOSO) to support multiple federated Identity Providers (IdPs) so that users can log in to the Dashboard by using single sign-on (SSO) and select their authentication realm.
Before you begin Copy linkLink copied!
- You have installed RHOSO.
- You have multiple external OpenID Connect (OIDC) IdPs configured in your environment.
About this task Copy linkLink copied!
The RHOSO deployment of multiple federated IdPs implements the Web SSO authentication flow because the openstack CLI does not support multiple IdPs.
Procedure Copy linkLink copied!
-
Choose a name to uniquely identify each IdP.
In this example there are two IdPs, whose names are referenced as
<idp_name_1>and<idp_name_2>. -
Obtain the following settings from each IdP administrator:
- The
FQDNfor each IdP that is referenced in this procedure as<fqdn_1>and<fqdn_2>. - The federation
Realm Namefor each IdP that is referenced in this procedure as<realm_name_1>and<realm_name_2>. - The
Client IDfor each IdP that is referenced in this procedure as<client_id_1>and<client_id_2>. - The
Client Secretfor each IdP that is referenced in this procedure as<client_secret_1>and<client_secret_2>. - The
Provider Metadata URLfor each IdP that is referenced in this procedure as<provider_metadata_url_1>and<provider_metadata_url_2>.
- The
-
Retrieve the Identity service (keystone) public endpoint:
$ oc get keystoneapis.keystone.openstack.org -o json | jq '.items[0].status.apiEndpoints.public'This Identity service endpoint is referenced in this procedure as
<keystone_url>. -
Provide the IdP administrators with the following information:
- Web origin:
https://<keystone_url> - Redirect URIs:
https://<keystone_url>/v3/auth/OS-FEDERATION/websso/openidProvide a URI for each IdP containing their unique IdP name, which must end in a trailing
/. You must send each URI to the corresponding IdP administrator:https://<keystone_url>/v3/auth/OS-FEDERATION/identity_providers/<idp_name_1>/protocols/openid/websso/ https://<keystone_url>/v3/auth/OS-FEDERATION/identity_providers/<idp_name_2>/protocols/openid/websso/ - Each federation client must have Implicit flow enabled and not Authorization code flow.
- Web origin:
-
Create a custom resource (CR) file for a secret called
keystone-httpd-override:The following specified OIDC parameter and associated Apache configuration is added to provide the most failsafe solution that supports the login of users from multiple IdPs. Consequently, previous sessions are not saved and users must reauthenticate themselves after they log out of the Dashboard:
OIDCAuthRequestParams "prompt=login" <IfModule headers_module> <Location "/v3/local-logout/clear"> Header always add Set-Cookie "mod_auth_openidc_session=deleted; Path=/; Max-Age=0; HttpOnly; Secure; SameSite=None" </Location> </IfModule> RewriteEngine On RewriteRule ^/v3/auth/OS-FEDERATION/identity_providers/(<idp_name_1>|<idp_name_2>)/protocols/openid/websso$ \ /v3/local-logout/clear [R=302,L] RewriteRule ^/v3/local-logout/clear$ \ /v3/auth/OS-FEDERATION/websso/openid [R=302,L,QSA,NE]If users in your multiple federated IdP deployment do not belong to more than one IdP then you can allow users to reopen the Dashboard they have closed without providing any authentication. In this case, you must remove this OIDC parameter and provide a different Apache
LocationMatchconfiguration to save the previous sessions.apiVersion: v1 kind: Secret metadata: name: keystone-httpd-override namespace: openstack type: Opaque stringData: federation.conf: | # Example OIDC directives for the *public* endpoint OIDCClaimPrefix "OIDC-" OIDCResponseType "id_token" OIDCScope "openid email profile" OIDCClaimDelimiter ";" OIDCPassUserInfoAs "claims" OIDCPassClaimsAs "both" OIDCCryptoPassphrase "<crypto_pass>" OIDCRedirectURI "<keystone_url>/v3/redirect_uri/" OIDCMetadataDir "/var/lib/httpd/metadata" OIDCAuthRequestParams "prompt=login" <IfModule headers_module> <Location "/v3/local-logout/clear"> Header always add Set-Cookie "mod_auth_openidc_session=deleted; Path=/; Max-Age=0; HttpOnly; Secure; SameSite=None" </Location> </IfModule> RewriteEngine On RewriteRule ^/v3/auth/OS-FEDERATION/identity_providers/(<idp_name_1>|<idp_name_2>)/protocols/openid/websso$ \ /v3/local-logout/clear [R=302,L] RewriteRule ^/v3/local-logout/clear$ \ /v3/auth/OS-FEDERATION/websso/openid [R=302,L,QSA,NE] <Location "/v3/auth/OS-FEDERATION/websso/openid"> AuthType openid-connect Require valid-user </Location> <Location "/v3/redirect_uri"> AuthType openid-connect Require valid-user </Location>Important The full value of theOIDCRedirectURIparameter must end in a trailing/.where:
-
<crypto_pass> - Specifies a user-defined passphrase to use when encrypting data for the OpenID Connect handshake.
-
<keystone_url> - Specifies the Identity service endpoint value that you retrieved in step 3.
-
<idp_name_1> - Specifies the unique IdP name that you specified in step 1.
-
<idp_name_2> - Specifies the unique IdP name that you specified in step 1.
-
-
Create the
keystone-httpd-overridesecret:$ oc create -f keystone-httpd-override.yaml -
Retrieve the URL for the Dashboard:
$ oc get horizons.horizon.openstack.org -o json | jq -r '.items[0].status.endpoint' -
Use the following Ansible Playbook to create a secret called
federation-realm-data:- name: Download realm1 OpenID configuration ansible.builtin.uri: url: "<provider_metadata_url_1>" method: GET return_content: true validate_certs: false register: openid_wellknown_config1 - name: Download realm2 OpenID configuration ansible.builtin.uri: url: "<provider_metadata_url_2>" method: GET return_content: true validate_certs: false register: openid_wellknown_config2 - name: Set federation_config_items ansible.builtin.set_fact: federation_config_items: - filename: "<fqdn_1>%2Fauth%2Frealms%2F<realm_name_1>.conf" contents: | { "scope" : "openid email profile" } - filename: "<fqdn_1>%2Fauth%2Frealms%2F<realm_name_1>.client" contents: "{{ {'client_id': <client_id_1>, 'client_secret': <client_secret_1> } | to_json }}" - filename: "<fqdn_1>%2Fauth%2Frealms%2F<realm_name_1>.provider" contents: | {{ openid_wellknown_config1.content }} - filename: "<fqdn_2>%2Fauth%2Frealms%2F<realm_name_2>.conf" contents: | { "scope" : "openid email profile" } - filename: "<fqdn_2>%2Fauth%2Frealms%2F<realm_name_2>.client" contents: "{{ {'client_id': <client_id_2>, 'client_secret': <client_secret_2>} | to_json }}" - filename: "<fqdn_2>%2Fauth%2Frealms%2F<realm_name_2>.provider" contents: | {{ openid_wellknown_config2.content }} - name: Generate the final federation_config.json string (as a dictionary) ansible.builtin.set_fact: _raw_federation_config_json_value: | { {% for item in federation_config_items %} "{{ item.filename }}": {{ item.contents }}{% if not loop.last %},{% endif %} {% endfor %} } - name: Final JSON string for Secret stringData ansible.builtin.set_fact: federation_config_json_string: "{{ _raw_federation_config_json_value }}" - name: Create a Kubernetes Secret with federation metadata kubernetes.core.k8s: state: present definition: apiVersion: v1 kind: Secret type: Opaque metadata: name: federation-realm-data namespace: openstack stringData: federation-config.json: "{{ federation_config_json_string }}"- Replace the IdP variables with the values you obtained from the IdP administrators in step 2.
-
Open your
OpenStackControlPlanecustom resource (CR) file,openstack_control_plane.yaml, on your workstation. -
Edit the
keystonesection of the OpenStackControlPlane CR:- Remove
externalfrom themethods =comma-delimited list. - Add the
httpdCustomization.customConfigSecretparameter and set this value to the key created in thekeystone-httpd-override.yamlCR file in step 5. - Add the
httpdCustomization.federatedRealmConfigparameter and set this value to thefederation-realm-datasecret created by the Ansible Playbook in step 8.keystone: template: customServiceConfig: | [federation] trusted_dashboard=<horizon_endpoint>/dashboard/auth/websso/ [openid] remote_id_attribute=HTTP_OIDC_ISS [auth] methods = password,token,oauth1,mapped,application_credential,openid httpdCustomization: customConfigSecret: keystone-httpd-override federatedRealmConfig: federation-realm-datawhere:
<horizon_endpoint>- Specifies the Dashboard URL you retrieved in step 7.
- Remove
-
Edit the
horizonsection of theOpenStackControlPlaneCR:horizon: template: customServiceConfig: | # Point horizon to the keystone public endpoint OPENSTACK_KEYSTONE_URL = "<keystone_endpoint>/v3" # Enable WebSSO in horizon WEBSSO_ENABLED = True # Provide login options in the horizon dropdown menu WEBSSO_CHOICES = ( ("credentials", _("Keystone Credentials")), ("OIDC1", _("OpenID Connect IdP1")), ("OIDC2", _("OpenID Connect IdP2")), ) # Map the "OIDC" choice of horizon to the keystone IDP and protocol WEBSSO_IDP_MAPPING = { "OIDC1": ("<idp_name1>", "openid"), "OIDC2": ("<idp_name2>", "openid"), } -
Update the control plane:
$ oc apply -f openstack_control_plane.yaml -
Optional: If your federated users obtain their project roles through mapped groups and you must create OpenStack Orchestration service (heat) stacks or use other trust-based workflows, then specify the authorization TTL for each IdP:
$ openstack identity provider set --authorization-ttl <ttl_minutes> <idp_name_1> $ openstack identity provider set --authorization-ttl <ttl_minutes> <idp_name_2>where:
-
<ttl_minutes> -
Specifies the time in minutes that the Identity service retains the group memberships of a federated user. Set the shortest TTL that covers the interval between the user logging in and the trust-based operation completing.
Note Each federated login retains the memberships for another full TTL window.
-