Browse code

Add authenticated TLS support for API

Docker-DCO-1.1-Signed-off-by: Johannes 'fish' Ziemke <github@freigeist.org> (github: discordianfish)

Johannes 'fish' Ziemke authored on 2014/02/27 21:47:59
Showing 19 changed files
... ...
@@ -3,6 +3,7 @@ package api
3 3
 import (
4 4
 	"bufio"
5 5
 	"bytes"
6
+	"crypto/tls"
6 7
 	"encoding/base64"
7 8
 	"encoding/json"
8 9
 	"errors"
... ...
@@ -57,8 +58,8 @@ func (cli *DockerCli) getMethod(name string) (func(...string) error, bool) {
57 57
 	return method.Interface().(func(...string) error), true
58 58
 }
59 59
 
60
-func ParseCommands(proto, addr string, args ...string) error {
61
-	cli := NewDockerCli(os.Stdin, os.Stdout, os.Stderr, proto, addr)
60
+func ParseCommands(proto, addr string, tlsConfig *tls.Config, args ...string) error {
61
+	cli := NewDockerCli(os.Stdin, os.Stdout, os.Stderr, proto, addr, tlsConfig)
62 62
 
63 63
 	if len(args) > 0 {
64 64
 		method, exists := cli.getMethod(args[0])
... ...
@@ -2026,6 +2027,13 @@ func (cli *DockerCli) CmdLoad(args ...string) error {
2026 2026
 	return nil
2027 2027
 }
2028 2028
 
2029
+func (cli *DockerCli) dial() (net.Conn, error) {
2030
+	if cli.tlsConfig != nil && cli.proto != "unix" {
2031
+		return tls.Dial(cli.proto, cli.addr, cli.tlsConfig)
2032
+	}
2033
+	return net.Dial(cli.proto, cli.addr)
2034
+}
2035
+
2029 2036
 func (cli *DockerCli) call(method, path string, data interface{}, passAuthInfo bool) (io.ReadCloser, int, error) {
2030 2037
 	params := bytes.NewBuffer(nil)
2031 2038
 	if data != nil {
... ...
@@ -2078,7 +2086,7 @@ func (cli *DockerCli) call(method, path string, data interface{}, passAuthInfo b
2078 2078
 	} else if method == "POST" {
2079 2079
 		req.Header.Set("Content-Type", "plain/text")
2080 2080
 	}
2081
-	dial, err := net.Dial(cli.proto, cli.addr)
2081
+	dial, err := cli.dial()
2082 2082
 	if err != nil {
2083 2083
 		if strings.Contains(err.Error(), "connection refused") {
2084 2084
 			return nil, -1, ErrConnectionRefused
... ...
@@ -2140,7 +2148,7 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer, h
2140 2140
 		}
2141 2141
 	}
2142 2142
 
2143
-	dial, err := net.Dial(cli.proto, cli.addr)
2143
+	dial, err := cli.dial()
2144 2144
 	if err != nil {
2145 2145
 		if strings.Contains(err.Error(), "connection refused") {
2146 2146
 			return fmt.Errorf("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?")
... ...
@@ -2196,7 +2204,7 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.Rea
2196 2196
 	req.Header.Set("Content-Type", "plain/text")
2197 2197
 	req.Host = cli.addr
2198 2198
 
2199
-	dial, err := net.Dial(cli.proto, cli.addr)
2199
+	dial, err := cli.dial()
2200 2200
 	if err != nil {
2201 2201
 		if strings.Contains(err.Error(), "connection refused") {
2202 2202
 			return fmt.Errorf("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?")
... ...
@@ -2388,7 +2396,7 @@ func readBody(stream io.ReadCloser, statusCode int, err error) ([]byte, int, err
2388 2388
 	return body, statusCode, nil
2389 2389
 }
2390 2390
 
2391
-func NewDockerCli(in io.ReadCloser, out, err io.Writer, proto, addr string) *DockerCli {
2391
+func NewDockerCli(in io.ReadCloser, out, err io.Writer, proto, addr string, tlsConfig *tls.Config) *DockerCli {
2392 2392
 	var (
2393 2393
 		isTerminal = false
2394 2394
 		terminalFd uintptr
... ...
@@ -2412,6 +2420,7 @@ func NewDockerCli(in io.ReadCloser, out, err io.Writer, proto, addr string) *Doc
2412 2412
 		err:        err,
2413 2413
 		isTerminal: isTerminal,
2414 2414
 		terminalFd: terminalFd,
2415
+		tlsConfig:  tlsConfig,
2415 2416
 	}
2416 2417
 }
2417 2418
 
... ...
@@ -2424,4 +2433,5 @@ type DockerCli struct {
2424 2424
 	err        io.Writer
2425 2425
 	isTerminal bool
2426 2426
 	terminalFd uintptr
2427
+	tlsConfig  *tls.Config
2427 2428
 }
... ...
@@ -4,6 +4,8 @@ import (
4 4
 	"bufio"
5 5
 	"bytes"
6 6
 	"code.google.com/p/go.net/websocket"
7
+	"crypto/tls"
8
+	"crypto/x509"
7 9
 	"encoding/base64"
8 10
 	"encoding/json"
9 11
 	"expvar"
... ...
@@ -1129,9 +1131,8 @@ func changeGroup(addr string, nameOrGid string) error {
1129 1129
 
1130 1130
 // ListenAndServe sets up the required http.Server and gets it listening for
1131 1131
 // each addr passed in and does protocol specific checking.
1132
-func ListenAndServe(proto, addr string, eng *engine.Engine, logging, enableCors bool, dockerVersion string, socketGroup string) error {
1133
-	r, err := createRouter(eng, logging, enableCors, dockerVersion)
1134
-
1132
+func ListenAndServe(proto, addr string, job *engine.Job) error {
1133
+	r, err := createRouter(job.Eng, job.GetenvBool("Logging"), job.GetenvBool("EnableCors"), job.Getenv("Version"))
1135 1134
 	if err != nil {
1136 1135
 		return err
1137 1136
 	}
... ...
@@ -1151,17 +1152,43 @@ func ListenAndServe(proto, addr string, eng *engine.Engine, logging, enableCors
1151 1151
 		return err
1152 1152
 	}
1153 1153
 
1154
+	if proto != "unix" && (job.GetenvBool("Tls") || job.GetenvBool("TlsVerify")) {
1155
+		tlsCert := job.Getenv("TlsCert")
1156
+		tlsKey := job.Getenv("TlsKey")
1157
+		cert, err := tls.LoadX509KeyPair(tlsCert, tlsKey)
1158
+		if err != nil {
1159
+			return fmt.Errorf("Couldn't load X509 key pair (%s, %s): %s. Key encrypted?",
1160
+				tlsCert, tlsKey, err)
1161
+		}
1162
+		tlsConfig := &tls.Config{
1163
+			NextProtos:   []string{"http/1.1"},
1164
+			Certificates: []tls.Certificate{cert},
1165
+		}
1166
+		if job.GetenvBool("TlsVerify") {
1167
+			certPool := x509.NewCertPool()
1168
+			file, err := ioutil.ReadFile(job.Getenv("TlsCa"))
1169
+			if err != nil {
1170
+				return fmt.Errorf("Couldn't read CA certificate: %s", err)
1171
+			}
1172
+			certPool.AppendCertsFromPEM(file)
1173
+
1174
+			tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
1175
+			tlsConfig.ClientCAs = certPool
1176
+		}
1177
+		l = tls.NewListener(l, tlsConfig)
1178
+	}
1179
+
1154 1180
 	// Basic error and sanity checking
1155 1181
 	switch proto {
1156 1182
 	case "tcp":
1157
-		if !strings.HasPrefix(addr, "127.0.0.1") {
1183
+		if !strings.HasPrefix(addr, "127.0.0.1") && !job.GetenvBool("TlsVerify") {
1158 1184
 			log.Println("/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\")
1159 1185
 		}
1160 1186
 	case "unix":
1161 1187
 		if err := os.Chmod(addr, 0660); err != nil {
1162 1188
 			return err
1163 1189
 		}
1164
-
1190
+		socketGroup := job.Getenv("SocketGroup")
1165 1191
 		if socketGroup != "" {
1166 1192
 			if err := changeGroup(addr, socketGroup); err != nil {
1167 1193
 				if socketGroup == "docker" {
... ...
@@ -1197,7 +1224,7 @@ func ServeApi(job *engine.Job) engine.Status {
1197 1197
 		protoAddrParts := strings.SplitN(protoAddr, "://", 2)
1198 1198
 		go func() {
1199 1199
 			log.Printf("Listening for HTTP on %s (%s)\n", protoAddrParts[0], protoAddrParts[1])
1200
-			chErrors <- ListenAndServe(protoAddrParts[0], protoAddrParts[1], job.Eng, job.GetenvBool("Logging"), job.GetenvBool("EnableCors"), job.Getenv("Version"), job.Getenv("SocketGroup"))
1200
+			chErrors <- ListenAndServe(protoAddrParts[0], protoAddrParts[1], job)
1201 1201
 		}()
1202 1202
 	}
1203 1203
 
... ...
@@ -70,7 +70,7 @@ func main() {
70 70
 	bufErr := bytes.NewBuffer(nil)
71 71
 
72 72
 	// Instanciate the Docker CLI
73
-	cli := docker.NewDockerCli(nil, bufOut, bufErr, "unix", "/var/run/docker.sock")
73
+	cli := docker.NewDockerCli(nil, bufOut, bufErr, "unix", "/var/run/docker.sock", false, nil)
74 74
 	// Retrieve the container info
75 75
 	if err := cli.CmdInspect(flag.Arg(0)); err != nil {
76 76
 		// As of docker v0.6.3, CmdInspect always returns nil
... ...
@@ -1,7 +1,10 @@
1 1
 package main
2 2
 
3 3
 import (
4
+	"crypto/tls"
5
+	"crypto/x509"
4 6
 	"fmt"
7
+	"io/ioutil"
5 8
 	"log"
6 9
 	"os"
7 10
 	"strings"
... ...
@@ -16,6 +19,16 @@ import (
16 16
 	"github.com/dotcloud/docker/utils"
17 17
 )
18 18
 
19
+const (
20
+	defaultCaFile   = "ca.pem"
21
+	defaultKeyFile  = "key.pem"
22
+	defaultCertFile = "cert.pem"
23
+)
24
+
25
+var (
26
+	dockerConfDir = os.Getenv("HOME") + "/.docker/"
27
+)
28
+
19 29
 func main() {
20 30
 	if selfPath := utils.SelfPath(); strings.Contains(selfPath, ".dockerinit") {
21 31
 		// Running in init mode
... ...
@@ -43,6 +56,11 @@ func main() {
43 43
 		flExecDriver         = flag.String([]string{"e", "-exec-driver"}, "native", "Force the docker runtime to use a specific exec driver")
44 44
 		flHosts              = opts.NewListOpts(api.ValidateHost)
45 45
 		flMtu                = flag.Int([]string{"#mtu", "-mtu"}, 0, "Set the containers network MTU; if no value is provided: default to the default route MTU or 1500 if no default route is available")
46
+		flTls                = flag.Bool([]string{"-tls"}, false, "Use TLS; implied by tls-verify flags")
47
+		flTlsVerify          = flag.Bool([]string{"-tlsverify"}, false, "Use TLS and verify the remote (daemon: verify client, client: verify daemon)")
48
+		flCa                 = flag.String([]string{"-tlscacert"}, dockerConfDir+defaultCaFile, "Trust only remotes providing a certificate signed by the CA given here")
49
+		flCert               = flag.String([]string{"-tlscert"}, dockerConfDir+defaultCertFile, "Path to TLS certificate file")
50
+		flKey                = flag.String([]string{"-tlskey"}, dockerConfDir+defaultKeyFile, "Path to TLS key file")
46 51
 	)
47 52
 	flag.Var(&flDns, []string{"#dns", "-dns"}, "Force docker to use specific DNS servers")
48 53
 	flag.Var(&flHosts, []string{"H", "-host"}, "tcp://host:port, unix://path/to/socket, fd://* or fd://socketfd to use in daemon mode. Multiple sockets can be specified")
... ...
@@ -73,6 +91,7 @@ func main() {
73 73
 	if *flDebug {
74 74
 		os.Setenv("DEBUG", "1")
75 75
 	}
76
+
76 77
 	if *flDaemon {
77 78
 		if flag.NArg() != 0 {
78 79
 			flag.Usage()
... ...
@@ -140,6 +159,12 @@ func main() {
140 140
 		job.SetenvBool("EnableCors", *flEnableCors)
141 141
 		job.Setenv("Version", dockerversion.VERSION)
142 142
 		job.Setenv("SocketGroup", *flSocketGroup)
143
+
144
+		job.SetenvBool("Tls", *flTls)
145
+		job.SetenvBool("TlsVerify", *flTlsVerify)
146
+		job.Setenv("TlsCa", *flCa)
147
+		job.Setenv("TlsCert", *flCert)
148
+		job.Setenv("TlsKey", *flKey)
143 149
 		if err := job.Run(); err != nil {
144 150
 			log.Fatal(err)
145 151
 		}
... ...
@@ -148,14 +173,53 @@ func main() {
148 148
 			log.Fatal("Please specify only one -H")
149 149
 		}
150 150
 		protoAddrParts := strings.SplitN(flHosts.GetAll()[0], "://", 2)
151
-		if err := api.ParseCommands(protoAddrParts[0], protoAddrParts[1], flag.Args()...); err != nil {
152
-			if sterr, ok := err.(*utils.StatusError); ok {
151
+
152
+		var (
153
+			errc      error
154
+			tlsConfig tls.Config
155
+		)
156
+		tlsConfig.InsecureSkipVerify = true
157
+
158
+		// If we should verify the server, we need to load a trusted ca
159
+		if *flTlsVerify {
160
+			*flTls = true
161
+			certPool := x509.NewCertPool()
162
+			file, err := ioutil.ReadFile(*flCa)
163
+			if err != nil {
164
+				log.Fatalf("Couldn't read ca cert %s: %s", *flCa, err)
165
+			}
166
+			certPool.AppendCertsFromPEM(file)
167
+			tlsConfig.RootCAs = certPool
168
+			tlsConfig.InsecureSkipVerify = false
169
+		}
170
+
171
+		// If tls is enabled, try to load and send client certificates
172
+		if *flTls || *flTlsVerify {
173
+			_, errCert := os.Stat(*flCert)
174
+			_, errKey := os.Stat(*flKey)
175
+			if errCert == nil && errKey == nil {
176
+				*flTls = true
177
+				cert, err := tls.LoadX509KeyPair(*flCert, *flKey)
178
+				if err != nil {
179
+					log.Fatalf("Couldn't load X509 key pair: %s. Key encrypted?", err)
180
+				}
181
+				tlsConfig.Certificates = []tls.Certificate{cert}
182
+			}
183
+		}
184
+
185
+		if *flTls || *flTlsVerify {
186
+			errc = api.ParseCommands(protoAddrParts[0], protoAddrParts[1], &tlsConfig, flag.Args()...)
187
+		} else {
188
+			errc = api.ParseCommands(protoAddrParts[0], protoAddrParts[1], nil, flag.Args()...)
189
+		}
190
+		if errc != nil {
191
+			if sterr, ok := errc.(*utils.StatusError); ok {
153 192
 				if sterr.Status != "" {
154 193
 					log.Println(sterr.Status)
155 194
 				}
156 195
 				os.Exit(sterr.StatusCode)
157 196
 			}
158
-			log.Fatal(err)
197
+			log.Fatal(errc)
159 198
 		}
160 199
 	}
161 200
 }
162 201
new file mode 100644
... ...
@@ -0,0 +1,126 @@
0
+:title: Docker HTTPS Setup
1
+:description: How to setup docker with https
2
+:keywords: docker, example, https, daemon
3
+
4
+.. _running_docker_https:
5
+
6
+Running Docker with https
7
+=========================
8
+
9
+By default, Docker runs via a non-networked Unix socket. It can also optionally
10
+communicate using a HTTP socket.
11
+
12
+If you need Docker reachable via the network in a safe manner, you can enable
13
+TLS by specifying the `tlsverify` flag and pointing Docker's `tlscacert` flag to a
14
+trusted CA certificate.
15
+
16
+In daemon mode, it will only allow connections from clients authenticated by a
17
+certificate signed by that CA. In client mode, it will only connect to servers
18
+with a certificate signed by that CA.
19
+
20
+.. warning::
21
+
22
+  Using TLS and managing a CA is an advanced topic. Please make you self familiar
23
+  with openssl, x509 and tls before using it in production.
24
+
25
+Create a CA, server and client keys with OpenSSL
26
+------------------------------------------------
27
+
28
+First, initialize the CA serial file and generate CA private and public keys:
29
+
30
+.. code-block:: bash
31
+
32
+    $ echo 01 > ca.srl
33
+    $ openssl genrsa -des3 -out ca-key.pem
34
+    $ openssl req -new -x509 -days 365 -key ca-key.pem -out ca.pem
35
+
36
+Now that we have a CA, you can create a server key and certificate signing request.
37
+Make sure that `"Common Name (e.g. server FQDN or YOUR name)"` matches the hostname you will use
38
+to connect to Docker or just use '*' for a certificate valid for any hostname:
39
+
40
+.. code-block:: bash
41
+
42
+    $ openssl genrsa -des3 -out server-key.pem
43
+    $ openssl req -new -key server-key.pem -out server.csr
44
+
45
+Next we're going to sign the key with our CA:
46
+
47
+.. code-block:: bash
48
+
49
+    $ openssl x509 -req -days 365 -in server.csr -CA ca.pem -CAkey ca-key.pem \
50
+      -out server-cert.pem
51
+
52
+For client authentication, create a client key and certificate signing request:
53
+
54
+.. code-block:: bash
55
+
56
+    $ openssl genrsa -des3 -out client-key.pem
57
+    $ openssl req -new -key client-key.pem -out client.csr
58
+
59
+
60
+To make the key suitable for client authentication, create a extensions config file:
61
+
62
+.. code-block:: bash
63
+
64
+    $ echo extendedKeyUsage = clientAuth > extfile.cnf
65
+
66
+Now sign the key:
67
+
68
+.. code-block:: bash
69
+
70
+    $ openssl x509 -req -days 365 -in client.csr -CA ca.pem -CAkey ca-key.pem \
71
+      -out client-cert.pem -extfile extfile.cnf
72
+
73
+Finally you need to remove the passphrase from the client and server key:
74
+
75
+.. code-block:: bash
76
+
77
+    $ openssl rsa -in server-key.pem -out server-key.pem
78
+    $ openssl rsa -in client-key.pem -out client-key.pem
79
+  
80
+Now you can make the Docker daemon only accept connections from clients providing
81
+a certificate trusted by our CA:
82
+
83
+.. code-block:: bash
84
+
85
+    $ sudo docker -d --tlsverify --tlscacert=ca.pem --tlscert=server-cert.pem --tlskey=server-key.pem \
86
+      -H=0.0.0.0:4243
87
+
88
+To be able to connect to Docker and validate its certificate, you now need to provide your client keys,
89
+certificates and trusted CA:
90
+
91
+.. code-block:: bash
92
+
93
+   $ docker --tlsverify --tlscacert=ca.pem --tlscert=client-cert.pem --tlskey=client-key.pem \
94
+     -H=dns-name-of-docker-host:4243
95
+
96
+.. warning::
97
+
98
+  As shown in the example above, you don't have to run the ``docker``
99
+  client  with ``sudo`` or the ``docker`` group when you use
100
+  certificate authentication. That means anyone with the keys can
101
+  give any instructions to your Docker daemon, giving them root
102
+  access to the machine hosting the daemon. Guard these keys as you
103
+  would a root password!
104
+
105
+Other modes
106
+-----------
107
+If you don't want to have complete two-way authentication, you can run Docker in
108
+various other modes by mixing the flags.
109
+
110
+Daemon modes
111
+~~~~~~~~~~~~
112
+- tlsverify, tlscacert, tlscert, tlskey set: Authenticate clients
113
+- tls, tlscert, tlskey: Do not authenticate clients
114
+
115
+Client modes
116
+~~~~~~~~~~~~
117
+- tls: Authenticate server based on public/default CA pool
118
+- tlsverify, tlscacert: Authenticate server based on given CA
119
+- tls, tlscert, tlskey: Authenticate with client certificate, do not authenticate
120
+  server based on given CA
121
+- tlsverify, tlscacert, tlscert, tlskey: Authenticate with client certificate,
122
+  authenticate server based on given CA
123
+
124
+The client will send its client certificate if found, so you just need to drop
125
+your keys into `~/.docker/<ca, cert or key>.pem`
... ...
@@ -26,3 +26,4 @@ to more substantial services like those which you might find in production.
26 26
    using_supervisord
27 27
    cfengine_process_management
28 28
    python_web_app
29
+   https
... ...
@@ -86,6 +86,11 @@ Commands
86 86
       -s, --storage-driver="": Force the docker runtime to use a specific storage driver
87 87
       -e, --exec-driver="native": Force the docker runtime to use a specific exec driver
88 88
       -v, --version=false: Print version information and quit
89
+      --tls=false: Use TLS; implied by tls-verify flags
90
+      --tlscacert="~/.docker/ca.pem": Trust only remotes providing a certificate signed by the CA given here
91
+      --tlscert="~/.docker/cert.pem": Path to TLS certificate file
92
+      --tlskey="~/.docker/key.pem": Path to TLS key file
93
+      --tlsverify=false: Use TLS and verify the remote (daemon: verify client, client: verify daemon)
89 94
       --mtu=0: Set the containers network MTU; if no value is provided: default to the default route MTU or 1500 if no default route is available
90 95
 
91 96
 The Docker daemon is the persistent process that manages containers.  Docker uses the same binary for both the 
... ...
@@ -120,7 +120,7 @@ func assertPipe(input, output string, r io.Reader, w io.Writer, count int) error
120 120
 func TestRunHostname(t *testing.T) {
121 121
 	stdout, stdoutPipe := io.Pipe()
122 122
 
123
-	cli := api.NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
123
+	cli := api.NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil)
124 124
 	defer cleanup(globalEngine, t)
125 125
 
126 126
 	c := make(chan struct{})
... ...
@@ -165,7 +165,7 @@ func TestRunHostname(t *testing.T) {
165 165
 func TestRunWorkdir(t *testing.T) {
166 166
 	stdout, stdoutPipe := io.Pipe()
167 167
 
168
-	cli := api.NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
168
+	cli := api.NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil)
169 169
 	defer cleanup(globalEngine, t)
170 170
 
171 171
 	c := make(chan struct{})
... ...
@@ -210,7 +210,7 @@ func TestRunWorkdir(t *testing.T) {
210 210
 func TestRunWorkdirExists(t *testing.T) {
211 211
 	stdout, stdoutPipe := io.Pipe()
212 212
 
213
-	cli := api.NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
213
+	cli := api.NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil)
214 214
 	defer cleanup(globalEngine, t)
215 215
 
216 216
 	c := make(chan struct{})
... ...
@@ -255,7 +255,7 @@ func TestRunExit(t *testing.T) {
255 255
 	stdin, stdinPipe := io.Pipe()
256 256
 	stdout, stdoutPipe := io.Pipe()
257 257
 
258
-	cli := api.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
258
+	cli := api.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil)
259 259
 	defer cleanup(globalEngine, t)
260 260
 
261 261
 	c1 := make(chan struct{})
... ...
@@ -308,7 +308,7 @@ func TestRunDisconnect(t *testing.T) {
308 308
 	stdin, stdinPipe := io.Pipe()
309 309
 	stdout, stdoutPipe := io.Pipe()
310 310
 
311
-	cli := api.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
311
+	cli := api.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil)
312 312
 	defer cleanup(globalEngine, t)
313 313
 
314 314
 	c1 := make(chan struct{})
... ...
@@ -354,7 +354,7 @@ func TestRunDisconnectTty(t *testing.T) {
354 354
 	stdin, stdinPipe := io.Pipe()
355 355
 	stdout, stdoutPipe := io.Pipe()
356 356
 
357
-	cli := api.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
357
+	cli := api.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil)
358 358
 	defer cleanup(globalEngine, t)
359 359
 
360 360
 	c1 := make(chan struct{})
... ...
@@ -406,7 +406,7 @@ func TestRunAttachStdin(t *testing.T) {
406 406
 	stdin, stdinPipe := io.Pipe()
407 407
 	stdout, stdoutPipe := io.Pipe()
408 408
 
409
-	cli := api.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
409
+	cli := api.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil)
410 410
 	defer cleanup(globalEngine, t)
411 411
 
412 412
 	ch := make(chan struct{})
... ...
@@ -470,7 +470,7 @@ func TestRunDetach(t *testing.T) {
470 470
 	stdin, stdinPipe := io.Pipe()
471 471
 	stdout, stdoutPipe := io.Pipe()
472 472
 
473
-	cli := api.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
473
+	cli := api.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil)
474 474
 	defer cleanup(globalEngine, t)
475 475
 
476 476
 	ch := make(chan struct{})
... ...
@@ -517,7 +517,7 @@ func TestAttachDetach(t *testing.T) {
517 517
 	stdin, stdinPipe := io.Pipe()
518 518
 	stdout, stdoutPipe := io.Pipe()
519 519
 
520
-	cli := api.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
520
+	cli := api.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil)
521 521
 	defer cleanup(globalEngine, t)
522 522
 
523 523
 	ch := make(chan struct{})
... ...
@@ -550,7 +550,7 @@ func TestAttachDetach(t *testing.T) {
550 550
 
551 551
 	stdin, stdinPipe = io.Pipe()
552 552
 	stdout, stdoutPipe = io.Pipe()
553
-	cli = api.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
553
+	cli = api.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil)
554 554
 
555 555
 	ch = make(chan struct{})
556 556
 	go func() {
... ...
@@ -598,7 +598,7 @@ func TestAttachDetachTruncatedID(t *testing.T) {
598 598
 	stdin, stdinPipe := io.Pipe()
599 599
 	stdout, stdoutPipe := io.Pipe()
600 600
 
601
-	cli := api.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
601
+	cli := api.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil)
602 602
 	defer cleanup(globalEngine, t)
603 603
 
604 604
 	// Discard the CmdRun output
... ...
@@ -616,7 +616,7 @@ func TestAttachDetachTruncatedID(t *testing.T) {
616 616
 
617 617
 	stdin, stdinPipe = io.Pipe()
618 618
 	stdout, stdoutPipe = io.Pipe()
619
-	cli = api.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
619
+	cli = api.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil)
620 620
 
621 621
 	ch := make(chan struct{})
622 622
 	go func() {
... ...
@@ -663,7 +663,7 @@ func TestAttachDisconnect(t *testing.T) {
663 663
 	stdin, stdinPipe := io.Pipe()
664 664
 	stdout, stdoutPipe := io.Pipe()
665 665
 
666
-	cli := api.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
666
+	cli := api.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil)
667 667
 	defer cleanup(globalEngine, t)
668 668
 
669 669
 	go func() {
... ...
@@ -732,7 +732,7 @@ func TestAttachDisconnect(t *testing.T) {
732 732
 func TestRunAutoRemove(t *testing.T) {
733 733
 	t.Skip("Fixme. Skipping test for now, race condition")
734 734
 	stdout, stdoutPipe := io.Pipe()
735
-	cli := api.NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
735
+	cli := api.NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil)
736 736
 	defer cleanup(globalEngine, t)
737 737
 
738 738
 	c := make(chan struct{})
... ...
@@ -768,7 +768,7 @@ func TestRunAutoRemove(t *testing.T) {
768 768
 
769 769
 func TestCmdLogs(t *testing.T) {
770 770
 	t.Skip("Test not impemented")
771
-	cli := api.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, testDaemonProto, testDaemonAddr)
771
+	cli := api.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, testDaemonProto, testDaemonAddr, nil)
772 772
 	defer cleanup(globalEngine, t)
773 773
 
774 774
 	if err := cli.CmdRun(unitTestImageID, "sh", "-c", "ls -l"); err != nil {
... ...
@@ -786,7 +786,7 @@ func TestCmdLogs(t *testing.T) {
786 786
 // Expected behaviour: error out when attempting to bind mount non-existing source paths
787 787
 func TestRunErrorBindNonExistingSource(t *testing.T) {
788 788
 
789
-	cli := api.NewDockerCli(nil, nil, ioutil.Discard, testDaemonProto, testDaemonAddr)
789
+	cli := api.NewDockerCli(nil, nil, ioutil.Discard, testDaemonProto, testDaemonAddr, nil)
790 790
 	defer cleanup(globalEngine, t)
791 791
 
792 792
 	c := make(chan struct{})
... ...
@@ -806,7 +806,7 @@ func TestRunErrorBindNonExistingSource(t *testing.T) {
806 806
 func TestImagesViz(t *testing.T) {
807 807
 	stdout, stdoutPipe := io.Pipe()
808 808
 
809
-	cli := api.NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
809
+	cli := api.NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil)
810 810
 	defer cleanup(globalEngine, t)
811 811
 
812 812
 	image := buildTestImages(t, globalEngine)
... ...
@@ -856,7 +856,7 @@ func TestImagesViz(t *testing.T) {
856 856
 func TestImagesTree(t *testing.T) {
857 857
 	stdout, stdoutPipe := io.Pipe()
858 858
 
859
-	cli := api.NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
859
+	cli := api.NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil)
860 860
 	defer cleanup(globalEngine, t)
861 861
 
862 862
 	image := buildTestImages(t, globalEngine)
... ...
@@ -939,7 +939,7 @@ func TestRunCidFile(t *testing.T) {
939 939
 	}
940 940
 	tmpCidFile := path.Join(tmpDir, "cid")
941 941
 
942
-	cli := api.NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
942
+	cli := api.NewDockerCli(nil, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil)
943 943
 	defer cleanup(globalEngine, t)
944 944
 
945 945
 	c := make(chan struct{})
... ...
@@ -989,7 +989,7 @@ func TestContainerOrphaning(t *testing.T) {
989 989
 	defer os.RemoveAll(tmpDir)
990 990
 
991 991
 	// setup a CLI and server
992
-	cli := api.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, testDaemonProto, testDaemonAddr)
992
+	cli := api.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, testDaemonProto, testDaemonAddr, nil)
993 993
 	defer cleanup(globalEngine, t)
994 994
 	srv := mkServerFromEngine(globalEngine, t)
995 995
 
... ...
@@ -1049,8 +1049,8 @@ func TestCmdKill(t *testing.T) {
1049 1049
 	var (
1050 1050
 		stdin, stdinPipe   = io.Pipe()
1051 1051
 		stdout, stdoutPipe = io.Pipe()
1052
-		cli                = api.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr)
1053
-		cli2               = api.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, testDaemonProto, testDaemonAddr)
1052
+		cli                = api.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil)
1053
+		cli2               = api.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, testDaemonProto, testDaemonAddr, nil)
1054 1054
 	)
1055 1055
 	defer cleanup(globalEngine, t)
1056 1056
 
1057 1057
new file mode 100644
... ...
@@ -0,0 +1,23 @@
0
+-----BEGIN CERTIFICATE-----
1
+MIID0TCCAzqgAwIBAgIJAP2r7GqEJwSnMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD
2
+VQQGEwJVUzELMAkGA1UECBMCQ0ExFTATBgNVBAcTDFNhbkZyYW5jaXNjbzEVMBMG
3
+A1UEChMMRm9ydC1GdW5zdG9uMREwDwYDVQQLEwhjaGFuZ2VtZTERMA8GA1UEAxMI
4
+Y2hhbmdlbWUxETAPBgNVBCkTCGNoYW5nZW1lMR8wHQYJKoZIhvcNAQkBFhBtYWls
5
+QGhvc3QuZG9tYWluMB4XDTEzMTIwMzE2NTYzMFoXDTIzMTIwMTE2NTYzMFowgaIx
6
+CzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEVMBMGA1UEBxMMU2FuRnJhbmNpc2Nv
7
+MRUwEwYDVQQKEwxGb3J0LUZ1bnN0b24xETAPBgNVBAsTCGNoYW5nZW1lMREwDwYD
8
+VQQDEwhjaGFuZ2VtZTERMA8GA1UEKRMIY2hhbmdlbWUxHzAdBgkqhkiG9w0BCQEW
9
+EG1haWxAaG9zdC5kb21haW4wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALAn
10
+0xDw+5y7ZptQacq66pUhRu82JP2WU6IDgo5QUtNU6/CX5PwQATe/OnYTZQFbksxp
11
+AU9boG0FCkgxfsgPYXEuZxVEGKI2fxfKHOZZI8mrkWmj6eWU/0cvCjGVc9rTITP5
12
+sNQvg+hORyVDdNp2IdsbMJayiB3AQYMFx3vSDOMTAgMBAAGjggELMIIBBzAdBgNV
13
+HQ4EFgQUZu7DFz09q0QBa2+ymRm9qgK1NPswgdcGA1UdIwSBzzCBzIAUZu7DFz09
14
+q0QBa2+ymRm9qgK1NPuhgaikgaUwgaIxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJD
15
+QTEVMBMGA1UEBxMMU2FuRnJhbmNpc2NvMRUwEwYDVQQKEwxGb3J0LUZ1bnN0b24x
16
+ETAPBgNVBAsTCGNoYW5nZW1lMREwDwYDVQQDEwhjaGFuZ2VtZTERMA8GA1UEKRMI
17
+Y2hhbmdlbWUxHzAdBgkqhkiG9w0BCQEWEG1haWxAaG9zdC5kb21haW6CCQD9q+xq
18
+hCcEpzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAF8fJKKM+/oOdnNi
19
+zEd0M1+PmZOyqvjYQn/2ZR8UHH6Imgc/OPQKZXf0bVE1Txc/DaUNn9Isd1SuCuaE
20
+ic3vAIYYU7PmgeNN6vwec48V96T7jr+GAi6AVMhQEc2hHCfVtx11Xx+x6aHDZzJt
21
+Zxtf5lL6KSO9Y+EFwM+rju6hm5hW
22
+-----END CERTIFICATE-----
0 23
new file mode 100644
... ...
@@ -0,0 +1,73 @@
0
+Certificate:
1
+    Data:
2
+        Version: 3 (0x2)
3
+        Serial Number: 3 (0x3)
4
+    Signature Algorithm: sha1WithRSAEncryption
5
+        Issuer: C=US, ST=CA, L=SanFrancisco, O=Fort-Funston, OU=changeme, CN=changeme/name=changeme/emailAddress=mail@host.domain
6
+        Validity
7
+            Not Before: Dec  4 14:17:54 2013 GMT
8
+            Not After : Dec  2 14:17:54 2023 GMT
9
+        Subject: C=US, ST=CA, L=SanFrancisco, O=Fort-Funston, OU=changeme, CN=client/name=changeme/emailAddress=mail@host.domain
10
+        Subject Public Key Info:
11
+            Public Key Algorithm: rsaEncryption
12
+                Public-Key: (1024 bit)
13
+                Modulus:
14
+                    00:ca:c9:05:d0:09:4e:3e:a4:fc:d5:14:f4:a5:e8:
15
+                    34:d3:6b:51:e3:f3:62:ea:a1:f0:e8:ed:c4:2a:bc:
16
+                    f0:4f:ca:07:df:e3:88:fa:f4:21:99:35:0e:3d:ea:
17
+                    b0:86:e7:c4:d2:8a:83:2b:42:b8:ec:a3:99:62:70:
18
+                    81:46:cc:fc:a5:1d:d2:63:e8:eb:07:25:9a:e2:25:
19
+                    6d:11:56:f2:1a:51:a1:b6:3e:1c:57:32:e9:7b:2c:
20
+                    aa:1b:cc:97:2d:89:2d:b1:c9:5e:35:28:4d:7c:fa:
21
+                    65:31:3e:f7:70:dd:6e:0b:3c:58:af:a8:2e:24:c0:
22
+                    7e:4e:78:7d:0a:9e:8f:42:43
23
+                Exponent: 65537 (0x10001)
24
+        X509v3 extensions:
25
+            X509v3 Basic Constraints: 
26
+                CA:FALSE
27
+            Netscape Comment: 
28
+                Easy-RSA Generated Certificate
29
+            X509v3 Subject Key Identifier: 
30
+                DE:42:EF:2D:98:A3:6C:A8:AA:E0:8C:71:2C:9D:64:23:A9:E2:7E:81
31
+            X509v3 Authority Key Identifier: 
32
+                keyid:66:EE:C3:17:3D:3D:AB:44:01:6B:6F:B2:99:19:BD:AA:02:B5:34:FB
33
+                DirName:/C=US/ST=CA/L=SanFrancisco/O=Fort-Funston/OU=changeme/CN=changeme/name=changeme/emailAddress=mail@host.domain
34
+                serial:FD:AB:EC:6A:84:27:04:A7
35
+
36
+            X509v3 Extended Key Usage: 
37
+                TLS Web Client Authentication
38
+            X509v3 Key Usage: 
39
+                Digital Signature
40
+    Signature Algorithm: sha1WithRSAEncryption
41
+         1c:44:26:ea:e1:66:25:cb:e4:8e:57:1c:f6:b9:17:22:62:40:
42
+         12:90:8f:3b:b2:61:7a:54:94:8f:b1:20:0b:bf:a3:51:e3:fa:
43
+         1c:a1:be:92:3a:d0:76:44:c0:57:83:ab:6a:e4:1a:45:49:a4:
44
+         af:39:0d:60:32:fc:3a:be:d7:fb:5d:99:7a:1f:87:e7:d5:ab:
45
+         84:a2:5e:90:d8:bf:fa:89:6d:32:26:02:5e:31:35:68:7f:31:
46
+         f5:6b:51:46:bc:af:70:ed:5a:09:7d:ec:b2:48:4f:fe:c5:2f:
47
+         56:04:ad:f6:c1:d2:2a:e4:6a:c4:87:fe:08:35:c5:38:cb:5e:
48
+         4a:c4
49
+-----BEGIN CERTIFICATE-----
50
+MIIEFTCCA36gAwIBAgIBAzANBgkqhkiG9w0BAQUFADCBojELMAkGA1UEBhMCVVMx
51
+CzAJBgNVBAgTAkNBMRUwEwYDVQQHEwxTYW5GcmFuY2lzY28xFTATBgNVBAoTDEZv
52
+cnQtRnVuc3RvbjERMA8GA1UECxMIY2hhbmdlbWUxETAPBgNVBAMTCGNoYW5nZW1l
53
+MREwDwYDVQQpEwhjaGFuZ2VtZTEfMB0GCSqGSIb3DQEJARYQbWFpbEBob3N0LmRv
54
+bWFpbjAeFw0xMzEyMDQxNDE3NTRaFw0yMzEyMDIxNDE3NTRaMIGgMQswCQYDVQQG
55
+EwJVUzELMAkGA1UECBMCQ0ExFTATBgNVBAcTDFNhbkZyYW5jaXNjbzEVMBMGA1UE
56
+ChMMRm9ydC1GdW5zdG9uMREwDwYDVQQLEwhjaGFuZ2VtZTEPMA0GA1UEAxMGY2xp
57
+ZW50MREwDwYDVQQpEwhjaGFuZ2VtZTEfMB0GCSqGSIb3DQEJARYQbWFpbEBob3N0
58
+LmRvbWFpbjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAyskF0AlOPqT81RT0
59
+peg002tR4/Ni6qHw6O3EKrzwT8oH3+OI+vQhmTUOPeqwhufE0oqDK0K47KOZYnCB
60
+Rsz8pR3SY+jrByWa4iVtEVbyGlGhtj4cVzLpeyyqG8yXLYktscleNShNfPplMT73
61
+cN1uCzxYr6guJMB+Tnh9Cp6PQkMCAwEAAaOCAVkwggFVMAkGA1UdEwQCMAAwLQYJ
62
+YIZIAYb4QgENBCAWHkVhc3ktUlNBIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNV
63
+HQ4EFgQU3kLvLZijbKiq4IxxLJ1kI6nifoEwgdcGA1UdIwSBzzCBzIAUZu7DFz09
64
+q0QBa2+ymRm9qgK1NPuhgaikgaUwgaIxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJD
65
+QTEVMBMGA1UEBxMMU2FuRnJhbmNpc2NvMRUwEwYDVQQKEwxGb3J0LUZ1bnN0b24x
66
+ETAPBgNVBAsTCGNoYW5nZW1lMREwDwYDVQQDEwhjaGFuZ2VtZTERMA8GA1UEKRMI
67
+Y2hhbmdlbWUxHzAdBgkqhkiG9w0BCQEWEG1haWxAaG9zdC5kb21haW6CCQD9q+xq
68
+hCcEpzATBgNVHSUEDDAKBggrBgEFBQcDAjALBgNVHQ8EBAMCB4AwDQYJKoZIhvcN
69
+AQEFBQADgYEAHEQm6uFmJcvkjlcc9rkXImJAEpCPO7JhelSUj7EgC7+jUeP6HKG+
70
+kjrQdkTAV4OrauQaRUmkrzkNYDL8Or7X+12Zeh+H59WrhKJekNi/+oltMiYCXjE1
71
+aH8x9WtRRryvcO1aCX3sskhP/sUvVgSt9sHSKuRqxIf+CDXFOMteSsQ=
72
+-----END CERTIFICATE-----
0 73
new file mode 100644
... ...
@@ -0,0 +1,16 @@
0
+-----BEGIN PRIVATE KEY-----
1
+MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAMrJBdAJTj6k/NUU
2
+9KXoNNNrUePzYuqh8OjtxCq88E/KB9/jiPr0IZk1Dj3qsIbnxNKKgytCuOyjmWJw
3
+gUbM/KUd0mPo6wclmuIlbRFW8hpRobY+HFcy6XssqhvMly2JLbHJXjUoTXz6ZTE+
4
+93Ddbgs8WK+oLiTAfk54fQqej0JDAgMBAAECgYBOFEzKp2qbMEexe9ofL2N3rDDh
5
+xkrl8OijpzkLA6i78BxMFn4dsnZlWUpciMrjhsYAExkiRRSS+QMMJimAq1jzQqc3
6
+FAQV2XGYwkd0cUn7iZGvfNnEPysjsfyYQM+m+sT0ATj4BZjVShC6kkSjTdm1leLN
7
+OSvcHdcu3Xxg9ufF0QJBAPYdnNt5sIndt2WECePuRVi+uF4mlxTobFY0fjn26yhC
8
+4RsnhhD3Vldygo9gvnkwrAZYaALGSPBewes2InxvjA8CQQDS7erKiNXpwoqz5XiU
9
+SVEsIIVTdWzBjGbIqMOu/hUwM5FK4j6JTBks0aTGMyh0YV9L1EzM0X79J29JahCe
10
+iQKNAkBKNMOGqTpBV0hko1sYDk96YobUXG5RL4L6uvkUIQ7mJMQam+AgXXL7Ctuy
11
+v0iu4a38e8tgisiTMP7nHHtpaXihAkAOiN54/lzfMsykANgCP9scE1GcoqbP34Dl
12
+qttxH4kOPT9xzY1JoLjLYdbc4YGUI3GRpBt2sajygNkmUey7P+2xAkBBsVCZFvTw
13
+qHvOpPS2kX5ml5xoc/QAHK9N7kR+X7XFYx82RTVSqJEK4lPb+aEWn+CjiIewO4Q5
14
+ksDFuNxAzbhl
15
+-----END PRIVATE KEY-----
0 16
new file mode 100644
... ...
@@ -0,0 +1,73 @@
0
+Certificate:
1
+    Data:
2
+        Version: 3 (0x2)
3
+        Serial Number: 2 (0x2)
4
+    Signature Algorithm: sha1WithRSAEncryption
5
+        Issuer: C=US, ST=CA, L=SanFrancisco, O=Evil Inc, OU=changeme, CN=changeme/name=changeme/emailAddress=mail@host.domain
6
+        Validity
7
+            Not Before: Feb 24 17:54:59 2014 GMT
8
+            Not After : Feb 22 17:54:59 2024 GMT
9
+        Subject: C=US, ST=CA, L=SanFrancisco, O=Fort-Funston, OU=changeme, CN=client/name=changeme/emailAddress=mail@host.domain
10
+        Subject Public Key Info:
11
+            Public Key Algorithm: rsaEncryption
12
+                Public-Key: (1024 bit)
13
+                Modulus:
14
+                    00:e8:e2:2c:b8:d4:db:89:50:4f:47:1e:68:db:f7:
15
+                    e4:cc:47:41:63:75:03:37:50:7a:a8:4d:27:36:d5:
16
+                    15:01:08:b6:cf:56:f7:56:6d:3d:f9:e2:8d:1a:5d:
17
+                    bf:a0:24:5e:07:55:8e:d0:dc:f1:fa:19:87:1d:d6:
18
+                    b6:58:82:2e:ba:69:6d:e9:d9:c8:16:0d:1d:59:7f:
19
+                    f4:8e:58:10:01:3d:21:14:16:3c:ec:cd:8c:b7:0e:
20
+                    e6:7b:77:b4:f9:90:a5:17:01:bb:84:c6:b2:12:87:
21
+                    70:eb:9f:6d:4f:d0:68:8b:96:c0:e7:0b:51:b4:9d:
22
+                    1d:7b:6c:7b:be:89:6b:88:8b
23
+                Exponent: 65537 (0x10001)
24
+        X509v3 extensions:
25
+            X509v3 Basic Constraints: 
26
+                CA:FALSE
27
+            Netscape Comment: 
28
+                Easy-RSA Generated Certificate
29
+            X509v3 Subject Key Identifier: 
30
+                9E:F8:49:D0:A2:76:30:5C:AB:2B:8A:B5:8D:C6:45:1F:A7:F8:CF:85
31
+            X509v3 Authority Key Identifier: 
32
+                keyid:DC:A5:F1:76:DB:4E:CD:8E:EF:B1:23:56:1D:92:80:99:74:3B:EA:6F
33
+                DirName:/C=US/ST=CA/L=SanFrancisco/O=Evil Inc/OU=changeme/CN=changeme/name=changeme/emailAddress=mail@host.domain
34
+                serial:E7:21:1E:18:41:1B:96:83
35
+
36
+            X509v3 Extended Key Usage: 
37
+                TLS Web Client Authentication
38
+            X509v3 Key Usage: 
39
+                Digital Signature
40
+    Signature Algorithm: sha1WithRSAEncryption
41
+         48:76:c0:18:fa:0a:ee:4e:1a:ec:02:9d:d4:83:ca:94:54:a1:
42
+         3f:51:2f:3e:4b:95:c3:42:9b:71:a0:4b:d9:af:47:23:b9:1c:
43
+         fb:85:ba:76:e2:09:cb:65:bb:d2:7d:44:3d:4b:67:ba:80:83:
44
+         be:a8:ed:c4:b9:ea:1a:1b:c7:59:3b:d9:5c:0d:46:d8:c9:92:
45
+         cb:10:c5:f2:1a:38:a4:aa:07:2c:e3:84:16:79:c7:95:09:e3:
46
+         01:d2:15:a2:77:0b:8b:bf:94:04:e9:7f:c0:cd:e6:2e:64:cd:
47
+         1e:a3:32:ec:11:cc:62:ce:c7:4e:cd:ad:48:5c:b1:b8:e9:76:
48
+         b3:f9
49
+-----BEGIN CERTIFICATE-----
50
+MIIEDTCCA3agAwIBAgIBAjANBgkqhkiG9w0BAQUFADCBnjELMAkGA1UEBhMCVVMx
51
+CzAJBgNVBAgTAkNBMRUwEwYDVQQHEwxTYW5GcmFuY2lzY28xETAPBgNVBAoTCEV2
52
+aWwgSW5jMREwDwYDVQQLEwhjaGFuZ2VtZTERMA8GA1UEAxMIY2hhbmdlbWUxETAP
53
+BgNVBCkTCGNoYW5nZW1lMR8wHQYJKoZIhvcNAQkBFhBtYWlsQGhvc3QuZG9tYWlu
54
+MB4XDTE0MDIyNDE3NTQ1OVoXDTI0MDIyMjE3NTQ1OVowgaAxCzAJBgNVBAYTAlVT
55
+MQswCQYDVQQIEwJDQTEVMBMGA1UEBxMMU2FuRnJhbmNpc2NvMRUwEwYDVQQKEwxG
56
+b3J0LUZ1bnN0b24xETAPBgNVBAsTCGNoYW5nZW1lMQ8wDQYDVQQDEwZjbGllbnQx
57
+ETAPBgNVBCkTCGNoYW5nZW1lMR8wHQYJKoZIhvcNAQkBFhBtYWlsQGhvc3QuZG9t
58
+YWluMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDo4iy41NuJUE9HHmjb9+TM
59
+R0FjdQM3UHqoTSc21RUBCLbPVvdWbT354o0aXb+gJF4HVY7Q3PH6GYcd1rZYgi66
60
+aW3p2cgWDR1Zf/SOWBABPSEUFjzszYy3DuZ7d7T5kKUXAbuExrISh3Drn21P0GiL
61
+lsDnC1G0nR17bHu+iWuIiwIDAQABo4IBVTCCAVEwCQYDVR0TBAIwADAtBglghkgB
62
+hvhCAQ0EIBYeRWFzeS1SU0EgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQW
63
+BBSe+EnQonYwXKsrirWNxkUfp/jPhTCB0wYDVR0jBIHLMIHIgBTcpfF2207Nju+x
64
+I1YdkoCZdDvqb6GBpKSBoTCBnjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRUw
65
+EwYDVQQHEwxTYW5GcmFuY2lzY28xETAPBgNVBAoTCEV2aWwgSW5jMREwDwYDVQQL
66
+EwhjaGFuZ2VtZTERMA8GA1UEAxMIY2hhbmdlbWUxETAPBgNVBCkTCGNoYW5nZW1l
67
+MR8wHQYJKoZIhvcNAQkBFhBtYWlsQGhvc3QuZG9tYWluggkA5yEeGEEbloMwEwYD
68
+VR0lBAwwCgYIKwYBBQUHAwIwCwYDVR0PBAQDAgeAMA0GCSqGSIb3DQEBBQUAA4GB
69
+AEh2wBj6Cu5OGuwCndSDypRUoT9RLz5LlcNCm3GgS9mvRyO5HPuFunbiCctlu9J9
70
+RD1LZ7qAg76o7cS56hobx1k72VwNRtjJkssQxfIaOKSqByzjhBZ5x5UJ4wHSFaJ3
71
+C4u/lATpf8DN5i5kzR6jMuwRzGLOx07NrUhcsbjpdrP5
72
+-----END CERTIFICATE-----
0 73
new file mode 100644
... ...
@@ -0,0 +1,16 @@
0
+-----BEGIN PRIVATE KEY-----
1
+MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAOjiLLjU24lQT0ce
2
+aNv35MxHQWN1AzdQeqhNJzbVFQEIts9W91ZtPfnijRpdv6AkXgdVjtDc8foZhx3W
3
+tliCLrppbenZyBYNHVl/9I5YEAE9IRQWPOzNjLcO5nt3tPmQpRcBu4TGshKHcOuf
4
+bU/QaIuWwOcLUbSdHXtse76Ja4iLAgMBAAECgYADs+TmI2xCKKa6CL++D5jxrohZ
5
+nnionnz0xBVFh+nHlG3jqgxQsXf0yydXLfpn/2wHTdLxezHVuiYt0UYg7iD0CglW
6
++IjcgMebzyjLeYqYOE5llPlMvhp2HoEMYJNb+7bRrZ1WCITbu+Su0w1cgA7Cs+Ej
7
+VlfvGzN+qqnDThRUYQJBAPY0sMWZJKly8QhUmUvmcXdPczzSOf6Mm7gc5LR6wzxd
8
+vW7syuqk50qjqVqFpN81vCV7GoDxRUWbTM9ftf7JGFkCQQDyJc/1RMygE2o+enU1
9
+6UBxJyclXITEYtDn8aoEpLNc7RakP1WoPUKjZOnjkcoKcIkFNkSPeCfQujrb5f3F
10
+MkuDAkByAI/hzzmkpK5rFxEsjfX4Mve/L/DepyjrpaVY1IdWimlO1aJX6CeY7hNa
11
+8QsYt/74s/nfvtg+lNyKIV1aLq9xAkB+WSSNgfyTeg3x08vc+Xxajmdqoz/TiQwg
12
+OoTQL3A3iK5LvZBgXLasszcnOycFE3srcQmNItEDpGiZ3QPxJTEpAkEA45EE9NMJ
13
+SA7EGWSFlbz4f4u4oBeiDiJRJbGGfAyVxZlpCWUjPpg9+swsWoFEOjnGYaChAMk5
14
+nrOdMf15T6QF7Q==
15
+-----END PRIVATE KEY-----
0 16
new file mode 100644
... ...
@@ -0,0 +1,76 @@
0
+Certificate:
1
+    Data:
2
+        Version: 3 (0x2)
3
+        Serial Number: 4 (0x4)
4
+    Signature Algorithm: sha1WithRSAEncryption
5
+        Issuer: C=US, ST=CA, L=SanFrancisco, O=Fort-Funston, OU=changeme, CN=changeme/name=changeme/emailAddress=mail@host.domain
6
+        Validity
7
+            Not Before: Dec  4 15:01:20 2013 GMT
8
+            Not After : Dec  2 15:01:20 2023 GMT
9
+        Subject: C=US, ST=CA, L=SanFrancisco, O=Fort-Funston, OU=changeme, CN=*/name=changeme/emailAddress=mail@host.domain
10
+        Subject Public Key Info:
11
+            Public Key Algorithm: rsaEncryption
12
+                Public-Key: (1024 bit)
13
+                Modulus:
14
+                    00:c1:ff:7d:30:6f:64:4a:b1:92:b1:71:d1:c1:74:
15
+                    e2:1d:db:2d:11:24:e1:00:d4:00:ae:6f:c8:9e:ae:
16
+                    67:b3:4a:bd:f7:e6:9e:57:6d:19:4c:3c:23:94:2d:
17
+                    3d:d6:63:84:d8:fa:76:2b:38:12:c1:ed:20:9d:32:
18
+                    e0:e8:c2:bf:9a:77:70:04:3f:7f:ca:8c:2c:82:d6:
19
+                    3d:25:5c:02:1a:4f:64:93:03:dd:9c:42:97:5e:09:
20
+                    49:af:f0:c2:e1:30:08:0e:21:46:95:d1:13:59:c0:
21
+                    c8:76:be:94:0d:8b:43:67:21:33:b2:08:60:9d:76:
22
+                    a8:05:32:1e:f9:95:09:14:75
23
+                Exponent: 65537 (0x10001)
24
+        X509v3 extensions:
25
+            X509v3 Basic Constraints: 
26
+                CA:FALSE
27
+            Netscape Cert Type: 
28
+                SSL Server
29
+            Netscape Comment: 
30
+                Easy-RSA Generated Server Certificate
31
+            X509v3 Subject Key Identifier: 
32
+                14:02:FD:FD:DD:13:38:E0:71:EA:D1:BE:C0:0E:89:1A:2D:B6:19:06
33
+            X509v3 Authority Key Identifier: 
34
+                keyid:66:EE:C3:17:3D:3D:AB:44:01:6B:6F:B2:99:19:BD:AA:02:B5:34:FB
35
+                DirName:/C=US/ST=CA/L=SanFrancisco/O=Fort-Funston/OU=changeme/CN=changeme/name=changeme/emailAddress=mail@host.domain
36
+                serial:FD:AB:EC:6A:84:27:04:A7
37
+
38
+            X509v3 Extended Key Usage: 
39
+                TLS Web Server Authentication
40
+            X509v3 Key Usage: 
41
+                Digital Signature, Key Encipherment
42
+    Signature Algorithm: sha1WithRSAEncryption
43
+         40:0f:10:39:c4:b7:0f:0d:2f:bf:d2:16:cc:8e:d3:9a:fb:8b:
44
+         ce:4b:7b:0d:48:77:ce:f1:fe:d5:8f:ea:b1:71:ed:49:1d:9f:
45
+         23:3a:16:d4:70:7c:c5:29:bf:e4:90:34:d0:f0:00:24:f4:e4:
46
+         df:2c:c3:83:01:66:61:c9:a8:ab:29:e7:98:6d:27:89:4a:76:
47
+         c9:2e:19:8e:fe:6e:d5:f8:99:11:0e:97:67:4b:34:e3:1e:e3:
48
+         9f:35:00:a5:32:f9:b5:2c:f2:e0:c5:2e:cc:81:bd:18:dd:5c:
49
+         12:c8:6b:fa:0c:17:74:30:55:f6:6e:20:9a:6c:1e:09:b4:0c:
50
+         15:42
51
+-----BEGIN CERTIFICATE-----
52
+MIIEKjCCA5OgAwIBAgIBBDANBgkqhkiG9w0BAQUFADCBojELMAkGA1UEBhMCVVMx
53
+CzAJBgNVBAgTAkNBMRUwEwYDVQQHEwxTYW5GcmFuY2lzY28xFTATBgNVBAoTDEZv
54
+cnQtRnVuc3RvbjERMA8GA1UECxMIY2hhbmdlbWUxETAPBgNVBAMTCGNoYW5nZW1l
55
+MREwDwYDVQQpEwhjaGFuZ2VtZTEfMB0GCSqGSIb3DQEJARYQbWFpbEBob3N0LmRv
56
+bWFpbjAeFw0xMzEyMDQxNTAxMjBaFw0yMzEyMDIxNTAxMjBaMIGbMQswCQYDVQQG
57
+EwJVUzELMAkGA1UECBMCQ0ExFTATBgNVBAcTDFNhbkZyYW5jaXNjbzEVMBMGA1UE
58
+ChMMRm9ydC1GdW5zdG9uMREwDwYDVQQLEwhjaGFuZ2VtZTEKMAgGA1UEAxQBKjER
59
+MA8GA1UEKRMIY2hhbmdlbWUxHzAdBgkqhkiG9w0BCQEWEG1haWxAaG9zdC5kb21h
60
+aW4wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMH/fTBvZEqxkrFx0cF04h3b
61
+LREk4QDUAK5vyJ6uZ7NKvffmnldtGUw8I5QtPdZjhNj6dis4EsHtIJ0y4OjCv5p3
62
+cAQ/f8qMLILWPSVcAhpPZJMD3ZxCl14JSa/wwuEwCA4hRpXRE1nAyHa+lA2LQ2ch
63
+M7IIYJ12qAUyHvmVCRR1AgMBAAGjggFzMIIBbzAJBgNVHRMEAjAAMBEGCWCGSAGG
64
++EIBAQQEAwIGQDA0BglghkgBhvhCAQ0EJxYlRWFzeS1SU0EgR2VuZXJhdGVkIFNl
65
+cnZlciBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUFAL9/d0TOOBx6tG+wA6JGi22GQYw
66
+gdcGA1UdIwSBzzCBzIAUZu7DFz09q0QBa2+ymRm9qgK1NPuhgaikgaUwgaIxCzAJ
67
+BgNVBAYTAlVTMQswCQYDVQQIEwJDQTEVMBMGA1UEBxMMU2FuRnJhbmNpc2NvMRUw
68
+EwYDVQQKEwxGb3J0LUZ1bnN0b24xETAPBgNVBAsTCGNoYW5nZW1lMREwDwYDVQQD
69
+EwhjaGFuZ2VtZTERMA8GA1UEKRMIY2hhbmdlbWUxHzAdBgkqhkiG9w0BCQEWEG1h
70
+aWxAaG9zdC5kb21haW6CCQD9q+xqhCcEpzATBgNVHSUEDDAKBggrBgEFBQcDATAL
71
+BgNVHQ8EBAMCBaAwDQYJKoZIhvcNAQEFBQADgYEAQA8QOcS3Dw0vv9IWzI7TmvuL
72
+zkt7DUh3zvH+1Y/qsXHtSR2fIzoW1HB8xSm/5JA00PAAJPTk3yzDgwFmYcmoqynn
73
+mG0niUp2yS4Zjv5u1fiZEQ6XZ0s04x7jnzUApTL5tSzy4MUuzIG9GN1cEshr+gwX
74
+dDBV9m4gmmweCbQMFUI=
75
+-----END CERTIFICATE-----
0 76
new file mode 100644
... ...
@@ -0,0 +1,16 @@
0
+-----BEGIN PRIVATE KEY-----
1
+MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAMH/fTBvZEqxkrFx
2
+0cF04h3bLREk4QDUAK5vyJ6uZ7NKvffmnldtGUw8I5QtPdZjhNj6dis4EsHtIJ0y
3
+4OjCv5p3cAQ/f8qMLILWPSVcAhpPZJMD3ZxCl14JSa/wwuEwCA4hRpXRE1nAyHa+
4
+lA2LQ2chM7IIYJ12qAUyHvmVCRR1AgMBAAECgYAmwckb9RUfSwyYgLm8IYLPHiuJ
5
+wkllZfVg5Bo7gXJcQnFjZmJ56uTj8xvUjZlODIHM63TSO5ibv6kFXtXKCqZGd2M+
6
+wGbhZ0f+2GvKcwMmJERnIQjuoNaYSQLT0tM0VB9Iz0rJlZC+tzPZ+5pPqEumRdsS
7
+IzWNXfF42AhcbwAQYQJBAPVXtMYIJc9EZsz86ZcQiMPWUpCX5vnRmtwL8kKyR8D5
8
+4KfYeiowyFffSRMMcclwNHq7TgSXN+nIXM9WyzyzwikCQQDKbNA28AgZp9aT54HP
9
+WnbeE2pmt+uk/zl/BtxJSoK6H+69Jec+lf7EgL7HgOWYRSNot4uQWu8IhsHLTiUq
10
++0FtAkEAqwlRxRy4/x24bP+D+QRV0/D97j93joFJbE4Hved7jlSlAV4xDGilwlyv
11
+HNB4Iu5OJ6Gcaibhm+FKkmD3noHSwQJBAIpu3fokLzX0bS+bDFBU6qO3HXX/47xj
12
++tsfQvkwZrSI8AkU6c8IX0HdVhsz0FBRQAT2ORDQz1XCarfxykNZrwUCQQCGCBIc
13
+BBCWzhHlswlGidWJg3HqqO6hPPClEr3B5G87oCsdeYwiO23XT6rUnoJXfJHp6oCW
14
+5nCwDu5ZTP+khltg
15
+-----END PRIVATE KEY-----
0 16
new file mode 100644
... ...
@@ -0,0 +1,76 @@
0
+Certificate:
1
+    Data:
2
+        Version: 3 (0x2)
3
+        Serial Number: 3 (0x3)
4
+    Signature Algorithm: sha1WithRSAEncryption
5
+        Issuer: C=US, ST=CA, L=SanFrancisco, O=Evil Inc, OU=changeme, CN=changeme/name=changeme/emailAddress=mail@host.domain
6
+        Validity
7
+            Not Before: Feb 28 18:49:31 2014 GMT
8
+            Not After : Feb 26 18:49:31 2024 GMT
9
+        Subject: C=US, ST=CA, L=SanFrancisco, O=Fort-Funston, OU=changeme, CN=localhost/name=changeme/emailAddress=mail@host.domain
10
+        Subject Public Key Info:
11
+            Public Key Algorithm: rsaEncryption
12
+                Public-Key: (1024 bit)
13
+                Modulus:
14
+                    00:d1:08:58:24:60:a1:69:65:4b:76:46:8f:88:75:
15
+                    7c:49:3a:d8:03:cc:5b:58:c5:d1:bb:e5:f9:54:b9:
16
+                    75:65:df:7e:bb:fb:54:d4:b2:e9:6f:58:a2:a4:84:
17
+                    43:94:77:24:81:38:36:36:f0:66:65:26:e5:5b:2a:
18
+                    14:1c:a9:ae:57:7f:75:00:23:14:4b:61:58:e4:82:
19
+                    aa:15:97:94:bd:50:35:0d:5d:18:18:ed:10:6a:bb:
20
+                    d3:64:5a:eb:36:98:5b:58:a7:fe:67:48:c1:6c:3f:
21
+                    51:2f:02:65:96:54:77:9b:34:f9:a7:d2:63:54:6a:
22
+                    9e:02:5c:be:65:98:a4:b4:b5
23
+                Exponent: 65537 (0x10001)
24
+        X509v3 extensions:
25
+            X509v3 Basic Constraints: 
26
+                CA:FALSE
27
+            Netscape Cert Type: 
28
+                SSL Server
29
+            Netscape Comment: 
30
+                Easy-RSA Generated Server Certificate
31
+            X509v3 Subject Key Identifier: 
32
+                1F:E0:57:CA:CB:76:C9:C4:86:B9:EA:69:17:C0:F3:51:CE:95:40:EC
33
+            X509v3 Authority Key Identifier: 
34
+                keyid:DC:A5:F1:76:DB:4E:CD:8E:EF:B1:23:56:1D:92:80:99:74:3B:EA:6F
35
+                DirName:/C=US/ST=CA/L=SanFrancisco/O=Evil Inc/OU=changeme/CN=changeme/name=changeme/emailAddress=mail@host.domain
36
+                serial:E7:21:1E:18:41:1B:96:83
37
+
38
+            X509v3 Extended Key Usage: 
39
+                TLS Web Server Authentication
40
+            X509v3 Key Usage: 
41
+                Digital Signature, Key Encipherment
42
+    Signature Algorithm: sha1WithRSAEncryption
43
+         04:93:0e:28:01:94:18:f0:8c:7c:d3:0c:ad:e9:b7:46:b1:30:
44
+         65:ed:68:7c:8c:91:cd:1a:86:66:87:4a:4f:c0:97:bc:f7:85:
45
+         4b:38:79:31:b2:65:88:b1:76:16:9e:80:93:38:f4:b9:eb:65:
46
+         00:6d:bb:89:e0:a1:bf:95:5e:80:13:8e:01:73:d3:f1:08:73:
47
+         85:a5:33:75:0b:42:8a:a3:07:09:35:ef:d7:c6:58:eb:60:a3:
48
+         06:89:a0:53:99:e2:aa:41:90:e0:1a:d2:12:4b:48:7d:c3:9c:
49
+         ad:bd:0e:5e:5f:f7:09:0c:5d:7c:86:24:dd:92:d5:b3:14:06:
50
+         c7:9f
51
+-----BEGIN CERTIFICATE-----
52
+MIIEKjCCA5OgAwIBAgIBAzANBgkqhkiG9w0BAQUFADCBnjELMAkGA1UEBhMCVVMx
53
+CzAJBgNVBAgTAkNBMRUwEwYDVQQHEwxTYW5GcmFuY2lzY28xETAPBgNVBAoTCEV2
54
+aWwgSW5jMREwDwYDVQQLEwhjaGFuZ2VtZTERMA8GA1UEAxMIY2hhbmdlbWUxETAP
55
+BgNVBCkTCGNoYW5nZW1lMR8wHQYJKoZIhvcNAQkBFhBtYWlsQGhvc3QuZG9tYWlu
56
+MB4XDTE0MDIyODE4NDkzMVoXDTI0MDIyNjE4NDkzMVowgaMxCzAJBgNVBAYTAlVT
57
+MQswCQYDVQQIEwJDQTEVMBMGA1UEBxMMU2FuRnJhbmNpc2NvMRUwEwYDVQQKEwxG
58
+b3J0LUZ1bnN0b24xETAPBgNVBAsTCGNoYW5nZW1lMRIwEAYDVQQDEwlsb2NhbGhv
59
+c3QxETAPBgNVBCkTCGNoYW5nZW1lMR8wHQYJKoZIhvcNAQkBFhBtYWlsQGhvc3Qu
60
+ZG9tYWluMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDRCFgkYKFpZUt2Ro+I
61
+dXxJOtgDzFtYxdG75flUuXVl3367+1TUsulvWKKkhEOUdySBODY28GZlJuVbKhQc
62
+qa5Xf3UAIxRLYVjkgqoVl5S9UDUNXRgY7RBqu9NkWus2mFtYp/5nSMFsP1EvAmWW
63
+VHebNPmn0mNUap4CXL5lmKS0tQIDAQABo4IBbzCCAWswCQYDVR0TBAIwADARBglg
64
+hkgBhvhCAQEEBAMCBkAwNAYJYIZIAYb4QgENBCcWJUVhc3ktUlNBIEdlbmVyYXRl
65
+ZCBTZXJ2ZXIgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFB/gV8rLdsnEhrnqaRfA81HO
66
+lUDsMIHTBgNVHSMEgcswgciAFNyl8XbbTs2O77EjVh2SgJl0O+pvoYGkpIGhMIGe
67
+MQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExFTATBgNVBAcTDFNhbkZyYW5jaXNj
68
+bzERMA8GA1UEChMIRXZpbCBJbmMxETAPBgNVBAsTCGNoYW5nZW1lMREwDwYDVQQD
69
+EwhjaGFuZ2VtZTERMA8GA1UEKRMIY2hhbmdlbWUxHzAdBgkqhkiG9w0BCQEWEG1h
70
+aWxAaG9zdC5kb21haW6CCQDnIR4YQRuWgzATBgNVHSUEDDAKBggrBgEFBQcDATAL
71
+BgNVHQ8EBAMCBaAwDQYJKoZIhvcNAQEFBQADgYEABJMOKAGUGPCMfNMMrem3RrEw
72
+Ze1ofIyRzRqGZodKT8CXvPeFSzh5MbJliLF2Fp6Akzj0uetlAG27ieChv5VegBOO
73
+AXPT8QhzhaUzdQtCiqMHCTXv18ZY62CjBomgU5niqkGQ4BrSEktIfcOcrb0OXl/3
74
+CQxdfIYk3ZLVsxQGx58=
75
+-----END CERTIFICATE-----
0 76
new file mode 100644
... ...
@@ -0,0 +1,16 @@
0
+-----BEGIN PRIVATE KEY-----
1
+MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBANEIWCRgoWllS3ZG
2
+j4h1fEk62APMW1jF0bvl+VS5dWXffrv7VNSy6W9YoqSEQ5R3JIE4NjbwZmUm5Vsq
3
+FByprld/dQAjFEthWOSCqhWXlL1QNQ1dGBjtEGq702Ra6zaYW1in/mdIwWw/US8C
4
+ZZZUd5s0+afSY1RqngJcvmWYpLS1AgMBAAECgYAJXh9dGfuB1qlIFqduDR3RxlJR
5
+8UGSu+LHUeoXkuwg8aAjWoMVuSLe+5DmYIsKx0AajmNXmPRtyg1zRXJ7SltmubJ8
6
+6qQVDsRk6biMdkpkl6a9Gk2av40psD9/VPGxagEoop7IKYhf3AeKPvPiwVB2qFrl
7
+1aYMZm0aMR55pgRajQJBAOk8IsJDf0beooDZXVdv/oe4hcbM9fxO8Cn3qzoGImqD
8
+37LL+PCzDP7AEV3fk43SsZDeSk+LDX+h0o9nPyhzHasCQQDlb3aDgcQY9NaGLUWO
9
+moOCB3148eBVcAwCocu+OSkf7sbQdvXxgThBOrZl11wwRIMQqh99c2yeUwj+tELl
10
+3VcfAkBZTiNpCvtDIaBLge9RuZpWUXs3wec2cutWxnSTxSGMc25GQf/R+l0xdk2w
11
+ChmvpktDUzpU9sN2aXn8WuY+EMX9AkEApbLpUbKPUELLB958RLA819TW/lkZXjrs
12
+wZ3eSoR3ufM1rOqtVvyvBxUDE+wETWu9iHSFB5Ir2PA5J9JCGkbPmwJAFI1ndfBj
13
+iuyU93nFX0p+JE2wVHKx4dMzKCearNKiJh/lGDtUq3REGgamTNUnG8RAITUbxFs+
14
+Z1hrIq8xYl2LOQ==
15
+-----END PRIVATE KEY-----
0 16
new file mode 100644
... ...
@@ -0,0 +1,82 @@
0
+package docker
1
+
2
+import (
3
+	"crypto/tls"
4
+	"crypto/x509"
5
+	"github.com/dotcloud/docker/api"
6
+	"io/ioutil"
7
+	"testing"
8
+	"time"
9
+)
10
+
11
+const (
12
+	errBadCertificate = "remote error: bad certificate"
13
+	errCaUnknown      = "x509: certificate signed by unknown authority"
14
+)
15
+
16
+func getTlsConfig(certFile, keyFile string, t *testing.T) *tls.Config {
17
+	certPool := x509.NewCertPool()
18
+	file, err := ioutil.ReadFile("fixtures/https/ca.pem")
19
+	if err != nil {
20
+		t.Fatal(err)
21
+	}
22
+	certPool.AppendCertsFromPEM(file)
23
+
24
+	cert, err := tls.LoadX509KeyPair("fixtures/https/"+certFile, "fixtures/https/"+keyFile)
25
+	if err != nil {
26
+		t.Fatalf("Couldn't load X509 key pair: %s", err)
27
+	}
28
+	tlsConfig := &tls.Config{
29
+		RootCAs:      certPool,
30
+		Certificates: []tls.Certificate{cert},
31
+	}
32
+	return tlsConfig
33
+}
34
+
35
+// TestHttpsInfo connects via two-way authenticated HTTPS to the info endpoint
36
+func TestHttpsInfo(t *testing.T) {
37
+	cli := api.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, testDaemonProto,
38
+		testDaemonHttpsAddr, getTlsConfig("client-cert.pem", "client-key.pem", t))
39
+
40
+	setTimeout(t, "Reading command output time out", 10*time.Second, func() {
41
+		if err := cli.CmdInfo(); err != nil {
42
+			t.Fatal(err)
43
+		}
44
+	})
45
+}
46
+
47
+// TestHttpsInfoRogueCert connects via two-way authenticated HTTPS to the info endpoint
48
+// by using a rogue client certificate and checks that it fails with the expected error.
49
+func TestHttpsInfoRogueCert(t *testing.T) {
50
+	cli := api.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, testDaemonProto,
51
+		testDaemonHttpsAddr, getTlsConfig("client-rogue-cert.pem", "client-rogue-key.pem", t))
52
+
53
+	setTimeout(t, "Reading command output time out", 10*time.Second, func() {
54
+		err := cli.CmdInfo()
55
+		if err == nil {
56
+			t.Fatal("Expected error but got nil")
57
+		}
58
+		if err.Error() != errBadCertificate {
59
+			t.Fatalf("Expected error: %s, got instead: %s", errBadCertificate, err)
60
+		}
61
+	})
62
+}
63
+
64
+// TestHttpsInfoRogueServerCert connects via two-way authenticated HTTPS to the info endpoint
65
+// which provides a rogue server certificate and checks that it fails with the expected error
66
+func TestHttpsInfoRogueServerCert(t *testing.T) {
67
+	cli := api.NewDockerCli(nil, ioutil.Discard, ioutil.Discard, testDaemonProto,
68
+		testDaemonRogueHttpsAddr, getTlsConfig("client-cert.pem", "client-key.pem", t))
69
+
70
+	setTimeout(t, "Reading command output time out", 10*time.Second, func() {
71
+		err := cli.CmdInfo()
72
+		if err == nil {
73
+			t.Fatal("Expected error but got nil")
74
+		}
75
+
76
+		if err.Error() != errCaUnknown {
77
+			t.Fatalf("Expected error: %s, got instead: %s", errBadCertificate, err)
78
+		}
79
+
80
+	})
81
+}
... ...
@@ -24,21 +24,26 @@ import (
24 24
 )
25 25
 
26 26
 const (
27
-	unitTestImageName     = "docker-test-image"
28
-	unitTestImageID       = "83599e29c455eb719f77d799bc7c51521b9551972f5a850d7ad265bc1b5292f6" // 1.0
29
-	unitTestImageIDShort  = "83599e29c455"
30
-	unitTestNetworkBridge = "testdockbr0"
31
-	unitTestStoreBase     = "/var/lib/docker/unit-tests"
32
-	testDaemonAddr        = "127.0.0.1:4270"
33
-	testDaemonProto       = "tcp"
27
+	unitTestImageName        = "docker-test-image"
28
+	unitTestImageID          = "83599e29c455eb719f77d799bc7c51521b9551972f5a850d7ad265bc1b5292f6" // 1.0
29
+	unitTestImageIDShort     = "83599e29c455"
30
+	unitTestNetworkBridge    = "testdockbr0"
31
+	unitTestStoreBase        = "/var/lib/docker/unit-tests"
32
+	testDaemonAddr           = "127.0.0.1:4270"
33
+	testDaemonProto          = "tcp"
34
+	testDaemonHttpsProto     = "tcp"
35
+	testDaemonHttpsAddr      = "localhost:4271"
36
+	testDaemonRogueHttpsAddr = "localhost:4272"
34 37
 )
35 38
 
36 39
 var (
37 40
 	// FIXME: globalRuntime is deprecated by globalEngine. All tests should be converted.
38
-	globalRuntime   *docker.Runtime
39
-	globalEngine    *engine.Engine
40
-	startFds        int
41
-	startGoroutines int
41
+	globalRuntime          *docker.Runtime
42
+	globalEngine           *engine.Engine
43
+	globalHttpsEngine      *engine.Engine
44
+	globalRogueHttpsEngine *engine.Engine
45
+	startFds               int
46
+	startGoroutines        int
42 47
 )
43 48
 
44 49
 // FIXME: nuke() is deprecated by Runtime.Nuke()
... ...
@@ -117,8 +122,10 @@ func init() {
117 117
 	// (no tests are run directly in the base)
118 118
 	setupBaseImage()
119 119
 
120
-	// Create the "global runtime" with a long-running daemon for integration tests
120
+	// Create the "global runtime" with a long-running daemons for integration tests
121 121
 	spawnGlobalDaemon()
122
+	spawnLegitHttpsDaemon()
123
+	spawnRogueHttpsDaemon()
122 124
 	startFds, startGoroutines = utils.GetTotalUsedFds(), runtime.NumGoroutine()
123 125
 }
124 126
 
... ...
@@ -170,6 +177,61 @@ func spawnGlobalDaemon() {
170 170
 	}
171 171
 }
172 172
 
173
+func spawnLegitHttpsDaemon() {
174
+	if globalHttpsEngine != nil {
175
+		return
176
+	}
177
+	globalHttpsEngine = spawnHttpsDaemon(testDaemonHttpsAddr, "fixtures/https/ca.pem",
178
+		"fixtures/https/server-cert.pem", "fixtures/https/server-key.pem")
179
+}
180
+
181
+func spawnRogueHttpsDaemon() {
182
+	if globalRogueHttpsEngine != nil {
183
+		return
184
+	}
185
+	globalRogueHttpsEngine = spawnHttpsDaemon(testDaemonRogueHttpsAddr, "fixtures/https/ca.pem",
186
+		"fixtures/https/server-rogue-cert.pem", "fixtures/https/server-rogue-key.pem")
187
+}
188
+
189
+func spawnHttpsDaemon(addr, cacert, cert, key string) *engine.Engine {
190
+	t := log.New(os.Stderr, "", 0)
191
+	root, err := newTestDirectory(unitTestStoreBase)
192
+	if err != nil {
193
+		t.Fatal(err)
194
+	}
195
+	// FIXME: here we don't use NewTestEngine because it calls initserver with Autorestart=false,
196
+	// and we want to set it to true.
197
+
198
+	eng := newTestEngine(t, true, root)
199
+
200
+	// Spawn a Daemon
201
+	go func() {
202
+		utils.Debugf("Spawning https daemon for integration tests")
203
+		listenURL := &url.URL{
204
+			Scheme: testDaemonHttpsProto,
205
+			Host:   addr,
206
+		}
207
+		job := eng.Job("serveapi", listenURL.String())
208
+		job.SetenvBool("Logging", true)
209
+		job.SetenvBool("Tls", true)
210
+		job.SetenvBool("TlsVerify", true)
211
+		job.Setenv("TlsCa", cacert)
212
+		job.Setenv("TlsCert", cert)
213
+		job.Setenv("TlsKey", key)
214
+		if err := job.Run(); err != nil {
215
+			log.Fatalf("Unable to spawn the test daemon: %s", err)
216
+		}
217
+	}()
218
+
219
+	// Give some time to ListenAndServer to actually start
220
+	time.Sleep(time.Second)
221
+
222
+	if err := eng.Job("acceptconnections").Run(); err != nil {
223
+		log.Fatalf("Unable to accept connections for test api: %s", err)
224
+	}
225
+	return eng
226
+}
227
+
173 228
 // FIXME: test that ImagePull(json=true) send correct json output
174 229
 
175 230
 func GetTestImage(runtime *docker.Runtime) *docker.Image {