Gå til hovedinnhold

Sikkerhetsmodellen for hostede runnere

fremforges hostede runnere kjører kundenes Forgejo Actions-workflower på virtuelle ECS-maskiner i T Cloud Public, én per commit, i regionen EU-DE. En jobb i kø som ikke har en ledig VM for sin commit, klargjør en ny VM med innebygd Docker. Den VM-en kjører bare jobber fra den samme commiten i det samme repositoryet, én om gangen, venter i opptil 60 sekunder på en ny jobb og slås deretter av og slettes. Det finnes ingen varig runner-pool, og ingen VM kjører noen gang kode fra to commits, to repositoryer eller to kunder.

Denne siden beskriver isolasjonsgarantiene fremforge gir, grensene som gjelder per tenant, og trusselmodellen: hva kundekode kan gjøre, hva den ikke kan gjøre, og hvilke designvalg en workflow-forfatter bør ta for å holde secrets trygge.

Hvor workflower kjører

KomponentImplementasjon
BeregningVirtuelle maskiner i T Cloud Public ECS (Elastic Cloud Server), region eu-de, tre tilgjengelighetssoner
LivssyklusÉn flyktig VM per commit. Plattformen starter en ny ECS-VM når den første jobben for en commit står i kø, og sletter den når jobbene for den commiten er ferdige. To kunder, eller to commits, deler aldri en kjerne – sammenlignbart med GitHub-hostede og GitLab-hostede runnere når det gjelder isolasjon mellom tenanter. Jobber fra den samme commiten kan dele VM-en (for eksempel build og deretter deploy), og derfor bør en pipeline behandles som ett tillitsdomene – se Trusselmodell. Kundeworkflower deler aldri VM med plattformens arbeidslaster. Det finnes ingen varm pool (mål 0, av kostnadshensyn), så hver commit betaler de ~40 s ECS-oppstart.
Plassering i nettverketHver VM får sitt eget ENI i et eget runner-subnett, med en Security Group per VM som håndhever utgående trafikk. VM-ene ligger i et eget VPC-subnett fordelt over tre tilgjengelighetssoner
VM-kjøretidÉn VM per commit; klargjøres når den første jobben for commiten står i kø, og slettes når jobbene er ferdige. Maksimal veggklokketid per jobb (60 min som standard, 360 min som absolutt tak) håndheves på runneren
ReserveoppryddingEn egen opprydder utenfor hovedflyten sletter foreldreløse VM-er – runneren registrerte seg aldri, runneren avsluttet men slettingen ble ikke gjort, VM-en er eldre enn fristen for opprydding, eller et absolutt tak på 6 t 10 min.
Runner-binærUpstream forgejo-runner (act_runner) v13.2.0, ingen fork. Registrerer seg flyktig med --ephemeral, så en registrering brukes opp av én oppgave; agenten registrerer seg på nytt for hver videre oppgave fra den samme commiten, og plattformen sletter VM-en når jobbene for commiten er ferdige
VM-imageRunner-imaget for hostede runnere – debian 13 (trixie) som base, med verktøykjeder bakt inn fra binærfiler bufret i EU (ingen direkte upstream-nedlastinger under oppstart). Imaget er VM-ens oppstartsdisk; Docker-daemonen er forhåndsinstallert og kjører direkte på VM-en (ingen egen sidecar). Kaldstart tar ~40 s for hver commit – målet for den varme poolen er 0 av kostnadshensyn, så ingen VM er klargjort på forhånd

Isolasjonsgarantier

Nettverksisolasjon (per VM)

Hver runner-VM får sitt eget ENI, knyttet til en Security Group per VM. Som standard avviser SG-en all trafikk unntatt:

  • DNS (53/UDP, 53/TCP) til plattformens interne DNS-resolver
  • Utgående trafikk gjennom runnernes utgående proxy – den eneste utgående HTTP-veien

