2 jours DevOps : SSO multi-tenant, InfluxDB, et 756k points de données

devops
keycloak
oauth2-proxy
traefik
grafana
influxdb
nomad
retour-experience
Author

Sylvain Pham

Published

February 3, 2026

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-site1
  • partner-site2
  • partner-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:18086

Ré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 :

  1. Protégée par SSO pour l’interface /docs
  2. Accessible sans auth pour les appels M2M (/api/*)
  3. 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: 50

OAuth2-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

Liens