Gå til hovedindhold

Uforanderlige releases og release-attestation

En udgivet release er et løfte: disse filer, bygget fra denne commit, under dette tag. Uden uforanderlighed kan det løfte brydes i stilhed bagefter: et tag flyttes til en anden commit, et asset udskiftes med en anden binærfil, en release slettes og oprettes igen under samme navn. Uforanderlige releases gør hver af disse ting umulige, og en release-attestation lader enhver tjekke på sin egen maskine, at det, der blev hentet, er det, der blev udgivet.

Hvad der fryses

Når uforanderlige releases er slået til, fryses en release i det øjeblik, den udgives. Fra da af gælder:

ÆndringTilladt?
Redigere titlen, noterne eller pre-release-markeringenJa
Ændre tagnavnet eller mål-committenNej
Flytte tagget med git push --force eller slette det med git push --deleteNej
Gøre releasen til en kladde igenNej
Tilføje, omdøbe, erstatte eller slette et assetNej
Slette releasenJa, men dens tagnavn kan aldrig bruges igen i det repository

Alle overflader håndhæver samme regel: webbrugerfladen, REST API’et (HTTP 422 med en meddelelse, der starter med immutable release:) og git selv (pushet afvises af pre-receive-hooken med en meddelelse, der nævner tagget).

Frysning virker kun fremad. Når du slår indstillingen til, fryses intet, der allerede var udgivet, og når du slår den fra senere, tøs intet op. Hvis en frossen release virkelig skal ændres, så kontakt support: support kan ophæve frysningen af netop den release med en skriftlig begrundelse, der fremgår af din revisionslog.

Upload assets, før du udgiver releasen

En udgivet uforanderlig release tager ikke imod nye assets, så rækkefølgen betyder noget. Opret releasen som kladde, upload dine assets, og udgiv den derefter:

# 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_ID

Release-værktøjer, der opretter en udgivet release og uploader bagefter, vil fejle ved uploaden med 422. Assets, der kun er eksterne links, kan ikke være en del af en uforanderlig release, fordi der ikke er gemte bytes at tage et digest af; udgivelse af sådan en afvises.

Hvert asset har nu sit digest i API’et, ligesom på GitHub: "digest": "sha256:<hex>", og en uforanderlig release rapporterer "immutable": true.

Container-tags

Samme politik kan fryse tags på container-images i registret:

TilstandFrosset
off (standard)intet: alle tags kan overskrives
semverversionstags: v1.2.3, 1.2.3, 1.2.3-rc.1; latest, 1, 1.2, main, sha-… kan stadig flyttes
allalle tags undtagen de flytbare, du angiver (latest, edge-*, …)

Et push af et frossent tag, der peger det på et andet digest, afvises med registerfejlen DENIED (HTTP 403). Det er tilladt at pushe det samme image igen, så et CI-job, der køres igen, ikke går i stykker. Sletning af et frossent tag afvises, og oprydningsregler for pakker vælger aldrig et; frosne tags tæller stadig med i din lagerkvote. De signatur- og attestationstags, cosign skriver (sha256-<digest>.sig, .att, .sbom), kan altid flyttes. Container-tags vurderes efter den gældende politik, så du flytter eller sletter et bevidst ved at slå tilstanden fra.

Pakker

Udgivne pakkeversioner 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. Det er som standard slået til for alle organisationer. Containere følger i stedet tilstanden for container-tags ovenfor.

ÆndringTilladt?
Uploade en ny versionJa
Tilføje en ny fil til en eksisterende version (endnu et wheel til en platform, en Maven-classifier-jar)Ja
Uploade en fil igen med de samme bytes (et CI-job, der køres igen)Ja, ændrer intet
Erstatte en fil i en udgivet version med andre bytesNej
Slette en version eller en af dens filerJa
Udgive en slettet version igen, med hvilke bytes som helstNej: versionsnavnet forbliver pensioneret
Uploade en slettet fil igen med andre bytesNej

En afvist upload svarer HTTP 409 med en meddelelse, der starter med immutable package:, og svarheaderen X-Fremforge-Immutable fortæller hvorfor: overwrite, reupload eller retired. Hver afvisning skrives i din revisionslog som package.immutable.refused med pakken, versionen, filen, begge SHA-256-digests og hvem der forsøgte. Den vises også i registrets adgangslog som en blokeret upload.

De fleste registre afviser allerede at udgive en version to gange. Det, der ændres her:

  • Helm og Conan tillod tidligere, at en version blev overskrevet på stedet.
  • I alle registre kunne en version slettes og derefter udgives igen med andet indhold. Begge dele afvises nu.

Altid foranderlige:

  • Maven-versioner med -SNAPSHOT og maven-metadata.xml-filer, som omskrives efter hensigten.
  • De repository-indeksfiler, som Debian-, RPM-, Alpine- og Arch-registre vedligeholder.

