From 6d8269ba49974d3624ceac6b32015294e397f621 Mon Sep 17 00:00:00 2001 From: Michal Minar Date: Tue, 20 Jan 2015 09:55:36 -0500 Subject: [PATCH 1/5] Add --add-registry and --block-registry options to docker daemon The first option allows to prepend additional registries to a public one. These will be queried one after another when pulling images. The last registry given will be searched as first. For example: --add-registry=foo.com --add-registry=bar.com Would cause a docker pull myapp to search bar.com/myapp foo.com/myapp docker.io/myapp The second option builds a list of banned registries. Docker daemon will prevent user from any communication with such registries. This option recognizes a special keyword "public" denoting public Docker registry. So for example adding --block-registry=public to flags of previous example would result in searching the same registries except for `docker.io/myapp` This would allow companies and vendors to specify registries which contain content that the companies will not allowed to be stored at docker.io. Docker-DCO-1.1-Signed-off-by: Dan Walsh (github: rhatdan) Signed-off-by: Michal Minar --- contrib/completion/bash/docker | 2 ++ contrib/completion/fish/docker.fish | 4 ++-- docker/daemon.go | 14 ++++++------ docs/man/docker.1.md | 12 +++++------ graph/pull.go | 2 ++ registry/config.go | 33 ++++++++++++++++++++++++----- registry/service.go | 5 +++++ 7 files changed, 53 insertions(+), 19 deletions(-) diff --git a/contrib/completion/bash/docker b/contrib/completion/bash/docker index 4891194bd8672..0f208186d36f9 100755 --- a/contrib/completion/bash/docker +++ b/contrib/completion/bash/docker @@ -895,7 +895,9 @@ _docker() { ) local main_options_with_args=" + --add-registry --bip + --block-registry --bridge -b --dns --dns-search diff --git a/contrib/completion/fish/docker.fish b/contrib/completion/fish/docker.fish index 0af1169204093..0b68706dbe7e2 100644 --- a/contrib/completion/fish/docker.fish +++ b/contrib/completion/fish/docker.fish @@ -43,9 +43,11 @@ function __fish_print_docker_repositories --description 'Print a list of docker end # common options +complete -c docker -f -n '__fish_docker_no_subcommand' -l add-registry -d "Each given registry will be prepended to a list of registries queried during image pulls or searches. The last registry given will be queried first. They will be treated as insecure." complete -c docker -f -n '__fish_docker_no_subcommand' -l api-enable-cors -d 'Enable CORS headers in the remote API' complete -c docker -f -n '__fish_docker_no_subcommand' -s b -l bridge -d 'Attach containers to a pre-existing network bridge' complete -c docker -f -n '__fish_docker_no_subcommand' -l bip -d "Use this CIDR notation address for the network bridge's IP, not compatible with -b" +complete -c docker -f -n '__fish_docker_no_subcommand' -l block-registry -d 'Prevent docker daemon from contacting specified registries. Special keyword "public" represents public Docker registry.' complete -c docker -f -n '__fish_docker_no_subcommand' -s D -l debug -d 'Enable debug mode' complete -c docker -f -n '__fish_docker_no_subcommand' -s d -l daemon -d 'Enable daemon mode' complete -c docker -f -n '__fish_docker_no_subcommand' -l dns -d 'Force Docker to use specific DNS servers' @@ -64,8 +66,6 @@ complete -c docker -f -n '__fish_docker_no_subcommand' -l iptables -d "Enable Do complete -c docker -f -n '__fish_docker_no_subcommand' -l mtu -d 'Set the containers network MTU' complete -c docker -f -n '__fish_docker_no_subcommand' -s p -l pidfile -d 'Path to use for daemon PID file' complete -c docker -f -n '__fish_docker_no_subcommand' -l registry-mirror -d "Specify a preferred Docker registry mirror for pulls from official registry" -complete -c docker -f -n '__fish_docker_no_subcommand' -l registry-replace -d "Registry that shall replace official registry and index. Registry is expected to be insecure." -complete -c docker -f -n '__fish_docker_no_subcommand' -l registry-prepend -d "Each given registry will be prepended to a list of registries queried during image pulls or searches. The last registry given will be queried first. They will be treated as insecure." complete -c docker -f -n '__fish_docker_no_subcommand' -s s -l storage-driver -d 'Force the Docker runtime to use a specific storage driver' complete -c docker -f -n '__fish_docker_no_subcommand' -l selinux-enabled -d 'Enable selinux support. SELinux does not presently support the BTRFS storage driver' complete -c docker -f -n '__fish_docker_no_subcommand' -l storage-opt -d 'Set storage driver options' diff --git a/docker/daemon.go b/docker/daemon.go index f4f15e7aed1ee..376127b179799 100644 --- a/docker/daemon.go +++ b/docker/daemon.go @@ -80,16 +80,18 @@ func mainDaemon() { return } - if *registryCfg.DefaultRegistry != "" { - defaultRegistry, err := registry.ValidateIndexName(*registryCfg.DefaultRegistry) - if err != nil { - log.Fatal("Given invalid default registry \"%s\": %s", *registryCfg.DefaultRegistry, err.Error()) + for _, r := range registryCfg.BlockedRegistries.GetAll() { + if r == "public" { + r = registry.INDEXNAME + } + registry.BlockedRegistries[r] = struct{}{} + if r == registry.INDEXNAME { + registry.RegistryList = []string{} } - registry.RegistryList[0] = defaultRegistry } for _, r := range registryCfg.AdditionalRegistries.GetAll() { - if r != "" { + if _, ok := registry.BlockedRegistries[r]; !ok { registry.RegistryList = append([]string{r}, registry.RegistryList...) } } diff --git a/docs/man/docker.1.md b/docs/man/docker.1.md index ec6d32d5e5913..e398068f5fb1b 100644 --- a/docs/man/docker.1.md +++ b/docs/man/docker.1.md @@ -34,6 +34,9 @@ unix://[/path/to/socket] to use. The socket(s) to bind to in daemon mode specified using one or more tcp://host:port, unix:///path/to/socket, fd://* or fd://socketfd. +**--add-registry**=[] + Each given registry will be prepended to a list of registries queried during image pulls or searches. The last registry given will be queried first. They will be treated as insecure. Registry mirrors won't apply to them. + **--api-enable-cors**=*true*|*false* Enable CORS headers in the remote API. Default is false. @@ -43,6 +46,9 @@ unix://[/path/to/socket] to use. **--bip**="" Use the provided CIDR notation address for the dynamically created bridge (docker0); Mutually exclusive of \-b +**--block-registry**=[] + Prevent Docker daemon from contacting specified registries. Special keyword "public" represents public Docker registry. + **-d**=*true*|*false* Enable daemon mode. Default is false. @@ -91,12 +97,6 @@ unix://[/path/to/socket] to use. **--registry-mirror**=:// Prepend a registry mirror to be used for image pulls from public Docker registry. May be specified multiple times. -**--registry-prepend**=[] - Each given registry will be prepended to a list of registries queried during image pulls or searches. The last registry given will be queried first. They will be treated as insecure. Registry mirrors won't apply to them. - -**--registry-replace**="" - Registry that shall replace official Docker registry and index (e.g. 10.172.10.2:5000, private-registry.foo.bar). Additional registries added with --registry-prepend will be queried before this one. Use this option if you do not want to query official registry at all. It will be treated as insecure. Registry mirrors won't apply to given registry. - **-s**="" Force the Docker runtime to use a specific storage driver. diff --git a/graph/pull.go b/graph/pull.go index 65cb5eaf789ba..1f29e9afe30bb 100644 --- a/graph/pull.go +++ b/graph/pull.go @@ -28,6 +28,8 @@ func (s *TagStore) CmdRegistryPull(job *engine.Job) engine.Status { // the matching image is found. if registry.RepositoryNameHasIndex(tmp) { registries = []string{""} + } else if len(registries) == 0 { + return job.Errorf("No configured registry to pull from.") } for i, r := range registries { if i > 0 { diff --git a/registry/config.go b/registry/config.go index dba3a51c129c8..2447d889b04b5 100644 --- a/registry/config.go +++ b/registry/config.go @@ -18,7 +18,7 @@ import ( type Options struct { Mirrors opts.ListOpts InsecureRegistries opts.ListOpts - DefaultRegistry *string + BlockedRegistries opts.ListOpts AdditionalRegistries opts.ListOpts } @@ -33,6 +33,8 @@ const ( ) var ( + // A set of blocked registries + BlockedRegistries map[string]struct{} // List of registries to query. RegistryList = []string{INDEXNAME} ErrInvalidRepositoryName = errors.New("Invalid repository name (ex: \"registry.domain.tld/myrepos\")") @@ -41,8 +43,15 @@ var ( validRepo = regexp.MustCompile(`^([a-z0-9-_.]+)$`) ) +func init() { + BlockedRegistries = make(map[string]struct{}) +} + // IndexServerName returns the name of default index server. func IndexServerName() string { + if len(RegistryList) < 1 { + return "" + } return RegistryList[0] } @@ -53,6 +62,8 @@ func IndexServerAddress(indexName string) string { return INDEXSERVER } else if indexName != "" { return fmt.Sprintf("http://%s/v1/", indexName) + } else if IndexServerName() == "" { + return "" } else { return fmt.Sprintf("http://%s/v1/", IndexServerName()) } @@ -65,6 +76,8 @@ func RegistryServerAddress(indexName string) string { return REGISTRYSERVER } else if indexName != "" { return fmt.Sprintf("http://%s/v2/", indexName) + } else if IndexServerName() == "" { + return "" } else { return fmt.Sprintf("http://%s/v2/", IndexServerName()) } @@ -77,9 +90,10 @@ func (options *Options) InstallFlags() { flag.Var(&options.Mirrors, []string{"-registry-mirror"}, "Specify a preferred Docker registry mirror for pulls from official registry") options.InsecureRegistries = opts.NewListOpts(ValidateIndexName) flag.Var(&options.InsecureRegistries, []string{"-insecure-registry"}, "Enable insecure communication with specified registries (no certificate verification for HTTPS and enable HTTP fallback) (e.g., localhost:5000 or 10.20.0.0/16)") - options.DefaultRegistry = flag.String([]string{"-registry-replace"}, "", "Registry that shall replace official registry and index. It will be treated as insecure.") + options.BlockedRegistries = opts.NewListOpts(ValidateIndexName) + flag.Var(&options.BlockedRegistries, []string{"-block-registry"}, "Prevent Docker daemon from contacting specified registries. Special keyword \"public\" represents public Docker registry.") options.AdditionalRegistries = opts.NewListOpts(ValidateIndexName) - flag.Var(&options.AdditionalRegistries, []string{"-registry-prepend"}, "Each given registry will be prepended to a list of registries queried during image pulls or searches. The last registry given will be queried first. They will be treated as insecure.") + flag.Var(&options.AdditionalRegistries, []string{"-add-registry"}, "Each given registry will be prepended to a list of registries queried during image pulls or searches. The last registry given will be queried first. They will be treated as insecure.") } type netIPNet net.IPNet @@ -234,8 +248,13 @@ func ValidateMirror(val string) (string, error) { // ValidateIndexName validates an index name. func ValidateIndexName(val string) (string, error) { // 'index.docker.io' => 'docker.io' - if val == "index."+IndexServerName() { - val = IndexServerName() + if val == "index."+INDEXNAME { + val = INDEXNAME + } + for _, r := range RegistryList { + if val == "index."+r { + val = r + } } // *TODO: Check if valid hostname[:port]/ip[:port]? return val, nil @@ -373,6 +392,10 @@ func (config *ServiceConfig) NewRepositoryInfo(reposName string) (*RepositoryInf RemoteName: remoteName, } + if _, ok := BlockedRegistries[indexName]; ok { + return nil, fmt.Errorf("Blocked registry \"%s\"", indexName) + } + var err error repoInfo.Index, err = config.NewIndexInfo(indexName) if err != nil { diff --git a/registry/service.go b/registry/service.go index 88609d772ccc4..f282c0127c725 100644 --- a/registry/service.go +++ b/registry/service.go @@ -55,6 +55,9 @@ func (s *Service) Auth(job *engine.Job) engine.Status { // Use the official registry address if not specified. addr = IndexServerAddress("") } + if addr == "" { + return job.Errorf("No configured registry to authenticate to.") + } if index, err = ResolveIndexInfo(job, addr); err != nil { return job.Error(err) @@ -136,6 +139,8 @@ func (s *Service) Search(job *engine.Job) engine.Status { if err := doSearch(term); err != nil { return job.Error(err) } + } else if len(RegistryList) < 1 { + return job.Errorf("No configured repository to search.") } else { var ( err error From 01af9f8a8551fcf9c0d429a3a0d2e2f1af55fde5 Mon Sep 17 00:00:00 2001 From: Michal Minar Date: Thu, 29 Jan 2015 09:12:46 +0100 Subject: [PATCH 2/5] Additional registries shall be searched in the order given Switch to FIFO policy for --add-registry to match the behaviour of --registry-mirror. Signed-off-by: Michal Minar --- contrib/completion/fish/docker.fish | 2 +- docker/daemon.go | 4 +++- docs/man/docker.1.md | 2 +- registry/config.go | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/contrib/completion/fish/docker.fish b/contrib/completion/fish/docker.fish index 0b68706dbe7e2..775b4383f22d0 100644 --- a/contrib/completion/fish/docker.fish +++ b/contrib/completion/fish/docker.fish @@ -43,7 +43,7 @@ function __fish_print_docker_repositories --description 'Print a list of docker end # common options -complete -c docker -f -n '__fish_docker_no_subcommand' -l add-registry -d "Each given registry will be prepended to a list of registries queried during image pulls or searches. The last registry given will be queried first. They will be treated as insecure." +complete -c docker -f -n '__fish_docker_no_subcommand' -l add-registry -d "Each given registry will be queried before a public Docker registry during image pulls or searches. They will be searched in the order given and treated as insecure." complete -c docker -f -n '__fish_docker_no_subcommand' -l api-enable-cors -d 'Enable CORS headers in the remote API' complete -c docker -f -n '__fish_docker_no_subcommand' -s b -l bridge -d 'Attach containers to a pre-existing network bridge' complete -c docker -f -n '__fish_docker_no_subcommand' -l bip -d "Use this CIDR notation address for the network bridge's IP, not compatible with -b" diff --git a/docker/daemon.go b/docker/daemon.go index 376127b179799..64a33ea870e79 100644 --- a/docker/daemon.go +++ b/docker/daemon.go @@ -90,11 +90,13 @@ func mainDaemon() { } } + newRegistryList := []string{} for _, r := range registryCfg.AdditionalRegistries.GetAll() { if _, ok := registry.BlockedRegistries[r]; !ok { - registry.RegistryList = append([]string{r}, registry.RegistryList...) + newRegistryList = append(newRegistryList, r) } } + registry.RegistryList = append(newRegistryList, registry.RegistryList...) eng := engine.New() signal.Trap(eng.Shutdown) diff --git a/docs/man/docker.1.md b/docs/man/docker.1.md index e398068f5fb1b..41b07ca13816d 100644 --- a/docs/man/docker.1.md +++ b/docs/man/docker.1.md @@ -35,7 +35,7 @@ unix://[/path/to/socket] to use. tcp://host:port, unix:///path/to/socket, fd://* or fd://socketfd. **--add-registry**=[] - Each given registry will be prepended to a list of registries queried during image pulls or searches. The last registry given will be queried first. They will be treated as insecure. Registry mirrors won't apply to them. + Each given registry will be queried before a public Docker registry during image pulls or searches. They will be searched in the order given and treated as insecure. Registry mirrors won't apply to them. **--api-enable-cors**=*true*|*false* Enable CORS headers in the remote API. Default is false. diff --git a/registry/config.go b/registry/config.go index 2447d889b04b5..ceded865c0c0b 100644 --- a/registry/config.go +++ b/registry/config.go @@ -93,7 +93,7 @@ func (options *Options) InstallFlags() { options.BlockedRegistries = opts.NewListOpts(ValidateIndexName) flag.Var(&options.BlockedRegistries, []string{"-block-registry"}, "Prevent Docker daemon from contacting specified registries. Special keyword \"public\" represents public Docker registry.") options.AdditionalRegistries = opts.NewListOpts(ValidateIndexName) - flag.Var(&options.AdditionalRegistries, []string{"-add-registry"}, "Each given registry will be prepended to a list of registries queried during image pulls or searches. The last registry given will be queried first. They will be treated as insecure.") + flag.Var(&options.AdditionalRegistries, []string{"-add-registry"}, "Each given registry will be queried before a public Docker registry during image pulls or searches. They will be searched in the order given and treated as insecure.") } type netIPNet net.IPNet From 6a8eb880467f0bef4fa7523cd7627803515bacba Mon Sep 17 00:00:00 2001 From: Michal Minar Date: Thu, 29 Jan 2015 16:33:01 +0100 Subject: [PATCH 3/5] Move --(add|block)-registry options to daemon side These options shall be recognized by Docker daemon only. Spit out an error if user expects the client to handle them. Signed-off-by: Michal Minar --- daemon/config.go | 7 +++++++ docker/daemon.go | 4 ++-- docker/docker.go | 6 ++++++ registry/config.go | 10 ++-------- 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/daemon/config.go b/daemon/config.go index 99e5ce4c8b719..c7aaafa5cc9a6 100644 --- a/daemon/config.go +++ b/daemon/config.go @@ -6,6 +6,7 @@ import ( "github.com/docker/docker/daemon/networkdriver" "github.com/docker/docker/opts" flag "github.com/docker/docker/pkg/mflag" + "github.com/docker/docker/registry" ) const ( @@ -42,6 +43,8 @@ type Config struct { Context map[string][]string TrustKeyPath string Labels []string + BlockedRegistries opts.ListOpts + AdditionalRegistries opts.ListOpts } // InstallFlags adds command-line options to the top-level flag parser for @@ -71,6 +74,10 @@ func (config *Config) InstallFlags() { opts.IPListVar(&config.Dns, []string{"#dns", "-dns"}, "Force Docker to use specific DNS servers") opts.DnsSearchListVar(&config.DnsSearch, []string{"-dns-search"}, "Force Docker to use specific DNS search domains") opts.LabelListVar(&config.Labels, []string{"-label"}, "Set key=value labels to the daemon (displayed in `docker info`)") + config.BlockedRegistries = opts.NewListOpts(registry.ValidateIndexName) + flag.Var(&config.BlockedRegistries, []string{"-block-registry"}, "Prevent Docker daemon from contacting specified registries. Special keyword \"public\" represents public Docker registry.") + config.AdditionalRegistries = opts.NewListOpts(registry.ValidateIndexName) + flag.Var(&config.AdditionalRegistries, []string{"-add-registry"}, "Each given registry will be queried before a public Docker registry during image pulls or searches. They will be searched in the order given and treated as insecure.") } func getDefaultNetworkMtu() int { diff --git a/docker/daemon.go b/docker/daemon.go index 64a33ea870e79..58224c6a02e0e 100644 --- a/docker/daemon.go +++ b/docker/daemon.go @@ -80,7 +80,7 @@ func mainDaemon() { return } - for _, r := range registryCfg.BlockedRegistries.GetAll() { + for _, r := range daemonCfg.BlockedRegistries.GetAll() { if r == "public" { r = registry.INDEXNAME } @@ -91,7 +91,7 @@ func mainDaemon() { } newRegistryList := []string{} - for _, r := range registryCfg.AdditionalRegistries.GetAll() { + for _, r := range daemonCfg.AdditionalRegistries.GetAll() { if _, ok := registry.BlockedRegistries[r]; !ok { newRegistryList = append(newRegistryList, r) } diff --git a/docker/docker.go b/docker/docker.go index 6410171fab1ee..6e26ea7f6c210 100644 --- a/docker/docker.go +++ b/docker/docker.go @@ -84,6 +84,12 @@ func main() { log.Fatal(err) } + for _, lopt := range []string{"-add-registry", "-block-registry"} { + if flag.IsSet(lopt) { + log.Fatalf("The -%s option is recognized only by Docker daemon.", lopt) + } + } + var ( cli *client.DockerCli tlsConfig tls.Config diff --git a/registry/config.go b/registry/config.go index ceded865c0c0b..555568c2d2fdc 100644 --- a/registry/config.go +++ b/registry/config.go @@ -16,10 +16,8 @@ import ( // Options holds command line options. type Options struct { - Mirrors opts.ListOpts - InsecureRegistries opts.ListOpts - BlockedRegistries opts.ListOpts - AdditionalRegistries opts.ListOpts + Mirrors opts.ListOpts + InsecureRegistries opts.ListOpts } const ( @@ -90,10 +88,6 @@ func (options *Options) InstallFlags() { flag.Var(&options.Mirrors, []string{"-registry-mirror"}, "Specify a preferred Docker registry mirror for pulls from official registry") options.InsecureRegistries = opts.NewListOpts(ValidateIndexName) flag.Var(&options.InsecureRegistries, []string{"-insecure-registry"}, "Enable insecure communication with specified registries (no certificate verification for HTTPS and enable HTTP fallback) (e.g., localhost:5000 or 10.20.0.0/16)") - options.BlockedRegistries = opts.NewListOpts(ValidateIndexName) - flag.Var(&options.BlockedRegistries, []string{"-block-registry"}, "Prevent Docker daemon from contacting specified registries. Special keyword \"public\" represents public Docker registry.") - options.AdditionalRegistries = opts.NewListOpts(ValidateIndexName) - flag.Var(&options.AdditionalRegistries, []string{"-add-registry"}, "Each given registry will be queried before a public Docker registry during image pulls or searches. They will be searched in the order given and treated as insecure.") } type netIPNet net.IPNet From 95bd20f5778aa5693d1a423337d551dda79a79c7 Mon Sep 17 00:00:00 2001 From: Michal Minar Date: Thu, 29 Jan 2015 16:38:31 +0100 Subject: [PATCH 4/5] Mark --(add|block)-registry options as experimental These options shall be used at your own risk. Signed-off-by: Michal Minar --- contrib/completion/fish/docker.fish | 4 ++-- daemon/config.go | 4 ++-- docs/man/docker.1.md | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/contrib/completion/fish/docker.fish b/contrib/completion/fish/docker.fish index 775b4383f22d0..6f3739d1111d6 100644 --- a/contrib/completion/fish/docker.fish +++ b/contrib/completion/fish/docker.fish @@ -43,11 +43,11 @@ function __fish_print_docker_repositories --description 'Print a list of docker end # common options -complete -c docker -f -n '__fish_docker_no_subcommand' -l add-registry -d "Each given registry will be queried before a public Docker registry during image pulls or searches. They will be searched in the order given and treated as insecure." +complete -c docker -f -n '__fish_docker_no_subcommand' -l add-registry -d "[EXPERIMENTAL] Each given registry will be queried before a public Docker registry during image pulls or searches. They will be searched in the order given and treated as insecure." complete -c docker -f -n '__fish_docker_no_subcommand' -l api-enable-cors -d 'Enable CORS headers in the remote API' complete -c docker -f -n '__fish_docker_no_subcommand' -s b -l bridge -d 'Attach containers to a pre-existing network bridge' complete -c docker -f -n '__fish_docker_no_subcommand' -l bip -d "Use this CIDR notation address for the network bridge's IP, not compatible with -b" -complete -c docker -f -n '__fish_docker_no_subcommand' -l block-registry -d 'Prevent docker daemon from contacting specified registries. Special keyword "public" represents public Docker registry.' +complete -c docker -f -n '__fish_docker_no_subcommand' -l block-registry -d '[EXPERIMENTAL] Prevent docker daemon from contacting specified registries. Special keyword "public" represents public Docker registry.' complete -c docker -f -n '__fish_docker_no_subcommand' -s D -l debug -d 'Enable debug mode' complete -c docker -f -n '__fish_docker_no_subcommand' -s d -l daemon -d 'Enable daemon mode' complete -c docker -f -n '__fish_docker_no_subcommand' -l dns -d 'Force Docker to use specific DNS servers' diff --git a/daemon/config.go b/daemon/config.go index c7aaafa5cc9a6..f17032c2e0d20 100644 --- a/daemon/config.go +++ b/daemon/config.go @@ -75,9 +75,9 @@ func (config *Config) InstallFlags() { opts.DnsSearchListVar(&config.DnsSearch, []string{"-dns-search"}, "Force Docker to use specific DNS search domains") opts.LabelListVar(&config.Labels, []string{"-label"}, "Set key=value labels to the daemon (displayed in `docker info`)") config.BlockedRegistries = opts.NewListOpts(registry.ValidateIndexName) - flag.Var(&config.BlockedRegistries, []string{"-block-registry"}, "Prevent Docker daemon from contacting specified registries. Special keyword \"public\" represents public Docker registry.") + flag.Var(&config.BlockedRegistries, []string{"-block-registry"}, "[EXPERIMENTAL] Prevent Docker daemon from contacting specified registries. Special keyword \"public\" represents public Docker registry.") config.AdditionalRegistries = opts.NewListOpts(registry.ValidateIndexName) - flag.Var(&config.AdditionalRegistries, []string{"-add-registry"}, "Each given registry will be queried before a public Docker registry during image pulls or searches. They will be searched in the order given and treated as insecure.") + flag.Var(&config.AdditionalRegistries, []string{"-add-registry"}, "[EXPERIMENTAL] Each given registry will be queried before a public Docker registry during image pulls or searches. They will be searched in the order given and treated as insecure.") } func getDefaultNetworkMtu() int { diff --git a/docs/man/docker.1.md b/docs/man/docker.1.md index 41b07ca13816d..5104ae64c2a87 100644 --- a/docs/man/docker.1.md +++ b/docs/man/docker.1.md @@ -35,7 +35,7 @@ unix://[/path/to/socket] to use. tcp://host:port, unix:///path/to/socket, fd://* or fd://socketfd. **--add-registry**=[] - Each given registry will be queried before a public Docker registry during image pulls or searches. They will be searched in the order given and treated as insecure. Registry mirrors won't apply to them. + **EXPERIMENTAL** Each given registry will be queried before a public Docker registry during image pulls or searches. They will be searched in the order given and treated as insecure. Registry mirrors won't apply to them. **--api-enable-cors**=*true*|*false* Enable CORS headers in the remote API. Default is false. @@ -47,7 +47,7 @@ unix://[/path/to/socket] to use. Use the provided CIDR notation address for the dynamically created bridge (docker0); Mutually exclusive of \-b **--block-registry**=[] - Prevent Docker daemon from contacting specified registries. Special keyword "public" represents public Docker registry. + **EXPERIMENTAL** Prevent Docker daemon from contacting specified registries. Special keyword "public" represents public Docker registry. **-d**=*true*|*false* Enable daemon mode. Default is false. From 79509019e50a46e512ab28d87d20c0dc12d8c263 Mon Sep 17 00:00:00 2001 From: Michal Minar Date: Thu, 29 Jan 2015 21:38:16 +0100 Subject: [PATCH 5/5] Protect official registry when pulling during builds Ensure that registries missing hostname specified during builds will be pulled from official registry. If the official registry is blocked, the pull will fail. All the other pull jobs shall set `protectOfficialRegistry` env variable to `false` in order for additional registires added with `--add-registry` to have any effect. Prevent already pulled image to be updated from currently blocked registry. Also make sure that image tags can be operated upon when they miss the hostname part and docker has some additional repositories. Signed-off-by: Michal Minar --- api/server/server.go | 1 + builder/internals.go | 1 + graph/export.go | 4 +++- graph/pull.go | 5 +++++ graph/tags.go | 8 ++++++-- registry/session.go | 13 +++++++++++++ 6 files changed, 29 insertions(+), 3 deletions(-) diff --git a/api/server/server.go b/api/server/server.go index 5e337dfcbef2b..a2120cd3b4a36 100644 --- a/api/server/server.go +++ b/api/server/server.go @@ -565,6 +565,7 @@ func postImagesCreate(eng *engine.Engine, version version.Version, w http.Respon job.SetenvBool("parallel", version.GreaterThan("1.3")) job.SetenvJson("metaHeaders", metaHeaders) job.SetenvJson("authConfig", authConfig) + job.SetenvBool("protectOfficialRegistry", false) } else { //import if tag == "" { repo, tag = parsers.ParseRepositoryTag(repo) diff --git a/builder/internals.go b/builder/internals.go index 7523d121913f3..10ab0c5a916bf 100644 --- a/builder/internals.go +++ b/builder/internals.go @@ -438,6 +438,7 @@ func (b *Builder) pullImage(name string) (*imagepkg.Image, error) { job.SetenvBool("json", b.StreamFormatter.Json()) job.SetenvBool("parallel", true) job.SetenvJson("authConfig", pullRegistryAuth) + job.SetenvBool("protectOfficialRegistry", true) job.Stdout.Add(b.OutOld) if err := job.Run(); err != nil { return nil, err diff --git a/graph/export.go b/graph/export.go index 3f7ecd3c4e9e9..fcd96a6a01410 100644 --- a/graph/export.go +++ b/graph/export.go @@ -40,7 +40,9 @@ func (s *TagStore) CmdImageExport(job *engine.Job) engine.Status { } } for _, name := range job.Args { - name = registry.NormalizeLocalName(name) + if _, exists := s.Repositories[name]; !exists { + name = registry.NormalizeLocalName(name) + } log.Debugf("Serializing %s", name) rootRepo := s.Repositories[name] if rootRepo != nil { diff --git a/graph/pull.go b/graph/pull.go index 1f29e9afe30bb..0c9a5fc123289 100644 --- a/graph/pull.go +++ b/graph/pull.go @@ -30,6 +30,11 @@ func (s *TagStore) CmdRegistryPull(job *engine.Job) engine.Status { registries = []string{""} } else if len(registries) == 0 { return job.Errorf("No configured registry to pull from.") + } else if job.GetenvBool("protectOfficialRegistry") && registries[0] != registry.INDEXNAME { + // We must ensure that registry missing hostname will be pulled from + // official one, if the `protectOfficialRegistry` tells us so. + registries = []string{""} + tmp = fmt.Sprintf("%s/%s", registry.INDEXNAME, tmp) } for i, r := range registries { if i > 0 { diff --git a/graph/tags.go b/graph/tags.go index 6bdb296cd1814..346a42ce24b08 100644 --- a/graph/tags.go +++ b/graph/tags.go @@ -178,7 +178,9 @@ func (store *TagStore) Delete(repoName, tag string) (bool, error) { if err := store.reload(); err != nil { return false, err } - repoName = registry.NormalizeLocalName(repoName) + if _, exists := store.Repositories[repoName]; !exists { + repoName = registry.NormalizeLocalName(repoName) + } if r, exists := store.Repositories[repoName]; exists { if tag != "" { if _, exists2 := r[tag]; exists2 { @@ -240,10 +242,12 @@ func (store *TagStore) Get(repoName string) (Repository, error) { if err := store.reload(); err != nil { return nil, err } - repoName = registry.NormalizeLocalName(repoName) if r, exists := store.Repositories[repoName]; exists { return r, nil } + if r, exists := store.Repositories[registry.NormalizeLocalName(repoName)]; exists { + return r, nil + } return nil, nil } diff --git a/registry/session.go b/registry/session.go index 9905626a60297..cd50cc5399aca 100644 --- a/registry/session.go +++ b/registry/session.go @@ -200,6 +200,15 @@ func (r *Session) GetRemoteImageLayer(imgID, registry string, token []string, im return res.Body, nil } +func isEndpointBlocked(endpoint string) bool { + if parsedURL, err := url.Parse(endpoint); err == nil { + if _, ok := BlockedRegistries[parsedURL.Host]; !ok { + return false + } + } + return true +} + func (r *Session) GetRemoteTags(registries []string, repository string, token []string) (map[string]string, error) { if strings.Count(repository, "/") == 0 { // This will be removed once the Registry supports auto-resolution on @@ -207,6 +216,10 @@ func (r *Session) GetRemoteTags(registries []string, repository string, token [] repository = "library/" + repository } for _, host := range registries { + if isEndpointBlocked(host) { + log.Errorf("Cannot query blocked registry at %s for remote tags.", host) + continue + } endpoint := fmt.Sprintf("%srepositories/%s/tags", host, repository) req, err := r.reqFactory.NewRequest("GET", endpoint, nil)