Aller au contenu principal

Secrets et variables d’environnement

Les secrets sont des valeurs chiffrées injectées dans les jobs de workflow sous forme de variables d’environnement. Ils sont en écriture seule une fois créés : la valeur n’est jamais relisible via l’interface ou l’API. Cette page couvre les portées, la création, l’utilisation dans les workflows, la rotation et les limites.

Les secrets sont chiffrés au repos avec T Cloud Public DEW KMS et ne sont déchiffrés qu’au moment de l’exécution du job, sur le runner.

Les secrets au niveau de l’organisation se gèrent dans l’administration de l’organisation, à l’adresse frem.sh/<org>/_admin/secrets. Les secrets au niveau du dépôt se trouvent dans Paramètres → Actions → Secrets de chaque dépôt, dans Forgejo. Les environnements, avec leurs secrets, variables, règles de branches et approbateurs, se gèrent dans l’administration de l’organisation, à l’adresse frem.sh/<org>/_admin/environments. fremforge remet à un job les secrets de son environnement au moment où le job est confié à un runner, et c’est là qu’il applique l’ordre de priorité (environnement, puis dépôt, puis organisation).

Portées

PortéeOù la définirDisponible pour
Secret d’organisationAdministration de l’organisation → SecretsTous les dépôts de l’organisation
Secret de dépôtDépôt → Paramètres → SecretsCe dépôt uniquement
Secret CI à portéeAPI /orgs/<org>/ci-secrets (page d’administration de l’organisation : à venir)Les jobs dont le workflow, le job, la branche ou le tag, l’événement et l’environnement sont autorisés par la portée du secret, et seulement lorsque le job y fait référence (ci-dessous)
Secret d’environnementAdministration de l’organisation → Environnements → <env>Les jobs qui déclarent environment: <env>, sur une branche ou un tag autorisé par l’environnement, et uniquement les secrets auxquels le job fait référence (ci-dessous)

Priorité : un secret d’environnement l’emporte sur un secret à portée lié à un dépôt, qui l’emporte sur un secret à portée valable pour toute l’organisation, qui l’emporte sur un secret de dépôt, qui l’emporte sur un secret d’organisation. Lorsque le même nom existe à plusieurs niveaux, c’est la portée la plus spécifique qui l’emporte.

Créer des secrets

Via l’interface : rendez-vous dans la portée voulue, descendez jusqu’à Ajouter ou mettre à jour un secret, saisissez le nom (convention UPPER_SNAKE_CASE) et la valeur, puis cliquez sur Enregistrer le secret.

Via l’API :

# Org secret
curl -X PUT \
  -H "Authorization: Bearer <PAT>" \
  -H "Content-Type: application/json" \
  -d '{"name":"MY_SECRET","data":"the-value"}' \
  https://frem.sh/_app/api/v1/orgs/<org>/actions/secrets/MY_SECRET

# Repo secret
curl -X PUT \
  -H "Authorization: Bearer <PAT>" \
  -H "Content-Type: application/json" \
  -d '{"name":"MY_SECRET","data":"the-value"}' \
  https://frem.sh/api/v1/repos/<org>/<repo>/actions/secrets/MY_SECRET

Les secrets sont en écriture seule une fois créés. Vous ne pouvez pas relire la valeur d’un secret via l’API ou l’interface. Vous pouvez seulement l’écraser ou le supprimer.

Utiliser des secrets dans les workflows

Faites référence aux secrets avec ${{ secrets.NAME }}. Les secrets sont disponibles sous forme de variables d’environnement.

env:
  DATABASE_URL: ${{ secrets.DATABASE_URL }}

steps:
  - name: Deploy
    env:
      API_KEY: ${{ secrets.DEPLOY_API_KEY }}
    run: ./deploy.sh

Les secrets sont masqués dans les journaux : toute sortie de journal qui correspond exactement à la valeur d’un secret est remplacée par ***. Le masquage ne s’applique qu’aux correspondances exactes ; les variantes de la valeur encodées en base64, encodées pour une URL ou transformées d’une autre manière ne sont pas masquées automatiquement.

Secrets d’environnement et contrôles de déploiement

Les jobs qui déclarent environment: ne reçoivent que les secrets propres à cet environnement (en plus des secrets de dépôt et d’organisation). C’est voulu : les secrets d’environnement placent les identifiants de production derrière le processus d’approbation de l’environnement.

jobs:
  deploy-prod:
    runs-on: fremforge
    environment: production   # triggers approval gate if reviewers are configured
    steps:
      - name: Deploy
        env:
          DB_URL: ${{ secrets.PROD_DB_URL }}   # environment secret on 'production'
        run: ./deploy.sh

