ADR-0021: Signieren und Prüfen über die geteilten Dagger-Module, das Token holt der Workflow¶
- Status: accepted
- Datum: 2026-09-15
- Betrifft: Betrieb, Deployment, Lieferkette
- Bezug: supersedes
0020-signieren-und-pruefenin Entscheidung 3 („Signiert wird im Workflow, nicht in Dagger"); alles andere in ADR-0020 gilt weiter. Setzt #254 um. Die Module kommen aus stuttgart-things/dagger#376, #377, #378 und #379, veröffentlicht in v0.130.0 und v0.131.0.
Kontext¶
ADR-0020 hat das Signieren in ci.yml gelegt, als Shell zwischen
heruntergeladenen Werkzeugen: cosign, trivy und oras, drei Versionen, die
Renovate über einen eigenen Manager bewegen musste, und rund 120 Zeilen in
sign-image, sign-kustomize, verify-artefacts und verify-image-tags. Die
Gründe waren damals richtig: das OIDC-Token gehört dem Runner, und in
stuttgart-things/dagger konnte nichts signieren.
Seit v0.131.0 kann es. Das cosign-Modul signiert, attestiert und prüft,
trivy schreibt eine SBOM für eine Plattform, crane löst Tags zu Digests auf
und vergleicht zwei, kyverno testet eine Policy gegen erwartete Ergebnisse.
Vor dieser Entscheidung am 15.09. gegen die Artefakte geprüft, die main in
252 signiert hat:¶
| Aufruf | Ergebnis |
|---|---|
cosign verify, Image und Kustomize-Artefakt |
verifiziert, Subject …/ci.yml@refs/heads/main |
cosign verify-attestation --predicate-type cyclonedx |
die SBOM, 32 Komponenten |
cosign verify-refuses, fremde Identität |
abgelehnt, „refused, as it must" |
cosign sign mit Tag statt Digest |
abgelehnt, bevor ein Token gebraucht wird |
crane digest, crane same-digest |
die signierten Digests; ein fehlender Tag ist ein Fehler |
trivy sbom |
dieselben 32 Pakete wie die in CI attestierte SBOM |
kyverno test über policy/tests |
8 von 8 Erwartungen |
Nicht geprüft: sign und attest mit einem echten Token. Das kann nur ein
Lauf in dieser Pipeline.
Entscheidung¶
- Signieren, Attestieren und Prüfen laufen über die Module, aufgerufen mit
dagger call -mdirekt aus den Schritten inci.yml. Die Jobs installieren kein Werkzeug mehr;COSIGN_VERSION,TRIVY_VERSIONundORAS_VERSIONverschwinden ausci.ymlund mit ihnen der Renovate-Manager dafür. - Das Token holt der Workflow, nicht das Modul. Der Schritt fordert bei
Actions ein Token mit Audience
sigstorean, maskiert es und reicht es als Secret weiter.ACTIONS_ID_TOKEN_REQUEST_*gelangen nie in einen Container. Ein frisches Token pro signierendem Aufruf. - Die Identität bleibt, wie sie ist:
https://github.com/stuttgart-things/schmetterpause/.github/workflows/ci.yml@refs/…. Policy, Taskfile, Betriebsseite undscripts/signing_test.shändern sich dafür nicht. Vor dem Merge muss ein Lauf auf dem Pull Request dieses Subject inverify-artefactszeigen. - Die Modul-Versionen stehen im Taskfile, wie
KCL_MODULE:COSIGN_MODULE,CRANE_MODULE,TRIVY_MODULEundKYVERNO_MODULE, alle auf einem Tag. CI liest sie von dort;task verify:signature,task verify:sbomundtask policy:testrufen dieselben Referenzen auf. Renovate bewegt die vier gemeinsam. - Die Policy wird in der Pipeline getestet. Der Job
policy-testlässtkyverno testüberpolicy/testslaufen, mit der Kyverno-Version, die aufhomerun2-test1läuft (KYVERNO_VERSIONim Taskfile), und mit--warnings-as-errors. - Die Dagger-Engine in
ci.ymlgeht auf 0.21.9, die Version, die die Module deklarieren. Eine Engine für alle Aufrufe der Datei, nicht zwei.
Begründung¶
Warum die Einwände aus ADR-0020 nicht mehr tragen¶
„Das OIDC-Token gehört dem Runner." Das stimmt weiter, und deshalb fordert der Runner es an. Das Modul bekommt ein kurzlebiges Token, nicht die Berechtigung, eins anzufordern.
„task release sähe aus, als könnte es signieren." Nur, wenn das Signieren
in dagger/main.go läge. Es liegt in keiner Funktion dieses Repositorys: die
Pipeline ruft ein geteiltes Modul auf, und das Taskfile bietet nur die Prüfung
an, die ohne Token funktioniert.
„Das Kustomize-Artefakt kommt aus einem geteilten Workflow." Bleibt so, und
sign-kustomize bleibt ein Schritt danach. Nur der Digest kommt jetzt aus
crane statt aus oras | sed.
Warum dagger call -m aus dem Workflow und nicht aus dagger/main.go¶
dagger/main.go ist die Pipeline der Anwendung: task ci tut lokal dasselbe
wie in CI. Signieren ist dort nicht nachzustellen — ohne Token geht es
nirgends —, und die Module als Abhängigkeit in dagger.json einzutragen hieße,
dass jede Änderung an ihnen den Build der Anwendung berührt. Die Aufrufe
gehören zu den Schritten, die nach dem Push über veröffentlichte Artefakte
laufen, wie der Scan.
Warum kein geteilter Workflow¶
Fulcio schreibt den job_workflow_ref dessen, der das Token anfordert, als
Subject Alternative Name ins Zertifikat (pkg/identity/github/principal.go in
sigstore/fulcio). Forderte ein Template in github-workflow-templates das Token
an, signierte jedes Repository, das das Template nutzt, als dieselbe Identität.
Das Modul nimmt das Token nur entgegen; angefordert wird es hier.
Warum der Policy-Test in der Pipeline¶
Der erste Entwurf der Policy wurde von Kyverno 1.19.1 abgelehnt und übersah
Digest-Referenzen, und beides fiel nur auf, weil jemand von Hand geprüft hat
(ADR-0020). kyverno test prüft beide Richtungen: was abgewiesen werden muss
und was durchgehen muss. Die Version kommt aus dem Taskfile und nicht aus dem
Modul, weil der Test eine Aussage über den Cluster ist, auf dem die Policy
läuft.
Konsequenzen¶
- Positiv: Keine heruntergeladenen Binaries mehr in
ci.yml. Offener Punkt 6 aus ADR-0020 — Werkzeuge ohne Prüfsumme — gilt für cosign und trivy nicht mehr in dieser Form: sie kommen aus den Release-Images, die die Module pinnen. Nach Tag, nicht nach Digest. - Positiv: Die SBOM schreibt dasselbe trivy-Image wie der Scan, heute beide 0.64.1.
- Positiv:
verify-refusesist strenger als der Schritt, den es ersetzt.if cosign verify …; then exit 1wertete jeden Fehler als Ablehnung, auch eine nicht erreichbare Registry. Das Modul verlangt, dass cosign wegen der Identität ablehnt. - Positiv:
task verify:signatureundtask verify:sbombrauchen kein lokales cosign und kein jq mehr, nur Dagger. - Kosten: Eine Korrektur an einem Modul ist ein Release in
stuttgart-things/daggerund ein Pin-Bump hier. Der Trivy-404 aus #252 wäre so ein Fall gewesen. - Kosten: Jeder dieser Jobs startet eine eigene Dagger-Engine.
- Einschränkung: Der Policy-Test hängt an zwei veröffentlichten Releases,
v0.8.0(unsigniert) undv0.9.0(das erste signierte). Wird eins gelöscht, wird der Job rot. Er braucht außerdem Netz zughcr.iound Rekor.
Offene Punkte¶
signundattestmit echtem Token zeigen sich erst im ersten Lauf auf diesem Pull Request, danach aufmainund auf einem Release-Tag. Erledigt am 15.09.: auf #255 (…@refs/pull/255/merge), aufmain(62b6008,…@refs/heads/main) und am Tagv0.9.0(…@refs/tags/v0.9.0).- Die signierten Fixtures auf ein Release umstellen, sobald eins signiert
ist (v0.9.0, #253). Ein Release-Tag bleibt mit Absicht, ein Snapshot nur,
weil ihn niemand löscht. Erledigt am 15.09.: die Fixtures zeigen auf
v0.9.0. - Die Übung am Cluster aus ADR-0020 bleibt offen. Unter
Auditerledigt am 15.09. (ADR-0020, offener Punkt 1); dieDeny-Hälfte steht noch aus.