Merge pull request #1420 from dmcgowan/configurable-trust-key
Add option to disable signaturesmaster
						commit
						2177a6a1bf
					
				| 
						 | 
					@ -155,12 +155,19 @@ func NewApp(ctx context.Context, config *configuration.Configuration) *App {
 | 
				
			||||||
	app.configureRedis(config)
 | 
						app.configureRedis(config)
 | 
				
			||||||
	app.configureLogHook(config)
 | 
						app.configureLogHook(config)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if config.Compatibility.Schema1.TrustKey != "" {
 | 
				
			||||||
 | 
							app.trustKey, err = libtrust.LoadKeyFile(config.Compatibility.Schema1.TrustKey)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								panic(fmt.Sprintf(`could not load schema1 "signingkey" parameter: %v`, err))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
		// Generate an ephemeral key to be used for signing converted manifests
 | 
							// Generate an ephemeral key to be used for signing converted manifests
 | 
				
			||||||
		// for clients that don't support schema2.
 | 
							// for clients that don't support schema2.
 | 
				
			||||||
		app.trustKey, err = libtrust.GenerateECP256PrivateKey()
 | 
							app.trustKey, err = libtrust.GenerateECP256PrivateKey()
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			panic(err)
 | 
								panic(err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if config.HTTP.Host != "" {
 | 
						if config.HTTP.Host != "" {
 | 
				
			||||||
		u, err := url.Parse(config.HTTP.Host)
 | 
							u, err := url.Parse(config.HTTP.Host)
 | 
				
			||||||
| 
						 | 
					@ -176,6 +183,11 @@ func NewApp(ctx context.Context, config *configuration.Configuration) *App {
 | 
				
			||||||
		options = append(options, storage.DisableDigestResumption)
 | 
							options = append(options, storage.DisableDigestResumption)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if config.Compatibility.Schema1.DisableSignatureStore {
 | 
				
			||||||
 | 
							options = append(options, storage.DisableSchema1Signatures)
 | 
				
			||||||
 | 
							options = append(options, storage.Schema1SigningKey(app.trustKey))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// configure deletion
 | 
						// configure deletion
 | 
				
			||||||
	if d, ok := config.Storage["delete"]; ok {
 | 
						if d, ok := config.Storage["delete"]; ok {
 | 
				
			||||||
		e, ok := d["enabled"]
 | 
							e, ok := d["enabled"]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -28,11 +28,10 @@ type manifestStoreTestEnv struct {
 | 
				
			||||||
	tag        string
 | 
						tag        string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func newManifestStoreTestEnv(t *testing.T, name reference.Named, tag string) *manifestStoreTestEnv {
 | 
					func newManifestStoreTestEnv(t *testing.T, name reference.Named, tag string, options ...RegistryOption) *manifestStoreTestEnv {
 | 
				
			||||||
	ctx := context.Background()
 | 
						ctx := context.Background()
 | 
				
			||||||
	driver := inmemory.New()
 | 
						driver := inmemory.New()
 | 
				
			||||||
	registry, err := NewRegistry(ctx, driver, BlobDescriptorCacheProvider(
 | 
						registry, err := NewRegistry(ctx, driver, options...)
 | 
				
			||||||
		memory.NewInMemoryBlobDescriptorCacheProvider()), EnableDelete, EnableRedirect)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatalf("error creating registry: %v", err)
 | 
							t.Fatalf("error creating registry: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -53,13 +52,26 @@ func newManifestStoreTestEnv(t *testing.T, name reference.Named, tag string) *ma
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestManifestStorage(t *testing.T) {
 | 
					func TestManifestStorage(t *testing.T) {
 | 
				
			||||||
 | 
						testManifestStorage(t, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), EnableDelete, EnableRedirect)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestManifestStorageDisabledSignatures(t *testing.T) {
 | 
				
			||||||
 | 
						k, err := libtrust.GenerateECP256PrivateKey()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						testManifestStorage(t, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), EnableDelete, EnableRedirect, DisableSchema1Signatures, Schema1SigningKey(k))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func testManifestStorage(t *testing.T, options ...RegistryOption) {
 | 
				
			||||||
	repoName, _ := reference.ParseNamed("foo/bar")
 | 
						repoName, _ := reference.ParseNamed("foo/bar")
 | 
				
			||||||
	env := newManifestStoreTestEnv(t, repoName, "thetag")
 | 
						env := newManifestStoreTestEnv(t, repoName, "thetag", options...)
 | 
				
			||||||
	ctx := context.Background()
 | 
						ctx := context.Background()
 | 
				
			||||||
	ms, err := env.repository.Manifests(ctx)
 | 
						ms, err := env.repository.Manifests(ctx)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatal(err)
 | 
							t.Fatal(err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						equalSignatures := env.registry.(*registry).schema1SignaturesEnabled
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	m := schema1.Manifest{
 | 
						m := schema1.Manifest{
 | 
				
			||||||
		Versioned: manifest.Versioned{
 | 
							Versioned: manifest.Versioned{
 | 
				
			||||||
| 
						 | 
					@ -159,8 +171,14 @@ func TestManifestStorage(t *testing.T) {
 | 
				
			||||||
		t.Fatalf("unexpected manifest type from signedstore")
 | 
							t.Fatalf("unexpected manifest type from signedstore")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !bytes.Equal(fetchedManifest.Canonical, sm.Canonical) {
 | 
				
			||||||
 | 
							t.Fatalf("fetched payload does not match original payload: %q != %q", fetchedManifest.Canonical, sm.Canonical)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if equalSignatures {
 | 
				
			||||||
		if !reflect.DeepEqual(fetchedManifest, sm) {
 | 
							if !reflect.DeepEqual(fetchedManifest, sm) {
 | 
				
			||||||
		t.Fatalf("fetched manifest not equal: %#v != %#v", fetchedManifest, sm)
 | 
								t.Fatalf("fetched manifest not equal: %#v != %#v", fetchedManifest.Manifest, sm.Manifest)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_, pl, err := fetchedManifest.Payload()
 | 
						_, pl, err := fetchedManifest.Payload()
 | 
				
			||||||
| 
						 | 
					@ -196,9 +214,20 @@ func TestManifestStorage(t *testing.T) {
 | 
				
			||||||
		t.Fatalf("unexpected error fetching manifest by digest: %v", err)
 | 
							t.Fatalf("unexpected error fetching manifest by digest: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						byDigestManifest, ok := fetchedByDigest.(*schema1.SignedManifest)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							t.Fatalf("unexpected manifest type from signedstore")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !bytes.Equal(byDigestManifest.Canonical, fetchedManifest.Canonical) {
 | 
				
			||||||
 | 
							t.Fatalf("fetched manifest not equal: %q != %q", byDigestManifest.Canonical, fetchedManifest.Canonical)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if equalSignatures {
 | 
				
			||||||
		if !reflect.DeepEqual(fetchedByDigest, fetchedManifest) {
 | 
							if !reflect.DeepEqual(fetchedByDigest, fetchedManifest) {
 | 
				
			||||||
			t.Fatalf("fetched manifest not equal: %#v != %#v", fetchedByDigest, fetchedManifest)
 | 
								t.Fatalf("fetched manifest not equal: %#v != %#v", fetchedByDigest, fetchedManifest)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sigs, err := fetchedJWS.Signatures()
 | 
						sigs, err := fetchedJWS.Signatures()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
| 
						 | 
					@ -286,6 +315,7 @@ func TestManifestStorage(t *testing.T) {
 | 
				
			||||||
		t.Fatalf("payloads are not equal")
 | 
							t.Fatalf("payloads are not equal")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if equalSignatures {
 | 
				
			||||||
		receivedSigs, err := receivedJWS.Signatures()
 | 
							receivedSigs, err := receivedJWS.Signatures()
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			t.Fatalf("error getting signatures: %v", err)
 | 
								t.Fatalf("error getting signatures: %v", err)
 | 
				
			||||||
| 
						 | 
					@ -296,6 +326,7 @@ func TestManifestStorage(t *testing.T) {
 | 
				
			||||||
				t.Fatalf("mismatched signatures from remote: %v != %v", string(sig), string(expectedSigs[i]))
 | 
									t.Fatalf("mismatched signatures from remote: %v != %v", string(sig), string(expectedSigs[i]))
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Test deleting manifests
 | 
						// Test deleting manifests
 | 
				
			||||||
	err = ms.Delete(ctx, dgst)
 | 
						err = ms.Delete(ctx, dgst)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,6 +6,7 @@ import (
 | 
				
			||||||
	"github.com/docker/distribution/reference"
 | 
						"github.com/docker/distribution/reference"
 | 
				
			||||||
	"github.com/docker/distribution/registry/storage/cache"
 | 
						"github.com/docker/distribution/registry/storage/cache"
 | 
				
			||||||
	storagedriver "github.com/docker/distribution/registry/storage/driver"
 | 
						storagedriver "github.com/docker/distribution/registry/storage/driver"
 | 
				
			||||||
 | 
						"github.com/docker/libtrust"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// registry is the top-level implementation of Registry for use in the storage
 | 
					// registry is the top-level implementation of Registry for use in the storage
 | 
				
			||||||
| 
						 | 
					@ -17,6 +18,8 @@ type registry struct {
 | 
				
			||||||
	blobDescriptorCacheProvider cache.BlobDescriptorCacheProvider
 | 
						blobDescriptorCacheProvider cache.BlobDescriptorCacheProvider
 | 
				
			||||||
	deleteEnabled               bool
 | 
						deleteEnabled               bool
 | 
				
			||||||
	resumableDigestEnabled      bool
 | 
						resumableDigestEnabled      bool
 | 
				
			||||||
 | 
						schema1SignaturesEnabled    bool
 | 
				
			||||||
 | 
						schema1SigningKey           libtrust.PrivateKey
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// RegistryOption is the type used for functional options for NewRegistry.
 | 
					// RegistryOption is the type used for functional options for NewRegistry.
 | 
				
			||||||
| 
						 | 
					@ -43,6 +46,24 @@ func DisableDigestResumption(registry *registry) error {
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DisableSchema1Signatures is a functional option for NewRegistry. It disables
 | 
				
			||||||
 | 
					// signature storage and ensures all schema1 manifests will only be returned
 | 
				
			||||||
 | 
					// with a signature from a provided signing key.
 | 
				
			||||||
 | 
					func DisableSchema1Signatures(registry *registry) error {
 | 
				
			||||||
 | 
						registry.schema1SignaturesEnabled = false
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Schema1SigningKey returns a functional option for NewRegistry. It sets the
 | 
				
			||||||
 | 
					// signing key for adding a signature to all schema1 manifests. This should be
 | 
				
			||||||
 | 
					// used in conjunction with disabling signature store.
 | 
				
			||||||
 | 
					func Schema1SigningKey(key libtrust.PrivateKey) RegistryOption {
 | 
				
			||||||
 | 
						return func(registry *registry) error {
 | 
				
			||||||
 | 
							registry.schema1SigningKey = key
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// BlobDescriptorCacheProvider returns a functional option for
 | 
					// BlobDescriptorCacheProvider returns a functional option for
 | 
				
			||||||
// NewRegistry. It creates a cached blob statter for use by the
 | 
					// NewRegistry. It creates a cached blob statter for use by the
 | 
				
			||||||
// registry.
 | 
					// registry.
 | 
				
			||||||
| 
						 | 
					@ -87,6 +108,7 @@ func NewRegistry(ctx context.Context, driver storagedriver.StorageDriver, option
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		statter:                  statter,
 | 
							statter:                  statter,
 | 
				
			||||||
		resumableDigestEnabled:   true,
 | 
							resumableDigestEnabled:   true,
 | 
				
			||||||
 | 
							schema1SignaturesEnabled: true,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, option := range options {
 | 
						for _, option := range options {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -25,17 +25,32 @@ var _ ManifestHandler = &signedManifestHandler{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (ms *signedManifestHandler) Unmarshal(ctx context.Context, dgst digest.Digest, content []byte) (distribution.Manifest, error) {
 | 
					func (ms *signedManifestHandler) Unmarshal(ctx context.Context, dgst digest.Digest, content []byte) (distribution.Manifest, error) {
 | 
				
			||||||
	context.GetLogger(ms.ctx).Debug("(*signedManifestHandler).Unmarshal")
 | 
						context.GetLogger(ms.ctx).Debug("(*signedManifestHandler).Unmarshal")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var (
 | 
				
			||||||
 | 
							signatures [][]byte
 | 
				
			||||||
 | 
							err        error
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
						if ms.repository.schema1SignaturesEnabled {
 | 
				
			||||||
		// Fetch the signatures for the manifest
 | 
							// Fetch the signatures for the manifest
 | 
				
			||||||
	signatures, err := ms.signatures.Get(dgst)
 | 
							signatures, err = ms.signatures.Get(dgst)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	jsig, err := libtrust.NewJSONSignature(content, signatures...)
 | 
						jsig, err := libtrust.NewJSONSignature(content, signatures...)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ms.repository.schema1SigningKey != nil {
 | 
				
			||||||
 | 
							if err := jsig.Sign(ms.repository.schema1SigningKey); err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else if !ms.repository.schema1SignaturesEnabled {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("missing signing key with signature store disabled")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Extract the pretty JWS
 | 
						// Extract the pretty JWS
 | 
				
			||||||
	raw, err := jsig.PrettySignature("signatures")
 | 
						raw, err := jsig.PrettySignature("signatures")
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
| 
						 | 
					@ -75,6 +90,7 @@ func (ms *signedManifestHandler) Put(ctx context.Context, manifest distribution.
 | 
				
			||||||
		return "", err
 | 
							return "", err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ms.repository.schema1SignaturesEnabled {
 | 
				
			||||||
		// Grab each json signature and store them.
 | 
							// Grab each json signature and store them.
 | 
				
			||||||
		signatures, err := sm.Signatures()
 | 
							signatures, err := sm.Signatures()
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
| 
						 | 
					@ -84,6 +100,7 @@ func (ms *signedManifestHandler) Put(ctx context.Context, manifest distribution.
 | 
				
			||||||
		if err := ms.signatures.Put(revision.Digest, signatures...); err != nil {
 | 
							if err := ms.signatures.Put(revision.Digest, signatures...); err != nil {
 | 
				
			||||||
			return "", err
 | 
								return "", err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return revision.Digest, nil
 | 
						return revision.Digest, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue