mirror of https://github.com/docker/docs.git
rework export key
Signed-off-by: Riyaz Faizullabhoy <riyaz.faizullabhoy@docker.com>
This commit is contained in:
parent
3b3026c121
commit
12fd5aa246
|
@ -52,10 +52,10 @@ var cmdKeysBackupTemplate = usageTemplate{
|
|||
Long: "Backs up all of your accessible of keys. The keys are reencrypted with a new passphrase. The output is a ZIP file. If the --gun option is passed, only signing keys and no root keys will be backed up. Does not work on keys that are only in hardware (e.g. Yubikeys).",
|
||||
}
|
||||
|
||||
var cmdKeyExportRootTemplate = usageTemplate{
|
||||
var cmdKeyExportTemplate = usageTemplate{
|
||||
Use: "export [ keyID ] [ pemfilename ]",
|
||||
Short: "Export a root key on disk to a PEM file.",
|
||||
Long: "Exports a single root key on disk, without reencrypting. The output is a PEM file. Does not work on keys that are only in hardware (e.g. Yubikeys).",
|
||||
Short: "Export a private key on disk to a PEM file.",
|
||||
Long: "Exports a single private key on disk, without reencrypting. The output is a PEM file. Does not work on keys that are only in hardware (e.g. Yubikeys).",
|
||||
}
|
||||
|
||||
var cmdKeysRestoreTemplate = usageTemplate{
|
||||
|
@ -64,10 +64,10 @@ var cmdKeysRestoreTemplate = usageTemplate{
|
|||
Long: "Restores one or more keys from a ZIP file. If hardware key storage (e.g. a Yubikey) is available, root keys will be imported into the hardware, but not backed up to disk in the same location as the other, non-root keys.",
|
||||
}
|
||||
|
||||
var cmdKeyImportRootTemplate = usageTemplate{
|
||||
var cmdKeyImportTemplate = usageTemplate{
|
||||
Use: "import [ pemfilename ]",
|
||||
Short: "Imports a root key from a PEM file.",
|
||||
Long: "Imports a single root key from a PEM file. If a hardware key storage (e.g. Yubikey) is available, the root key will be imported into the hardware but not backed up on disk again.",
|
||||
Short: "Imports a key from a PEM file.",
|
||||
Long: "Imports a single key from a PEM file. If a hardware key storage (e.g. Yubikey) is available, the root key will be imported into the hardware but not backed up on disk again.",
|
||||
}
|
||||
|
||||
var cmdKeyRemoveTemplate = usageTemplate{
|
||||
|
@ -88,7 +88,7 @@ type keyCommander struct {
|
|||
getRetriever func() passphrase.Retriever
|
||||
|
||||
// these are for command line parsing - no need to set
|
||||
keysExportRootChangePassphrase bool
|
||||
keysExportChangePassphrase bool
|
||||
keysExportGUN string
|
||||
rotateKeyRole string
|
||||
rotateKeyServerManaged bool
|
||||
|
@ -99,7 +99,8 @@ func (k *keyCommander) GetCommand() *cobra.Command {
|
|||
cmd.AddCommand(cmdKeyListTemplate.ToCommand(k.keysList))
|
||||
cmd.AddCommand(cmdKeyGenerateRootKeyTemplate.ToCommand(k.keysGenerateRootKey))
|
||||
cmd.AddCommand(cmdKeysRestoreTemplate.ToCommand(k.keysRestore))
|
||||
cmd.AddCommand(cmdKeyImportRootTemplate.ToCommand(k.keysImportRoot))
|
||||
cmd.AddCommand(cmdKeyImportTemplate.ToCommand(k.keysImportRoot))
|
||||
|
||||
cmd.AddCommand(cmdKeyRemoveTemplate.ToCommand(k.keyRemove))
|
||||
cmd.AddCommand(cmdKeyPasswdTemplate.ToCommand(k.keyPassphraseChange))
|
||||
|
||||
|
@ -108,11 +109,11 @@ func (k *keyCommander) GetCommand() *cobra.Command {
|
|||
&k.keysExportGUN, "gun", "g", "", "Globally Unique Name to export keys for")
|
||||
cmd.AddCommand(cmdKeysBackup)
|
||||
|
||||
cmdKeyExportRoot := cmdKeyExportRootTemplate.ToCommand(k.keysExportRoot)
|
||||
cmdKeyExportRoot.Flags().BoolVarP(
|
||||
&k.keysExportRootChangePassphrase, "change-passphrase", "p", false,
|
||||
cmdKeyExport := cmdKeyExportTemplate.ToCommand(k.keysExport)
|
||||
cmdKeyExport.Flags().BoolVarP(
|
||||
&k.keysExportChangePassphrase, "change-passphrase", "p", false,
|
||||
"Set a new passphrase for the key being exported")
|
||||
cmd.AddCommand(cmdKeyExportRoot)
|
||||
cmd.AddCommand(cmdKeyExport)
|
||||
|
||||
cmdRotateKey := cmdRotateKeyTemplate.ToCommand(k.keysRotate)
|
||||
cmdRotateKey.Flags().BoolVarP(&k.rotateKeyServerManaged, "server-managed", "r",
|
||||
|
@ -236,8 +237,8 @@ func (k *keyCommander) keysBackup(cmd *cobra.Command, args []string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// keysExportRoot exports a root key by ID to a PEM file
|
||||
func (k *keyCommander) keysExportRoot(cmd *cobra.Command, args []string) error {
|
||||
// keysExport exports a key by ID to a PEM file
|
||||
func (k *keyCommander) keysExport(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 2 {
|
||||
cmd.Usage()
|
||||
return fmt.Errorf("Must specify key ID and output filename for export")
|
||||
|
@ -247,7 +248,7 @@ func (k *keyCommander) keysExportRoot(cmd *cobra.Command, args []string) error {
|
|||
exportFilename := args[1]
|
||||
|
||||
if len(keyID) != notary.Sha256HexSize {
|
||||
return fmt.Errorf("Please specify a valid root key ID")
|
||||
return fmt.Errorf("Please specify a valid key ID")
|
||||
}
|
||||
|
||||
config, err := k.configGetter()
|
||||
|
@ -258,24 +259,43 @@ func (k *keyCommander) keysExportRoot(cmd *cobra.Command, args []string) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cs := cryptoservice.NewCryptoService("", ks...)
|
||||
|
||||
// Search for this key, determine whether this key has a GUN
|
||||
keyGun := ""
|
||||
keyRole := ""
|
||||
for _, store := range ks {
|
||||
for keypath, role := range store.ListKeys() {
|
||||
if filepath.Base(keypath) == keyID {
|
||||
keyRole = role
|
||||
if role != data.CanonicalRootRole {
|
||||
dirPath := filepath.Dir(keypath)
|
||||
if dirPath != "." { // no gun
|
||||
keyGun = dirPath
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cs := cryptoservice.NewCryptoService(keyGun, ks...)
|
||||
|
||||
exportFile, err := os.Create(exportFilename)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error creating output file: %v", err)
|
||||
}
|
||||
if k.keysExportRootChangePassphrase {
|
||||
if k.keysExportChangePassphrase {
|
||||
// Must use a different passphrase retriever to avoid caching the
|
||||
// unlocking passphrase and reusing that.
|
||||
exportRetriever := k.getRetriever()
|
||||
err = cs.ExportRootKeyReencrypt(exportFile, keyID, exportRetriever)
|
||||
err = cs.ExportKeyReencrypt(exportFile, keyID, exportRetriever)
|
||||
} else {
|
||||
err = cs.ExportRootKey(exportFile, keyID)
|
||||
err = cs.ExportKey(exportFile, keyID, keyRole)
|
||||
}
|
||||
exportFile.Close()
|
||||
if err != nil {
|
||||
os.Remove(exportFilename)
|
||||
return fmt.Errorf("Error exporting root key: %v", err)
|
||||
return fmt.Errorf("Error exporting %s key: %v", keyRole, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
|
||||
"github.com/docker/notary/passphrase"
|
||||
"github.com/docker/notary/trustmanager"
|
||||
"github.com/docker/notary/tuf/data"
|
||||
)
|
||||
|
||||
const zipMadeByUNIX = 3 << 8
|
||||
|
@ -31,15 +32,18 @@ var (
|
|||
ErrNoKeysFoundForGUN = errors.New("no keys found for specified GUN")
|
||||
)
|
||||
|
||||
// ExportRootKey exports the specified root key to an io.Writer in PEM format.
|
||||
// ExportKey exports the specified key to an io.Writer in PEM format.
|
||||
// The key's existing encryption is preserved.
|
||||
func (cs *CryptoService) ExportRootKey(dest io.Writer, keyID string) error {
|
||||
func (cs *CryptoService) ExportKey(dest io.Writer, keyID, role string) error {
|
||||
var (
|
||||
pemBytes []byte
|
||||
err error
|
||||
)
|
||||
|
||||
for _, ks := range cs.keyStores {
|
||||
if role != data.CanonicalRootRole {
|
||||
keyID = filepath.Join(cs.gun, keyID)
|
||||
}
|
||||
pemBytes, err = ks.ExportKey(keyID)
|
||||
if err != nil {
|
||||
continue
|
||||
|
@ -59,9 +63,9 @@ func (cs *CryptoService) ExportRootKey(dest io.Writer, keyID string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// ExportRootKeyReencrypt exports the specified root key to an io.Writer in
|
||||
// ExportRootKeyReencrypt exports the specified private key to an io.Writer in
|
||||
// PEM format. The key is reencrypted with a new passphrase.
|
||||
func (cs *CryptoService) ExportRootKeyReencrypt(dest io.Writer, keyID string, newPassphraseRetriever passphrase.Retriever) error {
|
||||
func (cs *CryptoService) ExportKeyReencrypt(dest io.Writer, keyID string, newPassphraseRetriever passphrase.Retriever) error {
|
||||
privateKey, role, err := cs.GetPrivateKey(keyID)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -110,7 +114,7 @@ func (cs *CryptoService) ImportRootKey(source io.Reader) error {
|
|||
|
||||
for _, ks := range cs.keyStores {
|
||||
// don't redeclare err, we want the value carried out of the loop
|
||||
if err = ks.ImportKey(pemBytes, "root"); err == nil {
|
||||
if err = ks.ImportKey(pemBytes, data.CanonicalRootRole); err == nil {
|
||||
return nil //bail on the first keystore we import to
|
||||
}
|
||||
}
|
||||
|
|
|
@ -284,7 +284,7 @@ func TestImportExportRootKey(t *testing.T) {
|
|||
tempKeyFilePath := tempKeyFile.Name()
|
||||
defer os.Remove(tempKeyFilePath)
|
||||
|
||||
err = cs.ExportRootKey(tempKeyFile, rootKeyID)
|
||||
err = cs.ExportKey(tempKeyFile, rootKeyID, data.CanonicalRootRole)
|
||||
assert.NoError(t, err)
|
||||
tempKeyFile.Close()
|
||||
|
||||
|
@ -352,7 +352,7 @@ func TestImportExportRootKeyReencrypt(t *testing.T) {
|
|||
tempKeyFilePath := tempKeyFile.Name()
|
||||
defer os.Remove(tempKeyFilePath)
|
||||
|
||||
err = cs.ExportRootKeyReencrypt(tempKeyFile, rootKeyID, newPassphraseRetriever)
|
||||
err = cs.ExportKeyReencrypt(tempKeyFile, rootKeyID, newPassphraseRetriever)
|
||||
assert.NoError(t, err)
|
||||
tempKeyFile.Close()
|
||||
|
||||
|
|
Loading…
Reference in New Issue