Skip to content

Commit

Permalink
feat: add support for oci1.1 cosign signatures(using referrers) (#1963)
Browse files Browse the repository at this point in the history
- 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 <[email protected]>
  • Loading branch information
Andreea-Lupu authored Nov 6, 2023
1 parent 6a66a9b commit d506551
Show file tree
Hide file tree
Showing 21 changed files with 508 additions and 82 deletions.
22 changes: 21 additions & 1 deletion pkg/cli/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,27 @@ 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

artifactType := url.QueryEscape(common.ArtifactTypeCosign)
URL = fmt.Sprintf("%s/v2/%s/referrers/%s?artifactType=%s",
searchConf.ServURL, repo, digestStr, artifactType)

_, err = makeGETRequest(ctx, URL, username, password, searchConf.VerifyTLS,
searchConf.Debug, &referrers, searchConf.ResultWriter)
if err != nil {
return false
}

if len(referrers.Manifests) == 0 {
return false
}

return true
}

func (p *requestsPool) submitJob(job *httpJob) {
Expand Down
66 changes: 59 additions & 7 deletions pkg/cli/client/image_cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,17 @@ import (
"zotregistry.io/zot/pkg/test/signature"
)

//nolint:dupl
func TestSignature(t *testing.T) {
space := regexp.MustCompile(`\s+`)
repoName := "repo7"

Convey("Test from real server", t, func() {
Convey("Test with cosign signature(tag)", t, func() {
currentWorkingDir, err := os.Getwd()
So(err, ShouldBeNil)

defer func() { _ = os.Chdir(currentWorkingDir) }()

currentDir := t.TempDir()
err = os.Chdir(currentDir)
So(err, ShouldBeNil)
Expand All @@ -59,7 +63,6 @@ func TestSignature(t *testing.T) {
cm.StartAndWait(conf.HTTP.Port)
defer cm.StopServer()

repoName := "repo7"
image := CreateDefaultImage()
err = UploadImage(image, url, repoName, "1.0")
So(err, ShouldBeNil)
Expand Down Expand Up @@ -108,15 +111,68 @@ func TestSignature(t *testing.T) {
actual = strings.TrimSpace(space.ReplaceAllString(buff.String(), " "))
So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 1.0 linux/amd64 db573b01 true 854B")
})

Convey("Test with cosign signature(withReferrers)", t, func() {
currentWorkingDir, err := os.Getwd()
So(err, ShouldBeNil)

defer func() { _ = os.Chdir(currentWorkingDir) }()

currentDir := t.TempDir()
err = os.Chdir(currentDir)
So(err, ShouldBeNil)

port := test.GetFreePort()
url := test.GetBaseURL(port)
conf := config.New()
conf.HTTP.Port = port
defaultVal := true
conf.Extensions = &extconf.ExtensionConfig{
Search: &extconf.SearchConfig{BaseConfig: extconf.BaseConfig{Enable: &defaultVal}},
}
ctlr := api.NewController(conf)
ctlr.Config.Storage.RootDirectory = currentDir
cm := test.NewControllerManager(ctlr)
cm.StartAndWait(conf.HTTP.Port)
defer cm.StopServer()

err = os.Chdir(currentWorkingDir)
err = UploadImage(CreateDefaultImage(), url, repoName, "0.0.1")
So(err, ShouldBeNil)

err = signature.SignImageUsingCosign("repo7:0.0.1", port, true)
So(err, ShouldBeNil)

searchConfig := getTestSearchConfig(url, client.NewSearchService())

t.Logf("%s", ctlr.Config.Storage.RootDirectory)

buff := &bytes.Buffer{}
searchConfig.ResultWriter = buff
err = client.SearchAllImagesGQL(searchConfig)
So(err, ShouldBeNil)

actual := strings.TrimSpace(space.ReplaceAllString(buff.String(), " "))
So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 0.0.1 linux/amd64 db573b01 true 854B")

t.Log("Test getting all images using rest calls to get catalog and individual manifests")
buff = &bytes.Buffer{}
searchConfig.ResultWriter = buff
err = client.SearchAllImages(searchConfig)
So(err, ShouldBeNil)

actual = strings.TrimSpace(space.ReplaceAllString(buff.String(), " "))
So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 0.0.1 linux/amd64 db573b01 true 854B")
})

Convey("Test with notation signature", t, func() {
currentWorkingDir, err := os.Getwd()
So(err, ShouldBeNil)

defer func() { _ = os.Chdir(currentWorkingDir) }()

currentDir := t.TempDir()
err = os.Chdir(currentDir)
So(err, ShouldBeNil)
Expand All @@ -135,7 +191,6 @@ func TestSignature(t *testing.T) {
cm.StartAndWait(conf.HTTP.Port)
defer cm.StopServer()

repoName := "repo7"
err = UploadImage(CreateDefaultImage(), url, repoName, "0.0.1")
So(err, ShouldBeNil)

Expand Down Expand Up @@ -164,9 +219,6 @@ func TestSignature(t *testing.T) {
actual = strings.TrimSpace(space.ReplaceAllString(buff.String(), " "))
So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE")
So(actual, ShouldContainSubstring, "repo7 0.0.1 linux/amd64 db573b01 true 854B")

err = os.Chdir(currentWorkingDir)
So(err, ShouldBeNil)
})
}

Expand Down
1 change: 1 addition & 0 deletions pkg/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -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`)
Expand Down
16 changes: 8 additions & 8 deletions pkg/extensions/search/search_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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 := `{
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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)
})
})
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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 := `
Expand Down
2 changes: 1 addition & 1 deletion pkg/extensions/sync/references/oci.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
21 changes: 7 additions & 14 deletions pkg/extensions/sync/references/references.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import (
"zotregistry.io/zot/pkg/common"
client "zotregistry.io/zot/pkg/extensions/sync/httpclient"
"zotregistry.io/zot/pkg/log"
"zotregistry.io/zot/pkg/meta"
mTypes "zotregistry.io/zot/pkg/meta/types"
"zotregistry.io/zot/pkg/storage"
storageTypes "zotregistry.io/zot/pkg/storage/types"
Expand Down Expand Up @@ -218,20 +217,14 @@ func getNotationManifestsFromOCIRefs(ociRefs ispec.Index) []ispec.Descriptor {
return notaryManifests
}

func addSigToMeta(
metaDB mTypes.MetaDB, repo, sigType, tag string, signedManifestDig, referenceDigest godigest.Digest,
referenceBuf []byte, imageStore storageTypes.ImageStore, log log.Logger,
) error {
layersInfo, errGetLayers := meta.GetSignatureLayersInfo(repo, tag, referenceDigest.String(),
sigType, referenceBuf, imageStore, log)
func getCosignManifestsFromOCIRefs(ociRefs ispec.Index) []ispec.Descriptor {
cosignManifests := []ispec.Descriptor{}

if errGetLayers != nil {
return errGetLayers
for _, ref := range ociRefs.Manifests {
if ref.ArtifactType == common.ArtifactTypeCosign {
cosignManifests = append(cosignManifests, ref)
}
}

return metaDB.AddManifestSignature(repo, signedManifestDig, mTypes.SignatureMetadata{
SignatureType: sigType,
SignatureDigest: referenceDigest.String(),
LayersInfo: layersInfo,
})
return cosignManifests
}
11 changes: 0 additions & 11 deletions pkg/extensions/sync/references/references_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -440,14 +440,3 @@ func TestCompareArtifactRefs(t *testing.T) {
}
})
}

func TestAddSigToMeta(t *testing.T) {
Convey("Test addSigToMeta", t, func() {
imageStore := mocks.MockedImageStore{}
metaDB := mocks.MetaDBMock{}

err := addSigToMeta(metaDB, "repo", "cosign", "tag", godigest.FromString("signedmanifest"),
godigest.FromString("reference"), []byte("bad"), imageStore, log.Logger{})
So(err, ShouldNotBeNil)
})
}
Loading

0 comments on commit d506551

Please sign in to comment.