Jump to contentJump to page navigation: previous page [access key p]/next page [access key n]
documentation.suse.com / Documentation / Security Guide / Configuring keystone and horizon to use X.509 Client Certificates
Applies to SUSE OpenStack Cloud 9

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.

Note
Note

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.

  1. 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
  2. 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, the ca_file value should be a file that contains both the root and signing CA certificates (often found in /home/pki/cacert.pem).

  3. 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 mappingrules_file parameter. You can create the file with the following example command:

    touch /tmp/x509auth_mapping.json
  4. 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": [
                                ]
                            }
                        ]
                    }
    ]
  5. 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 the intermedia 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.

  6. 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.

Note
Note

You must perform the keystone configuration steps in the previous section before performing the following HAProxy configuration changes.

  1. Locate and open the ~/openstack/ardana/ansible/roles/haproxy/templates/haproxy.cfg file.

  2. 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 %}
  3. Commit the changes to your local git repository.

    git add -A
    git commit -m "Added HAProxy configuration changes"
  4. 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
  5. 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/

Note
Note

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.

  1. Edit the ~/openstack/ardana/ansible/roles/HZN-WEB/defaults/main.yml file and set the following parameter to True.

    horizon_websso_enabled: True
  2. 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"}
  3. 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.

  1. Import the desired certificate into your web browser's certificate store.

  2. 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.

  1. Create a new domain, named EXAMPLE.

    openstack domain create EXAMPLE
  2. Create a new project named xyz, under the EXAMPLE domain.

    openstack project create --domain EXAMPLE xyz
  3. Create a new user named Sam in the EXAMPLE domain. Set the password and email for the new account.

    openstack user create --domain EXAMPLE --password pass \
      --email sam@example.com --enable sam
  4. Create a new role named role1.

    openstack role create role1
  5. Grant the new role, role1 to the new user Sam from the EXAMPLE 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
  6. 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.

  1. User directs a web browser to the SUSE OpenStack Cloud horizon login landing page.

  2. The user selects the "X.509 Certificate" login option from the dropdown menu.

  3. horizon responds with an HTTP 302 redirect, redirecting the browser to the SSL-protected keystone (federated) authentication endpoint.

  4. 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)).

  5. The web browser establishes a 2-way SSL handshake with the keystone service.

  6. keystone, utilizing federation mapping, maps the user to a federated persona and issues an (federated) unscoped token.

  7. The token is then passed to the browser, along with JavaScript code that redirects the browser back to the horizon dashboard.

  8. The browser then logs into the horizon dashboard using the newly issued unscoped token to authenticate the user.

  9. horizon queries the keystone service for the list of federated projects the authenticated user has access to.

  10. horizon then rescopes the token to the first project, granting the user authorization.

  11. The login process is completed.

Image