Browse code

Make default tls host work

Signed-off-by: Lei Jitang <leijitang@huawei.com>

Lei Jitang authored on 2015/10/19 22:17:37
Showing 8 changed files
... ...
@@ -112,8 +112,13 @@ func NewDockerCli(in io.ReadCloser, out, err io.Writer, clientFlags *cli.ClientF
112 112
 			return errors.New("Please specify only one -H")
113 113
 		}
114 114
 
115
+		defaultHost := opts.DefaultTCPHost
116
+		if clientFlags.Common.TLSOptions != nil {
117
+			defaultHost = opts.DefaultTLSHost
118
+		}
119
+
115 120
 		var e error
116
-		if hosts[0], e = opts.ParseHost(hosts[0]); e != nil {
121
+		if hosts[0], e = opts.ParseHost(defaultHost, hosts[0]); e != nil {
117 122
 			return e
118 123
 		}
119 124
 
... ...
@@ -210,6 +210,7 @@ func (cli *DaemonCli) CmdDaemon(args ...string) error {
210 210
 	}
211 211
 	serverConfig = setPlatformServerConfig(serverConfig, cli.Config)
212 212
 
213
+	defaultHost := opts.DefaultHost
213 214
 	if commonFlags.TLSOptions != nil {
214 215
 		if !commonFlags.TLSOptions.InsecureSkipVerify {
215 216
 			// server requires and verifies client's certificate
... ...
@@ -220,6 +221,7 @@ func (cli *DaemonCli) CmdDaemon(args ...string) error {
220 220
 			logrus.Fatal(err)
221 221
 		}
222 222
 		serverConfig.TLSConfig = tlsConfig
223
+		defaultHost = opts.DefaultTLSHost
223 224
 	}
224 225
 
225 226
 	if len(commonFlags.Hosts) == 0 {
... ...
@@ -227,7 +229,7 @@ func (cli *DaemonCli) CmdDaemon(args ...string) error {
227 227
 	}
228 228
 	for i := 0; i < len(commonFlags.Hosts); i++ {
229 229
 		var err error
230
-		if commonFlags.Hosts[i], err = opts.ParseHost(commonFlags.Hosts[i]); err != nil {
230
+		if commonFlags.Hosts[i], err = opts.ParseHost(defaultHost, commonFlags.Hosts[i]); err != nil {
231 231
 			logrus.Fatalf("error parsing -H %s : %v", commonFlags.Hosts[i], err)
232 232
 		}
233 233
 	}
... ...
@@ -1723,3 +1723,37 @@ func (s *DockerDaemonSuite) TestDaemonStartWithoutHost(c *check.C) {
1723 1723
 	}()
1724 1724
 	c.Assert(s.d.Start(), check.IsNil)
1725 1725
 }
1726
+
1727
+func (s *DockerDaemonSuite) TestDaemonStartWithDefalutTlsHost(c *check.C) {
1728
+	s.d.useDefaultTLSHost = true
1729
+	defer func() {
1730
+		s.d.useDefaultTLSHost = false
1731
+	}()
1732
+	if err := s.d.Start(
1733
+		"--tlsverify",
1734
+		"--tlscacert", "fixtures/https/ca.pem",
1735
+		"--tlscert", "fixtures/https/server-cert.pem",
1736
+		"--tlskey", "fixtures/https/server-key.pem"); err != nil {
1737
+		c.Fatalf("Could not start daemon: %v", err)
1738
+	}
1739
+
1740
+	// The client with --tlsverify should also use default host localhost:2376
1741
+	tmpHost := os.Getenv("DOCKER_HOST")
1742
+	defer func() {
1743
+		os.Setenv("DOCKER_HOST", tmpHost)
1744
+	}()
1745
+
1746
+	os.Setenv("DOCKER_HOST", "")
1747
+
1748
+	out, _ := dockerCmd(
1749
+		c,
1750
+		"--tlsverify",
1751
+		"--tlscacert", "fixtures/https/ca.pem",
1752
+		"--tlscert", "fixtures/https/client-cert.pem",
1753
+		"--tlskey", "fixtures/https/client-key.pem",
1754
+		"version",
1755
+	)
1756
+	if !strings.Contains(out, "Server") {
1757
+		c.Fatalf("docker version should return information of server side")
1758
+	}
1759
+}
... ...
@@ -26,7 +26,9 @@ import (
26 26
 	"github.com/docker/docker/pkg/httputils"
27 27
 	"github.com/docker/docker/pkg/integration"
28 28
 	"github.com/docker/docker/pkg/ioutils"
29
+	"github.com/docker/docker/pkg/sockets"
29 30
 	"github.com/docker/docker/pkg/stringutils"
31
+	"github.com/docker/docker/pkg/tlsconfig"
30 32
 	"github.com/go-check/check"
31 33
 )
32 34
 
... ...
@@ -37,19 +39,26 @@ type Daemon struct {
37 37
 	Command     string
38 38
 	GlobalFlags []string
39 39
 
40
-	id             string
41
-	c              *check.C
42
-	logFile        *os.File
43
-	folder         string
44
-	root           string
45
-	stdin          io.WriteCloser
46
-	stdout, stderr io.ReadCloser
47
-	cmd            *exec.Cmd
48
-	storageDriver  string
49
-	execDriver     string
50
-	wait           chan error
51
-	userlandProxy  bool
52
-	useDefaultHost bool
40
+	id                string
41
+	c                 *check.C
42
+	logFile           *os.File
43
+	folder            string
44
+	root              string
45
+	stdin             io.WriteCloser
46
+	stdout, stderr    io.ReadCloser
47
+	cmd               *exec.Cmd
48
+	storageDriver     string
49
+	execDriver        string
50
+	wait              chan error
51
+	userlandProxy     bool
52
+	useDefaultHost    bool
53
+	useDefaultTLSHost bool
54
+}
55
+
56
+type clientConfig struct {
57
+	transport *http.Transport
58
+	scheme    string
59
+	addr      string
53 60
 }
54 61
 
55 62
 // NewDaemon returns a Daemon instance to be used for testing.
... ...
@@ -86,6 +95,50 @@ func NewDaemon(c *check.C) *Daemon {
86 86
 	}
87 87
 }
88 88
 
89
+func (d *Daemon) getClientConfig() (*clientConfig, error) {
90
+	var (
91
+		transport *http.Transport
92
+		scheme    string
93
+		addr      string
94
+		proto     string
95
+	)
96
+	if d.useDefaultTLSHost {
97
+		option := &tlsconfig.Options{
98
+			CAFile:   "fixtures/https/ca.pem",
99
+			CertFile: "fixtures/https/client-cert.pem",
100
+			KeyFile:  "fixtures/https/client-key.pem",
101
+		}
102
+		tlsConfig, err := tlsconfig.Client(*option)
103
+		if err != nil {
104
+			return nil, err
105
+		}
106
+		transport = &http.Transport{
107
+			TLSClientConfig: tlsConfig,
108
+		}
109
+		addr = fmt.Sprintf("%s:%d", opts.DefaultHTTPHost, opts.DefaultTLSHTTPPort)
110
+		scheme = "https"
111
+		proto = "tcp"
112
+	} else if d.useDefaultHost {
113
+		addr = opts.DefaultUnixSocket
114
+		proto = "unix"
115
+		scheme = "http"
116
+		transport = &http.Transport{}
117
+	} else {
118
+		addr = filepath.Join(d.folder, "docker.sock")
119
+		proto = "unix"
120
+		scheme = "http"
121
+		transport = &http.Transport{}
122
+	}
123
+
124
+	sockets.ConfigureTCPTransport(transport, proto, addr)
125
+
126
+	return &clientConfig{
127
+		transport: transport,
128
+		scheme:    scheme,
129
+		addr:      addr,
130
+	}, nil
131
+}
132
+
89 133
 // Start will start the daemon and return once it is ready to receive requests.
90 134
 // You can specify additional daemon flags.
91 135
 func (d *Daemon) Start(arg ...string) error {
... ...
@@ -98,7 +151,7 @@ func (d *Daemon) Start(arg ...string) error {
98 98
 		"--pidfile", fmt.Sprintf("%s/docker.pid", d.folder),
99 99
 		fmt.Sprintf("--userland-proxy=%t", d.userlandProxy),
100 100
 	)
101
-	if !d.useDefaultHost {
101
+	if !(d.useDefaultHost || d.useDefaultTLSHost) {
102 102
 		args = append(args, []string{"--host", d.sock()}...)
103 103
 	}
104 104
 	if root := os.Getenv("DOCKER_REMAP_ROOT"); root != "" {
... ...
@@ -160,25 +213,19 @@ func (d *Daemon) Start(arg ...string) error {
160 160
 		case <-time.After(2 * time.Second):
161 161
 			return fmt.Errorf("[%s] timeout: daemon does not respond", d.id)
162 162
 		case <-tick:
163
-			var (
164
-				c   net.Conn
165
-				err error
166
-			)
167
-			if d.useDefaultHost {
168
-				c, err = net.Dial("unix", "/var/run/docker.sock")
169
-			} else {
170
-				c, err = net.Dial("unix", filepath.Join(d.folder, "docker.sock"))
171
-			}
163
+			clientConfig, err := d.getClientConfig()
172 164
 			if err != nil {
173
-				continue
165
+				return err
174 166
 			}
175 167
 
176
-			client := httputil.NewClientConn(c, nil)
177
-			defer client.Close()
168
+			client := &http.Client{
169
+				Transport: clientConfig.transport,
170
+			}
178 171
 
179 172
 			req, err := http.NewRequest("GET", "/_ping", nil)
180 173
 			d.c.Assert(err, check.IsNil, check.Commentf("[%s] could not create new request", d.id))
181
-
174
+			req.URL.Host = clientConfig.addr
175
+			req.URL.Scheme = clientConfig.scheme
182 176
 			resp, err := client.Do(req)
183 177
 			if err != nil {
184 178
 				continue
... ...
@@ -289,34 +336,28 @@ func (d *Daemon) Restart(arg ...string) error {
289 289
 func (d *Daemon) queryRootDir() (string, error) {
290 290
 	// update daemon root by asking /info endpoint (to support user
291 291
 	// namespaced daemon with root remapped uid.gid directory)
292
-	var (
293
-		conn net.Conn
294
-		err  error
295
-	)
296
-	if d.useDefaultHost {
297
-		conn, err = net.Dial("unix", "/var/run/docker.sock")
298
-	} else {
299
-		conn, err = net.Dial("unix", filepath.Join(d.folder, "docker.sock"))
300
-	}
292
+	clientConfig, err := d.getClientConfig()
301 293
 	if err != nil {
302 294
 		return "", err
303 295
 	}
304
-	client := httputil.NewClientConn(conn, nil)
296
+
297
+	client := &http.Client{
298
+		Transport: clientConfig.transport,
299
+	}
305 300
 
306 301
 	req, err := http.NewRequest("GET", "/info", nil)
307 302
 	if err != nil {
308
-		client.Close()
309 303
 		return "", err
310 304
 	}
311 305
 	req.Header.Set("Content-Type", "application/json")
306
+	req.URL.Host = clientConfig.addr
307
+	req.URL.Scheme = clientConfig.scheme
312 308
 
313 309
 	resp, err := client.Do(req)
314 310
 	if err != nil {
315
-		client.Close()
316 311
 		return "", err
317 312
 	}
318 313
 	body := ioutils.NewReadCloserWrapper(resp.Body, func() error {
319
-		defer client.Close()
320 314
 		return resp.Body.Close()
321 315
 	})
322 316
 
... ...
@@ -16,7 +16,7 @@ var (
16 16
 	alphaRegexp  = regexp.MustCompile(`[a-zA-Z]`)
17 17
 	domainRegexp = regexp.MustCompile(`^(:?(:?[a-zA-Z0-9]|(:?[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9]))(:?\.(:?[a-zA-Z0-9]|(:?[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])))*)\.?\s*$`)
18 18
 	// DefaultHTTPHost Default HTTP Host used if only port is provided to -H flag e.g. docker daemon -H tcp://:8080
19
-	DefaultHTTPHost = "127.0.0.1"
19
+	DefaultHTTPHost = "localhost"
20 20
 
21 21
 	// DefaultHTTPPort Default HTTP Port used if only the protocol is provided to -H flag e.g. docker daemon -H tcp://
22 22
 	// TODO Windows. DefaultHTTPPort is only used on Windows if a -H parameter
... ...
@@ -342,7 +342,7 @@ func ValidateLabel(val string) (string, error) {
342 342
 
343 343
 // ValidateHost validates that the specified string is a valid host and returns it.
344 344
 func ValidateHost(val string) (string, error) {
345
-	_, err := parsers.ParseDockerDaemonHost(DefaultTCPHost, DefaultUnixSocket, val)
345
+	_, err := parsers.ParseDockerDaemonHost(DefaultTCPHost, DefaultTLSHost, DefaultUnixSocket, "", val)
346 346
 	if err != nil {
347 347
 		return val, err
348 348
 	}
... ...
@@ -352,8 +352,8 @@ func ValidateHost(val string) (string, error) {
352 352
 }
353 353
 
354 354
 // ParseHost and set defaults for a Daemon host string
355
-func ParseHost(val string) (string, error) {
356
-	host, err := parsers.ParseDockerDaemonHost(DefaultTCPHost, DefaultUnixSocket, val)
355
+func ParseHost(defaultHost, val string) (string, error) {
356
+	host, err := parsers.ParseDockerDaemonHost(DefaultTCPHost, DefaultTLSHost, DefaultUnixSocket, defaultHost, val)
357 357
 	if err != nil {
358 358
 		return val, err
359 359
 	}
... ...
@@ -445,9 +445,9 @@ func TestParseHost(t *testing.T) {
445 445
 		"fd://":                    "fd://",
446 446
 		"fd://something":           "fd://something",
447 447
 		"tcp://host:":              "tcp://host:2375",
448
-		"tcp://":                   "tcp://127.0.0.1:2375",
449
-		"tcp://:2375":              "tcp://127.0.0.1:2375", // default ip address
450
-		"tcp://:2376":              "tcp://127.0.0.1:2376", // default ip address
448
+		"tcp://":                   "tcp://localhost:2375",
449
+		"tcp://:2375":              "tcp://localhost:2375", // default ip address
450
+		"tcp://:2376":              "tcp://localhost:2376", // default ip address
451 451
 		"tcp://0.0.0.0:8080":       "tcp://0.0.0.0:8080",
452 452
 		"tcp://192.168.0.0:12000":  "tcp://192.168.0.0:12000",
453 453
 		"tcp://192.168:8080":       "tcp://192.168:8080",
... ...
@@ -458,12 +458,12 @@ func TestParseHost(t *testing.T) {
458 458
 	}
459 459
 
460 460
 	for value, errorMessage := range invalid {
461
-		if _, err := ParseHost(value); err == nil || err.Error() != errorMessage {
461
+		if _, err := ParseHost(defaultHTTPHost, value); err == nil || err.Error() != errorMessage {
462 462
 			t.Fatalf("Expected an error for %v with [%v], got [%v]", value, errorMessage, err)
463 463
 		}
464 464
 	}
465 465
 	for value, expected := range valid {
466
-		if actual, err := ParseHost(value); err != nil || actual != expected {
466
+		if actual, err := ParseHost(defaultHTTPHost, value); err != nil || actual != expected {
467 467
 			t.Fatalf("Expected for %v [%v], got [%v, %v]", value, expected, actual, err)
468 468
 		}
469 469
 	}
... ...
@@ -17,9 +17,12 @@ import (
17 17
 // Depending of the address specified, will use the defaultTCPAddr or defaultUnixAddr
18 18
 // defaultUnixAddr must be a absolute file path (no `unix://` prefix)
19 19
 // defaultTCPAddr must be the full `tcp://host:port` form
20
-func ParseDockerDaemonHost(defaultTCPAddr, defaultUnixAddr, addr string) (string, error) {
20
+func ParseDockerDaemonHost(defaultTCPAddr, defaultTLSHost, defaultUnixAddr, defaultAddr, addr string) (string, error) {
21 21
 	addr = strings.TrimSpace(addr)
22 22
 	if addr == "" {
23
+		if defaultAddr == defaultTLSHost {
24
+			return defaultTLSHost, nil
25
+		}
23 26
 		if runtime.GOOS != "windows" {
24 27
 			return fmt.Sprintf("unix://%s", defaultUnixAddr), nil
25 28
 		}
... ...
@@ -9,9 +9,10 @@ import (
9 9
 
10 10
 func TestParseDockerDaemonHost(t *testing.T) {
11 11
 	var (
12
-		defaultHTTPHost = "tcp://127.0.0.1:2376"
13
-		defaultUnix     = "/var/run/docker.sock"
14
-		defaultHOST     = "unix:///var/run/docker.sock"
12
+		defaultHTTPHost  = "tcp://localhost:2375"
13
+		defaultHTTPSHost = "tcp://localhost:2376"
14
+		defaultUnix      = "/var/run/docker.sock"
15
+		defaultHOST      = "unix:///var/run/docker.sock"
15 16
 	)
16 17
 	if runtime.GOOS == "windows" {
17 18
 		defaultHOST = defaultHTTPHost
... ...
@@ -28,37 +29,37 @@ func TestParseDockerDaemonHost(t *testing.T) {
28 28
 		"fd":   "Invalid bind address format: fd",
29 29
 	}
30 30
 	valids := map[string]string{
31
-		"0.0.0.1:":                    "tcp://0.0.0.1:2376",
31
+		"0.0.0.1:":                    "tcp://0.0.0.1:2375",
32 32
 		"0.0.0.1:5555":                "tcp://0.0.0.1:5555",
33 33
 		"0.0.0.1:5555/path":           "tcp://0.0.0.1:5555/path",
34
-		"[::1]:":                      "tcp://[::1]:2376",
34
+		"[::1]:":                      "tcp://[::1]:2375",
35 35
 		"[::1]:5555/path":             "tcp://[::1]:5555/path",
36
-		"[0:0:0:0:0:0:0:1]:":          "tcp://[0:0:0:0:0:0:0:1]:2376",
36
+		"[0:0:0:0:0:0:0:1]:":          "tcp://[0:0:0:0:0:0:0:1]:2375",
37 37
 		"[0:0:0:0:0:0:0:1]:5555/path": "tcp://[0:0:0:0:0:0:0:1]:5555/path",
38
-		":6666":                   "tcp://127.0.0.1:6666",
39
-		":6666/path":              "tcp://127.0.0.1:6666/path",
38
+		":6666":                   "tcp://localhost:6666",
39
+		":6666/path":              "tcp://localhost:6666/path",
40 40
 		"":                        defaultHOST,
41 41
 		" ":                       defaultHOST,
42 42
 		"  ":                      defaultHOST,
43 43
 		"tcp://":                  defaultHTTPHost,
44
-		"tcp://:7777":             "tcp://127.0.0.1:7777",
45
-		"tcp://:7777/path":        "tcp://127.0.0.1:7777/path",
46
-		" tcp://:7777/path ":      "tcp://127.0.0.1:7777/path",
44
+		"tcp://:7777":             "tcp://localhost:7777",
45
+		"tcp://:7777/path":        "tcp://localhost:7777/path",
46
+		" tcp://:7777/path ":      "tcp://localhost:7777/path",
47 47
 		"unix:///run/docker.sock": "unix:///run/docker.sock",
48 48
 		"unix://":                 "unix:///var/run/docker.sock",
49 49
 		"fd://":                   "fd://",
50 50
 		"fd://something":          "fd://something",
51
-		"localhost:":              "tcp://localhost:2376",
51
+		"localhost:":              "tcp://localhost:2375",
52 52
 		"localhost:5555":          "tcp://localhost:5555",
53 53
 		"localhost:5555/path":     "tcp://localhost:5555/path",
54 54
 	}
55 55
 	for invalidAddr, expectedError := range invalids {
56
-		if addr, err := ParseDockerDaemonHost(defaultHTTPHost, defaultUnix, invalidAddr); err == nil || err.Error() != expectedError {
56
+		if addr, err := ParseDockerDaemonHost(defaultHTTPHost, defaultHTTPSHost, defaultUnix, "", invalidAddr); err == nil || err.Error() != expectedError {
57 57
 			t.Errorf("tcp %v address expected error %v return, got %s and addr %v", invalidAddr, expectedError, err, addr)
58 58
 		}
59 59
 	}
60 60
 	for validAddr, expectedAddr := range valids {
61
-		if addr, err := ParseDockerDaemonHost(defaultHTTPHost, defaultUnix, validAddr); err != nil || addr != expectedAddr {
61
+		if addr, err := ParseDockerDaemonHost(defaultHTTPHost, defaultHTTPSHost, defaultUnix, "", validAddr); err != nil || addr != expectedAddr {
62 62
 			t.Errorf("%v -> expected %v, got (%v) addr (%v)", validAddr, expectedAddr, err, addr)
63 63
 		}
64 64
 	}