SOMMAIRE
0 périmètre
1 le layout d'install
2 hot-reload vs restart
3 signature de driver en pratique
4 l'agent comme service windows
5 config — un fichier, deux moitiés
6 côté serveur — docker compose
7 ce que cette série ne couvre pas
──[ 0. Périmètre ]──
Les dix dernières parties ont couvert le codebase. Celle-ci est la référence de
poche opérationnelle : où vivent les binaires sur un hôte, ce qu'un opérateur
peut changer sans redémarrer quel service, et ce qui est requis pour expédier un
build vers un endpoint réel.
C'est aussi le wrap-up. L'arc narratif s'arrête ici.
──[ 1. Le Layout D'Install ]──
C:\Program Files\WazabiEDR\
├── WazabiEDR_Driver.sys (chargé par le service WazabiEDR)
├── WazabiEDR_Driver.inf
├── WazabiEDR_Agent.exe (service WazabiEDR_Agent)
├── wedr-plugin.exe (CLI opérateur, pas un service)
└── plugins\
├── WazabiEDR_Plugin_DefenderBridge.exe
└── … (autres plugins vendor)
C:\ProgramData\WazabiEDR\
├── agent.json (config, lisible par tous)
├── spool\
│ ├── active.ndjson
│ ├── batch-001.ndjson.zst
│ └── plugins\
│ ├── active.ndjson
│ └── batch-001.ndjson.zst
└── plugins\
└── <plugin_id>.json (manifest, ACL : admin write)
Deux racines, volontairement séparées. `Program Files` porte les binaires qui
devraient être read-only au runtime et remplaçables seulement par un installeur
avec des privilèges élevés. `ProgramData` porte l'état qui peut être modifié au
runtime par l'agent lui-même.
`%ProgramData%WazabiEDRplugins` a l'ACL de manifest décrite en Partie 9 —
Administrators écrivent, tout le monde lit. Le répertoire de spool a une ACL
plus lâche (le service account de l'agent écrit ; seuls les Administrators
lisent, parce que les batchs contiennent du contenu d'événements qui peut
inclure des chemins ou valeurs sensibles).
──[ 2. Hot-Reload vs Restart ]──
changement effet
────────────────────────────── ────────────────────────────────
wedr-plugin enroll / revoke / update hot reload sous 5 s
wedr-plugin flag auto-launch demande un restart d'agent
modifications agent.json demandent un restart d'agent
remplacement du binaire driver demande restart driver + agent
(en pratique, demande un reboot)
remplacement du binaire plugin demande un restart du plugin ;
doctor signale la dérive sha256
redéploiement de l'image serveur zéro downtime via rolling
restart du conteneur API
L'ensemble hot-reload est petit mais il couvre les opérations effectuées en
routine (enrôlement, révocation, update après rebuild). Tout le reste est un
restart explicite parce que la simplicité d'implémentation est haute et le coût
est faible — un restart d'agent ne perd aucun événement grâce au spool sur
disque.
Le driver est le pire cas. Remplacer un `.sys` chargé pendant qu'un agent a un
handle ouvert sur le device est techniquement supporté (`SERVICE_STOP_PENDING` →
unload → start nouveau) mais très rarement vaut le risque opérationnel. La
pratique standard, c'est de planifier un reboot.
──[ 3. Signature de Driver en Pratique ]──
Posture de test pour le développement local :
bcdedit /set testsigning on
bcdedit /set hypervisorlaunchtype off # si HVCI est activé
`testsigning` (l'option de Boot Configuration Data qui active le Test Signing
Mode) autorise le loader kernel à accepter des drivers signés par un certificat
non-Microsoft, non-WHQL-cross-signé, y compris self-signed et (dans certaines
configurations) non signés. HVCI (Hypervisor-Protected Code Integrity, Partie 1)
impose des règles plus strictes par-dessus et doit être off pour les drivers
test-signés.
La posture de production est fondamentalement différente. Tout driver installé
sur des endpoints tiers doit être signé par un certificat auquel le loader
kernel fait confiance sans test signing. Pour les drivers kernel-mode ça demande
soit :
- Un certificat EV Code Signing + attestation signing
Microsoft Hardware Dev Center (le chemin moderne), soit
- WHQL cross-signing via le Hardware Compatibility Program
(le chemin plus ancien)
La soumission WHQL (Windows Hardware Quality Labs : le programme de
certification de compatibilité matérielle de Microsoft, utilisé comme ancre de
confiance pour les drivers kernel tiers signés en production) est une étape
opérationnelle non triviale. Elle demande un compte Microsoft Partner Center, un
cert EV d'une CA reconnue, un package de soumission qui inclut le driver,
l'`.inf`, le catalog, et un passage de HLK test. Le driver WazabiEDR actuel
n'est pas WHQL-signé ; la posture de déploiement v1, c'est des VM et des
machines de labo test-signées, pas des endpoints tiers.
──[ 4. L'Agent Comme Service Windows ]──
L'agent est enregistré avec le Service Control Manager via `sc.exe` :
sc.exe create WazabiEDR_Agent `
binPath= "\"C:\Program Files\WazabiEDR\WazabiEDR_Agent.exe\"" `
start= auto `
depend= WazabiEDR
`depend= WazabiEDR` déclare une dépendance sur le service driver — le SCM
démarre le driver en premier, et l'agent ne démarre que si le driver a chargé.
La dépendance simplifie l'attribution d'échec : un échec de chargement de driver
produit un clair "le service ou le groupe dont dépendait ce service n'a pas pu
démarrer" plutôt qu'un agent qui démarre et meurt ensuite sur
`CreateFileW(\.WazabiEDR)`.
Le service tourne en `LOCAL_SYSTEM` (le compte de service built-in à plus haut
privilège). La justification, c'est deux accès spécifiques : l'ACL du device du
driver n'accorde l'accès qu'à `SYSTEM`, et le répertoire de manifests est dans
un chemin dont l'accès en lecture effectif demande de traverser des répertoires
possédés par `SYSTEM`. `LOCAL_SERVICE` serait un principal plus serré mais
manque des droits pour l'IOCTL du driver.
Les logs du service sont écrits dans l'Event Log Windows sous une source
enregistrée (`WazabiEDR_Agent`). L'Event Log est l'évier standard : visible dans
Event Viewer, forwardable à n'importe quel SIEM que l'opérateur a déjà via WEF
(Windows Event Forwarding), et géré par l'OS avec sa propre politique de
rotation. Maintenir un fichier de log plain-text séparé dupliquerait des
fonctionnalités que l'OS fournit déjà.
──[ 5. Config — Un Fichier, Deux Moitiés ]──
`%ProgramData%WazabiEDRagent.json` est la seule surface de configuration. Deux
sections top-level :
{
"agent": {
"console_output": false,
"spool_dir": "C:\\ProgramData\\WazabiEDR\\spool",
"max_bytes_per_file": 16777216,
"max_age": "1h",
"max_total_bytes": 1073741824,
"channel_capacity": 4096,
"zstd_level": 6
},
"shipper": {
"url": "https://wazabi.example.com/api/v1/agents/checkin",
"agent_token": "…",
"hmac_secret": "…",
"client_cert": "C:\\ProgramData\\WazabiEDR\\agent.crt",
"client_key": "C:\\ProgramData\\WazabiEDR\\agent.key",
"ca_bundle": "C:\\ProgramData\\WazabiEDR\\ca.pem"
}
}
Le bloc `shipper` est optionnel. L'omettre met l'agent en mode spool-only (utile
pour les essais air-gappés et pour le développement). Le bloc `agent` a des
défauts documentés pour chaque clé ; les clés manquantes retombent sur le
défaut, les clés inconnues sont loggées comme warnings plutôt qu'erreurs pour
qu'un agent plus récent ne refuse pas une config plus ancienne.
La politique forward-compatible a un trade — une faute de frappe sur une clé
connue est aussi un warning, pas une erreur, parce que le parser de config ne
peut pas distinguer "nouvelle clé inconnue" de "vieille clé mal orthographiée".
Les opérateurs qui veulent de la strictness peuvent lancer `WazabiEDR_Agent
--check-config` comme opération séparée qui valide contre le schéma courant.
──[ 6. Côté Serveur — Docker Compose ]──
Le dépôt serveur livre un `docker-compose.yml` qui monte quatre conteneurs :
┌────────────────────┐
│ wazabi-api │ workers uvicorn derrière un reverse proxy
│ wazabi-postgres │ état
│ wazabi-opensearch │ events + alertes
│ wazabi-redis │ cache + file de commandes
└────────────────────┘
`make up` met toute la stack en ligne. Les migrations de base de données
tournent au démarrage du conteneur API via Alembic ; le premier démarrage
populate les index templates OpenSearch (Partie 6). Pour les redéploiements,
`docker compose pull && docker compose up -d` effectue un rolling restart du
conteneur API tout en laissant les trois conteneurs stateful (Postgres,
OpenSearch, Redis) en cours d'exécution — ceux-ci persistent via des volumes
nommés et ne sont redémarrés que pour des mises à niveau de version d'engine.
Les déploiements de production substitueraient des équivalents managés pour les
trois datastores (un Postgres managé pour l'état, un Elasticsearch / OpenSearch
managé pour les événements, un Redis managé pour le cache). Le fichier Compose
est le layout de référence et l'environnement de développement ; ce n'est pas un
template de déploiement de production.
──[ 7. Ce Que Cette Série Ne Couvre Pas ]──
Par honnêteté :
La UI console est hors périmètre. Il y a un projet frontend séparé qui appelle
l'API du serveur ; il ne fait pas partie de ce codebase et n'est pas couvert
ici.
Le moteur de règles de détection est hors périmètre. Les endpoints `rules` du
serveur sont des stubs ; le langage de règle "Waza" référencé dans
l'`ARCHITECTURE.md` du serveur est un projet séparé sur une timeline séparée. La
collecte de télémétrie (cette série) et la génération d'alertes sont des couches
produit distinctes.
Le packaging en installeur MSI signé pour le déploiement d'entreprise est hors
périmètre. Les dépôts produisent des artefacts `.exe` et `.sys` ; les wrapper
dans un MSI qu'un administrateur d'entreprise peut déployer via Group Policy,
c'est une couche supplémentaire non couverte par ce codebase.
──[ fin de série ]──
Partie 0 ── Intro
Partie 1 ── Squelette de driver (KMDF en Rust)
Partie 2 ── Les cinq callbacks kernel
Partie 3 ── Wire format
Partie 4 ── Ring buffer et IOCTL en appel inversé
Partie 5 ── Agent user mode
Partie 6 ── Serveur (FastAPI + Postgres + OpenSearch + Redis)
Partie 7 ── Protocole plugin
Partie 8 ── SDK plugin
Partie 9 ── CLI de manifest
Partie 10 ── Defender Bridge
Partie 11 ── Packaging et déploiement ◀ vous êtes ici
L'intention de la série, c'était de dérouler une stack d'endpoint EDR
fonctionnelle depuis le kernel vers le haut, avec les décisions de design
exposées explicitement. Le codebase n'est pas feature-complet face à un EDR
commercial — il n'y a pas de console de production, pas de moteur de règles de
détection, pas de signature WHQL. Ce qu'il a, c'est un pipeline d'ingestion qui
fonctionne, chaque composant lisible par un développeur qui veut comprendre ce
que fait vraiment un EDR, et un ensemble de décisions documentées suffisamment
bien pour étendre sans deviner.
Les sept dépôts à lululufr/WazabiEDR_* sont la référence canonique. Chacun a un
`ARCHITECTURE.md` qui documente son module en profondeur. Cette série, c'était
la visite narrative cross-repo.