Skip to content

DRAFT: Add support for multiple IdP providers in Federation #3001

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 28 additions & 28 deletions hooks/playbooks/federation-controlplane-config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,29 @@
- name: Create kustomization to update Keystone to use Federation
hosts: "{{ cifmw_target_hook_host | default('localhost') }}"
tasks:
- name: Set urls for install type uni
- name: Read uni vars from federation role
ansible.builtin.set_fact:
cifmw_federation_keycloak_url: 'https://keycloak-openstack.apps.ocp.openstack.lab'
cifmw_federation_keystone_url: 'https://keystone-public-openstack.apps.ocp.openstack.lab'
cifmw_federation_horizon_url: 'https://horizon-openstack.apps.ocp.openstack.lab'
cifmw_federation_domain: "apps.ocp.openstack.lab"
when: cifmw_federation_deploy_type == "uni"

- name: Set urls for install type crc
- name: Read crc vars from federation role
ansible.builtin.set_fact:
cifmw_federation_keycloak_url: 'https://keycloak-openstack.apps-crc.testing'
cifmw_federation_keystone_url: 'https://keystone-public-openstack.apps-crc.testing'
cifmw_federation_horizon_url: 'https://horizon-openstack.apps-crc.testing'
cifmw_federation_domain: "apps-crc.testing"
when: cifmw_federation_deploy_type == "crc"

- name: Read all vars from federation role
ansible.builtin.import_role:
name: federation
vars_from: all-vars.yml

- name: Create file to customize keystone for Federation resources deployed in the control plane
ansible.builtin.copy:
dest: "{{ cifmw_basedir }}/artifacts/manifests/kustomizations/controlplane/keystone_federation.yaml"
content: |-
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- namespace: {{ namespace }}
- namespace: {{ cifmw_federation_run_osp_cmd_namespace }}
patches:
- target:
kind: OpenStackControlPlane
Expand All @@ -47,7 +48,6 @@
debug=true
[federation]
trusted_dashboard={{ cifmw_federation_horizon_url }}/dashboard/auth/websso/
sso_callback_template=/etc/keystone/sso_callback_template.html
[openid]
remote_id_attribute=HTTP_OIDC_ISS
[auth]
Expand All @@ -69,7 +69,7 @@
type: Opaque
metadata:
name: keycloakca
namespace: "openstack"
namespace: "{{ cifmw_federation_run_osp_cmd_namespace }}"
data:
KeyCloakCA: "{{ federation_sso_ca.content }}"

