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
| Bereik | Waar in te stellen | Beschikbaar voor |
|---|---|---|
| Secret van de organisatie | Organisatiebeheer → Secrets | Alle repository’s in de organisatie |
| Secret van de repository | Repository-instellingen → Geheimen | Alleen die repository |
| Afgebakend CI-secret | API /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 omgeving | Organisatiebeheer → 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_SECRETNa 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.shSecrets 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.shStel 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:
| Veld | Waarde |
|---|---|
| actor | de persoon die de run startte (de organisatie, bij een geplande run) |
repository, repository_id, workflow, job | waar het draaide |
run_id, run_number, run_attempt, ref, event | welke run |
environment | de omgeving die de job declareerde |
secrets | de NAMEN die de job ontving, elk met zijn bereik (environment, repo of org) |
withheld | namen 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):
| Veld | Komt overeen met | Voorbeeld |
|---|---|---|
workflows | de bestandsnaam van de workflow | deploy.yaml, release-*.yml |
jobs | de job-id (de sleutel onder jobs:) | deploy, publish-* |
refs | de branch of tag | refs/heads/main, refs/tags/v* |
events | de trigger van de workflow | push, workflow_dispatch, schedule, pull_request, pull_request_target, release |
environment | de job declareert deze omgeving, op een branch of tag die de omgeving toestaat | production |
* 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
- Maak eerst de nieuwe inloggegevens aan in het externe systeem.
- Overschrijf het secret in fremforge met de nieuwe waarde. Verwijder het niet om het daarna opnieuw aan te maken.
- Start een nieuwe workflowrun om te controleren of de nieuwe inloggegevens van begin tot eind werken.
- 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
| Onderdeel | Limiet |
|---|---|
| Maximale grootte van een secretwaarde | 64 KB |
| Secrets van de organisatie | 1.000 |
| Secrets van een repository | 100 per repository |
| Secrets van een omgeving | 100 per omgeving |
| Afgebakende CI-secrets | 500 per organisatie, 100 per repository |
| Patroon voor de naam van een secret | Letters, 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
- Actions en CI/CD: workflowsyntaxis en het permissions-blok
- OIDC-tokenfederatie: sleutelloze inloggegevens voor de cloud
- Organisatiebeheer: beheer van secrets op organisatieniveau