Naar de hoofdinhoud

Secrets en omgevingsvariabelen

Geheimen zijn versleutelde waarden die als omgevingsvariabelen in workflowjobs worden geïnjecteerd. Na het aanmaken kun je ze alleen nog schrijven. De waarde is nooit meer uit te lezen via de interface of de API. Deze pagina behandelt bereiken, aanmaken, gebruik in workflows, rotatie en limieten.

Secrets worden at rest versleuteld met T Cloud Public DEW KMS en pas op het moment dat de job wordt uitgevoerd op de runner ontsleuteld.

Secrets op organisatieniveau beheer je in het organisatiebeheer op frem.sh/<org>/_admin/secrets. Secrets op repositoryniveau staan in Forgejo onder Instellingen → Actions → Geheimen van elke repository. Omgevingen, met hun secrets, variabelen, branchregels en reviewers, beheer je in het organisatiebeheer op frem.sh/<org>/_admin/environments. fremforge geeft een job de secrets van zijn omgeving mee wanneer de job aan een runner wordt toegewezen, en past daar de voorrangsvolgorde toe (omgeving, dan repository, dan organisatie).

Bereiken

BereikWaar in te stellenBeschikbaar voor
Secret van de organisatieOrganisatiebeheer → SecretsAlle repository’s in de organisatie
Secret van de repositoryRepository-instellingen → GeheimenAlleen die repository
Afgebakend CI-secretAPI /orgs/<org>/ci-secrets (pagina in het organisatiebeheer: komt eraan)Jobs waarvan de workflow, job, branch of tag, het event en de omgeving binnen het bereik van het secret vallen, en alleen als de job ernaar verwijst (hieronder)
Secret van een omgevingOrganisatiebeheer → Omgevingen → <env>Jobs die environment: <env> declareren, op een branch of tag die de omgeving toestaat, en alleen de secrets waarnaar de job verwijst (hieronder)

Voorrang: een secret van een omgeving gaat vóór een afgebakend secret dat aan een repository is gebonden, dat vóór een afgebakend secret voor de hele organisatie gaat, dat vóór een secret van de repository gaat, dat weer vóór een secret van de organisatie gaat. Bestaat dezelfde naam op meerdere niveaus, dan wint het meest specifieke bereik.

Secrets aanmaken

Via de interface: ga naar het juiste bereik, scrol naar Secret toevoegen of bijwerken, vul de naam in (volgens de conventie UPPER_SNAKE_CASE) en de waarde, en klik op Secret opslaan.

Via de 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

Na het aanmaken kun je secrets alleen nog schrijven. Je kunt de waarde van een secret niet uitlezen via de API of de interface. Je kunt hem alleen overschrijven of verwijderen.

Secrets gebruiken in workflows

Verwijs naar secrets met ${{ secrets.NAME }}. Secrets zijn beschikbaar als omgevingsvariabelen.

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

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

Secrets worden in logs gemaskeerd: elke logregel die exact overeenkomt met de waarde van een secret, wordt vervangen door ***. Maskering werkt alleen bij exacte overeenkomsten; varianten van de waarde die met base64 of URL-codering zijn gecodeerd of op een andere manier zijn omgezet, worden niet automatisch gemaskeerd.

Secrets van omgevingen en deploymentpoorten

Jobs die environment: declareren, krijgen alleen de secrets die aan die omgeving zijn gekoppeld (plus de secrets van de repository en de organisatie). Dat is bewust zo: secrets van een omgeving zetten inloggegevens voor productie achter de goedkeuringsstroom van die omgeving.

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

Stel verplichte reviewers in onder Organisatiebeheer → Omgevingen → <env>. De job wacht op goedkeuring voordat de stap draait; het secret wordt pas geïnjecteerd wanneer de job is goedgekeurd en wordt uitgevoerd.

Namen van omgevingen worden zonder onderscheid tussen hoofd- en kleine letters vergeleken, zoals in Forgejo en GitHub: environment: Production is de omgeving production, met haar branchregel, reviewers en secrets. Verschillen twee omgevingen alleen in hoofdlettergebruik, dan wordt de omgeving gebruikt die precies zo gespeld is als de job haar declareert; is dat bij geen van beide zo, dan krijgt de job geen omgeving en weigert de deploymentpoort hem (environment_case_ambiguous). Hernoem dan een van de twee.

