Aller au contenu

Surveillance de l'intégrité des agents

La hub exécute toutes les 10 minutes un IntegrityCheckWorker sur tous les agents actifs. Il génère aucun trafic réseau supplémentaire vers les agents — il s’agit d’une analyse purement côté serveur de ce qui est déjà arrivé.

Ce qui est contrôlé

TypeDétectionSévérité
unsigned_payloadIngestion sans en-tête X-Monsys-Signature (ou agent sans clé publique pinée)moyen / élevé
signature_invalidSignature invalide vérifiée contre la clé publique pinéecritique
clock_driftÉcart horaire moyen (captured_at − server_now) > 5 min sur les dernières 20 métriquesélevé
cadence_anomalyMoyenne de l’intervalle d’ingestion en dehors [7s, 23s] (attendu 15s)moyen
flat_metricsÉcart-type CPU < 0.001 sur 30+ échantillons dans 1 minuteélevé
version_downgradeVersion de l’agent semver inférieure à celle précédemment observéeélevé

Modèle ouvert / résolu

integrity_anomalies est append-only pour l’utilisateur de la hub-app (seulement INSERT et UPDATE ; DELETE/TRUNCATE est privilège révoqué). Chaque résultat se trouve dans l’un des deux états :

  • ouvertresolved_at IS NULL
  • résolu — l’administrateur a cliqué sur “marquer résolu” ou la vérification passe à nouveau dans le cycle suivant

Les éléments résolus sont conservés comme traçage d’audit. Un auditeur peut voir exactement quand et par qui chaque anomalie a été constatée et fermée.

Tableau de bord

/integrity affiche 6 cartes KPI (une par type) avec le nombre d’éléments ouverts. La table ci-dessous donne la sévérité, l’agent (clicvable), une brève description, un JSON détaillé, la date de détection et un bouton pour résoudre.

Exemples

Exemple 1 : signature_invalid avec token volé

Un attaquant a volé le ms_... token, essaie d’envoyer des métriques depuis sa propre machine sans la clé de signature :

POST /api/v1/ingest
Authorization: Bearer ms_<volé>
Content-Type: application/json
(pas d'en-tête X-Monsys-Signature)
→ 403 Forbidden
→ integrity_anomalies row :
type : unsigned_payload
sévérité : élevée
résumé : agent a une clé de signature pinée mais n'a pas transmis d'en-tête X-Monsys-Signature
détecté à : 2026-05-09 22:03:40

Il apparaît sur /integrity, colore rouge dans la bande KPI, et l’administrateur voit dans les 30 secondes via SWR refresh une alerte critique ouverte.

Exemple 2 : flat_metrics — agent envoie fausse telemetrie

Un attaquant avec root sur le host remplace le collecteur de métriques par un script qui envoie constamment CPU=4.20% :

SELECT STDDEV_SAMP(cpu_usage_percent), COUNT(*)
FROM metrics
WHERE agent_id = $1 AND time > NOW() - INTERVAL '1 heure';
-- écart-type : 0,000 (sur 240 échantillons) → anomalie flat_metrics

Le worker upsert après 10 minutes :

type : flat_metrics
sévérité : élevée
résumé : CPU écart-type = 0 sur l'heure dernière — telemetrie semble manipulée
détail : {"échantillons": 240, "écart-type": 0.0}

SQL pour l’auditeur

-- Tous les anomalies ouvertes par agent, triées par sévérité
SELECT a.name, ia.type, ia.sévérité, ia.résumé, ia.détecté à
FROM integrity_anomalies ia
JOIN agents a ON a.id = ia.agent_id
WHERE ia.resolved_at IS NULL
ORDER BY CASE ia.sévérité
WHEN 'critique' THEN 1 WHEN 'élevée' THEN 2
WHEN 'moyen' THEN 3 WHEN 'faible' THEN 4 ELSE 5 END,
ia.détecté à DESC;
-- Nombre d'éléments résolus par type dans les 30 derniers jours ?
SELECT type, count(*)
FROM integrity_anomalies
WHERE resolved_at > NOW() - INTERVAL '30 jours'
GROUP BY type;
-- Qui a fermé quel élément ?
SELECT u.email, ia.type, ia.résumé, ia.resolved_at
FROM integrity_anomalies ia
JOIN users u ON u.id = ia.resolved_by
WHERE ia.resolved_at > NOW() - INTERVAL '30 jours'
ORDER BY ia.resolved_at DESC;

Limites

  • flat_metrics a un minimum de 30 échantillons nécessaires — les agents qui viennent d’être démarrés tombent sous la limite de détection.
  • clock_drift utilise l’horloge du hub comme référence. Un attaquant qui compromet également le host du hub (manipulation horloges) évite cette vérification.
  • version_downgrade reconnaît uniquement les versions semver au style 0.x.y. Les suffixes de build tels que 0.1.0-beta1 sont considérés comme équivalents à 0.1.0.