Kundekode kan ikke nå:

  • fremforge-API-et eller den private databasen bak det
  • Forgejos interne endepunkt (runner-VM-er snakker med Forgejo gjennom det offentlige apex-domenet bak Bunny med et runner-spesifikt scope; det interne endepunktet kan ikke nås)
  • Plattformtjenester i T Cloud Public (RDS, DCS, OBS, SFS-Turbo, IAM, KMS)
  • Andre tenanters runner-VM-er
  • Klyngens metadata-IP (169.254.169.254) – eksplisitt blokkert i SSRF-vernet
  • Private RFC1918-områder eller loopback

Security Group-en settes når VM-en klargjøres, og kan ikke endres fra innsiden av VM-en – VM-en har ingen IAM-agency knyttet til seg, så API-ene for nettverkskonfigurasjon kan ikke nås med identiteten fra metadatatjenesten.

Utgående trafikk

Workflow-steg som trenger det offentlige internett (klone et offentlig repo, laste ned et verktøy, kalle et SaaS-API), går gjennom runnernes utgående proxy via HTTPS_PROXY, som settes inn i VM-en. Denne proxyen:

  • Godtar alle offentlige unicast-IP-er, inkludert Carrier-Grade NAT (samme oppførsel som GitHub-hostede runnere – nødvendig for T Cloud Publics egne tjenester som SWR, som ligger i CGNAT)
  • Blokkerer private RFC1918-adresser, link-local, loopback, multicast, reserverte adresser og eksplisitte metadata-IP-er for skyen (169.254.169.254, T Cloud Publics 100.100.100.200)
  • Logger hvert forsøk på CONNECT-tunnel med vertsnavn og oppslått IP for revisjonsoppbevaring

Proxyen er bevisst romslig i samme forstand som GitHub-hostede runnere: kundeworkflower kan nå alle offentlige verter de trenger. Grensen går ved SSRF og den interne plattformen, ikke ved tillatelseslister for leverandører.

Pakkebuffere (suverene, i EU)

Før en workflow når det offentlige internett, går pakkeinstallasjoner og pull av imager gjennom organisasjonens egen avhengighetsproxy, som ligger i EU i den samme regionen som runner-VM-ene. Det delte Docker Hub-speilet og pakkebufferne i VPC-en ble avviklet i oktober 2026. Hver organisasjons proxy bruker sin egen pakkepolicy, egne upstream-legitimasjoner, egen tilgangslogg og egen lagringsgrense; en offentlig fil som flere organisasjoner puller, lagres én gang, og innhold som er hentet med en organisasjons egne legitimasjoner, leveres aldri til en annen. Med pull-through for runnere slått på (standard for alle organisasjoner som ikke har lagret egne innstillinger for avhengighetsproxyen), pekes hver jobb mot proxyen når den starter:

TrafikkSettes inn som
npm, pnpm, pip, uvjobbens npmrc, PIP_INDEX_URL, UV_DEFAULT_INDEX
Go, Maven, NuGet, Trivys sårbarhetsdatabase (hver av dem slik de står i runner_pull_through_ecosystems, alle som standard)GOPROXY, et Maven-speil av Central, en NuGet.Config for hele maskinen, TRIVY_DB_REPOSITORY og TRIVY_JAVA_DB_REPOSITORY
Containerimager fra ghcr.io, quay.io, mcr.microsoft.com, registry.k8s.io, public.ecr.aws, registry.access.redhat.com; og docker.io, registry.redhat.io, registry.connect.redhat.com bare med organisasjonens egen legitimasjon, aldri for pull requests fra forkerregisterkonfigurasjonen til VM-ens Docker-daemon

Jobben autentiserer seg mot proxyen med legitimasjon som er avgrenset til sin egen organisasjon og kjøring, så pakkeinstallasjoner går gjennom organisasjonens pakkepolicy og skadevaresjekker og vises i tilgangsloggen. For containerimager på denne veien er proxyen en buffer, ikke en sperre: en pull som den avviser, faller gjennom til registeret. Proxyen sjekkes ved starten av hver jobb; hvis den ikke kan nås, installerer og puller jobben fra de offentlige registrene gjennom den utgående proxyen, og reserveløsningen logges. Se Hosted runners.

