| 1 | # Supply chain notes |
| 2 | |
| 3 | ## The threat model |
| 4 | |
| 5 | The attacks this defends against are the ones where the source code is fine but the |
| 6 | artifact isn't: |
| 7 | |
| 8 | - a registry account is compromised and a tag is repointed at a malicious image |
| 9 | - a build runner is tampered with and produces a backdoored image |
| 10 | - a dependency pulled at build time has a known CVE that nobody looked at |
| 11 | - an image is deployed that nobody can actually account for the contents of |
| 12 | |
| 13 | Source-level scanning (the other repo) does nothing for any of these. They all |
| 14 | happen at or after build time, which is why this is a separate layer. |
| 15 | |
| 16 | ## How Sigstore answers each one |
| 17 | |
| 18 | - **Repointed tag** - the Kyverno policy resolves tags to digests on admission and |
| 19 | requires a signature over that digest. Moving a tag doesn't move the signature. |
| 20 | - **Tampered runner** - the signature is tied to the OIDC identity of the workflow |
| 21 | run. An image built somewhere else can't produce a signature from |
| 22 | `https://github.com/zionboggan/...`. |
| 23 | - **Vulnerable dependency** - grype scans the built image and fails the build on a |
| 24 | high or critical finding before signing happens, so unsigned-and-vulnerable never |
| 25 | even gets to the registry as a release. |
| 26 | - **Unaccountable contents** - the SBOM is generated from the actual image, signed |
| 27 | as an attestation, and required at admission. No SBOM, no deploy. |
| 28 | |
| 29 | ## Transparency log |
| 30 | |
| 31 | Every signature and attestation is recorded in Rekor. That gives an auditable, |
| 32 | append-only record of what was signed, by which identity, and when. If a signing |
| 33 | identity is ever misused, the log is where you'd find every artifact it touched. |
| 34 | |
| 35 | ## Why digests everywhere |
| 36 | |
| 37 | Tags are mutable; digests are not. Signing a tag is signing a pointer. Every step |
| 38 | here - build output, scan target, sign, attest, verify, and the admission rewrite - |
| 39 | operates on `@sha256:...`. The image that runs in the cluster is byte-for-byte the |
| 40 | image that was signed. |
| 41 | |
| 42 | ## Rotating to keys |
| 43 | |
| 44 | Keyless suits CI because there's no secret to hold. For an offline or air-gapped |
| 45 | verifier where you can't reach Fulcio/Rekor at verify time, generate a key pair |
| 46 | (`cosign generate-key-pair`) or back it with a KMS, sign with `--key`, and verify |
| 47 | with the public key. The pipeline structure is identical; only the attestor changes. |