2015-12-15 02:19:34 +00:00
package storage
import (
2017-08-11 22:31:16 +00:00
"context"
2016-05-14 21:49:08 +00:00
"errors"
2015-12-15 02:19:34 +00:00
"fmt"
2016-05-14 21:49:08 +00:00
"net/url"
2015-12-15 02:19:34 +00:00
2020-08-24 11:18:39 +00:00
"github.com/distribution/distribution/v3"
dcontext "github.com/distribution/distribution/v3/context"
2023-05-09 11:18:47 +00:00
"github.com/distribution/distribution/v3/manifest/schema1" //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
2020-08-24 11:18:39 +00:00
"github.com/distribution/distribution/v3/manifest/schema2"
2016-12-17 00:28:34 +00:00
"github.com/opencontainers/go-digest"
2015-12-15 02:19:34 +00:00
)
2016-05-14 21:49:08 +00:00
var (
2019-02-05 00:01:04 +00:00
errMissingURL = errors . New ( "missing URL on layer" )
errInvalidURL = errors . New ( "invalid URL on layer" )
2016-05-14 21:49:08 +00:00
)
2022-11-02 21:05:45 +00:00
// schema2ManifestHandler is a ManifestHandler that covers schema2 manifests.
2015-12-15 02:19:34 +00:00
type schema2ManifestHandler struct {
2016-07-22 00:16:47 +00:00
repository distribution . Repository
blobStore distribution . BlobStore
ctx context . Context
manifestURLs manifestURLs
2015-12-15 02:19:34 +00:00
}
var _ ManifestHandler = & schema2ManifestHandler { }
func ( ms * schema2ManifestHandler ) Unmarshal ( ctx context . Context , dgst digest . Digest , content [ ] byte ) ( distribution . Manifest , error ) {
2017-08-11 22:31:16 +00:00
dcontext . GetLogger ( ms . ctx ) . Debug ( "(*schema2ManifestHandler).Unmarshal" )
2015-12-15 02:19:34 +00:00
2018-09-05 20:40:42 +00:00
m := & schema2 . DeserializedManifest { }
if err := m . UnmarshalJSON ( content ) ; err != nil {
2015-12-15 02:19:34 +00:00
return nil , err
}
2018-09-05 20:40:42 +00:00
return m , nil
2015-12-15 02:19:34 +00:00
}
func ( ms * schema2ManifestHandler ) Put ( ctx context . Context , manifest distribution . Manifest , skipDependencyVerification bool ) ( digest . Digest , error ) {
2017-08-11 22:31:16 +00:00
dcontext . GetLogger ( ms . ctx ) . Debug ( "(*schema2ManifestHandler).Put" )
2015-12-15 02:19:34 +00:00
m , ok := manifest . ( * schema2 . DeserializedManifest )
if ! ok {
return "" , fmt . Errorf ( "non-schema2 manifest put to schema2ManifestHandler: %T" , manifest )
}
if err := ms . verifyManifest ( ms . ctx , * m , skipDependencyVerification ) ; err != nil {
return "" , err
}
mt , payload , err := m . Payload ( )
if err != nil {
return "" , err
}
revision , err := ms . blobStore . Put ( ctx , mt , payload )
if err != nil {
2017-08-11 22:31:16 +00:00
dcontext . GetLogger ( ctx ) . Errorf ( "error putting payload into blobstore: %v" , err )
2015-12-15 02:19:34 +00:00
return "" , err
}
return revision . Digest , nil
}
// verifyManifest ensures that the manifest content is valid from the
2015-12-17 01:26:13 +00:00
// perspective of the registry. As a policy, the registry only tries to store
// valid content, leaving trust policies of that content up to consumers.
2015-12-15 02:19:34 +00:00
func ( ms * schema2ManifestHandler ) verifyManifest ( ctx context . Context , mnfst schema2 . DeserializedManifest , skipDependencyVerification bool ) error {
var errs distribution . ErrManifestVerification
2018-07-23 22:03:17 +00:00
if mnfst . Manifest . SchemaVersion != 2 {
return fmt . Errorf ( "unrecognized manifest schema version %d" , mnfst . Manifest . SchemaVersion )
}
2016-10-13 00:20:27 +00:00
if skipDependencyVerification {
return nil
}
2015-12-15 02:19:34 +00:00
2016-10-13 00:20:27 +00:00
manifestService , err := ms . repository . Manifests ( ctx )
if err != nil {
return err
}
2015-12-15 02:19:34 +00:00
2016-10-13 00:20:27 +00:00
blobsService := ms . repository . Blobs ( ctx )
for _ , descriptor := range mnfst . References ( ) {
2021-04-15 06:44:04 +00:00
err := descriptor . Digest . Validate ( )
if err != nil {
errs = append ( errs , err , distribution . ErrManifestBlobUnknown { Digest : descriptor . Digest } )
continue
}
2016-10-13 00:20:27 +00:00
switch descriptor . MediaType {
case schema2 . MediaTypeForeignLayer :
// Clients download this layer from an external URL, so do not check for
2019-10-09 12:02:21 +00:00
// its presence.
2016-10-13 00:20:27 +00:00
if len ( descriptor . URLs ) == 0 {
err = errMissingURL
2016-05-14 21:49:08 +00:00
}
2016-10-13 00:20:27 +00:00
allow := ms . manifestURLs . allow
deny := ms . manifestURLs . deny
for _ , u := range descriptor . URLs {
var pu * url . URL
pu , err = url . Parse ( u )
if err != nil || ( pu . Scheme != "http" && pu . Scheme != "https" ) || pu . Fragment != "" || ( allow != nil && ! allow . MatchString ( u ) ) || ( deny != nil && deny . MatchString ( u ) ) {
err = errInvalidURL
break
2015-12-15 02:19:34 +00:00
}
2016-10-13 00:20:27 +00:00
}
2023-05-09 11:18:47 +00:00
case schema2 . MediaTypeManifest , schema1 . MediaTypeManifest : //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
2016-10-13 00:20:27 +00:00
var exists bool
exists , err = manifestService . Exists ( ctx , descriptor . Digest )
if err != nil || ! exists {
err = distribution . ErrBlobUnknown // just coerce to unknown.
}
2021-04-15 06:44:04 +00:00
if err != nil {
dcontext . GetLogger ( ms . ctx ) . WithError ( err ) . Debugf ( "failed to ensure exists of %v in manifest service" , descriptor . Digest )
}
2016-10-13 00:20:27 +00:00
fallthrough // double check the blob store.
default :
2021-04-15 06:44:04 +00:00
// check its presence
_ , err = blobsService . Stat ( ctx , descriptor . Digest )
2016-10-13 00:20:27 +00:00
}
2015-12-15 02:19:34 +00:00
2016-10-13 00:20:27 +00:00
if err != nil {
if err != distribution . ErrBlobUnknown {
errs = append ( errs , err )
2015-12-15 02:19:34 +00:00
}
2016-10-13 00:20:27 +00:00
// On error here, we always append unknown blob errors.
errs = append ( errs , distribution . ErrManifestBlobUnknown { Digest : descriptor . Digest } )
2015-12-15 02:19:34 +00:00
}
}
2016-10-13 00:20:27 +00:00
2015-12-15 02:19:34 +00:00
if len ( errs ) != 0 {
return errs
}
return nil
}