Merge pull request #33151 from nwt/push-foreign-layers
Add daemon option to push foreign layers
This commit is contained in:
commit
a30ef99e8d
16 changed files with 430 additions and 36 deletions
|
@ -10,9 +10,11 @@ import (
|
|||
|
||||
// ServiceConfig stores daemon registry services configuration.
|
||||
type ServiceConfig struct {
|
||||
InsecureRegistryCIDRs []*NetIPNet `json:"InsecureRegistryCIDRs"`
|
||||
IndexConfigs map[string]*IndexInfo `json:"IndexConfigs"`
|
||||
Mirrors []string
|
||||
AllowNondistributableArtifactsCIDRs []*NetIPNet
|
||||
AllowNondistributableArtifactsHostnames []string
|
||||
InsecureRegistryCIDRs []*NetIPNet `json:"InsecureRegistryCIDRs"`
|
||||
IndexConfigs map[string]*IndexInfo `json:"IndexConfigs"`
|
||||
Mirrors []string
|
||||
}
|
||||
|
||||
// NetIPNet is the net.IPNet type, which can be marshalled and
|
||||
|
|
|
@ -131,6 +131,7 @@ func TestLoadDaemonConfigWithEmbeddedOptions(t *testing.T) {
|
|||
|
||||
func TestLoadDaemonConfigWithRegistryOptions(t *testing.T) {
|
||||
content := `{
|
||||
"allow-nondistributable-artifacts": ["allow-nondistributable-artifacts.com"],
|
||||
"registry-mirrors": ["https://mirrors.docker.com"],
|
||||
"insecure-registries": ["https://insecure.docker.com"]
|
||||
}`
|
||||
|
@ -142,6 +143,7 @@ func TestLoadDaemonConfigWithRegistryOptions(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
require.NotNil(t, loadedConfig)
|
||||
|
||||
assert.Len(t, loadedConfig.AllowNondistributableArtifacts, 1)
|
||||
assert.Len(t, loadedConfig.Mirrors, 1)
|
||||
assert.Len(t, loadedConfig.InsecureRegistries, 1)
|
||||
}
|
||||
|
|
|
@ -1969,6 +1969,7 @@ _docker_daemon() {
|
|||
local options_with_args="
|
||||
$global_options_with_args
|
||||
--add-runtime
|
||||
--allow-nondistributable-artifacts
|
||||
--api-cors-header
|
||||
--authorization-plugin
|
||||
--bip
|
||||
|
|
|
@ -2603,6 +2603,7 @@ __docker_subcommand() {
|
|||
_arguments $(__docker_arguments) \
|
||||
$opts_help \
|
||||
"($help)*--add-runtime=[Register an additional OCI compatible runtime]:runtime:__docker_complete_runtimes" \
|
||||
"($help)*--allow-nondistributable-artifacts=[Push nondistributable artifacts to specified registries]:registry: " \
|
||||
"($help)--api-cors-header=[CORS headers in the Engine API]:CORS headers: " \
|
||||
"($help)*--authorization-plugin=[Authorization plugins to load]" \
|
||||
"($help -b --bridge)"{-b=,--bridge=}"[Attach containers to a network bridge]:bridge:_net_interfaces" \
|
||||
|
|
|
@ -48,6 +48,9 @@ func (daemon *Daemon) Reload(conf *config.Config) (err error) {
|
|||
if err := daemon.reloadLabels(conf, attributes); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := daemon.reloadAllowNondistributableArtifacts(conf, attributes); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := daemon.reloadInsecureRegistries(conf, attributes); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -217,6 +220,31 @@ func (daemon *Daemon) reloadLabels(conf *config.Config, attributes map[string]st
|
|||
return nil
|
||||
}
|
||||
|
||||
// reloadAllowNondistributableArtifacts updates the configuration with allow-nondistributable-artifacts options
|
||||
// and updates the passed attributes.
|
||||
func (daemon *Daemon) reloadAllowNondistributableArtifacts(conf *config.Config, attributes map[string]string) error {
|
||||
// Update corresponding configuration.
|
||||
if conf.IsValueSet("allow-nondistributable-artifacts") {
|
||||
daemon.configStore.AllowNondistributableArtifacts = conf.AllowNondistributableArtifacts
|
||||
if err := daemon.RegistryService.LoadAllowNondistributableArtifacts(conf.AllowNondistributableArtifacts); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare reload event attributes with updatable configurations.
|
||||
if daemon.configStore.AllowNondistributableArtifacts != nil {
|
||||
v, err := json.Marshal(daemon.configStore.AllowNondistributableArtifacts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
attributes["allow-nondistributable-artifacts"] = string(v)
|
||||
} else {
|
||||
attributes["allow-nondistributable-artifacts"] = "[]"
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// reloadInsecureRegistries updates configuration with insecure registry option
|
||||
// and updates the passed attributes
|
||||
func (daemon *Daemon) reloadInsecureRegistries(conf *config.Config, attributes map[string]string) error {
|
||||
|
|
|
@ -4,6 +4,7 @@ package daemon
|
|||
|
||||
import (
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -40,6 +41,61 @@ func TestDaemonReloadLabels(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestDaemonReloadAllowNondistributableArtifacts(t *testing.T) {
|
||||
daemon := &Daemon{
|
||||
configStore: &config.Config{},
|
||||
}
|
||||
|
||||
// Initialize daemon with some registries.
|
||||
daemon.RegistryService = 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.
|
||||
},
|
||||
})
|
||||
|
||||
registries := []string{
|
||||
"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)
|
||||
}
|
||||
|
||||
actual := []string{}
|
||||
serviceConfig := daemon.RegistryService.ServiceConfig()
|
||||
for _, value := range serviceConfig.AllowNondistributableArtifactsCIDRs {
|
||||
actual = append(actual, value.String())
|
||||
}
|
||||
for _, value := range serviceConfig.AllowNondistributableArtifactsHostnames {
|
||||
actual = append(actual, value)
|
||||
}
|
||||
|
||||
sort.Strings(registries)
|
||||
sort.Strings(actual)
|
||||
if !reflect.DeepEqual(registries, actual) {
|
||||
t.Fatalf("expected %v, got %v\n", registries, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDaemonReloadMirrors(t *testing.T) {
|
||||
daemon := &Daemon{}
|
||||
daemon.RegistryService = registry.NewService(registry.ServiceOptions{
|
||||
|
|
|
@ -23,20 +23,28 @@ func (ld *v2LayerDescriptor) Descriptor() distribution.Descriptor {
|
|||
}
|
||||
|
||||
func (ld *v2LayerDescriptor) open(ctx context.Context) (distribution.ReadSeekCloser, error) {
|
||||
blobs := ld.repo.Blobs(ctx)
|
||||
rsc, err := blobs.Open(ctx, ld.digest)
|
||||
|
||||
if len(ld.src.URLs) == 0 {
|
||||
blobs := ld.repo.Blobs(ctx)
|
||||
return blobs.Open(ctx, ld.digest)
|
||||
return rsc, err
|
||||
}
|
||||
|
||||
var (
|
||||
err error
|
||||
rsc distribution.ReadSeekCloser
|
||||
)
|
||||
// We're done if the registry has this blob.
|
||||
if err == nil {
|
||||
// Seek does an HTTP GET. If it succeeds, the blob really is accessible.
|
||||
if _, err = rsc.Seek(0, os.SEEK_SET); err == nil {
|
||||
return rsc, nil
|
||||
}
|
||||
rsc.Close()
|
||||
}
|
||||
|
||||
// Find the first URL that results in a 200 result code.
|
||||
for _, url := range ld.src.URLs {
|
||||
logrus.Debugf("Pulling %v from foreign URL %v", ld.digest, url)
|
||||
rsc = transport.NewHTTPReadSeeker(http.DefaultClient, url, nil)
|
||||
|
||||
// Seek does an HTTP GET. If it succeeds, the blob really is accessible.
|
||||
_, err = rsc.Seek(0, os.SEEK_SET)
|
||||
if err == nil {
|
||||
break
|
||||
|
|
|
@ -141,6 +141,7 @@ func (p *v2Pusher) pushV2Tag(ctx context.Context, ref reference.NamedTagged, id
|
|||
hmacKey: hmacKey,
|
||||
repoInfo: p.repoInfo.Name,
|
||||
ref: p.ref,
|
||||
endpoint: p.endpoint,
|
||||
repo: p.repo,
|
||||
pushState: &p.pushState,
|
||||
}
|
||||
|
@ -239,6 +240,7 @@ type v2PushDescriptor struct {
|
|||
hmacKey []byte
|
||||
repoInfo reference.Named
|
||||
ref reference.Named
|
||||
endpoint registry.APIEndpoint
|
||||
repo distribution.Repository
|
||||
pushState *pushState
|
||||
remoteDescriptor distribution.Descriptor
|
||||
|
@ -259,10 +261,13 @@ func (pd *v2PushDescriptor) DiffID() layer.DiffID {
|
|||
}
|
||||
|
||||
func (pd *v2PushDescriptor) Upload(ctx context.Context, progressOutput progress.Output) (distribution.Descriptor, error) {
|
||||
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
|
||||
// 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ A self-sufficient runtime for containers.
|
|||
|
||||
Options:
|
||||
--add-runtime runtime Register an additional OCI compatible runtime (default [])
|
||||
--allow-nondistributable-artifacts list Push nondistributable artifacts to specified registries (default [])
|
||||
--api-cors-header string Set CORS headers in the Engine API
|
||||
--authorization-plugin list Authorization plugins to load (default [])
|
||||
--bip string Specify network bridge IP
|
||||
|
@ -828,6 +829,32 @@ To set the DNS search domain for all Docker containers, use:
|
|||
$ sudo dockerd --dns-search example.com
|
||||
```
|
||||
|
||||
#### Allow push of nondistributable artifacts
|
||||
|
||||
Some images (e.g., Windows base images) contain artifacts whose distribution is
|
||||
restricted by license. When these images are pushed to a registry, restricted
|
||||
artifacts are not included.
|
||||
|
||||
To override this behavior for specific registries, use the
|
||||
`--allow-nondistributable-artifacts` option in one of the following forms:
|
||||
|
||||
* `--allow-nondistributable-artifacts myregistry:5000` tells the Docker daemon
|
||||
to push nondistributable artifacts to myregistry:5000.
|
||||
* `--allow-nondistributable-artifacts 10.1.0.0/16` tells the Docker daemon to
|
||||
push nondistributable artifacts to all registries whose resolved IP address
|
||||
is within the subnet described by the CIDR syntax.
|
||||
|
||||
This option can be used multiple times.
|
||||
|
||||
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.
|
||||
|
||||
#### Insecure registries
|
||||
|
||||
Docker considers a private registry either secure or insecure. In the rest of
|
||||
|
@ -1261,6 +1288,7 @@ This is a full example of the allowed configuration options on Linux:
|
|||
"default-gateway-v6": "",
|
||||
"icc": false,
|
||||
"raw-logs": false,
|
||||
"allow-nondistributable-artifacts": [],
|
||||
"registry-mirrors": [],
|
||||
"seccomp-profile": "",
|
||||
"insecure-registries": [],
|
||||
|
@ -1330,6 +1358,7 @@ This is a full example of the allowed configuration options on Windows:
|
|||
"bridge": "",
|
||||
"fixed-cidr": "",
|
||||
"raw-logs": false,
|
||||
"allow-nondistributable-artifacts": [],
|
||||
"registry-mirrors": [],
|
||||
"insecure-registries": [],
|
||||
"disable-legacy-registry": false
|
||||
|
@ -1361,6 +1390,7 @@ The list of currently supported options that can be reconfigured is this:
|
|||
- `runtimes`: it updates the list of available OCI runtimes that can
|
||||
be used to run containers
|
||||
- `authorization-plugin`: specifies the authorization plugins to use.
|
||||
- `allow-nondistributable-artifacts`: Replaces the set of registries to which the daemon will push nondistributable artifacts with a new set of registries.
|
||||
- `insecure-registries`: it replaces the daemon insecure registries with a new set of insecure registries. If some existing insecure registries in daemon's configuration are not in newly reloaded insecure resgitries, these existing ones will be removed from daemon's config.
|
||||
- `registry-mirrors`: it replaces the daemon registry mirrors with a new set of registry mirrors. If some existing registry mirrors in daemon's configuration are not in newly reloaded registry mirrors, these existing ones will be removed from daemon's config.
|
||||
|
||||
|
|
|
@ -428,7 +428,7 @@ func (s *DockerDaemonSuite) TestDaemonEvents(c *check.C) {
|
|||
out, err = s.d.Cmd("events", "--since=0", "--until", daemonUnixTime(c))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
c.Assert(out, checker.Contains, fmt.Sprintf("daemon reload %s (cluster-advertise=, cluster-store=, cluster-store-opts={}, debug=true, default-runtime=runc, default-shm-size=67108864, insecure-registries=[], labels=[\"bar=foo\"], live-restore=false, max-concurrent-downloads=1, max-concurrent-uploads=5, name=%s, registry-mirrors=[], runtimes=runc:{docker-runc []}, shutdown-timeout=10)", daemonID, daemonName))
|
||||
c.Assert(out, checker.Contains, fmt.Sprintf("daemon reload %s (allow-nondistributable-artifacts=[], cluster-advertise=, cluster-store=, cluster-store-opts={}, debug=true, default-runtime=runc, default-shm-size=67108864, insecure-registries=[], labels=[\"bar=foo\"], live-restore=false, max-concurrent-downloads=1, max-concurrent-uploads=5, name=%s, registry-mirrors=[], runtimes=runc:{docker-runc []}, shutdown-timeout=10)", daemonID, daemonName))
|
||||
}
|
||||
|
||||
func (s *DockerDaemonSuite) TestDaemonEventsWithFilters(c *check.C) {
|
||||
|
|
|
@ -7,6 +7,7 @@ dockerd - Enable daemon mode
|
|||
# SYNOPSIS
|
||||
**dockerd**
|
||||
[**--add-runtime**[=*[]*]]
|
||||
[**--allow-nondistributable-artifacts**[=*[]*]]
|
||||
[**--api-cors-header**=[=*API-CORS-HEADER*]]
|
||||
[**--authorization-plugin**[=*[]*]]
|
||||
[**-b**|**--bridge**[=*BRIDGE*]]
|
||||
|
@ -116,6 +117,20 @@ $ sudo dockerd --add-runtime runc=runc --add-runtime custom=/usr/local/bin/my-ru
|
|||
|
||||
**Note**: defining runtime arguments via the command line is not supported.
|
||||
|
||||
**--allow-nondistributable-artifacts**=[]
|
||||
Push nondistributable artifacts to the specified registries.
|
||||
|
||||
List can contain elements with CIDR notation to specify a whole subnet.
|
||||
|
||||
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.
|
||||
|
||||
**--api-cors-header**=""
|
||||
Set CORS headers in the Engine API. Default is cors disabled. Give urls like
|
||||
"http://foo, http://bar, ...". Give "*" to allow all.
|
||||
|
|
|
@ -18,8 +18,9 @@ import (
|
|||
|
||||
// ServiceOptions holds command line options.
|
||||
type ServiceOptions struct {
|
||||
Mirrors []string `json:"registry-mirrors,omitempty"`
|
||||
InsecureRegistries []string `json:"insecure-registries,omitempty"`
|
||||
AllowNondistributableArtifacts []string `json:"allow-nondistributable-artifacts,omitempty"`
|
||||
Mirrors []string `json:"registry-mirrors,omitempty"`
|
||||
InsecureRegistries []string `json:"insecure-registries,omitempty"`
|
||||
|
||||
// V2Only controls access to legacy registries. If it is set to true via the
|
||||
// command line flag the daemon will not attempt to contact v1 legacy registries
|
||||
|
@ -74,9 +75,11 @@ var lookupIP = net.LookupIP
|
|||
// InstallCliFlags adds command-line options to the top-level flag parser for
|
||||
// the current process.
|
||||
func (options *ServiceOptions) InstallCliFlags(flags *pflag.FlagSet) {
|
||||
ana := opts.NewNamedListOptsRef("allow-nondistributable-artifacts", &options.AllowNondistributableArtifacts, ValidateIndexName)
|
||||
mirrors := opts.NewNamedListOptsRef("registry-mirrors", &options.Mirrors, ValidateMirror)
|
||||
insecureRegistries := opts.NewNamedListOptsRef("insecure-registries", &options.InsecureRegistries, ValidateIndexName)
|
||||
|
||||
flags.Var(ana, "allow-nondistributable-artifacts", "Allow push of nondistributable artifacts to registry")
|
||||
flags.Var(mirrors, "registry-mirror", "Preferred Docker registry mirror")
|
||||
flags.Var(insecureRegistries, "insecure-registry", "Enable insecure registry communication")
|
||||
|
||||
|
@ -95,12 +98,50 @@ func newServiceConfig(options ServiceOptions) *serviceConfig {
|
|||
V2Only: options.V2Only,
|
||||
}
|
||||
|
||||
config.LoadAllowNondistributableArtifacts(options.AllowNondistributableArtifacts)
|
||||
config.LoadMirrors(options.Mirrors)
|
||||
config.LoadInsecureRegistries(options.InsecureRegistries)
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
// LoadAllowNondistributableArtifacts loads allow-nondistributable-artifacts registries into config.
|
||||
func (config *serviceConfig) LoadAllowNondistributableArtifacts(registries []string) error {
|
||||
cidrs := map[string]*registrytypes.NetIPNet{}
|
||||
hostnames := map[string]bool{}
|
||||
|
||||
for _, r := range registries {
|
||||
if _, err := ValidateIndexName(r); err != nil {
|
||||
return err
|
||||
}
|
||||
if validateNoScheme(r) != nil {
|
||||
return fmt.Errorf("allow-nondistributable-artifacts registry %s should not contain '://'", r)
|
||||
}
|
||||
|
||||
if _, ipnet, err := net.ParseCIDR(r); err == nil {
|
||||
// Valid CIDR.
|
||||
cidrs[ipnet.String()] = (*registrytypes.NetIPNet)(ipnet)
|
||||
} else if err := validateHostPort(r); err == nil {
|
||||
// Must be `host:port` if not CIDR.
|
||||
hostnames[r] = true
|
||||
} else {
|
||||
return fmt.Errorf("allow-nondistributable-artifacts registry %s is not valid: %v", r, err)
|
||||
}
|
||||
}
|
||||
|
||||
config.AllowNondistributableArtifactsCIDRs = make([]*(registrytypes.NetIPNet), 0)
|
||||
for _, c := range cidrs {
|
||||
config.AllowNondistributableArtifactsCIDRs = append(config.AllowNondistributableArtifactsCIDRs, c)
|
||||
}
|
||||
|
||||
config.AllowNondistributableArtifactsHostnames = make([]string, 0)
|
||||
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 {
|
||||
|
@ -211,6 +252,25 @@ skip:
|
|||
return nil
|
||||
}
|
||||
|
||||
// allowNondistributableArtifacts returns true if the provided hostname is part of the list of regsitries
|
||||
// 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 allowNondistributableArtifacts(config *serviceConfig, 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.
|
||||
//
|
||||
|
@ -229,10 +289,17 @@ func isSecureIndex(config *serviceConfig, indexName string) bool {
|
|||
return index.Secure
|
||||
}
|
||||
|
||||
host, _, err := net.SplitHostPort(indexName)
|
||||
return !isCIDRMatch(config.InsecureRegistryCIDRs, indexName)
|
||||
}
|
||||
|
||||
// isCIDRMatch returns true if URLHost matches an element of cidrs. URLHost is 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, false is returned.
|
||||
func isCIDRMatch(cidrs []*registrytypes.NetIPNet, URLHost string) bool {
|
||||
host, _, err := net.SplitHostPort(URLHost)
|
||||
if err != nil {
|
||||
// assume indexName is of the form `host` without the port and go on.
|
||||
host = indexName
|
||||
// Assume URLHost is of the form `host` without the port and go on.
|
||||
host = URLHost
|
||||
}
|
||||
|
||||
addrs, err := lookupIP(host)
|
||||
|
@ -249,15 +316,15 @@ func isSecureIndex(config *serviceConfig, indexName string) bool {
|
|||
|
||||
// Try CIDR notation only if addrs has any elements, i.e. if `host`'s IP could be determined.
|
||||
for _, addr := range addrs {
|
||||
for _, ipnet := range config.InsecureRegistryCIDRs {
|
||||
for _, ipnet := range cidrs {
|
||||
// check if the addr falls in the subnet
|
||||
if (*net.IPNet)(ipnet).Contains(addr) {
|
||||
return false
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
return false
|
||||
}
|
||||
|
||||
// ValidateMirror validates an HTTP(S) registry mirror
|
||||
|
|
|
@ -1,10 +1,129 @@
|
|||
package registry
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
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://mytest.com"},
|
||||
err: "allow-nondistributable-artifacts registry http://mytest.com should not contain '://'",
|
||||
},
|
||||
{
|
||||
registries: []string{"https://mytest.com"},
|
||||
err: "allow-nondistributable-artifacts registry https://mytest.com should not contain '://'",
|
||||
},
|
||||
{
|
||||
registries: []string{"HTTP://mytest.com"},
|
||||
err: "allow-nondistributable-artifacts registry HTTP://mytest.com should not contain '://'",
|
||||
},
|
||||
{
|
||||
registries: []string{"svn://mytest.com"},
|
||||
err: "allow-nondistributable-artifacts registry svn://mytest.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{`mytest.com:500000`},
|
||||
err: `allow-nondistributable-artifacts registry mytest.com:500000 is not valid: invalid port "500000"`,
|
||||
},
|
||||
{
|
||||
registries: []string{`"mytest.com"`},
|
||||
err: `allow-nondistributable-artifacts registry "mytest.com" is not valid: invalid host "\"mytest.com\""`,
|
||||
},
|
||||
{
|
||||
registries: []string{`"mytest.com:5000"`},
|
||||
err: `allow-nondistributable-artifacts registry "mytest.com:5000" is not valid: invalid host "\"mytest.com"`,
|
||||
},
|
||||
}
|
||||
for _, testCase := range testCases {
|
||||
config := newServiceConfig(ServiceOptions{})
|
||||
err := config.LoadAllowNondistributableArtifacts(testCase.registries)
|
||||
if testCase.err == "" {
|
||||
if err != nil {
|
||||
t.Fatalf("expect no error, got '%s'", err)
|
||||
}
|
||||
|
||||
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.com",
|
||||
|
|
|
@ -811,6 +811,48 @@ func TestAddRequiredHeadersToRedirectedRequests(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestAllowNondistributableArtifacts(t *testing.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.domain.com", []string{"42.42.0.0/16"}, false},
|
||||
{"invalid.domain.com", []string{"invalid.domain.com"}, true},
|
||||
{"invalid.domain.com:5000", []string{"invalid.domain.com"}, false},
|
||||
{"invalid.domain.com:5000", []string{"invalid.domain.com:5000"}, true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
config := newServiceConfig(ServiceOptions{
|
||||
AllowNondistributableArtifacts: tt.registries,
|
||||
})
|
||||
if v := allowNondistributableArtifacts(config, 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) {
|
||||
tests := []struct {
|
||||
addr string
|
||||
|
|
|
@ -31,6 +31,7 @@ type Service interface {
|
|||
Search(ctx context.Context, term string, limit int, authConfig *types.AuthConfig, userAgent string, headers map[string][]string) (*registrytypes.SearchResults, error)
|
||||
ServiceConfig() *registrytypes.ServiceConfig
|
||||
TLSConfig(hostname string) (*tls.Config, error)
|
||||
LoadAllowNondistributableArtifacts([]string) error
|
||||
LoadMirrors([]string) error
|
||||
LoadInsecureRegistries([]string) error
|
||||
}
|
||||
|
@ -56,13 +57,17 @@ func (s *DefaultService) ServiceConfig() *registrytypes.ServiceConfig {
|
|||
defer s.mu.Unlock()
|
||||
|
||||
servConfig := registrytypes.ServiceConfig{
|
||||
InsecureRegistryCIDRs: make([]*(registrytypes.NetIPNet), 0),
|
||||
IndexConfigs: make(map[string]*(registrytypes.IndexInfo)),
|
||||
Mirrors: make([]string, 0),
|
||||
AllowNondistributableArtifactsCIDRs: make([]*(registrytypes.NetIPNet), 0),
|
||||
AllowNondistributableArtifactsHostnames: make([]string, 0),
|
||||
InsecureRegistryCIDRs: make([]*(registrytypes.NetIPNet), 0),
|
||||
IndexConfigs: make(map[string]*(registrytypes.IndexInfo)),
|
||||
Mirrors: make([]string, 0),
|
||||
}
|
||||
|
||||
// construct a new ServiceConfig which will not retrieve s.Config directly,
|
||||
// and look up items in s.config with mu locked
|
||||
servConfig.AllowNondistributableArtifactsCIDRs = append(servConfig.AllowNondistributableArtifactsCIDRs, s.config.ServiceConfig.AllowNondistributableArtifactsCIDRs...)
|
||||
servConfig.AllowNondistributableArtifactsHostnames = append(servConfig.AllowNondistributableArtifactsHostnames, s.config.ServiceConfig.AllowNondistributableArtifactsHostnames...)
|
||||
servConfig.InsecureRegistryCIDRs = append(servConfig.InsecureRegistryCIDRs, s.config.ServiceConfig.InsecureRegistryCIDRs...)
|
||||
|
||||
for key, value := range s.config.ServiceConfig.IndexConfigs {
|
||||
|
@ -74,6 +79,14 @@ func (s *DefaultService) ServiceConfig() *registrytypes.ServiceConfig {
|
|||
return &servConfig
|
||||
}
|
||||
|
||||
// LoadAllowNondistributableArtifacts loads allow-nondistributable-artifacts registries for Service.
|
||||
func (s *DefaultService) LoadAllowNondistributableArtifacts(registries []string) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
return s.config.LoadAllowNondistributableArtifacts(registries)
|
||||
}
|
||||
|
||||
// LoadMirrors loads registry mirrors for Service
|
||||
func (s *DefaultService) LoadMirrors(mirrors []string) error {
|
||||
s.mu.Lock()
|
||||
|
@ -235,12 +248,13 @@ func (s *DefaultService) ResolveRepository(name reference.Named) (*RepositoryInf
|
|||
|
||||
// APIEndpoint represents a remote API endpoint
|
||||
type APIEndpoint struct {
|
||||
Mirror bool
|
||||
URL *url.URL
|
||||
Version APIVersion
|
||||
Official bool
|
||||
TrimHostname bool
|
||||
TLSConfig *tls.Config
|
||||
Mirror bool
|
||||
URL *url.URL
|
||||
Version APIVersion
|
||||
AllowNondistributableArtifacts bool
|
||||
Official bool
|
||||
TrimHostname bool
|
||||
TLSConfig *tls.Config
|
||||
}
|
||||
|
||||
// ToV1Endpoint returns a V1 API endpoint based on the APIEndpoint
|
||||
|
|
|
@ -44,6 +44,8 @@ func (s *DefaultService) lookupV2Endpoints(hostname string) (endpoints []APIEndp
|
|||
return endpoints, nil
|
||||
}
|
||||
|
||||
ana := allowNondistributableArtifacts(s.config, hostname)
|
||||
|
||||
tlsConfig, err = s.tlsConfig(hostname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -55,9 +57,10 @@ func (s *DefaultService) lookupV2Endpoints(hostname string) (endpoints []APIEndp
|
|||
Scheme: "https",
|
||||
Host: hostname,
|
||||
},
|
||||
Version: APIVersion2,
|
||||
TrimHostname: true,
|
||||
TLSConfig: tlsConfig,
|
||||
Version: APIVersion2,
|
||||
AllowNondistributableArtifacts: ana,
|
||||
TrimHostname: true,
|
||||
TLSConfig: tlsConfig,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -67,8 +70,9 @@ func (s *DefaultService) lookupV2Endpoints(hostname string) (endpoints []APIEndp
|
|||
Scheme: "http",
|
||||
Host: hostname,
|
||||
},
|
||||
Version: APIVersion2,
|
||||
TrimHostname: true,
|
||||
Version: APIVersion2,
|
||||
AllowNondistributableArtifacts: ana,
|
||||
TrimHostname: true,
|
||||
// used to check if supposed to be secure via InsecureSkipVerify
|
||||
TLSConfig: tlsConfig,
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue