Signed-off-by: Tibor Vass <teabee89@gmail.com>
| ... | ... |
@@ -56,7 +56,7 @@ func (config *Config) InstallFlags() {
|
| 56 | 56 |
flag.StringVar(&config.BridgeIP, []string{"#bip", "-bip"}, "", "Use this CIDR notation address for the network bridge's IP, not compatible with -b")
|
| 57 | 57 |
flag.StringVar(&config.BridgeIface, []string{"b", "-bridge"}, "", "Attach containers to a pre-existing network bridge\nuse 'none' to disable container networking")
|
| 58 | 58 |
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)")
|
|
| 60 | 60 |
flag.BoolVar(&config.InterContainerCommunication, []string{"#icc", "-icc"}, true, "Enable inter-container communication")
|
| 61 | 61 |
flag.StringVar(&config.GraphDriver, []string{"s", "-storage-driver"}, "", "Force the Docker runtime to use a specific storage driver")
|
| 62 | 62 |
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() {
|
| 68 | 68 |
opts.IPListVar(&config.Dns, []string{"#dns", "-dns"}, "Force Docker to use specific DNS servers")
|
| 69 | 69 |
opts.DnsSearchListVar(&config.DnsSearch, []string{"-dns-search"}, "Force Docker to use specific DNS search domains")
|
| 70 | 70 |
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") |
|
| 71 | 79 |
} |
| 72 | 80 |
|
| 73 | 81 |
func getDefaultNetworkMtu() int {
|
| ... | ... |
@@ -70,7 +70,7 @@ expect an integer, and they can only be specified once. |
| 70 | 70 |
-g, --graph="/var/lib/docker" Path to use as the root of the Docker runtime |
| 71 | 71 |
-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. |
| 72 | 72 |
--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) |
|
| 74 | 74 |
--ip=0.0.0.0 Default IP address to use when binding container ports |
| 75 | 75 |
--ip-forward=true Enable net.ipv4.ip_forward |
| 76 | 76 |
--ip-masq=true Enable IP masquerading for bridge's IP range |
| ... | ... |
@@ -12,6 +12,9 @@ import ( |
| 12 | 12 |
log "github.com/Sirupsen/logrus" |
| 13 | 13 |
) |
| 14 | 14 |
|
| 15 |
+// for mocking in unit tests |
|
| 16 |
+var lookupIP = net.LookupIP |
|
| 17 |
+ |
|
| 15 | 18 |
// scans string for api version in the URL path. returns the trimmed hostname, if version found, string and API version. |
| 16 | 19 |
func scanForAPIVersion(hostname string) (string, APIVersion) {
|
| 17 | 20 |
var ( |
| ... | ... |
@@ -79,7 +82,10 @@ func newEndpoint(hostname string, insecureRegistries []string) (*Endpoint, error |
| 79 | 79 |
if err != nil {
|
| 80 | 80 |
return nil, err |
| 81 | 81 |
} |
| 82 |
- endpoint.secure = isSecure(endpoint.URL.Host, insecureRegistries) |
|
| 82 |
+ endpoint.secure, err = isSecure(endpoint.URL.Host, insecureRegistries) |
|
| 83 |
+ if err != nil {
|
|
| 84 |
+ return nil, err |
|
| 85 |
+ } |
|
| 83 | 86 |
return &endpoint, nil |
| 84 | 87 |
} |
| 85 | 88 |
|
| ... | ... |
@@ -152,30 +158,56 @@ func (e Endpoint) Ping() (RegistryInfo, error) {
|
| 152 | 152 |
|
| 153 | 153 |
// isSecure returns false if the provided hostname is part of the list of insecure registries. |
| 154 | 154 |
// Insecure registries accept HTTP and/or accept HTTPS with certificates from unknown CAs. |
| 155 |
-func isSecure(hostname string, insecureRegistries []string) bool {
|
|
| 155 |
+// |
|
| 156 |
+// The list of insecure registries can contain an element with CIDR notation to specify a whole subnet. |
|
| 157 |
+// If the subnet contains one of the IPs of the registry specified by hostname, the latter is considered |
|
| 158 |
+// insecure. |
|
| 159 |
+// |
|
| 160 |
+// hostname should be a URL.Host (`host:port` or `host`) |
|
| 161 |
+func isSecure(hostname string, insecureRegistries []string) (bool, error) {
|
|
| 156 | 162 |
if hostname == IndexServerURL.Host {
|
| 157 |
- return true |
|
| 163 |
+ return true, nil |
|
| 158 | 164 |
} |
| 159 | 165 |
|
| 160 | 166 |
host, _, err := net.SplitHostPort(hostname) |
| 161 |
- |
|
| 162 | 167 |
if err != nil {
|
| 168 |
+ // assume hostname is of the form `host` without the port and go on. |
|
| 163 | 169 |
host = hostname |
| 164 | 170 |
} |
| 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 |
|
| 171 |
+ addrs, err := lookupIP(host) |
|
| 172 |
+ if err != nil {
|
|
| 173 |
+ ip := net.ParseIP(host) |
|
| 174 |
+ if ip == nil {
|
|
| 175 |
+ // if resolving `host` fails, error out, since host is to be net.Dial-ed anyway |
|
| 176 |
+ return true, fmt.Errorf("issecure: could not resolve %q: %v", host, err)
|
|
| 177 |
+ } |
|
| 178 |
+ addrs = []net.IP{ip}
|
|
| 179 |
+ } |
|
| 180 |
+ if len(addrs) == 0 {
|
|
| 181 |
+ return true, fmt.Errorf("issecure: could not resolve %q", host)
|
|
| 182 |
+ } |
|
| 183 |
+ |
|
| 184 |
+ for _, addr := range addrs {
|
|
| 185 |
+ for _, r := range insecureRegistries {
|
|
| 186 |
+ // hostname matches insecure registry |
|
| 187 |
+ if hostname == r {
|
|
| 188 |
+ return false, nil |
|
| 189 |
+ } |
|
| 190 |
+ |
|
| 191 |
+ // now assume a CIDR was passed to --insecure-registry |
|
| 192 |
+ _, ipnet, err := net.ParseCIDR(r) |
|
| 193 |
+ if err != nil {
|
|
| 194 |
+ // if could not parse it as a CIDR, even after removing |
|
| 195 |
+ // assume it's not a CIDR and go on with the next candidate |
|
| 196 |
+ continue |
|
| 197 |
+ } |
|
| 198 |
+ |
|
| 199 |
+ // check if the addr falls in the subnet |
|
| 200 |
+ if ipnet.Contains(addr) {
|
|
| 201 |
+ return false, nil |
|
| 202 |
+ } |
|
| 177 | 203 |
} |
| 178 | 204 |
} |
| 179 | 205 |
|
| 180 |
- return true |
|
| 206 |
+ return true, nil |
|
| 181 | 207 |
} |
| ... | ... |
@@ -2,9 +2,11 @@ package registry |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
"encoding/json" |
| 5 |
+ "errors" |
|
| 5 | 6 |
"fmt" |
| 6 | 7 |
"io" |
| 7 | 8 |
"io/ioutil" |
| 9 |
+ "net" |
|
| 8 | 10 |
"net/http" |
| 9 | 11 |
"net/http/httptest" |
| 10 | 12 |
"net/url" |
| ... | ... |
@@ -80,6 +82,11 @@ var ( |
| 80 | 80 |
"latest": "42d718c941f5c532ac049bf0b0ab53f0062f09a03afd4aa4a02c098e46032b9d", |
| 81 | 81 |
}, |
| 82 | 82 |
} |
| 83 |
+ mockHosts = map[string][]net.IP{
|
|
| 84 |
+ "": {net.ParseIP("0.0.0.0")},
|
|
| 85 |
+ "localhost": {net.ParseIP("127.0.0.1"), net.ParseIP("::1")},
|
|
| 86 |
+ "example.com": {net.ParseIP("42.42.42.42")},
|
|
| 87 |
+ } |
|
| 83 | 88 |
) |
| 84 | 89 |
|
| 85 | 90 |
func init() {
|
| ... | ... |
@@ -106,6 +113,25 @@ func init() {
|
| 106 | 106 |
panic(err) |
| 107 | 107 |
} |
| 108 | 108 |
insecureRegistries = []string{URL.Host}
|
| 109 |
+ |
|
| 110 |
+ // override net.LookupIP |
|
| 111 |
+ lookupIP = func(host string) ([]net.IP, error) {
|
|
| 112 |
+ if host == "127.0.0.1" {
|
|
| 113 |
+ // I believe in future Go versions this will fail, so let's fix it later |
|
| 114 |
+ return net.LookupIP(host) |
|
| 115 |
+ } |
|
| 116 |
+ for h, addrs := range mockHosts {
|
|
| 117 |
+ if host == h {
|
|
| 118 |
+ return addrs, nil |
|
| 119 |
+ } |
|
| 120 |
+ for _, addr := range addrs {
|
|
| 121 |
+ if addr.String() == host {
|
|
| 122 |
+ return []net.IP{addr}, nil
|
|
| 123 |
+ } |
|
| 124 |
+ } |
|
| 125 |
+ } |
|
| 126 |
+ return nil, errors.New("lookup: no such host")
|
|
| 127 |
+ } |
|
| 109 | 128 |
} |
| 110 | 129 |
|
| 111 | 130 |
func handlerAccessLog(handler http.Handler) http.Handler {
|
| ... | ... |
@@ -333,19 +333,26 @@ func TestIsSecure(t *testing.T) {
|
| 333 | 333 |
{"localhost:5000", []string{"localhost:5000"}, false},
|
| 334 | 334 |
{"localhost", []string{"example.com"}, false},
|
| 335 | 335 |
{"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},
|
|
| 339 | 339 |
{"localhost", []string{"example.com"}, false},
|
| 340 | 340 |
{"127.0.0.1", []string{"example.com"}, false},
|
| 341 |
- {"example.com", []string{}, true},
|
|
| 341 |
+ {"example.com", nil, true},
|
|
| 342 | 342 |
{"example.com", []string{"example.com"}, false},
|
| 343 | 343 |
{"127.0.0.1", []string{"example.com"}, false},
|
| 344 | 344 |
{"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},
|
|
| 345 | 350 |
} |
| 346 | 351 |
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)
|
|
| 349 | 356 |
} |
| 350 | 357 |
} |
| 351 | 358 |
} |