github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/cli/e2e/image/push_test.go (about)

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