From af8a1ab06b639e09aacf2db23c89d47bb5fa80f3 Mon Sep 17 00:00:00 2001 From: Andreea-Lupu Date: Tue, 17 Oct 2023 17:51:14 +0300 Subject: [PATCH] feat: add support for oci1.1 cosign signatures(using referrers) - Cosign supports 2 types of signature formats: 1. Using tag -> each new signature of the same manifest is added as a new layer of the signature manifest having that specific tag("{alghoritm}-{digest_of_signed_manifest}.sig") 2. Using referrers -> each new signature of the same manifest is added as a new manifest - For adding these cosign signature to metadb, we reserved index 0 of the list of cosign signatures for tag-based signatures. When a new tag-based signature is added for the same manifest, the element on first position in its list of cosign signatures(in metadb) will be updated/overwritten. When a new cosign signature(using referrers) will be added for the same manifest this new signature will be appended to the list of cosign signatures. Signed-off-by: Andreea-Lupu --- THIRD-PARTY-LICENSES.md | 30 +++--- pkg/cli/client/client.go | 21 +++- pkg/common/common.go | 1 + pkg/extensions/search/search_test.go | 16 +-- pkg/extensions/sync/references/oci.go | 2 +- pkg/extensions/sync/references/references.go | 13 +++ pkg/extensions/sync/sync_test.go | 103 ++++++++++++++++++- pkg/meta/boltdb/boltdb.go | 48 ++++++--- pkg/meta/dynamodb/dynamodb.go | 48 ++++++--- pkg/meta/meta_test.go | 18 +++- pkg/meta/parse.go | 6 ++ pkg/meta/types/types.go | 1 + pkg/storage/common/common.go | 5 + pkg/storage/gc/gc.go | 4 +- pkg/storage/storage.go | 5 + pkg/test/oci-utils/oci_layout.go | 16 ++- pkg/test/signature/cosign.go | 20 ++-- test/blackbox/annotations.bats | 76 +++++++++++++- 18 files changed, 367 insertions(+), 66 deletions(-) diff --git a/THIRD-PARTY-LICENSES.md b/THIRD-PARTY-LICENSES.md index c2c33d17db..779b3875da 100644 --- a/THIRD-PARTY-LICENSES.md +++ b/THIRD-PARTY-LICENSES.md @@ -6,7 +6,7 @@ cloud.google.com/go/internal|https://github.com/googleapis/google-cloud-go/blob/ cloud.google.com/go/storage|https://github.com/googleapis/google-cloud-go/blob/storage/v1.31.0/storage/LICENSE|Apache-2.0 cloud.google.com/go|https://github.com/googleapis/google-cloud-go/blob/v0.110.7/LICENSE|Apache-2.0 filippo.io/edwards25519|https://github.com/FiloSottile/edwards25519/blob/v1.0.0/LICENSE|BSD-3-Clause -github.com/99designs/gqlgen|https://github.com/99designs/gqlgen/blob/v0.17.39/LICENSE|MIT +github.com/99designs/gqlgen|https://github.com/99designs/gqlgen/blob/v0.17.40/LICENSE|MIT github.com/AdaLogics/go-fuzz-headers|https://github.com/AdaLogics/go-fuzz-headers/blob/ced1acdcaa24/LICENSE|Apache-2.0 github.com/AdamKorcz/go-118-fuzz-build|https://github.com/AdamKorcz/go-118-fuzz-build/blob/8075edf89bb0/LICENSE|Apache-2.0 github.com/AliyunContainerService/ack-ram-tool/pkg/credentials/alibabacloudsdkgo/helper|https://github.com/AliyunContainerService/ack-ram-tool/blob/pkg/credentials/alibabacloudsdkgo/helper/v0.2.0/pkg/credentials/alibabacloudsdkgo/helper/LICENSE|Apache-2.0 @@ -63,13 +63,13 @@ github.com/aquasecurity/go-version/pkg/part|https://github.com/aquasecurity/go-v github.com/aquasecurity/table|https://github.com/aquasecurity/table/blob/v1.8.0/LICENSE|MIT github.com/aquasecurity/tml|https://github.com/aquasecurity/tml/blob/v0.6.1/LICENSE|Unlicense github.com/asaskevich/govalidator|https://github.com/asaskevich/govalidator/blob/a9d515a09cc2/LICENSE|MIT -github.com/aws/aws-sdk-go-v2/config|https://github.com/aws/aws-sdk-go-v2/blob/config/v1.18.44/config/LICENSE.txt|Apache-2.0 -github.com/aws/aws-sdk-go-v2/credentials|https://github.com/aws/aws-sdk-go-v2/blob/credentials/v1.13.42/credentials/LICENSE.txt|Apache-2.0 +github.com/aws/aws-sdk-go-v2/config|https://github.com/aws/aws-sdk-go-v2/blob/config/v1.19.1/config/LICENSE.txt|Apache-2.0 +github.com/aws/aws-sdk-go-v2/credentials|https://github.com/aws/aws-sdk-go-v2/blob/credentials/v1.13.43/credentials/LICENSE.txt|Apache-2.0 github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue|https://github.com/aws/aws-sdk-go-v2/blob/feature/dynamodb/attributevalue/v1.10.43/feature/dynamodb/attributevalue/LICENSE.txt|Apache-2.0 -github.com/aws/aws-sdk-go-v2/feature/ec2/imds|https://github.com/aws/aws-sdk-go-v2/blob/feature/ec2/imds/v1.13.12/feature/ec2/imds/LICENSE.txt|Apache-2.0 +github.com/aws/aws-sdk-go-v2/feature/ec2/imds|https://github.com/aws/aws-sdk-go-v2/blob/feature/ec2/imds/v1.13.13/feature/ec2/imds/LICENSE.txt|Apache-2.0 github.com/aws/aws-sdk-go-v2/internal/configsources|https://github.com/aws/aws-sdk-go-v2/blob/internal/configsources/v1.1.43/internal/configsources/LICENSE.txt|Apache-2.0 github.com/aws/aws-sdk-go-v2/internal/endpoints/v2|https://github.com/aws/aws-sdk-go-v2/blob/internal/endpoints/v2.4.37/internal/endpoints/v2/LICENSE.txt|Apache-2.0 -github.com/aws/aws-sdk-go-v2/internal/ini|https://github.com/aws/aws-sdk-go-v2/blob/internal/ini/v1.3.44/internal/ini/LICENSE.txt|Apache-2.0 +github.com/aws/aws-sdk-go-v2/internal/ini|https://github.com/aws/aws-sdk-go-v2/blob/internal/ini/v1.3.45/internal/ini/LICENSE.txt|Apache-2.0 github.com/aws/aws-sdk-go-v2/internal/sync/singleflight|https://github.com/aws/aws-sdk-go-v2/blob/v1.21.2/internal/sync/singleflight/LICENSE|BSD-3-Clause github.com/aws/aws-sdk-go-v2/service/dynamodb/types|https://github.com/aws/aws-sdk-go-v2/blob/service/dynamodb/v1.23.0/service/dynamodb/LICENSE.txt|Apache-2.0 github.com/aws/aws-sdk-go-v2/service/dynamodbstreams/types|https://github.com/aws/aws-sdk-go-v2/blob/service/dynamodbstreams/v1.15.7/service/dynamodbstreams/LICENSE.txt|Apache-2.0 @@ -81,14 +81,14 @@ github.com/aws/aws-sdk-go-v2/service/ecrpublic|https://github.com/aws/aws-sdk-go github.com/aws/aws-sdk-go-v2/service/ecr|https://github.com/aws/aws-sdk-go-v2/blob/service/ecr/v1.17.18/service/ecr/LICENSE.txt|Apache-2.0 github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding|https://github.com/aws/aws-sdk-go-v2/blob/service/internal/accept-encoding/v1.9.15/service/internal/accept-encoding/LICENSE.txt|Apache-2.0 github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery|https://github.com/aws/aws-sdk-go-v2/blob/service/internal/endpoint-discovery/v1.7.37/service/internal/endpoint-discovery/LICENSE.txt|Apache-2.0 -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url|https://github.com/aws/aws-sdk-go-v2/blob/service/internal/presigned-url/v1.9.36/service/internal/presigned-url/LICENSE.txt|Apache-2.0 +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url|https://github.com/aws/aws-sdk-go-v2/blob/service/internal/presigned-url/v1.9.37/service/internal/presigned-url/LICENSE.txt|Apache-2.0 github.com/aws/aws-sdk-go-v2/service/secretsmanager|https://github.com/aws/aws-sdk-go-v2/blob/service/secretsmanager/v1.21.6/service/secretsmanager/LICENSE.txt|Apache-2.0 -github.com/aws/aws-sdk-go-v2/service/ssooidc|https://github.com/aws/aws-sdk-go-v2/blob/service/ssooidc/v1.17.2/service/ssooidc/LICENSE.txt|Apache-2.0 -github.com/aws/aws-sdk-go-v2/service/sso|https://github.com/aws/aws-sdk-go-v2/blob/service/sso/v1.15.1/service/sso/LICENSE.txt|Apache-2.0 -github.com/aws/aws-sdk-go-v2/service/sts|https://github.com/aws/aws-sdk-go-v2/blob/service/sts/v1.23.1/service/sts/LICENSE.txt|Apache-2.0 +github.com/aws/aws-sdk-go-v2/service/ssooidc|https://github.com/aws/aws-sdk-go-v2/blob/service/ssooidc/v1.17.3/service/ssooidc/LICENSE.txt|Apache-2.0 +github.com/aws/aws-sdk-go-v2/service/sso|https://github.com/aws/aws-sdk-go-v2/blob/service/sso/v1.15.2/service/sso/LICENSE.txt|Apache-2.0 +github.com/aws/aws-sdk-go-v2/service/sts|https://github.com/aws/aws-sdk-go-v2/blob/service/sts/v1.23.2/service/sts/LICENSE.txt|Apache-2.0 github.com/aws/aws-sdk-go-v2|https://github.com/aws/aws-sdk-go-v2/blob/v1.21.2/LICENSE.txt|Apache-2.0 -github.com/aws/aws-sdk-go/internal/sync/singleflight|https://github.com/aws/aws-sdk-go/blob/v1.46.1/internal/sync/singleflight/LICENSE|BSD-3-Clause -github.com/aws/aws-sdk-go|https://github.com/aws/aws-sdk-go/blob/v1.46.1/LICENSE.txt|Apache-2.0 +github.com/aws/aws-sdk-go/internal/sync/singleflight|https://github.com/aws/aws-sdk-go/blob/v1.46.7/internal/sync/singleflight/LICENSE|BSD-3-Clause +github.com/aws/aws-sdk-go|https://github.com/aws/aws-sdk-go/blob/v1.46.7/LICENSE.txt|Apache-2.0 github.com/aws/smithy-go/internal/sync/singleflight|https://github.com/aws/smithy-go/blob/v1.15.0/internal/sync/singleflight/LICENSE|BSD-3-Clause github.com/aws/smithy-go|https://github.com/aws/smithy-go/blob/v1.15.0/LICENSE|Apache-2.0 github.com/awslabs/amazon-ecr-credential-helper/ecr-login|https://github.com/awslabs/amazon-ecr-credential-helper/blob/396b2034c795/ecr-login/LICENSE|Apache-2.0 @@ -198,7 +198,7 @@ github.com/google/gofuzz|https://github.com/google/gofuzz/blob/v1.2.0/LICENSE|Ap github.com/google/licenseclassifier/v2|https://github.com/google/licenseclassifier/blob/v2.0.0/v2/LICENSE|Apache-2.0 github.com/google/s2a-go|https://github.com/google/s2a-go/blob/v0.1.7/LICENSE.md|Apache-2.0 github.com/google/shlex|https://github.com/google/shlex/blob/e7afc7fbc510/COPYING|Apache-2.0 -github.com/google/uuid|https://github.com/google/uuid/blob/v1.3.1/LICENSE|BSD-3-Clause +github.com/google/uuid|https://github.com/google/uuid/blob/v1.4.0/LICENSE|BSD-3-Clause github.com/google/wire|https://github.com/google/wire/blob/v0.5.0/LICENSE|Apache-2.0 github.com/googleapis/enterprise-certificate-proxy/client|https://github.com/googleapis/enterprise-certificate-proxy/blob/v0.3.1/LICENSE|Apache-2.0 github.com/googleapis/gax-go/v2|https://github.com/googleapis/gax-go/blob/v2.12.0/v2/LICENSE|BSD-3-Clause @@ -295,7 +295,7 @@ github.com/mpvl/unique|https://github.com/mpvl/unique/blob/cbe035fff7de/LICENSE| github.com/munnerz/goautoneg|https://github.com/munnerz/goautoneg/blob/a7dc8b61c822/LICENSE|BSD-3-Clause github.com/nmcclain/asn1-ber|https://github.com/nmcclain/asn1-ber/blob/2661553a0484/LICENSE|BSD-3-Clause github.com/nmcclain/ldap|https://github.com/nmcclain/ldap/blob/7f8d1e44eeba/LICENSE|BSD-3-Clause -github.com/notaryproject/notation-core-go|https://github.com/notaryproject/notation-core-go/blob/v1.0.0/LICENSE|Apache-2.0 +github.com/notaryproject/notation-core-go|https://github.com/notaryproject/notation-core-go/blob/v1.0.1/LICENSE|Apache-2.0 github.com/notaryproject/notation-go|https://github.com/notaryproject/notation-go/blob/v1.0.0/LICENSE|Apache-2.0 github.com/nozzle/throttler|https://github.com/nozzle/throttler/blob/2ea982251481/LICENSE|Apache-2.0 github.com/oklog/ulid|https://github.com/oklog/ulid/blob/v1.3.1/LICENSE|Apache-2.0 @@ -392,7 +392,7 @@ github.com/zclconf/go-cty-yaml|https://github.com/zclconf/go-cty-yaml/blob/v1.0. github.com/zclconf/go-cty/cty|https://github.com/zclconf/go-cty/blob/v1.13.0/LICENSE|MIT github.com/zeebo/errs|https://github.com/zeebo/errs/blob/v1.3.0/LICENSE|MIT github.com/zitadel/oidc|https://github.com/zitadel/oidc/blob/v1.13.5/LICENSE|Apache-2.0 -go.etcd.io/bbolt|https://github.com/etcd-io/bbolt/blob/v1.3.7/LICENSE|MIT +go.etcd.io/bbolt|https://github.com/etcd-io/bbolt/blob/v1.3.8/LICENSE|MIT go.mongodb.org/mongo-driver|https://github.com/mongodb/mongo-go-driver/blob/v1.11.3/LICENSE|Apache-2.0 go.mozilla.org/pkcs7|https://github.com/mozilla-services/pkcs7/blob/33d05740a352/LICENSE|MIT go.opencensus.io|https://github.com/census-instrumentation/opencensus-go/blob/v0.24.0/LICENSE|Apache-2.0 @@ -441,7 +441,7 @@ google.golang.org/genproto/googleapis/rpc|https://github.com/googleapis/go-genpr google.golang.org/genproto/googleapis/type/expr|https://github.com/googleapis/go-genproto/blob/007df8e322eb/LICENSE|Apache-2.0 google.golang.org/genproto/googleapis/type|https://github.com/googleapis/go-genproto/blob/007df8e322eb/LICENSE|Apache-2.0 google.golang.org/genproto/protobuf/field_mask|https://github.com/googleapis/go-genproto/blob/007df8e322eb/LICENSE|Apache-2.0 -google.golang.org/grpc|https://github.com/grpc/grpc-go/blob/v1.58.2/LICENSE|Apache-2.0 +google.golang.org/grpc|https://github.com/grpc/grpc-go/blob/v1.58.3/LICENSE|Apache-2.0 google.golang.org/protobuf|https://github.com/protocolbuffers/protobuf-go/blob/v1.31.0/LICENSE|BSD-3-Clause gopkg.in/cheggaaa/pb.v1|https://github.com/cheggaaa/pb/blob/v1.0.28/LICENSE|BSD-3-Clause gopkg.in/go-jose/go-jose.v2/json|https://github.com/go-jose/go-jose/blob/v2.6.1/json/LICENSE|BSD-3-Clause diff --git a/pkg/cli/client/client.go b/pkg/cli/client/client.go index d2dcf7a6ef..c7b7f463fb 100644 --- a/pkg/cli/client/client.go +++ b/pkg/cli/client/client.go @@ -509,7 +509,26 @@ func isCosignSigned(ctx context.Context, repo, digestStr string, searchConf Sear _, err := makeGETRequest(ctx, URL, username, password, searchConf.VerifyTLS, searchConf.Debug, &result, searchConf.ResultWriter) - return err == nil + if err == nil { + return true + } + + var referrers ispec.Index + + URL = fmt.Sprintf("%s/v2/%s/referrers/%s?artifactType=%s", + searchConf.ServURL, repo, digestStr, common.ArtifactTypeCosign) + + _, err = makeGETRequest(ctx, URL, username, password, searchConf.VerifyTLS, + searchConf.Debug, &referrers, searchConf.ResultWriter) + if err != nil { + return false + } + + if len(referrers.Manifests) > 0 { + return true + } + + return false } func (p *requestsPool) submitJob(job *httpJob) { diff --git a/pkg/common/common.go b/pkg/common/common.go index e374e21fcc..846b7e0c2e 100644 --- a/pkg/common/common.go +++ b/pkg/common/common.go @@ -27,6 +27,7 @@ const ( // same value as github.com/notaryproject/notation-go/registry.ArtifactTypeNotation (assert by internal test). // reason used: to reduce zot minimal binary size (otherwise adds oras.land/oras-go/v2 deps). ArtifactTypeNotation = "application/vnd.cncf.notary.signature" + ArtifactTypeCosign = "application/vnd.dev.cosign.artifact.sig.v1+json" ) var cosignTagRule = regexp.MustCompile(`sha256\-.+\.sig`) diff --git a/pkg/extensions/search/search_test.go b/pkg/extensions/search/search_test.go index 56b9b2b527..982ffced96 100644 --- a/pkg/extensions/search/search_test.go +++ b/pkg/extensions/search/search_test.go @@ -1349,7 +1349,7 @@ func TestExpandedRepoInfo(t *testing.T) { } So(found, ShouldEqual, true) - err = signature.SignImageUsingCosign("zot-cve-test:0.0.1", port) + err = signature.SignImageUsingCosign("zot-cve-test:0.0.1", port, false) So(err, ShouldBeNil) resp, err = resty.R().Get(baseURL + graphqlQueryPrefix + "?query=" + url.QueryEscape(query)) @@ -1421,7 +1421,7 @@ func TestExpandedRepoInfo(t *testing.T) { } So(found, ShouldEqual, true) - err = signature.SignImageUsingCosign("zot-test@"+testManifestDigest.String(), port) + err = signature.SignImageUsingCosign("zot-test@"+testManifestDigest.String(), port, false) So(err, ShouldBeNil) resp, err = resty.R().Get(baseURL + graphqlQueryPrefix + "/query?query=" + url.QueryEscape(query)) @@ -3759,7 +3759,7 @@ func TestGlobalSearchFiltering(t *testing.T) { ) So(err, ShouldBeNil) - err = signature.SignImageUsingCosign("signed-repo:test", port) + err = signature.SignImageUsingCosign("signed-repo:test", port, false) So(err, ShouldBeNil) query := `{ @@ -4323,7 +4323,7 @@ func TestMetaDBWhenSigningImages(t *testing.T) { ` Convey("Sign with cosign", func() { - err = signature.SignImageUsingCosign("repo1:1.0.1", port) + err = signature.SignImageUsingCosign("repo1:1.0.1", port, false) So(err, ShouldBeNil) resp, err := resty.R().Get(baseURL + graphqlQueryPrefix + "?query=" + url.QueryEscape(queryImage1)) @@ -4403,7 +4403,7 @@ func TestMetaDBWhenSigningImages(t *testing.T) { }, } - err := signature.SignImageUsingCosign("repo1:1.0.1", port) + err := signature.SignImageUsingCosign("repo1:1.0.1", port, false) So(err, ShouldNotBeNil) }) }) @@ -4443,7 +4443,7 @@ func TestMetaDBWhenSigningImages(t *testing.T) { }) Convey("Sign with cosign index", func() { - err = signature.SignImageUsingCosign("repo1:index", port) + err = signature.SignImageUsingCosign("repo1:index", port, false) So(err, ShouldBeNil) resp, err := resty.R().Get(baseURL + graphqlQueryPrefix + "?query=" + url.QueryEscape(queryIndex)) @@ -4572,7 +4572,7 @@ func RunMetaDBIndexTests(baseURL, port string) { responseImage := responseImages[0] So(len(responseImage.Manifests), ShouldEqual, 3) - err = signature.SignImageUsingCosign(fmt.Sprintf("repo@%s", multiarchImage.DigestStr()), port) + err = signature.SignImageUsingCosign(fmt.Sprintf("repo@%s", multiarchImage.DigestStr()), port, false) So(err, ShouldBeNil) resp, err = resty.R().Get(baseURL + graphqlQueryPrefix + "?query=" + url.QueryEscape(query)) @@ -5301,7 +5301,7 @@ func TestMetaDBWhenDeletingImages(t *testing.T) { Convey("Delete a cosign signature", func() { repo := "repo1" - err := signature.SignImageUsingCosign("repo1:1.0.1", port) + err := signature.SignImageUsingCosign("repo1:1.0.1", port, false) So(err, ShouldBeNil) query := ` diff --git a/pkg/extensions/sync/references/oci.go b/pkg/extensions/sync/references/oci.go index 6519b05eb0..5123079760 100644 --- a/pkg/extensions/sync/references/oci.go +++ b/pkg/extensions/sync/references/oci.go @@ -53,7 +53,7 @@ func (ref OciReferences) IsSigned(ctx context.Context, remoteRepo, subjectDigest return false } - if len(getNotationManifestsFromOCIRefs(index)) > 0 { + if len(getNotationManifestsFromOCIRefs(index)) > 0 || len(getCosignManifestsFromOCIRefs(index)) > 0 { return true } diff --git a/pkg/extensions/sync/references/references.go b/pkg/extensions/sync/references/references.go index 9e448100b7..d27623f9a9 100644 --- a/pkg/extensions/sync/references/references.go +++ b/pkg/extensions/sync/references/references.go @@ -218,6 +218,18 @@ func getNotationManifestsFromOCIRefs(ociRefs ispec.Index) []ispec.Descriptor { return notaryManifests } +func getCosignManifestsFromOCIRefs(ociRefs ispec.Index) []ispec.Descriptor { + cosignManifests := []ispec.Descriptor{} + + for _, ref := range ociRefs.Manifests { + if ref.ArtifactType == common.ArtifactTypeCosign { + cosignManifests = append(cosignManifests, ref) + } + } + + return cosignManifests +} + func addSigToMeta( metaDB mTypes.MetaDB, repo, sigType, tag string, signedManifestDig, referenceDigest godigest.Digest, referenceBuf []byte, imageStore storageTypes.ImageStore, log log.Logger, @@ -232,6 +244,7 @@ func addSigToMeta( return metaDB.AddManifestSignature(repo, signedManifestDig, mTypes.SignatureMetadata{ SignatureType: sigType, SignatureDigest: referenceDigest.String(), + SignatureTag: tag, LayersInfo: layersInfo, }) } diff --git a/pkg/extensions/sync/sync_test.go b/pkg/extensions/sync/sync_test.go index aed6c0765b..06a5e01d66 100644 --- a/pkg/extensions/sync/sync_test.go +++ b/pkg/extensions/sync/sync_test.go @@ -746,7 +746,7 @@ func TestOnDemand(t *testing.T) { So(err, ShouldBeNil) // sign using cosign - err = signature.SignImageUsingCosign(fmt.Sprintf("remote-repo@%s", manifestDigest.String()), port) + err = signature.SignImageUsingCosign(fmt.Sprintf("remote-repo@%s", manifestDigest.String()), port, false) So(err, ShouldBeNil) // add cosign sbom @@ -4593,6 +4593,100 @@ func TestSignatures(t *testing.T) { So(err, ShouldBeNil) So(resp.StatusCode(), ShouldEqual, http.StatusOK) }) + + Convey("Verify sync oci1.1 cosign signatures", t, func() { + updateDuration, _ := time.ParseDuration("30m") + + sctlr, srcBaseURL, _, _, _ := makeUpstreamServer(t, false, false) + + scm := test.NewControllerManager(sctlr) + scm.StartAndWait(sctlr.Config.HTTP.Port) + defer scm.StopServer() + + // create repo, push and sign it + repoName := testSignedImage + var digest godigest.Digest + So(func() { digest = pushRepo(srcBaseURL, repoName) }, ShouldNotPanic) + + splittedURL := strings.SplitAfter(srcBaseURL, ":") + srcPort := splittedURL[len(splittedURL)-1] + t.Logf(srcPort) + + err := signature.SignImageUsingCosign(fmt.Sprintf("%s@%s", repoName, digest.String()), srcPort, true) + So(err, ShouldBeNil) + + regex := ".*" + var semver bool + var tlsVerify bool + onlySigned := true + + syncRegistryConfig := syncconf.RegistryConfig{ + Content: []syncconf.Content{ + { + Prefix: "**", + Tags: &syncconf.Tags{ + Regex: ®ex, + Semver: &semver, + }, + }, + }, + URLs: []string{srcBaseURL}, + PollInterval: updateDuration, + TLSVerify: &tlsVerify, + CertDir: "", + OnlySigned: &onlySigned, + OnDemand: true, + } + + defaultVal := true + syncConfig := &syncconf.Config{ + Enable: &defaultVal, + Registries: []syncconf.RegistryConfig{syncRegistryConfig}, + } + + dctlr, destBaseURL, _, destClient := makeDownstreamServer(t, false, syncConfig) + + dcm := test.NewControllerManager(dctlr) + dcm.StartAndWait(dctlr.Config.HTTP.Port) + defer dcm.StopServer() + + // wait for sync + var destTagsList TagsList + + for { + resp, err := destClient.R().Get(destBaseURL + "/v2/" + repoName + "/tags/list") + if err != nil { + panic(err) + } + + err = json.Unmarshal(resp.Body(), &destTagsList) + if err != nil { + panic(err) + } + + if len(destTagsList.Tags) > 0 { + break + } + + time.Sleep(500 * time.Millisecond) + } + + time.Sleep(1 * time.Second) + + // get oci references from downstream, should be synced + getOCIReferrersURL := destBaseURL + path.Join("/v2", repoName, "referrers", digest.String()) + resp, err := resty.R().Get(getOCIReferrersURL) + So(err, ShouldBeNil) + So(resp, ShouldNotBeEmpty) + So(resp.StatusCode(), ShouldEqual, http.StatusOK) + + var index ispec.Index + + err = json.Unmarshal(resp.Body(), &index) + So(err, ShouldBeNil) + + So(len(index.Manifests), ShouldEqual, 3) + }) } func getPortFromBaseURL(baseURL string) string { @@ -4626,7 +4720,10 @@ func TestSyncedSignaturesMetaDB(t *testing.T) { err = signature.SignImageUsingNotary(repoName+":"+tag, srcPort, true) So(err, ShouldBeNil) - err = signature.SignImageUsingCosign(repoName+":"+tag, srcPort) + err = signature.SignImageUsingCosign(repoName+":"+tag, srcPort, true) + So(err, ShouldBeNil) + + err = signature.SignImageUsingCosign(repoName+":"+tag, srcPort, false) So(err, ShouldBeNil) // Create destination registry @@ -4676,7 +4773,7 @@ func TestSyncedSignaturesMetaDB(t *testing.T) { imageSignatures := repoMeta.Signatures[signedImage.DigestStr()] So(imageSignatures, ShouldContainKey, zcommon.CosignSignature) - So(len(imageSignatures[zcommon.CosignSignature]), ShouldEqual, 1) + So(len(imageSignatures[zcommon.CosignSignature]), ShouldEqual, 2) So(imageSignatures, ShouldContainKey, zcommon.NotationSignature) So(len(imageSignatures[zcommon.NotationSignature]), ShouldEqual, 1) }) diff --git a/pkg/meta/boltdb/boltdb.go b/pkg/meta/boltdb/boltdb.go index b3a223a21a..1d5272e40c 100644 --- a/pkg/meta/boltdb/boltdb.go +++ b/pkg/meta/boltdb/boltdb.go @@ -762,7 +762,7 @@ func (bdw *BoltDB) GetMultipleRepoMeta(ctx context.Context, filter func(repoMeta } func (bdw *BoltDB) AddManifestSignature(repo string, signedManifestDigest godigest.Digest, - sygMeta mTypes.SignatureMetadata, + sigMeta mTypes.SignatureMetadata, ) error { err := bdw.DB.Update(func(tx *bbolt.Tx) error { buck := tx.Bucket([]byte(RepoMetaBuck)) @@ -778,11 +778,11 @@ func (bdw *BoltDB) AddManifestSignature(repo string, signedManifestDigest godige Signatures: map[string]*proto_go.ManifestSignatures{ signedManifestDigest.String(): { Map: map[string]*proto_go.SignaturesInfo{ - sygMeta.SignatureType: { + sigMeta.SignatureType: { List: []*proto_go.SignatureInfo{ { - SignatureManifestDigest: sygMeta.SignatureDigest, - LayersInfo: mConvert.GetProtoLayersInfo(sygMeta.LayersInfo), + SignatureManifestDigest: sigMeta.SignatureDigest, + LayersInfo: mConvert.GetProtoLayersInfo(sigMeta.LayersInfo), }, }, }, @@ -818,26 +818,46 @@ func (bdw *BoltDB) AddManifestSignature(repo string, signedManifestDigest godige } signatureSlice := &proto_go.SignaturesInfo{List: []*proto_go.SignatureInfo{}} - if sigSlice, found := manifestSignatures.Map[sygMeta.SignatureType]; found { + if sigSlice, found := manifestSignatures.Map[sigMeta.SignatureType]; found { signatureSlice = sigSlice } - if !common.ProtoSignatureAlreadyExists(signatureSlice.List, sygMeta) { - switch sygMeta.SignatureType { + if !common.ProtoSignatureAlreadyExists(signatureSlice.List, sigMeta) { + switch sigMeta.SignatureType { case zcommon.NotationSignature: signatureSlice.List = append(signatureSlice.List, &proto_go.SignatureInfo{ - SignatureManifestDigest: sygMeta.SignatureDigest, - LayersInfo: mConvert.GetProtoLayersInfo(sygMeta.LayersInfo), + SignatureManifestDigest: sigMeta.SignatureDigest, + LayersInfo: mConvert.GetProtoLayersInfo(sigMeta.LayersInfo), }) case zcommon.CosignSignature: - signatureSlice.List = []*proto_go.SignatureInfo{{ - SignatureManifestDigest: sygMeta.SignatureDigest, - LayersInfo: mConvert.GetProtoLayersInfo(sygMeta.LayersInfo), - }} + newCosignSig := &proto_go.SignatureInfo{ + SignatureManifestDigest: sigMeta.SignatureDigest, + LayersInfo: mConvert.GetProtoLayersInfo(sigMeta.LayersInfo), + } + + if zcommon.IsCosignTag(sigMeta.SignatureTag) { + // the entry for "sha256-{digest}.sig" signatures should be overwritten if + // it exists or added on the first position if it doesn't exists + if len(signatureSlice.GetList()) == 0 { + signatureSlice.List = []*proto_go.SignatureInfo{newCosignSig} + } else { + signatureSlice.List[0] = newCosignSig + } + } else { + // the first position should be reserved for "sha256-{digest}.sig" signatures + if len(signatureSlice.GetList()) == 0 { + signatureSlice.List = []*proto_go.SignatureInfo{{ + SignatureManifestDigest: "", + LayersInfo: []*proto_go.LayersInfo{}, + }} + } + + signatureSlice.List = append(signatureSlice.List, newCosignSig) + } } } - manifestSignatures.Map[sygMeta.SignatureType] = signatureSlice + manifestSignatures.Map[sigMeta.SignatureType] = signatureSlice protoRepoMeta.Signatures[signedManifestDigest.String()] = manifestSignatures repoMetaBlob, err = proto.Marshal(protoRepoMeta) diff --git a/pkg/meta/dynamodb/dynamodb.go b/pkg/meta/dynamodb/dynamodb.go index 99cb7815a3..f16e7ac85b 100644 --- a/pkg/meta/dynamodb/dynamodb.go +++ b/pkg/meta/dynamodb/dynamodb.go @@ -1013,7 +1013,7 @@ func (dwr *DynamoDB) UpdateSignaturesValidity(repo string, manifestDigest godige } func (dwr *DynamoDB) AddManifestSignature(repo string, signedManifestDigest godigest.Digest, - sygMeta mTypes.SignatureMetadata, + sigMeta mTypes.SignatureMetadata, ) error { protoRepoMeta, err := dwr.getProtoRepoMeta(context.Background(), repo) if err != nil { @@ -1026,11 +1026,11 @@ func (dwr *DynamoDB) AddManifestSignature(repo string, signedManifestDigest godi Signatures: map[string]*proto_go.ManifestSignatures{ signedManifestDigest.String(): { Map: map[string]*proto_go.SignaturesInfo{ - sygMeta.SignatureType: { + sigMeta.SignatureType: { List: []*proto_go.SignatureInfo{ { - SignatureManifestDigest: sygMeta.SignatureDigest, - LayersInfo: mConvert.GetProtoLayersInfo(sygMeta.LayersInfo), + SignatureManifestDigest: sigMeta.SignatureDigest, + LayersInfo: mConvert.GetProtoLayersInfo(sigMeta.LayersInfo), }, }, }, @@ -1055,26 +1055,46 @@ func (dwr *DynamoDB) AddManifestSignature(repo string, signedManifestDigest godi } signatureSlice := &proto_go.SignaturesInfo{List: []*proto_go.SignatureInfo{}} - if sigSlice, found := manifestSignatures.Map[sygMeta.SignatureType]; found { + if sigSlice, found := manifestSignatures.Map[sigMeta.SignatureType]; found { signatureSlice = sigSlice } - if !common.ProtoSignatureAlreadyExists(signatureSlice.List, sygMeta) { - switch sygMeta.SignatureType { + if !common.ProtoSignatureAlreadyExists(signatureSlice.List, sigMeta) { + switch sigMeta.SignatureType { case zcommon.NotationSignature: signatureSlice.List = append(signatureSlice.List, &proto_go.SignatureInfo{ - SignatureManifestDigest: sygMeta.SignatureDigest, - LayersInfo: mConvert.GetProtoLayersInfo(sygMeta.LayersInfo), + SignatureManifestDigest: sigMeta.SignatureDigest, + LayersInfo: mConvert.GetProtoLayersInfo(sigMeta.LayersInfo), }) case zcommon.CosignSignature: - signatureSlice.List = []*proto_go.SignatureInfo{{ - SignatureManifestDigest: sygMeta.SignatureDigest, - LayersInfo: mConvert.GetProtoLayersInfo(sygMeta.LayersInfo), - }} + newCosignSig := &proto_go.SignatureInfo{ + SignatureManifestDigest: sigMeta.SignatureDigest, + LayersInfo: mConvert.GetProtoLayersInfo(sigMeta.LayersInfo), + } + + if zcommon.IsCosignTag(sigMeta.SignatureTag) { + // the entry for "sha256-{digest}.sig" signatures should be overwritten if + // it exists or added on the first position if it doesn't exists + if len(signatureSlice.GetList()) == 0 { + signatureSlice.List = []*proto_go.SignatureInfo{newCosignSig} + } else { + signatureSlice.List[0] = newCosignSig + } + } else { + // the first position should be reserved for "sha256-{digest}.sig" signatures + if len(signatureSlice.GetList()) == 0 { + signatureSlice.List = []*proto_go.SignatureInfo{{ + SignatureManifestDigest: "", + LayersInfo: []*proto_go.LayersInfo{}, + }} + } + + signatureSlice.List = append(signatureSlice.List, newCosignSig) + } } } - manifestSignatures.Map[sygMeta.SignatureType] = signatureSlice + manifestSignatures.Map[sigMeta.SignatureType] = signatureSlice protoRepoMeta.Signatures[signedManifestDigest.String()] = manifestSignatures return dwr.setProtoRepoMeta(protoRepoMeta.Name, protoRepoMeta) diff --git a/pkg/meta/meta_test.go b/pkg/meta/meta_test.go index 08997c9b26..b7b3b1dbaa 100644 --- a/pkg/meta/meta_test.go +++ b/pkg/meta/meta_test.go @@ -1277,18 +1277,31 @@ func RunMetaDBTests(t *testing.T, metaDB mTypes.MetaDB, preparationFuncs ...func }) So(err, ShouldBeNil) + err = metaDB.AddManifestSignature(repo1, image1.Digest, mTypes.SignatureMetadata{ + SignatureType: "cosign", + SignatureTag: fmt.Sprintf("sha256-%s.sig", image1.Digest.Encoded()), + SignatureDigest: "digesttag", + LayersInfo: []mTypes.LayerInfo{{LayerDigest: "layer-digest", LayerContent: []byte{10}}}, + }) + So(err, ShouldBeNil) + repoMeta, err := metaDB.GetRepoMeta(ctx, repo1) So(err, ShouldBeNil) So(repoMeta.Signatures[image1.Digest.String()]["cosign"][0].SignatureManifestDigest, + ShouldResemble, "digesttag") + So(repoMeta.Signatures[image1.Digest.String()]["cosign"][1].SignatureManifestDigest, ShouldResemble, "digest") imageMeta, err := metaDB.GetImageMeta(image1.Digest) fullImageMeta := convert.GetFullImageMeta(tag1, repoMeta, imageMeta) So(err, ShouldBeNil) - So(fullImageMeta.Signatures["cosign"][0].SignatureManifestDigest, ShouldResemble, "digest") + So(fullImageMeta.Signatures["cosign"][0].SignatureManifestDigest, ShouldResemble, "digesttag") So(fullImageMeta.Signatures["cosign"][0].LayersInfo[0].LayerDigest, ShouldResemble, "layer-digest") So(fullImageMeta.Signatures["cosign"][0].LayersInfo[0].LayerContent[0], ShouldEqual, 10) + So(fullImageMeta.Signatures["cosign"][1].SignatureManifestDigest, ShouldResemble, "digest") + So(fullImageMeta.Signatures["cosign"][1].LayersInfo[0].LayerDigest, ShouldResemble, "layer-digest") + So(fullImageMeta.Signatures["cosign"][1].LayersInfo[0].LayerContent[0], ShouldEqual, 10) }) Convey("Test UpdateSignaturesValidity", func() { @@ -1307,6 +1320,7 @@ func RunMetaDBTests(t *testing.T, metaDB mTypes.MetaDB, preparationFuncs ...func err = metaDB.AddManifestSignature(repo1, image1.Digest(), mTypes.SignatureMetadata{ SignatureType: "cosign", SignatureDigest: image1.DigestStr(), + SignatureTag: fmt.Sprintf("sha256-%s.sig", image1.Digest().Encoded()), LayersInfo: []mTypes.LayerInfo{layerInfo}, }) So(err, ShouldBeNil) @@ -1427,6 +1441,7 @@ func RunMetaDBTests(t *testing.T, metaDB mTypes.MetaDB, preparationFuncs ...func err := metaDB.AddManifestSignature(repo1, image1.Digest(), mTypes.SignatureMetadata{ SignatureType: "cosign", + SignatureTag: fmt.Sprintf("sha256-%s.sig", image1.Digest().Encoded()), SignatureDigest: "digest", }) So(err, ShouldBeNil) @@ -1452,6 +1467,7 @@ func RunMetaDBTests(t *testing.T, metaDB mTypes.MetaDB, preparationFuncs ...func err = metaDB.AddManifestSignature(repo1, image1.Digest(), mTypes.SignatureMetadata{ SignatureType: "cosign", + SignatureTag: fmt.Sprintf("sha256-%s.sig", image1.Digest().Encoded()), SignatureDigest: "digest", }) So(err, ShouldBeNil) diff --git a/pkg/meta/parse.go b/pkg/meta/parse.go index d3797a2759..334ee3932d 100644 --- a/pkg/meta/parse.go +++ b/pkg/meta/parse.go @@ -286,6 +286,7 @@ func SetImageMetaFromInput(repo, reference, mediaType string, digest godigest.Di mTypes.SignatureMetadata{ SignatureType: sigType, SignatureDigest: digest.String(), + SignatureTag: reference, LayersInfo: layers, }) if err != nil { @@ -339,6 +340,11 @@ func isSignature(reference string, manifestContent ispec.Manifest) (bool, string return true, NotationType, manifestContent.Subject.Digest } + // check cosign signature + if manifestArtifactType == zcommon.ArtifactTypeCosign && manifestContent.Subject != nil { + return true, CosignType, manifestContent.Subject.Digest + } + if tag := reference; zcommon.IsCosignTag(reference) { prefixLen := len("sha256-") digestLen := 64 diff --git a/pkg/meta/types/types.go b/pkg/meta/types/types.go index 3d3b209099..58b780a9f4 100644 --- a/pkg/meta/types/types.go +++ b/pkg/meta/types/types.go @@ -295,6 +295,7 @@ type SignatureInfo struct { type SignatureMetadata struct { SignatureType string SignatureDigest string + SignatureTag string LayersInfo []LayerInfo } diff --git a/pkg/storage/common/common.go b/pkg/storage/common/common.go index 8573b4a6ba..59f593e2dd 100644 --- a/pkg/storage/common/common.go +++ b/pkg/storage/common/common.go @@ -610,6 +610,11 @@ func IsSignature(descriptor ispec.Descriptor) bool { return true } + // is cosign signature (OCI 1.1 support) + if descriptor.ArtifactType == zcommon.ArtifactTypeCosign { + return true + } + // is notation signature if descriptor.ArtifactType == zcommon.ArtifactTypeNotation { return true diff --git a/pkg/storage/gc/gc.go b/pkg/storage/gc/gc.go index 220bfd6894..ede312ed56 100644 --- a/pkg/storage/gc/gc.go +++ b/pkg/storage/gc/gc.go @@ -253,9 +253,11 @@ func (gc GarbageCollect) cleanReferrer(repo string, index *ispec.Index, manifest referenced := isManifestReferencedInIndex(index, subject.Digest) var signatureType string - // check if its notation signature + // check if its notation or cosign signature if artifactType == zcommon.ArtifactTypeNotation { signatureType = storage.NotationType + } else if artifactType == zcommon.ArtifactTypeCosign { + signatureType = storage.CosignType } if !referenced { diff --git a/pkg/storage/storage.go b/pkg/storage/storage.go index 7d3cf4cd75..e9f5e37ee4 100644 --- a/pkg/storage/storage.go +++ b/pkg/storage/storage.go @@ -227,6 +227,11 @@ func CheckIsImageSignature(repoName string, manifestBlob []byte, reference strin return true, NotationType, manifestContent.Subject.Digest, nil } + // check cosign signature (OCI 1.1 support) + if manifestArtifactType == zcommon.ArtifactTypeCosign && manifestContent.Subject != nil { + return true, CosignType, manifestContent.Subject.Digest, nil + } + if tag := reference; zcommon.IsCosignTag(reference) { prefixLen := len("sha256-") digestLen := 64 diff --git a/pkg/test/oci-utils/oci_layout.go b/pkg/test/oci-utils/oci_layout.go index 7d3528a5c4..81e189ba1d 100644 --- a/pkg/test/oci-utils/oci_layout.go +++ b/pkg/test/oci-utils/oci_layout.go @@ -262,13 +262,27 @@ func (olu BaseOciLayoutUtils) checkCosignSignature(name string, digest godigest. reference := fmt.Sprintf("sha256-%s.sig", digest.Encoded()) _, _, _, err := imageStore.GetImageManifest(name, reference) //nolint: dogsled + if err == nil { + return true + } + + olu.Log.Info().Err(err).Str("repository", name).Str("digest", + digest.String()).Msg("invalid cosign signature") + + mediaType := common.ArtifactTypeCosign + + referrers, err := imageStore.GetReferrers(name, digest, []string{mediaType}) if err != nil { olu.Log.Info().Err(err).Str("repository", name).Str("digest", - digest.String()).Msg("invalid cosign signature") + digest.String()).Str("mediatype", mediaType).Msg("invalid cosign signature") return false } + if len(referrers.Manifests) == 0 { + return false + } + return true } diff --git a/pkg/test/signature/cosign.go b/pkg/test/signature/cosign.go index 7ef0a89935..52d6d646f3 100644 --- a/pkg/test/signature/cosign.go +++ b/pkg/test/signature/cosign.go @@ -30,7 +30,7 @@ func GetCosignSignatureTagForDigest(manifestDigest godigest.Digest) string { return manifestDigest.Algorithm().String() + "-" + manifestDigest.Encoded() + ".sig" } -func SignImageUsingCosign(repoTag, port string) error { +func SignImageUsingCosign(repoTag, port string, withReferrers bool) error { cwd, err := os.Getwd() if err != nil { return err @@ -59,13 +59,21 @@ func SignImageUsingCosign(repoTag, port string) error { const timeoutPeriod = 5 + signOpts := options.SignOptions{ + Registry: options.RegistryOptions{AllowInsecure: true}, + AnnotationOptions: options.AnnotationOptions{Annotations: []string{"tag=1.0"}}, + Upload: true, + } + + if withReferrers { + signOpts.RegistryExperimental = options.RegistryExperimentalOptions{ + RegistryReferrersMode: options.RegistryReferrersModeOCI11, + } + } + // sign the image return sign.SignCmd(&options.RootOptions{Verbose: true, Timeout: timeoutPeriod * time.Minute}, options.KeyOpts{KeyRef: path.Join(tdir, "cosign.key"), PassFunc: generate.GetPass}, - options.SignOptions{ - Registry: options.RegistryOptions{AllowInsecure: true}, - AnnotationOptions: options.AnnotationOptions{Annotations: []string{"tag=1.0"}}, - Upload: true, - }, + signOpts, []string{imageURL}) } diff --git a/test/blackbox/annotations.bats b/test/blackbox/annotations.bats index bfada64b0a..2cdd901901 100644 --- a/test/blackbox/annotations.bats +++ b/test/blackbox/annotations.bats @@ -115,7 +115,7 @@ function teardown_file() { [ $(echo "${lines[-1]}" | jq '.data.ImageList.Results[0].Licenses') = '"GPLv2"' ] } -@test "sign/verify with cosign" { +@test "sign/verify with cosign (only tag-based signatures)" { run curl -X POST -H "Content-Type: application/json" --data '{ "query": "{ ImageList(repo: \"annotations\") { Results { RepoName Tag Manifests {Digest ConfigDigest Size Layers { Size Digest }} Vendor Licenses }}}"}' http://localhost:8080/v2/_zot/ext/search [ "$status" -eq 0 ] [ $(echo "${lines[-1]}" | jq '.data.ImageList.Results[0].RepoName') = '"annotations"' ] @@ -133,6 +133,80 @@ function teardown_file() { [[ "$sigName" == *"${digest}"* ]] } +@test "sign/verify with cosign (only referrers)" { + run curl -X POST -H "Content-Type: application/json" --data '{ "query": "{ ImageList(repo: \"annotations\") { Results { RepoName Tag Manifests {Digest ConfigDigest Size Layers { Size Digest }} Vendor Licenses }}}"}' http://localhost:8080/v2/_zot/ext/search + [ "$status" -eq 0 ] + [ $(echo "${lines[-1]}" | jq '.data.ImageList.Results[0].RepoName') = '"annotations"' ] + local digest=$(echo "${lines[-1]}" | jq -r '.data.ImageList.Results[0].Manifests[0].Digest') + + export COSIGN_OCI_EXPERIMENTAL=1 + export COSIGN_EXPERIMENTAL=1 + run cosign initialize + [ "$status" -eq 0 ] + run cosign generate-key-pair --output-key-prefix "${BATS_FILE_TMPDIR}/cosign-sign-test-experimental" + [ "$status" -eq 0 ] + run cosign sign --registry-referrers-mode=oci-1-1 --key ${BATS_FILE_TMPDIR}/cosign-sign-test-experimental.key localhost:8080/annotations:latest --yes + [ "$status" -eq 0 ] + run cosign verify --key ${BATS_FILE_TMPDIR}/cosign-sign-test-experimental.pub localhost:8080/annotations:latest + [ "$status" -eq 0 ] + local sigName=$(echo "${lines[-1]}" | jq '.[].critical.image."docker-manifest-digest"') + [[ "$sigName" == *"${digest}"* ]] + unset COSIGN_OCI_EXPERIMENTAL + unset COSIGN_EXPERIMENTAL +} + +@test "sign/verify with cosign (tag and referrers)" { + run curl -X POST -H "Content-Type: application/json" --data '{ "query": "{ ImageList(repo: \"annotations\") { Results { RepoName Tag Manifests {Digest ConfigDigest Size Layers { Size Digest }} Vendor Licenses }}}"}' http://localhost:8080/v2/_zot/ext/search + [ "$status" -eq 0 ] + [ $(echo "${lines[-1]}" | jq '.data.ImageList.Results[0].RepoName') = '"annotations"' ] + local digest=$(echo "${lines[-1]}" | jq -r '.data.ImageList.Results[0].Manifests[0].Digest') + + export COSIGN_OCI_EXPERIMENTAL=1 + export COSIGN_EXPERIMENTAL=1 + run cosign initialize + [ "$status" -eq 0 ] + + run cosign generate-key-pair --output-key-prefix "${BATS_FILE_TMPDIR}/cosign-sign-test-tag-1" + [ "$status" -eq 0 ] + run cosign sign --key ${BATS_FILE_TMPDIR}/cosign-sign-test-tag-1.key localhost:8080/annotations:latest --yes + [ "$status" -eq 0 ] + + run cosign generate-key-pair --output-key-prefix "${BATS_FILE_TMPDIR}/cosign-sign-test-referrers-1" + [ "$status" -eq 0 ] + run cosign sign --registry-referrers-mode=oci-1-1 --key ${BATS_FILE_TMPDIR}/cosign-sign-test-referrers-1.key localhost:8080/annotations:latest --yes + [ "$status" -eq 0 ] + + run cosign generate-key-pair --output-key-prefix "${BATS_FILE_TMPDIR}/cosign-sign-test-tag-2" + [ "$status" -eq 0 ] + run cosign sign --key ${BATS_FILE_TMPDIR}/cosign-sign-test-tag-2.key localhost:8080/annotations:latest --yes + [ "$status" -eq 0 ] + + run cosign verify --key ${BATS_FILE_TMPDIR}/cosign-sign-test-tag-1.pub localhost:8080/annotations:latest + [ "$status" -eq 0 ] + local sigName=$(echo "${lines[-1]}" | jq '.[].critical.image."docker-manifest-digest"') + [[ "$sigName" == *"${digest}"* ]] + run cosign verify --key ${BATS_FILE_TMPDIR}/cosign-sign-test-tag-2.pub localhost:8080/annotations:latest + [ "$status" -eq 0 ] + local sigName=$(echo "${lines[-1]}" | jq '.[].critical.image."docker-manifest-digest"') + [[ "$sigName" == *"${digest}"* ]] + run cosign verify --key ${BATS_FILE_TMPDIR}/cosign-sign-test-referrers-1.pub localhost:8080/annotations:latest + [ "$status" -eq 0 ] + local sigName=$(echo "${lines[-1]}" | jq '.[].critical.image."docker-manifest-digest"') + [[ "$sigName" == *"${digest}"* ]] + + run cosign generate-key-pair --output-key-prefix "${BATS_FILE_TMPDIR}/cosign-sign-test-referrers-2" + [ "$status" -eq 0 ] + run cosign sign --registry-referrers-mode=oci-1-1 --key ${BATS_FILE_TMPDIR}/cosign-sign-test-referrers-2.key localhost:8080/annotations:latest --yes + [ "$status" -eq 0 ] + run cosign verify --key ${BATS_FILE_TMPDIR}/cosign-sign-test-referrers-2.pub localhost:8080/annotations:latest + [ "$status" -eq 0 ] + local sigName=$(echo "${lines[-1]}" | jq '.[].critical.image."docker-manifest-digest"') + [[ "$sigName" == *"${digest}"* ]] + + unset COSIGN_OCI_EXPERIMENTAL + unset COSIGN_EXPERIMENTAL +} + @test "sign/verify with notation" { run curl -X POST -H "Content-Type: application/json" --data '{ "query": "{ ImageList(repo: \"annotations\") { Results { RepoName Tag Manifests {Digest ConfigDigest Size Layers { Size Digest }} Vendor Licenses }}}"}' http://localhost:8080/v2/_zot/ext/search [ "$status" -eq 0 ]