Een pull_request_target-run vanuit een fork krijgt geen omgeving, geen secrets van een omgeving en geen OIDC-claim ervan, wat de job ook declareert. Dat is strenger dan bij GitHub. Zie Bereik van secrets bij PR’s uit forks.

Secrets van een omgeving bereiken alleen de jobs die ernaar verwijzen

Sinds 2026-10-26 krijgt een job een secret van een omgeving alleen als de job ernaar verwijst: in zijn eigen steps, env:, with: of if:, of in de env: op workflowniveau, als ${{ secrets.NAME }} of ${{ secrets['NAME'] }}. Een secret waarnaar de job niet verwijst, wordt helemaal niet naar zijn runner gestuurd, en staat dus niet op de machine waarop de job draait.

${{ toJSON(secrets) }}, een berekende naam zoals ${{ secrets[format('{0}_KEY', 'DEPLOY')] }}, en secrets: inherit bij het aanroepen van een herbruikbare workflow verwijzen naar alle secrets van de omgeving, net als bij GitHub.

Een stap kan een secret alleen via zo’n expressie lezen, dus een workflow die nu werkt, blijft werken: de wijziging haalt alleen secrets weg die geen enkele stap kon lezen.

Kan fremforge het workflowbestand van de job op de commit van de job niet lezen, of de job er niet in vinden, dan krijgt de job geen enkel secret van de omgeving. De job draait wel, met zijn OIDC-token en zijn variabelen.

Wie welk secret kreeg: de leesaudit

Elke CI-job die een secret van een omgeving of een afgebakend secret ontvangt, of verwijst naar een secret dat hem geweigerd wordt, schrijft één vermelding naar het auditlog van de organisatie, met actie ci.secret.read:

VeldWaarde
actorde persoon die de run startte (de organisatie, bij een geplande run)
repository, repository_id, workflow, jobwaar het draaide
run_id, run_number, run_attempt, ref, eventwelke run
environmentde omgeving die de job declareerde
secretsde NAMEN die de job ontving, elk met zijn bereik (environment, repo of org)
withheldnamen waarnaar de job verwees maar die hij niet kon krijgen, elk met een reden: workflow-unreadable of job-not-found, en bij een afgebakend secret het veld van het bereik dat hem weigerde (scope-workflow, scope-job, scope-ref, scope-event, scope-environment) of undecryptable

De waarde van een secret wordt nooit vastgelegd, en ook geen hash ervan. Een job die naar geen enkel secret van de omgeving verwijst, krijgt er geen en schrijft geen vermelding.

De vermelding wordt geschreven voordat de secrets aan de runner worden gegeven. Kan ze niet worden opgeslagen, dan krijgt de job geen enkel secret: geen secret verlaat fremforge zonder dat het is vastgelegd. De on-call van fremforge wordt opgeroepen als dat gebeurt.

Vraag de vermeldingen op met de API:

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

De vermeldingen maken deel uit van de hashketen van het auditlog, de bewaartermijn ervan en de SIEM-stream, zoals elke andere auditvermelding.

Secrets van repository’s en organisaties die in Forgejo zijn ingesteld, vallen hier niet onder: Forgejo geeft ze nog steeds aan elke job van de repository, en het lezen ervan wordt niet vastgelegd. Gebruik in plaats daarvan een afgebakend CI-secret, en verwijder de Forgejo-kopie met dezelfde naam: zolang die bestaat, bereikt ze nog steeds de jobs die het bereik weigert.

Afgebakende CI-secrets

Een afgebakend CI-secret wordt door fremforge bewaard, voor de hele organisatie of voor één repository, en wordt alleen aan een job gegeven als elk veld van het bereik dat je hebt ingesteld overeenkomt (binnen een veld volstaat het als één van de vermelde waarden overeenkomt):

VeldKomt overeen metVoorbeeld
workflowsde bestandsnaam van de workflowdeploy.yaml, release-*.yml
jobsde job-id (de sleutel onder jobs:)deploy, publish-*
refsde branch of tagrefs/heads/main, refs/tags/v*
eventsde trigger van de workflowpush, workflow_dispatch, schedule, pull_request, pull_request_target, release
environmentde job declareert deze omgeving, op een branch of tag die de omgeving toestaatproduction

