This means that in addition to a signature, each distroless image now has an associated signed provenance. This provenance is an in-toto attestation and includes information around how each image was built, what command was run, and what build system was used. It also includes any special parameters that were passed in, the exact commit the images were built at, and more. This provenance is a useful tool for builds that need to be audited in the future.
Achieving SLSA 2 required some changes to the distroless build pipeline: we set up Tekton Pipelines and Tekton Chains in a GKE cluster to automate building images and generating provenance. Every time a pull request is merged to the distroless Github repo, a Tekton Pipeline is triggered. This Pipeline builds the distroless images, and Tekton Chains is responsible for generating signed provenance for each image. Tekton Chains stores the signed provenance alongside the image in an OCI registry and also stores a record of the provenance in the rekor transparency log.
Don't trust us?
You can try the build yourself. Because distroless builds are reproducible, all the information to replicate the build is in the provenance, and you or a trusted third party can build the image yourselves and verify the build is correct by matching image digests.
You can verify an attestation for a distroless image with cosign and the distroless public key:
$ cosign verify-attestation -key cosign.pub gcr.io/distroless/base@sha256:4f8aa0aba190e375a5a53bb71a303c89d9734c817714aeaca9bb23b82135ed91
Verification for gcr.io/distroless/base@sha256:4f8aa0aba190e375a5a53bb71a303c89d9734c817714aeaca9bb23b82135ed91 --
The following checks were performed on each of these signatures:
- The cosign claims were validated
- The signatures were verified against the specified public key
- Any certificates were verified against the Fulcio roots.
...
And you can find the provenance for the image in the rekor transparency log with the rekor-cli tool. For example, you could find the provenance for the above image by using the image’s digest and running:
$ rekor-cli search --sha sha256:4f8aa0aba190e375a5a53bb71a303c89d9734c817714aeaca9bb23b82135ed91
af7a9687d263504ccdb2759169c9903d8760775045c6e7554e365ec2bf29f6f8
$ rekor-cli get --uuid af7a9687d263504ccdb2759169c9903d8760775045c6e7554e365ec2bf29f6f8 --format json | jq -r .Attestation | base64 --decode | jq
{
"_type": "distroless-provenance",
"predicateType": "https://tekton.dev/chains/provenance",
"subject": [
{
"name": "gcr.io/distroless/base",
"digest": {
"sha256": "703a4726aedc9ec7a7e32251087565246db117bb9a141a7993d1c4bb4036660d"
}
},
{
"name": "gcr.io/distroless/base",
"digest": {
"sha256": "d322ed16d530596c37eee3eb57a039677502aa71f0e4739b0272b1ebd8be9bce"
}
},
{
"name": "gcr.io/distroless/base",
"digest": {
"sha256": "2dfdd5bf591d0da3f67a25f3fc96d929b256d5be3e0af084db10952e5da2c661"
}
},
{
"name": "gcr.io/distroless/base",
"digest": {
"sha256": "4f8aa0aba190e375a5a53bb71a303c89d9734c817714aeaca9bb23b82135ed91"
}
},
{
"name": "gcr.io/distroless/base",
"digest": {
"sha256": "dc0a793d83196a239abf3ba035b3d1a0c7a24184856c2649666e84bc82fc5980"
}
},
{
"name": "gcr.io/distroless/base-debian10",
"digest": {
"sha256": "2dfdd5bf591d0da3f67a25f3fc96d929b256d5be3e0af084db10952e5da2c661"
}
},
{
"name": "gcr.io/distroless/base-debian10",
"digest": {
"sha256": "703a4726aedc9ec7a7e32251087565246db117bb9a141a7993d1c4bb4036660d"
}
},
{
"name": "gcr.io/distroless/base-debian10",
"digest": {
"sha256": "4f8aa0aba190e375a5a53bb71a303c89d9734c817714aeaca9bb23b82135ed91"
}
},
{
"name": "gcr.io/distroless/base-debian10",
"digest": {
"sha256": "d322ed16d530596c37eee3eb57a039677502aa71f0e4739b0272b1ebd8be9bce"
}
},
{
"name": "gcr.io/distroless/base-debian10",
"digest": {
"sha256": "dc0a793d83196a239abf3ba035b3d1a0c7a24184856c2649666e84bc82fc5980"
}
},
{
"name": "gcr.io/distroless/base-debian11",
"digest": {
"sha256": "c9507268813f235b11e63a7ae01526b180c94858bd718d6b4746c9c0e8425f7a"
}
},
{
"name": "gcr.io/distroless/cc",
"digest": {
"sha256": "4af613acf571a1b86b1d3c50682caada0b82024e566c1c4c2fe485a70f3af47d"
}
},
{
"name": "gcr.io/distroless/cc",
"digest": {
"sha256": "2c4bb6b7236db0a55ec54ba8845e4031f5db2be957ac61867872bf42e56c4deb"
}
},
{
"name": "gcr.io/distroless/cc",
"digest": {
"sha256": "2c4bb6b7236db0a55ec54ba8845e4031f5db2be957ac61867872bf42e56c4deb"
}
},
{
"name": "gcr.io/distroless/cc-debian10",
"digest": {
"sha256": "4af613acf571a1b86b1d3c50682caada0b82024e566c1c4c2fe485a70f3af47d"
}
},
{
"name": "gcr.io/distroless/cc-debian10",
"digest": {
"sha256": "2c4bb6b7236db0a55ec54ba8845e4031f5db2be957ac61867872bf42e56c4deb"
}
},
{
"name": "gcr.io/distroless/cc-debian10",
"digest": {
"sha256": "2c4bb6b7236db0a55ec54ba8845e4031f5db2be957ac61867872bf42e56c4deb"
}
},
{
"name": "gcr.io/distroless/java",
"digest": {
"sha256": "deb41661be772c6256194eb1df6b526cc95a6f60e5f5b740dda2769b20778c51"
}
},
{
"name": "gcr.io/distroless/nodejs",
"digest": {
"sha256": "927dd07e7373e1883469c95f4ecb31fe63c3acd104aac1655e15cfa9ae0899bf"
}
},
{
"name": "gcr.io/distroless/nodejs",
"digest": {
"sha256": "927dd07e7373e1883469c95f4ecb31fe63c3acd104aac1655e15cfa9ae0899bf"
}
},
{
"name": "gcr.io/distroless/nodejs",
"digest": {
"sha256": "f106757268ab4e650b032e78df0372a35914ed346c219359b58b3d863ad9fb58"
}
},
{
"name": "gcr.io/distroless/nodejs-debian10",
"digest": {
"sha256": "927dd07e7373e1883469c95f4ecb31fe63c3acd104aac1655e15cfa9ae0899bf"
}
},
{
"name": "gcr.io/distroless/nodejs-debian10",
"digest": {
"sha256": "f106757268ab4e650b032e78df0372a35914ed346c219359b58b3d863ad9fb58"
}
},
{
"name": "gcr.io/distroless/nodejs-debian10",
"digest": {
"sha256": "927dd07e7373e1883469c95f4ecb31fe63c3acd104aac1655e15cfa9ae0899bf"
}
},
{
"name": "gcr.io/distroless/python3",
"digest": {
"sha256": "aa8a0358b2813e8b48a54c7504316c7dcea59d6ae50daa0228847de852c83878"
}
},
{
"name": "gcr.io/distroless/python3-debian10",
"digest": {
"sha256": "aa8a0358b2813e8b48a54c7504316c7dcea59d6ae50daa0228847de852c83878"
}
},
{
"name": "gcr.io/distroless/static",
"digest": {
"sha256": "9acfd1fdf62b26cbd4f3c31422cf1edf3b7b01a9ecee00a499ef8b7e3536914d"
}
},
{
"name": "gcr.io/distroless/static",
"digest": {
"sha256": "e50641dbb871f78831f9aa7ffa59ec8f44d4cc33ae4ee992c9f4b046040e97f2"
}
},
{
"name": "gcr.io/distroless/static-debian10",
"digest": {
"sha256": "9acfd1fdf62b26cbd4f3c31422cf1edf3b7b01a9ecee00a499ef8b7e3536914d"
}
},
{
"name": "gcr.io/distroless/static-debian10",
"digest": {
"sha256": "e50641dbb871f78831f9aa7ffa59ec8f44d4cc33ae4ee992c9f4b046040e97f2"
}
}
],
"predicate": {
"invocation": {
"parameters": [
"MANIFEST_SUBSECTION={string 0 []}",
"CHAINS-GIT_COMMIT={string 976c1c9bc178ac0371d8888d69893145c3df09f0 []}",
"CHAINS-GIT_URL={string https://github.com/GoogleContainerTools/distroless []}"
],
"recipe_uri": "task://distroless-provenance",
"event_id": "531c282f-806e-41e4-b3ad-b596c4283381",
"builder.id": "tekton-chains"
},
"recipe": {
"steps": [
{
"entryPoint": "#!/bin/sh\nset -ex\n\n# get the digests for a subset of images built, and store in the IMAGES result\ngo run provenance/provenance.go images $(params.MANIFEST_SUBSECTION) > $(results.IMAGES.path)\n",
"arguments": null,
"environment": {
"container": "provenance",
"image": "docker.io/library/golang@sha256:cb1a7482cb5cfc52527c5cdea5159419292360087d5249e3fe5472f3477be642"
},
"annotations": null
}
]
},
"metadata": {
"buildStartedOn": "2021-09-16T00:03:04Z",
"buildFinishedOn": "2021-09-16T00:04:36Z"
},
"materials": [
{
"uri": "https://github.com/GoogleContainerTools/distroless",
"digest": {
"revision": "976c1c9bc178ac0371d8888d69893145c3df09f0"
}
}
]
}
}
As you might guess, our next step is getting distroless to SLSA 3, which will require adding non-falsifiable provenance and isolated builds to the distroless supply chain. Stay tuned for more!
No comments:
Post a Comment
You are welcome to contribute comments, but they should be relevant to the conversation. We reserve the right to remove off-topic remarks in the interest of keeping the conversation focused and engaging. Shameless self-promotion is well, shameless, and will get canned.
Note: Only a member of this blog may post a comment.