Skip to content

Kestone SSO Support

Keycloak Setup

Configure client (keystone)

🧑‍🏭 TODO: Add realm setup in Keycloak

🧑‍🏭 TODO: Add client setup in Keycloak

How to inspect the token for a user?

Authentication Flows enabled in Keycloak

  • Standard Flow
  • Implicit Flow
  • Direct Access grants
  • OAuth 2.0 Device Authorization Grant
  • Service accounts roles
  • OIDC CIBA Grant

Warning

I'm not sure if Standard Flow is required (didn't test yet without). Also Direct Access grants is only for testing purposes to be able to retrieve a token and inspect it.

Get a token for user john.doe@virtomat.net using client keystone by placing a direct call to /token endpoint:

curl --location --request POST 'http://auth.virtomat.net/realms/virtomat/protocol/openid-connect/token' \
        --header 'Content-Type: application/x-www-form-urlencoded' \
        --data-urlencode 'client_id=keystone' \
        --data-urlencode 'client_secret=RTzvozXr07H2PH7JcbzM3ho6RI5SrYpA' \
        --data-urlencode 'username=john.doe@virtomat.net' \
        --data-urlencode 'password=kYHkxe6X' \
        --data-urlencode 'grant_type=password'

Response with the token (expand to see)
{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJ3alBhUDA3T3ZzMmxlTDVsUmc3WmVzOWpHLV9LOTRNZWpsNEJpOFA3ekNRIn0.eyJleHAiOjE3MjU0NjI0MjUsImlhdCI6MTcyNTQ2MjEyNSwianRpIjoiZTMyOGFlMWYtMzg1OS00Y2NiLWIyYTUtZjg5MGU1YjJmYzNmIiwiaXNzIjoiaHR0cHM6Ly9hdXRoLnZpcnRvbWF0Lm5ldC9yZWFsbXMvdmlydG9tYXQiLCJhdWQiOiJhY2NvdW50Iiwic3ViIjoiZDBkZmY5NzUtMTk1Ni00YzBjLTk1OTgtNzg0NmM0MDRlMTExIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoia2V5c3RvbmUiLCJzaWQiOiIzMzQyYjQ2MC1mNTBhLTQ2NTUtODA5Yy0wZTVlYzM3NzFhZWYiLCJhY3IiOiIxIiwiYWxsb3dlZC1vcmlnaW5zIjpbImh0dHBzOi8vY2xvdWQudmlydG9tYXQubmV0IiwiaHR0cHM6Ly9jbG91ZC52aXJ0b21hdC5uZXQ6NTAwMCJdLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsib2ZmbGluZV9hY2Nlc3MiLCJ1bWFfYXV0aG9yaXphdGlvbiIsImRlZmF1bHQtcm9sZXMtdmlydG9tYXQiXX0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6ImVtYWlsIHByb2ZpbGUiLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwibmFtZSI6IkpvaG4gRG9lIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiam9obi5kb2VAdmlydG9tYXQubmV0IiwiZ2l2ZW5fbmFtZSI6IkpvaG4iLCJmYW1pbHlfbmFtZSI6IkRvZSIsImVtYWlsIjoiam9obi5kb2VAdmlydG9tYXQubmV0In0.CaEPNm0607laQMViPJcVmHIXKyNbH5SzcQ8LslEgbWOX0SeyBOjffQbrsf_w7UUXUXwzOFLLLCvZVBpzLr9tfHYdWUuA63k3RzT8Mnh3V3w-sgYeZlAOg9fUzQycT6GYRujKpvlpLSar2BlUpkmZLFQzV1do74nhHAe8zRiuESsMEdVmUaOtQvcDIkS7XXEpktpiAspkn1OLv2eq8n5r__-N0Bj9fONKyROOZq5mngqMBeL60cI-Lko2EOy5hwWgkeV4kbi_RGzosIX88dCXXkyHaI_WCJpYe64YutHHDRi8NoyiQNjQSAukMr0XdMWXBwgMOUStS_ZyafnmOleG6g",
  "expires_in": 300,
  "refresh_expires_in": 1800,
  "refresh_token": "eyJhbGciOiJIUzUxMiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJmNTg3ZDE4My01YWE4LTRiNTYtOGYxNS1mNzYxOGE1MTZlMTUifQ.eyJleHAiOjE3MjU0NjM5MjUsImlhdCI6MTcyNTQ2MjEyNSwianRpIjoiYWZlZWU2YzEtMTcyMC00NzQ3LWE3MjctY2U0MmZmZWQ1MjA0IiwiaXNzIjoiaHR0cHM6Ly9hdXRoLnZpcnRvbWF0Lm5ldC9yZWFsbXMvdmlydG9tYXQiLCJhdWQiOiJodHRwczovL2F1dGgudmlydG9tYXQubmV0L3JlYWxtcy92aXJ0b21hdCIsInN1YiI6ImQwZGZmOTc1LTE5NTYtNGMwYy05NTk4LTc4NDZjNDA0ZTExMSIsInR5cCI6IlJlZnJlc2giLCJhenAiOiJrZXlzdG9uZSIsInNpZCI6IjMzNDJiNDYwLWY1MGEtNDY1NS04MDljLTBlNWVjMzc3MWFlZiIsInNjb3BlIjoiYmFzaWMgd2ViLW9yaWdpbnMgZW1haWwgYWNyIHByb2ZpbGUgcm9sZXMifQ.pg--y1d_Tn3cU02DEhTqHp9Ebg75Ja1a8lmLud_qmALH87ue3NGSeLJTwQSDktx-fdmm9MMNClfkvArKEYKe0A",
  "token_type": "Bearer",
  "not-before-policy": 0,
  "session_state": "3342b460-f50a-4655-809c-0e5ec3771aef",
  "scope": "email profile"
}

