Deploy-secrets: OIDC-federatie
fremforge wil je productiesecrets niet bewaren. Langlevende deploy-inloggegevens (AWS_ACCESS_KEY_ID, AZURE_CLIENT_SECRET, GCP_SA_KEY, …) die in de secretopslag van een CI-systeem staan, zijn een eeuwig terugkerende rotatieklus en een eeuwig risico bij een inbraak. De moderne aanpak is OIDC-federatie: je workflow vraagt de CI-server om een kortlevend identiteitstoken, de IAM van je cloud verifieert het token en geeft een kortlevende inloggegeven terug. fremforge levert dit van begin tot eind.
Hoe het werkt
Elke CI-job die op fremforge draait, krijgt een vers aangemaakte JSON Web Token, ondertekend door de runner-OIDC-issuer van fremforge op https://frem.sh/_app/runner/oidc. Het token bevat claims die de context van de job beschrijven: repository, ref, workflow en eventueel omgeving. De trust policy in de IAM van je cloud beslist op basis van die claims of er inloggegevens worden verstrekt.
sequenceDiagram
autonumber
actor User
participant FF as fremforge<br/>runner-OIDC issuer
participant Pod as runner VM
participant Cloud as Your cloud's<br/>IAM • STS / WIF / …
User->>FF: git push
FF->>Pod: dispatch job — mint OIDC JWT
Pod->>Cloud: "Here's my JWT, give me credentials"
Cloud->>Cloud: Verify JWT against fremforge JWKS<br/>+ check trust policy claims
Cloud->>Pod: 1h credentials
Pod->>Cloud: deploy / read / write
Claims waarop de trust policy van je cloud kan matchen:
| Claim | Voorbeeld | Wat het identificeert |
|---|---|---|
iss | https://frem.sh/_app/runner/oidc | fremforge zelf |
aud | sts.amazonaws.com | bij welke cloud je je authenticeert |
sub | repo:my-org/my-repo:environment:production (als environment: is gedeclareerd)repo:my-org/my-repo:ref:refs/heads/main (zonder omgeving) | welke workflowrun |
repository | my-org/my-repo | naam van de repository |
repository_owner | my-org | slug van de organisatie |
ref | refs/heads/main | Git-ref |
environment | production | naam van de omgeving die de workflow declareert |
actor | alice | Forgejo-gebruikersnaam die de run startte |
Deze claims volgen de vorm van GitHub Actions, dus bestaande trust policies en de actions actions/configure-aws-credentials / azure/login / google-github-actions/auth werken zonder aanpassingen.
Discovery-URL
Verwijs je cloud naar:
- Issuer-URL:
https://frem.sh/_app/runner/oidc - JWKS-URL:
https://frem.sh/_app/runner/oidc/.well-known/jwks.json - Discovery-document:
https://frem.sh/_app/runner/oidc/.well-known/openid-configuration
Alle drie zijn anoniem leesbaar.
Workflowpatronen
Een omgeving declareren
jobs:
deploy:
runs-on: fremforge-runner
environment: production
permissions:
id-token: write # required to mint the OIDC JWT
contents: read
steps:
- uses: actions/checkout@v4
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/fremforge-deploy-prod
aws-region: eu-central-1
- run: aws s3 sync ./build s3://my-prod-bucket/Met environment: production wordt de OIDC-claim sub repo:my-org/my-repo:environment:production, zodat je trust policy alleen toegang kan geven aan jobs die production declareren.
Zonder omgeving
Declareer je geen environment:, dan valt sub terug op repo:<owner>/<repo>:ref:<ref>. Trust policies die op ref matchen, werken net als op GitHub. De omgevingspoort aan de kant van fremforge geldt dan niet (die controleert alleen workflows die een omgeving declareren), dus de trust policy aan de kant van de cloud is je enige verdedigingslinie.
Voorbeelden van trust policies
AWS
IAM-trust policy voor een rol met OIDC-federatie:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789012:oidc-provider/frem.sh/_app/runner/oidc"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"frem.sh/_app/runner/oidc:aud": "sts.amazonaws.com"
},
"StringLike": {
"frem.sh/_app/runner/oidc:sub": "repo:my-org/my-repo:environment:production"
}
}
}]
}Eenmalige inrichting: registreer fremforge als OIDC-provider:
aws iam create-open-id-connect-provider \
--url https://frem.sh/_app/runner/oidc \
--client-id-list sts.amazonaws.com \
--thumbprint-list $(curl -s https://frem.sh/_app/runner/oidc/.well-known/jwks.json | jq -r '.keys[0].x5t')Azure (Workload Identity Federation)
Registreer de gefedereerde inloggegeven op een App Registration in Entra ID:
az ad app federated-credential create \
--id $APP_OBJECT_ID \
--parameters '{
"name": "fremforge-prod-deploy",
"issuer": "https://frem.sh/_app/runner/oidc",
"subject": "repo:my-org/my-repo:environment:production",
"audiences": ["api://AzureADTokenExchange"]
}'Workflow:
- uses: azure/login@v2
with:
client-id: ${{ vars.AZURE_CLIENT_ID }}
tenant-id: ${{ vars.AZURE_TENANT_ID }}
subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}GCP (Workload Identity Federation)
gcloud iam workload-identity-pools providers create-oidc fremforge-provider \
--workload-identity-pool=fremforge-pool \
--issuer-uri="https://frem.sh/_app/runner/oidc" \
--attribute-mapping="google.subject=assertion.sub,attribute.repository=assertion.repository,attribute.environment=assertion.environment" \
--attribute-condition="attribute.environment == 'production'"Workflow:
- uses: google-github-actions/auth@v2
with:
workload_identity_provider: projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/fremforge-pool/providers/fremforge-provider
service_account: deploy@my-project.iam.gserviceaccount.comT Cloud Public (OTC IAM Identity Provider)
T Cloud Public gebruikt IAM Identity Providers + token-exchange:
- Identity Provider in OTC Console → IAM → Identity Providers → Create:
- Name:
fremforge-runner-oidc - Protocol:
OpenID Connect - Issuer URL:
https://frem.sh/_app/runner/oidc - JWKS URL:
https://frem.sh/_app/runner/oidc/.well-known/jwks.json - Client ID:
iam.eu-de.otc.t-systems.com(of de STS-audience van je project)
- Name:
- Mappingregels vertalen claims naar een gefedereerde gebruiker. Voorbeeld dat een subject
repo:my-org/my-repo:environment:productionkoppelt aan de OTC-groepdeploy-prod:
[
{
"local": [
{ "user": { "name": "{0}" } },
{ "group": { "name": "deploy-prod" } }
],
"remote": [
{ "type": "sub" },
{ "type": "environment", "any_one_of": ["production"] }
]
}
]- Workflow: wissel de JWT in voor een OTC-token via het token-exchange-endpoint van IAM en gebruik daarna de teruggegeven
X-Subject-Tokenvoor elke aanroep van de OTC-API.
Bepalen welke refs waarheen mogen deployen
OIDC-federatie maakt het OPSLAAN van langlevende secrets overbodig, maar je moet nog steeds bepalen welke branches naar welke omgeving mogen deployen. Een workflow op een featurebranch KAN een JWT aanmaken die environment: production claimt; de poort is wat voorkomt dat die JWT wordt geaccepteerd.
fremforge biedt twee poorten, en je gebruikt ze het best allebei, als gelaagde verdediging.
Poort 1: trust policy aan de kant van de cloud (altijd vereist)
De trust policy in de IAM van je cloud heeft het laatste woord. Pin de claim sub op de vorm repository+omgeving EN pin de claim ref op de branch(es) van waaruit die omgeving mag deployen. Voorbeeld voor AWS:
{
"Condition": {
"StringEquals": {
"frem.sh/_app/runner/oidc:sub": "repo:my-org/my-repo:environment:production"
},
"StringLike": {
"frem.sh/_app/runner/oidc:ref": "refs/heads/main"
}
}
}Een featurebranch KAN de JWT aanmaken, maar AWS weigert inloggegevens te verstrekken omdat ref refs/heads/feature/foo is in plaats van refs/heads/main. Dit is het basispatroon dat GitHub aanbeveelt, en het werkt vanaf dag één.
Poort 2: deploymentbranchpatronen per omgeving aan de kant van fremforge (aanbevolen)
Onder Organisatiebeheer → Omgevingen (frem.sh/<org>/_admin/environments) stel je elke omgeving in zoals op GitHub. Elke omgeving heeft drie onderdelen:
Deploymentbranches en -tags: glob-patronen die worden vergeleken met de Git-ref die de run startte. Voorbeelden (bij de verkorte schrijfwijze voor branches wordt automatisch refs/heads/ ervoor gezet):
| Naam | Patronen | Effect |
|---|---|---|
production | main | alleen de branch main |
staging | maindeveloprelease/* | de branches main + develop + release/* |
pr-preview | * | elke branch (de poort aan de kant van de cloud heeft het echte gezag) |
release | refs/tags/v[0-9]+.* | alleen versietags (expliciet voorvoegsel refs/tags/) |
Workflows die environment: production declareren, worden bij het dispatchen geweigerd als de ref die de run startte niet overeenkomt. Workflows ZONDER declaratie van environment: draaien zonder beperking, zodat gewone CI op featurebranches blijft werken.
Secrets van een omgeving: versleuteld, beperkt tot de omgeving, en geïnjecteerd als ${{ secrets.NAME }} in de workflow EN als gewone omgevingsvariabelen als de environment: van de workflow overeenkomt. Dekt de secrets buiten de cloud-IAM (API-sleutels van derden, ondertekeningssleutels, webhook-secrets) die OIDC-federatie niet oplost.
Variabelen van een omgeving: niet-geheime waarden die tot de omgeving zijn beperkt. Hetzelfde injectiemodel.
En goedkeuringspoorten?
GitHub Actions Environments heeft een functie “Required reviewers”: een job pauzeert na de merge tot N mensen op Approve klikken in de Actions-UI. fremforge levert dit bewust niet, omdat Forgejo op hetzelfde moment in de tijdlijn al een sterkere poort heeft: branchbescherming op main met CODEOWNERS op .forgejo/workflows/* vereist een review van de pull request voordat een workflow die kan deployen binnenkomt. In 99% van de gevallen (1 PR = 1 deploy) IS de PR-review van Forgejo de goedkeuring van de deploy. Heb je het GitHub-geval nodig (1 PR kan N onafhankelijke deploys opleveren, met per deploy aparte goedkeurders), meld dan een issue; we geven goedkeurders op het moment van deployen (gepland) prioriteit op basis van de werkelijke vraag.
De poort aan de kant van fremforge voorkomt dat de JWT überhaupt wordt aangemaakt. De poort aan de kant van de cloud voorkomt dat een aangemaakte JWT wordt geaccepteerd. Combineer ze: ook als een toekomstige bug in fremforge een geweigerde dispatch zou doorlaten, weigert de IAM van de cloud nog steeds inloggegevens te verstrekken. En ook als de trust policy in de cloud van een klant een typfout heeft en een breder ref-patroon accepteert dan bedoeld, heeft fremforge nog steeds geweigerd de JWT aan te maken.
Geleidelijke onboarding
Declareer je environment: x in een workflow maar heb je nog geen environments-rij voor x ingesteld, dan staat fremforge de dispatch toe in plaats van hem te weigeren. Dat is bewust, zodat de workflows van een nieuwe klant niet breken zodra die een omgeving declareert; je trust policy aan de kant van de cloud is het vangnet tot je de rij aan de kant van fremforge toevoegt. Zodra er een rij bestaat, wordt de poort afgedwongen.
Vergrendelde modus: alleen beschermde branches
Voor omgevingen met hoge beveiligingseisen waarin je CI-uitvoering op onbeschermde refs VOLLEDIG wilt verbieden (niet alleen deploy-inloggegevens afschermen), zet je Organisatiebeheer → Omgevingen → Organisatiebrede vergrendeling → Workflows alleen op beveiligde branches uitvoeren aan:
Workflows alleen op beveiligde branches uitvoeren
Staat dit aan, dan weigert het platform ELKE workflow te dispatchen waarvan de branch die hem startte NIET in de lijst met beschermde branches van de repository staat (ingesteld via Forgejo’s eigen Instellingen → Branches → Voeg nieuwe regel toe). Dat geldt ook voor gewone CI (tests, lint): op onbeschermde branches draait niets. Handig in gereguleerde omgevingen waar je geen enkele code-uitvoering buiten gereviewde refs wilt; niet aanbevolen voor de normale ontwikkelstroom, omdat het testen op featurebranches dan niet meer werkt.
Zet je dit aan, dan bescherm je in Forgejo meestal de patronen main, develop en release/*, zodat je gewone ontwikkelbranches CI kunnen blijven draaien.
Aanbevolen aanpak
Voor gangbare SaaS- en productteams:
- Zet de deploypoort voor alleen beschermde branches niet aan, zodat gewone CI op featurebranches draait.
- Stel wel omgevingen in onder Organisatiebeheer → Omgevingen, met passende patronen voor deploymentbranches en -tags voor elk (production, staging, enz.).
- Stel wel trust policies in de cloud in met zowel een
sub-voorwaarde (vastgepind op de omgeving) ALS eenref-voorwaarde (vastgepind op de branch). - Bescherm
mainin Forgejo met CODEOWNERS-dekking op.forgejo/workflows/*, zodat wijzigingen in workflows een review nodig hebben.
Voor gereguleerde of vergrendelde omgevingen (bankwezen, defensie, zorg):
- Zet de deploypoort voor alleen beschermde branches wel aan.
- Stel daarbovenop omgevingen in voor fijnmazige controle per omgeving.
- Dezelfde aanpak voor de trust policy in de cloud als hierboven.
Deze combinatie volgt het deploypoortmodel dat GitHub Enterprise en AWS aanbevelen (omgevingen verplicht bij deploys + alleen beschermde branches), plus de optionele vergrendelschakelaar voor gereguleerde tenants.
Vergelijking: wat fremforge bewaart en wat je cloud bewaart
| Langlevende secrets | OIDC-federatie (deze pagina) | |
|---|---|---|
| Waar deploy-inloggegevens staan | Organisatiebeheer → Secrets of Repository → Instellingen → Actions → Geheimen in fremforge | In de IAM van je cloud. fremforge ziet ze nooit. |
| Levensduur van de inloggegevens | Tot je ze roteert | 1 uur (per trust policy in te stellen) |
| Als fremforge wordt gehackt | De aanvaller heeft je AWS-sleutels | De aanvaller kan OIDC-JWT’s aanmaken, maar de trust policy in de cloud begrenst wat die JWT’s kunnen (per omgeving, per repository, per ref) |
| Rotatieklus | Elk kwartaal, per secret per cloud | Geen, de sleutels roteren bij elke job |
| Audittrail | Handmatig: wie heeft het secret wanneer geëxporteerd | Elke OIDC-aanmaak staat in audit_events + de CloudTrail/Activity Log van je cloud |
Beperkingen
environment:in objectvorm (metname:+url:) wordt op naam ondersteund;url:wordt geparsed, maar momenteel niet als claim doorgegeven.- Omgevingsnamen uit een matrix (
environment: ${{ matrix.target }}) worden bij het aanmaken van het OIDC-token niet opgelost, waardoor er geen claimenvironmentwordt meegegeven. Gebruik per matrixjob een vasteenvironment:als je trust policies op basis van de omgevingsclaim nodig hebt. - Verplichte goedkeurders per omgeving (zoals de functie “wait for approval” van GitHub Environments) staan gepland als vervolg in fase 2; de handhaving is vandaag binair (beschermde branch ja/nee).
Zie ook
- Integriteit van de auditketen: elke OIDC-aanmaak levert een auditgebeurtenis op in de hashketen per tenant
- Model van de gehoste runners: wat de runner van de ene klant isoleert van die van een andere