For at holde andre versioner foranderlige, for eksempel en generisk pakke brugt som CI-cache, kan du angive dem som undtagelser på formen [<type>:][<name>@]<version>, hvor * og ? virker som jokertegn:

PostHolder foranderlig
generic:ci-cache@*alle versioner af den generiske pakke ci-cache
npm:@acme/ui@0.0.0-dev*udviklingsbuilds af én npm-pakke
*-nightlyalle pakkers *-nightly-versioner

En organisation kan også slå uforanderlighed for pakker helt fra eller tilbage til platformens standard (inherit). Indstillingen vurderes, når en skrivning sker, og navnene på versioner, der blev slettet, mens den var slået fra, huskes stadig. Når du slår den til, beskytter den derfor også versioner, der blev slettet tidligere.

Sådan slår du det til

Organisationens ejere: Sikkerhed › Politikker › Uforanderlige releases i konsollen (/<org>/_admin/security/policies/immutable-releases) eller API’et med et token, der har policy:write (læsning af indstillingerne kræver 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/immutability

Uforanderlighed for pakker bruger samme endpoint. packages er inherit (følg platformens standard, som er slået til), on eller off, og package_mutable_versions angiver undtagelserne. 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/immutability

Et repository kan tilsidesætte organisationen: on fryser altid releases dér, også mens organisationens standard er slået fra; off fravælger det og respekteres kun, når organisationen tillader fravalg; inherit følger organisationen.

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/web

Pull-mirrors fryses aldrig: deres tags tilhører det repository, de spejler.

Release-attestationen

For hver uforanderlig release signerer fremforge én in-toto-erklæring af typen https://in-toto.io/attestation/release/v0.1. Dens subjekter er:

  • hvert asset, ved navn og SHA-256 (beregnet, mens filen blev uploadet);
  • kilden, git+https://frem.sh/<org>/<repo>@refs/tags/<tag>, ved gitCommit.

Erklæringen tages fra et manifest, som Forgejo skriver i samme transaktion, som udgiver releasen, og kontrolleres igen mod den aktuelle release, før den signeres. Den signeres med de samme nøgler som alle andre attestationer fra fremforge (en Ed25519 DSSE-signatur plus et nøglefrit Sigstore-bundle fra fremforges egen Fulcio og tidsstempelmyndighed) og registreres i transparensloggen. Attestationen leveres af API’et og CLI’en; den vedhæftes aldrig selve releasen.

Signering forsinker aldrig en udgivelse. En release udgives først og signeres kort efter, så i et kort stykke tid er dens attestation pending. Pending er ikke en godkendelse: verifikationen siger PENDING og afslutter med sin egen kode.

Verificér en release

fremforge release verify acme/web v1.2.3 ./web-linux-amd64.tar.gz ./checksums.txt

Kommandoen henter attestationen, verificerer signaturen mod den udgivne trust root (https://www.frem.sh/.well-known/slsa-trust-root.json), tjekker, at erklæringen gælder dette repository og tag, hasher hver fil, du angiver, på din maskine og sammenligner med det signerede digest, og tjekker, at envelopen er i transparensloggen. En fil, som releasen ikke nævner, fejler.

Exit-kodeBetydning
0PASS
1FAIL (signaturen, erklæringen, en fils digest, loggen, eller attestationen blev afvist)
2forkert brug
3PENDING: udgivet, endnu ikke signeret; kør igen om lidt

Offline, helt uden netværk, med envelopen og en kopi af trust root:

fremforge release verify acme/web v1.2.3 ./web-linux-amd64.tar.gz \
  --offline --envelope=release.intoto.jsonl --trust-root=slsa-trust-root.json

Offline-verifikation tjekker signaturen, erklæringen og dine filer; den kan ikke tjekke attestationens status eller transparensloggen og siger det.

De rå data findes på GET /_app/api/v1/orgs/{org}/repos/{repo}/releases/{tag}/attestation (findings:read): status (pending, signed, failed, unfrozen), subjekterne, manifestet og, når den er signeret, kortlivede URL’er til DSSE-envelopen og Sigstore-bundlet. Kun signed har en envelope.

Ikke dækket

  • De automatisk genererede kildearkiver (de .zip- og .tar.gz-filer, Forgejo tilbyder for hvert tag) attesteres ikke: deres bytes er ikke stabile på tværs af git-versioner. Kildesubjektet navngiver i stedet taggets commit; verificér en checkout med git rev-parse v1.2.3^{commit}.
  • Releases, der blev udgivet, før uforanderlighed blev slået til, er ikke frosne og har ingen release-attestation.

Fejl

OverfladeForm
Forgejos REST API422 {"message": "immutable release: …"}
git pushremote: … pre-receive hook declined med årsagen
Container-register403 {"errors":[{"code":"DENIED", …}]}
fremforges APInot-enabled, invalid-value, invalid-repo, forgejo-error, not-found