mirror of
https://github.com/project-zot/zot.git
synced 2026-06-16 04:17:55 +08:00
fix: call notation-go libs instead of using notation binary (#1104)
fix: add loading notation path Signed-off-by: Andreea-Lupu <andreealupu1470@yahoo.com> Co-authored-by: Roxana Nemulescu <roxana.nemulescu@gmail.com>
This commit is contained in:
@@ -24,7 +24,6 @@ import (
|
||||
godigest "github.com/opencontainers/go-digest"
|
||||
"github.com/opencontainers/image-spec/specs-go"
|
||||
ispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
artifactspec "github.com/oras-project/artifacts-spec/specs-go/v1"
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
"gopkg.in/resty.v1"
|
||||
|
||||
@@ -4085,7 +4084,7 @@ func TestRepoDBWhenPushingImages(t *testing.T) {
|
||||
baseURL,
|
||||
"repo1",
|
||||
)
|
||||
So(err, ShouldBeNil)
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("SetManifestMeta succeeds but SetRepoTag fails", func() {
|
||||
@@ -4458,10 +4457,10 @@ func TestRepoDBWhenDeletingImages(t *testing.T) {
|
||||
|
||||
signatureReference := ""
|
||||
|
||||
var sigManifestContent artifactspec.Manifest
|
||||
var sigManifestContent ispec.Artifact
|
||||
|
||||
for _, manifest := range indexContent.Manifests {
|
||||
if manifest.MediaType == artifactspec.MediaTypeArtifactManifest {
|
||||
if manifest.MediaType == ispec.MediaTypeArtifactManifest {
|
||||
signatureReference = manifest.Digest.String()
|
||||
manifestBlob, _, _, err := storage.GetImageManifest(repo, signatureReference)
|
||||
So(err, ShouldBeNil)
|
||||
@@ -4854,6 +4853,82 @@ func TestBaseOciLayoutUtils(t *testing.T) {
|
||||
_, err := olu.GetImageInfo("", "")
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("CheckManifestSignature: notation", t, func() {
|
||||
// GetReferrers - fails => checkNotarySignature returns false
|
||||
mockStoreController := mocks.MockedImageStore{
|
||||
GetImageManifestFn: func(name, reference string) ([]byte, godigest.Digest, string, error) {
|
||||
return []byte{}, "", "", zerr.ErrRepoNotFound
|
||||
},
|
||||
GetReferrersFn: func(name string, digest godigest.Digest, mediaTypes []string) (ispec.Index, error) {
|
||||
return ispec.Index{}, ErrTestError
|
||||
},
|
||||
}
|
||||
|
||||
storeController := storage.StoreController{DefaultStore: mockStoreController}
|
||||
olu := common.NewBaseOciLayoutUtils(storeController, log.NewLogger("debug", ""))
|
||||
|
||||
check := olu.CheckManifestSignature("rep", godigest.FromString(""))
|
||||
So(check, ShouldBeFalse)
|
||||
|
||||
// checkNotarySignature -> true
|
||||
dir := t.TempDir()
|
||||
|
||||
port := GetFreePort()
|
||||
baseURL := GetBaseURL(port)
|
||||
conf := config.New()
|
||||
conf.HTTP.Port = port
|
||||
conf.Storage.RootDirectory = dir
|
||||
defaultVal := true
|
||||
conf.Extensions = &extconf.ExtensionConfig{
|
||||
Search: &extconf.SearchConfig{BaseConfig: extconf.BaseConfig{Enable: &defaultVal}},
|
||||
}
|
||||
|
||||
conf.Extensions.Search.CVE = nil
|
||||
|
||||
ctlr := api.NewController(conf)
|
||||
|
||||
ctlrManager := NewControllerManager(ctlr)
|
||||
ctlrManager.StartAndWait(port)
|
||||
defer ctlrManager.StopServer()
|
||||
|
||||
// push test image to repo
|
||||
config, layers, manifest, err := GetImageComponents(100)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
layersSize1 := 0
|
||||
for _, l := range layers {
|
||||
layersSize1 += len(l)
|
||||
}
|
||||
|
||||
repo := "repo"
|
||||
tag := "1.0.1"
|
||||
err = UploadImage(
|
||||
Image{
|
||||
Manifest: manifest,
|
||||
Config: config,
|
||||
Layers: layers,
|
||||
Tag: tag,
|
||||
},
|
||||
baseURL,
|
||||
repo,
|
||||
)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
olu = common.NewBaseOciLayoutUtils(ctlr.StoreController, log.NewLogger("debug", ""))
|
||||
manifestList, err := olu.GetImageManifests(repo)
|
||||
So(err, ShouldBeNil)
|
||||
So(len(manifestList), ShouldEqual, 1)
|
||||
|
||||
isSigned := olu.CheckManifestSignature(repo, manifestList[0].Digest)
|
||||
So(isSigned, ShouldBeFalse)
|
||||
|
||||
err = SignImageUsingNotary(fmt.Sprintf("%s:%s", repo, tag), port)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
isSigned = olu.CheckManifestSignature(repo, manifestList[0].Digest)
|
||||
So(isSigned, ShouldBeTrue)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSearchSize(t *testing.T) {
|
||||
|
||||
@@ -218,7 +218,7 @@ func (olu BaseOciLayoutUtils) checkNotarySignature(name string, digest godigest.
|
||||
imageStore := olu.StoreController.GetImageStore(name)
|
||||
mediaType := notreg.ArtifactTypeNotation
|
||||
|
||||
_, err := imageStore.GetOrasReferrers(name, digest, mediaType)
|
||||
referrers, err := imageStore.GetReferrers(name, digest, []string{mediaType})
|
||||
if err != nil {
|
||||
olu.Log.Info().Err(err).Str("repo", name).Str("digest",
|
||||
digest.String()).Str("mediatype", mediaType).Msg("invalid notary signature")
|
||||
@@ -226,6 +226,10 @@ func (olu BaseOciLayoutUtils) checkNotarySignature(name string, digest godigest.
|
||||
return false
|
||||
}
|
||||
|
||||
if len(referrers.Manifests) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -300,14 +300,14 @@ func syncRun(regCfg RegistryConfig,
|
||||
Err(err).Msgf("couldn't get upstream image %s cosign manifest", upstreamImageRef.DockerReference())
|
||||
}
|
||||
|
||||
refs, err := sig.getNotaryRefs(upstreamRepo, upstreamImageDigest.String())
|
||||
index, err := sig.getOCIRefs(upstreamRepo, upstreamImageDigest.String())
|
||||
if err != nil {
|
||||
log.Error().Str("errorType", common.TypeOf(err)).
|
||||
Err(err).Msgf("couldn't get upstream image %s notary references", upstreamImageRef.DockerReference())
|
||||
Err(err).Msgf("couldn't get upstream image %s OCI references", upstreamImageRef.DockerReference())
|
||||
}
|
||||
|
||||
// check if upstream image is signed
|
||||
if cosignManifest == nil && len(refs.References) == 0 {
|
||||
if cosignManifest == nil && len(getNotationManifestsFromOCIRefs(index)) == 0 {
|
||||
// upstream image not signed
|
||||
if regCfg.OnlySigned != nil && *regCfg.OnlySigned {
|
||||
// skip unsigned images
|
||||
@@ -355,12 +355,6 @@ func syncRun(regCfg RegistryConfig,
|
||||
return false, err
|
||||
}
|
||||
|
||||
index, err := sig.getOCIRefs(upstreamRepo, upstreamImageDigest.String())
|
||||
if err != nil {
|
||||
log.Error().Str("errorType", common.TypeOf(err)).
|
||||
Err(err).Msgf("couldn't get upstream image %s oci references", upstreamImageRef.DockerReference())
|
||||
}
|
||||
|
||||
err = sig.syncOCIRefs(localRepo, upstreamRepo, upstreamImageDigest.String(), index)
|
||||
if err != nil {
|
||||
return false, err
|
||||
@@ -374,10 +368,16 @@ func syncRun(regCfg RegistryConfig,
|
||||
return false, err
|
||||
}
|
||||
|
||||
err = sig.syncNotaryRefs(localRepo, upstreamRepo, upstreamImageDigest.String(), refs)
|
||||
refs, err := sig.getORASRefs(upstreamRepo, upstreamImageDigest.String())
|
||||
if err != nil {
|
||||
log.Error().Str("errorType", common.TypeOf(err)).
|
||||
Err(err).Msgf("couldn't copy image notary signature %s/%s:%s", utils.upstreamAddr, upstreamRepo, reference)
|
||||
Err(err).Msgf("couldn't get upstream image %s ORAS references", upstreamImageRef.DockerReference())
|
||||
}
|
||||
|
||||
err = sig.syncORASRefs(localRepo, upstreamRepo, upstreamImageDigest.String(), refs)
|
||||
if err != nil {
|
||||
log.Error().Str("errorType", common.TypeOf(err)).
|
||||
Err(err).Msgf("couldn't copy image ORAS references %s/%s:%s", utils.upstreamAddr, upstreamRepo, reference)
|
||||
|
||||
return false, err
|
||||
}
|
||||
@@ -409,27 +409,28 @@ func syncSignaturesArtifacts(sig *signaturesCopier, localRepo, upstreamRepo, ref
|
||||
return err
|
||||
}
|
||||
case artifactType == OrasArtifact:
|
||||
// is notary signature
|
||||
refs, err := sig.getNotaryRefs(upstreamRepo, reference)
|
||||
// is oras artifact
|
||||
refs, err := sig.getORASRefs(upstreamRepo, reference)
|
||||
if err != nil {
|
||||
sig.log.Error().Str("errorType", common.TypeOf(err)).
|
||||
Err(err).Msgf("couldn't get upstream image %s/%s:%s notary references", upstreamURL, upstreamRepo, reference)
|
||||
Err(err).Msgf("couldn't get upstream image %s/%s:%s ORAS references", upstreamURL, upstreamRepo, reference)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
err = sig.syncNotaryRefs(localRepo, upstreamRepo, reference, refs)
|
||||
err = sig.syncORASRefs(localRepo, upstreamRepo, reference, refs)
|
||||
if err != nil {
|
||||
sig.log.Error().Str("errorType", common.TypeOf(err)).
|
||||
Err(err).Msgf("couldn't copy image signature %s/%s:%s", upstreamURL, upstreamRepo, reference)
|
||||
Err(err).Msgf("couldn't copy image ORAS references %s/%s:%s", upstreamURL, upstreamRepo, reference)
|
||||
|
||||
return err
|
||||
}
|
||||
case artifactType == OCIReference:
|
||||
// this contains notary signatures
|
||||
index, err := sig.getOCIRefs(upstreamRepo, reference)
|
||||
if err != nil {
|
||||
sig.log.Error().Str("errorType", common.TypeOf(err)).
|
||||
Err(err).Msgf("couldn't get oci references %s/%s:%s", upstreamURL, upstreamRepo, reference)
|
||||
Err(err).Msgf("couldn't get OCI references %s/%s:%s", upstreamURL, upstreamRepo, reference)
|
||||
|
||||
return err
|
||||
}
|
||||
@@ -437,7 +438,7 @@ func syncSignaturesArtifacts(sig *signaturesCopier, localRepo, upstreamRepo, ref
|
||||
err = sig.syncOCIRefs(localRepo, upstreamRepo, reference, index)
|
||||
if err != nil {
|
||||
sig.log.Error().Str("errorType", common.TypeOf(err)).
|
||||
Err(err).Msgf("couldn't copy oci references %s/%s:%s", upstreamURL, upstreamRepo, reference)
|
||||
Err(err).Msgf("couldn't copy OCI references %s/%s:%s", upstreamURL, upstreamRepo, reference)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ func (sig *signaturesCopier) getCosignManifest(repo, digestStr string) (*ispec.M
|
||||
return &cosignManifest, nil
|
||||
}
|
||||
|
||||
func (sig *signaturesCopier) getNotaryRefs(repo, digestStr string) (ReferenceList, error) {
|
||||
func (sig *signaturesCopier) getORASRefs(repo, digestStr string) (ReferenceList, error) {
|
||||
var referrers ReferenceList
|
||||
|
||||
getReferrersURL := sig.upstreamURL
|
||||
@@ -89,12 +89,12 @@ func (sig *signaturesCopier) getNotaryRefs(repo, digestStr string) (ReferenceLis
|
||||
getReferrersURL.String(), "application/json", sig.log)
|
||||
if err != nil {
|
||||
if statusCode == http.StatusNotFound {
|
||||
sig.log.Info().Err(err).Msg("couldn't find any notary signatures/oras artifacts")
|
||||
sig.log.Info().Err(err).Msg("couldn't find any ORAS artifact")
|
||||
|
||||
return referrers, zerr.ErrSyncReferrerNotFound
|
||||
}
|
||||
|
||||
sig.log.Error().Err(err).Msg("couldn't get notary signatures/oras artifacts")
|
||||
sig.log.Error().Err(err).Msg("couldn't get ORAS artifacts")
|
||||
|
||||
return referrers, err
|
||||
}
|
||||
@@ -188,25 +188,25 @@ func (sig *signaturesCopier) syncCosignSignature(localRepo, remoteRepo, digestSt
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sig *signaturesCopier) syncNotaryRefs(localRepo, remoteRepo, digestStr string, referrers ReferenceList,
|
||||
func (sig *signaturesCopier) syncORASRefs(localRepo, remoteRepo, digestStr string, referrers ReferenceList,
|
||||
) error {
|
||||
if len(referrers.References) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
skipNotarySig, err := sig.canSkipNotaryRefs(localRepo, digestStr, referrers)
|
||||
skipORASRefs, err := sig.canSkipORASRefs(localRepo, digestStr, referrers)
|
||||
if err != nil {
|
||||
sig.log.Error().Err(err).Msgf("couldn't check if the upstream image %s:%s notary signature can be skipped",
|
||||
sig.log.Error().Err(err).Msgf("couldn't check if the upstream image %s:%s ORAS artifact can be skipped",
|
||||
remoteRepo, digestStr)
|
||||
}
|
||||
|
||||
if skipNotarySig {
|
||||
if skipORASRefs {
|
||||
return nil
|
||||
}
|
||||
|
||||
imageStore := sig.storeController.GetImageStore(localRepo)
|
||||
|
||||
sig.log.Info().Msg("syncing notary signatures")
|
||||
sig.log.Info().Msg("syncing ORAS artifacts")
|
||||
|
||||
for _, ref := range referrers.References {
|
||||
// get referrer manifest
|
||||
@@ -222,13 +222,13 @@ func (sig *signaturesCopier) syncNotaryRefs(localRepo, remoteRepo, digestStr str
|
||||
if err != nil {
|
||||
if statusCode == http.StatusNotFound {
|
||||
sig.log.Error().Str("errorType", common.TypeOf(err)).
|
||||
Err(err).Msgf("couldn't find any notary manifest: %s", getRefManifestURL.String())
|
||||
Err(err).Msgf("couldn't find any ORAS manifest: %s", getRefManifestURL.String())
|
||||
|
||||
return zerr.ErrSyncReferrerNotFound
|
||||
}
|
||||
|
||||
sig.log.Error().Str("errorType", common.TypeOf(err)).
|
||||
Err(err).Msgf("couldn't get notary manifest: %s", getRefManifestURL.String())
|
||||
Err(err).Msgf("couldn't get ORAS manifest: %s", getRefManifestURL.String())
|
||||
|
||||
return err
|
||||
}
|
||||
@@ -243,13 +243,13 @@ func (sig *signaturesCopier) syncNotaryRefs(localRepo, remoteRepo, digestStr str
|
||||
oras.MediaTypeArtifactManifest, body)
|
||||
if err != nil {
|
||||
sig.log.Error().Str("errorType", common.TypeOf(err)).
|
||||
Err(err).Msg("couldn't upload notary sig manifest")
|
||||
Err(err).Msg("couldn't upload ORAS manifest")
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
sig.log.Info().Msgf("successfully synced notary signature for repo %s digest %s", localRepo, digestStr)
|
||||
sig.log.Info().Msgf("successfully synced ORAS artifacts for repo %s digest %s", localRepo, digestStr)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -409,33 +409,33 @@ func (sig *signaturesCopier) syncOCIArtifact(localRepo, remoteRepo, reference st
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sig *signaturesCopier) canSkipNotaryRefs(localRepo, digestStr string, refs ReferenceList,
|
||||
func (sig *signaturesCopier) canSkipORASRefs(localRepo, digestStr string, refs ReferenceList,
|
||||
) (bool, error) {
|
||||
imageStore := sig.storeController.GetImageStore(localRepo)
|
||||
digest := godigest.Digest(digestStr)
|
||||
|
||||
// check notary signature already synced
|
||||
// check oras artifacts already synced
|
||||
if len(refs.References) > 0 {
|
||||
localRefs, err := imageStore.GetOrasReferrers(localRepo, digest, notreg.ArtifactTypeNotation)
|
||||
localRefs, err := imageStore.GetOrasReferrers(localRepo, digest, "")
|
||||
if err != nil {
|
||||
if errors.Is(err, zerr.ErrManifestNotFound) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
sig.log.Error().Str("errorType", common.TypeOf(err)).
|
||||
Err(err).Msgf("couldn't get local notary signature %s:%s manifest", localRepo, digestStr)
|
||||
Err(err).Msgf("couldn't get local ORAS artifact %s:%s manifest", localRepo, digestStr)
|
||||
|
||||
return false, err
|
||||
}
|
||||
|
||||
if !artifactDescriptorsEqual(localRefs, refs.References) {
|
||||
sig.log.Info().Msgf("upstream notary signatures %s:%s changed, syncing again", localRepo, digestStr)
|
||||
sig.log.Info().Msgf("upstream ORAS artifacts %s:%s changed, syncing again", localRepo, digestStr)
|
||||
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
sig.log.Info().Msgf("skipping notary signature %s:%s, already synced", localRepo, digestStr)
|
||||
sig.log.Info().Msgf("skipping ORAS artifact %s:%s, already synced", localRepo, digestStr)
|
||||
|
||||
return true, nil
|
||||
}
|
||||
@@ -608,3 +608,15 @@ func getCosignTagFromImageDigest(digestStr string) string {
|
||||
|
||||
return digestStr
|
||||
}
|
||||
|
||||
func getNotationManifestsFromOCIRefs(ociRefs ispec.Index) []ispec.Descriptor {
|
||||
notaryManifests := []ispec.Descriptor{}
|
||||
|
||||
for _, ref := range ociRefs.Manifests {
|
||||
if ref.ArtifactType == notreg.ArtifactTypeNotation {
|
||||
notaryManifests = append(notaryManifests, ref)
|
||||
}
|
||||
}
|
||||
|
||||
return notaryManifests
|
||||
}
|
||||
|
||||
@@ -334,15 +334,15 @@ func syncRegistry(ctx context.Context, regCfg RegistryConfig,
|
||||
return err
|
||||
}
|
||||
|
||||
refs, err := sig.getNotaryRefs(upstreamRepo, upstreamImageDigest.String())
|
||||
index, err := sig.getOCIRefs(upstreamRepo, upstreamImageDigest.String())
|
||||
if err != nil && !errors.Is(err, zerr.ErrSyncReferrerNotFound) {
|
||||
log.Error().Err(err).Msgf("couldn't get upstream image %s notary references", upstreamImageRef.DockerReference())
|
||||
log.Error().Err(err).Msgf("couldn't get upstream image %s OCI references", upstreamImageRef.DockerReference())
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// check if upstream image is signed
|
||||
if cosignManifest == nil && len(refs.References) == 0 {
|
||||
if cosignManifest == nil && len(getNotationManifestsFromOCIRefs(index)) == 0 {
|
||||
// upstream image not signed
|
||||
if regCfg.OnlySigned != nil && *regCfg.OnlySigned {
|
||||
// skip unsigned images
|
||||
@@ -399,17 +399,17 @@ func syncRegistry(ctx context.Context, regCfg RegistryConfig,
|
||||
|
||||
// sync signatures
|
||||
if err = retry.RetryIfNecessary(ctx, func() error {
|
||||
index, err := sig.getOCIRefs(upstreamRepo, upstreamImageDigest.String())
|
||||
if err != nil && !errors.Is(err, zerr.ErrSyncReferrerNotFound) {
|
||||
return err
|
||||
}
|
||||
|
||||
err = sig.syncOCIRefs(localRepo, upstreamRepo, upstreamImageDigest.String(), index)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = sig.syncNotaryRefs(localRepo, upstreamRepo, upstreamImageDigest.String(), refs)
|
||||
refs, err := sig.getORASRefs(upstreamRepo, upstreamImageDigest.String())
|
||||
if err != nil && !errors.Is(err, zerr.ErrSyncReferrerNotFound) {
|
||||
return err
|
||||
}
|
||||
|
||||
err = sig.syncORASRefs(localRepo, upstreamRepo, upstreamImageDigest.String(), refs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -358,8 +358,10 @@ func TestSyncInternal(t *testing.T) {
|
||||
So(err, ShouldBeNil)
|
||||
So(regURL, ShouldNotBeNil)
|
||||
|
||||
ref := artifactspec.Descriptor{
|
||||
Digest: "fakeDigest",
|
||||
ref := ispec.Descriptor{
|
||||
MediaType: ispec.MediaTypeArtifactManifest,
|
||||
Digest: "fakeDigest",
|
||||
ArtifactType: "application/vnd.cncf.notary.signature",
|
||||
}
|
||||
|
||||
desc := ispec.Descriptor{
|
||||
@@ -383,7 +385,7 @@ func TestSyncInternal(t *testing.T) {
|
||||
err = sig.syncCosignSignature(testImage, testImage, testImageTag, &manifest)
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
err = sig.syncNotaryRefs(testImage, testImage, "invalidDigest", ReferenceList{[]artifactspec.Descriptor{ref}})
|
||||
err = sig.syncOCIRefs(testImage, testImage, "invalidDigest", ispec.Index{Manifests: []ispec.Descriptor{ref}})
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
@@ -398,9 +400,11 @@ func TestSyncInternal(t *testing.T) {
|
||||
imageStore := local.NewImageStore(storageDir, false, storage.DefaultGCDelay,
|
||||
false, false, log, metrics, nil, nil)
|
||||
|
||||
refs := ReferenceList{[]artifactspec.Descriptor{
|
||||
refs := ispec.Index{Manifests: []ispec.Descriptor{
|
||||
{
|
||||
Digest: "fakeDigest",
|
||||
MediaType: ispec.MediaTypeArtifactManifest,
|
||||
Digest: "fakeDigest",
|
||||
ArtifactType: "application/vnd.cncf.notary.signature",
|
||||
},
|
||||
}}
|
||||
|
||||
@@ -425,14 +429,34 @@ func TestSyncInternal(t *testing.T) {
|
||||
client := &http.Client{}
|
||||
sig := newSignaturesCopier(client, Credentials{}, *regURL, storage.StoreController{DefaultStore: imageStore}, log)
|
||||
|
||||
canBeSkipped, err = sig.canSkipNotaryRefs(testImage, testImageManifestDigest.String(), refs)
|
||||
canBeSkipped, err = sig.canSkipOCIRefs(testImage, testImageManifestDigest.String(), refs)
|
||||
So(err, ShouldBeNil)
|
||||
So(canBeSkipped, ShouldBeFalse)
|
||||
|
||||
var index ispec.Index
|
||||
indexPath := path.Join(imageStore.RootDir(), testImage, "index.json")
|
||||
buf, err := os.ReadFile(indexPath)
|
||||
So(err, ShouldBeNil)
|
||||
err = json.Unmarshal(buf, &index)
|
||||
So(err, ShouldBeNil)
|
||||
index.Manifests = append(index.Manifests, ispec.Descriptor{
|
||||
MediaType: ispec.MediaTypeArtifactManifest,
|
||||
Digest: godigest.FromString(""),
|
||||
ArtifactType: "application/vnd.cncf.notary.signature",
|
||||
})
|
||||
indexBuf, err := json.Marshal(index)
|
||||
So(err, ShouldBeNil)
|
||||
err = os.WriteFile(indexPath, indexBuf, 0o600)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
canBeSkipped, err = sig.canSkipOCIRefs(testImage, testImageManifestDigest.String(), refs)
|
||||
So(err, ShouldBeNil)
|
||||
So(canBeSkipped, ShouldBeFalse)
|
||||
|
||||
err = os.Chmod(path.Join(imageStore.RootDir(), testImage, "index.json"), 0o000)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
canBeSkipped, err = sig.canSkipNotaryRefs(testImage, testImageManifestDigest.String(), refs)
|
||||
canBeSkipped, err = sig.canSkipOCIRefs(testImage, testImageManifestDigest.String(), refs)
|
||||
So(err, ShouldNotBeNil)
|
||||
So(canBeSkipped, ShouldBeFalse)
|
||||
|
||||
@@ -457,6 +481,65 @@ func TestSyncInternal(t *testing.T) {
|
||||
canBeSkipped, err = sig.canSkipCosignSignature(testImage, testImageManifestDigest.String(), &cosignManifest)
|
||||
So(err, ShouldBeNil)
|
||||
So(canBeSkipped, ShouldBeFalse)
|
||||
|
||||
// test canSkipOrasRefs()
|
||||
refList := ReferenceList{}
|
||||
canBeSkipped, err = sig.canSkipORASRefs(testImage, testImageManifestDigest.String(), refList)
|
||||
So(err, ShouldBeNil)
|
||||
So(canBeSkipped, ShouldBeTrue)
|
||||
|
||||
refList.References = append(refList.References, artifactspec.Descriptor{
|
||||
MediaType: artifactspec.MediaTypeArtifactManifest,
|
||||
Digest: godigest.FromString(""),
|
||||
ArtifactType: "application/vnd.oras.artifact.ref",
|
||||
})
|
||||
canBeSkipped, err = sig.canSkipORASRefs(testImage, testImageManifestDigest.String(), refList)
|
||||
So(err, ShouldBeNil)
|
||||
So(canBeSkipped, ShouldBeFalse)
|
||||
|
||||
err = sig.syncORASRefs(testImage, testImage, testImageManifestDigest.String(), refList)
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
buf, err = os.ReadFile(indexPath)
|
||||
So(err, ShouldBeNil)
|
||||
err = json.Unmarshal(buf, &index)
|
||||
So(err, ShouldBeNil)
|
||||
blobs := []artifactspec.Descriptor{}
|
||||
manifest := artifactspec.Manifest{
|
||||
MediaType: artifactspec.MediaTypeArtifactManifest,
|
||||
Blobs: blobs,
|
||||
Subject: &artifactspec.Descriptor{Digest: testImageManifestDigest},
|
||||
}
|
||||
manifestBuf, err := json.Marshal(manifest)
|
||||
So(err, ShouldBeNil)
|
||||
index.Manifests = append(index.Manifests, ispec.Descriptor{
|
||||
MediaType: artifactspec.MediaTypeArtifactManifest,
|
||||
Digest: godigest.FromBytes(manifestBuf),
|
||||
ArtifactType: "application/vnd.oras.artifact",
|
||||
})
|
||||
indexBuf, err = json.Marshal(index)
|
||||
So(err, ShouldBeNil)
|
||||
err = os.WriteFile(indexPath, indexBuf, 0o600)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = os.Chmod(path.Join(imageStore.RootDir(), testImage, "index.json"), 0o000)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
canBeSkipped, err = sig.canSkipORASRefs(testImage, testImageManifestDigest.String(), refList)
|
||||
So(err, ShouldNotBeNil)
|
||||
So(canBeSkipped, ShouldBeFalse)
|
||||
|
||||
err = os.Chmod(path.Join(imageStore.RootDir(), testImage, "index.json"), 0o755)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = os.WriteFile(path.Join(imageStore.RootDir(), testImage,
|
||||
"blobs", "sha256", godigest.FromBytes(manifestBuf).Encoded()),
|
||||
manifestBuf, 0o600)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
canBeSkipped, err = sig.canSkipORASRefs(testImage, testImageManifestDigest.String(), refList)
|
||||
So(err, ShouldBeNil)
|
||||
So(canBeSkipped, ShouldBeFalse)
|
||||
})
|
||||
|
||||
Convey("Test filterRepos()", t, func() {
|
||||
|
||||
+329
-100
@@ -10,7 +10,6 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -22,11 +21,10 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/notaryproject/notation-go"
|
||||
notreg "github.com/notaryproject/notation-go/registry"
|
||||
godigest "github.com/opencontainers/go-digest"
|
||||
ispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
oraspec "github.com/oras-project/artifacts-spec/specs-go/v1"
|
||||
artifactspec "github.com/oras-project/artifacts-spec/specs-go/v1"
|
||||
perr "github.com/pkg/errors"
|
||||
"github.com/sigstore/cosign/cmd/cosign/cli/generate"
|
||||
"github.com/sigstore/cosign/cmd/cosign/cli/options"
|
||||
@@ -74,33 +72,13 @@ type TagsList struct {
|
||||
}
|
||||
|
||||
type ReferenceList struct {
|
||||
References []notation.Descriptor `json:"references"`
|
||||
References []ispec.Descriptor `json:"references"`
|
||||
}
|
||||
|
||||
type catalog struct {
|
||||
Repositories []string `json:"repositories"`
|
||||
}
|
||||
|
||||
func copyFile(sourceFilePath, destFilePath string) error {
|
||||
destFile, err := os.Create(destFilePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer destFile.Close()
|
||||
|
||||
sourceFile, err := os.Open(sourceFilePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer sourceFile.Close()
|
||||
|
||||
if _, err = io.Copy(destFile, sourceFile); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func startUpstreamServer(
|
||||
t *testing.T, secure, basicAuth bool,
|
||||
) (*api.Controller, string, string, string, *resty.Client) {
|
||||
@@ -324,6 +302,180 @@ func TestORAS(t *testing.T) {
|
||||
So(err, ShouldBeNil)
|
||||
So(string(output), ShouldContainSubstring, "helloworld")
|
||||
})
|
||||
|
||||
Convey("Verify get and sync oras refs", t, func() {
|
||||
updateDuration, _ := time.ParseDuration("30m")
|
||||
|
||||
sctlr, srcBaseURL, srcDir, _, _ := startUpstreamServer(t, false, false)
|
||||
|
||||
defer func() {
|
||||
sctlr.Shutdown()
|
||||
}()
|
||||
|
||||
repoName := testImage
|
||||
var digest godigest.Digest
|
||||
So(func() { digest = pushRepo(srcBaseURL, repoName) }, ShouldNotPanic)
|
||||
|
||||
regex := ".*"
|
||||
var semver bool
|
||||
var tlsVerify bool
|
||||
|
||||
syncRegistryConfig := sync.RegistryConfig{
|
||||
Content: []sync.Content{
|
||||
{
|
||||
Prefix: repoName,
|
||||
Tags: &sync.Tags{
|
||||
Regex: ®ex,
|
||||
Semver: &semver,
|
||||
},
|
||||
},
|
||||
},
|
||||
URLs: []string{srcBaseURL},
|
||||
PollInterval: updateDuration,
|
||||
TLSVerify: &tlsVerify,
|
||||
CertDir: "",
|
||||
OnDemand: true,
|
||||
}
|
||||
|
||||
defaultVal := true
|
||||
syncConfig := &sync.Config{
|
||||
Enable: &defaultVal,
|
||||
Registries: []sync.RegistryConfig{syncRegistryConfig},
|
||||
}
|
||||
|
||||
dctlr, destBaseURL, destDir, destClient := startDownstreamServer(t, false, syncConfig)
|
||||
|
||||
defer func() {
|
||||
dctlr.Shutdown()
|
||||
}()
|
||||
|
||||
// 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 oras refs from downstream, should be synced
|
||||
getORASReferrersURL := destBaseURL + path.Join("/oras/artifacts/v1/", repoName, "manifests", digest.String(), "referrers") //nolint:lll
|
||||
|
||||
resp, err := resty.R().Get(getORASReferrersURL)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(resp, ShouldNotBeEmpty)
|
||||
So(resp.StatusCode(), ShouldEqual, http.StatusNotFound)
|
||||
|
||||
err = os.Chmod(path.Join(destDir, testImage, "index.json"), 0o000)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
resp, err = resty.R().Get(getORASReferrersURL)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(resp, ShouldNotBeEmpty)
|
||||
So(resp.StatusCode(), ShouldEqual, http.StatusInternalServerError)
|
||||
|
||||
err = os.Chmod(path.Join(destDir, testImage, "index.json"), 0o755)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
// get manifest digest from source
|
||||
resp, err = destClient.R().Get(srcBaseURL + "/v2/" + testImage + "/manifests/" + digest.String())
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 200)
|
||||
|
||||
digest = godigest.FromBytes(resp.Body())
|
||||
|
||||
content := []byte("blob content")
|
||||
adigest := pushBlob(srcBaseURL, repoName, content)
|
||||
|
||||
artifactManifest := ispec.Artifact{
|
||||
MediaType: artifactspec.MediaTypeArtifactManifest,
|
||||
ArtifactType: "application/vnd.oras.artifact",
|
||||
Blobs: []ispec.Descriptor{
|
||||
{
|
||||
MediaType: "application/octet-stream",
|
||||
Digest: adigest,
|
||||
Size: int64(len(content)),
|
||||
},
|
||||
},
|
||||
Subject: &ispec.Descriptor{
|
||||
MediaType: "application/vnd.oci.image.manifest.v1+json",
|
||||
Digest: digest,
|
||||
Size: int64(len(resp.Body())),
|
||||
},
|
||||
}
|
||||
|
||||
content, err = json.Marshal(artifactManifest)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
adigest = godigest.FromBytes(content)
|
||||
|
||||
// put OCI reference artifact mediaType artifact
|
||||
_, err = resty.R().SetHeader("Content-Type", artifactspec.MediaTypeArtifactManifest).
|
||||
SetBody(content).Put(srcBaseURL + fmt.Sprintf("/v2/%s/manifests/%s", repoName, adigest.String()))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = os.Chmod(path.Join(destDir, testImage, "index.json"), 0o000)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
resp, err = resty.R().Get(getORASReferrersURL)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(resp, ShouldNotBeEmpty)
|
||||
So(resp.StatusCode(), ShouldEqual, http.StatusInternalServerError)
|
||||
|
||||
err = os.Chmod(path.Join(destDir, testImage, "index.json"), 0o755)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
resp, err = resty.R().Get(getORASReferrersURL)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(resp, ShouldNotBeEmpty)
|
||||
|
||||
var refs ReferenceList
|
||||
|
||||
err = json.Unmarshal(resp.Body(), &refs)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(len(refs.References), ShouldEqual, 1)
|
||||
|
||||
err = os.RemoveAll(path.Join(destDir, repoName))
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = os.WriteFile(path.Join(srcDir, repoName, "blobs", "sha256", adigest.Encoded()), []byte("wrong content"), 0o600)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
_, err = resty.R().SetHeader("Content-Type", artifactspec.MediaTypeArtifactManifest).
|
||||
SetBody(content).Put(srcBaseURL + fmt.Sprintf("/v2/%s/manifests/%s", repoName, adigest.String()))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
resp, err = resty.R().Get(getORASReferrersURL)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(resp, ShouldNotBeEmpty)
|
||||
So(resp.StatusCode(), ShouldEqual, http.StatusNotFound)
|
||||
})
|
||||
}
|
||||
|
||||
func TestOnDemand(t *testing.T) {
|
||||
@@ -994,19 +1146,19 @@ func TestTLS(t *testing.T) {
|
||||
destClientCertDir := t.TempDir()
|
||||
|
||||
destFilePath := path.Join(destClientCertDir, "ca.crt")
|
||||
err = copyFile(CACert, destFilePath)
|
||||
err = test.CopyFile(CACert, destFilePath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
destFilePath = path.Join(destClientCertDir, "client.cert")
|
||||
err = copyFile(ClientCert, destFilePath)
|
||||
err = test.CopyFile(ClientCert, destFilePath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
destFilePath = path.Join(destClientCertDir, "client.key")
|
||||
err = copyFile(ClientKey, destFilePath)
|
||||
err = test.CopyFile(ClientKey, destFilePath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -1664,7 +1816,7 @@ func TestInvalidCerts(t *testing.T) {
|
||||
clientCertDir := t.TempDir()
|
||||
|
||||
destFilePath := path.Join(clientCertDir, "ca.crt")
|
||||
err := copyFile(CACert, destFilePath)
|
||||
err := test.CopyFile(CACert, destFilePath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -1681,13 +1833,13 @@ func TestInvalidCerts(t *testing.T) {
|
||||
}
|
||||
|
||||
destFilePath = path.Join(clientCertDir, "client.cert")
|
||||
err = copyFile(ClientCert, destFilePath)
|
||||
err = test.CopyFile(ClientCert, destFilePath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
destFilePath = path.Join(clientCertDir, "client.key")
|
||||
err = copyFile(ClientKey, destFilePath)
|
||||
err = test.CopyFile(ClientKey, destFilePath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -1739,7 +1891,7 @@ func TestCertsWithWrongPerms(t *testing.T) {
|
||||
clientCertDir := t.TempDir()
|
||||
|
||||
destFilePath := path.Join(clientCertDir, "ca.crt")
|
||||
err := copyFile(CACert, destFilePath)
|
||||
err := test.CopyFile(CACert, destFilePath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -1748,13 +1900,13 @@ func TestCertsWithWrongPerms(t *testing.T) {
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
destFilePath = path.Join(clientCertDir, "client.cert")
|
||||
err = copyFile(ClientCert, destFilePath)
|
||||
err = test.CopyFile(ClientCert, destFilePath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
destFilePath = path.Join(clientCertDir, "client.key")
|
||||
err = copyFile(ClientKey, destFilePath)
|
||||
err = test.CopyFile(ClientKey, destFilePath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -2439,25 +2591,25 @@ func TestPeriodicallySignaturesErr(t *testing.T) {
|
||||
|
||||
Convey("Trigger error on notary signature", func() {
|
||||
// trigger permission error on notary signature on upstream
|
||||
notaryURLPath := path.Join("/oras/artifacts/v1/", repoName, "manifests", imageManifestDigest.String(), "referrers")
|
||||
notaryURLPath := path.Join("/v2/", repoName, "referrers", imageManifestDigest.String())
|
||||
|
||||
// based on image manifest digest get referrers
|
||||
resp, err := resty.R().
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetQueryParam("artifactType", "application/vnd.cncf.notary.v2.signature").
|
||||
SetQueryParam("artifactType", "application/vnd.cncf.notary.signature").
|
||||
Get(srcBaseURL + notaryURLPath)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(resp, ShouldNotBeEmpty)
|
||||
|
||||
var referrers ReferenceList
|
||||
var referrers ispec.Index
|
||||
|
||||
err = json.Unmarshal(resp.Body(), &referrers)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
// read manifest
|
||||
var artifactManifest oraspec.Manifest
|
||||
for _, ref := range referrers.References {
|
||||
var artifactManifest ispec.Artifact
|
||||
for _, ref := range referrers.Manifests {
|
||||
refPath := path.Join(srcDir, repoName, "blobs", string(ref.Digest.Algorithm()), ref.Digest.Encoded())
|
||||
body, err := os.ReadFile(refPath)
|
||||
So(err, ShouldBeNil)
|
||||
@@ -2481,10 +2633,17 @@ func TestPeriodicallySignaturesErr(t *testing.T) {
|
||||
|
||||
// should not be synced nor sync on demand
|
||||
resp, err = resty.R().SetHeader("Content-Type", "application/json").
|
||||
SetQueryParam("artifactType", "application/vnd.cncf.notary.v2.signature").
|
||||
SetQueryParam("artifactType", "application/vnd.cncf.notary.signature").
|
||||
Get(destBaseURL + notaryURLPath)
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 404)
|
||||
So(resp.StatusCode(), ShouldEqual, 200)
|
||||
|
||||
var index ispec.Index
|
||||
|
||||
err = json.Unmarshal(resp.Body(), &index)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(len(index.Manifests), ShouldEqual, 0)
|
||||
})
|
||||
|
||||
Convey("Trigger error on artifact references", func() {
|
||||
@@ -2600,6 +2759,7 @@ func TestSignatures(t *testing.T) {
|
||||
defer func() { _ = os.Chdir(cwd) }()
|
||||
tdir := t.TempDir()
|
||||
_ = os.Chdir(tdir)
|
||||
|
||||
generateKeyPairs(tdir)
|
||||
|
||||
So(func() { signImage(tdir, srcPort, repoName, digest) }, ShouldNotPanic)
|
||||
@@ -2671,13 +2831,9 @@ func TestSignatures(t *testing.T) {
|
||||
|
||||
// notation verify the image
|
||||
image := fmt.Sprintf("localhost:%s/%s:%s", destPort, repoName, "1.0")
|
||||
cmd := exec.Command("notation", "verify", "--cert", "good", "--plain-http", image)
|
||||
out, err := cmd.CombinedOutput()
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
msg := string(out)
|
||||
So(msg, ShouldNotBeEmpty)
|
||||
So(strings.Contains(msg, "verification failure"), ShouldBeFalse)
|
||||
err = test.VerifyWithNotation(image, tdir)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
// cosign verify the image
|
||||
vrfy := verify.VerifyCommand{
|
||||
@@ -2703,23 +2859,23 @@ func TestSignatures(t *testing.T) {
|
||||
err = json.Unmarshal(resp.Body(), &index)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(len(index.Manifests), ShouldEqual, 2)
|
||||
So(len(index.Manifests), ShouldEqual, 3)
|
||||
|
||||
// test negative cases (trigger errors)
|
||||
// test notary signatures errors
|
||||
|
||||
// based on manifest digest get referrers
|
||||
getReferrersURL := srcBaseURL + path.Join("/oras/artifacts/v1/", repoName, "manifests", digest.String(), "referrers")
|
||||
getReferrersURL := srcBaseURL + path.Join("/v2/", repoName, "referrers", digest.String())
|
||||
|
||||
resp, err = resty.R().
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetQueryParam("artifactType", "application/vnd.cncf.notary.v2.signature").
|
||||
SetQueryParam("artifactType", "application/vnd.cncf.notary.signature").
|
||||
Get(getReferrersURL)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(resp, ShouldNotBeEmpty)
|
||||
|
||||
var referrers ReferenceList
|
||||
var referrers ispec.Index
|
||||
|
||||
err = json.Unmarshal(resp.Body(), &referrers)
|
||||
So(err, ShouldBeNil)
|
||||
@@ -2728,8 +2884,8 @@ func TestSignatures(t *testing.T) {
|
||||
err = os.RemoveAll(path.Join(destDir, repoName))
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
var artifactManifest oraspec.Manifest
|
||||
for _, ref := range referrers.References {
|
||||
var artifactManifest ispec.Artifact
|
||||
for _, ref := range referrers.Manifests {
|
||||
refPath := path.Join(srcDir, repoName, "blobs", string(ref.Digest.Algorithm()), ref.Digest.Encoded())
|
||||
body, err := os.ReadFile(refPath)
|
||||
So(err, ShouldBeNil)
|
||||
@@ -2757,7 +2913,7 @@ func TestSignatures(t *testing.T) {
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
// triggers perm denied on notary manifest on downstream
|
||||
for _, ref := range referrers.References {
|
||||
for _, ref := range referrers.Manifests {
|
||||
refPath := path.Join(destDir, repoName, "blobs", string(ref.Digest.Algorithm()), ref.Digest.Encoded())
|
||||
err := os.MkdirAll(refPath, 0o755)
|
||||
So(err, ShouldBeNil)
|
||||
@@ -3498,12 +3654,8 @@ func TestSignaturesOnDemand(t *testing.T) {
|
||||
|
||||
// notation verify the synced image
|
||||
image := fmt.Sprintf("localhost:%s/%s:%s", destPort, repoName, "1.0")
|
||||
cmd := exec.Command("notation", "verify", "--cert", "good", "--plain-http", image)
|
||||
out, err := cmd.CombinedOutput()
|
||||
err = test.VerifyWithNotation(image, tdir)
|
||||
So(err, ShouldBeNil)
|
||||
msg := string(out)
|
||||
So(msg, ShouldNotBeEmpty)
|
||||
So(strings.Contains(msg, "verification failure"), ShouldBeFalse)
|
||||
|
||||
// cosign verify the synced image
|
||||
vrfy := verify.VerifyCommand{
|
||||
@@ -3568,6 +3720,107 @@ func TestSignaturesOnDemand(t *testing.T) {
|
||||
err = os.Chmod(srcSignatureBlobPath, 0o755)
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
|
||||
Convey("Verify sync signatures on demand feature: notation - negative cases", t, func() {
|
||||
sctlr, srcBaseURL, srcDir, _, _ := startUpstreamServer(t, false, false)
|
||||
|
||||
defer func() {
|
||||
sctlr.Shutdown()
|
||||
}()
|
||||
|
||||
// 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]
|
||||
|
||||
cwd, err := os.Getwd()
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
defer func() { _ = os.Chdir(cwd) }()
|
||||
tdir := t.TempDir()
|
||||
_ = os.Chdir(tdir)
|
||||
|
||||
generateKeyPairs(tdir)
|
||||
|
||||
So(func() { signImage(tdir, srcPort, repoName, digest) }, ShouldNotPanic)
|
||||
|
||||
var tlsVerify bool
|
||||
|
||||
syncRegistryConfig := sync.RegistryConfig{
|
||||
URLs: []string{srcBaseURL},
|
||||
TLSVerify: &tlsVerify,
|
||||
CertDir: "",
|
||||
OnDemand: true,
|
||||
}
|
||||
|
||||
defaultVal := true
|
||||
syncConfig := &sync.Config{
|
||||
Enable: &defaultVal,
|
||||
Registries: []sync.RegistryConfig{syncRegistryConfig},
|
||||
}
|
||||
|
||||
destPort := test.GetFreePort()
|
||||
destConfig := config.New()
|
||||
destBaseURL := test.GetBaseURL(destPort)
|
||||
destConfig.HTTP.Port = destPort
|
||||
|
||||
destDir := t.TempDir()
|
||||
|
||||
destConfig.Storage.RootDirectory = destDir
|
||||
destConfig.Storage.Dedupe = false
|
||||
destConfig.Storage.GC = false
|
||||
|
||||
destConfig.Extensions = &extconf.ExtensionConfig{}
|
||||
destConfig.Extensions.Search = nil
|
||||
destConfig.Extensions.Sync = syncConfig
|
||||
destConfig.Log.Output = path.Join(destDir, "sync.log")
|
||||
|
||||
dctlr := api.NewController(destConfig)
|
||||
dcm := test.NewControllerManager(dctlr)
|
||||
|
||||
dcm.StartAndWait(destPort)
|
||||
|
||||
defer dcm.StopServer()
|
||||
|
||||
// trigger getOCIRefs error
|
||||
getReferrersURL := srcBaseURL + path.Join("/v2/", repoName, "referrers", digest.String())
|
||||
|
||||
resp, err := resty.R().
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetQueryParam("artifactType", "application/vnd.cncf.notary.signature").
|
||||
Get(getReferrersURL)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(resp, ShouldNotBeEmpty)
|
||||
|
||||
var referrers ispec.Index
|
||||
|
||||
err = json.Unmarshal(resp.Body(), &referrers)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
for _, ref := range referrers.Manifests {
|
||||
refPath := path.Join(srcDir, repoName, "blobs", string(ref.Digest.Algorithm()), ref.Digest.Encoded())
|
||||
err := os.Remove(refPath)
|
||||
So(err, ShouldBeNil)
|
||||
}
|
||||
|
||||
resp, err = resty.R().Get(destBaseURL + "/v2/" + testSignedImage + "/manifests/1.0")
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 200)
|
||||
|
||||
time.Sleep(3 * time.Second)
|
||||
|
||||
body, err := os.ReadFile(path.Join(destDir, "sync.log"))
|
||||
if err != nil {
|
||||
log.Fatalf("unable to read file: %v", err)
|
||||
}
|
||||
|
||||
So(string(body), ShouldContainSubstring, "couldn't find any oci reference")
|
||||
So(string(body), ShouldContainSubstring, "couldn't find upstream referrer")
|
||||
})
|
||||
}
|
||||
|
||||
func TestOnlySignaturesOnDemand(t *testing.T) {
|
||||
@@ -3641,12 +3894,8 @@ func TestOnlySignaturesOnDemand(t *testing.T) {
|
||||
|
||||
// sync signature on demand when upstream doesn't have the signature
|
||||
image := fmt.Sprintf("localhost:%s/%s:%s", destPort, repoName, "1.0")
|
||||
cmd := exec.Command("notation", "verify", "--cert", "good", "--plain-http", image)
|
||||
out, err := cmd.CombinedOutput()
|
||||
err = test.VerifyWithNotation(image, tdir)
|
||||
So(err, ShouldNotBeNil)
|
||||
msg := string(out)
|
||||
So(msg, ShouldNotBeEmpty)
|
||||
So(strings.Contains(msg, "signature failure"), ShouldBeTrue)
|
||||
|
||||
// cosign verify the synced image
|
||||
vrfy := verify.VerifyCommand{
|
||||
@@ -3664,12 +3913,8 @@ func TestOnlySignaturesOnDemand(t *testing.T) {
|
||||
|
||||
// now it should sync signatures on demand, even if we already have the image
|
||||
image = fmt.Sprintf("localhost:%s/%s:%s", destPort, repoName, "1.0")
|
||||
cmd = exec.Command("notation", "verify", "--cert", "good", "--plain-http", image)
|
||||
out, err = cmd.CombinedOutput()
|
||||
err = test.VerifyWithNotation(image, tdir)
|
||||
So(err, ShouldBeNil)
|
||||
msg = string(out)
|
||||
So(msg, ShouldNotBeEmpty)
|
||||
So(strings.Contains(msg, "verification failure"), ShouldBeFalse)
|
||||
|
||||
// cosign verify the synced image
|
||||
vrfy = verify.VerifyCommand{
|
||||
@@ -4002,14 +4247,9 @@ func TestSyncSignaturesDiff(t *testing.T) {
|
||||
|
||||
// notation verify the image
|
||||
image := fmt.Sprintf("localhost:%s/%s:%s", destPort, repoName, "1.0")
|
||||
cmd := exec.Command("notation", "verify", "--cert", "good", "--plain-http", image)
|
||||
out, err := cmd.CombinedOutput()
|
||||
err = test.VerifyWithNotation(image, tdir)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
msg := string(out)
|
||||
So(msg, ShouldNotBeEmpty)
|
||||
So(strings.Contains(msg, "verification failure"), ShouldBeFalse)
|
||||
|
||||
// cosign verify the image
|
||||
vrfy := verify.VerifyCommand{
|
||||
RegistryOptions: options.RegistryOptions{AllowInsecure: true},
|
||||
@@ -4033,14 +4273,9 @@ func TestSyncSignaturesDiff(t *testing.T) {
|
||||
|
||||
// notation verify the image
|
||||
image = fmt.Sprintf("localhost:%s/%s:%s", destPort, repoName, "1.0")
|
||||
cmd = exec.Command("notation", "verify", "--cert", "good", "--plain-http", image)
|
||||
out, err = cmd.CombinedOutput()
|
||||
err = test.VerifyWithNotation(image, tdir)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
msg = string(out)
|
||||
So(msg, ShouldNotBeEmpty)
|
||||
So(strings.Contains(msg, "verification failure"), ShouldBeFalse)
|
||||
|
||||
// cosign verify the image
|
||||
vrfy = verify.VerifyCommand{
|
||||
RegistryOptions: options.RegistryOptions{AllowInsecure: true},
|
||||
@@ -4721,18 +4956,12 @@ func generateKeyPairs(tdir string) {
|
||||
}
|
||||
}
|
||||
|
||||
// "notation" (notaryv2) doesn't yet support exported apis, so use the binary instead
|
||||
_, err := exec.LookPath("notation")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
test.NotationPathLock.Lock()
|
||||
defer test.NotationPathLock.Unlock()
|
||||
|
||||
os.Setenv("XDG_CONFIG_HOME", tdir)
|
||||
test.LoadNotationPath(tdir)
|
||||
|
||||
// generate a keypair
|
||||
cmd := exec.Command("notation", "cert", "generate-test", "--trust", "good")
|
||||
|
||||
err = cmd.Run()
|
||||
err := test.GenerateNotationCerts(tdir, "good")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -4771,23 +5000,23 @@ func signImage(tdir, port, repoName string, digest godigest.Digest) {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
test.NotationPathLock.Lock()
|
||||
defer test.NotationPathLock.Unlock()
|
||||
|
||||
test.LoadNotationPath(tdir)
|
||||
|
||||
// sign the image
|
||||
image := fmt.Sprintf("localhost:%s/%s:%s", port, repoName, "1.0")
|
||||
cmd := exec.Command("notation", "sign", "--key", "good", "--plain-http", image)
|
||||
|
||||
err = cmd.Run()
|
||||
err = test.SignWithNotation("good", image, tdir)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// verify the image
|
||||
cmd = exec.Command("notation", "verify", "--cert", "good", "--plain-http", image)
|
||||
out, err := cmd.CombinedOutput()
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
msg := string(out)
|
||||
So(msg, ShouldNotBeEmpty)
|
||||
So(strings.Contains(msg, "verification failure"), ShouldBeFalse)
|
||||
err = test.VerifyWithNotation(image, tdir)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func pushRepo(url, repoName string) godigest.Digest {
|
||||
|
||||
Reference in New Issue
Block a user