diff --git a/api/client/cli.go b/api/client/cli.go index 5e7f45b119..6bc3fc3507 100644 --- a/api/client/cli.go +++ b/api/client/cli.go @@ -13,6 +13,7 @@ import ( flag "github.com/docker/docker/pkg/mflag" "github.com/docker/docker/pkg/term" "github.com/docker/docker/registry" + "github.com/docker/libtrust" ) type DockerCli struct { @@ -22,6 +23,7 @@ type DockerCli struct { in io.ReadCloser out io.Writer err io.Writer + key libtrust.PrivateKey tlsConfig *tls.Config scheme string // inFd holds file descriptor of the client's STDIN, if it's a valid file @@ -98,7 +100,7 @@ func (cli *DockerCli) LoadConfigFile() (err error) { return err } -func NewDockerCli(in io.ReadCloser, out, err io.Writer, proto, addr string, tlsConfig *tls.Config) *DockerCli { +func NewDockerCli(in io.ReadCloser, out, err io.Writer, key libtrust.PrivateKey, proto, addr string, tlsConfig *tls.Config) *DockerCli { var ( inFd uintptr outFd uintptr @@ -135,6 +137,7 @@ func NewDockerCli(in io.ReadCloser, out, err io.Writer, proto, addr string, tlsC in: in, out: out, err: err, + key: key, inFd: inFd, outFd: outFd, isTerminalIn: isTerminalIn, diff --git a/docker/daemon.go b/docker/daemon.go index eef17efdc4..ed931bc471 100644 --- a/docker/daemon.go +++ b/docker/daemon.go @@ -79,6 +79,7 @@ func mainDaemon() { job.Setenv("TlsCa", *flCa) job.Setenv("TlsCert", *flCert) job.Setenv("TlsKey", *flKey) + job.Setenv("TrustKey", *flTrustKey) job.SetenvBool("BufferRequests", true) if err := job.Run(); err != nil { log.Fatal(err) diff --git a/docker/docker.go b/docker/docker.go index e906b2043e..215f15b3e3 100644 --- a/docker/docker.go +++ b/docker/docker.go @@ -7,6 +7,7 @@ import ( "io/ioutil" "log" "os" + "path" "strings" "github.com/docker/docker/api" @@ -15,12 +16,14 @@ import ( flag "github.com/docker/docker/pkg/mflag" "github.com/docker/docker/reexec" "github.com/docker/docker/utils" + "github.com/docker/libtrust" ) const ( - defaultCaFile = "ca.pem" - defaultKeyFile = "key.pem" - defaultCertFile = "cert.pem" + defaultTrustKeyFile = "key.json" + defaultCaFile = "ca.pem" + defaultKeyFile = "key.pem" + defaultCertFile = "cert.pem" ) func main() { @@ -61,6 +64,20 @@ func main() { } protoAddrParts := strings.SplitN(flHosts[0], "://", 2) + err := os.MkdirAll(path.Dir(*flTrustKey), 0700) + if err != nil { + log.Fatal(err) + } + trustKey, keyErr := libtrust.LoadKeyFile(*flTrustKey) + if keyErr == libtrust.ErrKeyFileDoesNotExist { + trustKey, keyErr = libtrust.GenerateECP256PrivateKey() + if keyErr == nil { + keyErr = libtrust.SaveKey(*flTrustKey, trustKey) + } + } + if keyErr != nil { + log.Fatal(keyErr) + } var ( cli *client.DockerCli tlsConfig tls.Config @@ -95,9 +112,9 @@ func main() { } if *flTls || *flTlsVerify { - cli = client.NewDockerCli(os.Stdin, os.Stdout, os.Stderr, protoAddrParts[0], protoAddrParts[1], &tlsConfig) + cli = client.NewDockerCli(os.Stdin, os.Stdout, os.Stderr, trustKey, protoAddrParts[0], protoAddrParts[1], &tlsConfig) } else { - cli = client.NewDockerCli(os.Stdin, os.Stdout, os.Stderr, protoAddrParts[0], protoAddrParts[1], nil) + cli = client.NewDockerCli(os.Stdin, os.Stdout, os.Stderr, trustKey, protoAddrParts[0], protoAddrParts[1], nil) } if err := cli.Cmd(flag.Args()...); err != nil { diff --git a/docker/flags.go b/docker/flags.go index b6152e22dc..9e8475d33d 100644 --- a/docker/flags.go +++ b/docker/flags.go @@ -29,13 +29,18 @@ var ( flTlsVerify = flag.Bool([]string{"-tlsverify"}, false, "Use TLS and verify the remote (daemon: verify client, client: verify daemon)") // these are initialized in init() below since their default values depend on dockerCertPath which isn't fully initialized until init() runs - flCa *string - flCert *string - flKey *string - flHosts []string + flTrustKey *string + flCa *string + flCert *string + flKey *string + flHosts []string ) func init() { + // placeholder for trust key flag + trustKeyDefault := filepath.Join(dockerCertPath, defaultTrustKeyFile) + flTrustKey = &trustKeyDefault + flCa = flag.String([]string{"-tlscacert"}, filepath.Join(dockerCertPath, defaultCaFile), "Trust only remotes providing a certificate signed by the CA given here") flCert = flag.String([]string{"-tlscert"}, filepath.Join(dockerCertPath, defaultCertFile), "Path to TLS certificate file") flKey = flag.String([]string{"-tlskey"}, filepath.Join(dockerCertPath, defaultKeyFile), "Path to TLS key file") diff --git a/integration/commands_test.go b/integration/commands_test.go index e388e560a6..532e6f79fa 100644 --- a/integration/commands_test.go +++ b/integration/commands_test.go @@ -14,6 +14,7 @@ import ( "github.com/docker/docker/pkg/log" "github.com/docker/docker/pkg/term" "github.com/docker/docker/utils" + "github.com/docker/libtrust" ) func closeWrap(args ...io.Closer) error { @@ -117,8 +118,12 @@ func TestRunDisconnect(t *testing.T) { stdin, stdinPipe := io.Pipe() stdout, stdoutPipe := io.Pipe() + key, err := libtrust.GenerateECP256PrivateKey() + if err != nil { + t.Fatal(err) + } - cli := client.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) + cli := client.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, key, testDaemonProto, testDaemonAddr, nil) defer cleanup(globalEngine, t) c1 := make(chan struct{}) @@ -163,8 +168,12 @@ func TestRunDisconnectTty(t *testing.T) { stdin, stdinPipe := io.Pipe() stdout, stdoutPipe := io.Pipe() + key, err := libtrust.GenerateECP256PrivateKey() + if err != nil { + t.Fatal(err) + } - cli := client.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) + cli := client.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, key, testDaemonProto, testDaemonAddr, nil) defer cleanup(globalEngine, t) c1 := make(chan struct{}) @@ -213,8 +222,12 @@ func TestRunDetach(t *testing.T) { stdin, stdinPipe := io.Pipe() stdout, stdoutPipe := io.Pipe() + key, err := libtrust.GenerateECP256PrivateKey() + if err != nil { + t.Fatal(err) + } - cli := client.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) + cli := client.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, key, testDaemonProto, testDaemonAddr, nil) defer cleanup(globalEngine, t) ch := make(chan struct{}) @@ -260,8 +273,12 @@ func TestRunDetach(t *testing.T) { func TestAttachDetach(t *testing.T) { stdin, stdinPipe := io.Pipe() stdout, stdoutPipe := io.Pipe() + key, err := libtrust.GenerateECP256PrivateKey() + if err != nil { + t.Fatal(err) + } - cli := client.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) + cli := client.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, key, testDaemonProto, testDaemonAddr, nil) defer cleanup(globalEngine, t) ch := make(chan struct{}) @@ -294,7 +311,7 @@ func TestAttachDetach(t *testing.T) { stdin, stdinPipe = io.Pipe() stdout, stdoutPipe = io.Pipe() - cli = client.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) + cli = client.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, key, testDaemonProto, testDaemonAddr, nil) ch = make(chan struct{}) go func() { @@ -341,8 +358,12 @@ func TestAttachDetach(t *testing.T) { func TestAttachDetachTruncatedID(t *testing.T) { stdin, stdinPipe := io.Pipe() stdout, stdoutPipe := io.Pipe() + key, err := libtrust.GenerateECP256PrivateKey() + if err != nil { + t.Fatal(err) + } - cli := client.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) + cli := client.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, key, testDaemonProto, testDaemonAddr, nil) defer cleanup(globalEngine, t) // Discard the CmdRun output @@ -360,7 +381,7 @@ func TestAttachDetachTruncatedID(t *testing.T) { stdin, stdinPipe = io.Pipe() stdout, stdoutPipe = io.Pipe() - cli = client.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) + cli = client.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, key, testDaemonProto, testDaemonAddr, nil) ch := make(chan struct{}) go func() { @@ -406,8 +427,12 @@ func TestAttachDetachTruncatedID(t *testing.T) { func TestAttachDisconnect(t *testing.T) { stdin, stdinPipe := io.Pipe() stdout, stdoutPipe := io.Pipe() + key, err := libtrust.GenerateECP256PrivateKey() + if err != nil { + t.Fatal(err) + } - cli := client.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) + cli := client.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, key, testDaemonProto, testDaemonAddr, nil) defer cleanup(globalEngine, t) go func() { @@ -461,7 +486,7 @@ func TestAttachDisconnect(t *testing.T) { // We closed stdin, expect /bin/cat to still be running // Wait a little bit to make sure container.monitor() did his thing - _, err := container.WaitStop(500 * time.Millisecond) + _, err = container.WaitStop(500 * time.Millisecond) if err == nil || !container.IsRunning() { t.Fatalf("/bin/cat is not running after closing stdin") } @@ -476,7 +501,11 @@ func TestAttachDisconnect(t *testing.T) { func TestRunAutoRemove(t *testing.T) { t.Skip("Fixme. Skipping test for now, race condition") stdout, stdoutPipe := io.Pipe() - cli := client.NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) + key, err := libtrust.GenerateECP256PrivateKey() + if err != nil { + t.Fatal(err) + } + cli := client.NewDockerCli(nil, stdoutPipe, ioutil.Discard, key, testDaemonProto, testDaemonAddr, nil) defer cleanup(globalEngine, t) c := make(chan struct{}) @@ -512,8 +541,12 @@ func TestRunAutoRemove(t *testing.T) { // Expected behaviour: error out when attempting to bind mount non-existing source paths func TestRunErrorBindNonExistingSource(t *testing.T) { + key, err := libtrust.GenerateECP256PrivateKey() + if err != nil { + t.Fatal(err) + } - cli := client.NewDockerCli(nil, nil, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) + cli := client.NewDockerCli(nil, nil, ioutil.Discard, key, testDaemonProto, testDaemonAddr, nil) defer cleanup(globalEngine, t) c := make(chan struct{}) diff --git a/integration/https_test.go b/integration/https_test.go index b15f4e5694..0705dc812f 100644 --- a/integration/https_test.go +++ b/integration/https_test.go @@ -9,6 +9,7 @@ import ( "time" "github.com/docker/docker/api/client" + "github.com/docker/libtrust" ) const ( @@ -37,7 +38,11 @@ func getTlsConfig(certFile, keyFile string, t *testing.T) *tls.Config { // TestHttpsInfo connects via two-way authenticated HTTPS to the info endpoint func TestHttpsInfo(t *testing.T) { - cli := client.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, testDaemonProto, + key, err := libtrust.GenerateECP256PrivateKey() + if err != nil { + t.Fatal(err) + } + cli := client.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, key, testDaemonProto, testDaemonHttpsAddr, getTlsConfig("client-cert.pem", "client-key.pem", t)) setTimeout(t, "Reading command output time out", 10*time.Second, func() { @@ -50,7 +55,11 @@ func TestHttpsInfo(t *testing.T) { // TestHttpsInfoRogueCert connects via two-way authenticated HTTPS to the info endpoint // by using a rogue client certificate and checks that it fails with the expected error. func TestHttpsInfoRogueCert(t *testing.T) { - cli := client.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, testDaemonProto, + key, err := libtrust.GenerateECP256PrivateKey() + if err != nil { + t.Fatal(err) + } + cli := client.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, key, testDaemonProto, testDaemonHttpsAddr, getTlsConfig("client-rogue-cert.pem", "client-rogue-key.pem", t)) setTimeout(t, "Reading command output time out", 10*time.Second, func() { @@ -67,7 +76,11 @@ func TestHttpsInfoRogueCert(t *testing.T) { // TestHttpsInfoRogueServerCert connects via two-way authenticated HTTPS to the info endpoint // which provides a rogue server certificate and checks that it fails with the expected error func TestHttpsInfoRogueServerCert(t *testing.T) { - cli := client.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, testDaemonProto, + key, err := libtrust.GenerateECP256PrivateKey() + if err != nil { + t.Fatal(err) + } + cli := client.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, key, testDaemonProto, testDaemonRogueHttpsAddr, getTlsConfig("client-cert.pem", "client-key.pem", t)) setTimeout(t, "Reading command output time out", 10*time.Second, func() {