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:
| Ændring | Tilladt? |
|---|---|
| Redigere titlen, noterne eller pre-release-markeringen | Ja |
| Ændre tagnavnet eller mål-committen | Nej |
Flytte tagget med git push --force eller slette det med git push --delete | Nej |
| Gøre releasen til en kladde igen | Nej |
| Tilføje, omdøbe, erstatte eller slette et asset | Nej |
| Slette releasen | Ja, 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_IDRelease-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:
| Tilstand | Frosset |
|---|---|
off (standard) | intet: alle tags kan overskrives |
semver | versionstags: v1.2.3, 1.2.3, 1.2.3-rc.1; latest, 1, 1.2, main, sha-… kan stadig flyttes |
all | alle 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.
| Ændring | Tilladt? |
|---|---|
| Uploade en ny version | Ja |
| 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 bytes | Nej |
| Slette en version eller en af dens filer | Ja |
| Udgive en slettet version igen, med hvilke bytes som helst | Nej: versionsnavnet forbliver pensioneret |
| Uploade en slettet fil igen med andre bytes | Nej |
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
-SNAPSHOTogmaven-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:
| Post | Holder foranderlig |
|---|---|
generic:ci-cache@* | alle versioner af den generiske pakke ci-cache |
npm:@acme/ui@0.0.0-dev* | udviklingsbuilds af én npm-pakke |
*-nightly | alle 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/immutabilityUforanderlighed 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/immutabilityEt 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/webPull-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>, vedgitCommit.
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.txtKommandoen 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-kode | Betydning |
|---|---|
0 | PASS |
1 | FAIL (signaturen, erklæringen, en fils digest, loggen, eller attestationen blev afvist) |
2 | forkert brug |
3 | PENDING: 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.jsonOffline-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 medgit 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
| Overflade | Form |
|---|---|
| Forgejos REST API | 422 {"message": "immutable release: …"} |
| git push | remote: … pre-receive hook declined med årsagen |
| Container-register | 403 {"errors":[{"code":"DENIED", …}]} |
| fremforges API | not-enabled, invalid-value, invalid-repo, forgejo-error, not-found |