github.com/mika/distribution@v2.2.2-0.20160108133430-a75790e3d8e0+incompatible/registry/storage/signedmanifesthandler.go (about) 1 package storage 2 3 import ( 4 "encoding/json" 5 "fmt" 6 7 "github.com/docker/distribution" 8 "github.com/docker/distribution/context" 9 "github.com/docker/distribution/digest" 10 "github.com/docker/distribution/manifest/schema1" 11 "github.com/docker/distribution/reference" 12 "github.com/docker/libtrust" 13 ) 14 15 // signedManifestHandler is a ManifestHandler that covers schema1 manifests. It 16 // can unmarshal and put schema1 manifests that have been signed by libtrust. 17 type signedManifestHandler struct { 18 repository *repository 19 blobStore *linkedBlobStore 20 ctx context.Context 21 signatures *signatureStore 22 } 23 24 var _ ManifestHandler = &signedManifestHandler{} 25 26 func (ms *signedManifestHandler) Unmarshal(ctx context.Context, dgst digest.Digest, content []byte) (distribution.Manifest, error) { 27 context.GetLogger(ms.ctx).Debug("(*signedManifestHandler).Unmarshal") 28 // Fetch the signatures for the manifest 29 signatures, err := ms.signatures.Get(dgst) 30 if err != nil { 31 return nil, err 32 } 33 34 jsig, err := libtrust.NewJSONSignature(content, signatures...) 35 if err != nil { 36 return nil, err 37 } 38 39 // Extract the pretty JWS 40 raw, err := jsig.PrettySignature("signatures") 41 if err != nil { 42 return nil, err 43 } 44 45 var sm schema1.SignedManifest 46 if err := json.Unmarshal(raw, &sm); err != nil { 47 return nil, err 48 } 49 return &sm, nil 50 } 51 52 func (ms *signedManifestHandler) Put(ctx context.Context, manifest distribution.Manifest, skipDependencyVerification bool) (digest.Digest, error) { 53 context.GetLogger(ms.ctx).Debug("(*signedManifestHandler).Put") 54 55 sm, ok := manifest.(*schema1.SignedManifest) 56 if !ok { 57 return "", fmt.Errorf("non-schema1 manifest put to signedManifestHandler: %T", manifest) 58 } 59 60 if err := ms.verifyManifest(ms.ctx, *sm, skipDependencyVerification); err != nil { 61 return "", err 62 } 63 64 mt := schema1.MediaTypeManifest 65 payload := sm.Canonical 66 67 revision, err := ms.blobStore.Put(ctx, mt, payload) 68 if err != nil { 69 context.GetLogger(ctx).Errorf("error putting payload into blobstore: %v", err) 70 return "", err 71 } 72 73 // Link the revision into the repository. 74 if err := ms.blobStore.linkBlob(ctx, revision); err != nil { 75 return "", err 76 } 77 78 // Grab each json signature and store them. 79 signatures, err := sm.Signatures() 80 if err != nil { 81 return "", err 82 } 83 84 if err := ms.signatures.Put(revision.Digest, signatures...); err != nil { 85 return "", err 86 } 87 88 return revision.Digest, nil 89 } 90 91 // verifyManifest ensures that the manifest content is valid from the 92 // perspective of the registry. It ensures that the signature is valid for the 93 // enclosed payload. As a policy, the registry only tries to store valid 94 // content, leaving trust policies of that content up to consumers. 95 func (ms *signedManifestHandler) verifyManifest(ctx context.Context, mnfst schema1.SignedManifest, skipDependencyVerification bool) error { 96 var errs distribution.ErrManifestVerification 97 98 if len(mnfst.Name) > reference.NameTotalLengthMax { 99 errs = append(errs, 100 distribution.ErrManifestNameInvalid{ 101 Name: mnfst.Name, 102 Reason: fmt.Errorf("manifest name must not be more than %v characters", reference.NameTotalLengthMax), 103 }) 104 } 105 106 if !reference.NameRegexp.MatchString(mnfst.Name) { 107 errs = append(errs, 108 distribution.ErrManifestNameInvalid{ 109 Name: mnfst.Name, 110 Reason: fmt.Errorf("invalid manifest name format"), 111 }) 112 } 113 114 if len(mnfst.History) != len(mnfst.FSLayers) { 115 errs = append(errs, fmt.Errorf("mismatched history and fslayer cardinality %d != %d", 116 len(mnfst.History), len(mnfst.FSLayers))) 117 } 118 119 if _, err := schema1.Verify(&mnfst); err != nil { 120 switch err { 121 case libtrust.ErrMissingSignatureKey, libtrust.ErrInvalidJSONContent, libtrust.ErrMissingSignatureKey: 122 errs = append(errs, distribution.ErrManifestUnverified{}) 123 default: 124 if err.Error() == "invalid signature" { // TODO(stevvooe): This should be exported by libtrust 125 errs = append(errs, distribution.ErrManifestUnverified{}) 126 } else { 127 errs = append(errs, err) 128 } 129 } 130 } 131 132 if !skipDependencyVerification { 133 for _, fsLayer := range mnfst.References() { 134 _, err := ms.repository.Blobs(ctx).Stat(ctx, fsLayer.Digest) 135 if err != nil { 136 if err != distribution.ErrBlobUnknown { 137 errs = append(errs, err) 138 } 139 140 // On error here, we always append unknown blob errors. 141 errs = append(errs, distribution.ErrManifestBlobUnknown{Digest: fsLayer.Digest}) 142 } 143 } 144 } 145 if len(errs) != 0 { 146 return errs 147 } 148 149 return nil 150 }