Configurez les approbateurs requis sous Administration de l’organisation → Environnements → <env>. Le job se met en pause dans l’attente d’une approbation avant l’exécution de l’étape ; le secret n’est injecté qu’une fois le job approuvé et en cours d’exécution.

Les noms d’environnements sont comparés sans tenir compte de la casse, comme dans Forgejo et GitHub : environment: Production désigne l’environnement production, avec sa règle de branches, ses approbateurs et ses secrets. Si deux environnements ne diffèrent que par la casse, c’est celui dont l’orthographe correspond exactement à la déclaration du job qui est utilisé ; si aucun ne correspond exactement, le job ne reçoit aucun environnement et son contrôle de déploiement le refuse (environment_case_ambiguous). Renommez l’un des deux.

Une exécution pull_request_target issue d’un fork ne reçoit ni environnement, ni ses secrets, ni son claim OIDC, quoi que déclare le job. C’est plus strict que GitHub. Voir Portée des secrets pour les pull requests de forks.

Les secrets d’environnement n’atteignent que les jobs qui y font référence

Depuis le 2026-10-26, un job ne reçoit un secret d’environnement que s’il y fait référence : dans ses propres étapes, env:, with: ou if:, ou dans le env: au niveau du workflow, sous la forme ${{ secrets.NAME }} ou ${{ secrets['NAME'] }}. Un secret auquel le job ne fait pas référence n’est pas du tout envoyé à son runner : il n’est donc pas présent sur la machine où s’exécute le job.

${{ toJSON(secrets) }}, un nom calculé comme ${{ secrets[format('{0}_KEY', 'DEPLOY')] }} et secrets: inherit sur un appel de workflow réutilisable font référence à tous les secrets de l’environnement, comme sur GitHub.

Une étape ne peut lire un secret qu’au travers d’une telle expression : un workflow qui fonctionne aujourd’hui continue donc de fonctionner. Le changement retire seulement des secrets qu’aucune étape ne pouvait lire.

Si fremforge ne peut pas lire le fichier de workflow du job au commit du job, ou ne trouve pas le job dans ce fichier, le job ne reçoit aucun des secrets de l’environnement. Le job s’exécute quand même, avec son jeton OIDC et ses variables.

Qui a reçu quel secret : l’audit des lectures

Chaque job CI qui reçoit un secret d’environnement ou un secret à portée, ou qui fait référence à un secret qui lui est refusé, écrit une entrée dans le journal d’audit de l’organisation, avec l’action ci.secret.read :

ChampValeur
acteurla personne qui a lancé l’exécution (l’organisation, pour une exécution planifiée)
repository, repository_id, workflow, joboù le job s’est exécuté
run_id, run_number, run_attempt, ref, eventquelle exécution
environmentl’environnement déclaré par le job
secretsles NOMS reçus par le job, chacun avec sa portée (environment, repo ou org)
withheldles noms auxquels le job a fait référence mais qui n’ont pas pu lui être remis, chacun avec un motif : workflow-unreadable ou job-not-found, et pour un secret à portée, le champ de portée qui l’a refusé (scope-workflow, scope-job, scope-ref, scope-event, scope-environment) ou undecryptable

La valeur d’un secret n’est jamais enregistrée, pas plus que son empreinte. Un job qui ne fait référence à aucun des secrets de l’environnement n’en reçoit aucun et n’écrit pas d’entrée.

L’entrée est écrite avant que les secrets soient remis au runner. Si elle ne peut pas être enregistrée, le job n’en reçoit aucun : aucun secret ne quitte fremforge sans être consigné. L’astreinte de fremforge est alertée lorsque cela se produit.

Interrogez les entrées avec l’API :

curl -H "Authorization: Bearer $FREMFORGE_TOKEN" \
  "https://frem.sh/_app/api/v1/orgs/<org>/audit?action=ci.secret.read&since=2026-10-26"

Les entrées font partie de la chaîne de hachage du journal d’audit, de sa rétention et de son flux SIEM, comme toutes les autres entrées d’audit.

Les secrets de dépôt et d’organisation définis dans Forgejo ne sont pas couverts : Forgejo les remet toujours à tous les jobs du dépôt, et leurs lectures ne sont pas consignées. Utilisez plutôt un secret CI à portée, et supprimez la copie Forgejo portant le même nom : tant qu’elle existe, elle atteint toujours les jobs que la portée refuse.

Secrets CI à portée

Un secret CI à portée est conservé par fremforge, pour toute l’organisation ou pour un seul dépôt, et n’est remis à un job que si chacun des champs de portée que vous avez définis correspond (au sein d’un champ, n’importe laquelle des valeurs listées peut correspondre) :

