Image-signaturer: signera med cosign, tillämpa vid pull
fremforge kontrollerar cosign-signaturen för varje container-image som din organisation hämtar från frem.sh. Du bestämmer vems signaturer du litar på, och om en pull utan signatur ska registreras (skuggläge) eller avvisas (tillämpning).
Kontrollen körs på exakt den image-digest som levereras, i samma ögonblick som pullen sker, så den gäller lika för docker pull, Kubernetes, CI-jobb och alla andra OCI-klienter.
Signera en image i fremforge Actions
fremforge kör sin egen Sigstore: en certifikatutfärdare (Fulcio) på sign.frem.sh och en tidsstämplingstjänst enligt RFC 3161 på tsa.frem.sh. Den kör ingen transparenslogg (ingen Rekor) och ingen logg för certifikattransparens: tidsstämpeln från tsa.frem.sh är det som bevisar när imagen signerades.
Förtroenderoten
Rotcertifikatet för sign.frem.sh och certifikatkedjan för tsa.frem.sh publiceras på https://www.frem.sh/.well-known/slsa-trust-root.json, som keyless.fulcio_root_ca_pem och keyless.tsa_cert_chain_pem. Hämta dem en gång och checka in dem i det repo som signerar eller verifierar: en hostad runner når sign.frem.sh och tsa.frem.sh, men inte www.frem.sh.
curl -sSf https://www.frem.sh/.well-known/slsa-trust-root.json -o slsa-trust-root.json
jq -r .keyless.fulcio_root_ca_pem slsa-trust-root.json > fulcio-root.pem
jq -r .keyless.tsa_cert_chain_pem slsa-trust-root.json > tsa-chain.pemRotens subject är CN=fremforge sigstore Root CA, O=fremverk, C=DE.
Ett jobb signerar med sin egen kortlivade identitet, så det finns ingen signeringsnyckel att lagra eller rotera. Be runnerns OIDC-slutpunkt om en token med audience sigstore. Certifikatet anger då ditt arbetsflöde:
https://frem.sh/<org>/<repo>/.forgejo/workflows/<file>@<ref>med utfärdaren https://frem.sh/_app/runner/oidc. Arbetsflödet behöver enable-openid-connect: true på översta nivån (Forgejo härleder det inte från permissions) och jobbet behöver id-token: write:
enable-openid-connect: true
jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- name: Sign the image
env:
IMAGE: frem.sh/acme/api@${{ env.DIGEST }}
TOKEN: ${{ secrets.FORGEJO_TOKEN }}
run: |
set -euo pipefail
ID_TOKEN=$(curl -sSf -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
"$ACTIONS_ID_TOKEN_REQUEST_URL&audience=sigstore" | jq -r .value)
# fulcio-root.pem and tsa-chain.pem: committed from the published
# trust root (see "The trust root" above).
cosign trusted-root create \
--fulcio "url=https://sign.frem.sh,certificate-chain=fulcio-root.pem" \
--tsa "url=https://tsa.frem.sh/api/v1/timestamp,certificate-chain=tsa-chain.pem" \
--out trusted-root.json
cosign signing-config create \
--fulcio="url=https://sign.frem.sh,api-version=1,start-time=2026-01-01T00:00:00Z,operator=frem.sh" \
--tsa="url=https://tsa.frem.sh/api/v1/timestamp,api-version=1,start-time=2026-01-01T00:00:00Z,operator=frem.sh" \
--tsa-config="EXACT:1" --out signing-config.json
echo "$TOKEN" | cosign login frem.sh -u ci --password-stdin
cosign sign --yes --signing-config signing-config.json \
--trusted-root trusted-root.json --identity-token "$ID_TOKEN" "$IMAGE"cosign är installerat i den hostade runnerns image. Signera digesten (image@sha256:…), inte en tagg: signaturen är bunden till digesten.
Verifiera en signatur
Bygg trusted-root.json med samma kommando cosign trusted-root create, och ange sedan exakt vilket arbetsflöde som måste ha signerat:
cosign verify --trusted-root trusted-root.json \
--use-signed-timestamps --insecure-ignore-tlog --insecure-ignore-sct \
--certificate-oidc-issuer https://frem.sh/_app/runner/oidc \
--certificate-identity https://frem.sh/acme/api/.forgejo/workflows/release.yaml@refs/heads/main \
frem.sh/acme/api@sha256:…--certificate-oidc-issuerär alltidhttps://frem.sh/_app/runner/oidcför ett jobb i fremforge Actions.--certificate-identityärhttps://frem.sh/<org>/<repo>/.forgejo/workflows/<file>@<ref>, där<ref>är den fullständiga ref som jobbet kördes på (refs/heads/main,refs/tags/v1.2.0). För att godta alla taggar, använd--certificate-identity-regexp '^https://frem\.sh/acme/api/\.forgejo/workflows/release\.yaml@refs/tags/v.+$'.--use-signed-timestampstar signeringstiden från tidsstämpeln fråntsa.frem.sh, och--insecure-ignore-tloghoppar över kontrollen mot transparensloggen, eftersom det inte finns någon Rekor.--insecure-ignore-sctkrävs eftersom det inte finns någon logg för certifikattransparens, så certifikaten har ingen signerad certifikattidsstämpel (SCT). Vad det kostar: det finns ingen publik logg där ett felaktigt utfärdat certifikat skulle synas. Certifikatet måste fortfarande kedja till fremforges rot ovan, ha exakt den utfärdare och identitet som du anger, och vara giltigt vid den tidpunkt dåtsa.frem.shsignerade.
Andra sätt att signera fungerar också:
- Ett nyckelpar som du hanterar:
cosign sign --key cosign.key. Lägg till den publika nyckeln (cosign.pub) i förtroendepolicyn. - Nyckellöst från GitHub Actions (publika Sigstore): slå på Lita även på den publika Sigstore. Det verifieras offline mot en låst förtroenderot; inget skickas till sigstore.dev när en image hämtas.
Båda cosign-formaten läses: standardformatet bundle i cosign v3 (lagrat under taggen sha256-<digest>, eftersom registret inte har något referrers-API) och det äldre formatet .sig.
Förtroendepolicyn
Ställ in den under Säkerhet › Policyer › Image-policyer › Image-signaturer vid pull, med API:t, eller med Terraform eller OpenTofu.
| Inställning | Betydelse |
|---|---|
enabled | Kontrollera signaturer när en image hämtas. |
mode | shadow (standard): leverera imagen och registrera en would-block. enforce: avvisa pullen med 403. |
signers | Betrodda nyckellösa signerare, en lista. Var och en är en issuer ensam, som litar på alla subjects som den utfärdaren certifierar, eller en issuer och en subject_regex som hela certifikatets subject måste matcha. |
keys | Betrodda publika nycklar: ECDSA P-256 eller P-384, Ed25519, eller RSA på minst 2048 bitar. |
scope | Vilka images den gäller: image-namn i din organisation, eventuellt med en tagg (api, team/*, api:prod-*). Tomt betyder alla images. |
trust_public_sigstore | Lita även på signaturer som gjorts med den publika Sigstore. |
En pull godkänns när minst en signatur för den levererade digesten verifieras och dess signerare är betrodd: en av dina nycklar, eller ett certifikat som matchar en signerare. Om både signers och keys är tomma är bara din egen organisations jobb i fremforge Actions betrodda: ett certifikat från fremforges Sigstore med utfärdaren https://frem.sh/_app/runner/oidc och ett subject som börjar med https://frem.sh/<your org>/. Ange de arbetsflöden du menar så snart du kan.
Avgränsning efter tagg täcker även pull via digest. En digest anger ingen tagg, så en post som api:prod-* gäller även varje pull av api@sha256:…. Annars skulle en pull via digest vara ett sätt att kringgå den.
I konsolen är Betrodda signerare (nyckellösa) ett enda textfält: en signerare per rad, utfärdarens URL, sedan eventuellt ett mellanslag och subject-regexen. En utfärdare ensam litar på allt den certifierar, så för en publik utfärdare som GitHub Actions eller fremforge Actions bör du alltid lägga till ett subject. En lista som sidan avvisar kommer tillbaka med orsaken och det du skrev, och inget sparas.
En subject-regex får inte upprepa en grupp som i sig upprepas (till exempel (a+)+), och får inte använda bakåtreferenser eller lookaround: sådana mönster kan ta exponentiell tid, och de avvisas när du sparar.
Vad en pull får
| Digesten… | shadow | enforce |
|---|---|---|
| har en signatur från en betrodd signerare | levereras | levereras |
| är inte signerad | levereras, registry.pull.would_block | 403 DENIED, registry.pull.blocked |
| är signerad, men inte av en betrodd signerare | levereras, would-block | 403, blockerad |
| har en signatur som inte verifieras, eller som anger en annan image | levereras, would-block | 403, blockerad |
| kan inte kontrolleras (registret svarade inte) | levereras, loggas | 503 med Retry-After |
403-svaret anger vilket fall det var, namnger signeraren när det finns en, och länkar till inställningssidan.
Två sorters manifest kontrolleras inte: de signaturer, attesteringar och SBOM:er som är kopplade till images (så att cosign verify självt fortsätter att fungera), och andra OCI-artefakter som Helm-charts. Det avgörs utifrån själva manifestet, inte utifrån dess tagg.
Images för flera plattformar. cosign signerar normalt image-indexet. När ett index godkänns godkänns även dess plattformsmanifest, så den efterföljande pullen av linux/amd64 via digest levereras.
Ett verifierat resultat återanvänds i en timme, och ett misslyckat i en minut, så en signatur som du pushar efter ett avslag räknas inom en minut. En ändring av förtroendepolicyn gäller direkt.
Rulla ut den
- Slå på kontrollen i läget
shadowoch låt den gå några dagar. - Läs would-blocks i granskningsloggen (
registry.pull.would_blockmedgate: signature) eller under Senaste signaturkontroller på inställningssidan. - Signera det som saknas, begränsa
scopeom det behövs, och byt sedan tillenforce.
API
GET och PUT /orgs/{org}/image-signature-policy på https://frem.sh/_app/api/v1, med policy:read och policy:write. PUT ersätter hela policyn: ett fält du utelämnar får sitt standardvärde.
Fram till 2026-10-16 var betrodda signerare två fält, issuers (en utfärdare med vilket subject som helst) och identities (en utfärdare med en subject-regex). Båda fungerar fortfarande och GET returnerar dem fortfarande, bredvid signers. En PUT får skicka vilken form som helst, eller alla tre när de stämmer överens, så att en läs-ändra-skriv av de gamla fälten fortsätter att fungera. Fält som inte stämmer överens avvisas med 400 invalid-value.
curl -sS -X PUT -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
"https://frem.sh/_app/api/v1/orgs/acme/image-signature-policy" \
-d '{"enabled": true, "mode": "shadow",
"signers": [{"issuer": "https://frem.sh/_app/runner/oidc",
"subject_regex": "https://frem\\.sh/acme/[^/]+/\\.forgejo/workflows/release\\.yaml@refs/tags/v.+"}],
"scope": ["api", "worker:prod-*"]}'En ogiltig policy avvisas med 400 invalid-value, som anger fältet.
Granskning och SIEM
- Varje ändring:
security_policy.image_signature.updated, med vad som ändrades. - Varje beslut:
registry.pull.blockedellerregistry.pull.would_blockmedgate: signatureochreason(unsigned,untrustedellerinvalid), högst en gång i timmen per image-digest, med föregående timmes antal. De når din SIEM-ström som alla andra granskningshändelser, och en blockering utlöser också larm-webhookensecurity.pull.blocked.
Signaturfynd i pull requests
I varje pull request kontrollerar fremforge signaturerna för de container-images som ditt repo låser via digest, det vill säga en rad image: som slutar med @sha256:…. Kontrollen letar i YAML-filer, Helm-mallar, Dockerfiles och Containerfiles, och täcker upp till 50 images per skanning. Resultatet är statusen fremforge/cosign-verify på pull requesten och kolumnen Cosign på sidan Container-images.
Varje image får ett av följande resultat:
- Signerad: en signatur verifierades, och signeraren visas. Två sorters signerare är betrodda:
- din organisations egna arbetsflöden i fremforge Actions, signerade via fremforges Sigstore och kontrollerade mot den betrodda tidsstämpeln;
- en signerare i den publika Sigstore, kontrollerad mot dess transparenslogg.
- Inte signerad: registret svarade och har ingen signatur från en betrodd signerare. En signatur som gjorts av en annan organisations arbetsflöde räknas som inte signerad.
- Ingen betrodd tidsstämpel: din organisations eget arbetsflöde signerade imagen via fremforges Sigstore, men utan den betrodda tidsstämpeln. En sådan signatur kan inte kontrolleras när dess kortlivade certifikat har gått ut, så den räknas som inte signerad. Signera om med signeringskonfigurationen i Signera en image i fremforge Actions.
- Kunde inte kontrolleras: kontrollen kunde inte läsa signaturen. Sidan Container-images visar orsaken, till exempel att registret inte betjänas av din beroendeproxy, att imagen ligger i en annan organisations register, eller att registret inte svarade. Resultatet kunde inte kontrolleras rapporteras aldrig som inte signerad.
Kontrollen läser images via din organisations beroendeproxy, från din organisations register på frem.sh, och från ett privat register som din organisation har en inloggningsuppgift för. Den verifierar offline, så den kontaktar aldrig själv imagens register eller Sigstore. Båda cosign-formaten läses.
Images i ett privat register
En image i ett register utanför fremforge, till exempel registry.gitlab.com/acme/tool@sha256:… eller en privat Harbor, kontrolleras när din organisation har sparat en skrivskyddad inloggningsuppgift för det registrets värd:
- Slå på funktionen och lägg till en inloggningsuppgift under Säkerhet › Åtkomst › Registerinloggningar (eller via API:t för registerinloggningar). Det kräver en ägare av organisationen, och funktionen är avstängd tills en ägare slår på den.
- En inloggningsuppgift per värd,
kind: oci, med ett obligatoriskt utgångsdatum på högst 366 dagar. Docker Hub, ghcr.io, quay.io och Red Hats register finns inte i den här listan: images därifrån använder i stället din organisations Inloggningsuppgifter för upstream i beroendeproxyn. - Inloggningsuppgiften stannar inom fremforge. Beroendeproxyn använder den för att läsa imagens signatur, och bara för en skanning av din organisation. Skannern har den aldrig, den skickas aldrig till en annan värd, och inget som läses med den cachas.
När inloggningsuppgiften inte kan användas blir imagen Kunde inte kontrolleras, med en av följande orsaker:
- har gått ut: inloggningsuppgiften har passerat sitt utgångsdatum. En ägare ändrar utgångsdatumet eller ersätter den under Registerinloggningar;
- avvisad: registret svarade 401 eller 403. En ägare kontrollerar inloggningsuppgiften eller ersätter den under Registerinloggningar;
- hastighetsbegränsad: kontrollen nådde läsgränsen för inloggningsuppgiften. fremforge kontrollerar igen senare;
- oanvändbar: fremforge kunde inte använda den sparade inloggningsuppgiften. Felet ligger hos fremforge, som kontrollerar igen senare.
Ett register som din organisation inte har någon inloggningsuppgift för förblir Kunde inte kontrolleras, eftersom registret inte betjänas av din beroendeproxy.
Statusen är informativ: den godkänns alltid och blockerar aldrig en merge. Kontrollen vid pull ovan är en separat kontroll.
Images som fremforge levererar från sitt eget plattformsregister visar “signed by fremforge” när fremforges release-bygge har signerat dem. För de imagesen är det bygget den enda betrodda signeraren, och det är aldrig betrott för dina egna images.
Se även
- Image-skanning och Skadliga paket: de andra kontrollerna vid pull.
- SBOM:er.