Expand All @@ -82,32 +82,32 @@
kind: Secret
metadata:
name: keystone-httpd-override
namespace: openstack
namespace: "{{ cifmw_federation_run_osp_cmd_namespace }}"
type: Opaque
stringData:
federation.conf: |
OIDCClaimPrefix "{{ cifmw_keystone_OIDC_ClaimPrefix }}"
OIDCResponseType "{{ cifmw_keystone_OIDC_ResponseType }}"
OIDCScope "{{ cifmw_keystone_OIDC_Scope }}"
OIDCClaimDelimiter "{{ cifmw_keystone_OIDC_ClaimDelimiter }}"
OIDCPassUserInfoAs "{{ cifmw_keystone_OIDC_PassUserInfoAs }}"
OIDCPassClaimsAs "{{ cifmw_keystone_OIDC_PassClaimsAs }}"
OIDCProviderMetadataURL "{{ cifmw_keystone_OIDC_ProviderMetadataURL }}"
OIDCClientID "{{ cifmw_keystone_OIDC_ClientID }}"
OIDCClientSecret "{{ cifmw_keystone_OIDC_ClientSecret }}"
OIDCCryptoPassphrase "{{ cifmw_keystone_OIDC_CryptoPassphrase }}"
OIDCOAuthClientID "{{ cifmw_keystone_OIDC_OAuthClientID }}"
OIDCOAuthClientSecret "{{ cifmw_keystone_OIDC_OAuthClientSecret }}"
OIDCOAuthIntrospectionEndpoint "{{ cifmw_keystone_OIDC_OAuthIntrospectionEndpoint }}"
OIDCRedirectURI "{{ cifmw_federation_keystone_url }}/v3/auth/OS-FEDERATION/identity_providers/{{ cifmw_keystone_OIDC_provider_name }}/protocols/openid/websso/"
OIDCClaimPrefix "{{ cifmw_federation_keystone_OIDC_ClaimPrefix }}"
OIDCResponseType "{{ cifmw_federation_keystone_OIDC_ResponseType }}"
OIDCScope "{{ cifmw_federation_keystone_OIDC_Scope }}"
OIDCClaimDelimiter "{{ cifmw_federation_keystone_OIDC_ClaimDelimiter }}"
OIDCPassUserInfoAs "{{ cifmw_federation_keystone_OIDC_PassUserInfoAs }}"
OIDCPassClaimsAs "{{ cifmw_federation_keystone_OIDC_PassClaimsAs }}"
OIDCProviderMetadataURL "{{ cifmw_federation_keystone_OIDC_ProviderMetadataURL }}"
OIDCClientID "{{ cifmw_federation_keystone_OIDC_ClientID }}"
OIDCClientSecret "{{ cifmw_federation_keystone_OIDC_ClientSecret }}"
OIDCCryptoPassphrase "{{ cifmw_federation_keystone_OIDC_CryptoPassphrase }}"
OIDCOAuthClientID "{{ cifmw_federation_keystone_OIDC_ClientID }}"
OIDCOAuthClientSecret "{{ cifmw_federation_keystone_OIDC_ClientSecret }}"
OIDCOAuthIntrospectionEndpoint "{{ cifmw_federation_keystone_OIDC_OAuthIntrospectionEndpoint }}"
OIDCRedirectURI "{{ cifmw_federation_keystone_url }}/v3/auth/OS-FEDERATION/identity_providers/{{ cifmw_federation_IdpName }}/protocols/openid/websso/"
LogLevel debug

<LocationMatch "/v3/auth/OS-FEDERATION/identity_providers/{{ cifmw_keystone_OIDC_provider_name }}/protocols/openid/websso">
<LocationMatch "/v3/auth/OS-FEDERATION/identity_providers/{{ cifmw_federation_IdpName }}/protocols/openid/websso">
AuthType "openid-connect"
Require valid-user
</LocationMatch>

<Location ~ "/v3/OS-FEDERATION/identity_providers/{{ cifmw_keystone_OIDC_provider_name }}/protocols/openid/auth">
<Location ~ "/v3/OS-FEDERATION/identity_providers/{{ cifmw_federation_IdpName }}/protocols/openid/auth">
AuthType oauth20
Require valid-user
</Location>
Expand Down
32 changes: 22 additions & 10 deletions hooks/playbooks/federation-horizon-controlplane-config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,32 @@
- name: Create kustomization to update Horizon to use Federation
hosts: "{{ cifmw_target_hook_host | default('localhost') }}"
tasks:
- name: Set urls for install type uni
- name: Read uni vars from federation role
ansible.builtin.set_fact:
cifmw_federation_keycloak_url: 'https://keycloak-openstack.apps.ocp.openstack.lab'
cifmw_federation_keystone_url: 'https://keystone-public-openstack.apps.ocp.openstack.lab'
cifmw_federation_horizon_url: 'https://horizon-openstack.apps.ocp.openstack.lab'
cifmw_federation_domain: "apps.ocp.openstack.lab"
when: cifmw_federation_deploy_type == "uni"

- name: Set urls for install type crc
- name: Read crc vars from federation role
ansible.builtin.set_fact:
cifmw_federation_keycloak_url: 'https://keycloak-openstack.apps-crc.testing'
cifmw_federation_keystone_url: 'https://keystone-public-openstack.apps-crc.testing'
cifmw_federation_horizon_url: 'https://horizon-openstack.apps-crc.testing'
cifmw_federation_domain: "apps-crc.testing"
when: cifmw_federation_deploy_type == "crc"

- name: Read all vars from federation role
ansible.builtin.import_role:
name: federation
vars_from: all-vars.yml

- name: Set websso settings for single IdP
ansible.builtin.set_fact:
cifmw_federation_websso_choices: '("OIDC", _("OpenID Connect")),'
cifmw_federation_websso_idp_mapping: '"OIDC": ("{{ cifmw_federation_IdpName }}", "openid"),'

