github.com/noxiouz/docker@v0.7.3-0.20160629055221-3d231c78e8c5/integration-cli/trust_server.go (about) 1 package main 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "net" 7 "net/http" 8 "os" 9 "os/exec" 10 "path/filepath" 11 "strings" 12 "time" 13 14 "github.com/docker/docker/cliconfig" 15 "github.com/docker/docker/pkg/integration/checker" 16 "github.com/docker/docker/pkg/tlsconfig" 17 "github.com/go-check/check" 18 ) 19 20 var notaryBinary = "notary" 21 var notaryServerBinary = "notary-server" 22 23 type keyPair struct { 24 Public string 25 Private string 26 } 27 28 type testNotary struct { 29 cmd *exec.Cmd 30 dir string 31 keys []keyPair 32 } 33 34 const notaryHost = "localhost:4443" 35 const notaryURL = "https://" + notaryHost 36 37 func newTestNotary(c *check.C) (*testNotary, error) { 38 // generate server config 39 template := `{ 40 "server": { 41 "http_addr": "%s", 42 "tls_key_file": "%s", 43 "tls_cert_file": "%s" 44 }, 45 "trust_service": { 46 "type": "local", 47 "hostname": "", 48 "port": "", 49 "key_algorithm": "ed25519" 50 }, 51 "logging": { 52 "level": "debug" 53 }, 54 "storage": { 55 "backend": "memory" 56 } 57 }` 58 tmp, err := ioutil.TempDir("", "notary-test-") 59 if err != nil { 60 return nil, err 61 } 62 confPath := filepath.Join(tmp, "config.json") 63 config, err := os.Create(confPath) 64 defer config.Close() 65 if err != nil { 66 return nil, err 67 } 68 69 workingDir, err := os.Getwd() 70 if err != nil { 71 return nil, err 72 } 73 if _, err := fmt.Fprintf(config, template, notaryHost, filepath.Join(workingDir, "fixtures/notary/localhost.key"), filepath.Join(workingDir, "fixtures/notary/localhost.cert")); err != nil { 74 os.RemoveAll(tmp) 75 return nil, err 76 } 77 78 // generate client config 79 clientConfPath := filepath.Join(tmp, "client-config.json") 80 clientConfig, err := os.Create(clientConfPath) 81 defer clientConfig.Close() 82 if err != nil { 83 return nil, err 84 } 85 template = `{ 86 "trust_dir" : "%s", 87 "remote_server": { 88 "url": "%s", 89 "skipTLSVerify": true 90 } 91 }` 92 if _, err = fmt.Fprintf(clientConfig, template, filepath.Join(cliconfig.ConfigDir(), "trust"), notaryURL); err != nil { 93 os.RemoveAll(tmp) 94 return nil, err 95 } 96 97 // load key fixture filenames 98 var keys []keyPair 99 for i := 1; i < 5; i++ { 100 keys = append(keys, keyPair{ 101 Public: filepath.Join(workingDir, fmt.Sprintf("fixtures/notary/delgkey%v.crt", i)), 102 Private: filepath.Join(workingDir, fmt.Sprintf("fixtures/notary/delgkey%v.key", i)), 103 }) 104 } 105 106 // run notary-server 107 cmd := exec.Command(notaryServerBinary, "-config", confPath) 108 if err := cmd.Start(); err != nil { 109 os.RemoveAll(tmp) 110 if os.IsNotExist(err) { 111 c.Skip(err.Error()) 112 } 113 return nil, err 114 } 115 116 testNotary := &testNotary{ 117 cmd: cmd, 118 dir: tmp, 119 keys: keys, 120 } 121 122 // Wait for notary to be ready to serve requests. 123 for i := 1; i <= 20; i++ { 124 if err = testNotary.Ping(); err == nil { 125 break 126 } 127 time.Sleep(10 * time.Millisecond * time.Duration(i*i)) 128 } 129 130 if err != nil { 131 c.Fatalf("Timeout waiting for test notary to become available: %s", err) 132 } 133 134 return testNotary, nil 135 } 136 137 func (t *testNotary) Ping() error { 138 tlsConfig := tlsconfig.ClientDefault 139 tlsConfig.InsecureSkipVerify = true 140 client := http.Client{ 141 Transport: &http.Transport{ 142 Proxy: http.ProxyFromEnvironment, 143 Dial: (&net.Dialer{ 144 Timeout: 30 * time.Second, 145 KeepAlive: 30 * time.Second, 146 }).Dial, 147 TLSHandshakeTimeout: 10 * time.Second, 148 TLSClientConfig: &tlsConfig, 149 }, 150 } 151 resp, err := client.Get(fmt.Sprintf("%s/v2/", notaryURL)) 152 if err != nil { 153 return err 154 } 155 if resp.StatusCode != 200 { 156 return fmt.Errorf("notary ping replied with an unexpected status code %d", resp.StatusCode) 157 } 158 return nil 159 } 160 161 func (t *testNotary) Close() { 162 t.cmd.Process.Kill() 163 os.RemoveAll(t.dir) 164 } 165 166 func (s *DockerTrustSuite) trustedCmd(cmd *exec.Cmd) { 167 pwd := "12345678" 168 trustCmdEnv(cmd, notaryURL, pwd, pwd) 169 } 170 171 func (s *DockerTrustSuite) trustedCmdWithServer(cmd *exec.Cmd, server string) { 172 pwd := "12345678" 173 trustCmdEnv(cmd, server, pwd, pwd) 174 } 175 176 func (s *DockerTrustSuite) trustedCmdWithPassphrases(cmd *exec.Cmd, rootPwd, repositoryPwd string) { 177 trustCmdEnv(cmd, notaryURL, rootPwd, repositoryPwd) 178 } 179 180 func trustCmdEnv(cmd *exec.Cmd, server, rootPwd, repositoryPwd string) { 181 env := []string{ 182 "DOCKER_CONTENT_TRUST=1", 183 fmt.Sprintf("DOCKER_CONTENT_TRUST_SERVER=%s", server), 184 fmt.Sprintf("DOCKER_CONTENT_TRUST_ROOT_PASSPHRASE=%s", rootPwd), 185 fmt.Sprintf("DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE=%s", repositoryPwd), 186 } 187 cmd.Env = append(os.Environ(), env...) 188 } 189 190 func (s *DockerTrustSuite) setupTrustedImage(c *check.C, name string) string { 191 repoName := fmt.Sprintf("%v/dockercli/%s:latest", privateRegistryURL, name) 192 // tag the image and upload it to the private registry 193 dockerCmd(c, "tag", "busybox", repoName) 194 195 pushCmd := exec.Command(dockerBinary, "push", repoName) 196 s.trustedCmd(pushCmd) 197 out, _, err := runCommandWithOutput(pushCmd) 198 199 if err != nil { 200 c.Fatalf("Error running trusted push: %s\n%s", err, out) 201 } 202 if !strings.Contains(string(out), "Signing and pushing trust metadata") { 203 c.Fatalf("Missing expected output on trusted push:\n%s", out) 204 } 205 206 if out, status := dockerCmd(c, "rmi", repoName); status != 0 { 207 c.Fatalf("Error removing image %q\n%s", repoName, out) 208 } 209 210 return repoName 211 } 212 213 func notaryClientEnv(cmd *exec.Cmd) { 214 pwd := "12345678" 215 env := []string{ 216 fmt.Sprintf("NOTARY_ROOT_PASSPHRASE=%s", pwd), 217 fmt.Sprintf("NOTARY_TARGETS_PASSPHRASE=%s", pwd), 218 fmt.Sprintf("NOTARY_SNAPSHOT_PASSPHRASE=%s", pwd), 219 fmt.Sprintf("NOTARY_DELEGATION_PASSPHRASE=%s", pwd), 220 } 221 cmd.Env = append(os.Environ(), env...) 222 } 223 224 func (s *DockerTrustSuite) notaryInitRepo(c *check.C, repoName string) { 225 initCmd := exec.Command(notaryBinary, "-c", filepath.Join(s.not.dir, "client-config.json"), "init", repoName) 226 notaryClientEnv(initCmd) 227 out, _, err := runCommandWithOutput(initCmd) 228 if err != nil { 229 c.Fatalf("Error initializing notary repository: %s\n", out) 230 } 231 } 232 233 func (s *DockerTrustSuite) notaryCreateDelegation(c *check.C, repoName, role string, pubKey string, paths ...string) { 234 pathsArg := "--all-paths" 235 if len(paths) > 0 { 236 pathsArg = "--paths=" + strings.Join(paths, ",") 237 } 238 239 delgCmd := exec.Command(notaryBinary, "-c", filepath.Join(s.not.dir, "client-config.json"), 240 "delegation", "add", repoName, role, pubKey, pathsArg) 241 notaryClientEnv(delgCmd) 242 out, _, err := runCommandWithOutput(delgCmd) 243 if err != nil { 244 c.Fatalf("Error adding %s role to notary repository: %s\n", role, out) 245 } 246 } 247 248 func (s *DockerTrustSuite) notaryPublish(c *check.C, repoName string) { 249 pubCmd := exec.Command(notaryBinary, "-c", filepath.Join(s.not.dir, "client-config.json"), "publish", repoName) 250 notaryClientEnv(pubCmd) 251 out, _, err := runCommandWithOutput(pubCmd) 252 if err != nil { 253 c.Fatalf("Error publishing notary repository: %s\n", out) 254 } 255 } 256 257 func (s *DockerTrustSuite) notaryImportKey(c *check.C, repoName, role string, privKey string) { 258 impCmd := exec.Command(notaryBinary, "-c", filepath.Join(s.not.dir, "client-config.json"), "key", 259 "import", privKey, "-g", repoName, "-r", role) 260 notaryClientEnv(impCmd) 261 out, _, err := runCommandWithOutput(impCmd) 262 if err != nil { 263 c.Fatalf("Error importing key to notary repository: %s\n", out) 264 } 265 } 266 267 func (s *DockerTrustSuite) notaryListTargetsInRole(c *check.C, repoName, role string) map[string]string { 268 listCmd := exec.Command(notaryBinary, "-c", filepath.Join(s.not.dir, "client-config.json"), "list", 269 repoName, "-r", role) 270 notaryClientEnv(listCmd) 271 out, _, err := runCommandWithOutput(listCmd) 272 if err != nil { 273 c.Fatalf("Error listing targets in notary repository: %s\n", out) 274 } 275 276 // should look something like: 277 // NAME DIGEST SIZE (BYTES) ROLE 278 // ------------------------------------------------------------------------------------------------------ 279 // latest 24a36bbc059b1345b7e8be0df20f1b23caa3602e85d42fff7ecd9d0bd255de56 1377 targets 280 281 targets := make(map[string]string) 282 283 // no target 284 lines := strings.Split(strings.TrimSpace(out), "\n") 285 if len(lines) == 1 && strings.Contains(out, "No targets present in this repository.") { 286 return targets 287 } 288 289 // otherwise, there is at least one target 290 c.Assert(len(lines), checker.GreaterOrEqualThan, 3) 291 292 for _, line := range lines[2:] { 293 tokens := strings.Fields(line) 294 c.Assert(tokens, checker.HasLen, 4) 295 targets[tokens[0]] = tokens[3] 296 } 297 298 return targets 299 } 300 301 func (s *DockerTrustSuite) assertTargetInRoles(c *check.C, repoName, target string, roles ...string) { 302 // check all the roles 303 for _, role := range roles { 304 targets := s.notaryListTargetsInRole(c, repoName, role) 305 roleName, ok := targets[target] 306 c.Assert(ok, checker.True) 307 c.Assert(roleName, checker.Equals, role) 308 } 309 } 310 311 func (s *DockerTrustSuite) assertTargetNotInRoles(c *check.C, repoName, target string, roles ...string) { 312 targets := s.notaryListTargetsInRole(c, repoName, "targets") 313 314 roleName, ok := targets[target] 315 if ok { 316 for _, role := range roles { 317 c.Assert(roleName, checker.Not(checker.Equals), role) 318 } 319 } 320 }