Tip

You can then paste the token into tools like jwt.io and inspect

JWT payload data example (expand to see)
{
  "exp": 1725462425,
  "iat": 1725462125,
  "jti": "e328ae1f-3859-4ccb-b2a5-f890e5b2fc3f",
  "iss": "https://auth.virtomat.net/realms/virtomat",
  "aud": "account",
  "sub": "d0dff975-1956-4c0c-9598-7846c404e111",
  "typ": "Bearer",
  "azp": "keystone",
  "sid": "3342b460-f50a-4655-809c-0e5ec3771aef",
  "acr": "1",
  "allowed-origins": [
    "https://cloud.virtomat.net",
    "https://cloud.virtomat.net:5000"
  ],
  "realm_access": {
    "roles": [
      "offline_access",
      "uma_authorization",
      "default-roles-virtomat"
    ]
  },
  "resource_access": {
    "account": {
      "roles": [
        "manage-account",
        "manage-account-links",
        "view-profile"
      ]
    }
  },
  "scope": "email profile",
  "email_verified": true,
  "name": "John Doe",
  "preferred_username": "john.doe@virtomat.net",
  "given_name": "John",
  "family_name": "Doe",
  "email": "john.doe@virtomat.net"
}

Keystone Setup

External references:
Keystone Mapping Combinations (Yoga)
Configure keystone mappings
Keystone with OpenID Connect

Configure using OSA (Openstack Ansible)

🧑‍🏭 TODO: Add git repository reference

Snippet from user_variables.yaml (expand to see)
keystone_sp:
  apache_mod: 'mod_auth_openidc'
  cadf_notifications: true
  cadf_notifications_opt_out:
    - identity.authenticate.failed
    - identity.authenticate.pending
    - identity.authenticate.success
  trusted_dashboard_list:
    - "https://{{ horizon_server_name }}/auth/websso/"
    - "https://{{ external_lb_vip_address }}/auth/websso/"
  trusted_idp_list:
    - name: "oidc-idp"
      oidc_provider_metadata_url: https://auth.virtomat.net/realms/virtomat/.well-known/openid-configuration
      oidc_client_id: keystone
      oidc_client_secret: RTzvozXr07H2PH7JcbzM3ho6RI5SrYpA
      oidc_redirect_path: "/oidc_redirect"
      oidc_crypto_passphrase: a-random-secret-used-by-apache-oidc-and-balancer-not-keycloak
      oidc_auth_verify_jwks_uri: "https://auth.virtomat.net/realms/virtomat/protocol/openid-connect/certs"
      domain_id: default
      entity_ids:
        - 'https://auth.virtomat.net/realms/virtomat'
      federated_identities:
        - domain: default
          project: federated_project
          group: federated_users
          role: _member_
      protocols:
        - name: openid
          mapping:
            name: openid-mapping
            rules:
              - remote:
                  - type: OIDC-email
                  - type: OIDC-given_name
                  - type: OIDC-family_name
                local:
                  - projects:
                      - name: '{1}.{2}-ws'
                        roles:
                          - name: "_member_"
                          - name: "load-balancer_member"
                    domain:
                      id: default
                  - user:
                      email: '{0}'
                      name: '{1} {2}'
                      domain:
                        id: default

🧑‍🏭 TODO: Add explanation of how the rules engine works (explain the rules)

To update the mappings using openstack-ansible, once the user_variables.yaml file is configured properly, execute the playbook:

11:46 AM /opt/openstack-ansible
root@ansible-dev# openstack-ansible playbooks/os-keystone-install.yml 

Configure using CLI

To update a particular rule using the openstack cli, you need the json equivalent of the rules object.

Rules list for a mapping in JSON format (expand to see)
[
  {
    "remote": [
      {
        "type": "OIDC-email"
      },
      {
        "type": "OIDC-given_name"
      },
      {
        "type": "OIDC-family_name"
      }
    ],
    "local": [
      {
        "projects": [
          {
            "name": "{1}.{2}-ws",
            "roles": [
              {
                "name": "_member_"
              },
              {
                "name": "load-balancer_member"
              }
            ]
          }
        ],
        "domain": {
          "id": "default"
        },
        "user": {
          "name": "{1} {2}",
          "email": "{0}",
          "domain": {
            "id": "default"
          }
        }
      }
    ]
  }
]

Assuming you have the rules json in a file called openid-mapping-rules.json we can use openstack cli to set/update:

openstack mapping set openid-mapping --rules openid-mapping-rules.json