-
Notifications
You must be signed in to change notification settings - Fork 1.3k
add OCI artifact version of attestation manifest #5573
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
This adds a new exporter option oci-artifact. If set then it changes the attestation manifest to be exported with following changes: - subject field is added to attestation manifest - config uses empty descriptor instead of fake image config - artifactType property is set The intention is to change this behavior from opt-in to opt-out in some future release when target is OCI image. Signed-off-by: Tonis Tiigi <[email protected]>
37d5e8b to
0251a3d
Compare
|
Thanks for getting this started! There's some fallback logic the spec requires when this is pushed to a registry without the referrers API. We have that documented at: https://github.com/opencontainers/distribution-spec/blob/main/spec.md#pushing-manifests-with-subject ETA: here's what that fallback tag would look like with a single entry: https://explore.ggcr.dev/?image=sudobmitch%2Fdemo:sha256-bc2046336420a2852ecf915786c20f73c4c1b50d7803aae1fd30c971a7d1cead |
|
I see this as more of an intermediate change -- the attestation still ends up in the index, which is the current means of discoverability, it's just now annotated with an appropriate subject so that when referrers are implemented, it shows up in the API (and so that the link between the attestation and its manifest is clear in the data model). Once referrers are implemented more universally (especially Docker Hub, but perhaps others?), then we can think about removing them from the index too. In other words, I don't see a reason to implement the fallback tags here. (IMO the spec ought to say SHOULD there instead of MUST, but that comes from a place of thinking the fallback is a really poor compromise for UX and the upsides don't outweigh the down. To be very explicit, if buildkit/buildx implements the fallback behavior, I'll be patching it out in my own builds and/or pushing in a way that doesn't involve the fallback logic.) |
|
We had made it a "must" because registries are not required to support the referrers response for content pushed before the server is upgraded if it doesn't also have the fallback tag. If buildkit pushes content without the fallback support, it may not be discovered when a registry later supports the referrers API. This was a concession for large registries like Docker Hub that may have a lot of content they don't want to rescan. Registries can assume well behaved clients, and only need to rescan the small percentage of manifests referenced by a fallback tag. If large registries no longer have the concern about rescanning all of the pre-existing content, we could propose a change to the spec from a "client must" to a "registry must". |
|
Thanks @tonistiigi. This is a great first step. Making this opt-in is a good solution to prevent registries that don't yet support/accept a Implementing the fallback mechanism and eventually using that for DOI (although @tianon indicated he wouldn't, so this is theoretical) would have quite a few negative effects and surprises for our users on Docker Hub. Again, great addition. |
|
Was this feature meant to remove the Assumed usage
I realize there are docs added related to the feature, but when attempting to leverage it, the manifest index created does not seem to have any difference from # NOTE: `--attest type=provenance,mode=min` is implicit by default
$ docker buildx build \
--output type=image,push=true,oci-mediatypes=true,oci-artifact=true \
--platform linux/amd64,linux/arm64 \
--tag ghcr.io/polarathene/example:test \
.
=> => pushing layers
=> => pushing manifest for ghcr.io/polarathene/example:test@sha256:a5dddf803c71cbad6fc45ebf1931584213562a2f4184a6c42e27a6312907aa5fI have tried this on a fresh VPS install with Fedora 41 and Docker repo added for packages. Attempted to use the inspection tooling with containerd image storage, but apparently that only works with an actual registry? So I've tried with ReproductionThis issue was closed when this PR was merged. I assume the output was meant to differ, but my attempt to reproduce with the above usage appears to be the same. I can replicate all those steps as shown below, but I assumed this feature was meant to fix the original manifest index published? Click to view reproductionQuery manifests via $ curl -fsSL https://github.com/google/go-containerregistry/releases/download/v0.20.3/go-containerregistry_Linux_x86_64.tar.gz | tar -xz crane
$ ./crane manifest ghcr.io/polarathene/example:test@sha256:a5dddf803c71cbad6fc45ebf1931584213562a2f4184a6c42e27a6312907aa5f
{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.index.v1+json",
"manifests": [
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:6f81b18808466808136cd43e68a156f7a58937bd4e50edacce158ac5300cbce5",
"size": 668,
"platform": {
"architecture": "amd64",
"os": "linux"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:c921e8c46326db1dbd537eaf8d9566408497e105d20a5f05baeaab09afff54b4",
"size": 668,
"platform": {
"architecture": "arm64",
"os": "linux"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:6f1a7b24844a8ff6314eaa7fe99432de112731ef4e3a3811f58f07e062beea2d",
"size": 914,
"annotations": {
"vnd.docker.reference.digest": "sha256:6f81b18808466808136cd43e68a156f7a58937bd4e50edacce158ac5300cbce5",
"vnd.docker.reference.type": "attestation-manifest"
},
"platform": {
"architecture": "unknown",
"os": "unknown"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:47fe6f33b043ac8b3999f3d76b4d5cc46a8c4dcff4a4eef5f1a9e826ef96c988",
"size": 914,
"annotations": {
"vnd.docker.reference.digest": "sha256:c921e8c46326db1dbd537eaf8d9566408497e105d20a5f05baeaab09afff54b4",
"vnd.docker.reference.type": "attestation-manifest"
},
"platform": {
"architecture": "unknown",
"os": "unknown"
}
}
]
}Last entry from prior output is $ ./crane manifest ghcr.io/polarathene/example:test@sha256:c921e8c46326db1dbd537eaf8d9566408497e105d20a5f05baeaab09afff54b4
{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"config": {
"mediaType": "application/vnd.oci.image.config.v1+json",
"digest": "sha256:b418f2217afb0de9b0b74a40e0407264d7986883dae7cc317adef8249b67db93",
"size": 837
},
"layers": [
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:6e771e15690e2fabf2332d3a3b744495411d6e0b00b2aea64419b58b0066cf81",
"size": 3993029
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:103341866df2e1f713ff4439251d77dc8ab241ef0ed0647ff96c517aab401307",
"size": 139
}
]
}Use $ curl -fsSL https://github.com/oras-project/oras/releases/download/v1.2.2/oras_1.2.2_linux_amd64.tar.gz | tar -xz oras
$ echo "Hello, world!" > hi.txt
$ ./oras attach --artifact-type doc/example ghcr.io/polarathene/example:test@sha256:a5dddf803c71cbad6fc45ebf1931584213562a2f4184a6c42e27a6312907aa5f hi.txt
✓ Exists application/vnd.oci.empty.v1+json
└─ sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a
✓ Uploaded hi.txt
✓ Uploaded application/vnd.oci.image.manifest.v1+json
✓ Uploaded application/vnd.oci.image.manifest.v1+json
Attached to [registry] ghcr.io/polarathene/example:test@sha256:a5dddf803c71cbad6fc45ebf1931584213562a2f4184a6c42e27a6312907aa5f
Digest: sha256:5539a7809e319343ea450ad87eb4520c071fe0aad6987d4fcf4c3ea7f1175ebd
$ ./crane manifest ghcr.io/polarathene/example:sha256-a5dddf803c71cbad6fc45ebf1931584213562a2f4184a6c42e27a6312907aa5f | jq .
{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.index.v1+json",
"manifests": [
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:5539a7809e319343ea450ad87eb4520c071fe0aad6987d4fcf4c3ea7f1175ebd",
"size": 722,
"annotations": {
"org.opencontainers.image.created": "2025-03-05T04:48:31Z"
},
"artifactType": "doc/example"
}
]
}Querying that digest from the output of that index or the $ ./crane manifest ghcr.io/polarathene/example@sha256:5539a7809e319343ea450ad87eb4520c071fe0aad6987d4fcf4c3ea7f1175ebd | jq .
{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"artifactType": "doc/example",
"config": {
"mediaType": "application/vnd.oci.empty.v1+json",
"digest": "sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a",
"size": 2,
"data": "e30="
},
"layers": [
{
"mediaType": "application/vnd.oci.image.layer.v1.tar",
"digest": "sha256:d9014c4624844aa5bac314773d6b689ad467fa4e1d1a50a1b8a99d5a95f72ff5",
"size": 14,
"annotations": {
"org.opencontainers.image.title": "hi.txt"
}
}
],
"subject": {
"mediaType": "application/vnd.oci.image.index.v1+json",
"digest": "sha256:a5dddf803c71cbad6fc45ebf1931584213562a2f4184a6c42e27a6312907aa5f",
"size": 1607
},
"annotations": {
"org.opencontainers.image.created": "2025-03-05T04:48:31Z"
}
}Expectation
Invalid ignoreThis portion of the original image index: {
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:47fe6f33b043ac8b3999f3d76b4d5cc46a8c4dcff4a4eef5f1a9e826ef96c988",
"size": 914,
"annotations": {
"vnd.docker.reference.digest": "sha256:c921e8c46326db1dbd537eaf8d9566408497e105d20a5f05baeaab09afff54b4",
"vnd.docker.reference.type": "attestation-manifest"
},
"platform": {
"architecture": "unknown",
"os": "unknown"
}
}Should be similar to this when using That would remove the Perhaps I've tried to use the feature incorrectly. I've tried a variety of things, but the docs are a bit sparse on this. |
|
TL;DR: Regardless of the JSON outputs via any raw manifest command:
Click to view JSON{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.index.v1+json",
"manifests": [
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:6f81b18808466808136cd43e68a156f7a58937bd4e50edacce158ac5300cbce5",
"size": 668,
"platform": {
"architecture": "amd64",
"os": "linux"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:c921e8c46326db1dbd537eaf8d9566408497e105d20a5f05baeaab09afff54b4",
"size": 668,
"platform": {
"architecture": "arm64",
"os": "linux"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:6f1a7b24844a8ff6314eaa7fe99432de112731ef4e3a3811f58f07e062beea2d",
"size": 914,
"annotations": {
"vnd.docker.reference.digest": "sha256:6f81b18808466808136cd43e68a156f7a58937bd4e50edacce158ac5300cbce5",
"vnd.docker.reference.type": "attestation-manifest"
},
"platform": {
"architecture": "unknown",
"os": "unknown"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:47fe6f33b043ac8b3999f3d76b4d5cc46a8c4dcff4a4eef5f1a9e826ef96c988",
"size": 914,
"annotations": {
"vnd.docker.reference.digest": "sha256:c921e8c46326db1dbd537eaf8d9566408497e105d20a5f05baeaab09afff54b4",
"vnd.docker.reference.type": "attestation-manifest"
},
"platform": {
"architecture": "unknown",
"os": "unknown"
}
}
]
}Query the digest for the first entry (of the above output returned) with the crane manifest ghcr.io/polarathene/example:test \
| jq -r '[ .manifests[]
| select(.annotations | has("vnd.docker.reference.digest"))
| .digest
] | first' \
crane manifest "ghcr.io/polarathene/example@$(cat -)"
# Results in the equivalent of:
crane manifest ghcr.io/polarathene/example@sha256:6f1a7b24844a8ff6314eaa7fe99432de112731ef4e3a3811f58f07e062beea2dNow you'll get one of these outputs depending on
Click to view JSON{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"config": {
"mediaType": "application/vnd.oci.image.config.v1+json",
"digest": "sha256:50dc03b6fbf201ebc7f100aceba6733b588b162b620c2bff62e62130eac44c37",
"size": 167
},
"layers": [
{
"mediaType": "application/vnd.in-toto+json",
"digest": "sha256:00ac79d774183fefad81b466e1c69de1933917bd7cf6257b593c756e38215635",
"size": 1014,
"annotations": {
"in-toto.io/predicate-type": "https://slsa.dev/provenance/v0.2"
}
}
]
}
Click to view JSON{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"artifactType": "application/vnd.docker.attestation.manifest.v1+json",
"config": {
"mediaType": "application/vnd.oci.empty.v1+json",
"digest": "sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a",
"size": 2,
"data": "e30="
},
"layers": [
{
"mediaType": "application/vnd.in-toto+json",
"digest": "sha256:c285ccb8f717d177ace6b50792a21989874996ecb0a2f522d933437032b92290",
"size": 1014,
"annotations": {
"in-toto.io/predicate-type": "https://slsa.dev/provenance/v0.2"
}
}
],
"subject": {
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:6f81b18808466808136cd43e68a156f7a58937bd4e50edacce158ac5300cbce5",
"size": 668,
"platform": {
"architecture": "amd64",
"os": "linux"
}
}
}So ExpectationI was under the belief that the image index published would have also been adapted similar to the documented example at the Cosign Bundle Specification, Click to view JSON (ignore this is incorrect){
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.index.v1+json",
"manifests": [
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:6f81b18808466808136cd43e68a156f7a58937bd4e50edacce158ac5300cbce5",
"size": 668,
"platform": {
"architecture": "amd64",
"os": "linux"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:c921e8c46326db1dbd537eaf8d9566408497e105d20a5f05baeaab09afff54b4",
"size": 668,
"platform": {
"architecture": "arm64",
"os": "linux"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:6f1a7b24844a8ff6314eaa7fe99432de112731ef4e3a3811f58f07e062beea2d",
"artifactType": "application/vnd.docker.attestation.manifest.v1+json"
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:47fe6f33b043ac8b3999f3d76b4d5cc46a8c4dcff4a4eef5f1a9e826ef96c988",
"artifactType": "application/vnd.docker.attestation.manifest.v1+json"
}
]
} |
| "golang.org/x/sync/errgroup" | ||
| ) | ||
|
|
||
| const attestationManifestArtifactType = "application/vnd.docker.attestation.manifest.v1+json" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This value should probably be added to https://github.com/moby/buildkit/blob/f198c3849aebe9b1b1ddd7286e831834014355ec/docs/attestations/attestation-storage.md too, right? 👀
fixes #5561
This adds a new exporter option oci-artifact. If set then it changes the attestation manifest to be exported with following changes:
The intention is to change this behavior from opt-in to opt-out in some future release when target is OCI image.
Example https://explore.ggcr.dev/?image=docker.io/tonistiigi/buildkit@sha256:bf8355824354aee83a9874f7178ea91a17752603a949e89f00f9e3ca5404db35&mt=application%2Fvnd.oci.image.manifest.v1%2Bjson&size=915
Quick test: