Retour sur deux jours intenses : déployer un SSO multi-tenant avec gestion des rôles, migrer InfluxDB vers Nomad, ingérer des centaines de milliers de points de données IoT, et configurer des dashboards Grafana par partenaire. Le tout avec quelques surprises en chemin.
Le contexte
Une plateforme data avec plusieurs services (API, dashboards, documentation) et deux types d’utilisateurs :
- Staff : équipe interne, accès à tout, MFA obligatoire
- Partenaires : accès limité à leurs propres données, pas de MFA
L’objectif : unifier l’authentification avec Keycloak tout en gardant une séparation stricte des accès.
Jour 1 : L’authentification multi-tenant
Migration Nomad UI vers SSO
Premier service migré : l’interface Nomad. Simple sur le papier - ajouter OAuth2-Proxy devant.
Navigateur → Traefik → OAuth2-Proxy → Nomad UI
↓
Keycloak
La subtilité : créer un client Keycloak dédié (nomad) plutôt que réutiliser le client générique. Permet de tracer les connexions par service.
MFA conditionnel : staff uniquement
Keycloak permet de créer des flows d’authentification conditionnels. L’idée : exiger l’OTP uniquement si l’utilisateur a le rôle staff.
browser-staff-mfa
├── Username Password Form (REQUIRED)
└── Conditional OTP (CONDITIONAL)
├── Condition - user role [staff] (REQUIRED)
└── OTP Form (REQUIRED)
Résultat :
| Utilisateur | Rôle | MFA |
|---|---|---|
| Équipe interne | staff | OTP requis |
| Partenaires | partner | Pas de MFA |
Contrôle d’accès par rôle
Plusieurs partenaires, chacun avec son compte dedié :
partner-site1partner-site2partner-site3- etc.
Plus 2 comptes transversaux avec accès global à tous les sites.
Restriction OAuth2-Proxy :
args = [
"--allowed-role=staff",
# ...
]
Les partenaires reçoivent un 403 sur Nomad UI et la documentation interne. Ils n’ont accès qu’à Grafana.
Grafana : permissions par équipe
Configuration des équipes Grafana pour isoler les données :
| Équipe | Membres | Accès |
|---|---|---|
| Site-1 | partner-site1 | Dossier Site 1 uniquement |
| Site-2 | partner-site2 | Dossier Site 2 uniquement |
| Admins | compte-transversal | Tous les dossiers |
Le mapping des rôles Keycloak → Grafana :
GF_AUTH_GENERIC_OAUTH_ROLE_ATTRIBUTE_PATH=contains(roles, 'staff') && 'Editor' || 'Viewer'Staff = Editor (peut modifier), Partenaires = Viewer (lecture seule).
Jour 2 : Les données
InfluxDB sur Nomad : le piège des volumes
Migrer InfluxDB de Docker Compose vers Nomad. Ça devrait être simple…
Driver Failure: volumes are not enabled; cannot mount host paths
Nomad n’autorise pas les bind mounts directs par défaut. Il faut déclarer les volumes explicitement :
# /etc/nomad.d/volumes.hcl
client {
host_volume "influxdb_data" {
path = "/server-storage/influxdb2"
read_only = false
}
}
Et redémarrer Nomad (reload ne suffit pas pour les volumes).
Ingestion de 756k points IoT
Données de capteurs électriques depuis une API partenaire. Le script Python récupère et injecte dans InfluxDB.
Problème : InfluxDB n’est accessible que depuis le réseau interne.
Solution : Tunnel SSH.
ssh -f -N -L 18086:10.0.0.5:8086 bastion
# Puis utiliser http://localhost:18086Résultat :
✅ ~750k points ingérés
📦 Plusieurs unités traitées
📅 Période: ~1 an de données historiques
Import de 15 dashboards Grafana
L’API Grafana pour importer des dashboards :
jq -c "{dashboard: ., overwrite: true}" dashboard.json | \
curl -X POST "http://grafana:3000/api/dashboards/db" \
-H "Content-Type: application/json" \
--user "admin:password" \
-d @-Piège : L’authentification Keycloak ne fonctionne pas pour l’API. Il faut utiliser le compte admin local.
Protection de l’API avec bypass intelligent
L’API doit être :
- Protégée par SSO pour l’interface
/docs - Accessible sans auth pour les appels M2M (
/api/*) - Accessible sans SSO pour les utilisateurs internes (via proxy d’entreprise)
Solution : routes Traefik avec priorités
# Priorité haute : proxy interne → accès direct
api-internal:
rule: "Host(`api.example.com`) && ClientIP(`194.9.98.0/24`)"
service: api-backend
priority: 100
# Priorité basse : autres → OAuth2-Proxy
api:
rule: "Host(`api.example.com`)"
service: oauth2-proxy-api
priority: 50OAuth2-Proxy avec skip-auth :
args = [
"--skip-auth-route=^/api/",
"--skip-auth-route=^/openapi.json$",
# ...
]
Les difficultés rencontrées
| Problème | Cause | Solution |
|---|---|---|
| redirect_uri invalide | URL non configurée dans Keycloak | Ajouter via API admin |
| Volumes Nomad | host_volume non déclaré | Config + restart Nomad |
| API Grafana 401 | OAuth ne marche pas pour l’API | Utiliser admin local |
| Test proxy interne | Pas d’accès au réseau | Demander à un collègue |
Récapitulatif
| Métrique | Valeur |
|---|---|
| Services migrés vers SSO | 5 |
| Comptes partenaires créés | ~10 |
| Dashboards Grafana | 15 |
| Points de données ingérés | ~750k |
| Cafés consommés | ∞ |
Ce qui reste
Technologies utilisées
| Composant | Rôle |
|---|---|
| Keycloak | Identity Provider (SSO OIDC) |
| OAuth2-Proxy | Proxy d’authentification |
| Traefik | Reverse proxy / routing |
| Nomad | Orchestrateur de containers |
| InfluxDB | Base de données time-series |
| Grafana | Visualisation / dashboards |