Merge "Add support to OpenID Connect Authentication flow"
This commit is contained in:
commit
87d8bd414d
@ -557,6 +557,7 @@ enable_glance: "{{ enable_openstack_core | bool }}"
|
|||||||
enable_haproxy: "yes"
|
enable_haproxy: "yes"
|
||||||
enable_keepalived: "{{ enable_haproxy | bool }}"
|
enable_keepalived: "{{ enable_haproxy | bool }}"
|
||||||
enable_keystone: "{{ enable_openstack_core | bool }}"
|
enable_keystone: "{{ enable_openstack_core | bool }}"
|
||||||
|
enable_keystone_federation: "{{ (keystone_identity_providers | length > 0) and (keystone_identity_mappings | length > 0) }}"
|
||||||
enable_mariadb: "yes"
|
enable_mariadb: "yes"
|
||||||
enable_memcached: "yes"
|
enable_memcached: "yes"
|
||||||
enable_neutron: "{{ enable_openstack_core | bool }}"
|
enable_neutron: "{{ enable_openstack_core | bool }}"
|
||||||
@ -1011,6 +1012,7 @@ enable_neutron_horizon_policy_file: "{{ enable_neutron }}"
|
|||||||
enable_nova_horizon_policy_file: "{{ enable_nova }}"
|
enable_nova_horizon_policy_file: "{{ enable_nova }}"
|
||||||
|
|
||||||
horizon_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ horizon_tls_port if kolla_enable_tls_internal | bool else horizon_port }}"
|
horizon_internal_endpoint: "{{ internal_protocol }}://{{ kolla_internal_fqdn | put_address_in_context('url') }}:{{ horizon_tls_port if kolla_enable_tls_internal | bool else horizon_port }}"
|
||||||
|
horizon_public_endpoint: "{{ public_protocol }}://{{ kolla_external_fqdn | put_address_in_context('url') }}:{{ horizon_tls_port if kolla_enable_tls_external | bool else horizon_port }}"
|
||||||
|
|
||||||
###################
|
###################
|
||||||
# External Ceph options
|
# External Ceph options
|
||||||
@ -1158,3 +1160,45 @@ swift_public_endpoint: "{{ public_protocol }}://{{ swift_external_fqdn | put_add
|
|||||||
octavia_admin_endpoint: "{{ admin_protocol }}://{{ octavia_internal_fqdn | put_address_in_context('url') }}:{{ octavia_api_port }}"
|
octavia_admin_endpoint: "{{ admin_protocol }}://{{ octavia_internal_fqdn | put_address_in_context('url') }}:{{ octavia_api_port }}"
|
||||||
octavia_internal_endpoint: "{{ internal_protocol }}://{{ octavia_internal_fqdn | put_address_in_context('url') }}:{{ octavia_api_port }}"
|
octavia_internal_endpoint: "{{ internal_protocol }}://{{ octavia_internal_fqdn | put_address_in_context('url') }}:{{ octavia_api_port }}"
|
||||||
octavia_public_endpoint: "{{ public_protocol }}://{{ octavia_external_fqdn | put_address_in_context('url') }}:{{ octavia_api_port }}"
|
octavia_public_endpoint: "{{ public_protocol }}://{{ octavia_external_fqdn | put_address_in_context('url') }}:{{ octavia_api_port }}"
|
||||||
|
|
||||||
|
###################################
|
||||||
|
# Identity federation configuration
|
||||||
|
###################################
|
||||||
|
# Here we configure all of the IdPs meta informations that will be required to implement identity federation with OpenStack Keystone.
|
||||||
|
# We require the administrator to enter the following metadata:
|
||||||
|
# * name (internal name of the IdP in Keystone);
|
||||||
|
# * openstack_domain (the domain in Keystone that the IdP belongs to)
|
||||||
|
# * protocol (the federated protocol used by the IdP; e.g. openid or saml);
|
||||||
|
# * identifier (the IdP identifier; e.g. https://accounts.google.com);
|
||||||
|
# * public_name (the public name that will be shown for users in Horizon);
|
||||||
|
# * attribute_mapping (the attribute mapping to be used for this IdP. This mapping is configured in the "keystone_identity_mappings" configuration);
|
||||||
|
# * metadata_folder (folder containing all the identity provider metadata as jsons named as the identifier without the protocol
|
||||||
|
# and with '/' escaped as %2F followed with '.provider' or '.client' or '.conf'; e.g. accounts.google.com.provider; PS, all .conf,
|
||||||
|
# .provider and .client jsons must be in the folder, even if you dont override any conf in the .conf json, you must leave it as an empty json '{}');
|
||||||
|
# * certificate_file (the path to the Identity Provider certificate file, the file must be named as 'certificate-key-id.pem';
|
||||||
|
# e.g. LRVweuT51StjMdsna59jKfB3xw0r8Iz1d1J1HeAbmlw.pem; You can find the key-id in the Identity provider '.well-known/openid-configuration' jwks_uri as kid);
|
||||||
|
#
|
||||||
|
# The IdPs meta information are to be presented to Kolla-Ansible as the following example:
|
||||||
|
# keystone_identity_providers:
|
||||||
|
# - name: "myidp1"
|
||||||
|
# openstack_domain: "my-domain"
|
||||||
|
# protocol: "openid"
|
||||||
|
# identifier: "https://accounts.google.com"
|
||||||
|
# public_name: "Authenticate via myidp1"
|
||||||
|
# attribute_mapping: "mappingId1"
|
||||||
|
# metadata_folder: "path/to/metadata/folder"
|
||||||
|
# certificate_file: "path/to/certificate/file.pem"
|
||||||
|
#
|
||||||
|
# We also need to configure the attribute mapping that is used by IdPs.
|
||||||
|
# The configuration of attribute mappings is a list of objects, where each
|
||||||
|
# object must have a 'name' (that mapps to the 'attribute_mapping' to the IdP
|
||||||
|
# object in the IdPs set), and the 'file' with a full qualified path to a mapping file.
|
||||||
|
# keystone_identity_mappings:
|
||||||
|
# - name: "mappingId1"
|
||||||
|
# file: "/full/qualified/path/to/mapping/json/file/to/mappingId1"
|
||||||
|
# - name: "mappingId2"
|
||||||
|
# file: "/full/qualified/path/to/mapping/json/file/to/mappingId2"
|
||||||
|
# - name: "mappingId3"
|
||||||
|
# file: "/full/qualified/path/to/mapping/json/file/to/mappingId3"
|
||||||
|
keystone_identity_providers: []
|
||||||
|
keystone_identity_mappings: []
|
||||||
|
@ -15,3 +15,5 @@ haproxy_backend_tcp_extra: []
|
|||||||
|
|
||||||
haproxy_health_check: "check inter 2000 rise 2 fall 5"
|
haproxy_health_check: "check inter 2000 rise 2 fall 5"
|
||||||
haproxy_health_check_ssl: "check check-ssl inter 2000 rise 2 fall 5"
|
haproxy_health_check_ssl: "check check-ssl inter 2000 rise 2 fall 5"
|
||||||
|
|
||||||
|
haproxy_enable_federation_openid: "{{ keystone_identity_providers | selectattr('protocol','equalto','openid') | list | count > 0 }}"
|
||||||
|
@ -123,7 +123,7 @@ horizon_extra_volumes: "{{ default_extra_volumes }}"
|
|||||||
# OpenStack
|
# OpenStack
|
||||||
####################
|
####################
|
||||||
horizon_logging_debug: "{{ openstack_logging_debug }}"
|
horizon_logging_debug: "{{ openstack_logging_debug }}"
|
||||||
horizon_keystone_url: "{{ keystone_internal_url }}/v3"
|
horizon_keystone_url: "{{ keystone_public_url if horizon_use_keystone_public_url | bool else keystone_internal_url }}/v3"
|
||||||
|
|
||||||
|
|
||||||
####################
|
####################
|
||||||
@ -149,3 +149,9 @@ horizon_murano_source_version: "{{ kolla_source_version }}"
|
|||||||
# TLS
|
# TLS
|
||||||
####################
|
####################
|
||||||
horizon_enable_tls_backend: "{{ kolla_enable_tls_backend }}"
|
horizon_enable_tls_backend: "{{ kolla_enable_tls_backend }}"
|
||||||
|
|
||||||
|
# This variable was created for administrators to define which one of the Keystone's URLs should be configured in Horizon.
|
||||||
|
# In some cases, such as when using OIDC, horizon will need to be configured with Keystone's public URL.
|
||||||
|
# Therefore, instead of overriding the whole "horizon_keystone_url", this change allows an easier integration because
|
||||||
|
# the Keystone public URL is already defined with variable "keystone_public_url".
|
||||||
|
horizon_use_keystone_public_url: False
|
||||||
|
@ -209,8 +209,9 @@ OPENSTACK_HOST = "{{ kolla_internal_fqdn }}"
|
|||||||
OPENSTACK_KEYSTONE_URL = "{{ horizon_keystone_url }}"
|
OPENSTACK_KEYSTONE_URL = "{{ horizon_keystone_url }}"
|
||||||
OPENSTACK_KEYSTONE_DEFAULT_ROLE = "{{ keystone_default_user_role }}"
|
OPENSTACK_KEYSTONE_DEFAULT_ROLE = "{{ keystone_default_user_role }}"
|
||||||
|
|
||||||
|
{% if enable_keystone_federation | bool %}
|
||||||
# Enables keystone web single-sign-on if set to True.
|
# Enables keystone web single-sign-on if set to True.
|
||||||
#WEBSSO_ENABLED = False
|
WEBSSO_ENABLED = True
|
||||||
|
|
||||||
# Determines which authentication choice to show as default.
|
# Determines which authentication choice to show as default.
|
||||||
#WEBSSO_INITIAL_CHOICE = "credentials"
|
#WEBSSO_INITIAL_CHOICE = "credentials"
|
||||||
@ -223,13 +224,13 @@ OPENSTACK_KEYSTONE_DEFAULT_ROLE = "{{ keystone_default_user_role }}"
|
|||||||
# Do not remove the mandatory credentials mechanism.
|
# Do not remove the mandatory credentials mechanism.
|
||||||
# Note: The last two tuples are sample mapping keys to a identity provider
|
# Note: The last two tuples are sample mapping keys to a identity provider
|
||||||
# and federation protocol combination (WEBSSO_IDP_MAPPING).
|
# and federation protocol combination (WEBSSO_IDP_MAPPING).
|
||||||
#WEBSSO_CHOICES = (
|
WEBSSO_KEYSTONE_URL = "{{ keystone_public_url }}/v3"
|
||||||
# ("credentials", _("Keystone Credentials")),
|
WEBSSO_CHOICES = (
|
||||||
# ("oidc", _("OpenID Connect")),
|
("credentials", _("Keystone Credentials")),
|
||||||
# ("saml2", _("Security Assertion Markup Language")),
|
{% for idp in keystone_identity_providers %}
|
||||||
# ("acme_oidc", "ACME - OpenID Connect"),
|
("{{ idp.name }}_{{ idp.protocol }}", "{{ idp.public_name }}"),
|
||||||
# ("acme_saml2", "ACME - SAML2"),
|
{% endfor %}
|
||||||
#)
|
)
|
||||||
|
|
||||||
# A dictionary of specific identity provider and federation protocol
|
# A dictionary of specific identity provider and federation protocol
|
||||||
# combinations. From the selected authentication mechanism, the value
|
# combinations. From the selected authentication mechanism, the value
|
||||||
@ -238,10 +239,12 @@ OPENSTACK_KEYSTONE_DEFAULT_ROLE = "{{ keystone_default_user_role }}"
|
|||||||
# specific WebSSO endpoint in keystone, otherwise it will use the value
|
# specific WebSSO endpoint in keystone, otherwise it will use the value
|
||||||
# as the protocol_id when redirecting to the WebSSO by protocol endpoint.
|
# as the protocol_id when redirecting to the WebSSO by protocol endpoint.
|
||||||
# NOTE: The value is expected to be a tuple formatted as: (<idp_id>, <protocol_id>).
|
# NOTE: The value is expected to be a tuple formatted as: (<idp_id>, <protocol_id>).
|
||||||
#WEBSSO_IDP_MAPPING = {
|
WEBSSO_IDP_MAPPING = {
|
||||||
# "acme_oidc": ("acme", "oidc"),
|
{% for idp in keystone_identity_providers %}
|
||||||
# "acme_saml2": ("acme", "saml2"),
|
"{{ idp.name }}_{{ idp.protocol }}": ("{{ idp.name }}", "{{ idp.protocol }}"),
|
||||||
#}
|
{% endfor %}
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
# Disable SSL certificate checks (useful for self-signed certificates):
|
# Disable SSL certificate checks (useful for self-signed certificates):
|
||||||
#OPENSTACK_SSL_NO_VERIFY = True
|
#OPENSTACK_SSL_NO_VERIFY = True
|
||||||
|
@ -18,6 +18,7 @@ keystone_services:
|
|||||||
tls_backend: "{{ keystone_enable_tls_backend }}"
|
tls_backend: "{{ keystone_enable_tls_backend }}"
|
||||||
port: "{{ keystone_public_port }}"
|
port: "{{ keystone_public_port }}"
|
||||||
listen_port: "{{ keystone_public_listen_port }}"
|
listen_port: "{{ keystone_public_listen_port }}"
|
||||||
|
backend_http_extra: "{{ ['balance source'] if enable_keystone_federation | bool else [] }}"
|
||||||
keystone_external:
|
keystone_external:
|
||||||
enabled: "{{ enable_keystone }}"
|
enabled: "{{ enable_keystone }}"
|
||||||
mode: "http"
|
mode: "http"
|
||||||
@ -25,6 +26,7 @@ keystone_services:
|
|||||||
tls_backend: "{{ keystone_enable_tls_backend }}"
|
tls_backend: "{{ keystone_enable_tls_backend }}"
|
||||||
port: "{{ keystone_public_port }}"
|
port: "{{ keystone_public_port }}"
|
||||||
listen_port: "{{ keystone_public_listen_port }}"
|
listen_port: "{{ keystone_public_listen_port }}"
|
||||||
|
backend_http_extra: "{{ ['balance source'] if enable_keystone_federation | bool else [] }}"
|
||||||
keystone_admin:
|
keystone_admin:
|
||||||
enabled: "{{ enable_keystone }}"
|
enabled: "{{ enable_keystone }}"
|
||||||
mode: "http"
|
mode: "http"
|
||||||
@ -179,3 +181,23 @@ keystone_ks_services:
|
|||||||
# TLS
|
# TLS
|
||||||
####################
|
####################
|
||||||
keystone_enable_tls_backend: "{{ kolla_enable_tls_backend }}"
|
keystone_enable_tls_backend: "{{ kolla_enable_tls_backend }}"
|
||||||
|
|
||||||
|
###############################
|
||||||
|
# OpenStack identity federation
|
||||||
|
###############################
|
||||||
|
# Default OpenID Connect remote attribute key
|
||||||
|
keystone_remote_id_attribute_oidc: "HTTP_OIDC_ISS"
|
||||||
|
keystone_container_federation_oidc_metadata_folder: "{{ '/etc/apache2/metadata' if kolla_base_distro in ['debian', 'ubuntu'] else '/etc/httpd/metadata' }}"
|
||||||
|
keystone_container_federation_oidc_idp_certificate_folder: "{{ '/etc/apache2/cert' if kolla_base_distro in ['debian', 'ubuntu'] else '/etc/httpd/cert' }}"
|
||||||
|
keystone_container_federation_oidc_attribute_mappings_folder: "{{ container_config_directory }}/federation/oidc/attribute_maps"
|
||||||
|
keystone_host_federation_oidc_metadata_folder: "{{ node_config_directory }}/keystone/federation/oidc/metadata"
|
||||||
|
keystone_host_federation_oidc_idp_certificate_folder: "{{ node_config_directory }}/keystone/federation/oidc/cert"
|
||||||
|
keystone_host_federation_oidc_attribute_mappings_folder: "{{ node_config_directory }}/keystone/federation/oidc/attribute_maps"
|
||||||
|
|
||||||
|
# These variables are used to define multiple trusted Horizon dashboards.
|
||||||
|
# keystone_trusted_dashboards: ['<https://dashboardServerOne/auth/websso/>', '<https://dashboardServerTwo/auth/websso/>', '<https://dashboardServerN/auth/websso/>']
|
||||||
|
keystone_trusted_dashboards: "{{ ['%s://%s/auth/websso/' % (public_protocol, kolla_external_fqdn), '%s/auth/websso/' % (horizon_public_endpoint)] if enable_horizon | bool else [] }}"
|
||||||
|
keystone_enable_federation_openid: "{{ enable_keystone_federation | bool and keystone_identity_providers | selectattr('protocol','equalto','openid') | list | count > 0 }}"
|
||||||
|
keystone_should_remove_attribute_mappings: False
|
||||||
|
keystone_should_remove_identity_providers: False
|
||||||
|
keystone_federation_oidc_scopes: "openid email profile"
|
||||||
|
86
ansible/roles/keystone/tasks/config-federation-oidc.yml
Normal file
86
ansible/roles/keystone/tasks/config-federation-oidc.yml
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
---
|
||||||
|
- name: Remove OpenID certificate and metadata files
|
||||||
|
become: true
|
||||||
|
vars:
|
||||||
|
keystone: "{{ keystone_services['keystone'] }}"
|
||||||
|
file:
|
||||||
|
state: absent
|
||||||
|
path: "{{ item }}"
|
||||||
|
when:
|
||||||
|
- inventory_hostname in groups[keystone.group]
|
||||||
|
with_items:
|
||||||
|
- "{{ keystone_host_federation_oidc_metadata_folder }}"
|
||||||
|
- "{{ keystone_host_federation_oidc_idp_certificate_folder }}"
|
||||||
|
- "{{ keystone_host_federation_oidc_attribute_mappings_folder }}"
|
||||||
|
|
||||||
|
- name: Create OpenID configuration directories
|
||||||
|
vars:
|
||||||
|
keystone: "{{ keystone_services['keystone'] }}"
|
||||||
|
file:
|
||||||
|
dest: "{{ item }}"
|
||||||
|
state: "directory"
|
||||||
|
mode: "0770"
|
||||||
|
become: true
|
||||||
|
with_items:
|
||||||
|
- "{{ keystone_host_federation_oidc_metadata_folder }}"
|
||||||
|
- "{{ keystone_host_federation_oidc_idp_certificate_folder }}"
|
||||||
|
- "{{ keystone_host_federation_oidc_attribute_mappings_folder }}"
|
||||||
|
when:
|
||||||
|
- inventory_hostname in groups[keystone.group]
|
||||||
|
|
||||||
|
- name: Copying OpenID Identity Providers metadata
|
||||||
|
vars:
|
||||||
|
keystone: "{{ keystone_services['keystone'] }}"
|
||||||
|
become: true
|
||||||
|
copy:
|
||||||
|
src: "{{ item.metadata_folder }}/"
|
||||||
|
dest: "{{ keystone_host_federation_oidc_metadata_folder }}"
|
||||||
|
mode: "0660"
|
||||||
|
with_items: "{{ keystone_identity_providers }}"
|
||||||
|
when:
|
||||||
|
- item.protocol == 'openid'
|
||||||
|
- inventory_hostname in groups[keystone.group]
|
||||||
|
|
||||||
|
- name: Copying OpenID Identity Providers certificate
|
||||||
|
vars:
|
||||||
|
keystone: "{{ keystone_services['keystone'] }}"
|
||||||
|
become: true
|
||||||
|
copy:
|
||||||
|
src: "{{ item.certificate_file }}"
|
||||||
|
dest: "{{ keystone_host_federation_oidc_idp_certificate_folder }}"
|
||||||
|
mode: "0660"
|
||||||
|
with_items: "{{ keystone_identity_providers }}"
|
||||||
|
when:
|
||||||
|
- item.protocol == 'openid'
|
||||||
|
- inventory_hostname in groups[keystone.group]
|
||||||
|
|
||||||
|
- name: Copying OpenStack Identity Providers attribute mappings
|
||||||
|
vars:
|
||||||
|
keystone: "{{ keystone_services['keystone'] }}"
|
||||||
|
become: true
|
||||||
|
copy:
|
||||||
|
src: "{{ item.file }}"
|
||||||
|
dest: "{{ keystone_host_federation_oidc_attribute_mappings_folder }}/{{ item.file | basename }}"
|
||||||
|
mode: "0660"
|
||||||
|
with_items: "{{ keystone_identity_mappings }}"
|
||||||
|
when:
|
||||||
|
- inventory_hostname in groups[keystone.group]
|
||||||
|
|
||||||
|
- name: Setting the certificates files variable
|
||||||
|
become: true
|
||||||
|
vars:
|
||||||
|
keystone: "{{ keystone_services['keystone'] }}"
|
||||||
|
find:
|
||||||
|
path: "{{ keystone_host_federation_oidc_idp_certificate_folder }}"
|
||||||
|
pattern: "*.pem"
|
||||||
|
register: certificates_path
|
||||||
|
when:
|
||||||
|
- inventory_hostname in groups[keystone.group]
|
||||||
|
|
||||||
|
- name: Setting the certificates variable
|
||||||
|
vars:
|
||||||
|
keystone: "{{ keystone_services['keystone'] }}"
|
||||||
|
set_fact:
|
||||||
|
keystone_federation_openid_certificate_key_ids: "{{ certificates_path.files | map(attribute='path') | map('regex_replace', '^.*/(.*)\\.pem$', '\\1#' + keystone_container_federation_oidc_idp_certificate_folder + '/\\1.pem') | list }}" # noqa 204
|
||||||
|
when:
|
||||||
|
- inventory_hostname in groups[keystone.group]
|
@ -144,6 +144,10 @@
|
|||||||
notify:
|
notify:
|
||||||
- Restart {{ item.key }} container
|
- Restart {{ item.key }} container
|
||||||
|
|
||||||
|
- include_tasks: config-federation-oidc.yml
|
||||||
|
when:
|
||||||
|
- keystone_enable_federation_openid | bool
|
||||||
|
|
||||||
- name: Copying over wsgi-keystone.conf
|
- name: Copying over wsgi-keystone.conf
|
||||||
vars:
|
vars:
|
||||||
keystone: "{{ keystone_services.keystone }}"
|
keystone: "{{ keystone_services.keystone }}"
|
||||||
|
@ -19,3 +19,7 @@
|
|||||||
- import_tasks: register.yml
|
- import_tasks: register.yml
|
||||||
|
|
||||||
- import_tasks: check.yml
|
- import_tasks: check.yml
|
||||||
|
|
||||||
|
- include_tasks: register_identity_providers.yml
|
||||||
|
when:
|
||||||
|
- enable_keystone_federation | bool
|
||||||
|
238
ansible/roles/keystone/tasks/register_identity_providers.yml
Normal file
238
ansible/roles/keystone/tasks/register_identity_providers.yml
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
---
|
||||||
|
- name: List configured attribute mappings (that can be used by IdPs)
|
||||||
|
command: >
|
||||||
|
docker exec -t keystone openstack
|
||||||
|
--os-auth-url={{ openstack_auth.auth_url }}
|
||||||
|
--os-password={{ openstack_auth.password }}
|
||||||
|
--os-username={{ openstack_auth.username }}
|
||||||
|
--os-project-name={{ openstack_auth.project_name }}
|
||||||
|
--os-identity-api-version=3
|
||||||
|
--os-interface {{ openstack_interface }}
|
||||||
|
--os-project-domain-name {{ openstack_auth.domain_name }}
|
||||||
|
--os-user-domain-name {{ openstack_auth.domain_name }}
|
||||||
|
--os-region-name {{ openstack_region_name }}
|
||||||
|
{% if openstack_cacert != '' %}--os-cacert {{ openstack_cacert }} {% endif %}
|
||||||
|
mapping list -c ID --format value
|
||||||
|
run_once: True
|
||||||
|
become: True
|
||||||
|
register: existing_mappings_register
|
||||||
|
|
||||||
|
- name: Register existing mappings
|
||||||
|
set_fact:
|
||||||
|
existing_mappings: "{{ existing_mappings_register.stdout_lines | map('trim') | list }}"
|
||||||
|
|
||||||
|
- name: Remove unmanaged attribute mappings
|
||||||
|
command: >
|
||||||
|
docker exec -t keystone openstack
|
||||||
|
--os-auth-url={{ openstack_auth.auth_url }}
|
||||||
|
--os-password={{ openstack_auth.password }}
|
||||||
|
--os-username={{ openstack_auth.username }}
|
||||||
|
--os-project-name={{ openstack_auth.project_name }}
|
||||||
|
--os-identity-api-version=3
|
||||||
|
--os-interface {{ openstack_interface }}
|
||||||
|
--os-project-domain-name {{ openstack_auth.domain_name }}
|
||||||
|
--os-user-domain-name {{ openstack_auth.domain_name }}
|
||||||
|
--os-region-name {{ openstack_region_name }}
|
||||||
|
{% if openstack_cacert != '' %}--os-cacert {{ openstack_cacert }} {% endif %}
|
||||||
|
mapping delete {{ item }}
|
||||||
|
run_once: True
|
||||||
|
become: true
|
||||||
|
with_items: "{{ existing_mappings }}"
|
||||||
|
when:
|
||||||
|
- item not in (keystone_identity_mappings | map(attribute='name') | list)
|
||||||
|
- keystone_should_remove_attribute_mappings
|
||||||
|
|
||||||
|
- name: Create unexisting domains
|
||||||
|
become: true
|
||||||
|
kolla_toolbox:
|
||||||
|
module_name: "os_keystone_domain"
|
||||||
|
module_args:
|
||||||
|
name: "{{ item.openstack_domain }}"
|
||||||
|
auth: "{{ openstack_auth }}"
|
||||||
|
endpoint_type: "{{ openstack_interface }}"
|
||||||
|
cacert: "{{ openstack_cacert }}"
|
||||||
|
region_name: "{{ openstack_region_name }}"
|
||||||
|
run_once: True
|
||||||
|
with_items: "{{ keystone_identity_providers }}"
|
||||||
|
|
||||||
|
- name: Register attribute mappings in OpenStack
|
||||||
|
become: true
|
||||||
|
command: >
|
||||||
|
docker exec -t keystone openstack
|
||||||
|
--os-auth-url={{ openstack_auth.auth_url }}
|
||||||
|
--os-password={{ openstack_auth.password }}
|
||||||
|
--os-username={{ openstack_auth.username }}
|
||||||
|
--os-project-name={{ openstack_auth.project_name }}
|
||||||
|
--os-identity-api-version=3
|
||||||
|
--os-interface {{ openstack_interface }}
|
||||||
|
--os-project-domain-name {{ openstack_auth.domain_name }}
|
||||||
|
--os-user-domain-name {{ openstack_auth.domain_name }}
|
||||||
|
--os-region-name {{ openstack_region_name }}
|
||||||
|
{% if openstack_cacert != '' %}--os-cacert {{ openstack_cacert }} {% endif %}
|
||||||
|
mapping create
|
||||||
|
--rules "{{ keystone_container_federation_oidc_attribute_mappings_folder }}/{{ item.file | basename }}"
|
||||||
|
{{ item.name }}
|
||||||
|
run_once: True
|
||||||
|
when:
|
||||||
|
- item.name not in existing_mappings
|
||||||
|
with_items: "{{ keystone_identity_mappings }}"
|
||||||
|
|
||||||
|
- name: Update existing attribute mappings in OpenStack
|
||||||
|
become: true
|
||||||
|
command: >
|
||||||
|
docker exec -t keystone openstack
|
||||||
|
--os-auth-url={{ openstack_auth.auth_url }}
|
||||||
|
--os-password={{ openstack_auth.password }}
|
||||||
|
--os-username={{ openstack_auth.username }}
|
||||||
|
--os-project-name={{ openstack_auth.project_name }}
|
||||||
|
--os-identity-api-version=3
|
||||||
|
--os-interface {{ openstack_interface }}
|
||||||
|
--os-project-domain-name {{ openstack_auth.domain_name }}
|
||||||
|
--os-user-domain-name {{ openstack_auth.domain_name }}
|
||||||
|
--os-region-name {{ openstack_region_name }}
|
||||||
|
{% if openstack_cacert != '' %}--os-cacert {{ openstack_cacert }} {% endif %}
|
||||||
|
mapping set
|
||||||
|
--rules "{{ keystone_container_federation_oidc_attribute_mappings_folder }}/{{ item.file | basename }}"
|
||||||
|
{{ item.name }}
|
||||||
|
run_once: True
|
||||||
|
when:
|
||||||
|
- item.name in existing_mappings
|
||||||
|
with_items: "{{ keystone_identity_mappings }}"
|
||||||
|
|
||||||
|
- name: List configured IdPs
|
||||||
|
become: true
|
||||||
|
command: >
|
||||||
|
docker exec -t keystone openstack
|
||||||
|
--os-auth-url={{ openstack_auth.auth_url }}
|
||||||
|
--os-password={{ openstack_auth.password }}
|
||||||
|
--os-username={{ openstack_auth.username }}
|
||||||
|
--os-project-name={{ openstack_auth.project_name }}
|
||||||
|
--os-identity-api-version=3
|
||||||
|
--os-interface {{ openstack_interface }}
|
||||||
|
--os-project-domain-name {{ openstack_auth.domain_name }}
|
||||||
|
--os-user-domain-name {{ openstack_auth.domain_name }}
|
||||||
|
--os-region-name {{ openstack_region_name }}
|
||||||
|
{% if openstack_cacert != '' %}--os-cacert {{ openstack_cacert }} {% endif %}
|
||||||
|
identity provider list -c ID --format value
|
||||||
|
run_once: True
|
||||||
|
register: existing_idps_register
|
||||||
|
|
||||||
|
- name: Register existing idps
|
||||||
|
set_fact:
|
||||||
|
existing_idps: "{{ existing_idps_register.stdout.split('\n') | map('trim') | list }}"
|
||||||
|
|
||||||
|
- name: Remove unmanaged identity providers
|
||||||
|
become: true
|
||||||
|
command: >
|
||||||
|
docker exec -t keystone openstack
|
||||||
|
--os-auth-url={{ openstack_auth.auth_url }}
|
||||||
|
--os-password={{ openstack_auth.password }}
|
||||||
|
--os-username={{ openstack_auth.username }}
|
||||||
|
--os-project-name={{ openstack_auth.project_name }}
|
||||||
|
--os-identity-api-version=3
|
||||||
|
--os-interface {{ openstack_interface }}
|
||||||
|
--os-project-domain-name {{ openstack_auth.domain_name }}
|
||||||
|
--os-user-domain-name {{ openstack_auth.domain_name }}
|
||||||
|
--os-region-name {{ openstack_region_name }}
|
||||||
|
{% if openstack_cacert != '' %}--os-cacert {{ openstack_cacert }} {% endif %}
|
||||||
|
identity provider delete {{ item }}
|
||||||
|
run_once: True
|
||||||
|
with_items: "{{ existing_idps }}"
|
||||||
|
when:
|
||||||
|
- item not in (keystone_identity_providers | map(attribute='name') | list)
|
||||||
|
- keystone_should_remove_identity_providers
|
||||||
|
|
||||||
|
- name: Register Identity Providers in OpenStack
|
||||||
|
become: true
|
||||||
|
command: >
|
||||||
|
docker exec -t keystone openstack
|
||||||
|
--os-auth-url={{ openstack_auth.auth_url }}
|
||||||
|
--os-password={{ openstack_auth.password }}
|
||||||
|
--os-username={{ openstack_auth.username }}
|
||||||
|
--os-project-name={{ openstack_auth.project_name }}
|
||||||
|
--os-identity-api-version=3
|
||||||
|
--os-interface {{ openstack_interface }}
|
||||||
|
--os-project-domain-name {{ openstack_auth.domain_name }}
|
||||||
|
--os-user-domain-name {{ openstack_auth.domain_name }}
|
||||||
|
--os-region-name {{ openstack_region_name }}
|
||||||
|
{% if openstack_cacert != '' %}--os-cacert {{ openstack_cacert }} {% endif %}
|
||||||
|
identity provider create
|
||||||
|
--description "{{ item.public_name }}"
|
||||||
|
--remote-id "{{ item.identifier }}"
|
||||||
|
--domain "{{ item.openstack_domain }}"
|
||||||
|
{{ item.name }}
|
||||||
|
run_once: True
|
||||||
|
when:
|
||||||
|
- item.name not in existing_idps
|
||||||
|
with_items: "{{ keystone_identity_providers }}"
|
||||||
|
|
||||||
|
- name: Update Identity Providers in OpenStack according to Kolla-Ansible configuraitons
|
||||||
|
become: true
|
||||||
|
command: >
|
||||||
|
docker exec -t keystone openstack
|
||||||
|
--os-auth-url={{ openstack_auth.auth_url }}
|
||||||
|
--os-password={{ openstack_auth.password }}
|
||||||
|
--os-username={{ openstack_auth.username }}
|
||||||
|
--os-project-name={{ openstack_auth.project_name }}
|
||||||
|
--os-identity-api-version=3
|
||||||
|
--os-interface {{ openstack_interface }}
|
||||||
|
--os-project-domain-name {{ openstack_auth.domain_name }}
|
||||||
|
--os-user-domain-name {{ openstack_auth.domain_name }}
|
||||||
|
--os-region-name {{ openstack_region_name }}
|
||||||
|
{% if openstack_cacert != '' %}--os-cacert {{ openstack_cacert }} {% endif %}
|
||||||
|
identity provider set
|
||||||
|
--description "{{ item.public_name }}"
|
||||||
|
--remote-id "{{ item.identifier }}"
|
||||||
|
"{{ item.name }}"
|
||||||
|
run_once: True
|
||||||
|
when:
|
||||||
|
- item.name in existing_idps
|
||||||
|
with_items: "{{ keystone_identity_providers }}"
|
||||||
|
|
||||||
|
- name: Configure attribute mappings for each Identity Provider. (We expect the mappings to be configured by the operator)
|
||||||
|
become: true
|
||||||
|
command: >
|
||||||
|
docker exec -t keystone openstack
|
||||||
|
--os-auth-url={{ openstack_auth.auth_url }}
|
||||||
|
--os-password={{ openstack_auth.password }}
|
||||||
|
--os-username={{ openstack_auth.username }}
|
||||||
|
--os-project-name={{ openstack_auth.project_name }}
|
||||||
|
--os-identity-api-version=3
|
||||||
|
--os-interface {{ openstack_interface }}
|
||||||
|
--os-project-domain-name {{ openstack_auth.domain_name }}
|
||||||
|
--os-user-domain-name {{ openstack_auth.domain_name }}
|
||||||
|
--os-region-name {{ openstack_region_name }}
|
||||||
|
{% if openstack_cacert != '' %}--os-cacert {{ openstack_cacert }} {% endif %}
|
||||||
|
federation protocol create
|
||||||
|
--mapping {{ item.attribute_mapping }}
|
||||||
|
--identity-provider {{ item.name }}
|
||||||
|
{{ item.protocol }}
|
||||||
|
run_once: True
|
||||||
|
when:
|
||||||
|
- item.name not in existing_idps
|
||||||
|
with_items: "{{ keystone_identity_providers }}"
|
||||||
|
|
||||||
|
- name: Update attribute mappings for each Identity Provider. (We expect the mappings to be configured by the operator).
|
||||||
|
become: true
|
||||||
|
command: >
|
||||||
|
docker exec -t keystone openstack
|
||||||
|
--os-auth-url={{ openstack_auth.auth_url }}
|
||||||
|
--os-password={{ openstack_auth.password }}
|
||||||
|
--os-username={{ openstack_auth.username }}
|
||||||
|
--os-project-name={{ openstack_auth.project_name }}
|
||||||
|
--os-identity-api-version=3
|
||||||
|
--os-interface {{ openstack_interface }}
|
||||||
|
--os-project-domain-name {{ openstack_auth.domain_name }}
|
||||||
|
--os-user-domain-name {{ openstack_auth.domain_name }}
|
||||||
|
--os-region-name {{ openstack_region_name }}
|
||||||
|
{% if openstack_cacert != '' %}--os-cacert {{ openstack_cacert }} {% endif %}
|
||||||
|
federation protocol set
|
||||||
|
--identity-provider {{ item.name }}
|
||||||
|
--mapping {{ item.attribute_mapping }}
|
||||||
|
{{ item.protocol }}
|
||||||
|
run_once: True
|
||||||
|
register: result
|
||||||
|
failed_when: result.rc not in [0, 1] # This command returns RC 1 on success, so we need to add this to avoid fails.
|
||||||
|
when:
|
||||||
|
- item.name in existing_idps
|
||||||
|
with_items: "{{ keystone_identity_providers }}"
|
@ -77,3 +77,18 @@ connection_string = {{ osprofiler_backend_connection_string }}
|
|||||||
[cors]
|
[cors]
|
||||||
allowed_origin = {{ grafana_public_endpoint }}
|
allowed_origin = {{ grafana_public_endpoint }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{% if enable_keystone_federation %}
|
||||||
|
[federation]
|
||||||
|
{% for dashboard in keystone_trusted_dashboards %}
|
||||||
|
trusted_dashboard = {{ dashboard }}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
sso_callback_template = /etc/keystone/sso_callback_template.html
|
||||||
|
|
||||||
|
[openid]
|
||||||
|
remote_id_attribute = {{ keystone_remote_id_attribute_oidc }}
|
||||||
|
|
||||||
|
[auth]
|
||||||
|
methods = password,token,openid,application_credential
|
||||||
|
{% endif %}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{% set keystone_dir = 'apache2/conf-enabled' if kolla_base_distro in ['ubuntu', 'debian'] else 'httpd/conf.d' %}
|
{% set keystone_dir = 'apache2/conf-enabled' if kolla_base_distro in ['ubuntu', 'debian'] else 'httpd/conf.d' %}
|
||||||
|
{% set apache_user = 'www-data' if kolla_base_distro in ['ubuntu', 'debian'] else 'apache' %}
|
||||||
{
|
{
|
||||||
"command": "/usr/bin/keystone-startup.sh",
|
"command": "/usr/bin/keystone-startup.sh",
|
||||||
"config_files": [
|
"config_files": [
|
||||||
@ -52,6 +53,22 @@
|
|||||||
"owner": "keystone",
|
"owner": "keystone",
|
||||||
"perm": "0600"
|
"perm": "0600"
|
||||||
}{% endif %}
|
}{% endif %}
|
||||||
|
{% if keystone_enable_federation_openid %},
|
||||||
|
{
|
||||||
|
"source": "{{ container_config_directory }}/federation/oidc/metadata",
|
||||||
|
"dest": "{{ keystone_container_federation_oidc_metadata_folder }}",
|
||||||
|
"owner": "{{ apache_user }}:{{ apache_user }}",
|
||||||
|
"perm": "0600",
|
||||||
|
"merge": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "{{ container_config_directory }}/federation/oidc/cert",
|
||||||
|
"dest": "{{ keystone_container_federation_oidc_idp_certificate_folder }}",
|
||||||
|
"owner": "{{ apache_user }}:{{ apache_user }}",
|
||||||
|
"perm": "0600",
|
||||||
|
"merge": true
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
],
|
],
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
@ -61,7 +78,17 @@
|
|||||||
{
|
{
|
||||||
"path": "/var/log/kolla/keystone/keystone.log",
|
"path": "/var/log/kolla/keystone/keystone.log",
|
||||||
"owner": "keystone:keystone"
|
"owner": "keystone:keystone"
|
||||||
|
},{% if keystone_enable_federation_openid %}
|
||||||
|
{
|
||||||
|
"path": "{{ keystone_container_federation_oidc_metadata_folder }}",
|
||||||
|
"owner": "{{ apache_user }}:{{ apache_user }}",
|
||||||
|
"perm": "0700"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "{{ keystone_container_federation_oidc_idp_certificate_folder }}",
|
||||||
|
"owner": "{{ apache_user }}:{{ apache_user }}",
|
||||||
|
"perm": "0700"
|
||||||
|
},{% endif %}
|
||||||
{
|
{
|
||||||
"path": "/etc/keystone/fernet-keys",
|
"path": "/etc/keystone/fernet-keys",
|
||||||
"owner": "keystone:keystone",
|
"owner": "keystone:keystone",
|
||||||
|
@ -51,6 +51,51 @@ LogLevel info
|
|||||||
SSLCertificateFile /etc/keystone/certs/keystone-cert.pem
|
SSLCertificateFile /etc/keystone/certs/keystone-cert.pem
|
||||||
SSLCertificateKeyFile /etc/keystone/certs/keystone-key.pem
|
SSLCertificateKeyFile /etc/keystone/certs/keystone-key.pem
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{% if keystone_enable_federation_openid %}
|
||||||
|
OIDCClaimPrefix "OIDC-"
|
||||||
|
OIDCClaimDelimiter ";"
|
||||||
|
OIDCResponseType "id_token"
|
||||||
|
OIDCScope "{{ keystone_federation_oidc_scopes }}"
|
||||||
|
OIDCMetadataDir {{ keystone_container_federation_oidc_metadata_folder }}
|
||||||
|
{% if keystone_federation_openid_certificate_key_ids | length > 0 %}
|
||||||
|
OIDCOAuthVerifyCertFiles {{ keystone_federation_openid_certificate_key_ids | join(" ") }}
|
||||||
|
{% endif %}
|
||||||
|
OIDCCryptoPassphrase {{ keystone_federation_openid_crypto_password }}
|
||||||
|
OIDCRedirectURI {{ keystone_public_url }}/redirect_uri
|
||||||
|
|
||||||
|
<Location ~ "/redirect_uri">
|
||||||
|
Require valid-user
|
||||||
|
AuthType openid-connect
|
||||||
|
</Location>
|
||||||
|
|
||||||
|
{# WebSSO authentication endpoint -#}
|
||||||
|
<Location /v3/auth/OS-FEDERATION/websso/openid>
|
||||||
|
Require valid-user
|
||||||
|
AuthType openid-connect
|
||||||
|
</Location>
|
||||||
|
|
||||||
|
{% for idp in keystone_identity_providers %}
|
||||||
|
{% if idp.protocol == 'openid' %}
|
||||||
|
<LocationMatch /v3/auth/OS-FEDERATION/identity_providers/{{ idp.name }}/protocols/{{ idp.protocol }}/websso>
|
||||||
|
Require valid-user
|
||||||
|
AuthType openid-connect
|
||||||
|
</LocationMatch>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{# CLI / API authentication endpoint -#}
|
||||||
|
{% for idp in keystone_identity_providers %}
|
||||||
|
{% if idp.protocol == 'openid' %}
|
||||||
|
<LocationMatch /v3/OS-FEDERATION/identity_providers/{{ idp.name }}/protocols/{{ idp.protocol }}/auth>
|
||||||
|
Require valid-user
|
||||||
|
{# Note(jasonanderson): `auth-openidc` is a special auth type that can -#}
|
||||||
|
{# additionally handle verifying bearer tokens -#}
|
||||||
|
AuthType auth-openidc
|
||||||
|
</LocationMatch>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
</VirtualHost>
|
</VirtualHost>
|
||||||
|
|
||||||
<VirtualHost *:{{ keystone_admin_listen_port }}>
|
<VirtualHost *:{{ keystone_admin_listen_port }}>
|
||||||
|
@ -21,3 +21,4 @@ We welcome everyone to join our project!
|
|||||||
bug-triage
|
bug-triage
|
||||||
ptl-guide
|
ptl-guide
|
||||||
release-management
|
release-management
|
||||||
|
setup-identity-provider
|
||||||
|
193
doc/source/contributor/setup-identity-provider.rst
Normal file
193
doc/source/contributor/setup-identity-provider.rst
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
.. _setup-identity-provider:
|
||||||
|
|
||||||
|
============================
|
||||||
|
Test Identity Provider setup
|
||||||
|
============================
|
||||||
|
|
||||||
|
This guide shows how to create an Identity Provider that handles the OpenID
|
||||||
|
Connect protocol to authenticate users when
|
||||||
|
:keystone-doc:`using Federation with OpenStack
|
||||||
|
</admin/federation/configure_federation.html>` (these configurations must not
|
||||||
|
be used in a production environment).
|
||||||
|
|
||||||
|
Keycloak
|
||||||
|
========
|
||||||
|
|
||||||
|
Keycloak is a Java application that implements an Identity Provider handling
|
||||||
|
both OpenID Connect and SAML protocols.
|
||||||
|
|
||||||
|
To setup a Keycloak instance for testing is pretty simple with Docker.
|
||||||
|
|
||||||
|
Creating the Docker Keycloak instance
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Run the docker command:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
docker run -p 8080:8080 -p 8443:8443 -e KEYCLOAK_USER=admin -e KEYCLOAK_PASSWORD=admin quay.io/keycloak/keycloak:latest
|
||||||
|
|
||||||
|
This will create a Keycloak instance that has the admin credentials as
|
||||||
|
admin/admin and is listening on port 8080.
|
||||||
|
|
||||||
|
After creating the instance, you will need to log in to the Keycloak as
|
||||||
|
administrator and setup the first Identity Provider.
|
||||||
|
|
||||||
|
Creating an Identity Provider with Keycloak
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The following guide assumes that the steps are executed from the same machine
|
||||||
|
(localhost), but you can change the hostname if you want to run it from
|
||||||
|
elsewhere.
|
||||||
|
|
||||||
|
In this guide, we will use the 'new_realm' as the realm name in Keycloak, so,
|
||||||
|
if you want to use any other realm name, you must to change 'new_realm' in the
|
||||||
|
URIs used in the guide and replace the 'new_realm' with the realm name that you
|
||||||
|
are using.
|
||||||
|
|
||||||
|
- Access the admin console on http://localhost:8080/auth/ in the Administration Console option.
|
||||||
|
- Authenticate using the credentials defined in the creation step.
|
||||||
|
- Create a new realm in the http://localhost:8080/auth/admin/master/console/#/create/realm page.
|
||||||
|
- After creating a realm, you will need to create a client to be used by Keystone; to do it, just access http://localhost:8080/auth/admin/master/console/#/create/client/new_realm.
|
||||||
|
- To create a client, you will need to set the client_id (just choose anyone),
|
||||||
|
the protocol (must be openid-connect) and the Root Url (you can leave it
|
||||||
|
blank)
|
||||||
|
- After creating the client, you will need to update some client's attributes
|
||||||
|
like:
|
||||||
|
|
||||||
|
- Enable the Implicit flow (this one allows you to use the OpenStack CLI with
|
||||||
|
oidcv3 plugin)
|
||||||
|
- Set Access Type to confidential
|
||||||
|
- Add the Horizon and Keystone URIs to the Valid Redirect URIs. Keystone should be within the '/redirect_uri' path, for example: https://horizon.com/ and https://keystone.com/redirect_uri
|
||||||
|
- Save the changes
|
||||||
|
- Access the client's Mappers tab to add the user's attributes that will be
|
||||||
|
shared with the client (Keystone):
|
||||||
|
|
||||||
|
- In this guide, we will need the following attribute mappers in Keycloak:
|
||||||
|
|
||||||
|
==================================== ==============
|
||||||
|
name/user attribute/token claim name mapper type
|
||||||
|
==================================== ==============
|
||||||
|
openstack-user-domain user attribute
|
||||||
|
openstack-default-project user attribute
|
||||||
|
==================================== ==============
|
||||||
|
|
||||||
|
- After creating the client, you will need to create a user in that realm to
|
||||||
|
log in OpenStack via identity federation
|
||||||
|
- To create a user, access http://localhost:8080/auth/admin/master/console/#/create/user/new_realm and fill the form with the user's data
|
||||||
|
- After creating the user, you can access the tab "Credentials" to set the
|
||||||
|
user's password
|
||||||
|
- Then, in the tab "Attributes", you must set the authorization attributes to
|
||||||
|
be used by Keystone, these attributes are defined in the :ref:`attribute
|
||||||
|
mapping <attribute_mapping>` in Keystone
|
||||||
|
|
||||||
|
After you create the Identity provider, you will need to get some data from the
|
||||||
|
Identity Provider to configure in Kolla-Ansible
|
||||||
|
|
||||||
|
Configuring Kolla Ansible to use the Identity Provider
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
This section is about how one can get the data needed in
|
||||||
|
:ref:`Setup OIDC via Kolla Ansible <setup-oidc-kolla-ansible>`.
|
||||||
|
|
||||||
|
- name: The realm name, in this case it will be "new_realm"
|
||||||
|
- identifier: http://localhost:8080/auth/realms/new_realm/ (again, the "new_realm" is the name of the realm)
|
||||||
|
- certificate_file: This one can be downloaded from http://localhost:8080/auth/admin/master/console/#/realms/new_realm/keys
|
||||||
|
- metadata_folder:
|
||||||
|
|
||||||
|
- localhost%3A8080%2Fauth%2Frealms%2Fnew_realm.client:
|
||||||
|
|
||||||
|
- client_id: Access http://localhost:8080/auth/admin/master/console/#/realms/new_realm/clients , and access the client you created for Keystone, copy the Client ID displayed in the page
|
||||||
|
- client_secret: In the same page you got the client_id, access the tab
|
||||||
|
"Credentials" and copy the secret value
|
||||||
|
- localhost%3A8080%2Fauth%2Frealms%2Fnew_realm.provider: Copy the json from http://localhost:8080/auth/realms/new_realm/.well-known/openid-configuration (the "new_realm" is the realm name)
|
||||||
|
- localhost%3A8080%2Fauth%2Frealms%2Fnew_realm.conf: You can leave this file
|
||||||
|
as an empty json "{}"
|
||||||
|
|
||||||
|
|
||||||
|
After you finished the configuration of the Identity Provider, your main
|
||||||
|
configuration should look something like the following:
|
||||||
|
|
||||||
|
.. code-block::
|
||||||
|
|
||||||
|
keystone_identity_providers:
|
||||||
|
- name: "new_realm"
|
||||||
|
openstack_domain: "new_domain"
|
||||||
|
protocol: "openid"
|
||||||
|
identifier: "http://localhost:8080/auth/realms/new_realm"
|
||||||
|
public_name: "Authenticate via new_realm"
|
||||||
|
attribute_mapping: "attribute_mapping_keycloak_new_realm"
|
||||||
|
metadata_folder: "/root/inDev/meta-idp"
|
||||||
|
certificate_file: "/root/inDev/certs/LRVweuT51StjMdsna59jKfB3xw0r8Iz1d1J1HeAbmlw.pem"
|
||||||
|
keystone_identity_mappings:
|
||||||
|
- name: "attribute_mapping_keycloak_new_realm"
|
||||||
|
file: "/root/inDev/attr_map/attribute_mapping.json"
|
||||||
|
|
||||||
|
Then, after deploying OpenStack, you should be able to log in Horizon
|
||||||
|
using the "Authenticate using" -> "Authenticate via new_realm", and writing
|
||||||
|
"new_realm.com" in the "E-mail or domain name" field. After that, you will be
|
||||||
|
redirected to a new page to choose the Identity Provider in Keystone. Just click in the link
|
||||||
|
"localhost:8080/auth/realms/new_realm"; this will redirect you to Keycloak (idP) where
|
||||||
|
you will need to log in with the user that you created. If the user's
|
||||||
|
attributes in Keycloak are ok, the user will be created in OpenStack and you will
|
||||||
|
be able to log in Horizon.
|
||||||
|
|
||||||
|
.. _attribute_mapping:
|
||||||
|
|
||||||
|
Attribute mapping
|
||||||
|
~~~~~~~~~~~~~~~~~
|
||||||
|
This section shows how to create the attribute mapping to map an Identity
|
||||||
|
Provider user to a Keystone user (ephemeral).
|
||||||
|
|
||||||
|
The 'OIDC-' prefix in the remote types is defined in the 'OIDCClaimPrefix'
|
||||||
|
configuration in the wsgi-keystone.conf file; this prefix must be in the
|
||||||
|
attribute mapping as the mod-oidc-wsgi is adding the prefix in the user's
|
||||||
|
attributes before sending it to Keystone. The attribute 'openstack-user-domain'
|
||||||
|
will define the user's domain in OpenStack and the attribute
|
||||||
|
'openstack-default-project' will define the user's project in the OpenStack
|
||||||
|
(the user will be assigned with the role 'member' in the project)
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"local": [
|
||||||
|
{
|
||||||
|
"user": {
|
||||||
|
"name": "{0}",
|
||||||
|
"email": "{1}",
|
||||||
|
"domain": {
|
||||||
|
"name": "{2}"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domain": {
|
||||||
|
"name": "{2}"
|
||||||
|
},
|
||||||
|
"projects": [
|
||||||
|
{
|
||||||
|
"name": "{3}",
|
||||||
|
"roles": [
|
||||||
|
{
|
||||||
|
"name": "member"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"remote": [
|
||||||
|
{
|
||||||
|
"type": "OIDC-preferred_username"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "OIDC-email"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "OIDC-openstack-user-domain"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "OIDC-openstack-default-project"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
@ -40,3 +40,241 @@ be configured in Keystone as necessary.
|
|||||||
|
|
||||||
Further infomation on Fernet tokens is available in the :keystone-doc:`Keystone
|
Further infomation on Fernet tokens is available in the :keystone-doc:`Keystone
|
||||||
documentation <admin/fernet-token-faq.html>`.
|
documentation <admin/fernet-token-faq.html>`.
|
||||||
|
|
||||||
|
Federated identity
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Keystone allows users to be authenticated via identity federation. This means
|
||||||
|
integrating OpenStack Keystone with an identity provider. The use of identity
|
||||||
|
federation allows users to access OpenStack services without the necessity of
|
||||||
|
an account in the OpenStack environment per se. The authentication is then
|
||||||
|
off-loaded to the identity provider of the federation.
|
||||||
|
|
||||||
|
To enable identity federation, you will need to execute a set of configurations
|
||||||
|
in multiple OpenStack systems. Therefore, it is easier to use Kolla Ansible
|
||||||
|
to execute this process for operators.
|
||||||
|
|
||||||
|
For upstream documentations, please see
|
||||||
|
:keystone-doc:`Configuring Keystone for Federation
|
||||||
|
<admin/federation/configure_federation.html>`
|
||||||
|
|
||||||
|
Supported protocols
|
||||||
|
~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
OpenStack supports both OpenID Connect and SAML protocols for federated
|
||||||
|
identity, but for now, kolla Ansible supports only OpenID Connect.
|
||||||
|
Therefore, if you desire to use SAML in your environment, you will need
|
||||||
|
to set it up manually or extend Kolla Ansible to also support it.
|
||||||
|
|
||||||
|
.. _setup-oidc-kolla-ansible:
|
||||||
|
|
||||||
|
Setting up OpenID Connect via Kolla Ansible
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
First, you will need to register the OpenStack (Keystone) in your Identity
|
||||||
|
provider as a Service Provider.
|
||||||
|
|
||||||
|
After registering Keystone, you will need to add the Identity Provider
|
||||||
|
configurations in your kolla-ansible globals configuration as the example
|
||||||
|
below:
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
keystone_identity_providers:
|
||||||
|
- name: "myidp1"
|
||||||
|
openstack_domain: "my-domain"
|
||||||
|
protocol: "openid"
|
||||||
|
identifier: "https://accounts.google.com"
|
||||||
|
public_name: "Authenticate via myidp1"
|
||||||
|
attribute_mapping: "mappingId1"
|
||||||
|
metadata_folder: "path/to/metadata/folder"
|
||||||
|
certificate_file: "path/to/certificate/file.pem"
|
||||||
|
|
||||||
|
keystone_identity_mappings:
|
||||||
|
- name: "mappingId1"
|
||||||
|
file: "/full/qualified/path/to/mapping/json/file/to/mappingId1"
|
||||||
|
|
||||||
|
Identity providers configurations
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
name
|
||||||
|
****
|
||||||
|
|
||||||
|
The internal name of the Identity provider in OpenStack.
|
||||||
|
|
||||||
|
openstack_domain
|
||||||
|
****************
|
||||||
|
|
||||||
|
The OpenStack domain that the Identity Provider belongs.
|
||||||
|
|
||||||
|
protocol
|
||||||
|
********
|
||||||
|
|
||||||
|
The federated protocol used by the IdP; e.g. openid or saml. We support only
|
||||||
|
OpenID connect right now.
|
||||||
|
|
||||||
|
identifier
|
||||||
|
**********
|
||||||
|
|
||||||
|
The Identity provider URL; e.g. https://accounts.google.com .
|
||||||
|
|
||||||
|
public_name
|
||||||
|
***********
|
||||||
|
|
||||||
|
The Identity provider public name that will be shown for users in the Horizon
|
||||||
|
login page.
|
||||||
|
|
||||||
|
attribute_mapping
|
||||||
|
*****************
|
||||||
|
|
||||||
|
The attribute mapping to be used for the Identity Provider. This mapping is
|
||||||
|
expected to already exist in OpenStack or be configured in the
|
||||||
|
`keystone_identity_mappings` property.
|
||||||
|
|
||||||
|
metadata_folder
|
||||||
|
***************
|
||||||
|
|
||||||
|
Path to the folder containing all of the identity provider metadata as JSON
|
||||||
|
files.
|
||||||
|
|
||||||
|
The metadata folder must have all your Identity Providers configurations,
|
||||||
|
the name of the files will be the name (with path) of the Issuer configuration.
|
||||||
|
Such as:
|
||||||
|
|
||||||
|
.. code-block::
|
||||||
|
|
||||||
|
- <IDP metadata directory>
|
||||||
|
- keycloak.example.org%2Fauth%2Frealms%2Fidp.client
|
||||||
|
|
|
||||||
|
- keycloak.example.org%2Fauth%2Frealms%2Fidp.conf
|
||||||
|
|
|
||||||
|
- keycloak.example.org%2Fauth%2Frealms%2Fidp.provider
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
The name of the file must be URL-encoded if needed. For example, if you have
|
||||||
|
an Issuer with ``/`` in the URL, then you need to escape it to ``%2F`` by
|
||||||
|
applying a URL escape in the file name.
|
||||||
|
|
||||||
|
The content of these files must be a JSON
|
||||||
|
|
||||||
|
``client``:
|
||||||
|
|
||||||
|
The ``.client`` file handles the Service Provider credentials in the Issuer.
|
||||||
|
|
||||||
|
During the first step, when you registered the OpenStack as a
|
||||||
|
Service Provider in the Identity Provider, you submitted a `cliend_id` and
|
||||||
|
generated a `client_secret`, so these are the values you must use in this
|
||||||
|
JSON file.
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
{
|
||||||
|
"client_id":"<openid_client_id>",
|
||||||
|
"client_secret":"<openid_client_secret>"
|
||||||
|
}
|
||||||
|
|
||||||
|
``conf``:
|
||||||
|
|
||||||
|
This file will be a JSON that overrides some of the OpenID Connect options. The
|
||||||
|
options that can be overridden are listed in the
|
||||||
|
`OpenID Connect Apache2 plugin documentation`_.
|
||||||
|
.. _`OpenID Connect Apache2 plugin documentation`: https://github.com/zmartzone/mod_auth_openidc/wiki/Multiple-Providers#opclient-configuration
|
||||||
|
|
||||||
|
If you do not want to override the config values, you can leave this file as
|
||||||
|
an empty JSON file such as ``{}``.
|
||||||
|
|
||||||
|
``provider``:
|
||||||
|
|
||||||
|
This file will contain all specifications about the IdentityProvider. To
|
||||||
|
simplify, you can just use the JSON returned in the ``.well-known``
|
||||||
|
Identity provider's endpoint:
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
{
|
||||||
|
"issuer": "https://accounts.google.com",
|
||||||
|
"authorization_endpoint": "https://accounts.google.com/o/oauth2/v2/auth",
|
||||||
|
"token_endpoint": "https://oauth2.googleapis.com/token",
|
||||||
|
"userinfo_endpoint": "https://openidconnect.googleapis.com/v1/userinfo",
|
||||||
|
"revocation_endpoint": "https://oauth2.googleapis.com/revoke",
|
||||||
|
"jwks_uri": "https://www.googleapis.com/oauth2/v3/certs",
|
||||||
|
"response_types_supported": [
|
||||||
|
"code",
|
||||||
|
"token",
|
||||||
|
"id_token",
|
||||||
|
"code token",
|
||||||
|
"code id_token",
|
||||||
|
"token id_token",
|
||||||
|
"code token id_token",
|
||||||
|
"none"
|
||||||
|
],
|
||||||
|
"subject_types_supported": [
|
||||||
|
"public"
|
||||||
|
],
|
||||||
|
"id_token_signing_alg_values_supported": [
|
||||||
|
"RS256"
|
||||||
|
],
|
||||||
|
"scopes_supported": [
|
||||||
|
"openid",
|
||||||
|
"email",
|
||||||
|
"profile"
|
||||||
|
],
|
||||||
|
"token_endpoint_auth_methods_supported": [
|
||||||
|
"client_secret_post",
|
||||||
|
"client_secret_basic"
|
||||||
|
],
|
||||||
|
"claims_supported": [
|
||||||
|
"aud",
|
||||||
|
"email",
|
||||||
|
"email_verified",
|
||||||
|
"exp",
|
||||||
|
"family_name",
|
||||||
|
"given_name",
|
||||||
|
"iat",
|
||||||
|
"iss",
|
||||||
|
"locale",
|
||||||
|
"name",
|
||||||
|
"picture",
|
||||||
|
"sub"
|
||||||
|
],
|
||||||
|
"code_challenge_methods_supported": [
|
||||||
|
"plain",
|
||||||
|
"S256"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
certificate_file
|
||||||
|
****************
|
||||||
|
|
||||||
|
Path to the Identity Provider certificate file, the file must be named as
|
||||||
|
'certificate-key-id.pem'. E.g.
|
||||||
|
|
||||||
|
.. code-block::
|
||||||
|
|
||||||
|
- fb8ca5b7d8d9a5c6c6788071e866c6c40f3fc1f9.pem
|
||||||
|
|
||||||
|
You can find the key-id in the Identity provider
|
||||||
|
`.well-known/openid-configuration` `jwks_uri` like in
|
||||||
|
`https://www.googleapis.com/oauth2/v3/certs` :
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
{
|
||||||
|
"keys": [
|
||||||
|
{
|
||||||
|
"e": "AQAB",
|
||||||
|
"use": "sig",
|
||||||
|
"n": "zK8PHf_6V3G5rU-viUOL1HvAYn7q--dxMoU...",
|
||||||
|
"kty": "RSA",
|
||||||
|
"kid": "fb8ca5b7d8d9a5c6c6788071e866c6c40f3fc1f9",
|
||||||
|
"alg": "RS256"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
The public key is different from the certificate, the file in this
|
||||||
|
configuration must be the Identity provider's certificate and not the
|
||||||
|
Identity provider's public key.
|
||||||
|
@ -252,3 +252,8 @@ redis_master_password:
|
|||||||
####################
|
####################
|
||||||
prometheus_mysql_exporter_database_password:
|
prometheus_mysql_exporter_database_password:
|
||||||
prometheus_alertmanager_password:
|
prometheus_alertmanager_password:
|
||||||
|
|
||||||
|
###############################
|
||||||
|
# OpenStack identity federation
|
||||||
|
###############################
|
||||||
|
keystone_federation_openid_crypto_password:
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Add support for the OpenID Connect authentication protocol in Keystone and
|
||||||
|
enables both ID and access token authentication flows.
|
Loading…
Reference in New Issue
Block a user