- name: Set websso settings for multiple IdP
ansible.builtin.set_fact:
cifmw_federation_websso_choices: '("OIDC1", _("OpenID Connect IdP1")),("OIDC2", _("OpenID Connect IdP2")),'
cifmw_federation_websso_idp_mapping: '"OIDC1": ("{{ cifmw_federation_IdpName }}", "openid"),"OIDC2": ("{{ cifmw_federation_IdpName2 }}", "openid"),'
when: cifmw_federation_deploy_multirealm is true

- name: Create file to customize horizon for Federation resources deployed in the control plane
ansible.builtin.copy:
dest: "{{ cifmw_basedir }}/artifacts/manifests/kustomizations/controlplane/horizon_federation.yaml"
Expand Down Expand Up @@ -43,8 +55,8 @@
WEBSSO_ENABLED = True
WEBSSO_CHOICES = (
("credentials", _("Keystone Credentials")),
("OIDC", _("OpenID Connect")),
{{ cifmw_federation_websso_choices }}
)
WEBSSO_IDP_MAPPING = {
"OIDC": ("{{ cifmw_keystone_OIDC_provider_name }}", "openid"),
{{ cifmw_federation_websso_idp_mapping }}
}
206 changes: 206 additions & 0 deletions hooks/playbooks/federation-multirealm-controlplane-config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
---
- name: Create kustomization to update Keystone to use MultiRealm Federation
hosts: "{{ cifmw_target_hook_host | default('localhost') }}"
tasks:
- name: Read uni vars from federation role
ansible.builtin.set_fact:
cifmw_federation_domain: "apps.ocp.openstack.lab"
when: cifmw_federation_deploy_type == "uni"

- name: Read crc vars from federation role
ansible.builtin.set_fact:
cifmw_federation_domain: "apps-crc.testing"
when: cifmw_federation_deploy_type == "crc"

- name: Read all vars from federation role
ansible.builtin.import_role:
name: federation
vars_from: all-vars.yml

- name: Create file to customize keystone for IPA deployed in the control plane
ansible.builtin.copy:
dest: "{{ cifmw_basedir }}/artifacts/manifests/kustomizations/controlplane/keystone_multirealm_federation.yaml"
content: |-
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- namespace: {{ cifmw_federation_run_osp_cmd_namespace }}
patches:
- target:
kind: OpenStackControlPlane
name: .*
patch: |-
- op: add
path: /spec/tls
value: {}
- op: add
path: /spec/tls/caBundleSecretName
value: keycloakca
- op: add
path: /spec/keystone/template/httpdCustomization
value:
customConfigSecret: keystone-httpd-override
- op: add
path: /spec/keystone/template/federatedRealmConfig
value: federation-realm-data
- op: add
path: /spec/keystone/template/federationMountPath
value: "/etc/httpd/conf/oidcmetadatadir/"
- op: add
path: /spec/keystone/template/customServiceConfig
value: |
[DEFAULT]
insecure_debug=true
debug=true
[federation]
trusted_dashboard={{ cifmw_federation_horizon_url }}/dashboard/auth/websso/
[openid]
remote_id_attribute=HTTP_OIDC_ISS
[auth]
methods = password,token,oauth1,mapped,application_credential,openid
mode: "0644"

- name: Get ingress operator CA cert
ansible.builtin.slurp:
src: "{{ [ ansible_user_dir, 'ci-framework-data', 'tmp', 'ingress-operator-ca.crt'] | path_join }}"
register: federation_sso_ca

- name: Add Keycloak CA secret
kubernetes.core.k8s:
kubeconfig: "{{ cifmw_openshift_kubeconfig }}"
state: present
definition:
apiVersion: v1
kind: Secret
type: Opaque
metadata:
name: keycloakca
namespace: "{{ cifmw_federation_run_osp_cmd_namespace }}"
data:
KeyCloakCA: "{{ federation_sso_ca.content }}"

