mirror of
https://github.com/moby/moby.git
synced 2026-01-11 18:51:37 +00:00
remove support for non-distributable artifacts and deprecate API fields and config
Non-distributable artifacts (foreign layers) were introduced in commit05bd04350bto accommodate Windows images, for which the EULA did not allow layers to be distributed through registries other than those hosted by Microsoft. The concept of foreign / non-distributable layers was adopted by the OCI distribution spec in [oci#233]. These restrictions were relaxed later to allow distributing these images through non-public registries, for which a configuration was added in the daemon in67fdf574d5. In 2022, Microsoft updated the EULA and [removed these restrictions altogether][1], and the OCI distribution spec deprecated the functionality in [oci#965]. In 2023, Microsoft [removed the use of foreign data layers][2] for their images, making this functionality obsolete. This patch: - Deprecates the `--allow-nondistributable-artifacts` daemon flag and corresponding `allow-nondistributable-artifacts` field in `daemon.json`. Setting either option will no longer take an effect, but a deprecation warning log is added to raise awareness about the deprecation. This warning is planned to become an error in the next release. - Deprecates the `RegistryConfig.AllowNondistributableArtifactsCIDRs` and `RegistryConfig.AllowNondistributableArtifactsHostnames` fields in the `GET /info` API response. For API version v1.48 and lower, the fields are still included in the response, but always `null`. In API version v1.49 and higher, the field will be omitted entirely. - Deprecates the `api/types/registry/ServiceConfig.AllowNondistributableArtifactsCIDRs` field. - Deprecates the `api/types/registry/ServiceConfig.AllowNondistributableArtifactsHostnames` field. - Deprecates the `registry.ServiceOptions.AllowNondistributableArtifacts` field. [oci#233]: https://github.com/opencontainers/image-spec/pull/233 [oci#965]: https://github.com/opencontainers/image-spec/pull/965 [1]: https://techcommunity.microsoft.com/blog/containers/announcing-windows-container-base-image-redistribution-rights-change/3645201 [2]: https://techcommunity.microsoft.com/blog/containers/announcing-removal-of-foreign-layers-from-windows-container-images/3846833 Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
@@ -100,6 +100,12 @@ func (s *systemRouter) getInfo(ctx context.Context, w http.ResponseWriter, r *ht
|
||||
// Containerd field introduced in API v1.46.
|
||||
info.Containerd = nil
|
||||
}
|
||||
if versions.LessThan(version, "1.47") {
|
||||
// Field is omitted in API 1.48 and up, but should still be included
|
||||
// in older versions, even if no values are set.
|
||||
info.RegistryConfig.AllowNondistributableArtifactsCIDRs = []*registry.NetIPNet{}
|
||||
info.RegistryConfig.AllowNondistributableArtifactsHostnames = []string{}
|
||||
}
|
||||
|
||||
// TODO(thaJeztah): Expected commits are deprecated, and should no longer be set in API 1.49.
|
||||
info.ContainerdCommit.Expected = info.ContainerdCommit.ID //nolint:staticcheck // ignore SA1019: field is deprecated, but still used on API < v1.49.
|
||||
|
||||
@@ -5983,55 +5983,27 @@ definitions:
|
||||
List of IP ranges to which nondistributable artifacts can be pushed,
|
||||
using the CIDR syntax [RFC 4632](https://tools.ietf.org/html/4632).
|
||||
|
||||
Some images (for example, Windows base images) contain artifacts
|
||||
whose distribution is restricted by license. When these images are
|
||||
pushed to a registry, restricted artifacts are not included.
|
||||
|
||||
This configuration override this behavior, and enables the daemon to
|
||||
push nondistributable artifacts to all registries whose resolved IP
|
||||
address is within the subnet described by the CIDR syntax.
|
||||
|
||||
This option is useful when pushing images containing
|
||||
nondistributable artifacts to a registry on an air-gapped network so
|
||||
hosts on that network can pull the images without connecting to
|
||||
another server.
|
||||
|
||||
> **Warning**: Nondistributable artifacts typically have restrictions
|
||||
> on how and where they can be distributed and shared. Only use this
|
||||
> feature to push artifacts to private registries and ensure that you
|
||||
> are in compliance with any terms that cover redistributing
|
||||
> nondistributable artifacts.
|
||||
<p><br /></p>
|
||||
|
||||
> **Deprecated**: Pushing nondistributable artifacts is now always enabled
|
||||
> and this field is always `null`. This field will be removed in a API v1.49.
|
||||
type: "array"
|
||||
items:
|
||||
type: "string"
|
||||
example: ["::1/128", "127.0.0.0/8"]
|
||||
example: []
|
||||
AllowNondistributableArtifactsHostnames:
|
||||
description: |
|
||||
List of registry hostnames to which nondistributable artifacts can be
|
||||
pushed, using the format `<hostname>[:<port>]` or `<IP address>[:<port>]`.
|
||||
|
||||
Some images (for example, Windows base images) contain artifacts
|
||||
whose distribution is restricted by license. When these images are
|
||||
pushed to a registry, restricted artifacts are not included.
|
||||
<p><br /></p>
|
||||
|
||||
This configuration override this behavior for the specified
|
||||
registries.
|
||||
|
||||
This option is useful when pushing images containing
|
||||
nondistributable artifacts to a registry on an air-gapped network so
|
||||
hosts on that network can pull the images without connecting to
|
||||
another server.
|
||||
|
||||
> **Warning**: Nondistributable artifacts typically have restrictions
|
||||
> on how and where they can be distributed and shared. Only use this
|
||||
> feature to push artifacts to private registries and ensure that you
|
||||
> are in compliance with any terms that cover redistributing
|
||||
> nondistributable artifacts.
|
||||
> **Deprecated**: Pushing nondistributable artifacts is now always enabled
|
||||
> and this field is always `null`. This field will be removed in a API v1.49.
|
||||
type: "array"
|
||||
items:
|
||||
type: "string"
|
||||
example: ["registry.internal.corp.example.com:3000", "[2001:db8:a0b:12f0::1]:443"]
|
||||
example: []
|
||||
InsecureRegistryCIDRs:
|
||||
description: |
|
||||
List of IP ranges of insecure registries, using the CIDR syntax
|
||||
|
||||
@@ -9,11 +9,29 @@ import (
|
||||
|
||||
// ServiceConfig stores daemon registry services configuration.
|
||||
type ServiceConfig struct {
|
||||
AllowNondistributableArtifactsCIDRs []*NetIPNet
|
||||
AllowNondistributableArtifactsHostnames []string
|
||||
InsecureRegistryCIDRs []*NetIPNet `json:"InsecureRegistryCIDRs"`
|
||||
IndexConfigs map[string]*IndexInfo `json:"IndexConfigs"`
|
||||
Mirrors []string
|
||||
AllowNondistributableArtifactsCIDRs []*NetIPNet `json:"AllowNondistributableArtifactsCIDRs,omitempty"` // Deprecated: non-distributable artifacts are deprecated and enabled by default. This field will be removed in the next release.
|
||||
AllowNondistributableArtifactsHostnames []string `json:"AllowNondistributableArtifactsHostnames,omitempty"` // Deprecated: non-distributable artifacts are deprecated and enabled by default. This field will be removed in the next release.
|
||||
|
||||
InsecureRegistryCIDRs []*NetIPNet `json:"InsecureRegistryCIDRs"`
|
||||
IndexConfigs map[string]*IndexInfo `json:"IndexConfigs"`
|
||||
Mirrors []string
|
||||
}
|
||||
|
||||
// MarshalJSON implements a custom marshaler to include legacy fields
|
||||
// in API responses.
|
||||
func (sc ServiceConfig) MarshalJSON() ([]byte, error) {
|
||||
tmp := map[string]interface{}{
|
||||
"InsecureRegistryCIDRs": sc.InsecureRegistryCIDRs,
|
||||
"IndexConfigs": sc.IndexConfigs,
|
||||
"Mirrors": sc.Mirrors,
|
||||
}
|
||||
if sc.AllowNondistributableArtifactsCIDRs != nil {
|
||||
tmp["AllowNondistributableArtifactsCIDRs"] = nil
|
||||
}
|
||||
if sc.AllowNondistributableArtifactsHostnames != nil {
|
||||
tmp["AllowNondistributableArtifactsHostnames"] = nil
|
||||
}
|
||||
return json.Marshal(tmp)
|
||||
}
|
||||
|
||||
// NetIPNet is the net.IPNet type, which can be marshalled and
|
||||
|
||||
30
api/types/registry/registry_test.go
Normal file
30
api/types/registry/registry_test.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
)
|
||||
|
||||
func TestServiceConfigMarshalLegacyFields(t *testing.T) {
|
||||
t.Run("without legacy fields", func(t *testing.T) {
|
||||
b, err := json.Marshal(&ServiceConfig{})
|
||||
assert.NilError(t, err)
|
||||
const expected = `{"IndexConfigs":null,"InsecureRegistryCIDRs":null,"Mirrors":null}`
|
||||
assert.Check(t, is.Equal(string(b), expected), "Legacy nondistributable-artifacts fields should be omitted in output")
|
||||
})
|
||||
|
||||
// Legacy fields should be returned when set to an empty slice. This is
|
||||
// used for API versions < 1.49.
|
||||
t.Run("with legacy fields", func(t *testing.T) {
|
||||
b, err := json.Marshal(&ServiceConfig{
|
||||
AllowNondistributableArtifactsCIDRs: []*NetIPNet{},
|
||||
AllowNondistributableArtifactsHostnames: []string{},
|
||||
})
|
||||
assert.NilError(t, err)
|
||||
const expected = `{"AllowNondistributableArtifactsCIDRs":null,"AllowNondistributableArtifactsHostnames":null,"IndexConfigs":null,"InsecureRegistryCIDRs":null,"Mirrors":null}`
|
||||
assert.Check(t, is.Equal(string(b), expected))
|
||||
})
|
||||
}
|
||||
@@ -18,6 +18,7 @@ func installCommonConfigFlags(conf *config.Config, flags *pflag.FlagSet) {
|
||||
insecureRegistries = opts.NewNamedListOptsRef("insecure-registries", &conf.InsecureRegistries, registry.ValidateIndexName)
|
||||
)
|
||||
flags.Var(allowNonDistributable, "allow-nondistributable-artifacts", "Allow push of nondistributable artifacts to registry")
|
||||
_ = flags.MarkDeprecated("allow-nondistributable-artifacts", "Pushing nondistributable artifacts is now enabled by default. ")
|
||||
flags.Var(registryMirrors, "registry-mirror", "Preferred Docker registry mirror")
|
||||
flags.Var(insecureRegistries, "insecure-registry", "Enable insecure registry communication")
|
||||
|
||||
|
||||
@@ -653,6 +653,11 @@ func loadDaemonCliConfig(opts *daemonOptions) (*config.Config, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(conf.AllowNondistributableArtifacts) > 0 {
|
||||
// TODO(thaJeztah): move to config.Validate and change into an error for v29.0 and remove in v30.0.
|
||||
log.G(context.TODO()).Warn(`DEPRECATED: The "allow-nondistributable-artifacts" config parameter is deprecated and always enabled; this option will be removed in the next release`)
|
||||
}
|
||||
|
||||
return conf, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -190,7 +190,6 @@ func TestLoadDaemonConfigWithEmbeddedOptions(t *testing.T) {
|
||||
|
||||
func TestLoadDaemonConfigWithRegistryOptions(t *testing.T) {
|
||||
content := `{
|
||||
"allow-nondistributable-artifacts": ["allow-nondistributable-artifacts.example.com"],
|
||||
"registry-mirrors": ["https://mirrors.example.com"],
|
||||
"insecure-registries": ["https://insecure-registry.example.com"]
|
||||
}`
|
||||
@@ -202,7 +201,6 @@ func TestLoadDaemonConfigWithRegistryOptions(t *testing.T) {
|
||||
assert.NilError(t, err)
|
||||
assert.Assert(t, loadedConfig != nil)
|
||||
|
||||
assert.Check(t, is.Len(loadedConfig.AllowNondistributableArtifacts, 1))
|
||||
assert.Check(t, is.Len(loadedConfig.Mirrors, 1))
|
||||
assert.Check(t, is.Len(loadedConfig.InsecureRegistries, 1))
|
||||
}
|
||||
|
||||
@@ -231,10 +231,6 @@ func (daemon *Daemon) reloadLabels(txn *reloadTxn, newCfg *configStore, conf *co
|
||||
// reloadRegistryConfig updates the configuration with registry options
|
||||
// and updates the passed attributes.
|
||||
func (daemon *Daemon) reloadRegistryConfig(txn *reloadTxn, newCfg *configStore, conf *config.Config, attributes map[string]string) error {
|
||||
// Update corresponding configuration.
|
||||
if conf.IsValueSet("allow-nondistributable-artifacts") {
|
||||
newCfg.ServiceOptions.AllowNondistributableArtifacts = conf.AllowNondistributableArtifacts
|
||||
}
|
||||
if conf.IsValueSet("insecure-registries") {
|
||||
newCfg.ServiceOptions.InsecureRegistries = conf.InsecureRegistries
|
||||
}
|
||||
@@ -248,7 +244,6 @@ func (daemon *Daemon) reloadRegistryConfig(txn *reloadTxn, newCfg *configStore,
|
||||
}
|
||||
txn.OnCommit(func() error { commit(); return nil })
|
||||
|
||||
attributes["allow-nondistributable-artifacts"] = marshalAttributeSlice(newCfg.ServiceOptions.AllowNondistributableArtifacts)
|
||||
attributes["insecure-registries"] = marshalAttributeSlice(newCfg.ServiceOptions.InsecureRegistries)
|
||||
attributes["registry-mirrors"] = marshalAttributeSlice(newCfg.ServiceOptions.Mirrors)
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ package daemon // import "github.com/docker/docker/daemon"
|
||||
|
||||
import (
|
||||
"os"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/containerd/log"
|
||||
@@ -11,7 +10,6 @@ import (
|
||||
"github.com/docker/docker/libnetwork"
|
||||
"github.com/docker/docker/registry"
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
)
|
||||
|
||||
// muteLogs suppresses logs that are generated during the test
|
||||
@@ -62,61 +60,6 @@ func TestDaemonReloadLabels(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDaemonReloadAllowNondistributableArtifacts(t *testing.T) {
|
||||
daemon := newDaemonForReloadT(t, &config.Config{})
|
||||
muteLogs(t)
|
||||
|
||||
var err error
|
||||
// Initialize daemon with some registries.
|
||||
daemon.registryService, err = registry.NewService(registry.ServiceOptions{
|
||||
AllowNondistributableArtifacts: []string{
|
||||
"127.0.0.0/8",
|
||||
"10.10.1.11:5000",
|
||||
"10.10.1.22:5000", // This will be removed during reload.
|
||||
"docker1.com",
|
||||
"docker2.com", // This will be removed during reload.
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
registries := []string{
|
||||
"::1/128",
|
||||
"127.0.0.0/8",
|
||||
"10.10.1.11:5000",
|
||||
"10.10.1.33:5000", // This will be added during reload.
|
||||
"docker1.com",
|
||||
"docker3.com", // This will be added during reload.
|
||||
}
|
||||
|
||||
newConfig := &config.Config{
|
||||
CommonConfig: config.CommonConfig{
|
||||
ServiceOptions: registry.ServiceOptions{
|
||||
AllowNondistributableArtifacts: registries,
|
||||
},
|
||||
ValuesSet: map[string]interface{}{
|
||||
"allow-nondistributable-artifacts": registries,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := daemon.Reload(newConfig); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var actual []string
|
||||
serviceConfig := daemon.registryService.ServiceConfig()
|
||||
for _, value := range serviceConfig.AllowNondistributableArtifactsCIDRs {
|
||||
actual = append(actual, value.String())
|
||||
}
|
||||
actual = append(actual, serviceConfig.AllowNondistributableArtifactsHostnames...)
|
||||
|
||||
sort.Strings(registries)
|
||||
sort.Strings(actual)
|
||||
assert.Check(t, is.DeepEqual(registries, actual))
|
||||
}
|
||||
|
||||
func TestDaemonReloadMirrors(t *testing.T) {
|
||||
daemon := &Daemon{
|
||||
imageService: images.NewImageService(images.ImageServiceConfig{}),
|
||||
|
||||
@@ -297,16 +297,6 @@ func (pd *pushDescriptor) DiffID() layer.DiffID {
|
||||
}
|
||||
|
||||
func (pd *pushDescriptor) Upload(ctx context.Context, progressOutput progress.Output) (distribution.Descriptor, error) {
|
||||
// Skip foreign layers unless this registry allows nondistributable artifacts.
|
||||
if !pd.endpoint.AllowNondistributableArtifacts {
|
||||
if fs, ok := pd.layer.(distribution.Describable); ok {
|
||||
if d := fs.Descriptor(); len(d.URLs) > 0 {
|
||||
progress.Update(progressOutput, pd.ID(), "Skipped foreign layer")
|
||||
return d, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
diffID := pd.DiffID()
|
||||
|
||||
pd.pushState.Lock()
|
||||
|
||||
@@ -17,6 +17,11 @@ keywords: "API, Docker, rcli, REST, documentation"
|
||||
|
||||
[Docker Engine API v1.48](https://docs.docker.com/reference/api/engine/version/v1.48/) documentation
|
||||
|
||||
# Deprecated: The "allow-nondistributable-artifacts" daemon configuration is
|
||||
deprecated and enabled by default. The `AllowNondistributableArtifactsCIDRs`
|
||||
and `AllowNondistributableArtifactsHostnames` fields in the `RegistryConfig`
|
||||
struct in the `GET /info` response will now always be `null` and will be
|
||||
omitted in API v1.49.
|
||||
* `GET /images/{name}/history` now supports a `platform` parameter (JSON
|
||||
encoded OCI Platform type) that allows to specify a platform to show the
|
||||
history of.
|
||||
|
||||
@@ -414,8 +414,7 @@ func (s *DockerDaemonSuite) TestDaemonEvents(c *testing.T) {
|
||||
// otherwise just check for names being present.
|
||||
expectedSubstrings := []string{
|
||||
` daemon reload ` + info.ID + " ",
|
||||
`(allow-nondistributable-artifacts=[`,
|
||||
` debug=true, `,
|
||||
`debug=true, `,
|
||||
` default-ipc-mode=`,
|
||||
` default-runtime=`,
|
||||
` default-shm-size=`,
|
||||
|
||||
@@ -6,7 +6,6 @@ dockerd - Enable daemon mode
|
||||
# SYNOPSIS
|
||||
**dockerd**
|
||||
[**--add-runtime**[=*[]*]]
|
||||
[**--allow-nondistributable-artifacts**[=*[]*]]
|
||||
[**--authorization-plugin**[=*[]*]]
|
||||
[**-b**|**--bridge**[=*BRIDGE*]]
|
||||
[**--bip**[=*BIP*]]
|
||||
|
||||
@@ -15,9 +15,10 @@ import (
|
||||
|
||||
// ServiceOptions holds command line options.
|
||||
type ServiceOptions struct {
|
||||
AllowNondistributableArtifacts []string `json:"allow-nondistributable-artifacts,omitempty"`
|
||||
Mirrors []string `json:"registry-mirrors,omitempty"`
|
||||
InsecureRegistries []string `json:"insecure-registries,omitempty"`
|
||||
AllowNondistributableArtifacts []string `json:"allow-nondistributable-artifacts,omitempty"` // Deprecated: non-distributable artifacts are deprecated and enabled by default. This field will be removed in the next release.
|
||||
|
||||
Mirrors []string `json:"registry-mirrors,omitempty"`
|
||||
InsecureRegistries []string `json:"insecure-registries,omitempty"`
|
||||
}
|
||||
|
||||
// serviceConfig holds daemon configuration for the registry service.
|
||||
@@ -80,9 +81,6 @@ func CertsDir() string {
|
||||
// newServiceConfig returns a new instance of ServiceConfig
|
||||
func newServiceConfig(options ServiceOptions) (*serviceConfig, error) {
|
||||
config := &serviceConfig{}
|
||||
if err := config.loadAllowNondistributableArtifacts(options.AllowNondistributableArtifacts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := config.loadMirrors(options.Mirrors); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -100,51 +98,12 @@ func (config *serviceConfig) copy() *registry.ServiceConfig {
|
||||
ic[key] = value
|
||||
}
|
||||
return ®istry.ServiceConfig{
|
||||
AllowNondistributableArtifactsCIDRs: append([]*registry.NetIPNet(nil), config.AllowNondistributableArtifactsCIDRs...),
|
||||
AllowNondistributableArtifactsHostnames: append([]string(nil), config.AllowNondistributableArtifactsHostnames...),
|
||||
InsecureRegistryCIDRs: append([]*registry.NetIPNet(nil), config.InsecureRegistryCIDRs...),
|
||||
IndexConfigs: ic,
|
||||
Mirrors: append([]string(nil), config.Mirrors...),
|
||||
InsecureRegistryCIDRs: append([]*registry.NetIPNet(nil), config.InsecureRegistryCIDRs...),
|
||||
IndexConfigs: ic,
|
||||
Mirrors: append([]string(nil), config.Mirrors...),
|
||||
}
|
||||
}
|
||||
|
||||
// loadAllowNondistributableArtifacts loads allow-nondistributable-artifacts registries into config.
|
||||
func (config *serviceConfig) loadAllowNondistributableArtifacts(registries []string) error {
|
||||
cidrs := map[string]*registry.NetIPNet{}
|
||||
hostnames := map[string]bool{}
|
||||
|
||||
for _, r := range registries {
|
||||
if _, err := ValidateIndexName(r); err != nil {
|
||||
return err
|
||||
}
|
||||
if hasScheme(r) {
|
||||
return invalidParamf("allow-nondistributable-artifacts registry %s should not contain '://'", r)
|
||||
}
|
||||
|
||||
if _, ipnet, err := net.ParseCIDR(r); err == nil {
|
||||
// Valid CIDR.
|
||||
cidrs[ipnet.String()] = (*registry.NetIPNet)(ipnet)
|
||||
} else if err = validateHostPort(r); err == nil {
|
||||
// Must be `host:port` if not CIDR.
|
||||
hostnames[r] = true
|
||||
} else {
|
||||
return invalidParamWrapf(err, "allow-nondistributable-artifacts registry %s is not valid", r)
|
||||
}
|
||||
}
|
||||
|
||||
config.AllowNondistributableArtifactsCIDRs = make([]*registry.NetIPNet, 0, len(cidrs))
|
||||
for _, c := range cidrs {
|
||||
config.AllowNondistributableArtifactsCIDRs = append(config.AllowNondistributableArtifactsCIDRs, c)
|
||||
}
|
||||
|
||||
config.AllowNondistributableArtifactsHostnames = make([]string, 0, len(hostnames))
|
||||
for h := range hostnames {
|
||||
config.AllowNondistributableArtifactsHostnames = append(config.AllowNondistributableArtifactsHostnames, h)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// loadMirrors loads mirrors to config, after removing duplicates.
|
||||
// Returns an error if mirrors contains an invalid mirror.
|
||||
func (config *serviceConfig) loadMirrors(mirrors []string) error {
|
||||
@@ -242,25 +201,6 @@ skip:
|
||||
return nil
|
||||
}
|
||||
|
||||
// allowNondistributableArtifacts returns true if the provided hostname is part of the list of registries
|
||||
// that allow push of nondistributable artifacts.
|
||||
//
|
||||
// The list can contain elements with CIDR notation to specify a whole subnet. If the subnet contains an IP
|
||||
// of the registry specified by hostname, true is returned.
|
||||
//
|
||||
// hostname should be a URL.Host (`host:port` or `host`) where the `host` part can be either a domain name
|
||||
// or an IP address. If it is a domain name, then it will be resolved to IP addresses for matching. If
|
||||
// resolution fails, CIDR matching is not performed.
|
||||
func (config *serviceConfig) allowNondistributableArtifacts(hostname string) bool {
|
||||
for _, h := range config.AllowNondistributableArtifactsHostnames {
|
||||
if h == hostname {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return isCIDRMatch(config.AllowNondistributableArtifactsCIDRs, hostname)
|
||||
}
|
||||
|
||||
// isSecureIndex returns false if the provided indexName is part of the list of insecure registries
|
||||
// Insecure registries accept HTTP and/or accept HTTPS with certificates from unknown CAs.
|
||||
//
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
package registry // import "github.com/docker/docker/registry"
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/errdefs"
|
||||
@@ -11,123 +8,6 @@ import (
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
)
|
||||
|
||||
func TestLoadAllowNondistributableArtifacts(t *testing.T) {
|
||||
testCases := []struct {
|
||||
registries []string
|
||||
cidrStrs []string
|
||||
hostnames []string
|
||||
err string
|
||||
}{
|
||||
{
|
||||
registries: []string{"1.2.3.0/24"},
|
||||
cidrStrs: []string{"1.2.3.0/24"},
|
||||
},
|
||||
{
|
||||
registries: []string{"2001:db8::/120"},
|
||||
cidrStrs: []string{"2001:db8::/120"},
|
||||
},
|
||||
{
|
||||
registries: []string{"127.0.0.1"},
|
||||
hostnames: []string{"127.0.0.1"},
|
||||
},
|
||||
{
|
||||
registries: []string{"127.0.0.1:8080"},
|
||||
hostnames: []string{"127.0.0.1:8080"},
|
||||
},
|
||||
{
|
||||
registries: []string{"2001:db8::1"},
|
||||
hostnames: []string{"2001:db8::1"},
|
||||
},
|
||||
{
|
||||
registries: []string{"[2001:db8::1]:80"},
|
||||
hostnames: []string{"[2001:db8::1]:80"},
|
||||
},
|
||||
{
|
||||
registries: []string{"[2001:db8::1]:80"},
|
||||
hostnames: []string{"[2001:db8::1]:80"},
|
||||
},
|
||||
{
|
||||
registries: []string{"1.2.3.0/24", "2001:db8::/120", "127.0.0.1", "127.0.0.1:8080"},
|
||||
cidrStrs: []string{"1.2.3.0/24", "2001:db8::/120"},
|
||||
hostnames: []string{"127.0.0.1", "127.0.0.1:8080"},
|
||||
},
|
||||
|
||||
{
|
||||
registries: []string{"http://myregistry.example.com"},
|
||||
err: "allow-nondistributable-artifacts registry http://myregistry.example.com should not contain '://'",
|
||||
},
|
||||
{
|
||||
registries: []string{"https://myregistry.example.com"},
|
||||
err: "allow-nondistributable-artifacts registry https://myregistry.example.com should not contain '://'",
|
||||
},
|
||||
{
|
||||
registries: []string{"HTTP://myregistry.example.com"},
|
||||
err: "allow-nondistributable-artifacts registry HTTP://myregistry.example.com should not contain '://'",
|
||||
},
|
||||
{
|
||||
registries: []string{"svn://myregistry.example.com"},
|
||||
err: "allow-nondistributable-artifacts registry svn://myregistry.example.com should not contain '://'",
|
||||
},
|
||||
{
|
||||
registries: []string{"-invalid-registry"},
|
||||
err: "Cannot begin or end with a hyphen",
|
||||
},
|
||||
{
|
||||
registries: []string{`mytest-.com`},
|
||||
err: `allow-nondistributable-artifacts registry mytest-.com is not valid: invalid host "mytest-.com"`,
|
||||
},
|
||||
{
|
||||
registries: []string{`1200:0000:AB00:1234:0000:2552:7777:1313:8080`},
|
||||
err: `allow-nondistributable-artifacts registry 1200:0000:AB00:1234:0000:2552:7777:1313:8080 is not valid: invalid host "1200:0000:AB00:1234:0000:2552:7777:1313:8080"`,
|
||||
},
|
||||
{
|
||||
registries: []string{`myregistry.example.com:500000`},
|
||||
err: `allow-nondistributable-artifacts registry myregistry.example.com:500000 is not valid: invalid port "500000"`,
|
||||
},
|
||||
{
|
||||
registries: []string{`"myregistry.example.com"`},
|
||||
err: `allow-nondistributable-artifacts registry "myregistry.example.com" is not valid: invalid host "\"myregistry.example.com\""`,
|
||||
},
|
||||
{
|
||||
registries: []string{`"myregistry.example.com:5000"`},
|
||||
err: `allow-nondistributable-artifacts registry "myregistry.example.com:5000" is not valid: invalid host "\"myregistry.example.com"`,
|
||||
},
|
||||
}
|
||||
for _, testCase := range testCases {
|
||||
config := emptyServiceConfig
|
||||
err := config.loadAllowNondistributableArtifacts(testCase.registries)
|
||||
if testCase.err == "" {
|
||||
if err != nil {
|
||||
t.Fatalf("expect no error, got '%s'", err)
|
||||
}
|
||||
|
||||
var cidrStrs []string
|
||||
for _, c := range config.AllowNondistributableArtifactsCIDRs {
|
||||
cidrStrs = append(cidrStrs, c.String())
|
||||
}
|
||||
|
||||
sort.Strings(testCase.cidrStrs)
|
||||
sort.Strings(cidrStrs)
|
||||
if (len(testCase.cidrStrs) > 0 || len(cidrStrs) > 0) && !reflect.DeepEqual(testCase.cidrStrs, cidrStrs) {
|
||||
t.Fatalf("expect AllowNondistributableArtifactsCIDRs to be '%+v', got '%+v'", testCase.cidrStrs, cidrStrs)
|
||||
}
|
||||
|
||||
sort.Strings(testCase.hostnames)
|
||||
sort.Strings(config.AllowNondistributableArtifactsHostnames)
|
||||
if (len(testCase.hostnames) > 0 || len(config.AllowNondistributableArtifactsHostnames) > 0) && !reflect.DeepEqual(testCase.hostnames, config.AllowNondistributableArtifactsHostnames) {
|
||||
t.Fatalf("expect AllowNondistributableArtifactsHostnames to be '%+v', got '%+v'", testCase.hostnames, config.AllowNondistributableArtifactsHostnames)
|
||||
}
|
||||
} else {
|
||||
if err == nil {
|
||||
t.Fatalf("expect error '%s', got no error", testCase.err)
|
||||
}
|
||||
if !strings.Contains(err.Error(), testCase.err) {
|
||||
t.Fatalf("expect error '%s', got '%s'", testCase.err, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateMirror(t *testing.T) {
|
||||
valid := []string{
|
||||
"http://mirror-1.example.com",
|
||||
@@ -263,60 +143,52 @@ func TestLoadInsecureRegistries(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNewServiceConfig(t *testing.T) {
|
||||
testCases := []struct {
|
||||
tests := []struct {
|
||||
doc string
|
||||
opts ServiceOptions
|
||||
errStr string
|
||||
}{
|
||||
{
|
||||
ServiceOptions{},
|
||||
"",
|
||||
doc: "empty config",
|
||||
},
|
||||
{
|
||||
ServiceOptions{
|
||||
doc: "invalid mirror",
|
||||
opts: ServiceOptions{
|
||||
Mirrors: []string{"example.com:5000"},
|
||||
},
|
||||
`invalid mirror: unsupported scheme "example.com" in "example.com:5000"`,
|
||||
errStr: `invalid mirror: unsupported scheme "example.com" in "example.com:5000"`,
|
||||
},
|
||||
{
|
||||
ServiceOptions{
|
||||
Mirrors: []string{"http://example.com:5000"},
|
||||
doc: "valid mirror",
|
||||
opts: ServiceOptions{
|
||||
Mirrors: []string{"https://example.com:5000"},
|
||||
},
|
||||
"",
|
||||
},
|
||||
{
|
||||
ServiceOptions{
|
||||
doc: "invalid insecure registry",
|
||||
opts: ServiceOptions{
|
||||
InsecureRegistries: []string{"[fe80::]/64"},
|
||||
},
|
||||
`insecure registry [fe80::]/64 is not valid: invalid host "[fe80::]/64"`,
|
||||
errStr: `insecure registry [fe80::]/64 is not valid: invalid host "[fe80::]/64"`,
|
||||
},
|
||||
{
|
||||
ServiceOptions{
|
||||
doc: "valid insecure registry",
|
||||
opts: ServiceOptions{
|
||||
InsecureRegistries: []string{"102.10.8.1/24"},
|
||||
},
|
||||
"",
|
||||
},
|
||||
{
|
||||
ServiceOptions{
|
||||
AllowNondistributableArtifacts: []string{"[fe80::]/64"},
|
||||
},
|
||||
`allow-nondistributable-artifacts registry [fe80::]/64 is not valid: invalid host "[fe80::]/64"`,
|
||||
},
|
||||
{
|
||||
ServiceOptions{
|
||||
AllowNondistributableArtifacts: []string{"102.10.8.1/24"},
|
||||
},
|
||||
"",
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
_, err := newServiceConfig(testCase.opts)
|
||||
if testCase.errStr != "" {
|
||||
assert.Check(t, is.Error(err, testCase.errStr))
|
||||
assert.Check(t, errdefs.IsInvalidParameter(err))
|
||||
} else {
|
||||
assert.Check(t, err)
|
||||
}
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.doc, func(t *testing.T) {
|
||||
_, err := newServiceConfig(tc.opts)
|
||||
if tc.errStr != "" {
|
||||
assert.Check(t, is.Error(err, tc.errStr))
|
||||
assert.Check(t, errdefs.IsInvalidParameter(err))
|
||||
} else {
|
||||
assert.Check(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -440,52 +440,6 @@ func TestMirrorEndpointLookup(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAllowNondistributableArtifacts(t *testing.T) {
|
||||
overrideLookupIP(t)
|
||||
tests := []struct {
|
||||
addr string
|
||||
registries []string
|
||||
expected bool
|
||||
}{
|
||||
{IndexName, nil, false},
|
||||
{"example.com", []string{}, false},
|
||||
{"example.com", []string{"example.com"}, true},
|
||||
{"localhost", []string{"localhost:5000"}, false},
|
||||
{"localhost:5000", []string{"localhost:5000"}, true},
|
||||
{"localhost", []string{"example.com"}, false},
|
||||
{"127.0.0.1:5000", []string{"127.0.0.1:5000"}, true},
|
||||
{"localhost", nil, false},
|
||||
{"localhost:5000", nil, false},
|
||||
{"127.0.0.1", nil, false},
|
||||
{"localhost", []string{"example.com"}, false},
|
||||
{"127.0.0.1", []string{"example.com"}, false},
|
||||
{"example.com", nil, false},
|
||||
{"example.com", []string{"example.com"}, true},
|
||||
{"127.0.0.1", []string{"example.com"}, false},
|
||||
{"127.0.0.1:5000", []string{"example.com"}, false},
|
||||
{"example.com:5000", []string{"42.42.0.0/16"}, true},
|
||||
{"example.com", []string{"42.42.0.0/16"}, true},
|
||||
{"example.com:5000", []string{"42.42.42.42/8"}, true},
|
||||
{"127.0.0.1:5000", []string{"127.0.0.0/8"}, true},
|
||||
{"42.42.42.42:5000", []string{"42.1.1.1/8"}, true},
|
||||
{"invalid.example.com", []string{"42.42.0.0/16"}, false},
|
||||
{"invalid.example.com", []string{"invalid.example.com"}, true},
|
||||
{"invalid.example.com:5000", []string{"invalid.example.com"}, false},
|
||||
{"invalid.example.com:5000", []string{"invalid.example.com:5000"}, true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
config, err := newServiceConfig(ServiceOptions{
|
||||
AllowNondistributableArtifacts: tt.registries,
|
||||
})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if v := config.allowNondistributableArtifacts(tt.addr); v != tt.expected {
|
||||
t.Errorf("allowNondistributableArtifacts failed for %q %v, expected %v got %v", tt.addr, tt.registries, tt.expected, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsSecureIndex(t *testing.T) {
|
||||
overrideLookupIP(t)
|
||||
tests := []struct {
|
||||
|
||||
@@ -104,7 +104,7 @@ func (s *Service) ResolveRepository(name reference.Named) (*RepositoryInfo, erro
|
||||
type APIEndpoint struct {
|
||||
Mirror bool
|
||||
URL *url.URL
|
||||
AllowNondistributableArtifacts bool
|
||||
AllowNondistributableArtifacts bool // Deprecated: non-distributable artifacts are deprecated and enabled by default. This field will be removed in the next release.
|
||||
Official bool
|
||||
TrimHostname bool // Deprecated: hostname is now trimmed unconditionally for remote names. This field will be removed in the next release.
|
||||
TLSConfig *tls.Config
|
||||
|
||||
@@ -8,8 +8,6 @@ import (
|
||||
)
|
||||
|
||||
func (s *Service) lookupV2Endpoints(hostname string, includeMirrors bool) ([]APIEndpoint, error) {
|
||||
ana := s.config.allowNondistributableArtifacts(hostname)
|
||||
|
||||
var endpoints []APIEndpoint
|
||||
if hostname == DefaultNamespace || hostname == IndexHostname {
|
||||
if includeMirrors {
|
||||
@@ -36,8 +34,6 @@ func (s *Service) lookupV2Endpoints(hostname string, includeMirrors bool) ([]API
|
||||
URL: DefaultV2Registry,
|
||||
Official: true,
|
||||
TLSConfig: tlsconfig.ServerDefault(),
|
||||
|
||||
AllowNondistributableArtifacts: ana,
|
||||
})
|
||||
|
||||
return endpoints, nil
|
||||
@@ -55,8 +51,6 @@ func (s *Service) lookupV2Endpoints(hostname string, includeMirrors bool) ([]API
|
||||
Host: hostname,
|
||||
},
|
||||
TLSConfig: tlsConfig,
|
||||
|
||||
AllowNondistributableArtifacts: ana,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -68,8 +62,6 @@ func (s *Service) lookupV2Endpoints(hostname string, includeMirrors bool) ([]API
|
||||
},
|
||||
// used to check if supposed to be secure via InsecureSkipVerify
|
||||
TLSConfig: tlsConfig,
|
||||
|
||||
AllowNondistributableArtifacts: ana,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user