fix(agent): chiffre l'API key au repos (IDataProtector) #116
No reviewers
Labels
No labels
bug
chore
design
dx:ci
dx:database
dx:dotnet
dx:infra
dx:security
dx:tests
dx:ui
etape-1
etape-2
etape-3
etape-4
etape-5
etape-6
etape-7
feature
priority-high
priority-low
priority-medium
security
spec
tech-debt
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
nonobis/Heimdall!116
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/agent-api-key-at-rest"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
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écritapiKeychiffrée + marqueurapiKeyProtected: true(format camelCase, lecture case-insensitive pour compat){StateDirectory}/dp-keys(0700), persisté pour survivre aux redémarragesLoadAsyncet réécrits chiffrés immédiatementSaveAsyncéchoue si aucun protector n'est fourni — pas de fallback plaintext silencieuxVérifications
dotnet build: 0 warning / 0 erreur (TreatWarningsAsErrors)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é.
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.