fix(agent): chiffre l'API key au repos (IDataProtector) #116

Merged
nonobis merged 1 commit from fix/agent-api-key-at-rest into main 2026-08-14 14:01:45 +02:00
Owner

Closes #21

L'API key de l'agent était persistée en clair dans agent-state.json (0600). Ce fix la chiffre au repos avec ASP.NET Core Data Protection (IDataProtector) :

  • AgentState.SaveAsync écrit apiKey chiffrée + marqueur apiKeyProtected: true (format camelCase, lecture case-insensitive pour compat)
  • Key ring Data Protection dans {StateDirectory}/dp-keys (0700), persisté pour survivre aux redémarrages
  • Migration automatique : les fichiers legacy en clair sont détectés au LoadAsync et réécrits chiffrés immédiatement
  • SaveAsync échoue si aucun protector n'est fourni — pas de fallback plaintext silencieux
  • Key ring non déchiffrable → état traité comme corrompu, l'agent se ré-enregistre (log warning)

Vérifications

  • dotnet build : 0 warning / 0 erreur (TreatWarningsAsErrors)
  • Tests Agent : 197/197 ✅ (dont 5 nouveaux : pas de clair sur disque, migration legacy, échec déchiffrement, fail-fast sans protector)
  • Suite complète : Domain 142, Application 391, Web 225, Api 2, Integration 125 ✅ (en isolation)
  • Échecs Infrastructure (24) : préexistants sur main (fixture Testcontainers PostgreSqlContainer — vérifié sur worktree baseline origin/main)

Prod hardening (option documentée dans le code)

IDataProtector protège contre la lecture du fichier d'état seul. Contre une compromission complète du volume (qui exposerait aussi le key ring), binder le key ring à un TPM/HSM ou le monter hors du volume sauvegardé.

Closes #21 L'API key de l'agent était persistée **en clair** dans `agent-state.json` (0600). Ce fix la chiffre au repos avec ASP.NET Core Data Protection (`IDataProtector`) : - `AgentState.SaveAsync` écrit `apiKey` chiffrée + marqueur `apiKeyProtected: true` (format camelCase, lecture case-insensitive pour compat) - Key ring Data Protection dans `{StateDirectory}/dp-keys` (0700), persisté pour survivre aux redémarrages - **Migration automatique** : les fichiers legacy en clair sont détectés au `LoadAsync` et réécrits chiffrés immédiatement - `SaveAsync` échoue si aucun protector n'est fourni — pas de fallback plaintext silencieux - Key ring non déchiffrable → état traité comme corrompu, l'agent se ré-enregistre (log warning) ## Vérifications - `dotnet build` : 0 warning / 0 erreur (TreatWarningsAsErrors) - Tests Agent : **197/197 ✅** (dont 5 nouveaux : pas de clair sur disque, migration legacy, échec déchiffrement, fail-fast sans protector) - Suite complète : Domain 142, Application 391, Web 225, Api 2, Integration 125 ✅ (en isolation) - Échecs Infrastructure (24) : **préexistants sur main** (fixture Testcontainers `PostgreSqlContainer` — vérifié sur worktree baseline origin/main) ## Prod hardening (option documentée dans le code) IDataProtector protège contre la lecture du fichier d'état seul. Contre une compromission complète du volume (qui exposerait aussi le key ring), binder le key ring à un TPM/HSM ou le monter hors du volume sauvegardé.
fix(agent): chiffre l'API key au repos (IDataProtector)
Some checks failed
CI / ci (pull_request) Failing after 11m19s
9f3f153ac8
L'API key était persistée en clair dans agent-state.json (0600), exposée
en cas de lecture du fichier (root, backup, volume). Elle est désormais
chiffrée au repos via ASP.NET Core Data Protection (IDataProtector),
key ring dans {StateDirectory}/dp-keys (0700, persisté pour survivre aux
redémarrages). Les fichiers legacy en clair sont migrés automatiquement
au premier LoadAsync (réécriture chiffrée). SaveAsync échoue si aucun
protector n'est fourni : pas de fallback plaintext silencieux.
Résout #21.
nonobis deleted branch fix/agent-api-key-at-rest 2026-08-14 14:01:46 +02:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
nonobis/Heimdall!116
No description provided.