TIL - Docker signature verification
Docker hardened images are signed with cosign
Image publishers usually only sign the multi-platform tag. This should provide enough trust since the multi-platform tag does not point to a sinlge docker image but to a manifest list which provides a list of all different tags for each platform with its digest.
Inspecting a multi-platform image
Lets take the aquasec/trivy image as example. We can use docker to retrieve the manifest with:
docker buildx imagetools inspect --raw aquasec/trivy@sha256:be1190afcb28352bfddc4ddeb71470835d16462af68d310f9f4bca710961a41e
Here we see the list of different platform images with their digests:
{
"schemaVersion": 2,
"mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
"manifests": [
{
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"size": 1159,
"digest": "sha256:85e87be1a96459c38a4eea47dc64eb2d342bb14cd4b4cef96adcf6ff03378b7c",
"platform": {
"architecture": "amd64",
"os": "linux"
}
},
{
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"size": 1159,
"digest": "sha256:06c0a1359e4d745c6d709f5b64421db79fe0b5602c87d6eb5d6ba7d3d79d91ab",
"platform": {
"architecture": "arm64",
"os": "linux"
}
},
{
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"size": 1159,
"digest": "sha256:6d2abdf6a6fcaa5d48921f0b372bee9ea3f4c29d028eec3e3e5a2d90d87feadf",
"platform": {
"architecture": "ppc64le",
"os": "linux"
}
},
{
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"size": 1159,
"digest": "sha256:1e480b4dc4d70168b6eeeebb64d101954a1e310cd8d4ba8c88c77c990659d63e",
"platform": {
"architecture": "s390x",
"os": "linux"
}
}
]
}
Checking image signature with cosign
This can be done with cosign verify, which verifies a signature and annotations on an image by checking the claims against the transparency log. Fulcio, which has such a good documentation, is commonly used as certificate authority.
To verify that the image was signed using the oidc announced by the publisher use the args –certificate-oidc-issuer and -certificate-identity-regexp. Cosign will check that the Fulcio certificate was signed using those values.
Lets see the output of a verified multi-platform tag:
cosign verify aquasec/trivy@sha256:be1190afcb28352bfddc4ddeb71470835d16462af68d310f9f4bca710961a41e \
--certificate-identity-regexp 'https://github.com/aquasecurity/trivy/.*' \
--certificate-oidc-issuer 'https://token.actions.githubusercontent.com'
This will return:
[
{
"critical": {
"identity": {
"docker-reference": "index.docker.io/aquasec/trivy:0.70.0"
},
"image": {
"docker-manifest-digest": "sha256:be1190afcb28352bfddc4ddeb71470835d16462af68d310f9f4bca710961a41e"
},
"type": "https://sigstore.dev/cosign/sign/v1"
},
"optional": {}
},
{
"critical": {
"identity": {
"docker-reference": "index.docker.io/aquasec/trivy:0.70.0"
},
"image": {
"docker-manifest-digest": "sha256:be1190afcb28352bfddc4ddeb71470835d16462af68d310f9f4bca710961a41e"
},
"type": "https://sigstore.dev/cosign/sign/v1"
},
"optional": {}
}
]
And lets compare that to a tag which was not signed, lets take a platform tag from above:
cosign verify aquasec/trivy@sha256:85e87be1a96459c38a4eea47dc64eb2d342bb14cd4b4cef96adcf6ff03378b7c \
--certificate-identity-regexp 'https://github.com/aquasecurity/trivy/.*' \
--certificate-oidc-issuer 'https://token.actions.githubusercontent.com'
Error: no signatures found
error during command execution: no signatures found
Or if we try to verify the multi-platform tag with an different issuer:
cosign verify aquasec/trivy@sha256:be1190afcb28352bfddc4ddeb71470835d16462af68d310f9f4bca710961a41e \
--certificate-identity-regexp 'https://github.com/aquasecurity/trivy/.*' \
--certificate-oidc-issuer 'https://google.com'
Error: no matching attestations: failed to verify certificate identity: no matching CertificateIdentity found, last error: expected issuer value "https://google.com", got "https://token.actions.githubusercontent.com"
failed to verify certificate identity: no matching CertificateIdentity found, last error: expected issuer value "https://google.com", got "https://token.actions.githubusercontent.com"
error during command execution: no matching attestations: failed to verify certificate identity: no matching CertificateIdentity found, last error: expected issuer value "https://google.com", got "https://token.actions.githubusercontent.com"
failed to verify certificate identity: no matching CertificateIdentity found, last error: expected issuer value "https://google.com", got "https://token.actions.githubusercontent.com"
A useful error which can help identify the correct issuer.
Similarly if use the wrong the identity (note the missing ‘y’ in trivy), a useful error message wil be given:
cosign verify aquasec/trivy@sha256:be1190afcb28352bfddc4ddeb71470835d16462af68d310f9f4bca710961a41e \
--certificate-identity-regexp 'https://github.com/aquasecurity/triv/.*' \
--certificate-oidc-issuer 'https://token.actions.githubusercontent.com'
Error: no matching attestations: failed to verify certificate identity: no matching CertificateIdentity found, last error: expected SAN value to match regex "https://github.com/aquasecurity/triv/.*", got "https://github.com/aquasecurity/trivy/.github/workflows/reusable-release.yaml@refs/tags/v0.70.0"
failed to verify certificate identity: no matching CertificateIdentity found, last error: expected SAN value to match regex "https://github.com/aquasecurity/triv/.*", got "https://github.com/aquasecurity/trivy/.github/workflows/reusable-release.yaml@refs/tags/v0.70.0"
error during command execution: no matching attestations: failed to verify certificate identity: no matching CertificateIdentity found, last error: expected SAN value to match regex "https://github.com/aquasecurity/triv/.*", got "https://github.com/aquasecurity/trivy/.github/workflows/reusable-release.yaml@refs/tags/v0.70.0"
failed to verify certificate identity: no matching CertificateIdentity found, last error: expected SAN value to match regex "https://github.com/aquasecurity/triv/.*", got "https://github.com/aquasecurity/trivy/.github/workflows/reusable-release.yaml@refs/tags/v0.70.0"
Links
As mentioned Fulcio has a very interesting and detailed documentation about the security of the certificates: