github.com/akerouanton/docker@v1.11.0-rc3/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 (s *DockerTrustSuite) trustedCmdWithDeprecatedEnvPassphrases(cmd *exec.Cmd, offlinePwd, taggingPwd string) { 181 trustCmdDeprecatedEnv(cmd, notaryURL, offlinePwd, taggingPwd) 182 } 183 184 func trustCmdEnv(cmd *exec.Cmd, server, rootPwd, repositoryPwd string) { 185 env := []string{ 186 "DOCKER_CONTENT_TRUST=1", 187 fmt.Sprintf("DOCKER_CONTENT_TRUST_SERVER=%s", server), 188 fmt.Sprintf("DOCKER_CONTENT_TRUST_ROOT_PASSPHRASE=%s", rootPwd), 189 fmt.Sprintf("DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE=%s", repositoryPwd), 190 } 191 cmd.Env = append(os.Environ(), env...) 192 } 193 194 // Helper method to test the old env variables OFFLINE and TAGGING that will 195 // be deprecated by 1.10 196 func trustCmdDeprecatedEnv(cmd *exec.Cmd, server, offlinePwd, taggingPwd string) { 197 env := []string{ 198 "DOCKER_CONTENT_TRUST=1", 199 fmt.Sprintf("DOCKER_CONTENT_TRUST_SERVER=%s", server), 200 fmt.Sprintf("DOCKER_CONTENT_TRUST_OFFLINE_PASSPHRASE=%s", offlinePwd), 201 fmt.Sprintf("DOCKER_CONTENT_TRUST_TAGGING_PASSPHRASE=%s", taggingPwd), 202 } 203 cmd.Env = append(os.Environ(), env...) 204 } 205 206 func (s *DockerTrustSuite) setupTrustedImage(c *check.C, name string) string { 207 repoName := fmt.Sprintf("%v/dockercli/%s:latest", privateRegistryURL, name) 208 // tag the image and upload it to the private registry 209 dockerCmd(c, "tag", "busybox", repoName) 210 211 pushCmd := exec.Command(dockerBinary, "push", repoName) 212 s.trustedCmd(pushCmd) 213 out, _, err := runCommandWithOutput(pushCmd) 214 215 if err != nil { 216 c.Fatalf("Error running trusted push: %s\n%s", err, out) 217 } 218 if !strings.Contains(string(out), "Signing and pushing trust metadata") { 219 c.Fatalf("Missing expected output on trusted push:\n%s", out) 220 } 221 222 if out, status := dockerCmd(c, "rmi", repoName); status != 0 { 223 c.Fatalf("Error removing image %q\n%s", repoName, out) 224 } 225 226 return repoName 227 } 228 229 func notaryClientEnv(cmd *exec.Cmd) { 230 pwd := "12345678" 231 env := []string{ 232 fmt.Sprintf("NOTARY_ROOT_PASSPHRASE=%s", pwd), 233 fmt.Sprintf("NOTARY_TARGETS_PASSPHRASE=%s", pwd), 234 fmt.Sprintf("NOTARY_SNAPSHOT_PASSPHRASE=%s", pwd), 235 fmt.Sprintf("NOTARY_DELEGATION_PASSPHRASE=%s", pwd), 236 } 237 cmd.Env = append(os.Environ(), env...) 238 } 239 240 func (s *DockerTrustSuite) notaryInitRepo(c *check.C, repoName string) { 241 initCmd := exec.Command(notaryBinary, "-c", filepath.Join(s.not.dir, "client-config.json"), "init", repoName) 242 notaryClientEnv(initCmd) 243 out, _, err := runCommandWithOutput(initCmd) 244 if err != nil { 245 c.Fatalf("Error initializing notary repository: %s\n", out) 246 } 247 } 248 249 func (s *DockerTrustSuite) notaryCreateDelegation(c *check.C, repoName, role string, pubKey string, paths ...string) { 250 pathsArg := "--all-paths" 251 if len(paths) > 0 { 252 pathsArg = "--paths=" + strings.Join(paths, ",") 253 } 254 255 delgCmd := exec.Command(notaryBinary, "-c", filepath.Join(s.not.dir, "client-config.json"), 256 "delegation", "add", repoName, role, pubKey, pathsArg) 257 notaryClientEnv(delgCmd) 258 out, _, err := runCommandWithOutput(delgCmd) 259 if err != nil { 260 c.Fatalf("Error adding %s role to notary repository: %s\n", role, out) 261 } 262 } 263 264 func (s *DockerTrustSuite) notaryPublish(c *check.C, repoName string) { 265 pubCmd := exec.Command(notaryBinary, "-c", filepath.Join(s.not.dir, "client-config.json"), "publish", repoName) 266 notaryClientEnv(pubCmd) 267 out, _, err := runCommandWithOutput(pubCmd) 268 if err != nil { 269 c.Fatalf("Error publishing notary repository: %s\n", out) 270 } 271 } 272 273 func (s *DockerTrustSuite) notaryImportKey(c *check.C, repoName, role string, privKey string) { 274 impCmd := exec.Command(notaryBinary, "-c", filepath.Join(s.not.dir, "client-config.json"), "key", 275 "import", privKey, "-g", repoName, "-r", role) 276 notaryClientEnv(impCmd) 277 out, _, err := runCommandWithOutput(impCmd) 278 if err != nil { 279 c.Fatalf("Error importing key to notary repository: %s\n", out) 280 } 281 } 282 283 func (s *DockerTrustSuite) notaryListTargetsInRole(c *check.C, repoName, role string) map[string]string { 284 listCmd := exec.Command(notaryBinary, "-c", filepath.Join(s.not.dir, "client-config.json"), "list", 285 repoName, "-r", role) 286 notaryClientEnv(listCmd) 287 out, _, err := runCommandWithOutput(listCmd) 288 if err != nil { 289 c.Fatalf("Error listing targets in notary repository: %s\n", out) 290 } 291 292 // should look something like: 293 // NAME DIGEST SIZE (BYTES) ROLE 294 // ------------------------------------------------------------------------------------------------------ 295 // latest 24a36bbc059b1345b7e8be0df20f1b23caa3602e85d42fff7ecd9d0bd255de56 1377 targets 296 297 targets := make(map[string]string) 298 299 // no target 300 lines := strings.Split(strings.TrimSpace(out), "\n") 301 if len(lines) == 1 && strings.Contains(out, "No targets present in this repository.") { 302 return targets 303 } 304 305 // otherwise, there is at least one target 306 c.Assert(len(lines), checker.GreaterOrEqualThan, 3) 307 308 for _, line := range lines[2:] { 309 tokens := strings.Fields(line) 310 c.Assert(tokens, checker.HasLen, 4) 311 targets[tokens[0]] = tokens[3] 312 } 313 314 return targets 315 } 316 317 func (s *DockerTrustSuite) assertTargetInRoles(c *check.C, repoName, target string, roles ...string) { 318 // check all the roles 319 for _, role := range roles { 320 targets := s.notaryListTargetsInRole(c, repoName, role) 321 roleName, ok := targets[target] 322 c.Assert(ok, checker.True) 323 c.Assert(roleName, checker.Equals, role) 324 } 325 } 326 327 func (s *DockerTrustSuite) assertTargetNotInRoles(c *check.C, repoName, target string, roles ...string) { 328 targets := s.notaryListTargetsInRole(c, repoName, "targets") 329 330 roleName, ok := targets[target] 331 if ok { 332 for _, role := range roles { 333 c.Assert(roleName, checker.Not(checker.Equals), role) 334 } 335 } 336 }