Hoppa till huvudinnehållet

Hemligheter för driftsättning: OIDC-federation

fremforge vill inte hålla dina hemligheter för produktion. Långlivade inloggningsuppgifter för driftsättning (AWS_ACCESS_KEY_ID, AZURE_CLIENT_SECRET, GCP_SA_KEY, …) som lagras i CI-systemets hemlighetslager är ett ständigt rotationsarbete och en ständig risk vid intrång. Det moderna upplägget är OIDC-federation: ditt arbetsflöde ber CI-servern om en kortlivad identitetstoken, ditt molns IAM verifierar token och lämnar tillbaka en kortlivad inloggningsuppgift. fremforge levererar detta från början till slut.

Så fungerar det

Varje CI-jobb som körs på fremforge får en nyskapad JSON Web Token signerad av fremforges OIDC-utfärdare för runners på https://frem.sh/_app/runner/oidc. Token innehåller claims som beskriver jobbets sammanhang (repo, ref, arbetsflöde, eventuellt miljö), och förtroendepolicyn i ditt molns IAM avgör utifrån dessa claims om inloggningsuppgifter ska beviljas.

    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 som ditt molns förtroendepolicy kan matcha på:

ClaimExempelVad det identifierar
isshttps://frem.sh/_app/runner/oidcfremforge självt
audsts.amazonaws.comvilket moln du autentiserar mot
subrepo:my-org/my-repo:environment:production (när environment: är deklarerad)
repo:my-org/my-repo:ref:refs/heads/main (utan miljö)
vilken körning av arbetsflödet
repositorymy-org/my-reporepots namn
repository_ownermy-orgorganisationens slug
refrefs/heads/maingit-ref
environmentproductionmiljönamnet som arbetsflödet deklarerar
actoraliceForgejo-användarnamnet som startade körningen

De motsvarar strukturen för claims i GitHub Actions, så befintliga förtroendepolicyer och actions som actions/configure-aws-credentials / azure/login / google-github-actions/auth fungerar utan ändringar.

Discovery-URL

Peka ditt moln mot:

  • Utfärdar-URL: https://frem.sh/_app/runner/oidc
  • JWKS-URL: https://frem.sh/_app/runner/oidc/.well-known/jwks.json
  • Discovery-dokument: https://frem.sh/_app/runner/oidc/.well-known/openid-configuration

Alla tre kan läsas anonymt.

Mönster för arbetsflöden

Deklarera en miljö

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/

environment: production ändrar OIDC-claimet sub till repo:my-org/my-repo:environment:production, så att din förtroendepolicy kan ge åtkomst bara till jobb som deklarerar production.

Utan miljö

När du inte deklarerar environment: faller sub tillbaka till repo:<owner>/<repo>:ref:<ref>. Förtroendepolicyer som matchar på ref fungerar på samma sätt som på GitHub. Spärren för miljöer på fremforges sida gäller inte (den spärrar bara arbetsflöden som deklarerar en miljö), så förtroendepolicyn på molnsidan är ditt enda skydd.

Exempel på förtroendepolicyer

AWS

IAM-förtroendepolicy för en OIDC-federerad roll:

{
  "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"
      }
    }
  }]
}

Engångskonfiguration: registrera fremforge som OIDC-leverantör:

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)

Registrera den federerade inloggningsuppgiften på en appregistrering i 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"]
  }'

Arbetsflöde:

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

Arbetsflöde:

- 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.com

T Cloud Public (OTC IAM Identity Provider)

T Cloud Public använder IAM Identity Providers + tokenutbyte:

  1. Identity Provider i 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 (eller ditt projekts STS-audience)
  2. Mappningsregler, som översätter claims till en federerad användare. Exempel som mappar ett subject repo:my-org/my-repo:environment:production till OTC-gruppen deploy-prod:
[
  {
    "local": [
      { "user": { "name": "{0}" } },
      { "group": { "name": "deploy-prod" } }
    ],
    "remote": [
      { "type": "sub" },
      { "type": "environment", "any_one_of": ["production"] }
    ]
  }
]
  1. Arbetsflöde: byt JWT:n mot en OTC-token via IAM:s slutpunkt för tokenutbyte, och använd sedan den returnerade X-Subject-Token för alla anrop till OTC:s API.

