Merge pull request #2474 from vikstrous/disable-v1-master
disable schema1 by default, add a config flag to enable itmaster
						commit
						ef859e1b21
					
				|  | @ -194,6 +194,8 @@ type Configuration struct { | |||
| 			// TrustKey is the signing key to use for adding the signature to
 | ||||
| 			// schema1 manifests.
 | ||||
| 			TrustKey string `yaml:"signingkeyfile,omitempty"` | ||||
| 			// Enabled determines if schema1 manifests should be pullable
 | ||||
| 			Enabled bool `yaml:"enabled,omitempty"` | ||||
| 		} `yaml:"schema1,omitempty"` | ||||
| 	} `yaml:"compatibility,omitempty"` | ||||
| 
 | ||||
|  |  | |||
|  | @ -7,6 +7,9 @@ storage: | |||
|         rootdirectory: /tmp/registry-dev | ||||
| http: | ||||
|     addr: 0.0.0.0:5000 | ||||
| compatibility: | ||||
|     schema1: | ||||
|         enabled: true | ||||
| auth: | ||||
|     token: | ||||
|         realm: "https://auth.localregistry:5559/token/" | ||||
|  |  | |||
|  | @ -10,6 +10,9 @@ http: | |||
|     tls: | ||||
|         certificate: "/etc/docker/registry/localregistry.cert" | ||||
|         key: "/etc/docker/registry/localregistry.key" | ||||
| compatibility: | ||||
|     schema1: | ||||
|         enabled: true | ||||
| auth: | ||||
|     token: | ||||
|         realm: "https://auth.localregistry:5559/token/" | ||||
|  |  | |||
|  | @ -10,6 +10,9 @@ http: | |||
|     tls: | ||||
|         certificate: "/etc/docker/registry/localregistry.cert" | ||||
|         key: "/etc/docker/registry/localregistry.key" | ||||
| compatibility: | ||||
|     schema1: | ||||
|         enabled: true | ||||
| auth: | ||||
|     token: | ||||
|         realm: "https://auth.localregistry:5556/token/" | ||||
|  |  | |||
|  | @ -280,6 +280,7 @@ proxy: | |||
| compatibility: | ||||
|   schema1: | ||||
|     signingkeyfile: /etc/registry/key.json | ||||
|     enabled: true | ||||
| validation: | ||||
|   manifests: | ||||
|     urls: | ||||
|  | @ -1067,6 +1068,7 @@ username (such as `batman`) and the password for that username. | |||
| compatibility: | ||||
|   schema1: | ||||
|     signingkeyfile: /etc/registry/key.json | ||||
|     enabled: true | ||||
| ``` | ||||
| 
 | ||||
| Use the `compatibility` structure to configure handling of older and deprecated | ||||
|  | @ -1077,6 +1079,7 @@ features. Each subsection defines such a feature with configurable behavior. | |||
| | Parameter | Required | Description                                           | | ||||
| |-----------|----------|-------------------------------------------------------| | ||||
| | `signingkeyfile` | no | The signing private key used to add signatures to `schema1` manifests. If no signing key is provided, a new ECDSA key is generated when the registry starts. | | ||||
| | `enabled` | no | If this is not set to true, `schema1` manifests cannot be pushed. | | ||||
| 
 | ||||
| ## `validation` | ||||
| 
 | ||||
|  |  | |||
|  | @ -20,6 +20,10 @@ var ErrManifestNotModified = errors.New("manifest not modified") | |||
| // performed
 | ||||
| var ErrUnsupported = errors.New("operation unsupported") | ||||
| 
 | ||||
| // ErrSchemaV1Unsupported is returned when a client tries to upload a schema v1
 | ||||
| // manifest but the registry is configured to reject it
 | ||||
| var ErrSchemaV1Unsupported = errors.New("manifest schema v1 unsupported") | ||||
| 
 | ||||
| // ErrTagUnknown is returned if the given tag is not known by the tag service
 | ||||
| type ErrTagUnknown struct { | ||||
| 	Tag string | ||||
|  |  | |||
|  | @ -25,7 +25,7 @@ func TestListener(t *testing.T) { | |||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 
 | ||||
| 	registry, err := storage.NewRegistry(ctx, inmemory.New(), storage.BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), storage.EnableDelete, storage.EnableRedirect, storage.Schema1SigningKey(k)) | ||||
| 	registry, err := storage.NewRegistry(ctx, inmemory.New(), storage.BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), storage.EnableDelete, storage.EnableRedirect, storage.Schema1SigningKey(k), storage.EnableSchema1) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("error creating registry: %v", err) | ||||
| 	} | ||||
|  |  | |||
|  | @ -2027,6 +2027,7 @@ func newTestEnvMirror(t *testing.T, deleteEnabled bool) *testEnv { | |||
| 			RemoteURL: "http://example.com", | ||||
| 		}, | ||||
| 	} | ||||
| 	config.Compatibility.Schema1.Enabled = true | ||||
| 
 | ||||
| 	return newTestEnvWithConfig(t, &config) | ||||
| 
 | ||||
|  | @ -2043,6 +2044,7 @@ func newTestEnv(t *testing.T, deleteEnabled bool) *testEnv { | |||
| 		}, | ||||
| 	} | ||||
| 
 | ||||
| 	config.Compatibility.Schema1.Enabled = true | ||||
| 	config.HTTP.Headers = headerConfig | ||||
| 
 | ||||
| 	return newTestEnvWithConfig(t, &config) | ||||
|  | @ -2565,6 +2567,7 @@ func TestProxyManifestGetByTag(t *testing.T) { | |||
| 			}}, | ||||
| 		}, | ||||
| 	} | ||||
| 	truthConfig.Compatibility.Schema1.Enabled = true | ||||
| 	truthConfig.HTTP.Headers = headerConfig | ||||
| 
 | ||||
| 	imageName, _ := reference.WithName("foo/bar") | ||||
|  | @ -2583,6 +2586,7 @@ func TestProxyManifestGetByTag(t *testing.T) { | |||
| 			RemoteURL: truthEnv.server.URL, | ||||
| 		}, | ||||
| 	} | ||||
| 	proxyConfig.Compatibility.Schema1.Enabled = true | ||||
| 	proxyConfig.HTTP.Headers = headerConfig | ||||
| 
 | ||||
| 	proxyEnv := newTestEnvWithConfig(t, &proxyConfig) | ||||
|  |  | |||
|  | @ -177,6 +177,10 @@ func NewApp(ctx context.Context, config *configuration.Configuration) *App { | |||
| 
 | ||||
| 	options = append(options, storage.Schema1SigningKey(app.trustKey)) | ||||
| 
 | ||||
| 	if config.Compatibility.Schema1.Enabled { | ||||
| 		options = append(options, storage.EnableSchema1) | ||||
| 	} | ||||
| 
 | ||||
| 	if config.HTTP.Host != "" { | ||||
| 		u, err := url.Parse(config.HTTP.Host) | ||||
| 		if err != nil { | ||||
|  |  | |||
|  | @ -95,7 +95,8 @@ func newManifestStoreTestEnv(t *testing.T, name, tag string) *manifestStoreTestE | |||
| 	ctx := context.Background() | ||||
| 	truthRegistry, err := storage.NewRegistry(ctx, inmemory.New(), | ||||
| 		storage.BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), | ||||
| 		storage.Schema1SigningKey(k)) | ||||
| 		storage.Schema1SigningKey(k), | ||||
| 		storage.EnableSchema1) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("error creating registry: %v", err) | ||||
| 	} | ||||
|  | @ -117,7 +118,7 @@ func newManifestStoreTestEnv(t *testing.T, name, tag string) *manifestStoreTestE | |||
| 		t.Fatalf(err.Error()) | ||||
| 	} | ||||
| 
 | ||||
| 	localRegistry, err := storage.NewRegistry(ctx, inmemory.New(), storage.BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), storage.EnableRedirect, storage.DisableDigestResumption, storage.Schema1SigningKey(k)) | ||||
| 	localRegistry, err := storage.NewRegistry(ctx, inmemory.New(), storage.BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), storage.EnableRedirect, storage.DisableDigestResumption, storage.Schema1SigningKey(k), storage.EnableSchema1) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("error creating registry: %v", err) | ||||
| 	} | ||||
|  |  | |||
|  | @ -26,7 +26,7 @@ type setupEnv struct { | |||
| func setupFS(t *testing.T) *setupEnv { | ||||
| 	d := inmemory.New() | ||||
| 	ctx := context.Background() | ||||
| 	registry, err := NewRegistry(ctx, d, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), EnableRedirect) | ||||
| 	registry, err := NewRegistry(ctx, d, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), EnableRedirect, EnableSchema1) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("error creating registry: %v", err) | ||||
| 	} | ||||
|  | @ -207,7 +207,7 @@ func testEq(a, b []string, size int) bool { | |||
| func setupBadWalkEnv(t *testing.T) *setupEnv { | ||||
| 	d := newBadListDriver() | ||||
| 	ctx := context.Background() | ||||
| 	registry, err := NewRegistry(ctx, d, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), EnableRedirect) | ||||
| 	registry, err := NewRegistry(ctx, d, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), EnableRedirect, EnableSchema1) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("error creating registry: %v", err) | ||||
| 	} | ||||
|  |  | |||
|  | @ -27,7 +27,7 @@ func createRegistry(t *testing.T, driver driver.StorageDriver, options ...Regist | |||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 	options = append([]RegistryOption{EnableDelete, Schema1SigningKey(k)}, options...) | ||||
| 	options = append([]RegistryOption{EnableDelete, Schema1SigningKey(k), EnableSchema1}, options...) | ||||
| 	registry, err := NewRegistry(ctx, driver, options...) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("Failed to construct namespace") | ||||
|  |  | |||
|  | @ -59,10 +59,18 @@ func TestManifestStorage(t *testing.T) { | |||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 	testManifestStorage(t, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), EnableDelete, EnableRedirect, Schema1SigningKey(k)) | ||||
| 	testManifestStorage(t, true, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), EnableDelete, EnableRedirect, Schema1SigningKey(k), EnableSchema1) | ||||
| } | ||||
| 
 | ||||
| func testManifestStorage(t *testing.T, options ...RegistryOption) { | ||||
| func TestManifestStorageV1Unsupported(t *testing.T) { | ||||
| 	k, err := libtrust.GenerateECP256PrivateKey() | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 	testManifestStorage(t, false, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), EnableDelete, EnableRedirect, Schema1SigningKey(k)) | ||||
| } | ||||
| 
 | ||||
| func testManifestStorage(t *testing.T, schema1Enabled bool, options ...RegistryOption) { | ||||
| 	repoName, _ := reference.WithName("foo/bar") | ||||
| 	env := newManifestStoreTestEnv(t, repoName, "thetag", options...) | ||||
| 	ctx := context.Background() | ||||
|  | @ -114,6 +122,15 @@ func testManifestStorage(t *testing.T, options ...RegistryOption) { | |||
| 		t.Fatalf("expected errors putting manifest with full verification") | ||||
| 	} | ||||
| 
 | ||||
| 	// If schema1 is not enabled, do a short version of this test, just checking
 | ||||
| 	// if we get the right error when we Put
 | ||||
| 	if !schema1Enabled { | ||||
| 		if err != distribution.ErrSchemaV1Unsupported { | ||||
| 			t.Fatalf("got the wrong error when schema1 is disabled: %s", err) | ||||
| 		} | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	switch err := err.(type) { | ||||
| 	case distribution.ErrManifestVerification: | ||||
| 		if len(err) != 2 { | ||||
|  |  | |||
|  | @ -19,6 +19,7 @@ type registry struct { | |||
| 	statter                      *blobStatter // global statter service.
 | ||||
| 	blobDescriptorCacheProvider  cache.BlobDescriptorCacheProvider | ||||
| 	deleteEnabled                bool | ||||
| 	schema1Enabled               bool | ||||
| 	resumableDigestEnabled       bool | ||||
| 	schema1SigningKey            libtrust.PrivateKey | ||||
| 	blobDescriptorServiceFactory distribution.BlobDescriptorServiceFactory | ||||
|  | @ -49,6 +50,13 @@ func EnableDelete(registry *registry) error { | |||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // EnableSchema1 is a functional option for NewRegistry. It enables pushing of
 | ||||
| // schema1 manifests.
 | ||||
| func EnableSchema1(registry *registry) error { | ||||
| 	registry.schema1Enabled = true | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // DisableDigestResumption is a functional option for NewRegistry. It should be
 | ||||
| // used if the registry is acting as a caching proxy.
 | ||||
| func DisableDigestResumption(registry *registry) error { | ||||
|  | @ -239,16 +247,30 @@ func (repo *repository) Manifests(ctx context.Context, options ...distribution.M | |||
| 		linkDirectoryPathSpec: manifestDirectoryPathSpec, | ||||
| 	} | ||||
| 
 | ||||
| 	ms := &manifestStore{ | ||||
| 		ctx:        ctx, | ||||
| 		repository: repo, | ||||
| 		blobStore:  blobStore, | ||||
| 		schema1Handler: &signedManifestHandler{ | ||||
| 	var v1Handler ManifestHandler | ||||
| 	if repo.schema1Enabled { | ||||
| 		v1Handler = &signedManifestHandler{ | ||||
| 			ctx:               ctx, | ||||
| 			schema1SigningKey: repo.schema1SigningKey, | ||||
| 			repository:        repo, | ||||
| 			blobStore:         blobStore, | ||||
| 		}, | ||||
| 		} | ||||
| 	} else { | ||||
| 		v1Handler = &v1UnsupportedHandler{ | ||||
| 			innerHandler: &signedManifestHandler{ | ||||
| 				ctx:               ctx, | ||||
| 				schema1SigningKey: repo.schema1SigningKey, | ||||
| 				repository:        repo, | ||||
| 				blobStore:         blobStore, | ||||
| 			}, | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	ms := &manifestStore{ | ||||
| 		ctx:            ctx, | ||||
| 		repository:     repo, | ||||
| 		blobStore:      blobStore, | ||||
| 		schema1Handler: v1Handler, | ||||
| 		schema2Handler: &schema2ManifestHandler{ | ||||
| 			ctx:          ctx, | ||||
| 			repository:   repo, | ||||
|  |  | |||
|  | @ -0,0 +1,23 @@ | |||
| package storage | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 
 | ||||
| 	"github.com/docker/distribution" | ||||
| 	digest "github.com/opencontainers/go-digest" | ||||
| ) | ||||
| 
 | ||||
| // signedManifestHandler is a ManifestHandler that unmarshals v1 manifests but
 | ||||
| // refuses to Put v1 manifests
 | ||||
| type v1UnsupportedHandler struct { | ||||
| 	innerHandler ManifestHandler | ||||
| } | ||||
| 
 | ||||
| var _ ManifestHandler = &v1UnsupportedHandler{} | ||||
| 
 | ||||
| func (v *v1UnsupportedHandler) Unmarshal(ctx context.Context, dgst digest.Digest, content []byte) (distribution.Manifest, error) { | ||||
| 	return v.innerHandler.Unmarshal(ctx, dgst, content) | ||||
| } | ||||
| func (v *v1UnsupportedHandler) Put(ctx context.Context, manifest distribution.Manifest, skipDependencyVerification bool) (digest.Digest, error) { | ||||
| 	return digest.Digest(""), distribution.ErrSchemaV1Unsupported | ||||
| } | ||||
		Loading…
	
		Reference in New Issue