diff --git a/api/client/trust.go b/api/client/trust.go index fe23c2f9d3..a9efa0c135 100644 --- a/api/client/trust.go +++ b/api/client/trust.go @@ -258,6 +258,11 @@ func (cli *DockerCli) trustedReference(ref reference.NamedTagged) (reference.Can if err != nil { return nil, err } + // Only list tags in the top level targets role or the releases delegation role - ignore + // all other delegation roles + if t.Role != releasesRole && t.Role != data.CanonicalTargetsRole { + return nil, notaryError(repoInfo.FullName(), fmt.Errorf("No trust data for %s", ref.Tag())) + } r, err := convertTarget(t.Target) if err != nil { return nil, err @@ -331,13 +336,28 @@ func (cli *DockerCli) trustedPull(repoInfo *registry.RepositoryInfo, ref registr fmt.Fprintf(cli.out, "Skipping target for %q\n", repoInfo.Name()) continue } + // Only list tags in the top level targets role or the releases delegation role - ignore + // all other delegation roles + if tgt.Role != releasesRole && tgt.Role != data.CanonicalTargetsRole { + continue + } refs = append(refs, t) } + if len(refs) == 0 { + return notaryError(repoInfo.FullName(), fmt.Errorf("No trusted tags for %s", repoInfo.FullName())) + } } else { t, err := notaryRepo.GetTargetByName(ref.String(), releasesRole, data.CanonicalTargetsRole) if err != nil { return notaryError(repoInfo.FullName(), err) } + // Only get the tag if it's in the top level targets role or the releases delegation role + // ignore it if it's in any other delegation roles + if t.Role != releasesRole && t.Role != data.CanonicalTargetsRole { + return notaryError(repoInfo.FullName(), fmt.Errorf("No trust data for %s", ref.String())) + } + + logrus.Debugf("retrieving target for %s role\n", t.Role) r, err := convertTarget(t.Target) if err != nil { return err @@ -441,39 +461,95 @@ func (cli *DockerCli) trustedPush(repoInfo *registry.RepositoryInfo, tag string, return err } - if err := repo.AddTarget(target, releasesRole); err != nil { - return err + // get the latest repository metadata so we can figure out which roles to sign + _, err = repo.Update(false) + + switch err.(type) { + case client.ErrRepoNotInitialized, client.ErrRepositoryNotExist: + keys := repo.CryptoService.ListKeys(data.CanonicalRootRole) + var rootKeyID string + // always select the first root key + if len(keys) > 0 { + sort.Strings(keys) + rootKeyID = keys[0] + } else { + rootPublicKey, err := repo.CryptoService.Create(data.CanonicalRootRole, data.ECDSAKey) + if err != nil { + return err + } + rootKeyID = rootPublicKey.ID() + } + + // Initialize the notary repository with a remotely managed snapshot key + if err := repo.Initialize(rootKeyID, data.CanonicalSnapshotRole); err != nil { + return notaryError(repoInfo.FullName(), err) + } + fmt.Fprintf(cli.out, "Finished initializing %q\n", repoInfo.FullName()) + err = repo.AddTarget(target, data.CanonicalTargetsRole) + case nil: + // already initialized and we have successfully downloaded the latest metadata + err = cli.addTargetToAllSignableRoles(repo, target) + default: + return notaryError(repoInfo.FullName(), err) } - err = repo.Publish() if err == nil { - fmt.Fprintf(cli.out, "Successfully signed %q:%s\n", repoInfo.FullName(), tag) - return nil - } else if _, ok := err.(client.ErrRepoNotInitialized); !ok { + err = repo.Publish() + } + + if err != nil { fmt.Fprintf(cli.out, "Failed to sign %q:%s - %s\n", repoInfo.FullName(), tag, err.Error()) return notaryError(repoInfo.FullName(), err) } - keys := repo.CryptoService.ListKeys(data.CanonicalRootRole) - - var rootKeyID string - // always select the first root key - if len(keys) > 0 { - sort.Strings(keys) - rootKeyID = keys[0] - } else { - rootPublicKey, err := repo.CryptoService.Create(data.CanonicalRootRole, data.ECDSAKey) - if err != nil { - return err - } - rootKeyID = rootPublicKey.ID() - } - - // Initialize the notary repository with a remotely managed snapshot key - if err := repo.Initialize(rootKeyID, data.CanonicalSnapshotRole); err != nil { - return notaryError(repoInfo.FullName(), err) - } - fmt.Fprintf(cli.out, "Finished initializing %q\n", repoInfo.FullName()) - - return notaryError(repoInfo.FullName(), repo.Publish()) + fmt.Fprintf(cli.out, "Successfully signed %q:%s\n", repoInfo.FullName(), tag) + return nil +} + +// Attempt to add the image target to all the top level delegation roles we can +// (based on whether we have the signing key and whether the role's path allows +// us to). +// If there are no delegation roles, we add to the targets role. +func (cli *DockerCli) addTargetToAllSignableRoles(repo *client.NotaryRepository, target *client.Target) error { + var signableRoles []string + + // translate the full key names, which includes the GUN, into just the key IDs + allCanonicalKeyIDs := make(map[string]struct{}) + for fullKeyID := range repo.CryptoService.ListAllKeys() { + allCanonicalKeyIDs[path.Base(fullKeyID)] = struct{}{} + } + + allDelegationRoles, err := repo.GetDelegationRoles() + if err != nil { + return err + } + + // if there are no delegation roles, then just try to sign it into the targets role + if len(allDelegationRoles) == 0 { + return repo.AddTarget(target, data.CanonicalTargetsRole) + } + + // there are delegation roles, find every delegation role we have a key for, and + // attempt to sign into into all those roles. + for _, delegationRole := range allDelegationRoles { + // We do not support signing any delegation role that isn't a direct child of the targets role. + // Also don't bother checking the keys if we can't add the target + // to this role due to path restrictions + if path.Dir(delegationRole.Name) != data.CanonicalTargetsRole || !delegationRole.CheckPaths(target.Name) { + continue + } + + for _, canonicalKeyID := range delegationRole.KeyIDs { + if _, ok := allCanonicalKeyIDs[canonicalKeyID]; ok { + signableRoles = append(signableRoles, delegationRole.Name) + break + } + } + } + + if len(signableRoles) == 0 { + return fmt.Errorf("no valid signing keys for delegation roles") + } + + return repo.AddTarget(target, signableRoles...) } diff --git a/docs/security/trust/content_trust.md b/docs/security/trust/content_trust.md index 9984e1a16e..f11e4e6ef7 100644 --- a/docs/security/trust/content_trust.md +++ b/docs/security/trust/content_trust.md @@ -108,19 +108,13 @@ $ docker pull someimage@sha256:d149ab53f8718e987c3a3024bb8aa0e2caadf6c0328f1d9d8 ``` Trust for an image tag is managed through the use of signing keys. A key set is -created when an operation using content trust is first invoked. Docker's content -trust makes use of four different keys: +created when an operation using content trust is first invoked. A key set consists +of the following classes of keys: -| Key | Description | -|---------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| root key | Root of content trust for a image tag. When content trust is enabled, you create the root key once. | -| target and snapshot | These two keys are known together as the "repository" key. When content trust is enabled, you create this key when you add a new image repository. If you have the root key, you can export the repository key and allow other publishers to sign the image tags. | -| timestamp | This key applies to a repository. It allows Docker repositories to have freshness security guarantees without requiring periodic content refreshes on the client's side. | - -With the exception of the timestamp, all the keys are generated and stored locally -client-side. The timestamp is safely generated and stored in a signing server that -is deployed alongside the Docker registry. All keys are generated in a backend -service that isn't directly exposed to the internet and are encrypted at rest. +- an offline key that is the root of content trust for a image tag +- repository or tagging keys that sign tags +- server-managed keys such as the timestamp key, which provides freshness + security guarantees for your repository The following image depicts the various signing keys and their relationships: @@ -133,9 +127,9 @@ The following image depicts the various signing keys and their relationships: >tag from this repository prior to the loss. You should backup the root key somewhere safe. Given that it is only required -to create new repositories, it is a good idea to store it offline. Make sure you -read [Manage keys for content trust](trust_key_mng.md) information -for details on securing, and backing up your keys. +to create new repositories, it is a good idea to store it offline. +For details on securing, and backing up your keys, make sure you +read how to [manage keys for content trust](trust_key_mng.md). ## Survey of typical content trust operations @@ -302,4 +296,5 @@ $ docker push --disable-content-trust docker/trusttest:untrusted * [Manage keys for content trust](trust_key_mng.md) * [Automation with content trust](trust_automation.md) +* [Delegations for content trust](trust_delegation.md) * [Play in a content trust sandbox](trust_sandbox.md) diff --git a/docs/security/trust/index.md b/docs/security/trust/index.md index 9c2119da0a..8f59693236 100644 --- a/docs/security/trust/index.md +++ b/docs/security/trust/index.md @@ -17,4 +17,5 @@ The following topics are available: * [Content trust in Docker](content_trust.md) * [Manage keys for content trust](trust_key_mng.md) * [Automation with content trust](trust_automation.md) +* [Delegations for content trust](trust_delegation.md) * [Play in a content trust sandbox](trust_sandbox.md) diff --git a/docs/security/trust/trust_automation.md b/docs/security/trust/trust_automation.md index b100d133dc..1b3d4564f0 100644 --- a/docs/security/trust/trust_automation.md +++ b/docs/security/trust/trust_automation.md @@ -73,7 +73,8 @@ unable to process Dockerfile: No trust data for notrust ## Related information -* [Content trust in Docker](content_trust.md) +* [Content trust in Docker](content_trust.md) * [Manage keys for content trust](trust_key_mng.md) +* [Delegations for content trust](trust_delegation.md) * [Play in a content trust sandbox](trust_sandbox.md) diff --git a/docs/security/trust/trust_delegation.md b/docs/security/trust/trust_delegation.md new file mode 100644 index 0000000000..ac803bc9f1 --- /dev/null +++ b/docs/security/trust/trust_delegation.md @@ -0,0 +1,226 @@ + + +# Delegations for content trust + +Docker Engine supports the usage of the `targets/releases` delegation as the +canonical source of a trusted image tag. + +Using this delegation allows you to collaborate with other publishers without +sharing your repository key (a combination of your targets and snapshot keys - +please see "[Manage keys for content trust](trust_key_mng.md)" for more information). +A collaborator can keep their own delegation key private. + +The `targest/releases` delegation is currently an optional feature - in order +to set up delegations, you must use the Notary CLI: + +1. [Download the client](https://github.com/docker/notary/releases) and ensure that it is +available on your path + +2. Create a configuration file at `~/.notary/config.json` with the following content: + + ``` + { + "trust_dir" : "~/.docker/trust", + "remote_server": { + "url": "https://notary.docker.io" + } + } + ``` + + This tells Notary where the Docker Content Trust data is stored, and to use the + Notary server used for images in Docker Hub. + +For more detailed information about how to use Notary outside of the default +Docker Content Trust use cases, please refer to the +[the Notary CLI documentation](https://docs.docker.com/notary/getting_started/). + +Note that when publishing and listing delegation changes using the Notary client, +your Docker Hub credentials are required. + +## Generating delegation keys + +Your collaborator needs to generate a private key (either RSA or ECDSA) +and give you the public key so that you can add it to the `targets/releases` +delegation. + +The easiest way to for them to generate these keys is with OpenSSL. +Here is an example of how to generate a 2048-bit RSA portion key (all RSA keys +must be at least 2048 bits): + +``` +$ opensl genrsa -out delegation.key 2048 +Generating RSA private key, 2048 bit long modulus +....................................................+++ +............+++ +e is 65537 (0x10001) + +``` + +They should keep `delegation.key` private - this is what they will use to sign +tags. + +Then they need to generate a x509 certificate containing the public key, which is +what they will give to you. Here is the command to generate a CSR (certificate +signing request): + +``` +$ openssl req -new -sha256 -key delegation.key -out delegation.csr +``` + +Then they can send it to whichever CA you trust to sign certificates, or they +can self-sign the certificate (in this example, creating a certificate that is +valid for 1 year): + +``` +$ openssl x509 -req -days 365 -in delegation.csr -signkey delegation.key -out delegation.crt +``` + +Then they need to give you `delegation.crt`, whether it is self-signed or signed +by a CA. + +## Adding a delegation key to an existing repository + +If your repository was created using a version of Docker Engine prior to 1.11, +then before adding any delegations, you should rotate the snapshot key to the server +so that collaborators will not require your snapshot key to sign and publish tags: + +``` +$ notary key rotate docker.io// snapshot -r +``` + +This tells Notary to rotate a key for your particular image repository - note that +you must include the `docker.io/` prefix. `snapshot -r` specifies that you want +to rotate the snapshot key specifically, and you want the server to manage it (`-r` +stands for "remote"). + +When adding a delegation, your must acquire +[the PEM-encoded x509 certificate with the public key](#generating-delegation-keys) +of the collaborator you wish to delegate to. + +Assuming you have the certificate `delegation.crt`, you can add a delegation +for this user and then publish the delegation change: + +``` +$ notary delegation add docker.io// targets/releases delegation.crt --all-paths +$ notary publish docker.io// +``` + +The preceding example illustrates a request to add the delegation +`targets/releases` to the image repository, if it doesn't exist. Be sure to use +`targets/releases` - Notary supports multiple delegation roles, so if you mistype +the delegation name, the Notary CLI will not error. However, Docker Engine +supports reading only from `targets/releases`. + +It also adds the collaborator's public key to the delegation, enabling them to sign +the `targets/releases` delegation so long as they have the private key corresponding +to this public key. The `--all-paths` flags tells Notary not to restrict the tag +names that can be signed into `targets/releases`, which we highly recommend for +`targets/releases`. + +Publishing the changes tells the server about the changes to the `targets/releases` +delegation. + +After publishing, view the delegation information to ensure that you correctly added +the keys to `targets/releases`: + +``` +$ notary delegation list docker.io// + + ROLE PATHS KEY IDS THRESHOLD +--------------------------------------------------------------------------------------------------------------- + targets/releases "" 729c7094a8210fd1e780e7b17b7bb55c9a28a48b871b07f65d97baf93898523a 1 +``` + +You can see the `targets/releases` with its paths and the key ID you just added. + +Notary currently does not map collaborators names to keys, so we recommend +that you add and list delegation keys one at a time, and keep a mapping of the key +IDs to collaborators yourself should you need to remove a collaborator. + +## Removing a delegation key from an existing repository + +To revoke a collaborator's permission to sign tags for your image repository, you must +know the IDs of their keys, because you need to remove their keys from the +`targets/releases` delegation. + +``` +$ notary delegation remove docker.io// targets/releases 729c7094a8210fd1e780e7b17b7bb55c9a28a48b871b07f65d97baf93898523a + +Removal of delegation role targets/releases with keys [729c7094a8210fd1e780e7b17b7bb55c9a28a48b871b07f65d97baf93898523a], to repository "docker.io//" staged for next publish. +``` + +The revocation will take effect as soon as you publish: + +``` +$ notary publish docker.io// +``` + +Note that by removing all the keys from the `targets/releases` delegation, the +delegation (and any tags that are signed into it) is removed. That means that +these tags will all be deleted, and you may end up with older, legacy tags that +were signed directly by the targets key. + +## Removing the `targets/releases` delegation entirely from a repository + +If you've decided that delegations aren't for you, you can delete the +`targets/releases` delegation entirely. This also removes all the tags that +are currently in `targets/releases`, however, and you may end up with older, +legacy tags that were signed directly by the targets key. + +To delete the `targets/releases` delegation: + +``` +$ notary delegation remove docker.io// targets/releases + +Are you sure you want to remove all data for this delegation? (yes/no) +yes + +Forced removal (including all keys and paths) of delegation role targets/releases to repository "docker.io//" staged for next publish. + +$ notary publish docker.io// +``` + +## Pushing trusted data as a collaborator + +As a collaborator with a private key that has been added to a repository's +`targets/releases` delegation, you need to import the private key that you +generated into Content Trust. + +To do so, you can run: + +``` +$ notary key import delegation.key --role user +``` + +where `delegation.key` is the file containing your PEM-encoded private key. + +After you have done so, running `docker push` on any repository that +includes your key in the `targets/releases` delegation will automatically sign +tags using this imported key. + +## `docker push` behavior + +When running `docker push` with Docker Content Trust, Docker Engine +will attempt to sign and push with the `targets/releases` delegation if it exists. +If it does not, the targets key will be used to sign the tag, if the key is available. + +## `docker pull` and `docker build` behavior + +When running `docker pull` or `docker build` with Docker Content Trust, Docker +Engine will pull tags only signed by the `targets/releases` delegation role or +the legacy tags that were signed directly with the `targets` key. + +## Related information + +* [Content trust in Docker](content_trust.md) +* [Manage keys for content trust](trust_key_mng.md) +* [Automation with content trust](trust_automation.md) +* [Play in a content trust sandbox](trust_sandbox.md) diff --git a/docs/security/trust/trust_key_mng.md b/docs/security/trust/trust_key_mng.md index 20676acf15..c15b84bacc 100644 --- a/docs/security/trust/trust_key_mng.md +++ b/docs/security/trust/trust_key_mng.md @@ -11,18 +11,34 @@ parent= "smn_content_trust" # Manage keys for content trust Trust for an image tag is managed through the use of keys. Docker's content -trust makes use four different keys: +trust makes use of five different types of keys: | Key | Description | |---------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| root key | Root of content trust for a image tag. When content trust is enabled, you create the root key once. | -| target and snapshot | These two keys are known together as the "repository" key. When content trust is enabled, you create this key when you add a new image repository. If you have the root key, you can export the repository key and allow other publishers to sign the image tags. | -| timestamp | This key applies to a repository. It allows Docker repositories to have freshness security guarantees without requiring periodic content refreshes on the client's side. | +| root key | Root of content trust for a image tag. When content trust is enabled, you create the root key once. Also known as the offline key, because it should be kept offline. | +| targets | This key allows you to sign image tags, to manage delegations including delegated keys or permitted delegation paths. Also known as the repository key, since this key determines what tags can be signed into an image repository. | +| snapshot | This key signs the current collection of image tags, preventing mix and match attacks. +| timestamp | This key allows Docker image repositories to have freshness security guarantees without requiring periodic content refreshes on the client's side. | +| delegation | Delegation keys are optional tagging keys and allow you to delegate signing image tags to other publishers without having to share your targets key. | -With the exception of the timestamp, all the keys are generated and stored locally -client-side. The timestamp is safely generated and stored in a signing server that -is deployed alongside the Docker registry. All keys are generated in a backend -service that isn't directly exposed to the internet and are encrypted at rest. +When doing a `docker push` with Content Trust enabled for the first time, the +root, targets, snapshot, and timestamp keys are generated automatically for +the image repository: + +- The root and targets key are generated and stored locally client-side. + +- The timestamp and snapshot keys are safely generated and stored in a signing server + that is deployed alongside the Docker registry. These keys are generated in a backend + service that isn't directly exposed to the internet and are encrypted at rest. + +Delegation keys are optional, and not generated as part of the normal `docker` +workflow. They need to be +[manually generated and added to the repository](trust_delegation.md#generating-delegation-keys). + +Note: Prior to Docker Engine 1.11, the snapshot key was also generated and stored +locally client-side. [Use the Notary CLI to manage your snapshot key locally +again](https://docs.docker.com/notary/advanced_usage/#rotate-keys) for +repositories created with newer versions of Docker. ## Choosing a passphrase @@ -68,6 +84,7 @@ the new key. ## Related information -* [Content trust in Docker](content_trust.md) +* [Content trust in Docker](content_trust.md) * [Automation with content trust](trust_automation.md) +* [Delegations for content trust](trust_delegation.md) * [Play in a content trust sandbox](trust_sandbox.md) diff --git a/integration-cli/check_test.go b/integration-cli/check_test.go index 6ce801bc1b..aab8e4a786 100644 --- a/integration-cli/check_test.go +++ b/integration-cli/check_test.go @@ -2,8 +2,11 @@ package main import ( "fmt" + "os" + "path/filepath" "testing" + "github.com/docker/docker/cliconfig" "github.com/docker/docker/pkg/reexec" "github.com/go-check/check" ) @@ -206,5 +209,8 @@ func (s *DockerTrustSuite) TearDownTest(c *check.C) { if s.not != nil { s.not.Close() } + + // Remove trusted keys and metadata after test + os.RemoveAll(filepath.Join(cliconfig.ConfigDir(), "trust")) s.ds.TearDownTest(c) } diff --git a/integration-cli/docker_cli_build_test.go b/integration-cli/docker_cli_build_test.go index ad52222628..b11bfc1c78 100644 --- a/integration-cli/docker_cli_build_test.go +++ b/integration-cli/docker_cli_build_test.go @@ -5799,6 +5799,83 @@ func (s *DockerTrustSuite) TestBuildContextDirIsSymlink(c *check.C) { } } +func (s *DockerTrustSuite) TestTrustedBuildTagFromReleasesRole(c *check.C) { + testRequires(c, NotaryHosting) + + latestTag := s.setupTrustedImage(c, "trusted-build-releases-role") + repoName := strings.TrimSuffix(latestTag, ":latest") + + // Now create the releases role + s.notaryCreateDelegation(c, repoName, "targets/releases", s.not.keys[0].Public) + s.notaryImportKey(c, repoName, "targets/releases", s.not.keys[0].Private) + s.notaryPublish(c, repoName) + + // push a different tag to the releases role + otherTag := fmt.Sprintf("%s:other", repoName) + dockerCmd(c, "tag", "busybox", otherTag) + + pushCmd := exec.Command(dockerBinary, "push", otherTag) + s.trustedCmd(pushCmd) + out, _, err := runCommandWithOutput(pushCmd) + c.Assert(err, check.IsNil, check.Commentf("Trusted push failed: %s", out)) + s.assertTargetInRoles(c, repoName, "other", "targets/releases") + s.assertTargetNotInRoles(c, repoName, "other", "targets") + + out, status := dockerCmd(c, "rmi", otherTag) + c.Assert(status, check.Equals, 0, check.Commentf("docker rmi failed: %s", out)) + + dockerFile := fmt.Sprintf(` + FROM %s + RUN [] + `, otherTag) + + name := "testtrustedbuildreleasesrole" + + buildCmd := buildImageCmd(name, dockerFile, true) + s.trustedCmd(buildCmd) + out, _, err = runCommandWithOutput(buildCmd) + c.Assert(err, check.IsNil, check.Commentf("Trusted build failed: %s", out)) + c.Assert(out, checker.Contains, fmt.Sprintf("FROM %s@sha", repoName)) +} + +func (s *DockerTrustSuite) TestTrustedBuildTagIgnoresOtherDelegationRoles(c *check.C) { + testRequires(c, NotaryHosting) + + latestTag := s.setupTrustedImage(c, "trusted-build-releases-role") + repoName := strings.TrimSuffix(latestTag, ":latest") + + // Now create a non-releases delegation role + s.notaryCreateDelegation(c, repoName, "targets/other", s.not.keys[0].Public) + s.notaryImportKey(c, repoName, "targets/other", s.not.keys[0].Private) + s.notaryPublish(c, repoName) + + // push a different tag to the other role + otherTag := fmt.Sprintf("%s:other", repoName) + dockerCmd(c, "tag", "busybox", otherTag) + + pushCmd := exec.Command(dockerBinary, "push", otherTag) + s.trustedCmd(pushCmd) + out, _, err := runCommandWithOutput(pushCmd) + c.Assert(err, check.IsNil, check.Commentf("Trusted push failed: %s", out)) + s.assertTargetInRoles(c, repoName, "other", "targets/other") + s.assertTargetNotInRoles(c, repoName, "other", "targets") + + out, status := dockerCmd(c, "rmi", otherTag) + c.Assert(status, check.Equals, 0, check.Commentf("docker rmi failed: %s", out)) + + dockerFile := fmt.Sprintf(` + FROM %s + RUN [] + `, otherTag) + + name := "testtrustedbuildotherrole" + + buildCmd := buildImageCmd(name, dockerFile, true) + s.trustedCmd(buildCmd) + out, _, err = runCommandWithOutput(buildCmd) + c.Assert(err, check.NotNil, check.Commentf("Trusted build expected to fail: %s", out)) +} + // Issue #15634: COPY fails when path starts with "null" func (s *DockerSuite) TestBuildNullStringInAddCopyVolume(c *check.C) { name := "testbuildnullstringinaddcopyvolume" diff --git a/integration-cli/docker_cli_pull_trusted_test.go b/integration-cli/docker_cli_pull_trusted_test.go index fdaaaa1a82..6bc38e699f 100644 --- a/integration-cli/docker_cli_pull_trusted_test.go +++ b/integration-cli/docker_cli_pull_trusted_test.go @@ -254,3 +254,112 @@ func (s *DockerTrustSuite) TestTrustedPullDelete(c *check.C) { _, err = inspectFieldWithError(imageID, "Id") c.Assert(err, checker.NotNil, check.Commentf("image should have been deleted")) } + +func (s *DockerTrustSuite) TestTrustedPullReadsFromReleasesRole(c *check.C) { + testRequires(c, NotaryHosting) + repoName := fmt.Sprintf("%v/dockerclireleasesdelegationpulling/trusted", privateRegistryURL) + targetName := fmt.Sprintf("%s:latest", repoName) + + // Push with targets first, initializing the repo + dockerCmd(c, "tag", "busybox", targetName) + pushCmd := exec.Command(dockerBinary, "push", targetName) + s.trustedCmd(pushCmd) + out, _, err := runCommandWithOutput(pushCmd) + c.Assert(err, check.IsNil, check.Commentf(out)) + s.assertTargetInRoles(c, repoName, "latest", "targets") + + // Try pull, check we retrieve from targets role + pullCmd := exec.Command(dockerBinary, "-D", "pull", repoName) + s.trustedCmd(pullCmd) + out, _, err = runCommandWithOutput(pullCmd) + c.Assert(err, check.IsNil, check.Commentf(out)) + c.Assert(out, checker.Contains, "retrieving target for targets role") + + // Now we'll create the releases role, and try pushing and pulling + s.notaryCreateDelegation(c, repoName, "targets/releases", s.not.keys[0].Public) + s.notaryImportKey(c, repoName, "targets/releases", s.not.keys[0].Private) + s.notaryPublish(c, repoName) + + // try a pull, check that we can still pull because we can still read the + // old tag in the targets role + pullCmd = exec.Command(dockerBinary, "-D", "pull", repoName) + s.trustedCmd(pullCmd) + out, _, err = runCommandWithOutput(pullCmd) + c.Assert(err, check.IsNil, check.Commentf(out)) + c.Assert(out, checker.Contains, "retrieving target for targets role") + + // try a pull -a, check that it succeeds because we can still pull from the + // targets role + pullCmd = exec.Command(dockerBinary, "-D", "pull", "-a", repoName) + s.trustedCmd(pullCmd) + out, _, err = runCommandWithOutput(pullCmd) + c.Assert(err, check.IsNil, check.Commentf(out)) + + // Push, should sign with targets/releases + dockerCmd(c, "tag", "busybox", targetName) + pushCmd = exec.Command(dockerBinary, "push", targetName) + s.trustedCmd(pushCmd) + out, _, err = runCommandWithOutput(pushCmd) + s.assertTargetInRoles(c, repoName, "latest", "targets", "targets/releases") + + // Try pull, check we retrieve from targets/releases role + pullCmd = exec.Command(dockerBinary, "-D", "pull", repoName) + s.trustedCmd(pullCmd) + out, _, err = runCommandWithOutput(pullCmd) + c.Assert(out, checker.Contains, "retrieving target for targets/releases role") + + // Create another delegation that we'll sign with + s.notaryCreateDelegation(c, repoName, "targets/other", s.not.keys[1].Public) + s.notaryImportKey(c, repoName, "targets/other", s.not.keys[1].Private) + s.notaryPublish(c, repoName) + + dockerCmd(c, "tag", "busybox", targetName) + pushCmd = exec.Command(dockerBinary, "push", targetName) + s.trustedCmd(pushCmd) + out, _, err = runCommandWithOutput(pushCmd) + s.assertTargetInRoles(c, repoName, "latest", "targets", "targets/releases", "targets/other") + + // Try pull, check we retrieve from targets/releases role + pullCmd = exec.Command(dockerBinary, "-D", "pull", repoName) + s.trustedCmd(pullCmd) + out, _, err = runCommandWithOutput(pullCmd) + c.Assert(out, checker.Contains, "retrieving target for targets/releases role") +} + +func (s *DockerTrustSuite) TestTrustedPullIgnoresOtherDelegationRoles(c *check.C) { + testRequires(c, NotaryHosting) + repoName := fmt.Sprintf("%v/dockerclipullotherdelegation/trusted", privateRegistryURL) + targetName := fmt.Sprintf("%s:latest", repoName) + + // We'll create a repo first with a non-release delegation role, so that when we + // push we'll sign it into the delegation role + s.notaryInitRepo(c, repoName) + s.notaryCreateDelegation(c, repoName, "targets/other", s.not.keys[0].Public) + s.notaryImportKey(c, repoName, "targets/other", s.not.keys[0].Private) + s.notaryPublish(c, repoName) + + // Push should write to the delegation role, not targets + dockerCmd(c, "tag", "busybox", targetName) + pushCmd := exec.Command(dockerBinary, "push", targetName) + s.trustedCmd(pushCmd) + out, _, err := runCommandWithOutput(pushCmd) + c.Assert(err, check.IsNil, check.Commentf(out)) + s.assertTargetInRoles(c, repoName, "latest", "targets/other") + s.assertTargetNotInRoles(c, repoName, "latest", "targets") + + // Try pull - we should fail, since pull will only pull from the targets/releases + // role or the targets role + pullCmd := exec.Command(dockerBinary, "-D", "pull", repoName) + s.trustedCmd(pullCmd) + out, _, err = runCommandWithOutput(pullCmd) + c.Assert(err, check.NotNil, check.Commentf(out)) + c.Assert(out, checker.Contains, "No trust data for") + + // try a pull -a: we should fail since pull will only pull from the targets/releases + // role or the targets role + pullCmd = exec.Command(dockerBinary, "-D", "pull", "-a", repoName) + s.trustedCmd(pullCmd) + out, _, err = runCommandWithOutput(pullCmd) + c.Assert(err, check.NotNil, check.Commentf(out)) + c.Assert(out, checker.Contains, "No trusted tags for") +} diff --git a/integration-cli/docker_cli_push_test.go b/integration-cli/docker_cli_push_test.go index f45531206d..6459cc031a 100644 --- a/integration-cli/docker_cli_push_test.go +++ b/integration-cli/docker_cli_push_test.go @@ -497,37 +497,143 @@ func (s *DockerTrustSuite) TestTrustedPushWithExpiredTimestamp(c *check.C) { }) } -func (s *DockerTrustSuite) TestTrustedPushWithReleasesDelegation(c *check.C) { +func (s *DockerTrustSuite) TestTrustedPushWithReleasesDelegationOnly(c *check.C) { testRequires(c, NotaryHosting) - repoName := fmt.Sprintf("%v/dockerclireleasedelegation/trusted", privateRegistryURL) + repoName := fmt.Sprintf("%v/dockerclireleasedelegationinitfirst/trusted", privateRegistryURL) targetName := fmt.Sprintf("%s:latest", repoName) - pwd := "12345678" - s.setupDelegations(c, repoName, pwd) + s.notaryInitRepo(c, repoName) + s.notaryCreateDelegation(c, repoName, "targets/releases", s.not.keys[0].Public) + s.notaryPublish(c, repoName) + + s.notaryImportKey(c, repoName, "targets/releases", s.not.keys[0].Private) // tag the image and upload it to the private registry dockerCmd(c, "tag", "busybox", targetName) - pushCmd := exec.Command(dockerBinary, "-D", "push", targetName) - s.trustedCmdWithPassphrases(pushCmd, pwd, pwd) + pushCmd := exec.Command(dockerBinary, "push", targetName) + s.trustedCmd(pushCmd) out, _, err := runCommandWithOutput(pushCmd) c.Assert(err, check.IsNil, check.Commentf("trusted push failed: %s\n%s", err, out)) c.Assert(out, checker.Contains, "Signing and pushing trust metadata", check.Commentf("Missing expected output on trusted push with existing tag")) + // check to make sure that the target has been added to targets/releases and not targets + s.assertTargetInRoles(c, repoName, "latest", "targets/releases") + s.assertTargetNotInRoles(c, repoName, "latest", "targets") // Try pull after push + os.RemoveAll(filepath.Join(cliconfig.ConfigDir(), "trust")) + pullCmd := exec.Command(dockerBinary, "pull", targetName) s.trustedCmd(pullCmd) out, _, err = runCommandWithOutput(pullCmd) c.Assert(err, check.IsNil, check.Commentf(out)) c.Assert(string(out), checker.Contains, "Status: Downloaded", check.Commentf(out)) +} - // check to make sure that the target has been added to targets/releases and not targets - contents, err := ioutil.ReadFile(filepath.Join(cliconfig.ConfigDir(), "trust/tuf", repoName, "metadata/targets.json")) - c.Assert(err, check.IsNil, check.Commentf("Unable to read targets metadata")) - c.Assert(strings.Contains(string(contents), `"latest"`), checker.False, check.Commentf(string(contents))) +func (s *DockerTrustSuite) TestTrustedPushSignsAllFirstLevelRolesWeHaveKeysFor(c *check.C) { + testRequires(c, NotaryHosting) + repoName := fmt.Sprintf("%v/dockerclimanyroles/trusted", privateRegistryURL) + targetName := fmt.Sprintf("%s:latest", repoName) + s.notaryInitRepo(c, repoName) + s.notaryCreateDelegation(c, repoName, "targets/role1", s.not.keys[0].Public) + s.notaryCreateDelegation(c, repoName, "targets/role2", s.not.keys[1].Public) + s.notaryCreateDelegation(c, repoName, "targets/role3", s.not.keys[2].Public) - contents, err = ioutil.ReadFile(filepath.Join(cliconfig.ConfigDir(), "trust/tuf", repoName, "metadata/targets/releases.json")) - c.Assert(err, check.IsNil, check.Commentf("Unable to read targets/releases metadata")) - c.Assert(string(contents), checker.Contains, `"latest"`, check.Commentf(string(contents))) + // import everything except the third key + s.notaryImportKey(c, repoName, "targets/role1", s.not.keys[0].Private) + s.notaryImportKey(c, repoName, "targets/role2", s.not.keys[1].Private) + + s.notaryCreateDelegation(c, repoName, "targets/role1/subrole", s.not.keys[3].Public) + s.notaryImportKey(c, repoName, "targets/role1/subrole", s.not.keys[3].Private) + + s.notaryPublish(c, repoName) + + // tag the image and upload it to the private registry + dockerCmd(c, "tag", "busybox", targetName) + + pushCmd := exec.Command(dockerBinary, "push", targetName) + s.trustedCmd(pushCmd) + out, _, err := runCommandWithOutput(pushCmd) + c.Assert(err, check.IsNil, check.Commentf("trusted push failed: %s\n%s", err, out)) + c.Assert(out, checker.Contains, "Signing and pushing trust metadata", check.Commentf("Missing expected output on trusted push with existing tag")) + + // check to make sure that the target has been added to targets/role1 and targets/role2, and + // not targets (because there are delegations) or targets/role3 (due to missing key) or + // targets/role1/subrole (due to it being a second level delegation) + s.assertTargetInRoles(c, repoName, "latest", "targets/role1", "targets/role2") + s.assertTargetNotInRoles(c, repoName, "latest", "targets") + + // Try pull after push + os.RemoveAll(filepath.Join(cliconfig.ConfigDir(), "trust")) + + // pull should fail because none of these are the releases role + pullCmd := exec.Command(dockerBinary, "pull", targetName) + s.trustedCmd(pullCmd) + out, _, err = runCommandWithOutput(pullCmd) + c.Assert(err, check.NotNil, check.Commentf(out)) +} + +func (s *DockerTrustSuite) TestTrustedPushSignsForRolesWithKeysAndValidPaths(c *check.C) { + repoName := fmt.Sprintf("%v/dockerclirolesbykeysandpaths/trusted", privateRegistryURL) + targetName := fmt.Sprintf("%s:latest", repoName) + s.notaryInitRepo(c, repoName) + s.notaryCreateDelegation(c, repoName, "targets/role1", s.not.keys[0].Public, "l", "z") + s.notaryCreateDelegation(c, repoName, "targets/role2", s.not.keys[1].Public, "x", "y") + s.notaryCreateDelegation(c, repoName, "targets/role3", s.not.keys[2].Public, "latest") + s.notaryCreateDelegation(c, repoName, "targets/role4", s.not.keys[3].Public, "latest") + + // import everything except the third key + s.notaryImportKey(c, repoName, "targets/role1", s.not.keys[0].Private) + s.notaryImportKey(c, repoName, "targets/role2", s.not.keys[1].Private) + s.notaryImportKey(c, repoName, "targets/role4", s.not.keys[3].Private) + + s.notaryPublish(c, repoName) + + // tag the image and upload it to the private registry + dockerCmd(c, "tag", "busybox", targetName) + + pushCmd := exec.Command(dockerBinary, "push", targetName) + s.trustedCmd(pushCmd) + out, _, err := runCommandWithOutput(pushCmd) + c.Assert(err, check.IsNil, check.Commentf("trusted push failed: %s\n%s", err, out)) + c.Assert(out, checker.Contains, "Signing and pushing trust metadata", check.Commentf("Missing expected output on trusted push with existing tag")) + + // check to make sure that the target has been added to targets/role1 and targets/role4, and + // not targets (because there are delegations) or targets/role2 (due to path restrictions) or + // targets/role3 (due to missing key) + s.assertTargetInRoles(c, repoName, "latest", "targets/role1", "targets/role4") + s.assertTargetNotInRoles(c, repoName, "latest", "targets") + + // Try pull after push + os.RemoveAll(filepath.Join(cliconfig.ConfigDir(), "trust")) + + // pull should fail because none of these are the releases role + pullCmd := exec.Command(dockerBinary, "pull", targetName) + s.trustedCmd(pullCmd) + out, _, err = runCommandWithOutput(pullCmd) + c.Assert(err, check.NotNil, check.Commentf(out)) +} + +func (s *DockerTrustSuite) TestTrustedPushDoesntSignTargetsIfDelegationsExist(c *check.C) { + testRequires(c, NotaryHosting) + repoName := fmt.Sprintf("%v/dockerclireleasedelegationnotsignable/trusted", privateRegistryURL) + targetName := fmt.Sprintf("%s:latest", repoName) + s.notaryInitRepo(c, repoName) + s.notaryCreateDelegation(c, repoName, "targets/role1", s.not.keys[0].Public) + s.notaryPublish(c, repoName) + + // do not import any delegations key + + // tag the image and upload it to the private registry + dockerCmd(c, "tag", "busybox", targetName) + + pushCmd := exec.Command(dockerBinary, "push", targetName) + s.trustedCmd(pushCmd) + out, _, err := runCommandWithOutput(pushCmd) + c.Assert(err, check.NotNil, check.Commentf("trusted push succeeded but should have failed:\n%s", out)) + c.Assert(out, checker.Contains, "no valid signing keys", + check.Commentf("Missing expected output on trusted push without keys")) + + s.assertTargetNotInRoles(c, repoName, "latest", "targets", "targets/role1") } func (s *DockerRegistryAuthHtpasswdSuite) TestPushNoCredentialsNoRetry(c *check.C) { diff --git a/integration-cli/fixtures/notary/delgkey1.crt b/integration-cli/fixtures/notary/delgkey1.crt new file mode 100644 index 0000000000..306eeec9a2 --- /dev/null +++ b/integration-cli/fixtures/notary/delgkey1.crt @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIID8jCCAtqgAwIBAgIJAJkxr+7rAgXbMA0GCSqGSIb3DQEBBQUAMFgxCzAJBgNV +BAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEPMA0G +A1UEChMGRG9ja2VyMRMwEQYDVQQDEwpkZWxlZ2F0aW9uMCAXDTE2MDMwODAyNDEy +MFoYDzIxMTYwMjEzMDI0MTIwWjBYMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0Ex +FjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xDzANBgNVBAoTBkRvY2tlcjETMBEGA1UE +AxMKZGVsZWdhdGlvbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJuz +To1qoL/RY5pNxdPkP/jiO3f/RTvz20C90EweaKgRdIV/vTUUE+mMRQulpf1vpCP9 +uidGfEoJcq4jM1H59XTYUoUvGbAMP3Iu7Uz0rF5v+Glm82Z0WGI+PkOnwRN2bJi4 +LhAch6QlA/48IOFH/O9jnHYMb45lQFpm+gOvatRyGkPZCftD3ntkhVMk1OJ7EZC4 +LYiwzmuPEYusO/qVgcHkGtIxLWAjGmDzrV3Q5orPVwwUOxNQdRRU1L2bhfUsodcb +Fgi/LCz4xnGx4YpF0O24Y7/0SPotSyaT0RYyj/j/bIKvYB20g4P7469klde1Ariz +UEIf12PlaJ/H/PaIlEcCAwEAAaOBvDCBuTAdBgNVHQ4EFgQUXZK4ZGswIq54W4VZ +OJY7zXvvndwwgYkGA1UdIwSBgTB/gBRdkrhkazAirnhbhVk4ljvNe++d3KFcpFow +WDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1TYW4gRnJhbmNp +c2NvMQ8wDQYDVQQKEwZEb2NrZXIxEzARBgNVBAMTCmRlbGVnYXRpb26CCQCZMa/u +6wIF2zAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQA2ktnjxB2lXF+g +lTc2qp9LhjofgCWKwLgWEeCwXI2nUBNE4n00hA9or2wer2viWC4IJnG0kTyMzzYT +m1lBpZ8BP6S3sSkvhohaqS+gBIUVB7U65tAof/SY2UHpeVJ1YpTE4F1GAUfqSY7V +6IGHZAGiLeUS5kC6pzZA4siBhyCoYKRKEb9R82jSCHeFYS3ntwY1/gqcO/uIidVE +2hLHlx6vBx9BEfXv31AGLoB3YocSTZLATwlrDHUQG1+oNh5ejQU1x/z+Y62EG5Jb +u0yLDdJeSgup/DzPEoNpSihtdQZytKMK+KBmh22gDA5h+a6620zTZwCvJYxH9kkM +IClUWwuD +-----END CERTIFICATE----- diff --git a/integration-cli/fixtures/notary/delgkey1.key b/integration-cli/fixtures/notary/delgkey1.key new file mode 100644 index 0000000000..a0d0a30e6a --- /dev/null +++ b/integration-cli/fixtures/notary/delgkey1.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAm7NOjWqgv9Fjmk3F0+Q/+OI7d/9FO/PbQL3QTB5oqBF0hX+9 +NRQT6YxFC6Wl/W+kI/26J0Z8SglyriMzUfn1dNhShS8ZsAw/ci7tTPSsXm/4aWbz +ZnRYYj4+Q6fBE3ZsmLguEByHpCUD/jwg4Uf872OcdgxvjmVAWmb6A69q1HIaQ9kJ ++0Pee2SFUyTU4nsRkLgtiLDOa48Ri6w7+pWBweQa0jEtYCMaYPOtXdDmis9XDBQ7 +E1B1FFTUvZuF9Syh1xsWCL8sLPjGcbHhikXQ7bhjv/RI+i1LJpPRFjKP+P9sgq9g +HbSDg/vjr2SV17UCuLNQQh/XY+Von8f89oiURwIDAQABAoIBAB7DhfDRMaPU5n41 +gbIFNlKhuKhUCsT2wMqA9qgjlgAnOsOp4qObLPgHXBkaCLsTlPX7iw15ktM6HKul +jt1SqxoEKAHitYugT+Tqur5q1afvLcD9s3f54wC+VaUefzquOnTOZ2ONj4tyOODB +1qlMhQBzyRVWDbCv9tAl6p5RyaTh+8IULctlER6w9m3upT9NxoRi1PrPBCRiEKKo +4zDRvfbT/0ucLD20GS6trPv4ihTCTU7ydFujioDkFyNzCzYNGBnImpQ9/xeT5/Ys +IJQy9Tdn6V0rXMBBb1EhyBQYw5Oxy6d6tzhjvva6LaJBGo9yzX0NHt58Ymhgm1q/ +vscj1pECgYEAyegQFP7dkmUdXdNpdrIdCvKlvni3r/hwB/9H0sJHIJbfTusfzeLL +5Q8QSZAsaR7tSgJfr9GMdOjntvefYjKLfl3SnG/wF91m05eYfkeiZXc9RGe+XXGu +wv5u2m/G7a05XpW1JFX+1ORyj2x5KsvF7KDtWJyR5ryIsOwHZNGQpJ8CgYEAxWoo +r2eJBc9Xj5bhhS0VxUFODXImfeQF2aG2rSeuWMY7k4vmVkJwhBZiPW/dHBu1aMPh +/SY1W7cgzdVIf2RIF5MgzzkmoisEApZTiSwmP6A2bTx6miXwFCLTCHIDfiXJ0tQA +Nb+Ln+exks4BfCgKHOqWTcWizKNE/8Gb6SnhB1kCgYAgM1Z9QrhrpJyuXg0v1PA0 +0sYEPpRtCB416Ey4HCvj0qwClhUYbNc/zMs4MDok+b22U/KWw8C21H4/+/X7XzxI +BwaT1HZiF/lSPZcgbKRFsmKfCjyeAodwqctcIv+C4GGJ6C5fgSeHJHfwz8fzP1Rt +jKzNuQq71c2nCb2UIqgC2QKBgEieoJDFmVYVy7P6YMNIrnV9bGTt1NMCilRgdH6F +1lC5uzivge/BSPqN8V2AROoOF1GOnRcucvpmBx8wkhaqoQprCOqxr1CAWl1JRzly +kC9flCXi1YbW5dXCabb1metRo0h2zAz5hTcxV9UVCt7NK8svUFMTnKuCc+NRKTVA +PpMhAoGBAJ9rFgZpWHRVuzsangbGslq3fDYGENLJ2fPNjLgfgVLi+YotG9jfgQPW +QCvoSA1CChxzEJEB5hzEOEv9pThnBNg1LWNj+a3N5anW2UBHMEWeCrVFZwJMVdSd +srUFtap7da8iUddc+sHC5hHHFDBdqG4pDck/uTs3CNWRF/ZqzE/G +-----END RSA PRIVATE KEY----- diff --git a/integration-cli/fixtures/notary/delgkey2.crt b/integration-cli/fixtures/notary/delgkey2.crt new file mode 100644 index 0000000000..40f2db2cc1 --- /dev/null +++ b/integration-cli/fixtures/notary/delgkey2.crt @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIID8jCCAtqgAwIBAgIJAMi/AxlwFquJMA0GCSqGSIb3DQEBBQUAMFgxCzAJBgNV +BAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEPMA0G +A1UEChMGRG9ja2VyMRMwEQYDVQQDEwpkZWxlZ2F0aW9uMCAXDTE2MDMwODAyNDEy +MloYDzIxMTYwMjEzMDI0MTIyWjBYMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0Ex +FjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xDzANBgNVBAoTBkRvY2tlcjETMBEGA1UE +AxMKZGVsZWdhdGlvbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL/a +1GO+02jt1p0sME+YGaK4+uZ9jezrpkCXKMsMfItgqCKRTX7YVuR7tnRt/Y1DNVqR +nMeGc77soDag6eW4xrYrv9LwylUsOLanvK1d/8hDxZhzJjqlJBmz6BvLWDZUF9uu +OjULL8yuP2cmRogjn0bqmdeKztrZtDQqQiwsG02nVjfuvVi3rP4G4DhL5fUoHB0R +E6L9Su3/2OWGpdxZqkT7GAbjgLl4/4CXs00493m8xZIHXQ9559PiVlLfk6p6FjEV +7irZp7XXSe1My/0HGebFXkYqEL9+My2od4w+qJmBT23aTduGTo8IZC7g9lwKEykA +hWrYhR5tjkLvOsQIE7ECAwEAAaOBvDCBuTAdBgNVHQ4EFgQUHtEAVcwI3k7W5B6c +L3w+eKQRsIYwgYkGA1UdIwSBgTB/gBQe0QBVzAjeTtbkHpwvfD54pBGwhqFcpFow +WDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1TYW4gRnJhbmNp +c2NvMQ8wDQYDVQQKEwZEb2NrZXIxEzARBgNVBAMTCmRlbGVnYXRpb26CCQDIvwMZ +cBariTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAfjsMtZ+nJ7y5t +rH9xPwWMLmtC5MwzDRvTUAGbNbFwwm8AncfvsDmmERqsr8L2qhY8CZ9vsN4NjjBn +QRmM/ynYA8JTbf/5ZNDnD4D6qTXLgGFqyHcBaorcB9uQ8eiMOFAbhxLYfPrKaYdV +qj+MejcFa3HmzmYCSqsvxRhSje5b4sORe9/3jNheXsX8VZUpWtCHc3k4GiCU6KyS +gpnXkShU4sG92cK72L8pxmGTz8ynNMj/9WKkLxpNIv5u0/D01a3z4wx5k1zfRZiz +IQS+xqxV/ztY844MDknxENlYzcqGj0Fd6hE5OKZxnGaH83A5adldMLlnhG1rscGP +as9uwPYP +-----END CERTIFICATE----- diff --git a/integration-cli/fixtures/notary/delgkey2.key b/integration-cli/fixtures/notary/delgkey2.key new file mode 100644 index 0000000000..59e854786e --- /dev/null +++ b/integration-cli/fixtures/notary/delgkey2.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAv9rUY77TaO3WnSwwT5gZorj65n2N7OumQJcoywx8i2CoIpFN +fthW5Hu2dG39jUM1WpGcx4ZzvuygNqDp5bjGtiu/0vDKVSw4tqe8rV3/yEPFmHMm +OqUkGbPoG8tYNlQX2646NQsvzK4/ZyZGiCOfRuqZ14rO2tm0NCpCLCwbTadWN+69 +WLes/gbgOEvl9SgcHRETov1K7f/Y5Yal3FmqRPsYBuOAuXj/gJezTTj3ebzFkgdd +D3nn0+JWUt+TqnoWMRXuKtmntddJ7UzL/QcZ5sVeRioQv34zLah3jD6omYFPbdpN +24ZOjwhkLuD2XAoTKQCFatiFHm2OQu86xAgTsQIDAQABAoIBAQCDdASic1WXR58N +AgH4B1dJT0VaOBzOgIfhKbEhruSG+ys4xCY9Cy4+TyWskNBxweMEs1CgxVb5Mlh0 +Fb0tUXWVzFQazDFWOn6BaFy2zPk81nLFCeDfvdcGZWZb5PAECYpvUuk+/vM5Ywq+ +OlOJZB72EDhonwssmI4IUAwXCAGNKjLfC4L+3ZgA3+I1xgxisJ2XWNYSLwHzIDRh +U3zO2NpJi1edTNPltDBTb4iFhajX0SFgbARc+XVTpA3pgQujWo6CNB5YKCPuzIqr +GFsvGSZDVzOUnfOlitaYNW+QIWAQf8VLWULwyFrS5Cb2WR/k7AmojZVuDHvzWrtg +ZMG6b1mBAoGBAOV+3SiX8+khKBpxnOJLq0XlGjFNDWNNB34UIMVkFehRxpUr2261 +HDp4YiC9n7l47sjeFBk4IQf2vG/5MPpuqIixsk2W3siCASdMQypVZMG+zj6xDFfH +8rwQSeZhwjmk2a+A7qgnhqvd/qa7EYOnsn1tLf2iBB2EaHV9lWBJFX0lAoGBANYD +GbAPEiwh4Fns8pf59T3Lp0Q9XvAN3uh4DUU0mFrQ1HQHeXIw1IDCJ9JiRjLX7aHu +79EtDssVPQ9dv0MN5rRULtrutCfRLsomm385PLLBIgBdVApnVvJJIWhQkFFMrhFt +UP+483utiDOcCVXMxAy+1jx23EiWvl2H0xGIwsSdAoGBAMIcM+OJ4vxk1w7G2fNu +HUfZJ/ZbPd+n35Z8X9uVdBI0WMsDdW6GMYIjIJygxuCRsSak8EsEdqvNvkTXeN3Z +iyNTaYTG/1iI3YDnuEeuQrK9OKU+CzqUHHOFM3xxY15uWNFhNHt2MypbcnCD+aRp +y0bbefL1fpWY0OHPfvEZ39shAoGAPbVdJc/irIkEGMni1YGEflIHo/ySMGO/f4aG +RQs6Vw1aBS7WjN+ZlprlQpuFpElwwr2TttvoJRS1q4WbjakneZ3AeO5VUhnWBQIG +2jNV1jEsLbC7d9h+UJRXpq18P4T9uBauQV5CDspluIPoiS3m5cntGjgnomKc93kf +mjG1/10CgYA7kgOOva64sjWakL/IgDRiwr0YrJkAfPUZYwxYLHuiW9izUgngpqWd +1wtq+YCsc4l7t8u9Tahb8OE0KSN5RC6QM6b8yW9qFDZ68QAX00+sN6di4qyAZlm+ +rK05W/3JmyvQbvO+JVRQtegZ1ExCj7LGuGOQ5KIpWsBEM3ic9ZP9gw== +-----END RSA PRIVATE KEY----- diff --git a/integration-cli/fixtures/notary/delgkey3.crt b/integration-cli/fixtures/notary/delgkey3.crt new file mode 100644 index 0000000000..be34eab5c0 --- /dev/null +++ b/integration-cli/fixtures/notary/delgkey3.crt @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIID8jCCAtqgAwIBAgIJAI3uONxeFQJtMA0GCSqGSIb3DQEBBQUAMFgxCzAJBgNV +BAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEPMA0G +A1UEChMGRG9ja2VyMRMwEQYDVQQDEwpkZWxlZ2F0aW9uMCAXDTE2MDMwODAyNDEy +NFoYDzIxMTYwMjEzMDI0MTI0WjBYMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0Ex +FjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xDzANBgNVBAoTBkRvY2tlcjETMBEGA1UE +AxMKZGVsZWdhdGlvbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOii +Ij01MkSExgurs5owUNxNgRvrZFxNSNGfnscYZiaT/wNcocrOz40vvY29SOBEbCSW +oBlCi0rYu/7LZBqvsP3YItmifpJHGfRiZ6xEQ4rKznY8+8E3FHVChlmVv9x6QPhA +9OpATlSLvcdiXHbohdc+kQsl9qM93+QadRQLmtZ6H5Sv90d1MHNViX+8d/k2WyT0 +8u6fNv0ZHeltnZFYruF82YKJCOPdAJnCLUOXWRSG6xDhhvSewjxz6gFla5n8m+D9 +jvmIUUjoMEhjORUIVeA/lXT0AT3Lx0xE8uyhJQbp+hGtcPCcwYFZdz3yLcrxKO47 +nh6qOygf7I2fiR1ogqECAwEAAaOBvDCBuTAdBgNVHQ4EFgQUUqsFJdVoos2aewDh +m1r66zyXeI4wgYkGA1UdIwSBgTB/gBRSqwUl1WiizZp7AOGbWvrrPJd4jqFcpFow +WDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1TYW4gRnJhbmNp +c2NvMQ8wDQYDVQQKEwZEb2NrZXIxEzARBgNVBAMTCmRlbGVnYXRpb26CCQCN7jjc +XhUCbTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQADcyno8/AwNatf +pjgILCZl1DVrqaKEVbp6ciNgVRCF4cM0bE5W4mjd5tO8d3/yTilry2FPicqxiMps +oGroMFR+X1cZbOf0U32FyEW4EyWm2jjbiuEpnM5J/EeB/QfckqP6whS/QAM7PxDV +Sxd8sKDb9SOGZiickFU4QpG1fdmY/knrrtbzRl7Nk/3tBgRaq+Brg7YNZZKlpUNB +Hp3q0E+MFgVAojpcL7w1oSgoNev+cUNaBdPEmWIEi7F5rosCzmAIhuIY+ghmo9Qg +zy+byAcxLpujl8vZvE1nZKMKZ7oJayOOgjB2Ztk6bO1r+GPtK5VfqEPhKTRDbBlo +xS3tSCDJ +-----END CERTIFICATE----- diff --git a/integration-cli/fixtures/notary/delgkey3.key b/integration-cli/fixtures/notary/delgkey3.key new file mode 100644 index 0000000000..4790c95716 --- /dev/null +++ b/integration-cli/fixtures/notary/delgkey3.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEA6KIiPTUyRITGC6uzmjBQ3E2BG+tkXE1I0Z+exxhmJpP/A1yh +ys7PjS+9jb1I4ERsJJagGUKLSti7/stkGq+w/dgi2aJ+kkcZ9GJnrERDisrOdjz7 +wTcUdUKGWZW/3HpA+ED06kBOVIu9x2JcduiF1z6RCyX2oz3f5Bp1FAua1noflK/3 +R3Uwc1WJf7x3+TZbJPTy7p82/Rkd6W2dkViu4XzZgokI490AmcItQ5dZFIbrEOGG +9J7CPHPqAWVrmfyb4P2O+YhRSOgwSGM5FQhV4D+VdPQBPcvHTETy7KElBun6Ea1w +8JzBgVl3PfItyvEo7jueHqo7KB/sjZ+JHWiCoQIDAQABAoIBADvh8HpdBTGKFAjR +DAx2v3nWIZP0RgNUiZgcRJzvdOwdUJmm8KbqEZdAYMTpoqbINiY8971I2d5MaCgw +ZvZPn3nYdzAamgZBczbrVdCMSe6iQf9Bt3SHHycIFtlcqOSyO6Mr5V+fagptZk66 +zR52wG0l1+RMw25F8SogfV7JlfP7Qh5Bob0lEN2xpbhwLiNaaB+IHNe0FelmRvmJ +VUonoD0xaos25EXUES7J/9coiBqgRlDVHdUM0oaa/94UnxNPJnoNfte0yd+mC4LZ +JVHo0Zti3x/8SiCYMbLQs5L8AL8VtPu9OPfur/J8+9Rv0Rh+L1Ben+JWzCzUw1Cj +abH1zvkCgYEA9Q06Lu69ZLD31fTv46CphN+dGS/VgvMELkob6VQOhbV3RPhe6vqL +p7D67J53iq4rZY5KX3zuXZ+A5s48atc8gz+hTsrE022QVXmO2ZrE22bEpL+mwpsB +8//ul1UG51XTw6YR9CmLLD3Y4BgMjhSllx4Wwr9e9+PKl+DuSreqhxMCgYEA8wbf +P3zh85jPN92gBG8+VIbVlXYOTK0OllYUoLt4URmLRllmrB6LyRlpODCyb9+JymMg +WvAq5Bc0h8gMbSQEkYaAUq2CfSbyExASUHA+/nZglsTZhPkg5PJImntK6S58KAM7 +RJzyz20gxYA5H4KXFSiF+ONOE9X/cFUPxzF1AfsCgYBfgUY54GYEBkyxIIMWDhnD +ZXtOw6vNG3V3rP5v04jNZ8oSIVKs9fTT6FADREeGzxauv+QQjxo/dtjAG4TEhxpY +dMYjdTd8x2jHR1b7TCyI7eaZ5u/RTKRYOlj8tfC43GRqDiFVLZPGLFyIChdqkHVx +DhME15zls+vTgaCdkjNt7QKBgQCfwDywNx8wSZqtVnoBcD7AwYFUpi3wKTIVkLAu +mA0XAnuS2uGq8slgf9uynBAvifnBmDeEj6siFD7roozIkYyPPKLNtlC4hAlMjpv7 +VE2UZ6xGb0+tITaGSN2A7trnPS9P/g/PonvZ7hpEuWzTUbyOo/ytBn4ke99VsBSX +E+OeUQKBgQCgmcwCj2/IH+GOpe9qAG6MTMKK7k22O8fBCrcDybL1pMWIesJEbzpv +T5Atcx9L5ff6Q4Ysghb8ebXsErv4oZ72xyAwWJmbIaPllWn2ffUikzL3grSriWZy +0bz6P9sRqYpbdmX3oVvTfBP5kbv+mtDXOB3h5rGfczKWNMyuZmxDOg== +-----END RSA PRIVATE KEY----- diff --git a/integration-cli/fixtures/notary/delgkey4.crt b/integration-cli/fixtures/notary/delgkey4.crt new file mode 100644 index 0000000000..42869c8ef4 --- /dev/null +++ b/integration-cli/fixtures/notary/delgkey4.crt @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIID8jCCAtqgAwIBAgIJAKKDRMrryBRKMA0GCSqGSIb3DQEBBQUAMFgxCzAJBgNV +BAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEPMA0G +A1UEChMGRG9ja2VyMRMwEQYDVQQDEwpkZWxlZ2F0aW9uMCAXDTE2MDMwODAyNDEy +N1oYDzIxMTYwMjEzMDI0MTI3WjBYMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0Ex +FjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xDzANBgNVBAoTBkRvY2tlcjETMBEGA1UE +AxMKZGVsZWdhdGlvbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOCf +Wfff5mX/ko/Y790O04eR7h8/4YtZU3LFItcjhkphMf2V2BRlhWwwW6v96gTN1xsZ +1il6/YXjviWiLjhrtOVLQBE2yK0A7Wwdh9KJg3QgNqwtFrR1MA1LgWto1F7NyEMC +9H6Hc95+bgWx1jN0IflfPh1C1m/sA5xGqHDl+8YzJJUOoa5bh04Yk3aIeecatso/ +z7P5c6KicPcZIjhgjxHYB95It/oj8ZuY0hQZb7B5HEGNyBbT2F0vuElWtp+mXexr +6mzgzvHgaKG36bNCTLxr8BxGA/sbVn01LyI3wpk2uqWzyUFk21M4g2X46OPgKrh7 +2h5b+C0X8DUPi45djHcCAwEAAaOBvDCBuTAdBgNVHQ4EFgQUKcrfRFg+6o2l4xbt +Ll6hV9pjJh8wgYkGA1UdIwSBgTB/gBQpyt9EWD7qjaXjFu0uXqFX2mMmH6FcpFow +WDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1TYW4gRnJhbmNp +c2NvMQ8wDQYDVQQKEwZEb2NrZXIxEzARBgNVBAMTCmRlbGVnYXRpb26CCQCig0TK +68gUSjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAhdKgYUQ36JSPS +f3Dws09pM5hzSsSae4+nG9XckX6dVQ7sLKmjeeeLrXuzjfygir/+h9cHyShgXFH4 +ZbGpdzf6APG1KRag3/njqEWi+kKZZduxZKvI2EHJhj1xBtf8Qru0TgS7bHPlp9bl +1/61+aIrtj05LQhqzWzehuJFrmSdWP9cnNbvlPdOdgfgkKakAiLGwwGNvMQbqxaO +FIB4UPuPdQgm5bpimd5/CThKbpK9/0nr9K4po/m519nvEKxZzsDw5tefGp9Xqly3 +4pk9uyAxO/E2cL0cVA/WHTVTsHPbO7lXxBi6/EjiTUi0Nj1X+btO8+jCLkJyNY0m +qaiL5k9h +-----END CERTIFICATE----- diff --git a/integration-cli/fixtures/notary/delgkey4.key b/integration-cli/fixtures/notary/delgkey4.key new file mode 100644 index 0000000000..7573c2089e --- /dev/null +++ b/integration-cli/fixtures/notary/delgkey4.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEA4J9Z99/mZf+Sj9jv3Q7Th5HuHz/hi1lTcsUi1yOGSmEx/ZXY +FGWFbDBbq/3qBM3XGxnWKXr9heO+JaIuOGu05UtAETbIrQDtbB2H0omDdCA2rC0W +tHUwDUuBa2jUXs3IQwL0fodz3n5uBbHWM3Qh+V8+HULWb+wDnEaocOX7xjMklQ6h +rluHThiTdoh55xq2yj/Ps/lzoqJw9xkiOGCPEdgH3ki3+iPxm5jSFBlvsHkcQY3I +FtPYXS+4SVa2n6Zd7GvqbODO8eBoobfps0JMvGvwHEYD+xtWfTUvIjfCmTa6pbPJ +QWTbUziDZfjo4+AquHvaHlv4LRfwNQ+Ljl2MdwIDAQABAoIBAQCrN2wZsFZr2zK5 +aS/0/Y8poIe01Dm0wWMFcdULzm1XltzHIgfyDCx2ein2YPaXsNtNMhV3yuMiwqU3 +BHdc1GSv/vsX4/11Oea/6YaVafKEeuWRulC7PzRgffRpjh+LICqNQdxh8hfVOePd +fV/8GoKnFf0/yqmv6GQcJBPS8stGmFmjo4rkBGvBBMoiUtMYllQqdfH0DtpI24Jh +nR3lZKAPECkAciV7/Lx6+CUEaNOML2XPbLv6EyRh+J/r80jwE8myzpO7R6I+KCzo +R/xuBb/hrUh5Sd5YmuBMa6WfF9yqawTgmVvkpD9fkRusSPSQCq3oe+AugYWu6Fht +XBiZlvjJAoGBAPPBuUaagaUgHyjIzjbRPBHDhSYJpgYR4l/jcypfrl+m0OFC5acA +QG7Hr8AbStIPGtaJRn2pm8dNVPtFecPoi5jVWux2n5RqYlOnwY0tziuxbhU9GQ/W +oCp+99TJSMHFep0E7IoDk8YSxyA/86qk/Tx7KkUUlXv4sjJts17ZHxstAoGBAOvn +mF9rm8Y+Og17WlUQyf5j7g4soWG/4zMnoGpjocDfHVms/pASKbIBp5aFtDgWCmM5 +H7InptvBUInROHlooK6paJRDLbDgzVa/m+NLHoct7N25J4NiG8xV6Wv7hlrRp+XK +zyWL8iL95GnB21HJKvEiVBWvOuZnqfVcnzhbmzyzAoGAYT46jMkcyWRMKfgaFFJa +lXebybX1rtw5pClYC2KKbQxerk8C0SHPkqJFIe2BZtWxzj6LiZw9UkAuk+N+lUJT +VpBfKpCUTyA1w8vb8leAtXueQAjU07W6xdlLQ29dgDgpFzUcrF6K+G0LVXlN2xjh +EdzM2yxACmoHpQiQk1kpCK0CgYAz640Fs1FdmGR+gx+miUNr0eKbDAeY0/rVT2tm +/vai1HhJPGHqo5S5sNOJtXOsxG0U2YW4WDHJPArVyk57qiNzTaXOu9pai5+l8BYH +OIlHhzwSsKWZrQYhOudc9MblRi+Fy9U7lkl8mhSjkh8LKRNibwPCogZ8n2QwtGn2 +pXLNMQKBgQDxvs46CA0M9lGvpl0ggnC7bIYYUEvIlszlBh+o2CgF3IOFlGVcvCia +r18i7hTM5wbcct9OWDzZG4ejBIhtE+gMQ333ofQ64PPJOcfuHxT3Z/fMWfv/yDEj +4e4ZPK44ktcTvuusxAoSe5C5dbcNX2ymAhlRg/F0LyMkhw+qGh4xOQ== +-----END RSA PRIVATE KEY----- diff --git a/integration-cli/requirements.go b/integration-cli/requirements.go index a55e750f68..4bc2b654da 100644 --- a/integration-cli/requirements.go +++ b/integration-cli/requirements.go @@ -92,10 +92,10 @@ var ( // for now notary binary is built only if we're running inside // container through `make test`. Figure that out by testing if // notary-server binary is in PATH. - _, err := exec.LookPath(notaryBinary) + _, err := exec.LookPath(notaryServerBinary) return err == nil }, - fmt.Sprintf("Test requires an environment that can host %s in the same host", notaryBinary), + fmt.Sprintf("Test requires an environment that can host %s in the same host", notaryServerBinary), } NotaryServerHosting = testRequirement{ func() bool { diff --git a/integration-cli/trust_server.go b/integration-cli/trust_server.go index d54ac32f9b..308f624a23 100644 --- a/integration-cli/trust_server.go +++ b/integration-cli/trust_server.go @@ -12,19 +12,23 @@ import ( "time" "github.com/docker/docker/cliconfig" + "github.com/docker/docker/pkg/integration/checker" "github.com/docker/docker/pkg/tlsconfig" - "github.com/docker/notary/client" - "github.com/docker/notary/passphrase" - "github.com/docker/notary/tuf/data" "github.com/go-check/check" ) var notaryBinary = "notary" var notaryServerBinary = "notary-server" +type keyPair struct { + Public string + Private string +} + type testNotary struct { - cmd *exec.Cmd - dir string + cmd *exec.Cmd + dir string + keys []keyPair } const notaryHost = "localhost:4443" @@ -90,6 +94,15 @@ func newTestNotary(c *check.C) (*testNotary, error) { return nil, err } + // load key fixture filenames + var keys []keyPair + for i := 1; i < 5; i++ { + keys = append(keys, keyPair{ + Public: filepath.Join(workingDir, fmt.Sprintf("fixtures/notary/delgkey%v.crt", i)), + Private: filepath.Join(workingDir, fmt.Sprintf("fixtures/notary/delgkey%v.key", i)), + }) + } + // run notary-server cmd := exec.Command(notaryServerBinary, "-config", confPath) if err := cmd.Start(); err != nil { @@ -101,8 +114,9 @@ func newTestNotary(c *check.C) (*testNotary, error) { } testNotary := &testNotary{ - cmd: cmd, - dir: tmp, + cmd: cmd, + dir: tmp, + keys: keys, } // Wait for notary to be ready to serve requests. @@ -197,6 +211,7 @@ func (s *DockerTrustSuite) setupTrustedImage(c *check.C, name string) string { pushCmd := exec.Command(dockerBinary, "push", repoName) s.trustedCmd(pushCmd) out, _, err := runCommandWithOutput(pushCmd) + if err != nil { c.Fatalf("Error running trusted push: %s\n%s", err, out) } @@ -211,42 +226,110 @@ func (s *DockerTrustSuite) setupTrustedImage(c *check.C, name string) string { return repoName } -func notaryClientEnv(cmd *exec.Cmd, rootPwd, repositoryPwd string) { +func notaryClientEnv(cmd *exec.Cmd) { + pwd := "12345678" env := []string{ - fmt.Sprintf("NOTARY_ROOT_PASSPHRASE=%s", rootPwd), - fmt.Sprintf("NOTARY_TARGETS_PASSPHRASE=%s", repositoryPwd), - fmt.Sprintf("NOTARY_SNAPSHOT_PASSPHRASE=%s", repositoryPwd), + fmt.Sprintf("NOTARY_ROOT_PASSPHRASE=%s", pwd), + fmt.Sprintf("NOTARY_TARGETS_PASSPHRASE=%s", pwd), + fmt.Sprintf("NOTARY_SNAPSHOT_PASSPHRASE=%s", pwd), } cmd.Env = append(os.Environ(), env...) } -func (s *DockerTrustSuite) setupDelegations(c *check.C, repoName, pwd string) { +func (s *DockerTrustSuite) notaryInitRepo(c *check.C, repoName string) { initCmd := exec.Command(notaryBinary, "-c", filepath.Join(s.not.dir, "client-config.json"), "init", repoName) - notaryClientEnv(initCmd, pwd, pwd) + notaryClientEnv(initCmd) out, _, err := runCommandWithOutput(initCmd) if err != nil { c.Fatalf("Error initializing notary repository: %s\n", out) } +} - // no command line for this, so build by hand - nRepo, err := client.NewNotaryRepository(filepath.Join(cliconfig.ConfigDir(), "trust"), repoName, notaryURL, nil, passphrase.ConstantRetriever(pwd)) - if err != nil { - c.Fatalf("Error creating notary repository: %s\n", err) - } - delgKey, err := nRepo.CryptoService.Create("targets/releases", data.ECDSAKey) - if err != nil { - c.Fatalf("Error creating delegation key: %s\n", err) - } - err = nRepo.AddDelegation("targets/releases", []data.PublicKey{delgKey}, []string{""}) - if err != nil { - c.Fatalf("Error creating delegation: %s\n", err) +func (s *DockerTrustSuite) notaryCreateDelegation(c *check.C, repoName, role string, pubKey string, paths ...string) { + pathsArg := "--all-paths" + if len(paths) > 0 { + pathsArg = "--paths=" + strings.Join(paths, ",") } - // publishing first simulates the client pushing to a repo that they have been given delegated access to + delgCmd := exec.Command(notaryBinary, "-c", filepath.Join(s.not.dir, "client-config.json"), + "delegation", "add", repoName, role, pubKey, pathsArg) + notaryClientEnv(delgCmd) + out, _, err := runCommandWithOutput(delgCmd) + if err != nil { + c.Fatalf("Error adding %s role to notary repository: %s\n", role, out) + } +} + +func (s *DockerTrustSuite) notaryPublish(c *check.C, repoName string) { pubCmd := exec.Command(notaryBinary, "-c", filepath.Join(s.not.dir, "client-config.json"), "publish", repoName) - notaryClientEnv(pubCmd, pwd, pwd) - out, _, err = runCommandWithOutput(pubCmd) + notaryClientEnv(pubCmd) + out, _, err := runCommandWithOutput(pubCmd) if err != nil { c.Fatalf("Error publishing notary repository: %s\n", out) } } + +func (s *DockerTrustSuite) notaryImportKey(c *check.C, repoName, role string, privKey string) { + impCmd := exec.Command(notaryBinary, "-c", filepath.Join(s.not.dir, "client-config.json"), "key", + "import", privKey, "-g", repoName, "-r", role) + notaryClientEnv(impCmd) + out, _, err := runCommandWithOutput(impCmd) + if err != nil { + c.Fatalf("Error importing key to notary repository: %s\n", out) + } +} + +func (s *DockerTrustSuite) notaryListTargetsInRole(c *check.C, repoName, role string) map[string]string { + listCmd := exec.Command(notaryBinary, "-c", filepath.Join(s.not.dir, "client-config.json"), "list", + repoName, "-r", role) + notaryClientEnv(listCmd) + out, _, err := runCommandWithOutput(listCmd) + if err != nil { + c.Fatalf("Error listing targets in notary repository: %s\n", out) + } + + // should look something like: + // NAME DIGEST SIZE (BYTES) ROLE + // ------------------------------------------------------------------------------------------------------ + // latest 24a36bbc059b1345b7e8be0df20f1b23caa3602e85d42fff7ecd9d0bd255de56 1377 targets + + targets := make(map[string]string) + + // no target + lines := strings.Split(strings.TrimSpace(out), "\n") + if len(lines) == 1 && strings.Contains(out, "No targets present in this repository.") { + return targets + } + + // otherwise, there is at least one target + c.Assert(len(lines), checker.GreaterOrEqualThan, 3) + + for _, line := range lines[2:] { + tokens := strings.Fields(line) + c.Assert(tokens, checker.HasLen, 4) + targets[tokens[0]] = tokens[3] + } + + return targets +} + +func (s *DockerTrustSuite) assertTargetInRoles(c *check.C, repoName, target string, roles ...string) { + // check all the roles + for _, role := range roles { + targets := s.notaryListTargetsInRole(c, repoName, role) + roleName, ok := targets[target] + c.Assert(ok, checker.True) + c.Assert(roleName, checker.Equals, role) + } +} + +func (s *DockerTrustSuite) assertTargetNotInRoles(c *check.C, repoName, target string, roles ...string) { + targets := s.notaryListTargetsInRole(c, repoName, "targets") + + roleName, ok := targets[target] + if ok { + for _, role := range roles { + c.Assert(roleName, checker.Not(checker.Equals), role) + } + } +}