Skip to content

Commit 6aba75d

Browse files
committed
Add the possibility of specifying a subnet for --insecure-registry
Signed-off-by: Tibor Vass <[email protected]>
1 parent b0d219e commit 6aba75d

5 files changed

Lines changed: 98 additions & 25 deletions

File tree

daemon/config.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ func (config *Config) InstallFlags() {
5656
flag.StringVar(&config.BridgeIP, []string{"#bip", "-bip"}, "", "Use this CIDR notation address for the network bridge's IP, not compatible with -b")
5757
flag.StringVar(&config.BridgeIface, []string{"b", "-bridge"}, "", "Attach containers to a pre-existing network bridge\nuse 'none' to disable container networking")
5858
flag.StringVar(&config.FixedCIDR, []string{"-fixed-cidr"}, "", "IPv4 subnet for fixed IPs (ex: 10.20.0.0/16)\nthis subnet must be nested in the bridge subnet (which is defined by -b or --bip)")
59-
opts.ListVar(&config.InsecureRegistries, []string{"-insecure-registry"}, "Enable insecure communication with specified registries (no certificate verification for HTTPS and enable HTTP fallback)")
59+
opts.ListVar(&config.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)")
6060
flag.BoolVar(&config.InterContainerCommunication, []string{"#icc", "-icc"}, true, "Enable inter-container communication")
6161
flag.StringVar(&config.GraphDriver, []string{"s", "-storage-driver"}, "", "Force the Docker runtime to use a specific storage driver")
6262
flag.StringVar(&config.ExecDriver, []string{"e", "-exec-driver"}, "native", "Force the Docker runtime to use a specific exec driver")
@@ -68,6 +68,14 @@ func (config *Config) InstallFlags() {
6868
opts.IPListVar(&config.Dns, []string{"#dns", "-dns"}, "Force Docker to use specific DNS servers")
6969
opts.DnsSearchListVar(&config.DnsSearch, []string{"-dns-search"}, "Force Docker to use specific DNS search domains")
7070
opts.MirrorListVar(&config.Mirrors, []string{"-registry-mirror"}, "Specify a preferred Docker registry mirror")
71+
72+
// Localhost is by default considered as an insecure registry
73+
// This is a stop-gap for people who are running a private registry on localhost (especially on Boot2docker).
74+
//
75+
// TODO: should we deprecate this once it is easier for people to set up a TLS registry or change
76+
// daemon flags on boot2docker?
77+
// If so, do not forget to check the TODO in TestIsSecure
78+
config.InsecureRegistries = append(config.InsecureRegistries, "127.0.0.0/8")
7179
}
7280

7381
func getDefaultNetworkMtu() int {

docs/sources/reference/commandline/cli.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ expect an integer, and they can only be specified once.
7070
-g, --graph="/var/lib/docker" Path to use as the root of the Docker runtime
7171
-H, --host=[] The socket(s) to bind to in daemon mode or connect to in client mode, specified using one or more tcp://host:port, unix:///path/to/socket, fd://* or fd://socketfd.
7272
--icc=true Enable inter-container communication
73-
--insecure-registry=[] Enable insecure communication with specified registries (no certificate verification for HTTPS and enable HTTP fallback)
73+
--insecure-registry=[] Enable insecure communication with specified registries (no certificate verification for HTTPS and enable HTTP fallback) (ex: localhost:5000 or 10.20.0.0/16)
7474
--ip=0.0.0.0 Default IP address to use when binding container ports
7575
--ip-forward=true Enable net.ipv4.ip_forward
7676
--ip-masq=true Enable IP masquerading for bridge's IP range

registry/endpoint.go

Lines changed: 49 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ import (
1212
log "github.com/Sirupsen/logrus"
1313
)
1414

15+
// for mocking in unit tests
16+
var lookupIP = net.LookupIP
17+
1518
// scans string for api version in the URL path. returns the trimmed hostname, if version found, string and API version.
1619
func scanForAPIVersion(hostname string) (string, APIVersion) {
1720
var (
@@ -79,7 +82,10 @@ func newEndpoint(hostname string, insecureRegistries []string) (*Endpoint, error
7982
if err != nil {
8083
return nil, err
8184
}
82-
endpoint.secure = isSecure(endpoint.URL.Host, insecureRegistries)
85+
endpoint.secure, err = isSecure(endpoint.URL.Host, insecureRegistries)
86+
if err != nil {
87+
return nil, err
88+
}
8389
return &endpoint, nil
8490
}
8591

@@ -152,30 +158,56 @@ func (e Endpoint) Ping() (RegistryInfo, error) {
152158

153159
// isSecure returns false if the provided hostname is part of the list of insecure registries.
154160
// Insecure registries accept HTTP and/or accept HTTPS with certificates from unknown CAs.
155-
func isSecure(hostname string, insecureRegistries []string) bool {
161+
//
162+
// The list of insecure registries can contain an element with CIDR notation to specify a whole subnet.
163+
// If the subnet contains one of the IPs of the registry specified by hostname, the latter is considered
164+
// insecure.
165+
//
166+
// hostname should be a URL.Host (`host:port` or `host`)
167+
func isSecure(hostname string, insecureRegistries []string) (bool, error) {
156168
if hostname == IndexServerURL.Host {
157-
return true
169+
return true, nil
158170
}
159171

160172
host, _, err := net.SplitHostPort(hostname)
161-
162173
if err != nil {
174+
// assume hostname is of the form `host` without the port and go on.
163175
host = hostname
164176
}
165-
166-
if host == "127.0.0.1" || host == "localhost" {
167-
return false
168-
}
169-
170-
if len(insecureRegistries) == 0 {
171-
return true
172-
}
173-
174-
for _, h := range insecureRegistries {
175-
if hostname == h {
176-
return false
177+
addrs, err := lookupIP(host)
178+
if err != nil {
179+
ip := net.ParseIP(host)
180+
if ip == nil {
181+
// if resolving `host` fails, error out, since host is to be net.Dial-ed anyway
182+
return true, fmt.Errorf("issecure: could not resolve %q: %v", host, err)
183+
}
184+
addrs = []net.IP{ip}
185+
}
186+
if len(addrs) == 0 {
187+
return true, fmt.Errorf("issecure: could not resolve %q", host)
188+
}
189+
190+
for _, addr := range addrs {
191+
for _, r := range insecureRegistries {
192+
// hostname matches insecure registry
193+
if hostname == r {
194+
return false, nil
195+
}
196+
197+
// now assume a CIDR was passed to --insecure-registry
198+
_, ipnet, err := net.ParseCIDR(r)
199+
if err != nil {
200+
// if could not parse it as a CIDR, even after removing
201+
// assume it's not a CIDR and go on with the next candidate
202+
continue
203+
}
204+
205+
// check if the addr falls in the subnet
206+
if ipnet.Contains(addr) {
207+
return false, nil
208+
}
177209
}
178210
}
179211

180-
return true
212+
return true, nil
181213
}

registry/registry_mock_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ package registry
22

33
import (
44
"encoding/json"
5+
"errors"
56
"fmt"
67
"io"
78
"io/ioutil"
9+
"net"
810
"net/http"
911
"net/http/httptest"
1012
"net/url"
@@ -80,6 +82,11 @@ var (
8082
"latest": "42d718c941f5c532ac049bf0b0ab53f0062f09a03afd4aa4a02c098e46032b9d",
8183
},
8284
}
85+
mockHosts = map[string][]net.IP{
86+
"": {net.ParseIP("0.0.0.0")},
87+
"localhost": {net.ParseIP("127.0.0.1"), net.ParseIP("::1")},
88+
"example.com": {net.ParseIP("42.42.42.42")},
89+
}
8390
)
8491

8592
func init() {
@@ -106,6 +113,25 @@ func init() {
106113
panic(err)
107114
}
108115
insecureRegistries = []string{URL.Host}
116+
117+
// override net.LookupIP
118+
lookupIP = func(host string) ([]net.IP, error) {
119+
if host == "127.0.0.1" {
120+
// I believe in future Go versions this will fail, so let's fix it later
121+
return net.LookupIP(host)
122+
}
123+
for h, addrs := range mockHosts {
124+
if host == h {
125+
return addrs, nil
126+
}
127+
for _, addr := range addrs {
128+
if addr.String() == host {
129+
return []net.IP{addr}, nil
130+
}
131+
}
132+
}
133+
return nil, errors.New("lookup: no such host")
134+
}
109135
}
110136

111137
func handlerAccessLog(handler http.Handler) http.Handler {

registry/registry_test.go

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -333,19 +333,26 @@ func TestIsSecure(t *testing.T) {
333333
{"localhost:5000", []string{"localhost:5000"}, false},
334334
{"localhost", []string{"example.com"}, false},
335335
{"127.0.0.1:5000", []string{"127.0.0.1:5000"}, false},
336-
{"localhost", []string{}, false},
337-
{"localhost:5000", []string{}, false},
338-
{"127.0.0.1", []string{}, false},
336+
{"localhost", nil, false},
337+
{"localhost:5000", nil, false},
338+
{"127.0.0.1", nil, false},
339339
{"localhost", []string{"example.com"}, false},
340340
{"127.0.0.1", []string{"example.com"}, false},
341-
{"example.com", []string{}, true},
341+
{"example.com", nil, true},
342342
{"example.com", []string{"example.com"}, false},
343343
{"127.0.0.1", []string{"example.com"}, false},
344344
{"127.0.0.1:5000", []string{"example.com"}, false},
345+
{"example.com:5000", []string{"42.42.0.0/16"}, false},
346+
{"example.com", []string{"42.42.0.0/16"}, false},
347+
{"example.com:5000", []string{"42.42.42.42/8"}, false},
348+
{"127.0.0.1:5000", []string{"127.0.0.0/8"}, false},
349+
{"42.42.42.42:5000", []string{"42.1.1.1/8"}, false},
345350
}
346351
for _, tt := range tests {
347-
if sec := isSecure(tt.addr, tt.insecureRegistries); sec != tt.expected {
348-
t.Errorf("isSecure failed for %q %v, expected %v got %v", tt.addr, tt.insecureRegistries, tt.expected, sec)
352+
// TODO: remove this once we remove localhost insecure by default
353+
insecureRegistries := append(tt.insecureRegistries, "127.0.0.0/8")
354+
if sec, err := isSecure(tt.addr, insecureRegistries); err != nil || sec != tt.expected {
355+
t.Fatalf("isSecure failed for %q %v, expected %v got %v. Error: %v", tt.addr, insecureRegistries, tt.expected, sec, err)
349356
}
350357
}
351358
}

0 commit comments

Comments
 (0)