diff --git a/SPEC.md b/SPEC.md index a726d297ae9..f92f19e58ab 100644 --- a/SPEC.md +++ b/SPEC.md @@ -156,11 +156,12 @@ Implementations MAY store signatures objects in the same OCI repository as the t This section describes the way the properties from above are embedded into OCI objects that can be stored in a registry. Implementations MUST support storing signatures in at least the following object types: -* [Image Manifest V2 Schema 2](https://docs.docker.com/registry/spec/manifest-v2-2/) +* [OCI Image Manifest V1](#oci-image-manifest-v1) -#### Image Manifest V2 Schema 2 +#### OCI Image Manifest V1 -This section describes the way the mandatory and optional signature properties are embedded into an Image Manifest V2 Schema 2 object. +This section describes the way the mandatory and optional signature properties are embedded into an +[OCI Image Manifest V1](https://github.com/opencontainers/image-spec/blob/master/manifest.md) object. Only one image manifest is created for every signed object. Multiple signatures can be embedded in one image manifest. @@ -177,8 +178,8 @@ Example `payload`: ``` { "schemaVersion": 2, - "mediaType": "application/vnd.docker.distribution.manifest.v2+json", "config": { + "mediaType": "application/vnd.oci.image.config.v1+json", }, "layers": [ diff --git a/pkg/cosign/remote.go b/pkg/cosign/remote.go index da37cc22b69..5d430af8f70 100644 --- a/pkg/cosign/remote.go +++ b/pkg/cosign/remote.go @@ -52,7 +52,7 @@ func Descriptors(ref name.Reference) ([]v1.Descriptor, error) { return m.Layers, nil } -// SignatureImage +// SignatureImage returns the existing destination image, or a new, empty one. func SignatureImage(dstTag name.Reference, opts ...remote.Option) (v1.Image, error) { base, err := remote.Image(dstTag, opts...) if err != nil { @@ -60,7 +60,15 @@ func SignatureImage(dstTag name.Reference, opts ...remote.Option) (v1.Image, err if te.StatusCode != http.StatusNotFound { return nil, te } - base = empty.Image + if !DockerMediaTypes() { + base = mutate.MediaType(empty.Image, types.OCIManifestSchema1) + m, err := base.Manifest() + if err != nil { + // should never happen...? + return nil, err + } + m.Config.MediaType = types.OCIConfigJSON + } } else { return nil, err } diff --git a/pkg/cosign/upload.go b/pkg/cosign/upload.go index 6e4602e7dd4..8e75eed2e6e 100644 --- a/pkg/cosign/upload.go +++ b/pkg/cosign/upload.go @@ -34,10 +34,11 @@ import ( ) const ( - ExperimentalEnv = "COSIGN_EXPERIMENTAL" - repoEnv = "COSIGN_REPOSITORY" - ServerEnv = "REKOR_SERVER" - rekorServer = "https://api.rekor.dev" + ExperimentalEnv = "COSIGN_EXPERIMENTAL" + repoEnv = "COSIGN_REPOSITORY" + DockerMediaTypesEnv = "COSIGN_DOCKER_MEDIA_TYPES" + ServerEnv = "REKOR_SERVER" + rekorServer = "https://api.rekor.dev" ) func Experimental() bool { @@ -47,6 +48,13 @@ func Experimental() bool { return false } +func DockerMediaTypes() bool { + if b, err := strconv.ParseBool(os.Getenv(DockerMediaTypesEnv)); err == nil { + return b + } + return false +} + func DestinationRef(ref name.Reference, img *remote.Descriptor) (name.Reference, error) { dstTag := ref.Context().Tag(Munge(img.Descriptor)) wantRepo := os.Getenv(repoEnv) diff --git a/test/e2e_test_secrets.sh b/test/e2e_test_secrets.sh index 96ba797681b..48fbab3dc12 100755 --- a/test/e2e_test_secrets.sh +++ b/test/e2e_test_secrets.sh @@ -31,7 +31,8 @@ export COSIGN_PASSWORD=$pass ./cosign generate-key-pair img="us-central1-docker.pkg.dev/projectsigstore/cosign-ci/test" img2="us-central1-docker.pkg.dev/projectsigstore/cosign-ci/test-2" -for image in $img $img2 +legacy_img="us-central1-docker.pkg.dev/projectsigstore/cosign-ci/legacy-test" +for image in $img $img2 $legacy_img do (crane delete $(./cosign triangulate $image)) || true crane cp busybox $image @@ -42,6 +43,9 @@ done ./cosign sign -key cosign.key $img ./cosign verify -key cosign.pub $img +## confirm use of OCI media type in signature image +crane manifest $(./cosign triangulate $img) | grep -q "application/vnd.oci.image.config.v1+json" + ## sign/verify multiple images ./cosign sign -key cosign.key -a multiple=true $img $img2 ./cosign verify -key cosign.pub -a multiple=true $img $img2 @@ -56,6 +60,13 @@ if (./cosign verify -key cosign.pub -a foo=bar -a bar=baz $img); then false; fi ./cosign verify -key cosign.pub -a foo=bar -a bar=baz $img ./cosign verify -key cosign.pub -a bar=baz $img +# confirm the use of legacy (Docker) media types +COSIGN_DOCKER_MEDIA_TYPES=1 ./cosign sign -key cosign.key $legacy_img +./cosign verify -key cosign.pub $legacy_img +legacy_manifest=$(crane manifest $(./cosign triangulate $legacy_img)) +echo $legacy_manifest | grep -q "application/vnd.docker.distribution.manifest.v2+json" +echo $legacy_manifest | grep -q "application/vnd.docker.container.image.v1+json" + # wrong keys mkdir wrong && pushd wrong ../cosign generate-key-pair