ChampCorrespond àExemple
workflowsle nom du fichier de workflowdeploy.yaml, release-*.yml
jobsl’identifiant du job (la clé sous jobs:)deploy, publish-*
refsla branche ou le tagrefs/heads/main, refs/tags/v*
eventsle déclencheur du workflowpush, workflow_dispatch, schedule, pull_request, pull_request_target, release
environmentle job déclare cet environnement, sur une branche ou un tag que cet environnement autoriseproduction

* est le seul caractère générique. Un champ que vous omettez accepte tout, à une exception près : si aucun events n’est listé, une exécution sur la référence d’une pull request (refs/pull/...) ne reçoit jamais le secret. Pour remettre un secret à portée au code d’une pull request, listez explicitement pull_request (ou pull_request_target).

Le job doit aussi faire référence au secret (comme ci-dessus). Chaque remise, et chaque refus d’un secret auquel le job a fait référence, est consigné dans l’entrée ci.secret.read du job. Une pull request issue d’un fork n’en reçoit jamais.

Quels champs constituent une frontière de sécurité. Toute personne qui peut pousser vers le dépôt peut créer une branche dont le workflow contient un job nommé deploy dans un fichier nommé deploy.yaml, et le déclencher. workflows et jobs limitent donc les chemins de code qui reçoivent normalement le secret, mais ne le protègent pas des personnes ayant un accès en écriture. refs (en le faisant pointer vers des branches ou tags protégés) et environment (avec une règle de branches et des approbateurs requis) le protègent. C’est la même chose que sur GitHub.

Une exécution de la file de fusion (refs/heads/fremforge-queue/...) est un build du même dépôt mis en file par un mainteneur, et une portée refs vide l’accepte, comme n’importe quelle branche. Restreignez refs si un secret ne doit pas atteindre les exécutions de la file de fusion.

Les jobs d’un workflow réutilisable et, sur une version de Forgejo qui n’envoie pas encore l’identifiant du job, les jobs qui ont leur propre name: ou une matrice, ne reçoivent pas encore de secrets à portée.

Créez-en un ou remplacez-le (la valeur est en écriture seule ; chaque modification est auditée, avec la portée qu’elle a remplacée) :

curl -X PUT -H "Authorization: Bearer $FREMFORGE_TOKEN" -H 'Content-Type: application/json' \
  -d '{"value":"...","workflows":["deploy.yaml"],"jobs":["deploy"],"refs":["refs/heads/main"],"events":["push","workflow_dispatch"]}' \
  "https://frem.sh/_app/api/v1/orgs/<org>/ci-secrets/DEPLOY_KEY?repository_id=<forgejo repository id>"

Omettez repository_id pour un secret valable pour toute l’organisation. Un champ inconnu, un environment qui n’est pas un nom et une entrée de liste vide sont refusés (400) plutôt qu’ignorés : une faute de frappe ne peut donc jamais étendre un secret à tous les jobs. GET /orgs/<org>/ci-secrets liste les noms, les portées et la date de dernière lecture de chacun ; DELETE .../ci-secrets/<NAME>[?repository_id=] en supprime un. Scopes : ci-secrets:read, ci-secrets:write. Limites : 500 par organisation, 100 par dépôt, 20 motifs par champ, 64 Ko par valeur.

Pour gérer les secrets CI à portée sous forme de code, utilisez fremforge_ci_secret dans le provider fremforge (à partir de 0.9.0). Sa valeur est un argument en écriture seule et n’atteint jamais le state Terraform.

Rotation des secrets

  1. Créez d’abord le nouvel identifiant dans le système externe.
  2. Écrasez le secret dans fremforge avec la nouvelle valeur. Ne le supprimez pas pour le recréer.
  3. Déclenchez une nouvelle exécution de workflow pour vérifier que le nouvel identifiant fonctionne de bout en bout.
  4. Révoquez l’ancien identifiant dans le système externe.

Écraser le secret préserve la piste d’audit et évite une fenêtre pendant laquelle le secret manquerait aux jobs exécutés entre la suppression et la recréation.

Limites

ÉlémentLimite
Taille maximale de la valeur d’un secret64 Ko
Secrets d’organisation1 000
Secrets de dépôt100 par dépôt
Secrets d’environnement100 par environnement
Secrets CI à portée500 par organisation, 100 par dépôt
Format du nom d’un secretCaractères alphanumériques et tiret bas ; doit commencer par une lettre ; ne peut pas commencer par FORGEJO_ ni par GITHUB_

OIDC comme alternative aux secrets

Pour déployer vers des fournisseurs cloud (T Cloud Public, AWS, Azure, GCP), envisagez la fédération de jetons OIDC plutôt que des identifiants à longue durée de vie stockés comme secrets. OIDC délivre des jetons à courte durée de vie pour chaque job, limités à l’exécution de workflow concernée. Rien à faire tourner, rien qui puisse fuiter.

OIDC est la méthode recommandée pour tout job qui déploie vers une infrastructure cloud.

Voir aussi