diff --git a/cli/command/trust/key_generate.go b/cli/command/trust/key_generate.go index 2747dd870a..449aeb4e13 100644 --- a/cli/command/trust/key_generate.go +++ b/cli/command/trust/key_generate.go @@ -23,9 +23,9 @@ func newKeyGenerateCommand(dockerCli command.Streams) *cobra.Command { cmd := &cobra.Command{ Use: "key-generate NAME [NAME...]", Short: "Generate and load a signing key-pair", - Args: cli.RequiresMinArgs(1), + Args: cli.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - return setupPassphraseAndGenerateKeys(dockerCli, args) + return setupPassphraseAndGenerateKeys(dockerCli, args[0]) }, } return cmd @@ -36,56 +36,41 @@ var validKeyName = regexp.MustCompile(`^[a-zA-Z0-9\_]+[a-zA-Z0-9\_\-]*$`).MatchS // validate that all of the key names are unique and are alphanumeric + _ + - // and that we do not already have public key files in the current dir on disk -func validateKeyArgs(keyNames []string, cwdPath string) error { - uniqueKeyNames := map[string]struct{}{} - for _, keyName := range keyNames { - if !validKeyName(keyName) { - return fmt.Errorf("key name \"%s\" must not contain special characters", keyName) - } +func validateKeyArgs(keyName string, cwdPath string) error { + if !validKeyName(keyName) { + return fmt.Errorf("key name \"%s\" must not contain special characters", keyName) + } - if _, ok := uniqueKeyNames[keyName]; ok { - return fmt.Errorf("key names must be unique, found duplicate key name: \"%s\"", keyName) - } - uniqueKeyNames[keyName] = struct{}{} - - pubKeyFileName := keyName + ".pub" - if _, err := os.Stat(filepath.Join(cwdPath, pubKeyFileName)); err == nil { - return fmt.Errorf("public key file already exists: \"%s\"", pubKeyFileName) - } + pubKeyFileName := keyName + ".pub" + if _, err := os.Stat(filepath.Join(cwdPath, pubKeyFileName)); err == nil { + return fmt.Errorf("public key file already exists: \"%s\"", pubKeyFileName) } return nil } -func setupPassphraseAndGenerateKeys(streams command.Streams, keyNames []string) error { +func setupPassphraseAndGenerateKeys(streams command.Streams, keyName string) error { // always use a fresh passphrase for each key generation freshPassRetGetter := func() notary.PassRetriever { return trust.GetPassphraseRetriever(streams.In(), streams.Out()) } cwd, err := os.Getwd() if err != nil { return err } - return generateKeys(streams, keyNames, cwd, freshPassRetGetter) + return validateAndGenerateKey(streams, keyName, cwd, freshPassRetGetter) } -func generateKeys(streams command.Streams, keyNames []string, workingDir string, passphraseGetter func() notary.PassRetriever) error { - var genKeyErrs []string - if err := validateKeyArgs(keyNames, workingDir); err != nil { +func validateAndGenerateKey(streams command.Streams, keyName string, workingDir string, passphraseGetter func() notary.PassRetriever) error { + if err := validateKeyArgs(keyName, workingDir); err != nil { return err } - for _, keyName := range keyNames { - fmt.Fprintf(streams.Out(), "\nGenerating key for %s...\n", keyName) - freshPassRet := passphraseGetter() - if err := generateKey(keyName, workingDir, trust.GetTrustDirectory(), freshPassRet); err != nil { - fmt.Fprintf(streams.Out(), err.Error()) - genKeyErrs = append(genKeyErrs, keyName) - } else { - pubFileName := strings.Join([]string{keyName, "pub"}, ".") - fmt.Fprintf(streams.Out(), "Successfully generated and loaded private key. Corresponding public key available: %s\n", pubFileName) - } + fmt.Fprintf(streams.Out(), "\nGenerating key for %s...\n", keyName) + freshPassRet := passphraseGetter() + if err := generateKey(keyName, workingDir, trust.GetTrustDirectory(), freshPassRet); err != nil { + fmt.Fprintf(streams.Out(), err.Error()) + return fmt.Errorf("Error generating key for: %s", keyName) } + pubFileName := strings.Join([]string{keyName, "pub"}, ".") + fmt.Fprintf(streams.Out(), "Successfully generated and loaded private key. Corresponding public key available: %s\n", pubFileName) - if len(genKeyErrs) > 0 { - return fmt.Errorf("Error generating keys for: %s", strings.Join(genKeyErrs, ", ")) - } return nil } diff --git a/cli/command/trust/key_generate_test.go b/cli/command/trust/key_generate_test.go index 7c753fa13e..05afee342a 100644 --- a/cli/command/trust/key_generate_test.go +++ b/cli/command/trust/key_generate_test.go @@ -24,9 +24,15 @@ func TestTrustKeyGenerateErrors(t *testing.T) { }{ { name: "not-enough-args", - expectedError: "requires at least 1 argument", + expectedError: "requires exactly 1 argument", + }, + { + name: "too-many-args", + args: []string{"key-1", "key-2"}, + expectedError: "requires exactly 1 argument", }, } + tmpDir, err := ioutil.TempDir("", "docker-key-generate-test-") assert.NoError(t, err) defer os.RemoveAll(tmpDir) @@ -106,48 +112,19 @@ func TestValidateKeyArgs(t *testing.T) { assert.NoError(t, err) defer os.RemoveAll(pubKeyCWD) - err = validateKeyArgs([]string{"a", "b", "C_123", "key-name"}, pubKeyCWD) + err = validateKeyArgs("a", pubKeyCWD) assert.NoError(t, err) - err = validateKeyArgs([]string{"a", "a"}, pubKeyCWD) - assert.Error(t, err) - assert.Equal(t, err.Error(), "key names must be unique, found duplicate key name: \"a\"") - - err = validateKeyArgs([]string{"a/b"}, pubKeyCWD) + err = validateKeyArgs("a/b", pubKeyCWD) assert.Error(t, err) assert.Equal(t, err.Error(), "key name \"a/b\" must not contain special characters") - err = validateKeyArgs([]string{"-"}, pubKeyCWD) + err = validateKeyArgs("-", pubKeyCWD) assert.Error(t, err) assert.Equal(t, err.Error(), "key name \"-\" must not contain special characters") assert.NoError(t, ioutil.WriteFile(filepath.Join(pubKeyCWD, "a.pub"), []byte("abc"), notary.PrivExecPerms)) - err = validateKeyArgs([]string{"a"}, pubKeyCWD) + err = validateKeyArgs("a", pubKeyCWD) assert.Error(t, err) assert.Equal(t, err.Error(), "public key file already exists: \"a.pub\"") } - -func TestGenerateMultipleKeysOutput(t *testing.T) { - pubKeyCWD, err := ioutil.TempDir("", "pub-keys-") - assert.NoError(t, err) - defer os.RemoveAll(pubKeyCWD) - - passwd := "password" - cannedPasswordRetriever := func() notary.PassRetriever { return passphrase.ConstantRetriever(passwd) } - - cli := test.NewFakeCli(&fakeClient{}) - assert.NoError(t, generateKeys(cli, []string{"alice", "bob", "charlie"}, pubKeyCWD, cannedPasswordRetriever)) - - // Check the stdout prints: - assert.Contains(t, cli.OutBuffer().String(), "\nGenerating key for alice...\n") - assert.Contains(t, cli.OutBuffer().String(), "Successfully generated and loaded private key. Corresponding public key available: alice.pub\n") - assert.Contains(t, cli.OutBuffer().String(), "\nGenerating key for bob...\n") - assert.Contains(t, cli.OutBuffer().String(), "Successfully generated and loaded private key. Corresponding public key available: bob.pub\n") - assert.Contains(t, cli.OutBuffer().String(), "\nGenerating key for charlie...\n") - assert.Contains(t, cli.OutBuffer().String(), "Successfully generated and loaded private key. Corresponding public key available: charlie.pub\n") - - // Check that we have three key files: - cwdKeyFiles, err := ioutil.ReadDir(pubKeyCWD) - assert.NoError(t, err) - assert.Len(t, cwdKeyFiles, 3) -} diff --git a/docs/reference/commandline/trust_key_generate.md b/docs/reference/commandline/trust_key_generate.md new file mode 100644 index 0000000000..1bd8f1632b --- /dev/null +++ b/docs/reference/commandline/trust_key_generate.md @@ -0,0 +1,52 @@ +--- +title: "key-generate" +description: "The key-generate command description and usage" +keywords: "Key, notary, trust" +--- + + + +# trust key-generate + +```markdown +Usage: docker trust key-generate NAME + +Generate and load a signing key-pair + +``` + +## Description + +`docker trust key-generate` generates a key-pair to be used with signing, + and loads the private key into the local docker trust keystore. + +`docker trust key-generate` is currently experimental. + +## Examples + +### Generate a key-pair + +```bash +$ docker trust key-generate alice + +Generating key for alice... +Enter passphrase for new alice key with ID 17acf3c: +Repeat passphrase for new alice key with ID 17acf3c: +Successfully generated and loaded private key. Corresponding public key available: alice.pub +$ ls +alice.pub + +``` + +The private signing key is encrypted by the passphrase and loaded into the docker trust keystore. +All passphrase requests to sign with the key will be referred to by the provided `NAME`. + +The public key component `alice.pub` will be available in the current working directory, and can +be used directly by `docker trust signer-add`. diff --git a/docs/reference/commandline/trust_key_load.md b/docs/reference/commandline/trust_key_load.md new file mode 100644 index 0000000000..69123c04d1 --- /dev/null +++ b/docs/reference/commandline/trust_key_load.md @@ -0,0 +1,56 @@ +--- +title: "key-load" +description: "The key-load command description and usage" +keywords: "Key, notary, trust" +--- + + + +# trust key-load + +```markdown +Usage: docker trust key-load [OPTIONS] KEY + +Load a signing key + +``` + +## Description + +`docker trust key-load` adds private keys to the local docker trust keystore. To add a signer to a repository use `docker trust signer-add`. + +`docker trust key-load` is currently experimental. + +## Examples + +### Load a single private key + +For a private key `alice.pem` with permissions `-rw-------` + +```bash +$ docker trust key-load alice.pem + +Loading key from "alice.pem"... +Enter passphrase for new signer key with ID f8097df: +Repeat passphrase for new signer key with ID f8097df: +Successfully imported key from alice.pem + +``` +to specify a name use the `--name` flag + +```bash +$ docker trust key-load --name alice-key alice.pem + +Loading key from "alice.pem"... +Enter passphrase for new alice-key key with ID f8097df: +Repeat passphrase for new alice-key key with ID f8097df: +Successfully imported key from alice.pem + +``` diff --git a/docs/reference/commandline/trust_signer_add.md b/docs/reference/commandline/trust_signer_add.md new file mode 100644 index 0000000000..9a63c56930 --- /dev/null +++ b/docs/reference/commandline/trust_signer_add.md @@ -0,0 +1,213 @@ +--- +title: "signer-add" +description: "The signer-add command description and usage" +keywords: "signer, notary, trust" +--- + + + +# trust signer-add + +```markdown +Usage: docker trust signer-add [OPTIONS] NAME IMAGE [IMAGE...] + +Add a signer to one or more repositories + +``` + +## Description + +`docker trust signer-add` adds signers to signed repositories. + +`docker trust signer-add` is currently experimental. + +## Examples + +### Add a signer to a repo + +To add a new signer, `alice`, to this repository: + +```bash +$ docker trust inspect example/trust-demo + +No signatures for example/trust-demo + + +List of signers and their keys: + +SIGNER KEYS +bob 5600f5ab76a2 + +Administrative keys for example/trust-demo: +Repository Key: 642692c14c9fc399da523a5f4e24fe306a0a6ee1cc79a10e4555b3c6ab02f71e +Root Key: 3cb2228f6561e58f46dbc4cda4fcaff9d5ef22e865a94636f82450d1d2234949 +``` + +Add `alice` with `docker trust signer-add`: + +```bash +$ docker trust signer-add alice example/trust-demo --key alice.crt + + Adding signer "alice" to example/trust-demo... + Enter passphrase for repository key with ID 642692c: + Successfully added signer: alice to example/trust-demo +``` + +`docker trust inspect` now lists `alice` as a valid signer: + +```bash +$ docker trust inspect example/trust-demo + +No signatures for example/trust-demo + + +List of signers and their keys: + +SIGNER KEYS +alice 05e87edcaecb +bob 5600f5ab76a2 + +Administrative keys for example/trust-demo: +Repository Key: 642692c14c9fc399da523a5f4e24fe306a0a6ee1cc79a10e4555b3c6ab02f71e +Root Key: 3cb2228f6561e58f46dbc4cda4fcaff9d5ef22e865a94636f82450d1d2234949 +``` + +## Initialize a new repo and add a signer + +When adding a signer on a repo for the first time, `docker trust signer-add` sets up a new repo if it doesn't exist. + +```bash +$ docker trust inspect example/trust-demo +No signatures or cannot access example/trust-demo +``` + +```bash +$ docker trust signer-add alice example/trust-demo --key alice.crt + Initializing signed repository for example/trust-demo... + Enter passphrase for root key with ID 748121c: + Enter passphrase for new repository key with ID 95b9e55: + Repeat passphrase for new repository key with ID 95b9e55: + Successfully initialized "example/trust-demo" + + Adding signer "alice" to example/trust-demo... + Successfully added signer: alice to example/trust-demo +``` + +```bash +$ docker trust inspect example/trust-demo + +No signatures for example/trust-demo + + +SIGNED TAG DIGEST SIGNERS + +List of signers and their keys: + +SIGNER KEYS +alice 6d52b29d940f + +Administrative keys for example/trust-demo: +Repository Key: 95b9e5565eac3ef5ec01406801bdfb70feb40c17808d2222427c18046eb63beb +Root Key: 748121c14bd1461f6c58cb3ef39087c8fdc7633bb11a98af844fd9a04e208103 +``` + +## Add a signer to multiple repos +To add a signer, `alice`, to multiple repositories: + +```bash +$ docker trust inspect example/trust-demo +SIGNED TAG DIGEST SIGNERS +v1 74d4bfa917d55d53c7df3d2ab20a8d926874d61c3da5ef6de15dd2654fc467c4 bob + +List of signers and their keys: + +SIGNER KEYS +bob 5600f5ab76a2 + +Administrative keys for example/trust-demo: +Repository Key: ecc457614c9fc399da523a5f4e24fe306a0a6ee1cc79a10e4555b3c6ab02f71e +Root Key: 3cb2228f6561e58f46dbc4cda4fcaff9d5ef22e865a94636f82450d1d2234949 +``` +```bash +$ docker trust inspect example/trust-demo2 +SIGNED TAG DIGEST SIGNERS +v1 74d4bfa917d55d53c7df3d2ab20a8d926874d61c3da5ef6de15dd2654fc467c4 bob + +List of signers and their keys: + +SIGNER KEYS +bob 5600f5ab76a2 + +Administrative keys for example/trust-demo2: +Repository Key: ece554f14c9fc399da523a5f4e24fe306a0a6ee1cc79a10e4553d2ab20a8d9268 +Root Key: 3cb2228f6561e58f46dbc4cda4fcaff9d5ef22e865a94636f82450d1d2234949 +``` +Add `alice` to both repositories with a single `docker trust signer-add` command: + +```bash +$ docker trust signer-add alice example/trust-demo example/trust-demo2 -k alice.crt + +Adding signer "alice" to example/trust-demo... +Enter passphrase for repository key with ID 95b9e55: +Successfully added signer: alice to example/trust-demo + +Adding signer "alice" to example/trust-demo2... +Enter passphrase for repository key with ID ece554f: +Successfully added signer: alice to example/trust-demo2 +``` +`docker trust inspect` now lists `alice` as a valid signer of both `example/trust-demo` and `example/trust-demo2`: + + +```bash +$ docker trust inspect example/trust-demo +SIGNED TAG DIGEST SIGNERS +v1 74d4bfa917d55d53c7df3d2ab20a8d926874d61c3da5ef6de15dd2654fc467c4 bob + +List of signers and their keys: + +SIGNER KEYS +alice 05e87edcaecb +bob 5600f5ab76a2 + +Administrative keys for example/trust-demo: +Repository Key: 95b9e5514c9fc399da523a5f4e24fe306a0a6ee1cc79a10e4555b3c6ab02f71e +Root Key: 3cb2228f6561e58f46dbc4cda4fcaff9d5ef22e865a94636f82450d1d2234949 +``` +```bash +$ docker trust inspect example/trust-demo2 +SIGNED TAG DIGEST SIGNERS +v1 74d4bfa917d55d53c7df3d2ab20a8d926874d61c3da5ef6de15dd2654fc467c4 bob + +List of signers and their keys: + +SIGNER KEYS +alice 05e87edcaecb +bob 5600f5ab76a2 + +Administrative keys for example/trust-demo2: +Repository Key: ece554f14c9fc399da523a5f4e24fe306a0a6ee1cc79a10e4553d2ab20a8d9268 +Root Key: 3cb2228f6561e58f46dbc4cda4fcaff9d5ef22e865a94636f82450d1d2234949 +``` + + +`docker trust signer-add` adds signers to repositories on a best effort basis, so it will continue to add the signer to subsequent repositories if one attempt fails: + +```bash +$ docker trust signer-add alice example/unauthorized example/authorized -k alice.crt + +Adding signer "alice" to example/unauthorized... +you are not authorized to perform this operation: server returned 401. + +Adding signer "alice" to example/authorized... +Enter passphrase for repository key with ID c6772a0: +Successfully added signer: alice to example/authorized + +Failed to add signer to: example/unauthorized +``` diff --git a/docs/reference/commandline/trust_signer_remove.md b/docs/reference/commandline/trust_signer_remove.md new file mode 100644 index 0000000000..386fccbdb6 --- /dev/null +++ b/docs/reference/commandline/trust_signer_remove.md @@ -0,0 +1,168 @@ +--- +title: "signer-remove" +description: "The signer-remove command description and usage" +keywords: "signer, notary, trust" +--- + + + +# trust signer-remove + +```markdown +Usage: docker trust signer-remove [OPTIONS] NAME IMAGE [IMAGE...] + +Remove a signer from one or more repositories + +``` + +## Description + +`docker trust signer-remove` removes signers from signed repositories. + +`docker trust signer-remove` is currently experimental. + +## Examples + +### Remove a signer from a repo + +To remove an existing signer, `alice`, from this repository: + +```bash +$ docker trust inspect example/trust-demo + +No signatures for example/trust-demo + + +List of signers and their keys: + +SIGNER KEYS +alice 05e87edcaecb +bob 5600f5ab76a2 + +Administrative keys for example/trust-demo: +Repository Key: ecc457614c9fc399da523a5f4e24fe306a0a6ee1cc79a10e4555b3c6ab02f71e +Root Key: 3cb2228f6561e58f46dbc4cda4fcaff9d5ef22e865a94636f82450d1d2234949 +``` + +Remove `alice` with `docker trust signer-remove`: + +```bash +$ docker trust signer-remove alice example/trust-demo + Enter passphrase for repository key with ID 642692c: + Successfully removed alice from example/trust-demo + +``` + +`docker trust inspect` now does not list `alice` as a valid signer: + +```bash +$ docker trust inspect example/trust-demo + +No signatures for example/trust-demo + + +List of signers and their keys: + +SIGNER KEYS +bob 5600f5ab76a2 + +Administrative keys for example/trust-demo: +Repository Key: ecc457614c9fc399da523a5f4e24fe306a0a6ee1cc79a10e4555b3c6ab02f71e +Root Key: 3cb2228f6561e58f46dbc4cda4fcaff9d5ef22e865a94636f82450d1d2234949 +``` + +### Remove a signer from multiple repos + +To remove an existing signer, `alice`, from multiple repositories: + +```bash +$ docker trust inspect example/trust-demo +SIGNED TAG DIGEST SIGNERS +v1 74d4bfa917d55d53c7df3d2ab20a8d926874d61c3da5ef6de15dd2654fc467c4 alice, bob + +List of signers and their keys: + +SIGNER KEYS +alice 05e87edcaecb +bob 5600f5ab76a2 + +Administrative keys for example/trust-demo: +Repository Key: 95b9e5514c9fc399da523a5f4e24fe306a0a6ee1cc79a10e4555b3c6ab02f71e +Root Key: 3cb2228f6561e58f46dbc4cda4fcaff9d5ef22e865a94636f82450d1d2234949 +``` +```bash +$ docker trust inspect example/trust-demo2 +SIGNED TAG DIGEST SIGNERS +v1 74d4bfa917d55d53c7df3d2ab20a8d926874d61c3da5ef6de15dd2654fc467c4 alice, bob + +List of signers and their keys: + +SIGNER KEYS +alice 05e87edcaecb +bob 5600f5ab76a2 + +Administrative keys for example/trust-demo2: +Repository Key: ece554f14c9fc399da523a5f4e24fe306a0a6ee1cc79a10e4553d2ab20a8d9268 +Root Key: 3cb2228f6561e58f46dbc4cda4fcaff9d5ef22e865a94636f82450d1d2234949 +``` +Remove `alice` from both images with a single `docker trust signer-remove` command: + +```bash +$ docker trust signer-remove alice example/trust-demo example/trust-demo2 +Enter passphrase for repository key with ID 95b9e55: +Successfully removed alice from example/trust-demo +Enter passphrase for repository key with ID ece554f: +Successfully removed alice from example/trust-demo2 +``` +`docker trust inspect` no longer lists `alice` as a valid signer of either `example/trust-demo` or `example/trust-demo2`: +```bash +$ docker trust inspect example/trust-demo +SIGNED TAG DIGEST SIGNERS +v1 74d4bfa917d55d53c7df3d2ab20a8d926874d61c3da5ef6de15dd2654fc467c4 bob + +List of signers and their keys: + +SIGNER KEYS +bob 5600f5ab76a2 + +Administrative keys for example/trust-demo: +Repository Key: ecc457614c9fc399da523a5f4e24fe306a0a6ee1cc79a10e4555b3c6ab02f71e +Root Key: 3cb2228f6561e58f46dbc4cda4fcaff9d5ef22e865a94636f82450d1d2234949 +``` +```bash +$ docker trust inspect example/trust-demo2 +SIGNED TAG DIGEST SIGNERS +v1 74d4bfa917d55d53c7df3d2ab20a8d926874d61c3da5ef6de15dd2654fc467c4 bob + +List of signers and their keys: + +SIGNER KEYS +bob 5600f5ab76a2 + +Administrative keys for example/trust-demo2: +Repository Key: ece554f14c9fc399da523a5f4e24fe306a0a6ee1cc79a10e4553d2ab20a8d9268 +Root Key: 3cb2228f6561e58f46dbc4cda4fcaff9d5ef22e865a94636f82450d1d2234949 +``` + +`docker trust signer-remove` removes signers to repositories on a best effort basis, so it will continue to remove the signer from subsequent repositories if one attempt fails: + +```bash +$ docker trust signer-remove alice example/unauthorized example/authorized + +Removing signer "alice" from image example/unauthorized... +No signer alice for image example/unauthorized + +Removing signer "alice" from image example/authorized... +Enter passphrase for repository key with ID c6772a0: +Successfully removed alice from example/authorized + +Error removing signer from: example/unauthorized +``` +