mcp-framework/docs/secrets.md

272 lines
6.8 KiB
Markdown
Raw Normal View History

# Secrets
Le package `secretstore` supporte plusieurs politiques de backend :
- `auto` : comportement par défaut, utilise un backend keyring disponible et peut retomber sur l'environnement si `LookupEnv` est fourni
- `kwallet-only` : impose KWallet et retourne une erreur explicite si KWallet n'est pas disponible
- `keyring-any` : impose l'utilisation d'un backend keyring disponible
- `env-only` : lecture seule depuis les variables d'environnement
- `bitwarden-cli` : utilise le CLI Bitwarden (`bw`) comme backend de vault
Backends keyring typiques :
- macOS : Keychain
- Linux : Secret Service ou KWallet selon l'environnement
- Windows : Credential Manager
Ouverture recommandée depuis la policy du manifeste runtime (`mcp.toml` résolu près de l'exécutable) :
```go
store, err := secretstore.OpenFromManifest(secretstore.OpenFromManifestOptions{
ServiceName: "my-mcp",
LookupEnv: os.LookupEnv,
})
if err != nil {
return err
}
```
Exemple bas niveau :
```go
store, err := secretstore.Open(secretstore.Options{
ServiceName: "my-mcp",
BackendPolicy: secretstore.BackendAuto,
})
if err != nil {
return err
}
if err := store.SetSecret("api-token", "My MCP API token", token); err != nil {
return err
}
token, err = store.GetSecret("api-token")
switch {
case err == nil:
// secret found
case errors.Is(err, secretstore.ErrNotFound):
// first run
default:
return err
}
```
Pour imposer KWallet sur Linux :
```go
store, err := secretstore.Open(secretstore.Options{
2026-05-02 10:07:34 +00:00
ServiceName: "my-mcp",
BackendPolicy: secretstore.BackendKWalletOnly,
})
```
Pour imposer Bitwarden via son CLI :
```go
store, err := secretstore.Open(secretstore.Options{
2026-05-02 10:07:34 +00:00
ServiceName: "my-mcp",
BackendPolicy: secretstore.BackendBitwardenCLI,
// Optionnel si `bw` n'est pas dans le PATH :
// BitwardenCommand: "/usr/local/bin/bw",
})
```
## Cache Bitwarden
Le backend `bitwarden-cli` met en cache les lectures de secrets par défaut.
Le cache mémoire évite les appels répétés au CLI dans un même process. Le cache
disque est chiffré avec une clé dérivée de `BW_SESSION` via HKDF-SHA256 et
AES-GCM.
TTL par défaut : 10 minutes.
Pour désactiver le cache dans `mcp.toml` :
```toml
[secret_store]
backend_policy = "bitwarden-cli"
bitwarden_cache = false
```
Pour le désactiver sans modifier le manifeste :
```bash
MCP_FRAMEWORK_BITWARDEN_CACHE=0
```
Le fichier de cache et le binaire installé ne suffisent pas à déchiffrer les
secrets. Si `BW_SESSION` change ou disparaît, les entrées disque existantes
deviennent inutilisables. Cette protection ne couvre pas un attaquant qui peut
lire l'environnement ou la mémoire du process pendant l'exécution.
Pour vérifier explicitement que Bitwarden est prêt (login + unlock + `BW_SESSION`) :
```go
if err := secretstore.EnsureBitwardenReady(secretstore.Options{
BitwardenCommand: "bw",
LookupEnv: os.LookupEnv,
}); err != nil {
switch {
case errors.Is(err, secretstore.ErrBWNotLoggedIn):
// guider vers `bw login`
case errors.Is(err, secretstore.ErrBWLocked):
// guider vers `bw unlock --raw` puis export BW_SESSION
default:
// indisponibilité CLI/réseau
}
return err
}
```
Pour lancer un flux interactif `bw login` / `bw unlock --raw`, récupérer `BW_SESSION`
et le persister localement (fichier `0600` sous le répertoire de config utilisateur) :
```go
session, err := secretstore.LoginBitwarden(secretstore.BitwardenLoginOptions{
2026-05-02 10:07:34 +00:00
ServiceName: "my-mcp",
Stdin: os.Stdin,
Stdout: os.Stdout,
Stderr: os.Stderr,
})
if err != nil {
return err
}
fmt.Println("session chargée:", len(session) > 0)
```
Pour réinjecter automatiquement une session persistée dans l'environnement courant :
```go
loaded, err := secretstore.EnsureBitwardenSessionEnv(secretstore.BitwardenSessionOptions{
2026-05-02 10:07:34 +00:00
ServiceName: "my-mcp",
})
if err != nil {
return err
}
fmt.Println("session restaurée depuis disque:", loaded)
```
Pour stocker un secret structuré en JSON :
```go
type Credentials struct {
Host string `json:"host"`
Username string `json:"username"`
Password string `json:"password"`
}
err = secretstore.SetJSON(store, "imap-credentials", "IMAP credentials", Credentials{
Host: "imap.example.com",
Username: "alice",
Password: token,
})
if err != nil {
return err
}
creds, err := secretstore.GetJSON[Credentials](store, "imap-credentials")
if err != nil {
return err
}
_ = creds
```
Pour écrire puis confirmer immédiatement une relecture :
```go
if err := secretstore.SetSecretVerified(store, "api-token", "My MCP API token", token); err != nil {
return err
}
```
Pour connaître le backend effectif utilisé :
```go
effective := secretstore.EffectiveBackendPolicy(store)
fmt.Println("backend effectif:", effective) // bitwarden-cli, env-only, keyring-any...
```
Pour obtenir en un seul appel une description runtime (source manifeste, policy déclarée/effective, disponibilité) :
```go
desc, err := secretstore.DescribeRuntime(secretstore.DescribeRuntimeOptions{
ServiceName: "my-mcp",
LookupEnv: os.LookupEnv,
})
if err != nil {
return err
}
fmt.Println(secretstore.FormatBackendStatus(desc))
// declared=... effective=... display=... ready=... source=...
```
Pour un préflight réutilisable dans `setup`, `config show` et `config test` :
```go
report, err := secretstore.PreflightFromManifest(secretstore.PreflightOptions{
ServiceName: "my-mcp",
LookupEnv: os.LookupEnv,
})
if err != nil {
return err
}
fmt.Println(report.Status) // ready | fail
fmt.Println(report.Summary) // message court
fmt.Println(report.Remediation) // action recommandée
```
## Debug Bitwarden en 60 secondes
Tu peux activer les traces d'appels Bitwarden avec le flag CLI global `--debug`
(via `bootstrap`) ou en exportant `MCP_FRAMEWORK_BITWARDEN_DEBUG=1`.
Les commandes `bw` exécutées seront affichées (avec redaction des payloads sensibles).
1. Vérifier l'état de session :
```bash
bw status
```
2. Déverrouiller le vault et exporter `BW_SESSION` (ou utiliser `LoginBitwarden`) :
- Bash/Zsh :
```bash
export BW_SESSION="$(bw unlock --raw)"
```
- Fish :
```fish
set -x BW_SESSION (bw unlock --raw)
```
- PowerShell :
```powershell
$env:BW_SESSION = (bw unlock --raw)
```
3. Vérifier lecture/écriture rapide :
```go
if err := store.SetSecret("debug-token", "Debug token", "ok"); err != nil {
return err
}
_, err := store.GetSecret("debug-token")
return err
```
4. Interpréter les erreurs typées :
- `secretstore.ErrBWNotLoggedIn` : `bw login` requis.
- `secretstore.ErrBWLocked` : vault verrouillé ou `BW_SESSION` absent.
- `secretstore.ErrBWUnavailable` : CLI/réseau indisponible.
- `secretstore.ErrBackendUnavailable` : policy non satisfiable dans le contexte courant.
En mode `env-only`, `GetSecret("API_TOKEN")` lit la variable d'environnement `API_TOKEN`.
Les opérations d'écriture et de suppression retournent `secretstore.ErrReadOnly`.