Image-signaturer: signér med cosign, håndhæv ved hentning
fremforge tjekker cosign-signaturen på hvert container-image, din organisation henter fra frem.sh. Du bestemmer, hvis signaturer du stoler på, og om en hentning uden en signatur registreres (shadow) eller afvises (enforce).
Tjekket kører på præcis den image-digest, der leveres, i det øjeblik den hentes, så det gælder lige meget for docker pull, Kubernetes, CI-jobs og enhver anden OCI-klient.
Signér et image i fremforge Actions
fremforge kører sin egen Sigstore: en certifikatudsteder (Fulcio) på sign.frem.sh og en RFC 3161-tidsstempelmyndighed på tsa.frem.sh. Den kører ingen transparenslog (ingen Rekor) og ingen certificate transparency-log: det er tidsstemplet fra tsa.frem.sh, der beviser, hvornår imaget blev signeret.
Tillidsroden
Rodcertifikatet for sign.frem.sh og certifikatkæden for tsa.frem.sh offentliggøres 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 commit dem til det repository, der signerer eller verificerer: en hostet runner kan nå 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.pemRodens subject er CN=fremforge sigstore Root CA, O=fremverk, C=DE.
Et job signerer med sin egen kortlivede identitet, så der er ingen signeringsnøgle at opbevare eller rotere. Bed runnerens OIDC-endpoint om et token med audience sigstore. Certifikatet navngiver så dit workflow:
https://frem.sh/<org>/<repo>/.forgejo/workflows/<file>@<ref>med udstederen https://frem.sh/_app/runner/oidc. Workflowet skal have enable-openid-connect: true på øverste niveau (Forgejo udleder det ikke af permissions) og jobbet 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 installeret i den hostede runners image. Signér digesten (image@sha256:…), ikke et tag: signaturen er bundet til digesten.
Verificér en signatur
Byg trusted-root.json med den samme cosign trusted-root create-kommando, og angiv så præcis det workflow, der skal have signeret:
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 altidhttps://frem.sh/_app/runner/oidcfor et job i fremforge Actions.--certificate-identityerhttps://frem.sh/<org>/<repo>/.forgejo/workflows/<file>@<ref>, hvor<ref>er den fulde ref, jobbet kørte på (refs/heads/main,refs/tags/v1.2.0). Vil du acceptere alle tags, så brug--certificate-identity-regexp '^https://frem\.sh/acme/api/\.forgejo/workflows/release\.yaml@refs/tags/v.+$'.--use-signed-timestampstager signeringstidspunktet fra tidsstemplet fratsa.frem.sh, og--insecure-ignore-tlogspringer tjekket af transparensloggen over, fordi der ikke er nogen Rekor.--insecure-ignore-scter påkrævet, fordi der ikke er nogen certificate transparency-log, så certifikaterne har intet signed certificate timestamp (SCT). Det koster: ingen offentlig log, hvor et fejlagtigt udstedt certifikat ville dukke op. Certifikatet skal stadig kæde til fremforge-roden ovenfor, have præcis den udsteder og identitet, du angiver, og være gyldigt på det tidspunkt,tsa.frem.shsignerede.
Andre måder at signere på virker også:
- Et nøglepar, du selv administrerer:
cosign sign --key cosign.key. Tilføj den offentlige nøgle (cosign.pub) til tillidspolitikken. - Nøgleløs GitHub Actions (offentlig Sigstore): slå Stol også på den offentlige Sigstore til. Den verificeres offline mod en fastlåst tillidsrod; intet sendes til sigstore.dev, når et image hentes.
Begge cosign-formater læses: cosign v3’s standardformat, bundle (gemt under tagget sha256-<digest>, fordi registryet ikke har et referrers-API), og det ældre .sig-format.
Tillidspolitikken
Sæt den under Sikkerhed › Politikker › Image-politikker › Image-signaturer ved hentning, med API’et eller med Terraform eller OpenTofu.
| Indstilling | Betydning |
|---|---|
enabled | Tjek signaturer, når et image hentes. |
mode | shadow (standard): lever imaget, og registrér en would-block. enforce: afvis hentningen med 403. |
signers | Betroede nøgleløse underskrivere, én liste. Hver er enten en issuer alene, som stoler på ethvert subject, udstederen certificerer, eller en issuer og et subject_regex, som hele certifikatets subject skal matche. |
keys | Betroede offentlige nøgler: ECDSA P-256 eller P-384, Ed25519 eller RSA på mindst 2048 bit. |
scope | Hvilke images den gælder for: image-navne i din organisation, eventuelt med et tag (api, team/*, api:prod-*). Tom betyder alle images. |
trust_public_sigstore | Stol også på signaturer lavet med den offentlige Sigstore. |
En hentning går igennem, når mindst én signatur på den leverede digest kan verificeres, og dens underskriver er betroet: en af dine nøgler eller et certifikat, der matcher en underskriver. Er både signers og keys tomme, er det kun din egen organisations jobs i fremforge Actions, der er betroet: et certifikat fra fremforges Sigstore med udstederen https://frem.sh/_app/runner/oidc og et subject, der starter med https://frem.sh/<your org>/. Angiv de workflows, du mener, så snart du kan.
Afgrænsning efter tag dækker også hentning efter digest. En digest navngiver intet tag, så en post som api:prod-* gælder også for enhver hentning af api@sha256:…. Ellers ville hentning efter digest være en vej uden om den.
I konsollen er Betroede underskrivere ét tekstfelt: én underskriver pr. linje, udstederens URL og eventuelt et mellemrum og subject-regex’en. En udsteder alene stoler på alt, den certificerer, så for en offentlig udsteder som GitHub Actions eller fremforge Actions skal du altid tilføje et subject. En liste, som siden afviser, kommer tilbage med årsagen og det, du skrev, og intet gemmes.
En subject-regex må ikke gentage en gruppe, der selv gentages (for eksempel (a+)+), og må ikke bruge back-references eller lookaround: sådanne mønstre kan tage eksponentiel tid og afvises, når du gemmer.
Hvad en hentning får
| Digesten… | shadow | enforce |
|---|---|---|
| har en signatur fra en betroet underskriver | leveres | leveres |
| er ikke signeret | leveres, registry.pull.would_block | 403 DENIED, registry.pull.blocked |
| er signeret, men ikke af en betroet underskriver | leveres, would-block | 403, blokeret |
| har en signatur, der ikke kan verificeres, eller som navngiver et andet image | leveres, would-block | 403, blokeret |
| kan ikke tjekkes (registryet svarede ikke) | leveres, logges | 503 med Retry-After |
403-svaret siger, hvilket tilfælde det var, navngiver underskriveren, når der er en, og linker til indstillingssiden.
To slags manifester tjekkes ikke: signaturer, attestationer og SBOM’er, der er knyttet til images (så cosign verify selv bliver ved med at virke), og andre OCI-artefakter som Helm-charts. Det afgøres ud fra selve manifestet, ikke ud fra dets tag.
Multi-platform-images. cosign signerer normalt image-indekset. Når et indeks går igennem, gør dets platformsmanifester det også, så den efterfølgende hentning af linux/amd64 efter digest leveres.
Et verificeret resultat genbruges i en time og et mislykket i et minut, så en signatur, du pusher efter en afvisning, tæller inden for et minut. En ændring af tillidspolitikken træder i kraft med det samme.
Rul det ud
- Slå tjekket til i
shadow, og giv det et par dage. - Læs would-blocks i revisionsloggen (
registry.pull.would_blockmedgate: signature) eller under Seneste signaturtjek på indstillingssiden. - Signér det, der mangler, indsnævr
scopeom nødvendigt, og skift så 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 politikken: et felt, du udelader, får sin standardværdi.
Indtil 2026-10-16 var betroede underskrivere to felter, issuers (en udsteder med et vilkårligt subject) og identities (en udsteder med en subject-regex). Begge virker stadig, og GET returnerer dem stadig ved siden af signers. En PUT kan sende den ene eller den anden form eller alle tre, når de stemmer overens, så en læs-ændr-skriv af de gamle felter bliver ved med at virke. Felter, der ikke stemmer overens, afvises 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 politik afvises med 400 invalid-value, der navngiver feltet.
Revision og SIEM
- Hver ændring:
security_policy.image_signature.updatedmed det, der blev ændret. - Hver afgørelse:
registry.pull.blockedellerregistry.pull.would_blockmedgate: signatureogreason(unsigned,untrustedellerinvalid), højst én gang i timen pr. image-digest, med antallet fra den foregående time. De når din SIEM-strøm som alle andre revisionshændelser, og en blokering udløser også advarsels-webhookensecurity.pull.blocked.
Signaturfund i pull requests
På hver pull request tjekker fremforge signaturerne på de container-images, dit repository fastlåser med digest, altså en image:-linje, der ender på @sha256:…. Tjekket kigger i YAML-filer, Helm-skabeloner, Dockerfiles og Containerfiles og dækker op til 50 images pr. scanning. Resultatet er statussen fremforge/cosign-verify på pull requesten og kolonnen Cosign på siden Container-images.
Hvert image får et af disse resultater:
- Signeret: en signatur blev verificeret, og underskriveren vises. To slags underskrivere er betroede:
- din organisations egne workflows i fremforge Actions, signeret gennem fremforges Sigstore og tjekket på det betroede tidsstempel;
- en underskriver i den offentlige Sigstore, tjekket mod dens transparenslog.
- Ikke signeret: registryet svarede og har ingen signatur fra en betroet underskriver. En signatur lavet af en anden organisations workflow tæller som ikke signeret.
- Intet betroet tidsstempel: din organisations eget workflow signerede imaget gennem fremforges Sigstore, men uden det betroede tidsstempel. Sådan en signatur kan ikke tjekkes, når dens kortlivede certifikat er udløbet, så den tæller som ikke signeret. Signér igen med signeringskonfigurationen under Signér et image i fremforge Actions.
- Kunne ikke tjekkes: tjekket kunne ikke læse signaturen. Siden Container-images viser årsagen, for eksempel at registryet ikke betjenes af din afhængighedsproxy, at imaget ligger i en anden organisations registry, eller at registryet ikke svarede. Et resultat kunne ikke tjekkes rapporteres aldrig som ikke signeret.
Tjekket læser images gennem din organisations afhængighedsproxy, fra din organisations registry på frem.sh og fra et privat registry, som din organisation har legitimationsoplysninger til. Det verificerer offline, så det kontakter aldrig selv imagets registry eller Sigstore. Begge cosign-formater læses.
Images i et privat registry
Et image i et registry uden for fremforge, for eksempel registry.gitlab.com/acme/tool@sha256:… eller en privat Harbor, tjekkes, når din organisation har gemt skrivebeskyttede legitimationsoplysninger til den registry-vært:
- Slå det til, og tilføj legitimationsoplysninger under Sikkerhed › Adgang › Registerlegitimationer (eller via API’et for registerlegitimationer). Det kræver en ejer af organisationen, og funktionen er slået fra, indtil en ejer slår den til.
- Én legitimation pr. vært,
kind: oci, med en påkrævet udløbsdato på højst 366 dage. Docker Hub, ghcr.io, quay.io og Red Hats registries er ikke på denne liste: images derfra bruger i stedet din organisations upstream-legitimationsoplysninger i afhængighedsproxyen. - Legitimationsoplysningerne forbliver i fremforge. Afhængighedsproxyen bruger dem til at læse imagets signatur og kun til en scanning for din organisation. Scanneren har dem aldrig, de sendes aldrig til en anden vært, og intet, der læses med dem, caches.
Når legitimationsoplysningerne ikke kan bruges, er imaget Kunne ikke tjekkes med en af disse årsager:
- udløbet: legitimationen er udløbet. En ejer ændrer udløbsdatoen eller erstatter den under Registerlegitimationer;
- afvist: registryet svarede 401 eller 403. En ejer tjekker legitimationen eller erstatter den under Registerlegitimationer;
- begrænset: tjekket nåede læsegrænsen for legitimationen. fremforge tjekker igen senere;
- ubrugelig: fremforge kunne ikke bruge den gemte legitimation. Fejlen er fremforges, og den tjekker igen senere.
Et registry, som din organisation ikke har legitimationsoplysninger til, forbliver Kunne ikke tjekkes, fordi registryet ikke betjenes af din afhængighedsproxy.
Statussen er kun til orientering: den består altid og blokerer aldrig en merge. Tjekket ved hentning ovenfor er et separat tjek.
Images, som fremforge leverer fra sit eget platform-registry, vises som “signeret af fremforge”, når fremforges release-build signerede dem. For de images er det build den eneste betroede underskriver, og den er aldrig betroet for dine egne images.
Se også
- Image-scanning og Ondsindede pakker: de andre tjek ved hentning.
- SBOM’er.