github.com/ssdev-go/moby@v17.12.1-ce-rc2+incompatible/integration-cli/trust_server_test.go (about) 1 package main 2 3 import ( 4 "context" 5 "fmt" 6 "io/ioutil" 7 "net" 8 "net/http" 9 "os" 10 "os/exec" 11 "path/filepath" 12 "strings" 13 "time" 14 15 "github.com/docker/docker/api/types" 16 cliconfig "github.com/docker/docker/cli/config" 17 "github.com/docker/docker/integration-cli/checker" 18 "github.com/docker/docker/integration-cli/cli" 19 "github.com/docker/docker/integration-cli/fixtures/plugin" 20 "github.com/docker/docker/integration-cli/request" 21 "github.com/docker/go-connections/tlsconfig" 22 "github.com/go-check/check" 23 "github.com/gotestyourself/gotestyourself/icmd" 24 ) 25 26 var notaryBinary = "notary" 27 var notaryServerBinary = "notary-server" 28 29 type keyPair struct { 30 Public string 31 Private string 32 } 33 34 type testNotary struct { 35 cmd *exec.Cmd 36 dir string 37 keys []keyPair 38 } 39 40 const notaryHost = "localhost:4443" 41 const notaryURL = "https://" + notaryHost 42 43 var SuccessTagging = icmd.Expected{ 44 Err: "Tagging", 45 } 46 47 var SuccessSigningAndPushing = icmd.Expected{ 48 Out: "Signing and pushing trust metadata", 49 } 50 51 var SuccessDownloaded = icmd.Expected{ 52 Out: "Status: Downloaded", 53 } 54 55 var SuccessDownloadedOnStderr = icmd.Expected{ 56 Err: "Status: Downloaded", 57 } 58 59 func newTestNotary(c *check.C) (*testNotary, error) { 60 // generate server config 61 template := `{ 62 "server": { 63 "http_addr": "%s", 64 "tls_key_file": "%s", 65 "tls_cert_file": "%s" 66 }, 67 "trust_service": { 68 "type": "local", 69 "hostname": "", 70 "port": "", 71 "key_algorithm": "ed25519" 72 }, 73 "logging": { 74 "level": "debug" 75 }, 76 "storage": { 77 "backend": "memory" 78 } 79 }` 80 tmp, err := ioutil.TempDir("", "notary-test-") 81 if err != nil { 82 return nil, err 83 } 84 confPath := filepath.Join(tmp, "config.json") 85 config, err := os.Create(confPath) 86 if err != nil { 87 return nil, err 88 } 89 defer config.Close() 90 91 workingDir, err := os.Getwd() 92 if err != nil { 93 return nil, err 94 } 95 if _, err := fmt.Fprintf(config, template, notaryHost, filepath.Join(workingDir, "fixtures/notary/localhost.key"), filepath.Join(workingDir, "fixtures/notary/localhost.cert")); err != nil { 96 os.RemoveAll(tmp) 97 return nil, err 98 } 99 100 // generate client config 101 clientConfPath := filepath.Join(tmp, "client-config.json") 102 clientConfig, err := os.Create(clientConfPath) 103 if err != nil { 104 return nil, err 105 } 106 defer clientConfig.Close() 107 108 template = `{ 109 "trust_dir" : "%s", 110 "remote_server": { 111 "url": "%s", 112 "skipTLSVerify": true 113 } 114 }` 115 if _, err = fmt.Fprintf(clientConfig, template, filepath.Join(cliconfig.Dir(), "trust"), notaryURL); err != nil { 116 os.RemoveAll(tmp) 117 return nil, err 118 } 119 120 // load key fixture filenames 121 var keys []keyPair 122 for i := 1; i < 5; i++ { 123 keys = append(keys, keyPair{ 124 Public: filepath.Join(workingDir, fmt.Sprintf("fixtures/notary/delgkey%v.crt", i)), 125 Private: filepath.Join(workingDir, fmt.Sprintf("fixtures/notary/delgkey%v.key", i)), 126 }) 127 } 128 129 // run notary-server 130 cmd := exec.Command(notaryServerBinary, "-config", confPath) 131 if err := cmd.Start(); err != nil { 132 os.RemoveAll(tmp) 133 if os.IsNotExist(err) { 134 c.Skip(err.Error()) 135 } 136 return nil, err 137 } 138 139 testNotary := &testNotary{ 140 cmd: cmd, 141 dir: tmp, 142 keys: keys, 143 } 144 145 // Wait for notary to be ready to serve requests. 146 for i := 1; i <= 20; i++ { 147 if err = testNotary.Ping(); err == nil { 148 break 149 } 150 time.Sleep(10 * time.Millisecond * time.Duration(i*i)) 151 } 152 153 if err != nil { 154 c.Fatalf("Timeout waiting for test notary to become available: %s", err) 155 } 156 157 return testNotary, nil 158 } 159 160 func (t *testNotary) Ping() error { 161 tlsConfig := tlsconfig.ClientDefault() 162 tlsConfig.InsecureSkipVerify = true 163 client := http.Client{ 164 Transport: &http.Transport{ 165 Proxy: http.ProxyFromEnvironment, 166 Dial: (&net.Dialer{ 167 Timeout: 30 * time.Second, 168 KeepAlive: 30 * time.Second, 169 }).Dial, 170 TLSHandshakeTimeout: 10 * time.Second, 171 TLSClientConfig: tlsConfig, 172 }, 173 } 174 resp, err := client.Get(fmt.Sprintf("%s/v2/", notaryURL)) 175 if err != nil { 176 return err 177 } 178 if resp.StatusCode != http.StatusOK { 179 return fmt.Errorf("notary ping replied with an unexpected status code %d", resp.StatusCode) 180 } 181 return nil 182 } 183 184 func (t *testNotary) Close() { 185 t.cmd.Process.Kill() 186 t.cmd.Process.Wait() 187 os.RemoveAll(t.dir) 188 } 189 190 func trustedCmd(cmd *icmd.Cmd) func() { 191 pwd := "12345678" 192 cmd.Env = append(cmd.Env, trustEnv(notaryURL, pwd, pwd)...) 193 return nil 194 } 195 196 func trustedCmdWithServer(server string) func(*icmd.Cmd) func() { 197 return func(cmd *icmd.Cmd) func() { 198 pwd := "12345678" 199 cmd.Env = append(cmd.Env, trustEnv(server, pwd, pwd)...) 200 return nil 201 } 202 } 203 204 func trustedCmdWithPassphrases(rootPwd, repositoryPwd string) func(*icmd.Cmd) func() { 205 return func(cmd *icmd.Cmd) func() { 206 cmd.Env = append(cmd.Env, trustEnv(notaryURL, rootPwd, repositoryPwd)...) 207 return nil 208 } 209 } 210 211 func trustEnv(server, rootPwd, repositoryPwd string) []string { 212 env := append(os.Environ(), []string{ 213 "DOCKER_CONTENT_TRUST=1", 214 fmt.Sprintf("DOCKER_CONTENT_TRUST_SERVER=%s", server), 215 fmt.Sprintf("DOCKER_CONTENT_TRUST_ROOT_PASSPHRASE=%s", rootPwd), 216 fmt.Sprintf("DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE=%s", repositoryPwd), 217 }...) 218 return env 219 } 220 221 func (s *DockerTrustSuite) setupTrustedImage(c *check.C, name string) string { 222 repoName := fmt.Sprintf("%v/dockercli/%s:latest", privateRegistryURL, name) 223 // tag the image and upload it to the private registry 224 cli.DockerCmd(c, "tag", "busybox", repoName) 225 cli.Docker(cli.Args("push", repoName), trustedCmd).Assert(c, SuccessSigningAndPushing) 226 cli.DockerCmd(c, "rmi", repoName) 227 return repoName 228 } 229 230 func (s *DockerTrustSuite) setupTrustedplugin(c *check.C, source, name string) string { 231 repoName := fmt.Sprintf("%v/dockercli/%s:latest", privateRegistryURL, name) 232 233 client, err := request.NewClient() 234 c.Assert(err, checker.IsNil, check.Commentf("could not create test client")) 235 236 ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) 237 err = plugin.Create(ctx, client, repoName) 238 cancel() 239 c.Assert(err, checker.IsNil, check.Commentf("could not create test plugin")) 240 241 // tag the image and upload it to the private registry 242 // TODO: shouldn't need to use the CLI to do trust 243 cli.Docker(cli.Args("plugin", "push", repoName), trustedCmd).Assert(c, SuccessSigningAndPushing) 244 245 ctx, cancel = context.WithTimeout(context.Background(), 60*time.Second) 246 err = client.PluginRemove(ctx, repoName, types.PluginRemoveOptions{Force: true}) 247 cancel() 248 c.Assert(err, checker.IsNil, check.Commentf("failed to cleanup test plugin for trust suite")) 249 return repoName 250 } 251 252 func (s *DockerTrustSuite) notaryCmd(c *check.C, args ...string) string { 253 pwd := "12345678" 254 env := []string{ 255 fmt.Sprintf("NOTARY_ROOT_PASSPHRASE=%s", pwd), 256 fmt.Sprintf("NOTARY_TARGETS_PASSPHRASE=%s", pwd), 257 fmt.Sprintf("NOTARY_SNAPSHOT_PASSPHRASE=%s", pwd), 258 fmt.Sprintf("NOTARY_DELEGATION_PASSPHRASE=%s", pwd), 259 } 260 result := icmd.RunCmd(icmd.Cmd{ 261 Command: append([]string{notaryBinary, "-c", filepath.Join(s.not.dir, "client-config.json")}, args...), 262 Env: append(os.Environ(), env...), 263 }) 264 result.Assert(c, icmd.Success) 265 return result.Combined() 266 } 267 268 func (s *DockerTrustSuite) notaryInitRepo(c *check.C, repoName string) { 269 s.notaryCmd(c, "init", repoName) 270 } 271 272 func (s *DockerTrustSuite) notaryCreateDelegation(c *check.C, repoName, role string, pubKey string, paths ...string) { 273 pathsArg := "--all-paths" 274 if len(paths) > 0 { 275 pathsArg = "--paths=" + strings.Join(paths, ",") 276 } 277 278 s.notaryCmd(c, "delegation", "add", repoName, role, pubKey, pathsArg) 279 } 280 281 func (s *DockerTrustSuite) notaryPublish(c *check.C, repoName string) { 282 s.notaryCmd(c, "publish", repoName) 283 } 284 285 func (s *DockerTrustSuite) notaryImportKey(c *check.C, repoName, role string, privKey string) { 286 s.notaryCmd(c, "key", "import", privKey, "-g", repoName, "-r", role) 287 } 288 289 func (s *DockerTrustSuite) notaryListTargetsInRole(c *check.C, repoName, role string) map[string]string { 290 out := s.notaryCmd(c, "list", repoName, "-r", role) 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 }