Aller au contenu

Keycloak - Clients OIDC#

Contrairement à la terminologie habituelle client/serveur, un client OIDC désigne le compte sur lequel une application ou un service se connecte. Ce dernier comporte un ClientID et un Secret.

On évitera donc de parler de client Grafana pour désigner l'application, car son ClientID peut également s'appeler grafana, ce qui provoque une confusion à l'oral.

Provisionner un client OIDC#

Client OIDC pour NGOT#

1. Choix du Keycloak#

La première information nécessaire est de déterminer le Keycloak auquel se raccorder :

  • En fonction de la ligne (Production, Staging, etc.)
  • Selon les options attendues (Ex : Raccordement à Signon / MyPortal).

Exemple de Keycloak actuellement disponible :

  • Zone svc-signon-stg
    • Realm svc-signon-stg
      • Ligne de Staging
      • Raccordé sur Signon (Application MyPortal = NGOT_PRP)
      • Raccordé sur Caascad (Keycloak Corp)
  • Zone svc-signon-prd
    • Realm svc-signon-prd
      • Ligne de Production
      • Raccordé sur Signon (Application MyPortal = NGOT)
      • Raccordé sur Caascad (Keycloak Corp)

En cas de doutes, se rapprocher de l'équipe Apps.

2. Configuration#

Le provisioning du client se fait via le fichier envs-ng/contexts/ngot/keycloak_k8s_openid_clients.cue.

Les clients OIDC sont définis par realm pour chaque zone de service :

envs: ["svc-signon-stg"]: {
    zone: _

    configurations: ["keycloak_k8s_openid_clients"]: tfvars: {
        realms: {
            "svc-signon-stg": {
                openid_clients: {

                    // Configuration des clients OIDC
                    // du realm "svc-signon-stg"
                    // de la zone "svc-signon-stg"

                }
            }
        }
    }
}

Ajout d'un client#
                openid_clients: {
                    #OpenIDClient & {"nom-du-client": {
                            _zone: zone

                            // Paramètres du client

                        }
                    }
                }
Paramétrage d'un client#

Les paramètres disponibles en CUE se trouvent dans la définition #OpenIDClient du fichier envs-ng/contexts/keycloak_k8s_openid_clients.cue.

Ils sont appliqués par Terraform via la ressource keycloak_openid_client du fichier configurations/keycloak_k8s_openid_clients/keycloak.tf.

Info

L'ensemble des paramètres est décrit dans le provider keycloak.

Exemple d'un client avec override de certains paramètres :

                openid_clients: {
                    #OpenIDClient & {"nom-du-client": {
                        _zone: zone

                        access_type: "PUBLIC"

                        valid_redirect_uris: [
                            "https://nom-du-service.csfpriv.com/callback/path/example/",
                            "https://autre-nom.csfpriv.com/*",
                        ]

                        web_origins: [
                            "https://nom-du-service.csfpriv.com",
                        ]

                    }}
                }

Ajout des rôles sur un client#
                openid_clients: {
                    #OpenIDClient & {"nom-du-client": {
                        _zone: zone

                        roles: [
                                {
                                    keycloak_role: "admin"
                                },
                                {
                                    keycloak_role: "viewer"
                                },
                            ]
                    }}
                }

En option : on peut attribuer un rôle en fonction d'une valeur transmise par une source d'authentification externe (IDP) telle que Signon ou Caascad. Exemple pour Signon avec les habilitations MyPortal :

envs: ["svc-signon-stg"]: {
    zone: _

    configurations: ["keycloak_k8s_openid_clients"]: tfvars: {

        // S'assurer que cette ligne est présente, sinon voir avec l'équipe Apps
        _signon_aliases: {...} @read_output(config=keycloak_idp_signon, name=aliases)

        realms: {
            "svc-signon-stg": {

                // Pour le realm cible, s'assurer que l'on récupère son alias
                // _signon_aliases["<nom du realm>"]
                _signon_alias:  _signon_aliases["svc-signon-stg"]

                openid_clients: {
                    #OpenIDClient & {"nom-du-client": {
                        _zone: zone

                        roles: [
                            {
                                // Attribution du rôle "admin" aux utilisateurs ayant
                                // l'habilitation "NGOT_PRP-ADMIN"
                                saml_idp_mappers: (_signon_alias): "NGOT_PRP-ADMIN"
                                keycloak_role: "admin"
                            },
                            {
                                saml_idp_mappers: (_signon_alias): "NGOT_PRP-VIEWER"
                                keycloak_role: "viewer"
                            },
                        ]
                    }}
                }
            }
        }
    }
}

3. Déploiement#

Le déploiement des clients OIDC est à faire dans la zone du Keycloak concerné, par exemple :

# Dans le contexte NGOT d'envs-ng
trackbone apply -z svc-signon-stg -c keycloak_k8s_openid_clients

Objets Terraform attendus :

# Le client OIDC
keycloak_openid_client.clients["svc-signon-stg_nom-du-client"]

# Son secret Vault
vault_generic_secret.openid_clients["svc-signon-stg_nom-du-client"]

