mcp-framework/docs/secrets.md

6.8 KiB

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) :

store, err := secretstore.OpenFromManifest(secretstore.OpenFromManifestOptions{
	ServiceName: "my-mcp",
	LookupEnv:   os.LookupEnv,
})
if err != nil {
	return err
}

Exemple bas niveau :

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 :

store, err := secretstore.Open(secretstore.Options{
	ServiceName:   "my-mcp",
	BackendPolicy: secretstore.BackendKWalletOnly,
})

Pour imposer Bitwarden via son CLI :

store, err := secretstore.Open(secretstore.Options{
	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 :

[secret_store]
backend_policy = "bitwarden-cli"
bitwarden_cache = false

Pour le désactiver sans modifier le manifeste :

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) :

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) :

session, err := secretstore.LoginBitwarden(secretstore.BitwardenLoginOptions{
	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 :

loaded, err := secretstore.EnsureBitwardenSessionEnv(secretstore.BitwardenSessionOptions{
	ServiceName: "my-mcp",
})
if err != nil {
	return err
}
fmt.Println("session restaurée depuis disque:", loaded)

Pour stocker un secret structuré en JSON :

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 :

if err := secretstore.SetSecretVerified(store, "api-token", "My MCP API token", token); err != nil {
	return err
}

Pour connaître le backend effectif utilisé :

effective := secretstore.EffectiveBackendPolicy(store)
fmt.Println("backend effectif:", effective) // bitwarden-cli, env-only, keyring-any...

Pour obtenir en un seul appel une description runtime légère (source manifeste, policy déclarée/effective, backend affiché) :

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=...

DescribeRuntime ne contacte pas Bitwarden par défaut. Pour vérifier réellement la disponibilité du backend, utiliser le préflight :

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 :
bw status
  1. Déverrouiller le vault et exporter BW_SESSION (ou utiliser LoginBitwarden) :
  • Bash/Zsh :
export BW_SESSION="$(bw unlock --raw)"
  • Fish :
set -x BW_SESSION (bw unlock --raw)
  • PowerShell :
$env:BW_SESSION = (bw unlock --raw)
  1. Vérifier lecture/écriture rapide :
if err := store.SetSecret("debug-token", "Debug token", "ok"); err != nil {
	return err
}
_, err := store.GetSecret("debug-token")
return err
  1. 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.