Keystone with OpenID Connect

De TeriaHowto
Sauter à la navigation Sauter à la recherche

Configure an external Keycloak to provide authentication for Keystone
The objective is to identify a user and allow them to find their projects in Openstack.

Keycloak client and user configuration

This guide assumes you already have a working Keycloak server with a realm named cloud.ld.
What is covered here is how to :

  • create a client in Keycloak for Keystone
  • configure this client
  • creation of groups (one Keycloak group per Openstack project)
  • create a user

Client creation and settings

  • Got to your Keycloak admin console and configure a new client (click Create)
Keycloak admin console
  • Let's assume your client is named Keystone
    • Leave Root URL empty
    • Select openid-connect for Client Protocol
    • Select confidential for Access Type
    • Standard Flow Enabled, Implicit Flow Enabled and Direct Access Grants Enabled must be on
    • The *fqdn* for Valid Redirect URIs should match the value of kolla_external_fqdn. If your kolla_internal_fqdn is par1.cloud.ld, then your Valid Redirect URIs should be https://par1.cloud.ld:5000/redirect_uri. You can also add the URI of your Horizon dashboard
Keycloak client settings
  • Don't forget to copy the client's key (via Credentials tab)

Client mappers

Add the following mappers to the new client :

  • groups via Add Builtin button and type Group Membership
  • email via Add Builtin button and type User Property
  • username via Add Builtin button and type User Property
Keycloak client mappers

Creation of groups

One Keycloak group is created per Openstack project (Manage -> Groups).

Creation of user

Create one user, fill its username, email and affect one (or more) group(s) to this user (Manage -> Users).

Kolla-ansible configuration and deploy

Configuration

keystone_identity_providers:
  - name: "cloud.ld"
    openstack_domain: "cloud.ld"
    protocol: "openid"
    identifier: "https://sso.paris.ld/auth/realms/cloud.ld"
    public_name: "Authenticate via cloud.ld"
    attribute_mapping: "attribute_mapping_keycloak_cloud_ld"
    metadata_folder: "/etc/keycloak/meta-idp"
    certificate_file: "/etc/keycloak/certs/LdJnukYBN53JwEI8A-ITtmkW-544_RZe-jvQ0q0NCUGk.pem"
keystone_identity_mappings:
  - name: "attribute_mapping_keycloak_cloud_ld"
    file: "/etc/keycloak/attr_map/attribute_mapping.json"
keystone_federation_oidc_jwks_uri: "https://sso.paris.ld/auth/realms/cloud.ld/protocol/openid-connect/certs"
  • Use the following attribute mapping file (attribute_mapping.json) :
[
  {
    "local": [
      {
        "user": {
          "name": "{0}",
          "email": "{1}",
          "domain": {
            "name": "cloud.ld"
          }
        }
      },
      {
        "groups": "{2}",
        "domain": {
          "name": "cloud.ld"
        }
      }
    ],
    "remote": [
      {
        "type": "OIDC-preferred_username"
      },
      {
        "type": "OIDC-email"
      },
      {
        "type": "OIDC-groups"
      }
    ]
  }
]

Redeploy Keystone

With kolla-ansible configured, this is done with :

kolla-ansible deploy -i multinode --tags keystone

Patch keystone containers

This is the ugliest part... From Kolla Keystone containers (quay.io/openstack.kolla/keystone:2023.2-rocky-9), using openstack-cli with OpenID connect does not work and results in HTTP 502 errors ...
Apache in these containers is crashing (segfault) as soon as you run an openstack-cli command with OpenID connect.
The solution is to upgrade mod_auth_openidc in these containers to a more recent version (e.g. 2.4.15).
You can grab the RPM from https://github.com/OpenIDC/mod_auth_openidc/releases ; be careful, hiredis is a required dependency (you can grab it from https://dl.fedoraproject.org/pub/epel/9/Everything/x86_64/Packages/h/hiredis-1.0.2-1.el9.x86_64.rpm)
So ... For each keystone container :

docker cp mod_auth_openidc-2.4.15.1-1.el9.x86_64.rpm keystone:/tmp
docker cp hiredis-1.0.2-1.el9.x86_64.rpm keystone:/tmp
docker exec -it -u root keystone yum -y localinstall /tmp/hiredis-1.0.2-1.el9.x86_64.rpm
docker exec -it -u root keystone yum -y upgrade /tmp/mod_auth_openidc-2.4.15.1-1.el9.x86_64.rpm
docker exec -it -u root keystone rm -f /tmp/*.rpm
docker restart keystone

Openstack group and project configuration

As explained above, Keycloak users are mapped to specific groups and these groups correspond to projects in Openstack.

Group creation

Let's create a group named teria :

openstack group create --domain cloud.ld teria

Project creation and project affectation

A project named teria is created with the group teria as member.

openstack project create --description 'Teria' --domain cloud.ld teria
openstack role add --group teria --project teria member

Test !

export OS_AUTH_URL=https://auth.cloud.ld:5000/v3
export OS_PROJECT_NAME="teria"
export OS_PROJECT_DOMAIN_NAME="cloud.ld"
export OS_USERNAME="chris"
export OS_PASSWORD=<Keycloak_user_chris_password>
export OS_INTERFACE=public
export OS_IDENTITY_API_VERSION=3
export OS_CACERT=/etc/pki/tls/certs/ca-bundle.crt
export OS_AUTH_TYPE=v3oidcpassword
export OS_CLIENT_ID=keystone
export OS_CLIENT_SECRET=<Keycloak_client_keystone_key>
export OS_IDENTITY_PROVIDER=cloud.ld
export OS_PROTOCOL=openid
export OS_ACCESS_TOKEN_ENDPOINT=https://sso.paris.ld/auth/realms/cloud.ld/protocol/openid-connect/token

openstack user show chris
+---------------------+------------------------------------------------------------------------------------------+
| Field               | Value                                                                                    |
+---------------------+------------------------------------------------------------------------------------------+
| domain_id           | 09badf2ac3cf4486bac93e02fc3d73d7                                                         |
| email               | chris@teria.org                                                                          |
| enabled             | True                                                                                     |
| federated           | [{'idp_id': 'cloud.ld', 'protocols': [{'protocol_id': 'openid', 'unique_id': 'chris'}]}] |
| id                  | 1029524718feb03078319e57ebfa4e7051787f8dd3ec3cce215e5c44cdab09bb                         |
| name                | chris                                                                                    |
| options             | {}                                                                                       |
| password_expires_at | None                                                                                     |
+---------------------+------------------------------------------------------------------------------------------+