7 Configuring keystone and horizon to use X.509 Client Certificates #
The keystone service supports X.509 SSL cerificate authentication and authorization for accessing the horizon dashboard in SUSE OpenStack Cloud. This feature is disabled by default, and must be manually configured and enabled by running a number of Ansible playbooks.
Enabling client SSL certificate authentication and authorization for the horizon dashboard is a non-core feature in SUSE OpenStack Cloud.
7.1 Keystone Configuration #
To configure and enable X.509 SSL authentication and authorization support for the keystone service, perform the following steps.
Create a new configuration file named
x509auth.yml
and place it in any directory in your deployer node. For example, perform the following command to create the file in the/tmp
directory:touch /tmp/x509auth.yml
Edit the new file to include the following text. Note that YAML files are whitespace-sensitive. Preserve the indentation format of the following text.
keystone_x509auth_conf: identity_provider: id: intermediateca description: This is the trusted issuer HEX Id. mapping: id: x509_mapping1 rules_file: /tmp/x509auth_mapping.json protocol: id: x509 remote_id: intermediateca ca_file: /tmp/cacert.pem
The preceding example sets a number of configuration parameters for the X.509/keystone configuration. The following are detailed descriptions of each parameter.
identity_provider This section identifies and describes an outside identity provider.
id: Any unique, readable string that identifies the identitiy provider.
description: A description of the identity provider.
mapping: This section describes a JSON-format file that maps X.509 client certificate attributes to a local keystone user.
id: Any unique, readable string that identifies the user-certificate mapping.
rules_file: The filepath to a JSON file that contains the client certificate attributes mapping.
protocol: This section sets the cryptographic protocol to be used.
id: The cryptographic protocol used for the certificate-based authentication/authorization.
remote_id: By default, this field expects the client certificate's issuer's common name (CN) as a value. The expected value is set in the
keystone.conf
file, where the default setting is:remote_id_attribute = SSL_CLIENT_I_DN_CN
ca_file: The file that contains the client certificate's related intermediary and root CA certificates.
Note: In the
/tmp/x509auth.yml
file, theca_file
value should be a file that contains both the root and signing CA certificates (often found in/home/pki/cacert.pem
).Create a JSON-formatted mapping file. To do so, edit the
x509auth.yml
file you created in Step 2 to reference this file in the mapping→ rules_file parameter. You can create the file with the following example command:touch /tmp/x509auth_mapping.json
Edit the JSON file you created in Step 3 to include the following content:
[ { "local": [ { "user": { "name": "{0}", "domain": { "name": "{1}" }, "type": "local" } } ], "remote": [ { "type": "SSL_CLIENT_S_DN_CN" }, { "type": "SSL_CLIENT_S_DN_O" }, { "type": "SSL_CLIENT_I_DN", "any_one_of": [ ] } ] } ]
Enter the distinguished name(s) (DN) of the certificate issuer(s) that issued your client certificates into the any_one_of field in the remote block. The any_one_of field is a comma-separated list of all certificate issuers that you want the keystone service to trust.
All DNs in the any_one_of field must adhere to the following format: A descending list of DN elements, with each element separated by a forward slash (
/
).The following is an example of a properly formatted DN for a certificate issuer named
intermedia
./C=US/ST=California/O=EXAMPLE/OU=Engineering/CN=intermediateca/emailAddress=user@example.com
The following example file illustrates an
x509auth_mapping.json
file with theintermedia
certificate issuer added to the any_one_of field. Note that the DN string is in quotes.[ { "local": [ { "user": { "name": "{0}", "domain": { "name": "{1}" }, "type": "local" } } ], "remote": [ { "type": "SSL_CLIENT_S_DN_CN" }, { "type": "SSL_CLIENT_S_DN_O" }, { "type": "SSL_CLIENT_I_DN", "any_one_of": [ "/C=US/ST=California/O=EXAMPLE/OU=Engineering/CN=intermediateca/emailAddress=user@example.com" ] } ] } ]
The keystone service will trust all client certificates issued by any of the certificate issuers listed in the any_one_of field.
Run the following commands to enable the new X.509/keystone settings.
cd ~/scratch/ansible/next/ardana/ansible ansible-playbook -i hosts/verb_hosts keystone-reconfigure.yml -e@/tmp/x509auth.yml
7.2 HAProxy Configuration #
Because of the experimental nature of the HAProxy feature, it is important to minimize the risk of impacting other services. If you have implemented, or wish to implement the HAProxy feature alongside client SSL certificate login to the horizon dashboard in your cloud, please complete the following steps to make the necessary manual configuration changes.
You must perform the keystone configuration steps in the previous section before performing the following HAProxy configuration changes.
Locate and open the
~/openstack/ardana/ansible/roles/haproxy/templates/haproxy.cfg
file.Locate the following line in the
haproxy.cfg
file.listen {{ network.vip }}-{{ port }}
Enter the following codeblock in the open space immediately preceding the
listen {{ network.vip }}-{{ port }}
line.{%- if service == 'KEY_API' and port == '5000' %} {% set bind_defaults = 'ca-file /etc/ssl/private/cacert.pem verify optional' %} {%- endif %}
After entering the preceding code, your
haproxy.cfg
file should look like the following example.{%- if network.terminate_tls is defined and network.terminate_tls and port == '80' %} {% set port = '443' %} {%- endif %} {%- if service == 'KEY_API' and port == '5000' %} {% set bind_defaults = 'ca-file /etc/ssl/private/cacert.pem verify optional' %} {%- endif %} listen {{ network.vip }}-{{ port }} {%- set options = network.vip_options | default(vip_options_defaults) %} {%- if options > 0 %} {%- for option in options %} {{ option }} {%- endfor %} {%- endif %} bind {{ network.vip }}:{{ port }} {% if network.terminate_tls is defined and network.terminate_tls %} ssl crt {{ frontend_server_cert_directory }}/{{ network.cert_file }} {{ bind_defaults }} {% endif %}
Commit the changes to your local git repository.
git add -A git commit -m "Added HAProxy configuration changes"
Run the configuration processor and ready-deployment Ansible playbooks.
cd ~/openstack/ardana/ansible ansible-playbook -i hosts/localhost config-processor-run.yml ansible-playbook -i hosts/localhost ready-deployment.yml
Implement the HAProxy configuration changes.
cd ~/scratch/ansible/next/ardana/ansible ansible-playbook -i hosts/verb_hosts FND-CLU-reconfigure.yml
7.3 Create CA and client certificates #
An X.509 client certificate can be issued from any certificate authority (CA). You can use the openssl command-line tool to generate certificate signing requests (CSRs). Once a CA has signed your CSR, the CA will return a signed certificate that you can use to authenticate to horizon.
Read more about openssl here: https://www.openssl.org/
Your cloud's load balancer will reject any self-signed client SSL certificates. Ensure that all client certificates are signed by a certificate authority that your cloud recognizes.
7.4 Horizon Configuration #
Complete the following steps to configure horizon to support SSL certificate authorization and authentication.
Edit the
~/openstack/ardana/ansible/roles/HZN-WEB/defaults/main.yml
file and set the following parameter toTrue
.horizon_websso_enabled: True
Locate the last line in the
~/openstack/ardana/ansible/roles/HZN-WEB/defaults/main.yml
file. The default configuration for this line should look like the following.horizon_websso_choices: - {protocol: saml2, description: "ADFS Credentials"}
If your cloud does not have AD FS enabled, then replace the preceding
horizon_websso_choices:
parameter with the following.- {protocol: x509, description: "X.509 SSL Certificate"}
The resulting block should look like the following.
horizon_websso_choices: - {protocol: x509, description: "X.509 SSL Certificate"}
If your cloud does have AD FS enabled, then simply add the following parameter to the
horizon_websso_choices:
section. Do not replace the default parameter, add the following line to the existing block.- {protocol: saml2, description: "ADFS Credentials"}
If your cloud has AD FS enabled, the final block of your
~/openstack/ardana/ansible/roles/HZN-WEB/defaults/main.yml
should have the following entries.horizon_websso_choices: - {protocol: x509, description: "X.509 SSL Certificate"} - {protocol: saml2, description: "ADFS Credentials"}
Run the following commands to add your changes to the local git repository, and reconfigure the horizon service, enabling the changes made in Step 1:
cd ~/openstack git add -A git commit -m "my commit message" cd ~/openstack/ardana/ansible/ ansible-playbook -i hosts/localhost config-processor-run.yml ansible-playbook -i hosts/localhost ready-deployment.yml cd ~/scratch/ansible/next/ardana/ansible ansible-playbook -i hosts/verb_hosts horizon-reconfigure.yml
7.5 Browser configuration #
To enable your web browser to present a certificate to the horizon dashboard upon login, you first need to import the certificate. The steps to complete this action will vary from browser to browser. Please refer to your browser's documentation for specific instructions.
Import the desired certificate into your web browser's certificate store.
After importing the certificate, verify that it appears in your browser's certificate manager.
7.6 User accounts #
For the keystone service to use X.509 certificates to grant users access to horizon, there must be a keystone user account associated with each certificate. keystone associates user accounts with certificates by matching the common name (CN) and organization (O) of a presented certificate with the username and domain of an existing keystone user.
When an X.509 certificate is presented to horizon for authentication/authorization, horizon passes the certificate information along to the keystone service. keystone attempts to match the CN and O of the certificate with the username and domain of an existing local user account. For this operation to be successful, there must be a keystone user account and domain that match the CN and O of the certificate.
For example, if a user named Sam presents a certificate to horizon with the following information,
CN=sam
O=EXAMPLE
Then there must be an existing keystone user account with the following values,
Username=sam
Domain=EXAMPLE
Further, Sam's client certificate must have been issued by one of the
certificate issuers listed in the
any_one_of field in the
x509auth_mapping.json
file.
Also, when creating a local keystone user, you must assign the user account a project scope. Without a project scope, the authorization portion of the sign-on process will fail.
The following steps illustrate how to use the CLI to create a domain, create and manage a user, and assign a permissions role to the new user.
Create a new domain, named
EXAMPLE
.openstack domain create EXAMPLE
Create a new project named
xyz
, under theEXAMPLE
domain.openstack project create --domain EXAMPLE xyz
Create a new user named
Sam
in theEXAMPLE
domain. Set the password and email for the new account.openstack user create --domain EXAMPLE --password pass \ --email sam@example.com --enable sam
Create a new role named
role1
.openstack role create role1
Grant the new role,
role1
to the new userSam
from theEXAMPLE
domain. Note that both the user account and domain must be referenced by their unique ID numbers rather than their friendly names.openstack role add --user 04f3db9e7f3f45dc82e1d5f20b4acfcc \ --domain 6b64021839774991b5e0df16077f11eb role1
Add the user
Sam
to the newly-created project from Step 2. Note that the project and user account must be referenced by their respective unique ID numbers rather than their friendly names.openstack role add --project 4e2ad14406b247c7a9fc0a48c0b1713e \ --user 04f3db9e7f3f45dc82e1d5f20b4acfcc role1
7.7 How it works #
The SSL authentication and authorization process is detailed in the following steps.
User directs a web browser to the SUSE OpenStack Cloud horizon login landing page.
The user selects the "X.509 Certificate" login option from the dropdown menu.
horizon responds with an HTTP 302 redirect, redirecting the browser to the SSL-protected keystone (federated) authentication endpoint.
The browser then prompts user to select the certificate to use for the login (if there is more than one certificate for the given trusted Certificate Authority (CA)).
The web browser establishes a 2-way SSL handshake with the keystone service.
keystone, utilizing federation mapping, maps the user to a federated persona and issues an (federated) unscoped token.
The token is then passed to the browser, along with JavaScript code that redirects the browser back to the horizon dashboard.
The browser then logs into the horizon dashboard using the newly issued unscoped token to authenticate the user.
horizon queries the keystone service for the list of federated projects the authenticated user has access to.
horizon then rescopes the token to the first project, granting the user authorization.
The login process is completed.