Secrets
Les pipelines ont besoin d’identifiants : un mot de passe de registre, une clé de déploiement, un jeton pour une API. Dans un programme, ce sont des valeurs de type Secret, données sous forme de localisateurs plutôt que de valeurs. Vous choisissez où chacun est conservé :
- De votre côté, dans l’environnement de la machine qui exécute le moteur (
env:MELODIUM_SECRET_<NOM>), dans un fichier (file:<chemin>), ou dans un serveur Vault ou OpenBao (vault:<chemin>#<champ>). Cadence.CI ne reçoit jamais ces valeurs, seulement le nom et le type du secret. - Sur Cadence.CI, enregistrés pour votre organisation et lus par vos runs avec
api:<nom>. Les valeurs sont chiffrées, et personne ne les relit : les membres les écrivent, les runs les lisent.
Enregistrer des secrets
La page Secrets du tableau de bord, sous Ressources, liste les secrets de l’organisation.
| Administrateur | Opérateur | Membre | |
|---|---|---|---|
| Voir les noms | ✓ | ✓ | ✓ |
| Voir descriptions, versions, règles et accès | ✓ | ✓ | |
| Voir les secrets qu’un run a utilisés, sur sa page | ✓ | ✓ | ✓ |
| Ajouter un secret, donner une nouvelle valeur | ✓ | ✓ | |
| Modifier la description, autoriser les runs publics, supprimer | ✓ | ||
| Ajouter et supprimer des règles | ✓ |
- Les noms sont faits de lettres, chiffres,
_,.et-, en parties séparées par/, commeprod/db-password, 200 caractères au plus. Les noms sousplatform/sont réservés aux secrets que Cadence.CI utilise lui-même, comme les clés d’un compte cloud connecté ou d’un bucket, ou le jeton d’un add-on Clever Cloud. Les runs ne les lisent jamais, ils ne comptent pas dans le plan, et chacun se gère là où il sert. - Les valeurs sont du texte, ou un fichier pour les valeurs binaires, 64 Kio au plus. Une valeur est envoyée une fois et n’est plus jamais affichée, à personne. Une nouvelle valeur devient une nouvelle version, que les runs lisent dès lors.
- Les noms ne sont pas secrets. Un programme nomme les secrets qu’il lit, et les programmes sont affichés avec leurs runs, à tout le monde pour les runs publics. Ne mettez jamais rien de confidentiel dans un nom.
- Les plans permettent 50 secrets en Basic et sur le plan open source, 500 en Pro, et 10 sans plan. La page des tarifs montre ces limites telles que Cadence.CI les applique.
Les valeurs sont chiffrées en AES-256-GCM, chaque version avec sa propre clé, sous des clés conservées hors de la base de données. Supprimer un secret efface ses valeurs.
Règles
Les règles décident quels runs lisent et écrivent quels secrets. Une règle s’applique aux secrets nommés par son préfixe ou situés dessous : prod couvre prod et prod/db, pas production, et un préfixe vide couvre tous les secrets. Toutes les règles correspondantes s’appliquent :
- Tags requis : un run ne lit un secret correspondant que s’il porte tous ces tags, de même que les runs qui l’ont lancé.
- Modifiable par les exécutions : les runs portant les tags peuvent enregistrer les secrets correspondants.
Sans règle, tous les runs privés de l’organisation lisent tous les secrets, et aucun run n’en enregistre.
Les tags sont affichés sur la page du run. Un run lancé avec --api-report porte entrypoint=<nom> et les tags listés, séparés par des espaces, dans la variable d’environnement MELODIUM_API_TAGS ; les étapes qu’il distribue portent les tags que leur donne son programme. Comme les tags des runs qui ont lancé une étape comptent aussi, une étape n’obtient jamais plus que le run qui l’a distribuée.
Les accès de chaque secret, sur la page Secrets, listent ses lectures, écritures et refus, avec le run ou le membre et la raison d’un refus. Le journal des accès, sous la liste, rassemble ceux de tous les secrets, du plus récent au plus ancien, et les filtre par type ; les accès restent après la suppression de leur secret, sous le nom qu’il avait. Les valeurs n’apparaissent dans aucun des deux.
La page d’un run liste les secrets qu’il a lus, écrits ou s’est vu refuser, du plus ancien au plus récent, par leur nom seulement, avec la raison de chaque refus, que le run a aussi reçue dans ses logs. Tous les membres de l’organisation la voient.
L’équipe Cadence.CI peut lire les noms de vos secrets et leurs accès pour vous aider, jamais leurs valeurs, et ne modifie rien.
Lire des secrets dans les programmes
Sur un worker Cadence.CI, un paramètre Secret prend un localisateur api: comme n’importe quel autre :
use sql::SqlPool
use std/ops/option::|wrap
treatment database(const password: Secret<string> = "api:prod/db-password")
model pool: SqlPool(url="postgresql://app@db.example.com/app", password=|wrap<Secret<string>>(password))
{
}Le moteur lit la valeur sur Cadence.CI avec le jeton de la machine qui l’exécute, que seul ce run détient. Il garde la valeur jusqu’à sa fin, et ne la révèle qu’à l’élément qui l’utilise : ici SqlPool, à la connexion. Comme pour tout secret, la valeur est masquée dans les logs, et n’apparaît jamais dans les paramètres, les événements de debug ou les rapports. Avec --check-secrets, le moteur lit les secrets donnés en paramètres au démarrage, pour qu’un secret manquant ou un refus fasse échouer le run tout de suite.
Un refus donne sa raison, comme le tag qui manque à un run, jamais une valeur.
Le chapitre Secrets du Mélodium Language Book présente les valeurs Secret, les localisateurs, et les éléments qui les révèlent.
Depuis les runners GitHub et GitLab
Un programme lancé dans un job GitHub ou GitLab s’exécute hors de Cadence.CI, où les secrets api: sont refusés. Les étapes qu’il distribue s’exécutent sur des workers Cadence.CI, qui les lisent : donnez les secrets à ces étapes par référence, pour que seul le localisateur quitte le job.
use std/ops/option::|unwrap_or
use std/secret::|locate
treatment main()
{
// Envoyé par référence : le worker Cadence.CI qui exécute l'étape le lit.
deploy: deploy(token=|unwrap_or<Secret<string>>(
|locate<string>("api:prod/deploy-token", "deploy_token", "reference", false),
"api:prod/deploy-token"
))
}Ici deploy représente une étape que votre programme distribue, avec un paramètre Secret<string>. |locate donne la transmission explicitement, et rien pour un localisateur invalide, d’où |unwrap_or. Un secret écrit comme simple localisateur ne quitte jamais le moteur qui le résout : donné à une étape qui s’exécute ailleurs, il est refusé avec une erreur explicite. Voir Distribution dans le book.
Secrets des conteneurs
Les conteneurs d’une étape démarrent avant le moteur de sa machine : leurs identifiants de registre (pull_secret) et les variables secrètes des conteneurs de service (secret_env de |service_container) voyagent avec la demande de machine. Donnez-les avec |locate et la transmission reference : un secret enregistré sur Cadence.CI part alors par son nom, et Cadence.CI remet sa valeur à la machine quand elle démarre.
use cicd/runners::CicdDispatchEngine
use cicd/naive::simpleStep
use process/command::|command
use std/data/map::Map
use std/data/map::|map as |secret_map
use std/data/map::|entry as |secret_entry
use std/data/string_map::StringMap
use std/data/string_map::|map
use std/data/string_map::|entry
use std/ops/option::|wrap
use std/secret::|locate
use work/resources::|service_container
treatment test[dispatcher: CicdDispatchEngine]()
input trigger: Block<void>
output success: Block<void>
{
simpleStep[dispatcher=dispatcher](
name = "test",
image = "registry.example.com/app/test:latest",
pull_secret = |locate<string>("api:registry/docker-config", "registry", "reference", false),
service_containers = [
|service_container(
"postgres", 1024, 1000, 2048, _, [], "postgres:16", _,
|wrap<StringMap>(|map([|entry("POSTGRES_USER", "ci")])),
|wrap<Map>(|secret_map([
|secret_entry<Option<Secret<string>>>("POSTGRES_PASSWORD", |locate<string>("api:ci/postgres-password", "postgres_password", "reference", false))
])),
_
)
],
commands = [|command("sh", ["-c", "make test"])]
)
Self.trigger -> simpleStep.trigger
simpleStep.success -> Self.success
}Le run est refusé avant de démarrer, en nommant le secret, quand un secret qu’il nomme n’existe pas ou que les règles ne le lui permettent pas, et chaque lecture figure parmi les accès du secret. Un simple localisateur reste sur la machine qui le résout, et il est refusé pour les conteneurs. Les secrets gardés de votre côté partent par valeur, avec la transmission value.
Enregistrer des secrets depuis les programmes
Les programmes qui produisent des identifiants, comme un mot de passe de base de données généré ou la clé d’un serveur qu’ils ont créé, les enregistrent pour les runs suivants avec work/secret::store, ou storeBytes pour les valeurs binaires, plutôt que de les mettre dans des artefacts. Une règle marquée Modifiable par les exécutions doit correspondre au nom :
use std/engine/util::startup
use std/engine/log::logError
use crypto/random::token
use work/secret::store
// Génère un mot de passe et l'enregistre sur Cadence.CI, pour les runs suivants.
treatment main()
{
startup()
generate: token(name="db_password")
storePassword: store(name="generated/db-password")
logGenerateError: logError(label="generate")
logStoreError: logError(label="store")
startup.trigger -> generate.trigger,token -> storePassword.secret
generate.error -> logGenerateError.message
storePassword.error -> logStoreError.message
}La valeur n’est révélée que pour être envoyée à Cadence.CI. Chaque enregistrement ajoute une version qui garde trace du run.
Runs publics
Les runs d’une organisation qui détient le plan open source sont publics : n’importe qui peut lire leurs logs et leurs programmes.
- Un run public ne lit un secret que si un Administrateur l’a marqué Lisible par les exécutions publiques. Ne l’autorisez que pour des valeurs destinées aux runs publics, comme un jeton limité à la publication.
- Un run public n’enregistre jamais de secret.
- Les noms des secrets qu’un run public lit apparaissent dans son programme.
- Les valeurs restent masquées dans les logs, comme dans tout run.
- Les jetons d’identité des runs publics ont un sujet à part, que les relations de confiance établies pour l’organisation n’acceptent pas.
Pour aller plus loin
- Identité des runs : faire confiance à vos runs depuis vos comptes cloud et Vault, sans conserver de clés.
- Clés : leur format, et ce qui se passe quand l’un fuit.
- Ce qui passe par Cadence.CI.