Concepts

Gestion des secrets

Sources, fournisseurs externes, chiffrement, portée par action et injection sécurisée.

Xbee sépare la configuration partageable des valeurs sensibles. Le fichier xbee-env.yaml décrit la forme du modèle et les actions ; le fichier xbee-secret.yaml, placé à côté, fournit les valeurs ou les références permettant de les récupérer.

Déclarer un secret

La structure de xbee-secret.yaml reprend les chemins du modèle à surcharger :

# xbee-secret.yaml
database:
  password: env:DATABASE_PASSWORD
tls:
  privateKey: file:secrets/server.key

Le fichier n’est pas destiné au contrôle de version. Xbee l’ajoute au .gitignore et force ses permissions à 0600.

Quatre formes locales sont acceptées :

FormeRésultat
une-valeurValeur littérale
env:NOMContenu de la variable d’environnement NOM
file:cheminContenu du fichier ; un chemin relatif part de xbee-secret.yaml
literal:env:NOMValeur littérale env:NOM, sans résolution

Les références sont paresseuses : leur valeur n’est récupérée que si l’action en cours utilise le secret. Une variable absente, un fichier inaccessible ou un fournisseur en erreur interrompt l’exécution concernée.

Utiliser un gestionnaire externe

Les credentials d’accès au gestionnaire restent sur la machine qui exécute xbee. Ils ne sont pas copiés dans le modèle, l’enveloppe ou le guest. Xbee appelle la CLI officielle correspondante et utilise son mécanisme d’authentification habituel.

HashiCorp Vault

Prérequis : la commande vault doit être installée et authentifiée, par exemple avec VAULT_ADDR, VAULT_TOKEN ou la méthode de connexion configurée localement.

Syntaxe :

vault:<chemin-kv>#<champ>

Exemple pour lire le champ password de secret/application :

# xbee-secret.yaml
database:
  password: vault:secret/application#password

Xbee exécute l’équivalent de :

vault kv get -field=password secret/application

Le fragment #champ est obligatoire. La version KV et le point de montage restent gérés par la configuration de la CLI Vault et par le chemin fourni.

AWS Secrets Manager

Prérequis : la commande aws doit être installée. Elle peut utiliser un profil local, des variables d’environnement, AWS SSO ou une identité de workload disponible sur la machine contrôleur.

Syntaxe :

