mirror of
https://github.com/project-zot/zot.git
synced 2026-06-15 20:07:55 +08:00
storage: improve/fix oci image validation
Signed-off-by: Ramkumar Chinchani <rchincha@cisco.com>
This commit is contained in:
committed by
Ramkumar Chinchani
parent
8d6b36a61b
commit
87084f286b
+60
-18
@@ -466,6 +466,60 @@ func (is *ImageStoreFS) GetImageManifest(repo string, reference string) ([]byte,
|
||||
return buf, digest.String(), mediaType, nil
|
||||
}
|
||||
|
||||
func (is *ImageStoreFS) validateOCIManifest(repo string, reference string, manifest *ispec.Manifest) (string, error) {
|
||||
if manifest.SchemaVersion != SchemaVersion {
|
||||
is.log.Error().Int("SchemaVersion", manifest.SchemaVersion).Msg("invalid manifest")
|
||||
|
||||
return "", zerr.ErrBadManifest
|
||||
}
|
||||
|
||||
// validate image config
|
||||
config := manifest.Config
|
||||
if config.MediaType != ispec.MediaTypeImageConfig {
|
||||
return "", zerr.ErrBadManifest
|
||||
}
|
||||
|
||||
digest := config.Digest
|
||||
|
||||
blobPath := is.BlobPath(repo, digest)
|
||||
if _, err := os.Stat(blobPath); err != nil {
|
||||
is.log.Error().Err(err).Str("blobPath", blobPath).Msg("unable to find blob")
|
||||
|
||||
return digest.String(), zerr.ErrBlobNotFound
|
||||
}
|
||||
|
||||
blobFile, err := os.Open(blobPath)
|
||||
if err != nil {
|
||||
is.log.Error().Err(err).Str("blobPath", blobPath).Msg("unable to find blob")
|
||||
|
||||
return digest.String(), zerr.ErrBlobNotFound
|
||||
}
|
||||
|
||||
defer blobFile.Close()
|
||||
|
||||
dec := json.NewDecoder(blobFile)
|
||||
|
||||
var cspec ispec.Image
|
||||
if err := dec.Decode(&cspec); err != nil {
|
||||
return "", zerr.ErrBadManifest
|
||||
}
|
||||
|
||||
// validate the layers
|
||||
for _, l := range manifest.Layers {
|
||||
digest = l.Digest
|
||||
blobPath = is.BlobPath(repo, digest)
|
||||
is.log.Info().Str("blobPath", blobPath).Str("reference", reference).Msg("manifest layers")
|
||||
|
||||
if _, err := os.Stat(blobPath); err != nil {
|
||||
is.log.Error().Err(err).Str("blobPath", blobPath).Msg("unable to find blob")
|
||||
|
||||
return digest.String(), zerr.ErrBlobNotFound
|
||||
}
|
||||
}
|
||||
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// PutImageManifest adds an image manifest to the repository.
|
||||
func (is *ImageStoreFS) PutImageManifest(repo string, reference string, mediaType string,
|
||||
body []byte) (string, error) {
|
||||
@@ -475,6 +529,7 @@ func (is *ImageStoreFS) PutImageManifest(repo string, reference string, mediaTyp
|
||||
return "", err
|
||||
}
|
||||
|
||||
// validate the manifest
|
||||
if !IsSupportedMediaType(mediaType) {
|
||||
is.log.Debug().Interface("actual", mediaType).
|
||||
Interface("expected", ispec.MediaTypeImageManifest).Msg("bad manifest media type")
|
||||
@@ -489,29 +544,16 @@ func (is *ImageStoreFS) PutImageManifest(repo string, reference string, mediaTyp
|
||||
}
|
||||
|
||||
if mediaType == ispec.MediaTypeImageManifest {
|
||||
var m ispec.Manifest
|
||||
if err := json.Unmarshal(body, &m); err != nil {
|
||||
var manifest ispec.Manifest
|
||||
if err := json.Unmarshal(body, &manifest); err != nil {
|
||||
is.log.Error().Err(err).Msg("unable to unmarshal JSON")
|
||||
|
||||
return "", zerr.ErrBadManifest
|
||||
}
|
||||
|
||||
if m.SchemaVersion != SchemaVersion {
|
||||
is.log.Error().Int("SchemaVersion", m.SchemaVersion).Msg("invalid manifest")
|
||||
|
||||
return "", zerr.ErrBadManifest
|
||||
}
|
||||
|
||||
for _, l := range m.Layers {
|
||||
digest := l.Digest
|
||||
blobPath := is.BlobPath(repo, digest)
|
||||
is.log.Info().Str("blobPath", blobPath).Str("reference", reference).Msg("manifest layers")
|
||||
|
||||
if _, err := os.Stat(blobPath); err != nil {
|
||||
is.log.Error().Err(err).Str("blobPath", blobPath).Msg("unable to find blob")
|
||||
|
||||
return digest.String(), zerr.ErrBlobNotFound
|
||||
}
|
||||
digest, err := is.validateOCIManifest(repo, reference, &manifest)
|
||||
if err != nil {
|
||||
return digest, err
|
||||
}
|
||||
} else if mediaType == artifactspec.MediaTypeArtifactManifest {
|
||||
var m notation.Descriptor
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"zotregistry.io/zot/pkg/extensions/monitoring"
|
||||
"zotregistry.io/zot/pkg/log"
|
||||
"zotregistry.io/zot/pkg/storage"
|
||||
"zotregistry.io/zot/pkg/test"
|
||||
)
|
||||
|
||||
func TestStorageFSAPIs(t *testing.T) {
|
||||
@@ -57,10 +58,20 @@ func TestStorageFSAPIs(t *testing.T) {
|
||||
|
||||
annotationsMap := make(map[string]string)
|
||||
annotationsMap[ispec.AnnotationRefName] = "1.0"
|
||||
|
||||
cblob, cdigest := test.GetRandomImageConfig()
|
||||
_, clen, err := imgStore.FullBlobUpload("test", bytes.NewReader(cblob), cdigest.String())
|
||||
So(err, ShouldBeNil)
|
||||
So(clen, ShouldEqual, len(cblob))
|
||||
hasBlob, _, err := imgStore.CheckBlob("test", cdigest.String())
|
||||
So(err, ShouldBeNil)
|
||||
So(hasBlob, ShouldEqual, true)
|
||||
|
||||
manifest := ispec.Manifest{
|
||||
Config: ispec.Descriptor{
|
||||
Digest: digest,
|
||||
Size: int64(buflen),
|
||||
MediaType: "application/vnd.oci.image.config.v1+json",
|
||||
Digest: cdigest,
|
||||
Size: int64(len(cblob)),
|
||||
},
|
||||
Layers: []ispec.Descriptor{
|
||||
{
|
||||
@@ -188,12 +199,19 @@ func TestDedupeLinks(t *testing.T) {
|
||||
_, _, err = imgStore.GetBlob("dedupe1", digest.String(), "application/vnd.oci.image.layer.v1.tar+gzip")
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
manifest := ispec.Manifest{}
|
||||
manifest.SchemaVersion = 2
|
||||
manifest = ispec.Manifest{
|
||||
cblob, cdigest := test.GetRandomImageConfig()
|
||||
_, clen, err := imgStore.FullBlobUpload("dedupe1", bytes.NewReader(cblob), cdigest.String())
|
||||
So(err, ShouldBeNil)
|
||||
So(clen, ShouldEqual, len(cblob))
|
||||
hasBlob, _, err := imgStore.CheckBlob("dedupe1", cdigest.String())
|
||||
So(err, ShouldBeNil)
|
||||
So(hasBlob, ShouldEqual, true)
|
||||
|
||||
manifest := ispec.Manifest{
|
||||
Config: ispec.Descriptor{
|
||||
Digest: digest,
|
||||
Size: int64(buflen),
|
||||
MediaType: "application/vnd.oci.image.config.v1+json",
|
||||
Digest: cdigest,
|
||||
Size: int64(len(cblob)),
|
||||
},
|
||||
Layers: []ispec.Descriptor{
|
||||
{
|
||||
@@ -237,12 +255,19 @@ func TestDedupeLinks(t *testing.T) {
|
||||
_, _, err = imgStore.GetBlob("dedupe2", digest.String(), "application/vnd.oci.image.layer.v1.tar+gzip")
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
manifest = ispec.Manifest{}
|
||||
manifest.SchemaVersion = 2
|
||||
cblob, cdigest = test.GetRandomImageConfig()
|
||||
_, clen, err = imgStore.FullBlobUpload("dedupe2", bytes.NewReader(cblob), cdigest.String())
|
||||
So(err, ShouldBeNil)
|
||||
So(clen, ShouldEqual, len(cblob))
|
||||
hasBlob, _, err = imgStore.CheckBlob("dedupe2", cdigest.String())
|
||||
So(err, ShouldBeNil)
|
||||
So(hasBlob, ShouldEqual, true)
|
||||
|
||||
manifest = ispec.Manifest{
|
||||
Config: ispec.Descriptor{
|
||||
Digest: digest,
|
||||
Size: int64(buflen),
|
||||
MediaType: "application/vnd.oci.image.config.v1+json",
|
||||
Digest: cdigest,
|
||||
Size: int64(len(cblob)),
|
||||
},
|
||||
Layers: []ispec.Descriptor{
|
||||
{
|
||||
|
||||
+48
-13
@@ -28,6 +28,7 @@ import (
|
||||
"zotregistry.io/zot/pkg/log"
|
||||
"zotregistry.io/zot/pkg/storage"
|
||||
"zotregistry.io/zot/pkg/storage/s3"
|
||||
"zotregistry.io/zot/pkg/test"
|
||||
)
|
||||
|
||||
func cleanupStorage(store driver.StorageDriver, name string) {
|
||||
@@ -267,12 +268,21 @@ func TestStorageAPIs(t *testing.T) {
|
||||
})
|
||||
|
||||
Convey("Good image manifest", func() {
|
||||
cblob, cdigest := test.GetRandomImageConfig()
|
||||
_, clen, err := imgStore.FullBlobUpload("test", bytes.NewReader(cblob), cdigest.String())
|
||||
So(err, ShouldBeNil)
|
||||
So(clen, ShouldEqual, len(cblob))
|
||||
hasBlob, _, err := imgStore.CheckBlob("test", cdigest.String())
|
||||
So(err, ShouldBeNil)
|
||||
So(hasBlob, ShouldEqual, true)
|
||||
|
||||
annotationsMap := make(map[string]string)
|
||||
annotationsMap[ispec.AnnotationRefName] = "1.0"
|
||||
manifest := ispec.Manifest{
|
||||
Config: ispec.Descriptor{
|
||||
Digest: digest,
|
||||
Size: int64(buflen),
|
||||
MediaType: "application/vnd.oci.image.config.v1+json",
|
||||
Digest: cdigest,
|
||||
Size: int64(len(cblob)),
|
||||
},
|
||||
Layers: []ispec.Descriptor{
|
||||
{
|
||||
@@ -305,7 +315,7 @@ func TestStorageAPIs(t *testing.T) {
|
||||
_, err = imgStore.PutImageManifest("test", "2.0", ispec.MediaTypeImageManifest, manifestBuf)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
_, err := imgStore.PutImageManifest("test", "3.0", ispec.MediaTypeImageManifest, manifestBuf)
|
||||
_, err = imgStore.PutImageManifest("test", "3.0", ispec.MediaTypeImageManifest, manifestBuf)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
_, err = imgStore.GetImageTags("inexistent")
|
||||
@@ -330,7 +340,7 @@ func TestStorageAPIs(t *testing.T) {
|
||||
So(len(tags), ShouldEqual, 2)
|
||||
|
||||
// We deleted only one tag, make sure blob should not be removed.
|
||||
hasBlob, _, err := imgStore.CheckBlob("test", digest.String())
|
||||
hasBlob, _, err = imgStore.CheckBlob("test", digest.String())
|
||||
So(err, ShouldBeNil)
|
||||
So(hasBlob, ShouldEqual, true)
|
||||
|
||||
@@ -446,10 +456,19 @@ func TestStorageAPIs(t *testing.T) {
|
||||
})
|
||||
|
||||
Convey("Good image manifest", func() {
|
||||
cblob, cdigest := test.GetRandomImageConfig()
|
||||
_, clen, err := imgStore.FullBlobUpload("test", bytes.NewReader(cblob), cdigest.String())
|
||||
So(err, ShouldBeNil)
|
||||
So(clen, ShouldEqual, len(cblob))
|
||||
hasBlob, _, err := imgStore.CheckBlob("test", cdigest.String())
|
||||
So(err, ShouldBeNil)
|
||||
So(hasBlob, ShouldEqual, true)
|
||||
|
||||
manifest := ispec.Manifest{
|
||||
Config: ispec.Descriptor{
|
||||
Digest: digest,
|
||||
Size: int64(buflen),
|
||||
MediaType: "application/vnd.oci.image.config.v1+json",
|
||||
Digest: cdigest,
|
||||
Size: int64(len(cblob)),
|
||||
},
|
||||
Layers: []ispec.Descriptor{
|
||||
{
|
||||
@@ -531,12 +550,19 @@ func TestStorageAPIs(t *testing.T) {
|
||||
So(err, ShouldBeNil)
|
||||
So(blob, ShouldEqual, buflen)
|
||||
|
||||
manifest := ispec.Manifest{}
|
||||
manifest.SchemaVersion = 2
|
||||
manifest = ispec.Manifest{
|
||||
cblob, cdigest := test.GetRandomImageConfig()
|
||||
_, clen, err := imgStore.FullBlobUpload("replace", bytes.NewReader(cblob), cdigest.String())
|
||||
So(err, ShouldBeNil)
|
||||
So(clen, ShouldEqual, len(cblob))
|
||||
hasBlob, _, err := imgStore.CheckBlob("replace", cdigest.String())
|
||||
So(err, ShouldBeNil)
|
||||
So(hasBlob, ShouldEqual, true)
|
||||
|
||||
manifest := ispec.Manifest{
|
||||
Config: ispec.Descriptor{
|
||||
Digest: digest,
|
||||
Size: int64(buflen),
|
||||
MediaType: "application/vnd.oci.image.config.v1+json",
|
||||
Digest: cdigest,
|
||||
Size: int64(len(cblob)),
|
||||
},
|
||||
Layers: []ispec.Descriptor{
|
||||
{
|
||||
@@ -574,10 +600,19 @@ func TestStorageAPIs(t *testing.T) {
|
||||
So(err, ShouldBeNil)
|
||||
So(blob, ShouldEqual, buflen)
|
||||
|
||||
cblob, cdigest = test.GetRandomImageConfig()
|
||||
_, clen, err = imgStore.FullBlobUpload("replace", bytes.NewReader(cblob), cdigest.String())
|
||||
So(err, ShouldBeNil)
|
||||
So(clen, ShouldEqual, len(cblob))
|
||||
hasBlob, _, err = imgStore.CheckBlob("replace", cdigest.String())
|
||||
So(err, ShouldBeNil)
|
||||
So(hasBlob, ShouldEqual, true)
|
||||
|
||||
manifest = ispec.Manifest{
|
||||
Config: ispec.Descriptor{
|
||||
Digest: digest,
|
||||
Size: int64(buflen),
|
||||
MediaType: "application/vnd.oci.image.config.v1+json",
|
||||
Digest: cdigest,
|
||||
Size: int64(len(cblob)),
|
||||
},
|
||||
Layers: []ispec.Descriptor{
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user