Styra vilka refs som får driftsätta var

OIDC-federation tar bort behovet av att LAGRA långlivade hemligheter, men du behöver fortfarande bestämma vilka branches som får driftsätta till vilken miljö. Ett arbetsflöde på en feature-branch KAN skapa en JWT som påstår environment: production; spärren är det som hindrar att JWT:n godtas.

fremforge erbjuder två spärrar, och du bör använda båda som skydd i flera lager.

Spärr 1: förtroendepolicy på molnsidan (alltid obligatorisk)

Förtroendepolicyn i ditt molns IAM har sista ordet. Lås claimet sub till strukturen repo+miljö OCH lås claimet ref till den eller de branches som miljön får driftsättas från. Exempel för 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"
    }
  }
}

En feature-branch KAN skapa JWT:n, men AWS vägrar utfärda inloggningsuppgifter eftersom ref är refs/heads/feature/foo i stället för refs/heads/main. Det här är det grundmönster som GitHub rekommenderar och fungerar från första dagen.

Spärr 2: mönster för driftsättningsbranches per miljö på fremforges sida (rekommenderas)

Under Organisationens administration → Miljöer (frem.sh/<org>/_admin/environments) konfigurerar du varje miljö på GitHub-vis. Tre avsnitt per miljö:

Branches och taggar för driftsättning: glob-mönster som matchas mot den git-ref som startade körningen. Exempel (förkortningar för branches får automatiskt prefixet refs/heads/):