aws-sm:<identifiant-ou-ARN>[?region=<région>][#<champ-json>]

Secret JSON stocké dans AWS Secrets Manager :

{"username":"app","password":"change-me"}

Référence au seul champ password :

# xbee-secret.yaml
database:
  username: aws-sm:production/database?region=eu-west-3#username
  password: aws-sm:production/database?region=eu-west-3#password

Sans fragment #, xbee retourne la chaîne SecretString complète. Un fragment peut désigner un chemin JSON imbriqué, par exemple #credentials.password. Les secrets binaires AWS ne sont pas pris en charge par cette syntaxe.

Scaleway Secret Manager

Prérequis : la commande scw doit être installée et configurée. Elle utilise son profil ou les variables SCW_ACCESS_KEY, SCW_SECRET_KEY et les valeurs par défaut de région/projet.

Syntaxe :

scw-sm:<secret-id>[?region=<région>&revision=<version>][#<champ>]

Exemple utilisant la dernière version disponible :

# xbee-secret.yaml
database:
  password: scw-sm:11111111-1111-1111-1111-111111111111?region=fr-par#password

Exemple épinglé sur la révision 4 :

database:
  password: scw-sm:11111111-1111-1111-1111-111111111111?region=fr-par&revision=4#password

region est facultatif si le profil Scaleway en définit une. revision vaut latest par défaut. Le fragment #champ est facultatif pour un secret opaque et permet de sélectionner un champ d’un secret JSON.

Injecter un secret dans une action

Un secret ne doit pas être interpolé directement dans shell.cmd : il apparaîtrait potentiellement dans la liste des processus ou dans un diagnostic. Xbee refuse cette forme :

# Refusé
shell: deploy --token {{ .api.token }}

Utilisez secret-env pour une variable temporaire :

shell:
  secret-env:
    API_TOKEN: "{{ .api.token }}"
  cmd:
    - deploy --token "$API_TOKEN"

Ou secret-file lorsqu’un programme attend un fichier :

shell:
  secret-file:
    - content: "{{ .tls.privateKey }}"
      to: /run/xbee-secrets/server.key
      mode: "0400"
  cmd:
    - server --private-key /run/xbee-secrets/server.key

secret-env utilise un fichier d’environnement temporaire en 0600. secret-file crée sa cible exclusivement, refuse d’écraser un fichier existant et n’accepte que des modes réservés au propriétaire. Xbee supprime ces fichiers à la fin de l’action, même si celle-ci échoue. Une valeur secrète rendue directement dans un template est refusée pour la même raison.

Cycle de vie interne

Le traitement suit les étapes suivantes :

  1. Xbee remplace chaque référence de xbee-secret.yaml par un jeton opaque.
  2. Avant une action, il détermine les jetons réellement utilisés et ne résout que leurs fournisseurs.
  3. Il crée une enveloppe propre à cette exécution, chiffrée et authentifiée avec AES-256-GCM et protégée en 0600.
  4. Une nouvelle clé aléatoire est transmise au guest par l’entrée standard SSH ou Docker. Elle n’est placée ni dans un argument, ni dans une variable d’environnement, ni dans un fichier.
  5. Le guest déchiffre les valeurs en mémoire, exécute l’action, puis supprime la clé, l’enveloppe et les matérialisations temporaires.

Si l’action dépend de données connues uniquement dans le guest, xbee utilise un repli conservateur limité au modèle de l’hôte concerné, ou au seul pack init. Ce repli est signalé dans les logs et n’inclut jamais les secrets d’un autre hôte. Un jeton resté hors de la portée autorisée bloque l’action.

Les valeurs sont enregistrées dans le mécanisme de masquage centralisé et apparaissent comme ******** dans les logs xbee. Les jetons sont normalisés dans les hashes : une rotation de secret ne change pas l’identité d’une image ou d’un pack.

Exemple complet

# xbee-secret.yaml
application:
  apiToken: vault:secret/production/api#token
database:
  password: aws-sm:production/database?region=eu-west-3#password
tls:
  privateKey: scw-sm:11111111-1111-1111-1111-111111111111?region=fr-par
# Extrait d'une action du pack
shell:
  secret-env:
    API_TOKEN: "{{ .application.apiToken }}"
    DATABASE_PASSWORD: "{{ .database.password }}"
  secret-file:
    - content: "{{ .tls.privateKey }}"
      to: /run/xbee-secrets/tls.key
      mode: "0400"
  cmd:
    - application migrate
    - application start --tls-key /run/xbee-secrets/tls.key

Seuls les trois secrets référencés par cette action sont récupérés et transmis. Une autre valeur présente dans xbee-secret.yaml mais inutilisée ne déclenche aucun appel à son fournisseur.

Rotation et exploitation

  • Une référence externe est relue pour chaque action qui l’utilise ; une rotation est donc visible lors de l’exécution suivante.
  • Chaque appel fournisseur est limité à 30 secondes.
  • Pour une rotation atomique sur plusieurs champs, préférez un unique objet JSON et épinglez une version lorsque le fournisseur le permet.
  • Accordez à l’identité du contrôleur uniquement la permission de lire les chemins nécessaires.
  • Évitez d’activer les traces verbeuses des CLI externes : elles sont hors du mécanisme de masquage des logs xbee.

Références externes : Vault KV, AWS Secrets Manager et Scaleway Secret Manager.

Diagnostic

SymptômeVérification
environment variable ... is not setExporter la variable sur la machine contrôleur
vault secret provider failedVérifier vault status, l’authentification et la politique de lecture
aws secret provider failedVérifier aws sts get-caller-identity, la région et secretsmanager:GetSecretValue
scw secret provider failedVérifier scw info, la région, la révision et les permissions IAM
secret field ... does not existVérifier le fragment #champ et la structure JSON
action references a secret outside its execution scopeVérifier que le secret est injecté avec secret-env ou secret-file