From fbb01b816288c5cf3eb79358c035072766b6e0f0 Mon Sep 17 00:00:00 2001 From: Lei Jitang Date: Mon, 19 Oct 2015 21:17:37 +0800 Subject: [PATCH] Make default tls host work Signed-off-by: Lei Jitang --- api/client/cli.go | 7 +- docker/daemon.go | 4 +- integration-cli/docker_cli_daemon_test.go | 34 ++++++ integration-cli/docker_utils.go | 121 +++++++++++++++------- opts/opts.go | 8 +- opts/opts_test.go | 10 +- pkg/parsers/parsers.go | 5 +- pkg/parsers/parsers_test.go | 29 +++--- 8 files changed, 152 insertions(+), 66 deletions(-) diff --git a/api/client/cli.go b/api/client/cli.go index aa7e64b298..834c47a4d3 100644 --- a/api/client/cli.go +++ b/api/client/cli.go @@ -112,8 +112,13 @@ func NewDockerCli(in io.ReadCloser, out, err io.Writer, clientFlags *cli.ClientF return errors.New("Please specify only one -H") } + defaultHost := opts.DefaultTCPHost + if clientFlags.Common.TLSOptions != nil { + defaultHost = opts.DefaultTLSHost + } + var e error - if hosts[0], e = opts.ParseHost(hosts[0]); e != nil { + if hosts[0], e = opts.ParseHost(defaultHost, hosts[0]); e != nil { return e } diff --git a/docker/daemon.go b/docker/daemon.go index c551b5c082..fb68e70544 100644 --- a/docker/daemon.go +++ b/docker/daemon.go @@ -210,6 +210,7 @@ func (cli *DaemonCli) CmdDaemon(args ...string) error { } serverConfig = setPlatformServerConfig(serverConfig, cli.Config) + defaultHost := opts.DefaultHost if commonFlags.TLSOptions != nil { if !commonFlags.TLSOptions.InsecureSkipVerify { // server requires and verifies client's certificate @@ -220,6 +221,7 @@ func (cli *DaemonCli) CmdDaemon(args ...string) error { logrus.Fatal(err) } serverConfig.TLSConfig = tlsConfig + defaultHost = opts.DefaultTLSHost } if len(commonFlags.Hosts) == 0 { @@ -227,7 +229,7 @@ func (cli *DaemonCli) CmdDaemon(args ...string) error { } for i := 0; i < len(commonFlags.Hosts); i++ { var err error - if commonFlags.Hosts[i], err = opts.ParseHost(commonFlags.Hosts[i]); err != nil { + if commonFlags.Hosts[i], err = opts.ParseHost(defaultHost, commonFlags.Hosts[i]); err != nil { logrus.Fatalf("error parsing -H %s : %v", commonFlags.Hosts[i], err) } } diff --git a/integration-cli/docker_cli_daemon_test.go b/integration-cli/docker_cli_daemon_test.go index 6567809bfa..b94f00afe0 100644 --- a/integration-cli/docker_cli_daemon_test.go +++ b/integration-cli/docker_cli_daemon_test.go @@ -1723,3 +1723,37 @@ func (s *DockerDaemonSuite) TestDaemonStartWithoutHost(c *check.C) { }() c.Assert(s.d.Start(), check.IsNil) } + +func (s *DockerDaemonSuite) TestDaemonStartWithDefalutTlsHost(c *check.C) { + s.d.useDefaultTLSHost = true + defer func() { + s.d.useDefaultTLSHost = false + }() + if err := s.d.Start( + "--tlsverify", + "--tlscacert", "fixtures/https/ca.pem", + "--tlscert", "fixtures/https/server-cert.pem", + "--tlskey", "fixtures/https/server-key.pem"); err != nil { + c.Fatalf("Could not start daemon: %v", err) + } + + // The client with --tlsverify should also use default host localhost:2376 + tmpHost := os.Getenv("DOCKER_HOST") + defer func() { + os.Setenv("DOCKER_HOST", tmpHost) + }() + + os.Setenv("DOCKER_HOST", "") + + out, _ := dockerCmd( + c, + "--tlsverify", + "--tlscacert", "fixtures/https/ca.pem", + "--tlscert", "fixtures/https/client-cert.pem", + "--tlskey", "fixtures/https/client-key.pem", + "version", + ) + if !strings.Contains(out, "Server") { + c.Fatalf("docker version should return information of server side") + } +} diff --git a/integration-cli/docker_utils.go b/integration-cli/docker_utils.go index b9b48a53b0..035e3f22b2 100644 --- a/integration-cli/docker_utils.go +++ b/integration-cli/docker_utils.go @@ -26,7 +26,9 @@ import ( "github.com/docker/docker/pkg/httputils" "github.com/docker/docker/pkg/integration" "github.com/docker/docker/pkg/ioutils" + "github.com/docker/docker/pkg/sockets" "github.com/docker/docker/pkg/stringutils" + "github.com/docker/docker/pkg/tlsconfig" "github.com/go-check/check" ) @@ -37,19 +39,26 @@ type Daemon struct { Command string GlobalFlags []string - id string - c *check.C - logFile *os.File - folder string - root string - stdin io.WriteCloser - stdout, stderr io.ReadCloser - cmd *exec.Cmd - storageDriver string - execDriver string - wait chan error - userlandProxy bool - useDefaultHost bool + id string + c *check.C + logFile *os.File + folder string + root string + stdin io.WriteCloser + stdout, stderr io.ReadCloser + cmd *exec.Cmd + storageDriver string + execDriver string + wait chan error + userlandProxy bool + useDefaultHost bool + useDefaultTLSHost bool +} + +type clientConfig struct { + transport *http.Transport + scheme string + addr string } // NewDaemon returns a Daemon instance to be used for testing. @@ -86,6 +95,50 @@ func NewDaemon(c *check.C) *Daemon { } } +func (d *Daemon) getClientConfig() (*clientConfig, error) { + var ( + transport *http.Transport + scheme string + addr string + proto string + ) + if d.useDefaultTLSHost { + option := &tlsconfig.Options{ + CAFile: "fixtures/https/ca.pem", + CertFile: "fixtures/https/client-cert.pem", + KeyFile: "fixtures/https/client-key.pem", + } + tlsConfig, err := tlsconfig.Client(*option) + if err != nil { + return nil, err + } + transport = &http.Transport{ + TLSClientConfig: tlsConfig, + } + addr = fmt.Sprintf("%s:%d", opts.DefaultHTTPHost, opts.DefaultTLSHTTPPort) + scheme = "https" + proto = "tcp" + } else if d.useDefaultHost { + addr = opts.DefaultUnixSocket + proto = "unix" + scheme = "http" + transport = &http.Transport{} + } else { + addr = filepath.Join(d.folder, "docker.sock") + proto = "unix" + scheme = "http" + transport = &http.Transport{} + } + + sockets.ConfigureTCPTransport(transport, proto, addr) + + return &clientConfig{ + transport: transport, + scheme: scheme, + addr: addr, + }, nil +} + // Start will start the daemon and return once it is ready to receive requests. // You can specify additional daemon flags. func (d *Daemon) Start(arg ...string) error { @@ -98,7 +151,7 @@ func (d *Daemon) Start(arg ...string) error { "--pidfile", fmt.Sprintf("%s/docker.pid", d.folder), fmt.Sprintf("--userland-proxy=%t", d.userlandProxy), ) - if !d.useDefaultHost { + if !(d.useDefaultHost || d.useDefaultTLSHost) { args = append(args, []string{"--host", d.sock()}...) } if root := os.Getenv("DOCKER_REMAP_ROOT"); root != "" { @@ -160,25 +213,19 @@ func (d *Daemon) Start(arg ...string) error { case <-time.After(2 * time.Second): return fmt.Errorf("[%s] timeout: daemon does not respond", d.id) case <-tick: - var ( - c net.Conn - err error - ) - if d.useDefaultHost { - c, err = net.Dial("unix", "/var/run/docker.sock") - } else { - c, err = net.Dial("unix", filepath.Join(d.folder, "docker.sock")) - } + clientConfig, err := d.getClientConfig() if err != nil { - continue + return err } - client := httputil.NewClientConn(c, nil) - defer client.Close() + client := &http.Client{ + Transport: clientConfig.transport, + } req, err := http.NewRequest("GET", "/_ping", nil) d.c.Assert(err, check.IsNil, check.Commentf("[%s] could not create new request", d.id)) - + req.URL.Host = clientConfig.addr + req.URL.Scheme = clientConfig.scheme resp, err := client.Do(req) if err != nil { continue @@ -289,34 +336,28 @@ func (d *Daemon) Restart(arg ...string) error { func (d *Daemon) queryRootDir() (string, error) { // update daemon root by asking /info endpoint (to support user // namespaced daemon with root remapped uid.gid directory) - var ( - conn net.Conn - err error - ) - if d.useDefaultHost { - conn, err = net.Dial("unix", "/var/run/docker.sock") - } else { - conn, err = net.Dial("unix", filepath.Join(d.folder, "docker.sock")) - } + clientConfig, err := d.getClientConfig() if err != nil { return "", err } - client := httputil.NewClientConn(conn, nil) + + client := &http.Client{ + Transport: clientConfig.transport, + } req, err := http.NewRequest("GET", "/info", nil) if err != nil { - client.Close() return "", err } req.Header.Set("Content-Type", "application/json") + req.URL.Host = clientConfig.addr + req.URL.Scheme = clientConfig.scheme resp, err := client.Do(req) if err != nil { - client.Close() return "", err } body := ioutils.NewReadCloserWrapper(resp.Body, func() error { - defer client.Close() return resp.Body.Close() }) diff --git a/opts/opts.go b/opts/opts.go index 78059739a8..3d8e4b4816 100644 --- a/opts/opts.go +++ b/opts/opts.go @@ -16,7 +16,7 @@ var ( alphaRegexp = regexp.MustCompile(`[a-zA-Z]`) 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*$`) // DefaultHTTPHost Default HTTP Host used if only port is provided to -H flag e.g. docker daemon -H tcp://:8080 - DefaultHTTPHost = "127.0.0.1" + DefaultHTTPHost = "localhost" // DefaultHTTPPort Default HTTP Port used if only the protocol is provided to -H flag e.g. docker daemon -H tcp:// // TODO Windows. DefaultHTTPPort is only used on Windows if a -H parameter @@ -342,7 +342,7 @@ func ValidateLabel(val string) (string, error) { // ValidateHost validates that the specified string is a valid host and returns it. func ValidateHost(val string) (string, error) { - _, err := parsers.ParseDockerDaemonHost(DefaultTCPHost, DefaultUnixSocket, val) + _, err := parsers.ParseDockerDaemonHost(DefaultTCPHost, DefaultTLSHost, DefaultUnixSocket, "", val) if err != nil { return val, err } @@ -352,8 +352,8 @@ func ValidateHost(val string) (string, error) { } // ParseHost and set defaults for a Daemon host string -func ParseHost(val string) (string, error) { - host, err := parsers.ParseDockerDaemonHost(DefaultTCPHost, DefaultUnixSocket, val) +func ParseHost(defaultHost, val string) (string, error) { + host, err := parsers.ParseDockerDaemonHost(DefaultTCPHost, DefaultTLSHost, DefaultUnixSocket, defaultHost, val) if err != nil { return val, err } diff --git a/opts/opts_test.go b/opts/opts_test.go index 0e4e9562e7..baf5f53362 100644 --- a/opts/opts_test.go +++ b/opts/opts_test.go @@ -445,9 +445,9 @@ func TestParseHost(t *testing.T) { "fd://": "fd://", "fd://something": "fd://something", "tcp://host:": "tcp://host:2375", - "tcp://": "tcp://127.0.0.1:2375", - "tcp://:2375": "tcp://127.0.0.1:2375", // default ip address - "tcp://:2376": "tcp://127.0.0.1:2376", // default ip address + "tcp://": "tcp://localhost:2375", + "tcp://:2375": "tcp://localhost:2375", // default ip address + "tcp://:2376": "tcp://localhost:2376", // default ip address "tcp://0.0.0.0:8080": "tcp://0.0.0.0:8080", "tcp://192.168.0.0:12000": "tcp://192.168.0.0:12000", "tcp://192.168:8080": "tcp://192.168:8080", @@ -458,12 +458,12 @@ func TestParseHost(t *testing.T) { } for value, errorMessage := range invalid { - if _, err := ParseHost(value); err == nil || err.Error() != errorMessage { + if _, err := ParseHost(defaultHTTPHost, value); err == nil || err.Error() != errorMessage { t.Fatalf("Expected an error for %v with [%v], got [%v]", value, errorMessage, err) } } for value, expected := range valid { - if actual, err := ParseHost(value); err != nil || actual != expected { + if actual, err := ParseHost(defaultHTTPHost, value); err != nil || actual != expected { t.Fatalf("Expected for %v [%v], got [%v, %v]", value, expected, actual, err) } } diff --git a/pkg/parsers/parsers.go b/pkg/parsers/parsers.go index 14f4b75350..a604a9e12b 100644 --- a/pkg/parsers/parsers.go +++ b/pkg/parsers/parsers.go @@ -17,9 +17,12 @@ import ( // Depending of the address specified, will use the defaultTCPAddr or defaultUnixAddr // defaultUnixAddr must be a absolute file path (no `unix://` prefix) // defaultTCPAddr must be the full `tcp://host:port` form -func ParseDockerDaemonHost(defaultTCPAddr, defaultUnixAddr, addr string) (string, error) { +func ParseDockerDaemonHost(defaultTCPAddr, defaultTLSHost, defaultUnixAddr, defaultAddr, addr string) (string, error) { addr = strings.TrimSpace(addr) if addr == "" { + if defaultAddr == defaultTLSHost { + return defaultTLSHost, nil + } if runtime.GOOS != "windows" { return fmt.Sprintf("unix://%s", defaultUnixAddr), nil } diff --git a/pkg/parsers/parsers_test.go b/pkg/parsers/parsers_test.go index b6fe50a983..47b45281c7 100644 --- a/pkg/parsers/parsers_test.go +++ b/pkg/parsers/parsers_test.go @@ -9,9 +9,10 @@ import ( func TestParseDockerDaemonHost(t *testing.T) { var ( - defaultHTTPHost = "tcp://127.0.0.1:2376" - defaultUnix = "/var/run/docker.sock" - defaultHOST = "unix:///var/run/docker.sock" + defaultHTTPHost = "tcp://localhost:2375" + defaultHTTPSHost = "tcp://localhost:2376" + defaultUnix = "/var/run/docker.sock" + defaultHOST = "unix:///var/run/docker.sock" ) if runtime.GOOS == "windows" { defaultHOST = defaultHTTPHost @@ -28,37 +29,37 @@ func TestParseDockerDaemonHost(t *testing.T) { "fd": "Invalid bind address format: fd", } valids := map[string]string{ - "0.0.0.1:": "tcp://0.0.0.1:2376", + "0.0.0.1:": "tcp://0.0.0.1:2375", "0.0.0.1:5555": "tcp://0.0.0.1:5555", "0.0.0.1:5555/path": "tcp://0.0.0.1:5555/path", - "[::1]:": "tcp://[::1]:2376", + "[::1]:": "tcp://[::1]:2375", "[::1]:5555/path": "tcp://[::1]:5555/path", - "[0:0:0:0:0:0:0:1]:": "tcp://[0:0:0:0:0:0:0:1]:2376", + "[0:0:0:0:0:0:0:1]:": "tcp://[0:0:0:0:0:0:0:1]:2375", "[0:0:0:0:0:0:0:1]:5555/path": "tcp://[0:0:0:0:0:0:0:1]:5555/path", - ":6666": "tcp://127.0.0.1:6666", - ":6666/path": "tcp://127.0.0.1:6666/path", + ":6666": "tcp://localhost:6666", + ":6666/path": "tcp://localhost:6666/path", "": defaultHOST, " ": defaultHOST, " ": defaultHOST, "tcp://": defaultHTTPHost, - "tcp://:7777": "tcp://127.0.0.1:7777", - "tcp://:7777/path": "tcp://127.0.0.1:7777/path", - " tcp://:7777/path ": "tcp://127.0.0.1:7777/path", + "tcp://:7777": "tcp://localhost:7777", + "tcp://:7777/path": "tcp://localhost:7777/path", + " tcp://:7777/path ": "tcp://localhost:7777/path", "unix:///run/docker.sock": "unix:///run/docker.sock", "unix://": "unix:///var/run/docker.sock", "fd://": "fd://", "fd://something": "fd://something", - "localhost:": "tcp://localhost:2376", + "localhost:": "tcp://localhost:2375", "localhost:5555": "tcp://localhost:5555", "localhost:5555/path": "tcp://localhost:5555/path", } for invalidAddr, expectedError := range invalids { - if addr, err := ParseDockerDaemonHost(defaultHTTPHost, defaultUnix, invalidAddr); err == nil || err.Error() != expectedError { + if addr, err := ParseDockerDaemonHost(defaultHTTPHost, defaultHTTPSHost, defaultUnix, "", invalidAddr); err == nil || err.Error() != expectedError { t.Errorf("tcp %v address expected error %v return, got %s and addr %v", invalidAddr, expectedError, err, addr) } } for validAddr, expectedAddr := range valids { - if addr, err := ParseDockerDaemonHost(defaultHTTPHost, defaultUnix, validAddr); err != nil || addr != expectedAddr { + if addr, err := ParseDockerDaemonHost(defaultHTTPHost, defaultHTTPSHost, defaultUnix, "", validAddr); err != nil || addr != expectedAddr { t.Errorf("%v -> expected %v, got (%v) addr (%v)", validAddr, expectedAddr, err, addr) } }