1 package storage
2
3 import (
4 "context"
5 "encoding/json"
6 "fmt"
7
8 "github.com/distribution/reference"
9 "github.com/docker/distribution"
10 dcontext "github.com/docker/distribution/context"
11 "github.com/docker/distribution/manifest/schema1"
12 "github.com/docker/libtrust"
13 "github.com/opencontainers/go-digest"
14 )
15
16
17
18 type signedManifestHandler struct {
19 repository distribution.Repository
20 schema1SigningKey libtrust.PrivateKey
21 blobStore distribution.BlobStore
22 ctx context.Context
23 }
24
25 var _ ManifestHandler = &signedManifestHandler{}
26
27 func (ms *signedManifestHandler) Unmarshal(ctx context.Context, dgst digest.Digest, content []byte) (distribution.Manifest, error) {
28 dcontext.GetLogger(ms.ctx).Debug("(*signedManifestHandler).Unmarshal")
29
30 var (
31 signatures [][]byte
32 err error
33 )
34
35 jsig, err := libtrust.NewJSONSignature(content, signatures...)
36 if err != nil {
37 return nil, err
38 }
39
40 if ms.schema1SigningKey != nil {
41 if err := jsig.Sign(ms.schema1SigningKey); err != nil {
42 return nil, err
43 }
44 }
45
46
47 raw, err := jsig.PrettySignature("signatures")
48 if err != nil {
49 return nil, err
50 }
51
52 var sm schema1.SignedManifest
53 if err := json.Unmarshal(raw, &sm); err != nil {
54 return nil, err
55 }
56 return &sm, nil
57 }
58
59 func (ms *signedManifestHandler) Put(ctx context.Context, manifest distribution.Manifest, skipDependencyVerification bool) (digest.Digest, error) {
60 dcontext.GetLogger(ms.ctx).Debug("(*signedManifestHandler).Put")
61
62 sm, ok := manifest.(*schema1.SignedManifest)
63 if !ok {
64 return "", fmt.Errorf("non-schema1 manifest put to signedManifestHandler: %T", manifest)
65 }
66
67 if err := ms.verifyManifest(ms.ctx, *sm, skipDependencyVerification); err != nil {
68 return "", err
69 }
70
71 mt := schema1.MediaTypeManifest
72 payload := sm.Canonical
73
74 revision, err := ms.blobStore.Put(ctx, mt, payload)
75 if err != nil {
76 dcontext.GetLogger(ctx).Errorf("error putting payload into blobstore: %v", err)
77 return "", err
78 }
79
80 return revision.Digest, nil
81 }
82
83
84
85
86
87 func (ms *signedManifestHandler) verifyManifest(ctx context.Context, mnfst schema1.SignedManifest, skipDependencyVerification bool) error {
88 var errs distribution.ErrManifestVerification
89
90 if len(mnfst.Name) > reference.NameTotalLengthMax {
91 errs = append(errs,
92 distribution.ErrManifestNameInvalid{
93 Name: mnfst.Name,
94 Reason: fmt.Errorf("manifest name must not be more than %v characters", reference.NameTotalLengthMax),
95 })
96 }
97
98 if !reference.NameRegexp.MatchString(mnfst.Name) {
99 errs = append(errs,
100 distribution.ErrManifestNameInvalid{
101 Name: mnfst.Name,
102 Reason: fmt.Errorf("invalid manifest name format"),
103 })
104 }
105
106 if len(mnfst.History) != len(mnfst.FSLayers) {
107 errs = append(errs, fmt.Errorf("mismatched history and fslayer cardinality %d != %d",
108 len(mnfst.History), len(mnfst.FSLayers)))
109 }
110
111 if _, err := schema1.Verify(&mnfst); err != nil {
112 switch err {
113 case libtrust.ErrMissingSignatureKey, libtrust.ErrInvalidJSONContent, libtrust.ErrMissingSignatureKey:
114 errs = append(errs, distribution.ErrManifestUnverified{})
115 default:
116 if err.Error() == "invalid signature" {
117 errs = append(errs, distribution.ErrManifestUnverified{})
118 } else {
119 errs = append(errs, err)
120 }
121 }
122 }
123
124 if !skipDependencyVerification {
125 for _, fsLayer := range mnfst.References() {
126 _, err := ms.repository.Blobs(ctx).Stat(ctx, fsLayer.Digest)
127 if err != nil {
128 if err != distribution.ErrBlobUnknown {
129 errs = append(errs, err)
130 }
131
132
133 errs = append(errs, distribution.ErrManifestBlobUnknown{Digest: fsLayer.Digest})
134 }
135 }
136 }
137 if len(errs) != 0 {
138 return errs
139 }
140
141 return nil
142 }
143
View as plain text