Imagesignaturer: signer med cosign, håndhev ved pull
fremforge sjekker cosign-signaturen på hvert containerimage organisasjonen din puller fra frem.sh. Du bestemmer hvem sine signaturer du stoler på, og om en pull uten signatur skal registreres (shadow) eller avvises (enforce).
Sjekken kjøres på den nøyaktige image-digesten som leveres, i det øyeblikket pullen skjer, så den gjelder likt for docker pull, Kubernetes, CI-jobber og alle andre OCI-klienter.
Signer et image i fremforge Actions
fremforge kjører sin egen Sigstore: en sertifikatutsteder (Fulcio) på sign.frem.sh og en tidsstempeltjeneste etter RFC 3161 på tsa.frem.sh. Den kjører ingen transparenslogg (ingen Rekor) og ingen logg for sertifikattransparens: tidsstempelet fra tsa.frem.sh er det som beviser når imaget ble signert.
Tillitsroten
Rotsertifikatet til sign.frem.sh og sertifikatkjeden til tsa.frem.sh er publisert på https://www.frem.sh/.well-known/slsa-trust-root.json, som keyless.fulcio_root_ca_pem og keyless.tsa_cert_chain_pem. Hent dem én gang og committ dem til repositoryet som signerer eller verifiserer: en hostet runner når sign.frem.sh og tsa.frem.sh, men ikke 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 subjekt er CN=fremforge sigstore Root CA, O=fremverk, C=DE.
En jobb signerer med sin egen kortlivede identitet, så det finnes ingen signeringsnøkkel å lagre eller rotere. Be runnerens OIDC-endepunkt om et token med audiencen sigstore. Sertifikatet navngir da workflowen din:
https://frem.sh/<org>/<repo>/.forgejo/workflows/<file>@<ref>med utstederen https://frem.sh/_app/runner/oidc. Workflowen trenger enable-openid-connect: true på toppnivå (Forgejo utleder det ikke fra permissions), og jobben trenger 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 er installert i imaget til den hostede runneren. Signer digesten (image@sha256:…), ikke en tag: signaturen er bundet til digesten.
Verifiser en signatur
Bygg trusted-root.json med den samme kommandoen cosign trusted-root create, og navngi deretter den nøyaktige workflowen som må ha signert:
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-issuerer alltidhttps://frem.sh/_app/runner/oidcfor en jobb i fremforge Actions.--certificate-identityerhttps://frem.sh/<org>/<repo>/.forgejo/workflows/<file>@<ref>, der<ref>er hele refen jobben kjørte på (refs/heads/main,refs/tags/v1.2.0). For å godta alle tagger bruker du--certificate-identity-regexp '^https://frem\.sh/acme/api/\.forgejo/workflows/release\.yaml@refs/tags/v.+$'.--use-signed-timestampshenter signeringstidspunktet fra tidsstempelet fratsa.frem.sh, og--insecure-ignore-tloghopper over sjekken mot transparensloggen, fordi det ikke finnes noen Rekor.--insecure-ignore-scter påkrevd fordi det ikke finnes noen logg for sertifikattransparens, så sertifikatene har ikke noe signert sertifikattidsstempel (SCT). Hva det koster: ingen offentlig logg der et feilaktig utstedt sertifikat ville dukket opp. Sertifikatet må fortsatt kjede opp til fremforge-roten over, bære nøyaktig den utstederen og identiteten du navngir, og være gyldig på tidspunktettsa.frem.shsignerte.
Andre måter å signere på fungerer også:
- Et nøkkelpar du forvalter selv:
cosign sign --key cosign.key. Legg den offentlige nøkkelen (cosign.pub) til i tillitspolicyen. - Nøkkelfritt fra GitHub Actions (den offentlige Sigstore): slå på Also trust the public Sigstore. Det verifiseres offline mot en fastlåst tillitsrot; ingenting sendes til sigstore.dev når et image pulles.
Begge cosign-formatene leses: standard bundle-format i cosign v3 (lagret under taggen sha256-<digest>, fordi registeret ikke har noe referrers-API) og det eldre .sig-formatet.
Tillitspolicyen
Sett den under Sikkerhet › Policyer › Imagepolicyer › Imagesignaturer ved henting, med API-et eller med Terraform eller OpenTofu.
| Innstilling | Betydning |
|---|---|
enabled | Sjekk signaturer når et image pulles. |
mode | shadow (standard): lever imaget og registrer en would-block. enforce: avvis pullen med 403. |
signers | Klarerte nøkkelfrie signerere, én liste. Hver er enten en issuer alene, som klarerer alle subjekter den utstederen sertifiserer, eller en issuer og en subject_regex som hele sertifikatsubjektet må matche. |
keys | Klarerte offentlige nøkler: ECDSA P-256 eller P-384, Ed25519, eller RSA på minst 2048 biter. |
scope | Hvilke imager den gjelder: imagenavn i organisasjonen din, eventuelt med en tag (api, team/*, api:prod-*). Tom betyr alle imager. |
trust_public_sigstore | Klarer også signaturer laget med den offentlige Sigstore. |
En pull godkjennes når minst én signatur for den leverte digesten verifiseres og signereren er klarert: en av nøklene dine, eller et sertifikat som matcher en signerer. Hvis både signers og keys er tomme, er bare jobbene i organisasjonens egen fremforge Actions klarert: et sertifikat fra fremforges Sigstore med utstederen https://frem.sh/_app/runner/oidc og et subjekt som begynner med https://frem.sh/<your org>/. Navngi workflowene du mener, så snart du kan.
Avgrensning etter tag dekker også pulls etter digest. En digest navngir ingen tag, så en oppføring som api:prod-* gjelder også for hver pull av api@sha256:…. Ellers ville en pull etter digest vært en vei rundt den.
I konsollen er Trusted signers ett tekstfelt: én signerer per linje, utsteder-URL-en, deretter eventuelt et mellomrom og subjekt-regexen. En utsteder alene klarerer alt den sertifiserer, så for en offentlig utsteder som GitHub Actions eller fremforge Actions bør du alltid legge til et subjekt. En liste siden avviser, kommer tilbake med årsaken og det du skrev, og ingenting lagres.
En subjekt-regex kan ikke gjenta en gruppe som selv gjentas (for eksempel (a+)+), og kan ikke bruke tilbakereferanser eller lookaround: slike mønstre kan ta eksponentiell tid, og de avvises når du lagrer.
Hva en pull får
| Digesten … | shadow | enforce |
|---|---|---|
| har en signatur fra en klarert signerer | levert | levert |
| er ikke signert | levert, registry.pull.would_block | 403 DENIED, registry.pull.blocked |
| er signert, men ikke av en klarert signerer | levert, would-block | 403, blokkert |
| har en signatur som ikke verifiseres, eller som navngir et annet image | levert, would-block | 403, blokkert |
| kan ikke sjekkes (registeret svarte ikke) | levert, logget | 503 med Retry-After |
403-svaret sier hvilket tilfelle det var, navngir signereren når det finnes en, og lenker til innstillingssiden.
To typer manifester sjekkes ikke: signaturene, attesteringene og SBOM-ene som er knyttet til imager (slik at cosign verify selv fortsetter å fungere), og andre OCI-artefakter som Helm-charts. Dette avgjøres ut fra selve manifestet, ikke ut fra taggen.
Imager for flere plattformer. cosign signerer vanligvis image-indeksen. Når en indeks godkjennes, godkjennes også plattformmanifestene, så den påfølgende pullen av linux/amd64 etter digest leveres.
Et verifisert resultat gjenbrukes i en time, og et mislykket i ett minutt, så en signatur du pusher etter en avvisning, teller innen ett minutt. En endring i tillitspolicyen gjelder umiddelbart.
Rull det ut
- Slå på sjekken i
shadowog gi den noen dager. - Les would-blocks i revisjonsloggen (
registry.pull.would_blockmedgate: signature) eller under Recent signature checks på innstillingssiden. - Signer det som mangler, snevre inn
scopeom nødvendig, og bytt deretter tilenforce.
API
GET og PUT /orgs/{org}/image-signature-policy på https://frem.sh/_app/api/v1, med policy:read og policy:write. PUT erstatter hele policyen: et felt du utelater, får standardverdien.
Frem til 2026-10-16 var klarerte signerere to felt, issuers (en utsteder med hvilket som helst subjekt) og identities (en utsteder med en subjekt-regex). Begge fungerer fortsatt, og GET returnerer dem fortsatt ved siden av signers. En PUT kan sende den ene eller den andre formen, eller alle tre når de stemmer overens, så en les-endre-skriv av de gamle feltene fortsetter å fungere. Felt som ikke stemmer overens, avvises 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 ugyldig policy avvises med 400 invalid-value, som navngir feltet.
Revisjon og SIEM
- Hver endring:
security_policy.image_signature.updated, med hva som ble endret. - Hver avgjørelse:
registry.pull.blockedellerregistry.pull.would_blockmedgate: signatureogreason(unsigned,untrustedellerinvalid), høyst én gang i timen per image-digest, med antallet fra forrige time. Disse når SIEM-strømmen din som alle andre revisjonshendelser, og en blokkering utløser også varsel-webhookensecurity.pull.blocked.
Signaturfunn i pull requests
På hver pull request sjekker fremforge signaturene til containerimagene repositoryet ditt låser etter digest, det vil si en image:-linje som slutter på @sha256:…. Sjekken ser i YAML-filer, Helm-maler, Dockerfiles og Containerfiles, og dekker opptil 50 imager per skanning. Resultatet er statusen fremforge/cosign-verify på pull requesten og kolonnen Cosign på siden Container images.
Hvert image får ett av disse resultatene:
- Signert: en signatur ble verifisert, og signereren vises. To typer signerere er klarert:
- organisasjonens egne workflower i fremforge Actions, signert gjennom fremforges Sigstore og sjekket mot det klarerte tidsstempelet;
- en signerer i den offentlige Sigstore, sjekket mot dens transparenslogg.
- Ikke signert: registeret svarte og har ingen signatur fra en klarert signerer. En signatur laget av en annen organisasjons workflow regnes som ikke signert.
- No trusted timestamp: organisasjonens egen workflow signerte imaget gjennom fremforges Sigstore, men uten det klarerte tidsstempelet. En slik signatur kan ikke sjekkes etter at det kortlivede sertifikatet har utløpt, så den regnes som ikke signert. Signer på nytt med signeringskonfigurasjonen i Signer et image i fremforge Actions.
- Could not check: sjekken kunne ikke lese signaturen. Siden Container images viser årsaken, for eksempel at registeret ikke betjenes av avhengighetsproxyen din, at imaget ligger i en annen organisasjons register, eller at registeret ikke svarte. Et could not check-resultat rapporteres aldri som not signed.
Sjekken leser imager gjennom organisasjonens avhengighetsproxy, fra organisasjonens frem.sh-register og fra et privat register organisasjonen har legitimasjon til. Den verifiserer offline, så den kontakter aldri imagets register eller Sigstore selv. Begge cosign-formatene leses.
Imager i et privat register
Et image i et register utenfor fremforge, for eksempel registry.gitlab.com/acme/tool@sha256:… eller en privat Harbor, sjekkes når organisasjonen din har lagret en skrivebeskyttet legitimasjon for den registerverten:
- Slå det på og legg til legitimasjon under Sikkerhet › Tilgang › Registerlegitimasjon (eller via API-et for registerlegitimasjon). Dette krever en eier av organisasjonen, og funksjonen er av til en eier slår den på.
- Én legitimasjon per vert,
kind: oci, med en påkrevd utløpstid på høyst 366 dager. Docker Hub, ghcr.io, quay.io og Red Hat-registrene står ikke på denne listen: imager derfra bruker i stedet organisasjonens upstream-legitimasjon i avhengighetsproxyen. - Legitimasjonen blir værende i fremforge. Avhengighetsproxyen bruker den til å lese imagets signatur, og bare for en skanning av organisasjonen din. Skanneren har den aldri, den sendes aldri til en annen vert, og ingenting som leses med den, bufres.
Når legitimasjonen ikke kan brukes, blir imaget Could not check, med én av disse årsakene:
- expired: legitimasjonen har passert utløpstiden. En eier endrer utløpstiden eller erstatter den under Registerlegitimasjon;
- refused: registeret svarte 401 eller 403. En eier sjekker legitimasjonen eller erstatter den under Registerlegitimasjon;
- rate limited: sjekken nådde lesegrensen for legitimasjonen. fremforge sjekker igjen senere;
- unusable: fremforge kunne ikke bruke den lagrede legitimasjonen. Feilen er fremforges, og den sjekker igjen senere.
Et register organisasjonen ikke har legitimasjon til, forblir Could not check, fordi registeret ikke betjenes av avhengighetsproxyen din.
Statusen er informativ: den godkjennes alltid og blokkerer aldri en merge. Pull-sjekken over er en egen sjekk.
Imager som fremforge leverer fra sitt eget plattformregister, viser «signed by fremforge» når fremforges utgivelsesbygg signerte dem. For disse imagene er det bygget den eneste klarerte signereren, og det klareres aldri for dine egne imager.
Se også
- Imageskanning og Ondsinnede pakker: de andre pull-sjekkene.
- SBOM-er.