chalk
chalk copied to clipboard
add support for provenance during buildx builds
github docker push action now by default enables provenance builds --provenance
flag in docker buildx build
command:
https://docs.docker.com/build/attestations/slsa-provenance/
How it works is that instead of building a an image manifest to the registry, it sends a manifest list where one of the manifests is not for an os/arch but is instead a SLSA document. As such a --push
is required to use in conjunction with --provenance
(or sbom)
This in turn is not compatible with chalk as internally chalk normalizes:
docker buildx build --push ...
# to
docker buildx build --load ...
docker push ...
however --load
is not compatible with --provenance
for the same reason as why its not compatible with building multi-platform images - docker cannot import manifest lists. The way chalk currently handles multi-platform builds is it:
docker buildx build --platform=a,b ...
# to
docker buildx build --platform=a ...
docker push a .....
docker buildx build --platform=b ...
docker push b .....
docker buildx imagetools create ...
The same trick cannot be applied to provenance builds and so in order to support it chalk will need to natively handle manifest lists.
The reason why chalk needed to import the image locally is so that it can fully inspect it to add that metadata to the chalk report. As it turns out that is not strictly necessary. The docker image configuration (what chalk was inspecting) is available by interacting with the registry (via buildx imagetools inspect
). https://github.com/crashappsec/chalk/pull/147 uses that to inspect the entrypoint/cmd/shell of the image in the registry. We can use the same approach for inspecting/reporting manifest lists:
docker buildx build --push --metadata-file=/tmp/... ....
the metadata file will contain digests of the images which were pushed to the registry. we can use that digest to query the contianer metadata directly from the registry which contains most of the data chalk needs:
➜ docker buildx imagetools inspect nginx@sha256:a8758716bb6aa4d90071160d27028fe4eaee7ce8166221a97d30440c8eac2be6 --raw | jq '.config'
{
"ExposedPorts": {
"80/tcp": {}
},
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"NGINX_VERSION=1.25.3",
"NJS_VERSION=0.8.2",
"PKG_RELEASE=1~bookworm"
],
"Entrypoint": [
"/docker-entrypoint.sh"
],
"Cmd": [
"nginx",
"-g",
"daemon off;"
],
"Labels": {
"maintainer": "NGINX Docker Maintainers <[email protected]>"
},
"StopSignal": "SIGQUIT",
"ArgsEscaped": true,
"OnBuild": null
}
https://github.com/crashappsec/chalk/blob/27749bea6adf2e2e22d5f0cd5ff6651573eca53e/src/plugins/codecDocker.nim#L224-L264 https://docs.docker.com/engine/api/v1.44/#tag/Image/operation/ImageInspect
registry will have most of the data. some exceptions:
- registry will have the compressed size of the image, not uncompressed which
docker inspect
provides - chalk currently has a few fields such as
_IMAGE_LAST_TAG_TIME
which are in docker explicitly local cache-only. I think we can safely remove them as reporting anything from cache-only is not going to be reproducible so might be just noisy
with this approach the chalk flow will be:
- run
buildx build --push ...
with--metadata-file
(add if missing) - inspect pushed containers directly from the registry
- send report as usual
there are going to be nuances on how dockerfile wrapping works as well need to support multi-platform builds but it is def possible via BUILDTARGET
args:
FROM alpine as chalk
RUN mkdir /linux
RUN touch /linux/amd64
RUN touch /linux/arm64
FROM alpine
ARG TARGETPLATFORM
ARG BUILDPLATFORM
COPY --from=chalk /$TARGETPLATFORM /$TARGETPLATFORM
with that approach chalk will be able to copy platform-specific chalk binaries into each built image as well as copy platform-specific chalk.json
with unique METADATA_ID
per platform.
so all in all supporting manifest lists is not that bad given we can inspect the image from the registry. if we switch to this approach this will automatically support any new features docker adds such as sbom manifests or anything else docker adds in the future without trying to shim it on the build phase itself