NamnMönsterEffekt
productionmainbara main-branchen
stagingmain
develop
release/*
branches main + develop + release/*
pr-preview*vilken branch som helst (spärren på molnsidan är den verkliga auktoriteten)
releaserefs/tags/v[0-9]+.*bara versionstaggar (uttryckligt prefix refs/tags/)

Arbetsflöden som deklarerar environment: production avvisas vid start när den ref som startade dem inte matchar. Arbetsflöden UTAN deklarationen environment: körs utan begränsning, så vanlig CI på feature-branches fortsätter att fungera.

Miljösecrets: krypterade, avgränsade till miljön och injicerade som ${{ secrets.NAME }} i arbetsflödet OCH som vanliga miljövariabler när arbetsflödets environment: matchar. Täcker de fall av hemligheter som inte rör molnets IAM (API-nycklar från tredje part, signeringsnycklar, webhook-hemligheter) och som OIDC-federation inte löser.

Miljövariabler: värden som inte är hemliga, avgränsade till miljön. Samma injiceringsmodell.

Hur är det med spärrar för godkännande?

Miljöer i GitHub Actions har funktionen “Required reviewers”: ett jobb pausas efter merge tills N personer klickar på Approve i Actions-gränssnittet. fremforge levererar avsiktligt inte detta, eftersom Forgejo redan har en starkare spärr vid samma punkt i tidslinjen: branch-skydd på main med CODEOWNERS för .forgejo/workflows/* kräver granskning av pull requesten innan ett arbetsflöde som kan driftsätta landar. I 99 % av fallen (1 PR = 1 driftsättning) ÄR granskningen av pull requesten i Forgejo godkännandet av driftsättningen. Kunder som behöver GitHubs fall (1 PR kan ge N oberoende driftsättningar med separata godkännare per driftsättning) kan skicka in ett ärende, så prioriterar vi godkännare vid driftsättning (planerat) utifrån faktisk efterfrågan.

Spärren på fremforges sida hindrar att JWT:n skapas över huvud taget. Spärren på molnsidan hindrar att en skapad JWT godtas. Kombinera båda: även om en framtida bugg i fremforge skulle släppa igenom en avvisad start vägrar molnets IAM ändå att utfärda inloggningsuppgifter. Även om en kunds förtroendepolicy i molnet har ett skrivfel och godtar ett bredare ref-mönster än avsett, har fremforge ändå vägrat att skapa JWT:n.

Successiv införing

När du deklarerar environment: x i ett arbetsflöde men ännu inte har konfigurerat en rad i environments för x, tillåter fremforge starten i stället för att avvisa den. Det är avsiktligt, så att en ny kunds arbetsflöden inte går sönder i samma stund som de deklarerar en miljö; förtroendepolicyn på molnsidan är skyddsnätet tills du lägger till raden på fremforges sida. När en rad finns tillämpas spärren.

Låst läge: bara skyddade branches

För miljöer med höga säkerhetskrav där du vill förbjuda CI-körning på oskyddade refs HELT (inte bara spärra inloggningsuppgifter för driftsättning), aktivera Organisationens administration → Miljöer → Organisationsövergripande låsning → Kör bara arbetsflöden på skyddade branches:

Kör bara arbetsflöden på skyddade branches

När det är aktiverat vägrar plattformen starta NÅGOT arbetsflöde vars utlösande branch INTE finns i repots lista över skyddade branches (konfigureras via Forgejos inbyggda Inställningar → Grenar → Lägg till ny regel). Det inkluderar vanlig CI (tester, lint): inget körs på oskyddade branches. Användbart i reglerade miljöer där du inte vill ha någon kodkörning utanför granskade refs; rekommenderas inte för normalt utvecklingsflöde eftersom det bryter testning på feature-branches.

Om du aktiverar detta skyddar du vanligtvis mönstren main, develop och release/* i Forgejo så att dina vanliga utvecklingsbranches fortfarande kan köra CI.

Rekommenderad hållning

För typiska SaaS- och produktteam:

  1. Aktivera inte driftsättningsspärren för bara skyddade branches, låt vanlig CI köras på feature-branches.
  2. Konfigurera miljöer under Organisationens administration → Miljöer med lämpliga mönster för branches och taggar för driftsättning för var och en (production, staging osv.).
  3. Konfigurera förtroendepolicyer i molnet med både villkoret sub (låst till miljö) OCH ref (låst till branch).
  4. Skydda main i Forgejo med CODEOWNERS för .forgejo/workflows/* så att ändringar i arbetsflöden kräver granskning.

För reglerade miljöer och låsta miljöer (bank, försvar, vård):

  1. Aktivera driftsättningsspärren för bara skyddade branches.
  2. Konfigurera miljöer för detaljerad spärr per miljö ovanpå det.
  3. Samma hållning för förtroendepolicyer på molnsidan som ovan.

Den här kombinationen motsvarar den modell för driftsättningsspärrar som GitHub Enterprise och AWS rekommenderar (krav på miljöer vid driftsättning + bara skyddade branches) plus det valfria låsta läget för reglerade tenants.

Jämförelse: vad fremforge lagrar kontra ditt moln

Långlivade hemligheterOIDC-federation (den här sidan)
Var inloggningsuppgifterna för driftsättning finnsInställningar → Secrets i fremforgeI ditt molns IAM. fremforge ser dem aldrig.
Inloggningsuppgiftens livslängdTills du roterar1 timme (kan konfigureras per förtroendepolicy)
Om fremforge utsätts för intrångAngriparen har dina AWS-nycklarAngriparen kan skapa OIDC-JWT:er, men förtroendepolicyn på molnsidan begränsar vad de kan göra (per miljö, per repo, per ref)
RotationsarbeteVarje kvartal per hemlighet och molnInget, nycklarna roteras för varje jobb
GranskningsspårManuellt: vem som exporterade hemligheten och närVarje OIDC-utfärdande finns i audit_events + ditt molns CloudTrail/Activity Log

Begränsningar

  • environment: i objektform (med name: + url:) stöds via namnet; url: tolkas men exponeras för närvarande inte som ett claim.
  • Miljönamn som expanderas från en matris (environment: ${{ matrix.target }}) löses inte upp när OIDC-token skapas, och leder till att inget claim environment skickas. Använd en hårdkodad environment: per matrisjobb om du behöver förtroendepolicyer baserade på miljö-claimet.
  • Spärrar med obligatoriska godkännare per miljö (som funktionen “wait for approval” i GitHub Environments) följs upp som fas 2; dagens tillämpning är binär (skyddad branch ja/nej).

Se även