Uforanderlige utgivelser og utgivelsesattestering
En publisert utgivelse er et løfte: disse filene, bygd fra denne commiten, under denne taggen. Uten uforanderlighet kan løftet stille brytes i ettertid: en tagg som flyttes til en annen commit, en ressurs som byttes ut med en annen binærfil, en utgivelse som slettes og opprettes på nytt under samme navn. Uforanderlige utgivelser gjør hver av disse umulig, og en utgivelsesattestering lar hvem som helst sjekke, på sin egen maskin, at det de lastet ned, er det som ble publisert.
Hva som fryses
Når uforanderlige utgivelser er slått på, fryses en utgivelse i det øyeblikket den publiseres. Fra da av:
| Endring | Tillatt? |
|---|---|
| Endre tittelen, notatene, pre-release-flagget | Ja |
| Endre taggnavnet eller mål-commiten | Nei |
Flytte taggen med git push --force, eller slette den med git push --delete | Nei |
| Gjøre utgivelsen om til et utkast igjen | Nei |
| Legge til, gi nytt navn til, erstatte eller slette en ressurs | Nei |
| Slette utgivelsen | Ja, men taggnavnet kan aldri brukes igjen i det repositoryet |
Alle flater håndhever den samme regelen: nettgrensesnittet, REST-API-et (HTTP
422 med en melding som begynner med immutable release:) og git selv
(pushen avvises av pre-receive-hooken med en melding som navngir taggen).
Fryingen virker bare fremover. Å slå på innstillingen fryser ingenting som allerede var publisert, og å slå den av senere tiner ingenting. Hvis en frosset utgivelse virkelig må endres, kontakter du support: support kan oppheve fryseren for den ene utgivelsen, med en skriftlig begrunnelse som vises i revisjonsloggen din.
Publiser ressursene før du publiserer utgivelsen
En publisert uforanderlig utgivelse godtar ingen nye ressurser, så rekkefølgen betyr noe. Opprett utgivelsen som et utkast, last opp ressursene og publiser den deretter:
# 1. draft
curl -sS -X POST -H "Authorization: token $TOKEN" -H 'Content-Type: application/json' \
-d '{"tag_name":"v1.2.3","name":"v1.2.3","draft":true}' \
https://frem.sh/api/v1/repos/acme/web/releases
# 2. assets (repeat per file)
curl -sS -X POST -H "Authorization: token $TOKEN" -F attachment=@dist/web-linux-amd64.tar.gz \
"https://frem.sh/api/v1/repos/acme/web/releases/$RELEASE_ID/assets?name=web-linux-amd64.tar.gz"
# 3. publish — this is the moment it freezes
curl -sS -X PATCH -H "Authorization: token $TOKEN" -H 'Content-Type: application/json' \
-d '{"draft":false}' https://frem.sh/api/v1/repos/acme/web/releases/$RELEASE_IDUtgivelsesverktøy som oppretter en publisert utgivelse og laster opp etterpå,
vil feile ved opplastingen med 422. Ressurser som bare er eksterne
lenker, kan ikke være del av en uforanderlig utgivelse, fordi det ikke finnes
lagrede bytes å ta en digest av; publisering av en slik avvises.
Hver ressurs har nå digesten sin i API-et, slik som hos GitHub:
"digest": "sha256:<hex>", og en uforanderlig utgivelse rapporterer
"immutable": true.
Container-tagger
Den samme policyen kan fryse tagger for containerimager i registeret:
| Modus | Frosset |
|---|---|
off (standard) | ingenting: alle tagger kan overskrives |
semver | versjonstagger: v1.2.3, 1.2.3, 1.2.3-rc.1; latest, 1, 1.2, main, sha-… kan fortsatt flyttes |
all | alle tagger unntatt de flyttbare du lister opp (latest, edge-*, …) |
En push av en frosset tagg som peker den mot en annen digest, avvises med
registerfeilen DENIED (HTTP 403). Å pushe det samme imaget igjen er
tillatt, så en CI-jobb som kjøres på nytt, feiler ikke. Sletting av en frosset
tagg avvises, og oppryddingsregler for pakker velger aldri en slik; frosne
tagger teller fortsatt med i lagringskvoten din. Signatur- og
attesteringstaggene som cosign skriver (sha256-<digest>.sig, .att,
.sbom), kan alltid flyttes. Container-tagger vurderes mot den gjeldende
policyen, så å slå av modusen er måten å flytte eller slette en tagg bevisst
på.
Pakker
Publiserte pakkeversjoner er uforanderlige på fremforge: npm, Maven, PyPI, NuGet, Cargo, Go, Helm, Conan, Composer, RubyGems, Pub, Swift, Conda, CRAN, Debian, RPM, Alpine, Arch, Chef, Vagrant og generiske pakker. Dette er slått på for alle organisasjoner som standard. Containere følger i stedet modusen for container-tagger over.
| Endring | Tillatt? |
|---|---|
| Laste opp en ny versjon | Ja |
| Legge til en ny fil i en versjon som finnes (enda en wheel for en plattform, en Maven-classifier-jar) | Ja |
| Laste opp en fil igjen med de samme bytene (en CI-jobb som kjøres på nytt) | Ja, endrer ingenting |
| Erstatte en fil i en publisert versjon med andre bytes | Nei |
| Slette en versjon, eller en av filene i den | Ja |
| Publisere en slettet versjon igjen, med hvilke som helst bytes | Nei: versjonsnavnet forblir pensjonert |
| Laste opp en slettet fil igjen med andre bytes | Nei |
En avvist opplasting svarer med HTTP 409 og en melding som begynner med
immutable package:, og svarheaderen X-Fremforge-Immutable sier hvorfor:
overwrite, reupload eller retired. Hvert avslag skrives til
revisjonsloggen din som package.immutable.refused, med pakken, versjonen,
filen, begge SHA-256-digestene og hvem som prøvde. Det vises også i
tilgangsloggen for registeret som en blokkert
opplasting.
De fleste registre nekter allerede å publisere en versjon to ganger. Dette er det som endres her:
- Helm og Conan lot tidligere en versjon overskrives på stedet.
- I alle registre kunne en versjon slettes og deretter publiseres igjen med annet innhold. Begge deler avvises nå.
Alltid foranderlig:
- Maven-
-SNAPSHOT-versjoner ogmaven-metadata.xml-filer, som skrives om etter design. - Indeksfilene for repositoryer som Debian-, RPM-, Alpine- og Arch-registre vedlikeholder.
For å holde andre versjoner foranderlige, for eksempel en generisk pakke som
brukes som CI-buffer, lister du dem som unntak på formen
[<type>:][<name>@]<version>, der * og ? fungerer som jokertegn:
| Oppføring | Holder foranderlig |
|---|---|
generic:ci-cache@* | alle versjoner av den generiske pakken ci-cache |
npm:@acme/ui@0.0.0-dev* | utviklingsbygg av én npm-pakke |
*-nightly | *-nightly-versjonene av alle pakker |
En organisasjon kan også slå pakkeuforanderlighet helt av, eller tilbake til
plattformens standard (inherit). Innstillingen vurderes når en skriving
skjer, og navnene på versjoner som ble slettet mens den var av, huskes
fortsatt. Å slå den på beskytter derfor også versjoner som ble slettet
tidligere.
Slå det på
Eiere av organisasjonen: Sikkerhet › Policyer › Uforanderlige utgivelser i
konsollen (/<org>/_admin/security/policies/immutable-releases), eller API-et
med et token som har policy:write (å lese innstillingene krever
findings:read):
curl -sS -X PUT -H "Authorization: Bearer $FFP_TOKEN" -H 'Content-Type: application/json' \
-d '{"releases":true,"allow_repo_opt_out":false,"oci_mode":"semver","oci_mutable_tags":["latest"]}' \
https://frem.sh/_app/api/v1/orgs/acme/code-security/immutabilityPakkeuforanderlighet bruker det samme endepunktet. packages er inherit
(følg plattformens standard, som er på), on eller off, og
package_mutable_versions lister unntakene. Svaret rapporterer også
packages_effective:
curl -sS -X PUT -H "Authorization: Bearer $FFP_TOKEN" -H 'Content-Type: application/json' \
-d '{"packages":"inherit","package_mutable_versions":["generic:ci-cache@*"]}' \
https://frem.sh/_app/api/v1/orgs/acme/code-security/immutabilityEt repository kan overstyre organisasjonen: on fryser alltid utgivelser der,
selv om organisasjonens standard er av; off velger det bort og respekteres
bare når organisasjonen tillater bortvalg; inherit følger organisasjonen.
curl -sS -X PUT -H "Authorization: Bearer $FFP_TOKEN" -H 'Content-Type: application/json' \
-d '{"releases":"on"}' https://frem.sh/_app/api/v1/orgs/acme/code-security/immutability/repos/webPull-speil fryses aldri: taggene deres tilhører repositoryet de speiler.
Utgivelsesattesteringen
For hver uforanderlige utgivelse signerer fremforge én
in-toto-erklæring av typen
https://in-toto.io/attestation/release/v0.1. Subjektene er:
- hver ressurs, med navn og SHA-256 (beregnet mens filen ble lastet opp);
- kilden,
git+https://frem.sh/<org>/<repo>@refs/tags/<tag>, medgitCommit.
Erklæringen hentes fra et manifest som Forgejo skriver i den samme transaksjonen som publiserer utgivelsen, og kontrolleres på nytt mot den aktive utgivelsen før den signeres. Den signeres med de samme nøklene som alle andre fremforge-attesteringer (en Ed25519-DSSE-signatur, pluss en nøkkelfri Sigstore-bunt fra fremforges egen Fulcio og tidsstempelmyndighet) og føres inn i transparensloggen. Attesteringen serveres av API-et og CLI-en; den legges aldri ved selve utgivelsen.
Signering forsinker aldri en publisering. En utgivelse publiseres først og
signeres kort tid etter, så en kort stund er attesteringen pending. Pending
er ikke godkjent: verifiseringen sier PENDING og avslutter med sin egen kode.
Verifisere en utgivelse
fremforge release verify acme/web v1.2.3 ./web-linux-amd64.tar.gz ./checksums.txtDen henter attesteringen, verifiserer signaturen mot den publiserte
tillitsroten (https://www.frem.sh/.well-known/slsa-trust-root.json), sjekker
at erklæringen gjelder dette repositoryet og denne taggen, hasher hver fil du
oppgir på din maskin og sammenligner med den signerte digesten, og sjekker
at konvolutten står i transparensloggen. En fil som utgivelsen ikke oppgir,
feiler.
| Avslutningskode | Betydning |
|---|---|
0 | PASS |
1 | FAIL (signaturen, erklæringen, digesten til en fil, loggen, eller attesteringen ble avvist) |
2 | bruksfeil |
3 | PENDING: publisert, ikke signert ennå; kjør på nytt om litt |
Frakoblet, helt uten nettverk, med konvolutten og en kopi av tillitsroten:
fremforge release verify acme/web v1.2.3 ./web-linux-amd64.tar.gz \
--offline --envelope=release.intoto.jsonl --trust-root=slsa-trust-root.jsonFrakoblet verifisering sjekker signaturen, erklæringen og filene dine; den kan ikke sjekke attesteringens status eller transparensloggen, og sier det.
Rådataene ligger på
GET /_app/api/v1/orgs/{org}/repos/{repo}/releases/{tag}/attestation
(findings:read): status (pending, signed, failed, unfrozen),
subjektene, manifestet og, når den er signert, kortlivede URL-er til
DSSE-konvolutten og Sigstore-bunten. Bare signed har en konvolutt.
Ikke dekket
- De automatisk genererte kildearkivene (
.zipog.tar.gzsom Forgejo tilbyr for hver tagg) attesteres ikke: bytene deres er ikke stabile på tvers av git-versjoner. Kildesubjektet oppgir i stedet taggens commit; verifiser en utsjekking medgit rev-parse v1.2.3^{commit}. - Utgivelser som ble publisert før uforanderlighet ble slått på, er ikke frosset og har ingen utgivelsesattestering.
Feil
| Flate | Format |
|---|---|
| Forgejo REST-API | 422 {"message": "immutable release: …"} |
| git push | remote: … pre-receive hook declined med begrunnelsen |
| Containerregister | 403 {"errors":[{"code":"DENIED", …}]} |
| fremforge-API | not-enabled, invalid-value, invalid-repo, forgejo-error, not-found |