github.com/samwhited/moby@v1.13.1/integration-cli/docker_cli_pull_trusted_test.go (about) 1 package main 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "os/exec" 7 "strings" 8 "time" 9 10 "github.com/docker/docker/pkg/integration/checker" 11 "github.com/go-check/check" 12 ) 13 14 func (s *DockerTrustSuite) TestTrustedPull(c *check.C) { 15 repoName := s.setupTrustedImage(c, "trusted-pull") 16 17 // Try pull 18 pullCmd := exec.Command(dockerBinary, "pull", repoName) 19 s.trustedCmd(pullCmd) 20 out, _, err := runCommandWithOutput(pullCmd) 21 22 c.Assert(err, check.IsNil, check.Commentf(out)) 23 c.Assert(string(out), checker.Contains, "Tagging", check.Commentf(out)) 24 25 dockerCmd(c, "rmi", repoName) 26 // Try untrusted pull to ensure we pushed the tag to the registry 27 pullCmd = exec.Command(dockerBinary, "pull", "--disable-content-trust=true", repoName) 28 s.trustedCmd(pullCmd) 29 out, _, err = runCommandWithOutput(pullCmd) 30 c.Assert(err, check.IsNil, check.Commentf(out)) 31 c.Assert(string(out), checker.Contains, "Status: Downloaded", check.Commentf(out)) 32 33 } 34 35 func (s *DockerTrustSuite) TestTrustedIsolatedPull(c *check.C) { 36 repoName := s.setupTrustedImage(c, "trusted-isolated-pull") 37 38 // Try pull (run from isolated directory without trust information) 39 pullCmd := exec.Command(dockerBinary, "--config", "/tmp/docker-isolated", "pull", repoName) 40 s.trustedCmd(pullCmd) 41 out, _, err := runCommandWithOutput(pullCmd) 42 43 c.Assert(err, check.IsNil, check.Commentf(out)) 44 c.Assert(string(out), checker.Contains, "Tagging", check.Commentf(string(out))) 45 46 dockerCmd(c, "rmi", repoName) 47 } 48 49 func (s *DockerTrustSuite) TestUntrustedPull(c *check.C) { 50 repoName := fmt.Sprintf("%v/dockercliuntrusted/pulltest:latest", privateRegistryURL) 51 // tag the image and upload it to the private registry 52 dockerCmd(c, "tag", "busybox", repoName) 53 dockerCmd(c, "push", repoName) 54 dockerCmd(c, "rmi", repoName) 55 56 // Try trusted pull on untrusted tag 57 pullCmd := exec.Command(dockerBinary, "pull", repoName) 58 s.trustedCmd(pullCmd) 59 out, _, err := runCommandWithOutput(pullCmd) 60 61 c.Assert(err, check.NotNil, check.Commentf(out)) 62 c.Assert(string(out), checker.Contains, "Error: remote trust data does not exist", check.Commentf(out)) 63 } 64 65 func (s *DockerTrustSuite) TestPullWhenCertExpired(c *check.C) { 66 c.Skip("Currently changes system time, causing instability") 67 repoName := s.setupTrustedImage(c, "trusted-cert-expired") 68 69 // Certificates have 10 years of expiration 70 elevenYearsFromNow := time.Now().Add(time.Hour * 24 * 365 * 11) 71 72 runAtDifferentDate(elevenYearsFromNow, func() { 73 // Try pull 74 pullCmd := exec.Command(dockerBinary, "pull", repoName) 75 s.trustedCmd(pullCmd) 76 out, _, err := runCommandWithOutput(pullCmd) 77 78 c.Assert(err, check.NotNil, check.Commentf(out)) 79 c.Assert(string(out), checker.Contains, "could not validate the path to a trusted root", check.Commentf(out)) 80 }) 81 82 runAtDifferentDate(elevenYearsFromNow, func() { 83 // Try pull 84 pullCmd := exec.Command(dockerBinary, "pull", "--disable-content-trust", repoName) 85 s.trustedCmd(pullCmd) 86 out, _, err := runCommandWithOutput(pullCmd) 87 88 c.Assert(err, check.IsNil, check.Commentf(out)) 89 c.Assert(string(out), checker.Contains, "Status: Downloaded", check.Commentf(out)) 90 }) 91 } 92 93 func (s *DockerTrustSuite) TestTrustedPullFromBadTrustServer(c *check.C) { 94 repoName := fmt.Sprintf("%v/dockerclievilpull/trusted:latest", privateRegistryURL) 95 evilLocalConfigDir, err := ioutil.TempDir("", "evil-local-config-dir") 96 if err != nil { 97 c.Fatalf("Failed to create local temp dir") 98 } 99 100 // tag the image and upload it to the private registry 101 dockerCmd(c, "tag", "busybox", repoName) 102 103 pushCmd := exec.Command(dockerBinary, "push", repoName) 104 s.trustedCmd(pushCmd) 105 out, _, err := runCommandWithOutput(pushCmd) 106 107 c.Assert(err, check.IsNil, check.Commentf(out)) 108 c.Assert(string(out), checker.Contains, "Signing and pushing trust metadata", check.Commentf(out)) 109 dockerCmd(c, "rmi", repoName) 110 111 // Try pull 112 pullCmd := exec.Command(dockerBinary, "pull", repoName) 113 s.trustedCmd(pullCmd) 114 out, _, err = runCommandWithOutput(pullCmd) 115 116 c.Assert(err, check.IsNil, check.Commentf(out)) 117 c.Assert(string(out), checker.Contains, "Tagging", check.Commentf(out)) 118 dockerCmd(c, "rmi", repoName) 119 120 // Kill the notary server, start a new "evil" one. 121 s.not.Close() 122 s.not, err = newTestNotary(c) 123 124 c.Assert(err, check.IsNil, check.Commentf("Restarting notary server failed.")) 125 126 // In order to make an evil server, lets re-init a client (with a different trust dir) and push new data. 127 // tag an image and upload it to the private registry 128 dockerCmd(c, "--config", evilLocalConfigDir, "tag", "busybox", repoName) 129 130 // Push up to the new server 131 pushCmd = exec.Command(dockerBinary, "--config", evilLocalConfigDir, "push", repoName) 132 s.trustedCmd(pushCmd) 133 out, _, err = runCommandWithOutput(pushCmd) 134 135 c.Assert(err, check.IsNil, check.Commentf(out)) 136 c.Assert(string(out), checker.Contains, "Signing and pushing trust metadata", check.Commentf(out)) 137 138 // Now, try pulling with the original client from this new trust server. This should fail because the new root is invalid. 139 pullCmd = exec.Command(dockerBinary, "pull", repoName) 140 s.trustedCmd(pullCmd) 141 out, _, err = runCommandWithOutput(pullCmd) 142 if err == nil { 143 c.Fatalf("Continuing with cached data even though it's an invalid root rotation: %s\n%s", err, out) 144 } 145 if !strings.Contains(out, "could not rotate trust to a new trusted root") { 146 c.Fatalf("Missing expected output on trusted pull:\n%s", out) 147 } 148 } 149 150 func (s *DockerTrustSuite) TestTrustedPullWithExpiredSnapshot(c *check.C) { 151 c.Skip("Currently changes system time, causing instability") 152 repoName := fmt.Sprintf("%v/dockercliexpiredtimestamppull/trusted:latest", privateRegistryURL) 153 // tag the image and upload it to the private registry 154 dockerCmd(c, "tag", "busybox", repoName) 155 156 // Push with default passphrases 157 pushCmd := exec.Command(dockerBinary, "push", repoName) 158 s.trustedCmd(pushCmd) 159 out, _, err := runCommandWithOutput(pushCmd) 160 161 c.Assert(err, check.IsNil, check.Commentf(out)) 162 c.Assert(string(out), checker.Contains, "Signing and pushing trust metadata", check.Commentf(out)) 163 164 dockerCmd(c, "rmi", repoName) 165 166 // Snapshots last for three years. This should be expired 167 fourYearsLater := time.Now().Add(time.Hour * 24 * 365 * 4) 168 169 runAtDifferentDate(fourYearsLater, func() { 170 // Try pull 171 pullCmd := exec.Command(dockerBinary, "pull", repoName) 172 s.trustedCmd(pullCmd) 173 out, _, err = runCommandWithOutput(pullCmd) 174 175 c.Assert(err, check.NotNil, check.Commentf("Missing expected error running trusted pull with expired snapshots")) 176 c.Assert(string(out), checker.Contains, "repository out-of-date", check.Commentf(out)) 177 }) 178 } 179 180 func (s *DockerTrustSuite) TestTrustedOfflinePull(c *check.C) { 181 repoName := s.setupTrustedImage(c, "trusted-offline-pull") 182 183 pullCmd := exec.Command(dockerBinary, "pull", repoName) 184 s.trustedCmdWithServer(pullCmd, "https://invalidnotaryserver") 185 out, _, err := runCommandWithOutput(pullCmd) 186 187 c.Assert(err, check.NotNil, check.Commentf(out)) 188 c.Assert(string(out), checker.Contains, "error contacting notary server", check.Commentf(out)) 189 // Do valid trusted pull to warm cache 190 pullCmd = exec.Command(dockerBinary, "pull", repoName) 191 s.trustedCmd(pullCmd) 192 out, _, err = runCommandWithOutput(pullCmd) 193 194 c.Assert(err, check.IsNil, check.Commentf(out)) 195 c.Assert(string(out), checker.Contains, "Tagging", check.Commentf(out)) 196 197 dockerCmd(c, "rmi", repoName) 198 199 // Try pull again with invalid notary server, should use cache 200 pullCmd = exec.Command(dockerBinary, "pull", repoName) 201 s.trustedCmdWithServer(pullCmd, "https://invalidnotaryserver") 202 out, _, err = runCommandWithOutput(pullCmd) 203 204 c.Assert(err, check.IsNil, check.Commentf(out)) 205 c.Assert(string(out), checker.Contains, "Tagging", check.Commentf(out)) 206 } 207 208 func (s *DockerTrustSuite) TestTrustedPullDelete(c *check.C) { 209 repoName := fmt.Sprintf("%v/dockercli/%s:latest", privateRegistryURL, "trusted-pull-delete") 210 // tag the image and upload it to the private registry 211 _, err := buildImage(repoName, ` 212 FROM busybox 213 CMD echo trustedpulldelete 214 `, true) 215 216 pushCmd := exec.Command(dockerBinary, "push", repoName) 217 s.trustedCmd(pushCmd) 218 out, _, err := runCommandWithOutput(pushCmd) 219 if err != nil { 220 c.Fatalf("Error running trusted push: %s\n%s", err, out) 221 } 222 if !strings.Contains(string(out), "Signing and pushing trust metadata") { 223 c.Fatalf("Missing expected output on trusted push:\n%s", out) 224 } 225 226 if out, status := dockerCmd(c, "rmi", repoName); status != 0 { 227 c.Fatalf("Error removing image %q\n%s", repoName, out) 228 } 229 230 // Try pull 231 pullCmd := exec.Command(dockerBinary, "pull", repoName) 232 s.trustedCmd(pullCmd) 233 out, _, err = runCommandWithOutput(pullCmd) 234 235 c.Assert(err, check.IsNil, check.Commentf(out)) 236 237 matches := digestRegex.FindStringSubmatch(out) 238 c.Assert(matches, checker.HasLen, 2, check.Commentf("unable to parse digest from pull output: %s", out)) 239 pullDigest := matches[1] 240 241 imageID := inspectField(c, repoName, "Id") 242 243 imageByDigest := repoName + "@" + pullDigest 244 byDigestID := inspectField(c, imageByDigest, "Id") 245 246 c.Assert(byDigestID, checker.Equals, imageID) 247 248 // rmi of tag should also remove the digest reference 249 dockerCmd(c, "rmi", repoName) 250 251 _, err = inspectFieldWithError(imageByDigest, "Id") 252 c.Assert(err, checker.NotNil, check.Commentf("digest reference should have been removed")) 253 254 _, err = inspectFieldWithError(imageID, "Id") 255 c.Assert(err, checker.NotNil, check.Commentf("image should have been deleted")) 256 } 257 258 func (s *DockerTrustSuite) TestTrustedPullReadsFromReleasesRole(c *check.C) { 259 testRequires(c, NotaryHosting) 260 repoName := fmt.Sprintf("%v/dockerclireleasesdelegationpulling/trusted", privateRegistryURL) 261 targetName := fmt.Sprintf("%s:latest", repoName) 262 263 // Push with targets first, initializing the repo 264 dockerCmd(c, "tag", "busybox", targetName) 265 pushCmd := exec.Command(dockerBinary, "push", targetName) 266 s.trustedCmd(pushCmd) 267 out, _, err := runCommandWithOutput(pushCmd) 268 c.Assert(err, check.IsNil, check.Commentf(out)) 269 s.assertTargetInRoles(c, repoName, "latest", "targets") 270 271 // Try pull, check we retrieve from targets role 272 pullCmd := exec.Command(dockerBinary, "-D", "pull", repoName) 273 s.trustedCmd(pullCmd) 274 out, _, err = runCommandWithOutput(pullCmd) 275 c.Assert(err, check.IsNil, check.Commentf(out)) 276 c.Assert(out, checker.Contains, "retrieving target for targets role") 277 278 // Now we'll create the releases role, and try pushing and pulling 279 s.notaryCreateDelegation(c, repoName, "targets/releases", s.not.keys[0].Public) 280 s.notaryImportKey(c, repoName, "targets/releases", s.not.keys[0].Private) 281 s.notaryPublish(c, repoName) 282 283 // try a pull, check that we can still pull because we can still read the 284 // old tag in the targets role 285 pullCmd = exec.Command(dockerBinary, "-D", "pull", repoName) 286 s.trustedCmd(pullCmd) 287 out, _, err = runCommandWithOutput(pullCmd) 288 c.Assert(err, check.IsNil, check.Commentf(out)) 289 c.Assert(out, checker.Contains, "retrieving target for targets role") 290 291 // try a pull -a, check that it succeeds because we can still pull from the 292 // targets role 293 pullCmd = exec.Command(dockerBinary, "-D", "pull", "-a", repoName) 294 s.trustedCmd(pullCmd) 295 out, _, err = runCommandWithOutput(pullCmd) 296 c.Assert(err, check.IsNil, check.Commentf(out)) 297 298 // Push, should sign with targets/releases 299 dockerCmd(c, "tag", "busybox", targetName) 300 pushCmd = exec.Command(dockerBinary, "push", targetName) 301 s.trustedCmd(pushCmd) 302 out, _, err = runCommandWithOutput(pushCmd) 303 s.assertTargetInRoles(c, repoName, "latest", "targets", "targets/releases") 304 305 // Try pull, check we retrieve from targets/releases role 306 pullCmd = exec.Command(dockerBinary, "-D", "pull", repoName) 307 s.trustedCmd(pullCmd) 308 out, _, err = runCommandWithOutput(pullCmd) 309 c.Assert(out, checker.Contains, "retrieving target for targets/releases role") 310 311 // Create another delegation that we'll sign with 312 s.notaryCreateDelegation(c, repoName, "targets/other", s.not.keys[1].Public) 313 s.notaryImportKey(c, repoName, "targets/other", s.not.keys[1].Private) 314 s.notaryPublish(c, repoName) 315 316 dockerCmd(c, "tag", "busybox", targetName) 317 pushCmd = exec.Command(dockerBinary, "push", targetName) 318 s.trustedCmd(pushCmd) 319 out, _, err = runCommandWithOutput(pushCmd) 320 s.assertTargetInRoles(c, repoName, "latest", "targets", "targets/releases", "targets/other") 321 322 // Try pull, check we retrieve from targets/releases role 323 pullCmd = exec.Command(dockerBinary, "-D", "pull", repoName) 324 s.trustedCmd(pullCmd) 325 out, _, err = runCommandWithOutput(pullCmd) 326 c.Assert(out, checker.Contains, "retrieving target for targets/releases role") 327 } 328 329 func (s *DockerTrustSuite) TestTrustedPullIgnoresOtherDelegationRoles(c *check.C) { 330 testRequires(c, NotaryHosting) 331 repoName := fmt.Sprintf("%v/dockerclipullotherdelegation/trusted", privateRegistryURL) 332 targetName := fmt.Sprintf("%s:latest", repoName) 333 334 // We'll create a repo first with a non-release delegation role, so that when we 335 // push we'll sign it into the delegation role 336 s.notaryInitRepo(c, repoName) 337 s.notaryCreateDelegation(c, repoName, "targets/other", s.not.keys[0].Public) 338 s.notaryImportKey(c, repoName, "targets/other", s.not.keys[0].Private) 339 s.notaryPublish(c, repoName) 340 341 // Push should write to the delegation role, not targets 342 dockerCmd(c, "tag", "busybox", targetName) 343 pushCmd := exec.Command(dockerBinary, "push", targetName) 344 s.trustedCmd(pushCmd) 345 out, _, err := runCommandWithOutput(pushCmd) 346 c.Assert(err, check.IsNil, check.Commentf(out)) 347 s.assertTargetInRoles(c, repoName, "latest", "targets/other") 348 s.assertTargetNotInRoles(c, repoName, "latest", "targets") 349 350 // Try pull - we should fail, since pull will only pull from the targets/releases 351 // role or the targets role 352 pullCmd := exec.Command(dockerBinary, "-D", "pull", repoName) 353 s.trustedCmd(pullCmd) 354 out, _, err = runCommandWithOutput(pullCmd) 355 c.Assert(err, check.NotNil, check.Commentf(out)) 356 c.Assert(out, checker.Contains, "No trust data for") 357 358 // try a pull -a: we should fail since pull will only pull from the targets/releases 359 // role or the targets role 360 pullCmd = exec.Command(dockerBinary, "-D", "pull", "-a", repoName) 361 s.trustedCmd(pullCmd) 362 out, _, err = runCommandWithOutput(pullCmd) 363 c.Assert(err, check.NotNil, check.Commentf(out)) 364 c.Assert(out, checker.Contains, "No trusted tags for") 365 }