RBAC via SSO : rôle de realm Keycloak → niveau Grafana (authZ)
Machinerie additive/idempotente dans serveur_keycloak (rbac-oidc.yml) : rôles de realm + mapper 'roles' sur les clients choisis + assignations rôle→utilisateur. kcadm à chaud (zéro coupure SSO). Grafana : role_attribute_path (grafana-admin→Admin, grafana-editor→Editor, sinon Viewer). Prouvé sur id-sso-01 (idempotence) : testmail = grafana-editor. Complète le dashboard logs (Viewer-friendly) : les deux volets de la question « testmail peut-il voir les logs ? ». Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
4887d2aee8
commit
c5df8fac4a
6 changed files with 130 additions and 0 deletions
17
CHANGELOG.md
17
CHANGELOG.md
|
|
@ -1,5 +1,22 @@
|
|||
# CHANGELOG — Set-OPS
|
||||
|
||||
## 2026-07-04
|
||||
|
||||
### Ajouté
|
||||
- **UI des logs Loki : dashboard Grafana provisionné.** Loki n'a pas d'UI ; son UI est Grafana.
|
||||
Ajout d'un **dashboard « Journaux de la flotte »** (dossier Set-OPS) : sélecteur d'hôte multi +
|
||||
filtre regex insensible à la casse + panneau logs + débit par hôte. Référence Loki par une
|
||||
**variable de datasource** (`ds_loki`), pas un UID codé en dur (leçon : ajouter un uid explicite à
|
||||
une datasource déjà provisionnée casse le démarrage de Grafana). **Visible par les Viewers** (dont
|
||||
testmail) sans accès Explore. Prouvé sur obs-01 (dashboard chargé, Grafana actif).
|
||||
- **RBAC via SSO : rôle de realm → niveau Grafana.** Machinerie *additive et idempotente* dans
|
||||
`serveur_keycloak` (`rbac-oidc.yml`) : rôles de realm (`serveur_keycloak_realm_roles`), **mapper
|
||||
`roles`** sur les clients choisis (`_role_mapper_clients`, rôles de realm → claim `roles` dans
|
||||
ID token + userinfo), **assignations** rôle→utilisateur (`_role_assignments`). Côté Grafana,
|
||||
`role_attribute_path` (`grafana-admin→Admin`, `grafana-editor→Editor`, sinon Viewer). kcadm **à
|
||||
chaud, zéro coupure SSO**. Prouvé (idempotence `changed=0`) sur id-sso-01 : rôles créés, mapper
|
||||
présent, `testmail` = `grafana-editor` (→ Explore). Illustre l'**authZ** (vs authN du SSO).
|
||||
|
||||
## 2026-07-03
|
||||
|
||||
### Ajouté
|
||||
|
|
|
|||
|
|
@ -29,6 +29,9 @@ serveur_grafana_oidc_client_secret: "{{ vault_grafana_oidc | default('') }}" #
|
|||
serveur_grafana_oidc_realm: "chezlepro"
|
||||
serveur_grafana_oidc_base: "https://keycloak.{{ domaine_interne }}/realms/{{ serveur_grafana_oidc_realm }}/protocol/openid-connect"
|
||||
serveur_grafana_oidc_scopes: "openid email profile"
|
||||
# RBAC via SSO : mappe le claim 'roles' (rôles de realm Keycloak) vers le niveau Grafana.
|
||||
# Défaut : grafana-admin→Admin, grafana-editor→Editor, sinon Viewer. Vider pour désactiver.
|
||||
serveur_grafana_oidc_role_path: "contains(roles[*], 'grafana-admin') && 'Admin' || contains(roles[*], 'grafana-editor') && 'Editor' || 'Viewer'"
|
||||
|
||||
# Depot apt officiel Grafana (partage avec serveur_loki).
|
||||
serveur_grafana_depot_cle_url: "https://apt.grafana.com/gpg-full.key"
|
||||
|
|
|
|||
|
|
@ -13,4 +13,8 @@ Environment=GF_AUTH_GENERIC_OAUTH_AUTH_URL={{ serveur_grafana_oidc_base }}/auth
|
|||
Environment=GF_AUTH_GENERIC_OAUTH_TOKEN_URL={{ serveur_grafana_oidc_base }}/token
|
||||
Environment=GF_AUTH_GENERIC_OAUTH_API_URL={{ serveur_grafana_oidc_base }}/userinfo
|
||||
Environment=GF_AUTH_GENERIC_OAUTH_LOGIN_ATTRIBUTE_PATH=preferred_username
|
||||
{% if serveur_grafana_oidc_role_path %}
|
||||
Environment="GF_AUTH_GENERIC_OAUTH_ROLE_ATTRIBUTE_PATH={{ serveur_grafana_oidc_role_path }}"
|
||||
Environment=GF_AUTH_GENERIC_OAUTH_ROLE_ATTRIBUTE_STRICT=false
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
|
|
|||
|
|
@ -49,3 +49,12 @@ serveur_keycloak_truststore_path: "/etc/ssl/certs/ca-certificates.crt"
|
|||
# web_origins: ["https://grafana.{{ domaine_interne }}"]
|
||||
# secret: "{{ vault_grafana_oidc }}"
|
||||
serveur_keycloak_clients: []
|
||||
|
||||
# --- RBAC OIDC (additif, idempotent) ---
|
||||
# Rôles de realm à garantir (ex. ['grafana-admin', 'grafana-editor']).
|
||||
serveur_keycloak_realm_roles: []
|
||||
# Clients recevant le mapper « roles » (rôles de realm → claim 'roles' dans le jeton).
|
||||
serveur_keycloak_role_mapper_clients: []
|
||||
# Assignations rôle→utilisateur (ex. [{user: testmail, role: grafana-editor}]).
|
||||
# En prod, préférer l'assignation via groupe d'annuaire ; ici, explicite pour la preuve.
|
||||
serveur_keycloak_role_assignments: []
|
||||
|
|
|
|||
|
|
@ -146,6 +146,12 @@
|
|||
ansible.builtin.include_tasks: clients-oidc.yml
|
||||
when: serveur_keycloak_clients | length > 0
|
||||
|
||||
- name: Configurer le RBAC OIDC (roles de realm + mapper + assignations)
|
||||
ansible.builtin.include_tasks: rbac-oidc.yml
|
||||
when: >
|
||||
serveur_keycloak_realm_roles | length > 0
|
||||
or serveur_keycloak_role_assignments | length > 0
|
||||
|
||||
- name: Definir le theme de login du realm (identite Alliance Boreale)
|
||||
ansible.builtin.shell:
|
||||
executable: /bin/bash
|
||||
|
|
|
|||
91
roles/serveur_keycloak/tasks/rbac-oidc.yml
Normal file
91
roles/serveur_keycloak/tasks/rbac-oidc.yml
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
---
|
||||
# RBAC OIDC : rôles de realm + mapper de rôles sur les clients concernés + assignations.
|
||||
# Additif et idempotent (create-si-absent, kcadm à chaud — pas de restart Keycloak).
|
||||
# Permet à une app (ex. Grafana) de mapper un rôle du realm vers son niveau de droit.
|
||||
|
||||
- name: Assurer les roles de realm (kcadm, idempotent)
|
||||
ansible.builtin.shell:
|
||||
executable: /bin/bash
|
||||
cmd: |
|
||||
set -euo pipefail
|
||||
KC={{ serveur_keycloak_home }}/bin/kcadm.sh
|
||||
"$KC" config credentials --server http://localhost:8080 --realm master \
|
||||
--user {{ serveur_keycloak_admin_user }} --password "$KC_ADMIN_PW" >/dev/null
|
||||
if "$KC" get roles/{{ item }} -r {{ serveur_keycloak_realm }} >/dev/null 2>&1; then
|
||||
echo SETOPS_OK
|
||||
else
|
||||
"$KC" create roles -r {{ serveur_keycloak_realm }} -s name={{ item }} >/dev/null
|
||||
echo SETOPS_CHANGED
|
||||
fi
|
||||
environment:
|
||||
KC_ADMIN_PW: "{{ serveur_keycloak_admin_password }}"
|
||||
loop: "{{ serveur_keycloak_realm_roles }}"
|
||||
register: serveur_keycloak_role_reg
|
||||
changed_when: "'SETOPS_CHANGED' in serveur_keycloak_role_reg.stdout"
|
||||
no_log: true
|
||||
when:
|
||||
- not ansible_check_mode
|
||||
- serveur_keycloak_realm_roles | length > 0
|
||||
|
||||
- name: Assurer le mapper de roles de realm sur les clients (claim 'roles')
|
||||
ansible.builtin.shell:
|
||||
executable: /bin/bash
|
||||
cmd: |
|
||||
set -euo pipefail
|
||||
KC={{ serveur_keycloak_home }}/bin/kcadm.sh
|
||||
"$KC" config credentials --server http://localhost:8080 --realm master \
|
||||
--user {{ serveur_keycloak_admin_user }} --password "$KC_ADMIN_PW" >/dev/null
|
||||
CID=$("$KC" get clients -r {{ serveur_keycloak_realm }} -q clientId={{ item }} 2>/dev/null \
|
||||
| grep -oP '"id"\s*:\s*"\K[^"]+' | head -1)
|
||||
if [ -z "$CID" ]; then echo "SETOPS_OK client absent"; exit 0; fi
|
||||
if "$KC" get clients/$CID/protocol-mappers/models -r {{ serveur_keycloak_realm }} 2>/dev/null \
|
||||
| grep -q '"realm-roles"'; then
|
||||
echo SETOPS_OK
|
||||
else
|
||||
"$KC" create clients/$CID/protocol-mappers/models -r {{ serveur_keycloak_realm }} \
|
||||
-s name=realm-roles -s protocol=openid-connect \
|
||||
-s protocolMapper=oidc-usermodel-realm-role-mapper \
|
||||
-s 'config."claim.name"=roles' \
|
||||
-s 'config."jsonType.label"=String' \
|
||||
-s 'config."multivalued"=true' \
|
||||
-s 'config."id.token.claim"=true' \
|
||||
-s 'config."access.token.claim"=true' \
|
||||
-s 'config."userinfo.token.claim"=true' >/dev/null
|
||||
echo SETOPS_CHANGED
|
||||
fi
|
||||
environment:
|
||||
KC_ADMIN_PW: "{{ serveur_keycloak_admin_password }}"
|
||||
loop: "{{ serveur_keycloak_role_mapper_clients }}"
|
||||
register: serveur_keycloak_mapper_reg
|
||||
changed_when: "'SETOPS_CHANGED' in serveur_keycloak_mapper_reg.stdout"
|
||||
no_log: true
|
||||
when:
|
||||
- not ansible_check_mode
|
||||
- serveur_keycloak_role_mapper_clients | length > 0
|
||||
|
||||
- name: Assigner les roles de realm aux utilisateurs (add-roles idempotent)
|
||||
ansible.builtin.shell:
|
||||
executable: /bin/bash
|
||||
cmd: |
|
||||
set -euo pipefail
|
||||
KC={{ serveur_keycloak_home }}/bin/kcadm.sh
|
||||
"$KC" config credentials --server http://localhost:8080 --realm master \
|
||||
--user {{ serveur_keycloak_admin_user }} --password "$KC_ADMIN_PW" >/dev/null
|
||||
if "$KC" get-roles -r {{ serveur_keycloak_realm }} --uusername {{ item.user }} 2>/dev/null \
|
||||
| grep -q '"{{ item.role }}"'; then
|
||||
echo SETOPS_OK
|
||||
else
|
||||
"$KC" add-roles -r {{ serveur_keycloak_realm }} --uusername {{ item.user }} --rolename {{ item.role }} >/dev/null
|
||||
echo SETOPS_CHANGED
|
||||
fi
|
||||
environment:
|
||||
KC_ADMIN_PW: "{{ serveur_keycloak_admin_password }}"
|
||||
loop: "{{ serveur_keycloak_role_assignments }}"
|
||||
loop_control:
|
||||
label: "{{ item.user }} -> {{ item.role }}"
|
||||
register: serveur_keycloak_assign_reg
|
||||
changed_when: "'SETOPS_CHANGED' in serveur_keycloak_assign_reg.stdout"
|
||||
no_log: true
|
||||
when:
|
||||
- not ansible_check_mode
|
||||
- serveur_keycloak_role_assignments | length > 0
|
||||
Loading…
Reference in a new issue