# Un mapper générique de l'ensemble de ses rôles
keycloak_openid_user_client_role_protocol_mapper.mappers["svc-signon-stg_nom-du-client"]

# Ses rôles (optionnel)
keycloak_role.roles["svc-signon-stg_nom-du-client_admin"]
keycloak_role.roles["svc-signon-stg_nom-du-client_viewer"]

# Des mappers SAML ou OIDC (optionnel)
keycloak_custom_identity_provider_mapper.saml_idp_mappers["svc-signon-stg_nom-du-client_admin"]
keycloak_custom_identity_provider_mapper.saml_idp_mappers["svc-signon-stg_nom-du-client_viewer"]

Client OIDC pour Caascad#

1. Configuration#

Danger

Contacter l'équipe Apps pour le provisioning des clients OIDC sur les environnements Caascad.

Le provisioning du client se fait via le fichier :

Dans la section Clients definition, ajouter un client :

  • Exemple d'un client OIDC rocketchat sans rôles keycloak :

    #RocketChatClient: #OpenIDClient & {"rocketchat": {
        _zone: _
    
        valid_redirect_uris: [
            "https://chat.\(_zone.name).\(_zone.domain_name)/*",
        ]
    }}
    

  • Exemple d'un client OIDC nextcloud avec un rôle keycloak :

    #NextCloudClient: #OpenIDClient & {"nextcloud": {
        _zone: _
    
        valid_redirect_uris: [
            "https://cal.\(_zone.name).\(_zone.domain_name)/*",
        ]
    
        roles: [
            {
                keycloak_role: "admin"
            },
        ]
    }}
    

  • Exemple d'un client OIDC keycloak-ngot-svc-signon-stg pour interconnecter une zone signon sur NGOT avec Keycloak Corp :
    #NgotSignonStgClient: #OpenIDClient & {"keycloak-ngot-svc-signon-stg": {
        _zone: _
    
        valid_redirect_uris: [
            "https://auth.obs-signon-stg.csfpriv.com/realms/svc-signon-stg/broker/caascad/endpoint",
        ]
        roles: [
            {
                keycloak_role: "ngot-administrator"
            },
        ]
    }}
    

2. Déploiement#

Le déploiement des clients OIDC est à faire dans la zone du Keycloak concerné, par exemple :

# Dans le contexte PF d'envs-ng
trackbone apply -z ${ZONE} -c keycloak_k8s_openid_clients

Récupération des paramètres OIDC#

Les paramètres d'un client OIDC se récupèrent depuis Vault. Le vault et le path changent en fonction de type de plateforme (Caascad ou NGOT).

En CUE#

Exemple d'une chart Helm fictive avec un client OIDC sur la zone svc-signon-stg avec le realm svc-signon-stg :

envs: [string]: {
    zone: _
    configurations: ["my_service"]: #HelmConfig & {
        run: pre_tasks: {

            // ...

            my_service_oidc: vault.#Read & {
                url:  pre_tasks["vault_\(zone.infra_zone_name)"].url
                path: "secret/zones/fe/svc-signon-stg/keycloak/realms/svc-signon-stg/clients/my-service"
                data: {
                    id:                     string
                    secret:                 string
                    keycloak_url:           string
                    oidc_discovery_url:     string
                    authorization_endpoint: string
                    token_endpoint:         string
                    userinfo_endpoint:      string
                    end_session_endpoint:   string
                }
            }
        }

        let MyServiceOIDC = run.pre_tasks.my_service_oidc.data

        helm: {
            values: {
                chart_oidc_settings: {
                    client_id:     MyServiceOIDC.id
                    client_secret: MyServiceOIDC.secret

                    oidc_provider: MyServiceOIDC.keycloak_url

                    // ...

                }
            }
        }
    }
}

En shell#

Exemple de récupération de paramètre d'un client OIDC en shell :

export CLIENT_NAME='client-oidc'
export VAULT_ADDR='https://vault.infra-stg.caascad.com'
vault token lookup || vault login -method oidc

vault read secret/oidc/${CLIENT_NAME}
export CLIENT_NAME='client-oidc'
export VAULT_ADDR='https://vault.infra-prd.caascad.com'
vault token lookup || vault login -method oidc

vault read secret/oidc/${CLIENT_NAME}
export ZONE_NAME='ocb-example'
export CLIENT_NAME='client-oidc'
export VAULT_ADDR="https://vault.${ZONE_NAME}.caascad.com"
vault token lookup || vault login -method oidc

vault read secret/oidc/${CLIENT_NAME}
export CLIENT_NAME='client-oidc'
export VAULT_ADDR='https://vault.infra-stg.caascad.com'
export ZONE_NAME='svc-signon-stg'
vault token lookup || vault login -method oidc

vault read secret/zones/fe/${ZONE_NAME}/keycloak/realms/${ZONE_NAME}/clients/${CLIENT_NAME}
export CLIENT_NAME='client-oidc'
export VAULT_ADDR='https://vault.infra-prd.caascad.com'
export ZONE_NAME='svc-signon-prd'
vault token lookup || vault login -method oidc

vault read secret/zones/fe/${ZONE_NAME}/keycloak/realms/${ZONE_NAME}/clients/${CLIENT_NAME}