* is het enige jokerteken. Een veld dat je weglaat, laat alles toe, met één uitzondering: zonder vermelde events krijgt een run op de ref van een pull request (refs/pull/...) het secret nooit. Wil je code uit een pull request een afgebakend secret geven, vermeld dan uitdrukkelijk pull_request (of pull_request_target).

De job moet ook naar het secret verwijzen (zoals hierboven). Elke levering, en elke weigering van een secret waarnaar de job verwees, wordt vastgelegd in de vermelding ci.secret.read van de job. Een pull request uit een fork krijgt er nooit een.

Welke velden een beveiligingsgrens vormen. Iedereen die naar de repository kan pushen, kan een branch maken met een workflow met een job deploy in een bestand deploy.yaml, en die starten. workflows en jobs beperken dus via welke codepaden het secret normaal gesproken wordt ontvangen, maar ze beschermen het niet tegen mensen met schrijfrechten. refs (wijs naar beschermde branches of tags) en environment (met een branchregel en verplichte reviewers) doen dat wel. Dat is hetzelfde als bij GitHub.

Een run uit de samenvoegwachtrij (refs/heads/fremforge-queue/...) is een build in dezelfde repository die een maintainer in de wachtrij heeft gezet, en een leeg refs-bereik laat die toe, zoals elke branch. Beperk refs als een secret runs uit de samenvoegwachtrij niet mag bereiken.

Jobs in een herbruikbare workflow, en, op een Forgejo-versie die de job-id nog niet meestuurt, jobs met een eigen name: of een matrix, krijgen nog geen afgebakende secrets.

Een secret aanmaken of vervangen (de waarde kun je alleen schrijven; elke wijziging wordt geaudit, met het bereik dat werd vervangen):

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>"

Laat repository_id weg voor een secret voor de hele organisatie. Een onbekend veld, een environment die geen naam is en een lege regel in een lijst worden geweigerd (400) in plaats van genegeerd, zodat een tikfout een secret nooit voor elke job kan openzetten. GET /orgs/<org>/ci-secrets toont namen, bereiken en wanneer elk secret voor het laatst is gelezen; DELETE .../ci-secrets/<NAME>[?repository_id=] verwijdert er een. Scopes: ci-secrets:read, ci-secrets:write. Limieten: 500 per organisatie, 100 per repository, 20 patronen per veld, 64 KB per waarde.

Wil je scoped CI-secrets als code beheren, gebruik dan fremforge_ci_secret in de fremforge-provider (vanaf 0.9.0). De waarde is een write-only-argument en bereikt nooit de Terraform-state.

Secrets roteren

  1. Maak eerst de nieuwe inloggegevens aan in het externe systeem.
  2. Overschrijf het secret in fremforge met de nieuwe waarde. Verwijder het niet om het daarna opnieuw aan te maken.
  3. Start een nieuwe workflowrun om te controleren of de nieuwe inloggegevens van begin tot eind werken.
  4. Trek de oude inloggegevens in het externe systeem in.

Overschrijven bewaart het audittrail en voorkomt een periode waarin het secret ontbreekt voor jobs die tussen het verwijderen en opnieuw aanmaken draaien.

Limieten

OnderdeelLimiet
Maximale grootte van een secretwaarde64 KB
Secrets van de organisatie1.000
Secrets van een repository100 per repository
Secrets van een omgeving100 per omgeving
Afgebakende CI-secrets500 per organisatie, 100 per repository
Patroon voor de naam van een secretLetters, cijfers en underscore; moet met een letter beginnen; mag niet beginnen met FORGEJO_ of GITHUB_

OIDC als alternatief voor secrets

Overweeg voor deploys naar cloudproviders (T Cloud Public, AWS, Azure, GCP) OIDC-tokenfederatie in plaats van langlevende inloggegevens die als secrets zijn opgeslagen. OIDC geeft per job kortlevende tokens uit, afgebakend tot de specifieke workflowrun. Niets om te roteren, niets om te lekken.

OIDC is de aanbevolen werkwijze voor elke job die naar cloudinfrastructuur deployt.

Zie ook