- name: Create Keystone httpd override secret for Federation
kubernetes.core.k8s:
kubeconfig: "{{ cifmw_openshift_kubeconfig }}"
state: present
definition:
apiVersion: v1
kind: Secret
metadata:
name: keystone-httpd-override
namespace: "{{ cifmw_federation_run_osp_cmd_namespace }}"
type: Opaque
stringData:
federation.conf: |
OIDCClaimPrefix "{{ cifmw_federation_keystone_OIDC_ClaimPrefix }}"
OIDCResponseType "{{ cifmw_federation_keystone_OIDC_ResponseType }}"
OIDCScope "{{ cifmw_federation_keystone_OIDC_Scope }}"
OIDCClaimDelimiter "{{ cifmw_federation_keystone_OIDC_ClaimDelimiter }}"
OIDCPassUserInfoAs "{{ cifmw_federation_keystone_OIDC_PassUserInfoAs }}"
OIDCPassClaimsAs "{{ cifmw_federation_keystone_OIDC_PassClaimsAs }}"
OIDCCryptoPassphrase "{{ cifmw_federation_keystone_OIDC_CryptoPassphrase }}"
OIDCMetadataDir "/etc/httpd/conf/oidcmetadatadir/"
OIDCRedirectURI "{{ cifmw_federation_keystone_url }}/v3/redirect_uri"
LogLevel debug

<LocationMatch "/v3/auth/OS-FEDERATION/identity_providers/{{ cifmw_federation_IdpName }}/protocols/openid/websso">
AuthType "openid-connect"
Require valid-user
</LocationMatch>

<Location ~ "/v3/OS-FEDERATION/identity_providers/{{ cifmw_federation_IdpName }}/protocols/openid/auth">
AuthType oauth20
Require valid-user
</Location>

<LocationMatch "/v3/auth/OS-FEDERATION/identity_providers/{{ cifmw_federation_IdpName2 }}/protocols/openid/websso">
AuthType "openid-connect"
Require valid-user
</LocationMatch>

<Location ~ "/v3/OS-FEDERATION/identity_providers/{{ cifmw_federation_IdpName2 }}/protocols/openid/auth">
AuthType oauth20
Require valid-user
</Location>

<Location ~ "/redirect_uri">
Require valid-user
AuthType openid-connect
</Location>

<LocationMatch "/v3/auth/OS-FEDERATION/websso/openid">
AuthType "openid-connect"
Require valid-user
</LocationMatch>

- name: Download realm1 OpenID configuration
ansible.builtin.uri:
url: "{{ cifmw_federation_keystone_OIDC_ProviderMetadataURL }}"
method: GET
return_content: true
validate_certs: false
register: openid_wellknown_config1

- name: Download realm2 OpenID configuration
ansible.builtin.uri:
url: "{{ cifmw_federation_keystone_OIDC_ProviderMetadataURL2 }}"
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: "{{ cifmw_federation_keystone_idp1_conf_filename }}"
contents: |
{
"scope" : "openid email profile"
}
- filename: "{{ cifmw_federation_keystone_idp1_client_filename }}"
contents: "{{ {'client_id': cifmw_federation_keystone_OIDC_ClientID, 'client_secret': cifmw_federation_keystone_OIDC_ClientSecret } | to_json }}"
- filename: "{{ cifmw_federation_keystone_idp1_provider_filename }}"
contents: |
{{ openid_wellknown_config1.content }}
- filename: "{{ cifmw_federation_keystone_idp2_conf_filename }}"
contents: |
{
"scope" : "openid email profile"
}
- filename: "{{ cifmw_federation_keystone_idp2_client_filename }}"
contents: "{{ {'client_id': cifmw_federation_keystone_OIDC_ClientID2, 'client_secret': cifmw_federation_keystone_OIDC_ClientSecret2 } | to_json }}"
- filename: "{{ cifmw_federation_keystone_idp2_provider_filename }}"
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 %}
}

# IMPORTANT FIX: Apply `to_json` one final time to the _raw_federation_config_json_value
# to ensure it's a single, valid JSON string for stringData.
- name: Final JSON string for Secret stringData
ansible.builtin.set_fact:
federation_config_json_string: "{{ _raw_federation_config_json_value }}"

- name: Print the generated JSON string for verification
ansible.builtin.debug:
var: federation_config_json_string

- 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 }}"
Loading