registry/storage/validation/manifestlist.go (60 lines of code) (raw):

package validation import ( "context" "fmt" "github.com/docker/distribution" "github.com/docker/distribution/manifest/manifestlist" mlcompat "github.com/docker/distribution/manifest/manifestlist/compat" ) // ManifestListValidator ensures that a manifestlist is valid and optionally // verifies all manifest references. type ManifestListValidator struct { baseValidator } // NewManifestListValidator returns a new ManifestListValidator. func NewManifestListValidator(exister ManifestExister, bs distribution.BlobStatter, refLimit, payloadLimit int) *ManifestListValidator { return &ManifestListValidator{ baseValidator: baseValidator{ manifestExister: exister, blobStatter: bs, refLimit: refLimit, payloadLimit: payloadLimit, }, } } // Validate ensures that the manifest content is valid from the // perspective of the registry. As a policy, the registry only tries to store // valid content, leaving trust policies of that content up to consumers. func (v *ManifestListValidator) Validate(ctx context.Context, mnfst *manifestlist.DeserializedManifestList) error { var errs distribution.ErrManifestVerification if mnfst.SchemaVersion != 2 { return fmt.Errorf("unrecognized manifest list schema version %d", mnfst.SchemaVersion) } if err := v.exceedsPayloadSizeLimit(mnfst); err != nil { errs = append(errs, err) return errs } if err := v.exceedsRefLimit(mnfst); err != nil { errs = append(errs, err) return errs } // Docker buildkit uses OCI Image Indexes to store lists of layer blobs. // Ideally, we would not permit this behavior, but due to // https://gitlab.com/gitlab-org/container-registry/-/commit/06a098c632aee74619a06f88c23a06140f442a6f, // not being strictly backwards looking, historically it was possible to // retrieve a blob digest using manifest services during the validation step of // manifest puts, preventing the validation logic from rejecting these // manifests. Since buildkit is a fairly popular official docker tool, we // should allow only these manifest lists to contain layer blobs, // and reject all others. // // https://github.com/distribution/distribution/pull/864 if mlcompat.LikelyBuildxCache(mnfst) { for _, blobDescriptor := range mnfst.References() { _, err := v.blobStatter.Stat(ctx, blobDescriptor.Digest) if err != nil { if err != distribution.ErrBlobUnknown { errs = append(errs, err) } // On error here, we always append unknown blob errors. errs = append(errs, distribution.ErrManifestBlobUnknown{Digest: blobDescriptor.Digest}) } } } else { for _, manifestDescriptor := range mnfst.References() { exists, err := v.manifestExister.Exists(ctx, manifestDescriptor.Digest) if err != nil && err != distribution.ErrBlobUnknown { errs = append(errs, err) } if err != nil || !exists { // On error here, we always append unknown blob errors. errs = append(errs, distribution.ErrManifestBlobUnknown{Digest: manifestDescriptor.Digest}) } } } if len(errs) != 0 { return errs } return nil }