-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
docker trust: view, revoke, sign subcommands (experimental) #472
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Several copyedit-style comments. In addition, scrub all occurrences of "like so", "please", and future-facing prose like "will sign" or "will create". In docs, we live in the eternal present and we don't say "please". :) Likewise, the phrase "note that".
|
||
## Description | ||
|
||
Docker trust inspect provides detailed information on signed repositories. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mark this up: docker trust inspect
. If you don't feel comfortable starting the sentence with lowercase mono text, then The
docker trust inspect` command...."
## Description | ||
|
||
Docker trust inspect provides detailed information on signed repositories. | ||
This includes all image tags that are signed, who signed them, and who can sign |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it show unsigned images? This kind of implies not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will not display unsigned images.
This includes all image tags that are signed, who signed them, and who can sign | ||
new tags. | ||
|
||
By default, `docker trust inspect` will render results in a table. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/will render/renders
|
||
```bash | ||
$ docker trust inspect alpine:latest | ||
SIGNED TAG DIGEST SIGNERS |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a newline between input and output.
Root Key: a2489bcac7a79aa67b19b96c4a3bf0c675ffdf00c6d2fabe1a5df1115e80adce | ||
``` | ||
|
||
Note that the `SIGNED TAG` maps to the image tag itself, and associates to given image `DIGEST`. `SIGNERS` lists all entities who have signed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/Note that the/The
"and associates to given image" this phrase isn't parsing right for me. Maybe a rewrite?
Successfully signed "docker.io/example/trust-demo":v1 | ||
``` | ||
|
||
`docker trust inspect` should now list the new signature: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/should now list/lists
|
||
## Initialize a new repo and sign a tag | ||
|
||
When signing an image on a repo for the first time, `docker trust sign` sets up new keys and then signs the image. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/and then signs/before signing
@@ -78,18 +78,21 @@ to use `notary` with Docker images. | |||
|
|||
## Building Notary | |||
|
|||
Prerequisites: | |||
Note that our [latest stable release](https://github.com/docker/notary/releases) is at the head of the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/Note that our/Notary's
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
vendored code - we can address this in docker/notary
Run `make binaries`, which creates the Notary Client CLI binary at `bin/notary`. | ||
Note that `make binaries` assumes a standard Go directory structure, in which | ||
Run `make client`, which creates the Notary Client CLI binary at `bin/notary`. | ||
Note that `make client` assumes a standard Go directory structure, in which | ||
Notary is checked out to the `src` directory in your `GOPATH`. For example: | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Newlines
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
vendored code - we can address this in docker/notary
@@ -98,3 +101,5 @@ $GOPATH/ | |||
docker/ | |||
notary/ | |||
``` | |||
|
|||
To build the server and signer, please run `docker-compose build`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/please//
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
vendored code - we can address this in docker/notary
thank you @mstanleyjones for the review! Just updated with the fixes, and left a comment about one potential command output change. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool!
This is a big change so I looked over the first two commands for now.
Re: testing, I think the tests you have look great, but we need to fake the notary client so we're not actually making HTTP requests in unit tests.
An end-to-end test for each of the commands (inspect, revoke, sign) would be great as well. We'll want to add a notary
service to compose-env.yaml
so we're testing against an isolated service. I think that can be done in a separate follow up PR.
We just merged https://github.com/docker/cli/blob/master/TESTING.md which has a bit more information as well
// trustedPush handles content trust pushing of an image | ||
func trustedPush(ctx context.Context, cli command.Cli, repoInfo *registry.RepositoryInfo, ref reference.Named, authConfig types.AuthConfig, requestPrivilege types.RequestPrivilegeFunc) error { | ||
// TrustedPush handles content trust pushing of an image | ||
func TrustedPush(ctx context.Context, cli command.Cli, repoInfo *registry.RepositoryInfo, ref reference.Named, authConfig types.AuthConfig, requestPrivilege types.RequestPrivilegeFunc) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have a package at cli/trust
. Do you think it would make sense to move these functions (this one and the few below that are either already exported, or were changed to exported) into the cli/trust
package, now that they are being shared between multiple commands?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we certainly can for the GetSignableRoles
function and so I've moved over.
This one is a little more tricky because it calls imagePushPrivileged
- can we export that function?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ya, I think it makes sense to export it. There's nothing in there that's really specific to the command routing. The only change I would make is to accept a client.ImageAPIClient
instead of the full command.Cli
cli/command/trust/cmd.go
Outdated
func NewTrustCommand(dockerCli command.Cli) *cobra.Command { | ||
cmd := &cobra.Command{ | ||
Use: "trust", | ||
Short: "Sign images to establish trust", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current convention for these commands is "Manage x" (see docker --help
). So maybe something like: "Manage trust on Docker images" ?
cli/command/trust/helpers.go
Outdated
} | ||
if err = cl.Clear(""); err != nil { | ||
return err | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
minor: this if is unnecessary,
return cl.Clear("")
cli/command/trust/helpers.go
Outdated
|
||
const releasedRoleName = "Repo Admin" | ||
|
||
func checkLocalImageExistence(ctx context.Context, cli command.Cli, imageName string) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this is only used in sign.go
so would be more appropriate immediately following the function that calls it.
cli/command/trust/helpers.go
Outdated
return err | ||
} | ||
|
||
func getImageReferencesAndAuth(cli command.Cli, imgName string) (context.Context, reference.Named, *registry.RepositoryInfo, *types.AuthConfig, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Returning a context.Context
is a bit unconventional. I think this function should accept the context as the first arg.
Maybe as a follow up, it would be nice to have a type for reference.Named, *registry.RepositoryInfo, *types.AuthConfig
. It seems they are all directly related, so I think having a type would reduce the number of args sent to a couple of functions.
I also notice that getTag()
is always called after this. With a type we could parse out the tag immediately in this single step, and access the tag from .Tag()
.
cli/command/trust/inspect_test.go
Outdated
assert.NotContains(t, cli.OutBuffer().String(), "(Repo Admin)") | ||
} | ||
|
||
func TestTUFToSigner(t *testing.T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TestNotaryRoleToSigner
cli/command/trust/revoke.go
Outdated
}, | ||
} | ||
flags := cmd.Flags() | ||
flags.BoolVarP(&options.forceYes, "yes", "y", false, "Answer yes to the removal question (no confirmation)") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be consistent with other commands, I believe we use the text Do not prompt for confirmation
cli/command/trust/revoke.go
Outdated
return nil | ||
} | ||
|
||
func revokeTestHelper(notaryRepo *client.NotaryRepository, tag string) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does TestHelper
refer to here? Could this be revokeSignature()
?
cli/command/trust/revoke.go
Outdated
} | ||
defer clearChangeList(notaryRepo) | ||
if err := revokeTestHelper(notaryRepo, tag); err != nil { | ||
return fmt.Errorf("could not remove signature for %s: %s", remote, err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
errors.Wrapf(err, "could not remove...")
from pkg/errors
There's a couple other instances of these as well
cli/command/trust/inspect_test.go
Outdated
cli := test.NewFakeCli(&fakeClient{}) | ||
cmd := newInspectCommand(cli) | ||
cmd.SetArgs([]string{"alpine"}) | ||
assert.NoError(t, cmd.Execute()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this contact the "real" notary server? Is that what you were referring to by fixture images in the description?
I think maybe trust.GetNotaryRepository()
should be changed to Cli.GetNotaryClient()
(or something like that) which would return an interface instead of a client.NotaryRepostiory
struct . Once it's on the command.Cli
interface it makes it really easy to fake it in tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it does contact a real notary server. Is the idea that Cli.GetNotaryClient
would wrap a notary repo with a subset of the API? It seems that could certainly work
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't necessary have to wrap it with new structs, it could just return an Interface type instead of a struct, so that we can use a fake during testing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've been trying to wrap my head around this but I'm not sure if it makes sense since a NotaryRepository
is custom per-image. Is that OK in the context of command.Cli
, or are assuming that a command.Cli
may persist for longer than a single image command?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That does make it more difficult. We ran into this problem in #138 with the distribution client. In that case I wrapped the whole thing in an interface that was more appropriate for our use case in the cli (https://github.com/docker/cli/pull/138/files#diff-cb6e6f1ab385cb866626ee63bfa1a273R29).
It sounds like notary is using a similar pattern.
I think something like this in cli/command/cli.go
should work:
func (cli *DockerCli) NotaryClient(ref trust.ImageRefAndAuth) trust.Repository {
...
}
where trust.Repository
is the local interface we define (maybe in cli/trust
).
We can still have multiple instances per command.Cli
. In tests we can return a fakeTrustRepository
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
awesome, that would work. I'll implement this 👍
I think the design looks good. It sounds like you've already done a lot of work on the design before opening this PR so I mostly focused on the code. |
1f18ec9
to
ed1f469
Compare
@dnephin: thank you for the review! I've addressed all comments except for the following:
|
Sounds good. I think we can forget about the tag, I can't think of a good reason to hide the commands. |
efa876c
to
272b5a2
Compare
Some small updates: I've made a couple of tweaks to address small discrepancies in CLI output and we also have an interface for a trust repository incoming from Notary notaryproject/notary#1220 that will help provide testable mocks for this PR. I'll update here once that is merged and vendored. |
272b5a2
to
58b0e75
Compare
@dnephin: I've rebased and vendored in the notary client interface and exposed it as |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, looks good
cli/command/cli.go
Outdated
} | ||
|
||
// ImageRefAndAuth contains all reference information and the auth config for an image request | ||
type ImageRefAndAuth struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the cli/trust
package might be a better place for this type
cli/command/cli.go
Outdated
@@ -151,6 +158,89 @@ func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions) error { | |||
return nil | |||
} | |||
|
|||
// NotaryClient provides a Notary Repository to interact with signed metadata for an image | |||
func (cli *DockerCli) NotaryClient(imgRefAndAuth ImageRefAndAuth, forWrite bool) (notaryclient.Repository, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I always try to avoid boolean arguments because from the caller it's not really clear what true
or false
means. Maybe this could takes a []string
of actions, and there could be two predefined slices in cli/trust
var (
ActionsPullOnly = []string{"pull"}
ActionsPushAndPull = []string{"pull", "push"}
)
So from the caller it will be obvious what is being requested.
58b0e75
to
75bc720
Compare
Signed-off-by: Riyaz Faizullabhoy <[email protected]>
Signed-off-by: Riyaz Faizullabhoy <[email protected]>
Signed-off-by: Riyaz Faizullabhoy <[email protected]>
Signed-off-by: Riyaz Faizullabhoy <[email protected]>
Signed-off-by: Riyaz Faizullabhoy <[email protected]>
Signed-off-by: Riyaz Faizullabhoy <[email protected]>
…ommand semantics Signed-off-by: Riyaz Faizullabhoy <[email protected]>
Signed-off-by: Riyaz Faizullabhoy <[email protected]>
Signed-off-by: Riyaz Faizullabhoy <[email protected]>
Signed-off-by: Riyaz Faizullabhoy <[email protected]>
Signed-off-by: Riyaz Faizullabhoy <[email protected]>
Signed-off-by: Riyaz Faizullabhoy <[email protected]>
Signed-off-by: Riyaz Faizullabhoy <[email protected]>
Signed-off-by: Riyaz Faizullabhoy <[email protected]>
Signed-off-by: Riyaz Faizullabhoy <[email protected]>
Signed-off-by: Riyaz Faizullabhoy <[email protected]>
Signed-off-by: Riyaz Faizullabhoy <[email protected]>
941f7cb
to
e07f345
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
changes and putting it as "experimental" LGTM - we can address tweaking the commands/UX in follow ups
I verified that signing and viewing works from Windows. |
This PR introduces the
docker trust
subcommand. The command has been fleshed out in detail in this design document.docker trust
provides a direct notary signing experience beyond what is provided with theDOCKER_CONTENT_TRUST
environment variable.In this PR, we introduce three subcommands:
docker trust view
: displays signatures on image tags, and who signed themdocker trust revoke
: revokes signatures from signed image tag(s)docker trust sign
: adds a signature to an image tag, pushing the image if it doesn't already existcc @ashfall and @eiais who helped implement this feature