I det vanlige tilfellet (offentlige avhengigheter) er dette usynlig, så ingenting i workflowen din endres.

Hvis du bruker en privat indeks eller et privat register, er verdien som settes inn, standardverdien, så den går foran en standard som er satt i en committet konfigurasjonsfil (pip.conf, .npmrc, go env). Pek eksplisitt på den private kilden din, slik at den vinner; hvordan du gjør det for hvert verktøy, står nedenfor.

Bruk én indeks per installasjon: proxyen, eller én privat indeks som selv speiler det offentlige registeret. Legg aldri til en ekstra indeks ved siden av den første (--extra-index-url, PIP_EXTRA_INDEX_URL, UV_EXTRA_INDEX_URL): installasjonsverktøyet tar da et pakkenavn fra hvilken som helst indeks som tilbyr det, og det er slik angrep med avhengighetsforvirring virker. Se avhengighetsproxyens merknader om PyPI.

  • pip: pip install --index-url <your-private> ... (en --index-url på kommandolinjen overstyrer PIP_INDEX_URL som er satt inn), eller sett den i env: for workflow-steget, når den private indeksen speiler de offentlige pakkene du trenger. For å installere dine egne pakker fra organisasjonens register mens de offentlige kommer gjennom proxyen, bruker du to separate installasjoner, hver med sin ene indeks: offentlige avhengigheter først, deretter pakkene dine med --index-url https://frem.sh/api/packages/<org>/pypi/simple/ --no-deps.
  • uv: samme regel. Fest dine egne pakker til registeret ditt etter navn, med en indeks merket explicit = true og en [tool.uv.sources]-oppføring per pakke, slik at ingen navn kan løses fra begge.
  • npm: sett NPM_CONFIG_REGISTRY (og, for pnpm 11, PNPM_CONFIG_REGISTRY) i stegets env: til ett register som speiler det offentlige, eller behold proxyen og send bare ditt eget scope til registeret ditt (@<your-scope>:registry=…), som fester hvert navn i det scopet til ett register. Se Pakkebuffere.
  • Go: sett GOPRIVATE (f.eks. GOPRIVATE=git.example.com/*) slik at verktøykjeden går utenom proxyen og sjekksumdatabasen for de private modulstiene dine.

Isolasjon på VM-nivå

FlateHåndheving
ProsessWorkflow-steg kjører som VM-ens runner-bruker som standard; sudo er tilgjengelig, slik at workflower kan kjøre apt install, kjøre docker, konfigurere systemverktøy osv. (samme forventninger som til GitHub-hostede runnere). Grensen er hypervisoren under VM-en, ikke brukernavnerom
ArbeidsområdeLokalt kladdeområde på VM-en i /workspace (og /tmp) – tømmes mellom jobber og slettes sammen med VM-en. Se Jobber som deler en VM for hva som kan bli med fra én jobb til en annen i den samme commiten
IdentitetVM-en har ingen IAM-agency knyttet til seg. Spørringer mot metadatatjenesten returnerer bare nettverksidentiteten som trengs for oppstart; ingen tilgang til plattformens API
RessursgrenserVM-ens ECS-flavor setter et fast tak for CPU og minne. En kunde som løper løpsk, kan ikke bruke opp mer kapasitet enn VM-ens egne grenser. Løpsk bruk totalt begrenses av taket for samtidighet per tenant og plattformens beregningskapasitet
Container-kjøretidDocker (innebygd) – daemonen kjører på VM-verten, ikke som sidecar. docker build, docker compose, kind og testcontainers virker direkte uten DinD-kompleksitet. For workflower som trenger flere capabilities i container-kjøretiden, sender etiketten dind-privileged jobben til en flavor med KVM-nesting slått på
HypervisorT Cloud Publics KVM-baserte ECS-hypervisor – den samme VM-grensen for flere tenanter som brukes for alle kommersielle T Cloud Public-kunder som kjører ECS i regionen

Kjerneisolasjon mellom og innenfor tenanter

Dette er den bærende påstanden i modellen for hostede runnere. Hver VM er bundet til én commit i ett repository – en gjeste-Linux-kjerne som kjører på T Cloud Publics hypervisor. Den kjører jobbene for den commiten én om gangen, og plattformen sletter den når de er ferdige. En jobb fra en annen commit, et annet repository eller en annen organisasjon får alltid en annen VM med en annen kjerne.

MekanismeHvordan
VM per commitPlattformen starter en VM for en ventende jobb når ingen VM for den commiten er ledig. En flukt på kjernenivå (f.eks. CVE-2024-1086 nf_tables) kompromitterer bare DENNE engangs-VM-en og jobbene fra den samme commiten, ikke tenanter som ligger ved siden av
Rask nedstenging når den er ferdigEtter sin siste jobb venter VM-en i opptil 60 sekunder på en ny jobb fra den samme commiten, og slås deretter av og slettes. En egen opprydder utenfor hovedflyten fanger opp foreldreløse VM-er (runneren registrerte seg aldri, slettingen ble ikke gjort) innenfor et absolutt tak på 6 t 10 min
ENI og SG per VMNettverksisolasjon som er uavhengig av kjernegrensen. VM-en til tenant A kan ikke nå VM-en til tenant B over nettverket – separate ENI-er, separate Security Groups
Livssyklus for én commitHver jobb kjører i en ny jobbcontainer. En VM brukes aldri på nytt for en annen commit, et annet repository eller en annen tenant, så den har aldri secrets fra en tidligere tenant
OIDC-tokenerHver VM får et nytt OIDC-token signert av runnernes OIDC-utsteder med aud=fremforge og sub=repo:<owner>/<repo>:ref:<refname>. Kundens tillitspolicyer i skyen bruker denne audience og sub for assume_role_with_web_identity

Jobber som deler en VM

En VM brukes på nytt bare for jobber fra den samme commiten i det samme repositoryet (og dermed den samme organisasjonen): andre workflower, kjøringer eller nye kjøringer av den commiten, eller en utrulling etter byggingen. Mellom disse jobbene nullstiller fremforge alt den vet kan inneholde legitimasjon: jobbcontaineren, /workspace og /tmp, Docker-innloggingen og npm-konfigurasjonen, og de vanlige legitimasjonsfilene. Hvis nullstillingen ikke kan bekreftes, tar VM-en ikke flere jobber, og neste jobb får en ny VM.

Nullstillingen er ikke en ny VM. Jobber kan bruke VM-ens Docker-daemon, som er root på VM-en, så en jobb kan legge igjen noe til en senere jobb fra den samme commiten. Imager som er pullet med legitimasjon, verktøybufferen og Go-byggebufferen blir også liggende på VM-en. Behandle alle jobber fra én commit som ett tillitsdomene:

  • Ikke kjør kode du ikke stoler på (testdata fra bidragsytere, ikke-gjennomgåtte avhengighetsoppdateringer) i den samme commitens pipeline som en jobb som har produksjons-secrets.
  • Sett utrullinger bak en miljøgodkjenning (se Del opp validate- og apply-jobber), og foretrekk kortlivede OIDC-legitimasjoner fremfor lagrede nøkler.
  • Hvis en utrulling aldri må dele maskin med et bygg, kjører du den på en egen runner (BYO).

Grenser per tenant

GrenseStandardOverstyring
Samtidige jobber per tenant2 per bruker garantert, minst 10 og høyst 60 (prøveperiode: 2). Over det kan en betalende tenant bruke ledig kapasitet opptil 60, men bare så lenge 20 jobbenheter på plattformen forblir ledige, slik at ingen tenants topper forsinker andres garanterte jobber. Til sammenligning, GitHub-hostet: Free 20, Pro 40, Team 60, Enterprise 500Enterprise-on-Demand: en overstyring per tenant erstatter den garanterte andelen
Samtidige jobber totalt for alle tenanterBegrenset av plattformens beregningskapasitet i eu-de–
Tak for jobbrate7 jobbstarter per rullerende vindu på 5 minutter per enhet i tenantens tak for topper: 420 for en betalende tenant, 14 i prøveperioden. Det skalerer med et hevet tak for samtidighet, så det er en sikring mot misbruk og aldri grensen en normal pipeline møter først. GitHub publiserer ingen grense for jobbstarterOverstyring per tenant
Veggklokketid per jobb3600 s (60 min)Kan konfigureres per workflow-steg, men har et tak på 6 t
Pott med runner-minutterseat_cap × 1000 minutter/måned (eller et eksplisitt tak per tenant)Operatørkonfigurasjon; samsvarer med de offentlige prisene
VM-flavorc9.large.2 (2 vCPU / 4 GiB RAM) som standard; c9.xlarge.4 (4 vCPU / 16 GiB) for runs-on: [fremforge, large]Etiketten i runs-on: for workflow-steget sender jobben til andre flavorer

Når en tenant overskrider potten med runner-minutter og overforbruk ikke er slått på (eller er fryst), avviser plattformen utsendelsen: jobben i kø går over til Cancelled i Forgejo. Tenanter med overforbruk slått på fortsetter å kjøre og faktureres for det som overskrides.

Når en tenant når taket for samtidighet, venter flere jobber i kø og sendes ut så snart en jobbenhet blir ledig.

Hva kundekode IKKE kan gjøre

  • Nå fremforge-API-et, Forgejos interne endepunkt eller noen annen plattformtjeneste (API-et er bare tilgjengelig gjennom det offentlige Bunny-apex-domenet med riktig autentisering, akkurat som for enhver ekstern klient)
  • Lese en annen tenants runner-VM (separat ENI; trafikk mellom VM-er eksplisitt avvist i Security Group-en)
  • Lese en annen tenants secrets (workflow-secrets sendes bare til runneren som kjører workflowen som eier dem; når VM-en slettes, forsvinner legitimasjonene i minnet)
  • Beholde tilstand utover sin egen commit (VM-en slettes når jobbene for den commiten er ferdige, og ingen disk deles mellom VM-er)
  • Nå OTC-API-et direkte med privilegert scope (ingen IAM-agency knyttet til; identiteten fra metadatatjenesten har ingen roller)
  • Nå metadata-endepunktene i skyen (169.254.169.254, 100.100.100.200 – begge eksplisitt avvist i SSRF-vernet)
  • Bruke opp runner-poolen (tak for samtidighet per tenant pluss plattformens samlede beregningstak)
  • Sulte ut køen for andre tenanter (tak for samtidighet per tenant)
  • Bryte ut av hypervisoren (VM-en er grensen; verten under den tilhører T Cloud Public og er isolert av den samme VMM-en som skiller alle kommersielle ECS-tenanter i regionen)

Anbefalte workflow-former

Del opp validate- og apply-jobber

Kjør kode du ikke stoler på (PR-bygg, avhengighetsskanninger, testdata fra bidragsytere) i en validate-jobb uten produksjonslegitimasjon, og sett apply-jobben bak en miljøgodkjenning. Validate-jobben ser aldri secrets til apply-jobben. Begge jobbene hører likevel til den samme commiten, så de kan kjøre på den samme VM-en etter hverandre: se Jobber som deler en VM.

Fest uses:-referanser med SHA når forsyningskjeden er viktig

uses: actions/checkout@v4 løses til frem.sh/actions/checkout (fremforges innebygde Forgejo-pull-speil av github.com/actions/checkout, synkronisert hver 8. time). Speilet følger upstream-tagger, inkludert force-pusher, så å feste med SHA er den eneste måten å garantere byte-for-byte-stabilitet på:

# Bytewise-stable across maintainer force-pushes
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5  # v4.2.0

# Picks up upstream's current HEAD at last mirror sync — including
# any maintainer-credential-compromise force-push
- uses: actions/checkout@v4

Bruk OIDC for autentisering mot skyen, ikke langlivede secrets

Hver runner-VM får et nytt OIDC-token med aud=fremforge. Konfigurer tillitspolicyen hos skyleverandøren din til å godta dette tokenet og bytte det mot kortlivede legitimasjoner. Unngå å lagre langlivede AWS-tilgangsnøkler eller JSON-filer for GCP-tjenestekontoer i workflow-secrets, der de havner i prosessmiljøet.

Sett frister for workflow-steg

Sett timeout-minutes: på enkeltsteg for å begrense hvor lenge et steg som henger, kan kjøre. Taket for veggklokketid på VM-nivå (activeDeadlineSeconds=3600) er plattformens gulv; tidsavbrudd per steg gir raskere tilbakemelding.

Sammendrag av trusselmodellen

TrusselTiltak
Kundens workflow inneholder en legitimasjonPush-beskyttelse (Gitleaks) blokkerer commiten før den havner i historikken
Kundens workflow prøver å lese fremforge-plattformenENI og SG per VM avviser; ingen IAM-agency knyttet til VM-en; API-et kan bare nås via den offentlige, autentiserte veien
En tenants jobb prøver å lese en annens VMENI per VM på separate IP-er; SG avviser som standard på tvers av runner-subnettet; og de er på forskjellige VM-er – forskjellige kjerner
Flukt på kjernenivå (root i VM-en → vertskjernen via CVE)Kompromitterer bare engangs-VM-en som kjører jobbene for den commiten. Tenanter ved siden av blir det IKKE – de er på sin egen VM. VM-en slettes uansett omtrent ett minutt etter den siste jobben for commiten
Kundens workflow prøver å lekke data via DNSDNS er tillatt (workflower trenger det av legitime grunner), men den utgående proxyen logger hver CONNECT med fullt vertsnavn for revisjon
Kompromittert act_runner upstreamBinær testet upstream (vi forker ikke). Speilet gjennom EU-bufferen med Trivy-sperrer på runner-imaget. CVE-rettelser kommer via ukentlig ombygging
Kundens workflow prøver å nå metadata-IP-erEksplisitt avvisning i SSRF-vernet: 169.254.169.254 (AWS-stil) og 100.100.100.200 (T Cloud Public)
Kunden monopoliserer kapasitetenGarantert andel per tenant (2 per bruker, 10 til 60) med topper bare inn i ledig kapasitet som lar 20 jobbenheter stå ledige, tak for jobbrate (7 starter per toppenhet per 5 min), tak for minutter i potten
Utdatert runner-VM overlever oppgavenTaket for veggklokketid per jobb dreper jobben ved fristen; plattformen sletter VM-en når runneren avslutter; en egen opprydder utenfor hovedflyten fanger opp foreldreløse VM-er med et absolutt tak på 6 t 10 min

Sporbarhet

Hver utsendelse logges tre steder:

  1. Tabellen runner_jobs – tenant_id, repo, forgejo_task_id, started_at, finished_at, duration_seconds, vm_id. Kilde for faktureringsaggregering og Infrastructure-fanen i operatørkonsollen
  2. Tabellen metering_events – rader med code=runner_minute utledet fra runner_jobs.duration_seconds. Styrer fakturalinjene og tenantens panel «Runner minutes used»
  3. Operatørens loggstrøm – strukturerte oppstartslogger for operatøren, med løpende varsling om avviste utsendelser, VM-er som henger, overskredne frister og oppbrukt kapasitet

Tenanter ser sitt eget forbruk under Organisasjonsadministrasjon → Bygg og utrulling → CI-forbruk, med antall minutter per måned og en tabell over varighet per jobb.