1 package image
2
3 import (
4 "fmt"
5 "os"
6 "strings"
7 "testing"
8
9 "github.com/docker/cli/e2e/internal/fixtures"
10 "github.com/docker/cli/internal/test/environment"
11 "github.com/docker/cli/internal/test/output"
12 "gotest.tools/v3/assert"
13 "gotest.tools/v3/fs"
14 "gotest.tools/v3/golden"
15 "gotest.tools/v3/icmd"
16 "gotest.tools/v3/skip"
17 )
18
19 const (
20 notary = "/usr/local/bin/notary"
21
22 pubkey1 = "./testdata/notary/delgkey1.crt"
23 privkey1 = "./testdata/notary/delgkey1.key"
24 pubkey2 = "./testdata/notary/delgkey2.crt"
25 privkey2 = "./testdata/notary/delgkey2.key"
26 pubkey3 = "./testdata/notary/delgkey3.crt"
27 privkey3 = "./testdata/notary/delgkey3.key"
28 pubkey4 = "./testdata/notary/delgkey4.crt"
29 privkey4 = "./testdata/notary/delgkey4.key"
30 )
31
32 func TestPushAllTags(t *testing.T) {
33 skip.If(t, environment.RemoteDaemon())
34
35
36
37 environment.SkipIfNotPlatform(t, "linux/amd64")
38
39 _ = createImage(t, "push-all-tags", "latest", "v1", "v1.0", "v1.0.1")
40 result := icmd.RunCmd(icmd.Command("docker", "push", "--all-tags", registryPrefix+"/push-all-tags"))
41
42 result.Assert(t, icmd.Success)
43 golden.Assert(t, result.Stderr(), "push-with-content-trust-err.golden")
44 output.Assert(t, result.Stdout(), map[int]func(string) error{
45 0: output.Equals("The push refers to repository [registry:5000/push-all-tags]"),
46 1: output.Equals("7cd52847ad77: Preparing"),
47 3: output.Equals("latest: digest: sha256:e2e16842c9b54d985bf1ef9242a313f36b856181f188de21313820e177002501 size: 528"),
48 6: output.Equals("v1: digest: sha256:e2e16842c9b54d985bf1ef9242a313f36b856181f188de21313820e177002501 size: 528"),
49 9: output.Equals("v1.0: digest: sha256:e2e16842c9b54d985bf1ef9242a313f36b856181f188de21313820e177002501 size: 528"),
50 12: output.Equals("v1.0.1: digest: sha256:e2e16842c9b54d985bf1ef9242a313f36b856181f188de21313820e177002501 size: 528"),
51 })
52 }
53
54 func TestPushWithContentTrust(t *testing.T) {
55 skip.If(t, environment.RemoteDaemon())
56
57
58
59 environment.SkipIfNotPlatform(t, "linux/amd64")
60
61 dir := fixtures.SetupConfigFile(t)
62 defer dir.Remove()
63 image := createImage(t, "trust-push", "latest")
64
65 result := icmd.RunCmd(icmd.Command("docker", "push", image),
66 fixtures.WithConfig(dir.Path()),
67 fixtures.WithTrust,
68 fixtures.WithNotary,
69 fixtures.WithPassphrase("foo", "bar"),
70 )
71 result.Assert(t, icmd.Success)
72 golden.Assert(t, result.Stderr(), "push-with-content-trust-err.golden")
73 output.Assert(t, result.Stdout(), map[int]func(string) error{
74 0: output.Equals("The push refers to repository [registry:5000/trust-push]"),
75 1: output.Equals("7cd52847ad77: Preparing"),
76 3: output.Equals("latest: digest: sha256:e2e16842c9b54d985bf1ef9242a313f36b856181f188de21313820e177002501 size: 528"),
77 4: output.Equals("Signing and pushing trust metadata"),
78 5: output.Equals(`Finished initializing "registry:5000/trust-push"`),
79 6: output.Equals("Successfully signed registry:5000/trust-push:latest"),
80 })
81 }
82
83 func TestPushQuietErrors(t *testing.T) {
84 result := icmd.RunCmd(icmd.Command("docker", "push", "--quiet", "nosuchimage"))
85 result.Assert(t, icmd.Expected{
86 ExitCode: 1,
87 Err: "An image does not exist locally with the tag: nosuchimage",
88 })
89 }
90
91 func TestPushWithContentTrustUnreachableServer(t *testing.T) {
92 skip.If(t, environment.RemoteDaemon())
93
94 dir := fixtures.SetupConfigFile(t)
95 defer dir.Remove()
96 image := createImage(t, "trust-push-unreachable", "latest")
97
98 result := icmd.RunCmd(icmd.Command("docker", "push", image),
99 fixtures.WithConfig(dir.Path()),
100 fixtures.WithTrust,
101 fixtures.WithNotaryServer("https://invalidnotaryserver"),
102 )
103 result.Assert(t, icmd.Expected{
104 ExitCode: 1,
105 Err: "error contacting notary server",
106 })
107 }
108
109 func TestPushWithContentTrustExistingTag(t *testing.T) {
110 skip.If(t, environment.RemoteDaemon())
111
112 dir := fixtures.SetupConfigFile(t)
113 defer dir.Remove()
114 image := createImage(t, "trust-push-existing", "latest")
115
116 result := icmd.RunCmd(icmd.Command("docker", "push", image))
117 result.Assert(t, icmd.Success)
118
119 result = icmd.RunCmd(icmd.Command("docker", "push", image),
120 fixtures.WithConfig(dir.Path()),
121 fixtures.WithTrust,
122 fixtures.WithNotary,
123 fixtures.WithPassphrase("foo", "bar"),
124 )
125 result.Assert(t, icmd.Expected{
126 Out: "Signing and pushing trust metadata",
127 })
128
129
130 result = icmd.RunCmd(icmd.Command("docker", "push", image),
131 fixtures.WithConfig(dir.Path()),
132 fixtures.WithTrust,
133 fixtures.WithNotary,
134 fixtures.WithPassphrase("foo", "bar"),
135 )
136 result.Assert(t, icmd.Expected{
137 Out: "Signing and pushing trust metadata",
138 })
139 }
140
141 func TestPushWithContentTrustReleasesDelegationOnly(t *testing.T) {
142 skip.If(t, environment.RemoteDaemon())
143
144 role := "targets/releases"
145
146 dir := fixtures.SetupConfigFile(t)
147 defer dir.Remove()
148 copyPrivateKey(t, dir.Join("trust", "private"), privkey1)
149 notaryDir := setupNotaryConfig(t, dir)
150 defer notaryDir.Remove()
151 homeDir := fs.NewDir(t, "push_test_home")
152 defer notaryDir.Remove()
153
154 baseRef := fmt.Sprintf("%s/%s", registryPrefix, "trust-push-releases-delegation")
155 targetRef := fmt.Sprintf("%s:%s", baseRef, "latest")
156
157
158 notaryInit(t, notaryDir, homeDir, baseRef)
159
160 notaryAddDelegation(t, notaryDir, homeDir, baseRef, role, pubkey1)
161
162 notaryPublish(t, notaryDir, homeDir, baseRef)
163
164 notaryImportPrivateKey(t, notaryDir, homeDir, baseRef, role, privkey1)
165
166
167 icmd.RunCommand("docker", "pull", fixtures.AlpineImage).Assert(t, icmd.Success)
168 icmd.RunCommand("docker", "tag", fixtures.AlpineImage, targetRef).Assert(t, icmd.Success)
169 result := icmd.RunCmd(icmd.Command("docker", "push", targetRef),
170 fixtures.WithConfig(dir.Path()),
171 fixtures.WithTrust,
172 fixtures.WithNotary,
173 fixtures.WithPassphrase("foo", "foo"),
174 )
175 result.Assert(t, icmd.Expected{
176 Out: "Signing and pushing trust metadata",
177 })
178
179 targetsInRole := notaryListTargetsInRole(t, notaryDir, homeDir, baseRef, role)
180 assert.Assert(t, targetsInRole["latest"] == role, "%v", targetsInRole)
181 targetsInRole = notaryListTargetsInRole(t, notaryDir, homeDir, baseRef, "targets")
182 assert.Assert(t, targetsInRole["latest"] != "targets", "%v", targetsInRole)
183
184 result = icmd.RunCmd(icmd.Command("docker", "pull", targetRef),
185 fixtures.WithConfig(dir.Path()),
186 fixtures.WithTrust,
187 fixtures.WithNotary,
188 )
189 result.Assert(t, icmd.Success)
190 }
191
192 func TestPushWithContentTrustSignsAllFirstLevelRolesWeHaveKeysFor(t *testing.T) {
193 skip.If(t, environment.RemoteDaemon())
194
195 dir := fixtures.SetupConfigFile(t)
196 defer dir.Remove()
197 copyPrivateKey(t, dir.Join("trust", "private"), privkey1)
198 copyPrivateKey(t, dir.Join("trust", "private"), privkey2)
199 copyPrivateKey(t, dir.Join("trust", "private"), privkey3)
200 notaryDir := setupNotaryConfig(t, dir)
201 defer notaryDir.Remove()
202 homeDir := fs.NewDir(t, "push_test_home")
203 defer notaryDir.Remove()
204
205 baseRef := fmt.Sprintf("%s/%s", registryPrefix, "trust-push-releases-first-roles")
206 targetRef := fmt.Sprintf("%s:%s", baseRef, "latest")
207
208
209 notaryInit(t, notaryDir, homeDir, baseRef)
210
211 notaryAddDelegation(t, notaryDir, homeDir, baseRef, "targets/role1", pubkey1)
212 notaryAddDelegation(t, notaryDir, homeDir, baseRef, "targets/role2", pubkey2)
213 notaryAddDelegation(t, notaryDir, homeDir, baseRef, "targets/role3", pubkey3)
214 notaryAddDelegation(t, notaryDir, homeDir, baseRef, "targets/role1/subrole", pubkey3)
215
216 notaryImportPrivateKey(t, notaryDir, homeDir, baseRef, "targets/role1", privkey1)
217 notaryImportPrivateKey(t, notaryDir, homeDir, baseRef, "targets/role2", privkey2)
218 notaryImportPrivateKey(t, notaryDir, homeDir, baseRef, "targets/role1/subrole", privkey3)
219
220 notaryPublish(t, notaryDir, homeDir, baseRef)
221
222
223 icmd.RunCommand("docker", "pull", fixtures.AlpineImage).Assert(t, icmd.Success)
224 icmd.RunCommand("docker", "tag", fixtures.AlpineImage, targetRef).Assert(t, icmd.Success)
225 result := icmd.RunCmd(icmd.Command("docker", "push", targetRef),
226 fixtures.WithConfig(dir.Path()),
227 fixtures.WithTrust,
228 fixtures.WithNotary,
229 fixtures.WithPassphrase("foo", "foo"),
230 )
231 result.Assert(t, icmd.Expected{
232 Out: "Signing and pushing trust metadata",
233 })
234
235
236
237
238 targetsInRole := notaryListTargetsInRole(t, notaryDir, homeDir, baseRef, "targets/role1")
239 assert.Assert(t, targetsInRole["latest"] == "targets/role1", "%v", targetsInRole)
240 targetsInRole = notaryListTargetsInRole(t, notaryDir, homeDir, baseRef, "targets/role2")
241 assert.Assert(t, targetsInRole["latest"] == "targets/role2", "%v", targetsInRole)
242 targetsInRole = notaryListTargetsInRole(t, notaryDir, homeDir, baseRef, "targets")
243 assert.Assert(t, targetsInRole["latest"] != "targets", "%v", targetsInRole)
244
245 assert.NilError(t, os.RemoveAll(dir.Join("trust")))
246
247
248 result = icmd.RunCmd(icmd.Command("docker", "pull", targetRef),
249 fixtures.WithConfig(dir.Path()),
250 fixtures.WithTrust,
251 fixtures.WithNotary,
252 )
253 result.Assert(t, icmd.Expected{
254 ExitCode: 1,
255 })
256 }
257
258 func TestPushWithContentTrustSignsForRolesWithKeysAndValidPaths(t *testing.T) {
259 skip.If(t, environment.RemoteDaemon())
260
261 dir := fixtures.SetupConfigFile(t)
262 defer dir.Remove()
263 copyPrivateKey(t, dir.Join("trust", "private"), privkey1)
264 copyPrivateKey(t, dir.Join("trust", "private"), privkey2)
265 copyPrivateKey(t, dir.Join("trust", "private"), privkey3)
266 copyPrivateKey(t, dir.Join("trust", "private"), privkey4)
267 notaryDir := setupNotaryConfig(t, dir)
268 defer notaryDir.Remove()
269 homeDir := fs.NewDir(t, "push_test_home")
270 defer notaryDir.Remove()
271
272 baseRef := fmt.Sprintf("%s/%s", registryPrefix, "trust-push-releases-keys-valid-paths")
273 targetRef := fmt.Sprintf("%s:%s", baseRef, "latest")
274
275
276 notaryInit(t, notaryDir, homeDir, baseRef)
277
278 notaryAddDelegation(t, notaryDir, homeDir, baseRef, "targets/role1", pubkey1, "l", "z")
279 notaryAddDelegation(t, notaryDir, homeDir, baseRef, "targets/role2", pubkey2, "x", "y")
280 notaryAddDelegation(t, notaryDir, homeDir, baseRef, "targets/role3", pubkey3, "latest")
281 notaryAddDelegation(t, notaryDir, homeDir, baseRef, "targets/role4", pubkey4, "latest")
282
283 notaryImportPrivateKey(t, notaryDir, homeDir, baseRef, "targets/role1", privkey1)
284 notaryImportPrivateKey(t, notaryDir, homeDir, baseRef, "targets/role2", privkey2)
285 notaryImportPrivateKey(t, notaryDir, homeDir, baseRef, "targets/role4", privkey4)
286
287 notaryPublish(t, notaryDir, homeDir, baseRef)
288
289
290 icmd.RunCommand("docker", "pull", fixtures.AlpineImage).Assert(t, icmd.Success)
291 icmd.RunCommand("docker", "tag", fixtures.AlpineImage, targetRef).Assert(t, icmd.Success)
292 result := icmd.RunCmd(icmd.Command("docker", "push", targetRef),
293 fixtures.WithConfig(dir.Path()),
294 fixtures.WithTrust,
295 fixtures.WithNotary,
296 fixtures.WithPassphrase("foo", "foo"),
297 )
298 result.Assert(t, icmd.Expected{
299 Out: "Signing and pushing trust metadata",
300 })
301
302
303
304
305 targetsInRole := notaryListTargetsInRole(t, notaryDir, homeDir, baseRef, "targets/role1")
306 assert.Assert(t, targetsInRole["latest"] == "targets/role1", "%v", targetsInRole)
307 targetsInRole = notaryListTargetsInRole(t, notaryDir, homeDir, baseRef, "targets/role4")
308 assert.Assert(t, targetsInRole["latest"] == "targets/role4", "%v", targetsInRole)
309 targetsInRole = notaryListTargetsInRole(t, notaryDir, homeDir, baseRef, "targets")
310 assert.Assert(t, targetsInRole["latest"] != "targets", "%v", targetsInRole)
311
312 assert.NilError(t, os.RemoveAll(dir.Join("trust")))
313
314
315 result = icmd.RunCmd(icmd.Command("docker", "pull", targetRef),
316 fixtures.WithConfig(dir.Path()),
317 fixtures.WithTrust,
318 fixtures.WithNotary,
319 )
320 result.Assert(t, icmd.Expected{
321 ExitCode: 1,
322 })
323 }
324
325 func createImage(t *testing.T, repo string, tags ...string) string {
326 t.Helper()
327 icmd.RunCommand("docker", "pull", fixtures.AlpineImage).Assert(t, icmd.Success)
328
329 for _, tag := range tags {
330 image := fmt.Sprintf("%s/%s:%s", registryPrefix, repo, tag)
331 icmd.RunCommand("docker", "tag", fixtures.AlpineImage, image).Assert(t, icmd.Success)
332 }
333 return fmt.Sprintf("%s/%s:%s", registryPrefix, repo, tags[0])
334 }
335
336
337 func withNotaryPassphrase(pwd string) func(*icmd.Cmd) {
338 return func(c *icmd.Cmd) {
339 c.Env = append(c.Env, []string{
340 fmt.Sprintf("NOTARY_ROOT_PASSPHRASE=%s", pwd),
341 fmt.Sprintf("NOTARY_TARGETS_PASSPHRASE=%s", pwd),
342 fmt.Sprintf("NOTARY_SNAPSHOT_PASSPHRASE=%s", pwd),
343 fmt.Sprintf("NOTARY_DELEGATION_PASSPHRASE=%s", pwd),
344 }...)
345 }
346 }
347
348 func notaryImportPrivateKey(t *testing.T, notaryDir, homeDir *fs.Dir, baseRef, role, privkey string) {
349 t.Helper()
350 icmd.RunCmd(
351 icmd.Command(notary, "-c", notaryDir.Join("client-config.json"), "key", "import", privkey, "-g", baseRef, "-r", role),
352 withNotaryPassphrase("foo"),
353 fixtures.WithHome(homeDir.Path()),
354 ).Assert(t, icmd.Success)
355 }
356
357 func notaryPublish(t *testing.T, notaryDir, homeDir *fs.Dir, baseRef string) {
358 t.Helper()
359 icmd.RunCmd(
360 icmd.Command(notary, "-c", notaryDir.Join("client-config.json"), "publish", baseRef),
361 withNotaryPassphrase("foo"),
362 fixtures.WithHome(homeDir.Path()),
363 ).Assert(t, icmd.Success)
364 }
365
366 func notaryAddDelegation(t *testing.T, notaryDir, homeDir *fs.Dir, baseRef, role, pubkey string, paths ...string) {
367 t.Helper()
368 pathsArg := "--all-paths"
369 if len(paths) > 0 {
370 pathsArg = "--paths=" + strings.Join(paths, ",")
371 }
372 icmd.RunCmd(
373 icmd.Command(notary, "-c", notaryDir.Join("client-config.json"), "delegation", "add", baseRef, role, pubkey, pathsArg),
374 withNotaryPassphrase("foo"),
375 fixtures.WithHome(homeDir.Path()),
376 ).Assert(t, icmd.Success)
377 }
378
379 func notaryInit(t *testing.T, notaryDir, homeDir *fs.Dir, baseRef string) {
380 t.Helper()
381 icmd.RunCmd(
382 icmd.Command(notary, "-c", notaryDir.Join("client-config.json"), "init", baseRef),
383 withNotaryPassphrase("foo"),
384 fixtures.WithHome(homeDir.Path()),
385 ).Assert(t, icmd.Success)
386 }
387
388 func notaryListTargetsInRole(t *testing.T, notaryDir, homeDir *fs.Dir, baseRef, role string) map[string]string {
389 t.Helper()
390 result := icmd.RunCmd(
391 icmd.Command(notary, "-c", notaryDir.Join("client-config.json"), "list", baseRef, "-r", role),
392 fixtures.WithHome(homeDir.Path()),
393 )
394 out := result.Combined()
395
396
397
398
399
400
401 targets := make(map[string]string)
402
403
404 lines := strings.Split(strings.TrimSpace(out), "\n")
405 if len(lines) == 1 && strings.Contains(out, "No targets present in this repository.") {
406 return targets
407 }
408
409
410 assert.Assert(t, len(lines) >= 3, "output is %s", out)
411
412 for _, line := range lines[2:] {
413 tokens := strings.Fields(line)
414 assert.Assert(t, len(tokens) == 4)
415 targets[tokens[0]] = tokens[3]
416 }
417
418 return targets
419 }
420
421 func setupNotaryConfig(t *testing.T, dockerConfigDir fs.Dir) *fs.Dir {
422 t.Helper()
423 return fs.NewDir(t, "notary_test", fs.WithMode(0o700),
424 fs.WithFile("client-config.json", fmt.Sprintf(`
425 {
426 "trust_dir": "%s",
427 "remote_server": {
428 "url": "%s"
429 }
430 }`, dockerConfigDir.Join("trust"), fixtures.NotaryURL)),
431 )
432 }
433
434 func copyPrivateKey(t *testing.T, dir, source string) {
435 t.Helper()
436 icmd.RunCommand("/bin/cp", source, dir).Assert(t, icmd.Success)
437 }
438
View as plain text