2026-04-15 12:06:28 +00:00
|
|
|
# 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
|
2026-04-20 06:30:35 +00:00
|
|
|
- `bitwarden-cli` : utilise le CLI Bitwarden (`bw`) comme backend de vault
|
2026-04-15 12:06:28 +00:00
|
|
|
|
|
|
|
|
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",
|
2026-04-15 12:06:28 +00:00
|
|
|
BackendPolicy: secretstore.BackendKWalletOnly,
|
|
|
|
|
})
|
|
|
|
|
```
|
|
|
|
|
|
2026-04-20 06:30:35 +00:00
|
|
|
Pour imposer Bitwarden via son CLI :
|
|
|
|
|
|
|
|
|
|
```go
|
|
|
|
|
store, err := secretstore.Open(secretstore.Options{
|
2026-05-02 10:07:34 +00:00
|
|
|
ServiceName: "my-mcp",
|
2026-04-20 06:30:35 +00:00
|
|
|
BackendPolicy: secretstore.BackendBitwardenCLI,
|
|
|
|
|
// Optionnel si `bw` n'est pas dans le PATH :
|
|
|
|
|
// BitwardenCommand: "/usr/local/bin/bw",
|
|
|
|
|
})
|
|
|
|
|
```
|
|
|
|
|
|
2026-04-20 07:39:05 +00:00
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2026-04-20 10:38:58 +00:00
|
|
|
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",
|
2026-04-20 10:38:58 +00:00
|
|
|
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",
|
2026-04-20 10:38:58 +00:00
|
|
|
})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
fmt.Println("session restaurée depuis disque:", loaded)
|
|
|
|
|
```
|
|
|
|
|
|
2026-04-15 12:06:28 +00:00
|
|
|
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
|
|
|
|
|
```
|
|
|
|
|
|
2026-04-20 07:39:05 +00:00
|
|
|
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...
|
|
|
|
|
```
|
|
|
|
|
|
2026-04-20 08:56:15 +00:00
|
|
|
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
|
|
|
|
|
|
2026-04-20 09:36:07 +00:00
|
|
|
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).
|
|
|
|
|
|
2026-04-20 08:56:15 +00:00
|
|
|
1. Vérifier l'état de session :
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
bw status
|
|
|
|
|
```
|
|
|
|
|
|
2026-04-20 10:38:58 +00:00
|
|
|
2. Déverrouiller le vault et exporter `BW_SESSION` (ou utiliser `LoginBitwarden`) :
|
2026-04-20 08:56:15 +00:00
|
|
|
|
|
|
|
|
- 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.
|
|
|
|
|
|
2026-04-15 12:06:28 +00:00
|
|
|
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`.
|