mirror of https://github.com/docker/docs.git
Move passphrase logic to its own package
The logic to retrieve passphrase is generic and may be used by directly by clients. Signed-off-by: Derek McGowan <derek@mcgstyle.net> (github: dmcgowan)
This commit is contained in:
parent
ca98668cfc
commit
c35c1ea254
|
@ -14,6 +14,7 @@ import (
|
|||
"github.com/docker/notary/client/changelist"
|
||||
"github.com/docker/notary/cryptoservice"
|
||||
"github.com/docker/notary/keystoremanager"
|
||||
"github.com/docker/notary/pkg/passphrase"
|
||||
"github.com/docker/notary/trustmanager"
|
||||
"github.com/endophage/gotuf"
|
||||
tufclient "github.com/endophage/gotuf/client"
|
||||
|
@ -84,7 +85,7 @@ func NewTarget(targetName string, targetPath string) (*Target, error) {
|
|||
// It takes the base directory under where all the trust files will be stored
|
||||
// (usually ~/.docker/trust/).
|
||||
func NewNotaryRepository(baseDir, gun, baseURL string, rt http.RoundTripper,
|
||||
passphraseRetriever trustmanager.PassphraseRetriever) (*NotaryRepository, error) {
|
||||
passphraseRetriever passphrase.Retriever) (*NotaryRepository, error) {
|
||||
|
||||
keyStoreManager, err := keystoremanager.NewKeyStoreManager(baseDir, passphraseRetriever)
|
||||
if err != nil {
|
||||
|
|
|
@ -1,31 +1,27 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/sha256"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/pkg/term"
|
||||
notaryclient "github.com/docker/notary/client"
|
||||
"github.com/docker/notary/trustmanager"
|
||||
"github.com/docker/notary/pkg/passphrase"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// FIXME: This should not be hardcoded
|
||||
const hardcodedBaseURL = "https://notary-server:4443"
|
||||
|
||||
var retriever trustmanager.PassphraseRetriever
|
||||
var retriever passphrase.Retriever
|
||||
|
||||
func init() {
|
||||
retriever = getNotaryPassphraseRetriever()
|
||||
retriever = passphrase.PromptRetriever()
|
||||
}
|
||||
|
||||
var remoteTrustServer string
|
||||
|
@ -272,93 +268,6 @@ func verify(cmd *cobra.Command, args []string) {
|
|||
return
|
||||
}
|
||||
|
||||
func getNotaryPassphraseRetriever() trustmanager.PassphraseRetriever {
|
||||
userEnteredTargetsSnapshotsPass := false
|
||||
targetsSnapshotsPass := ""
|
||||
userEnteredRootsPass := false
|
||||
rootsPass := ""
|
||||
|
||||
return func(keyID string, alias string, createNew bool, numAttempts int) (string, bool, error) {
|
||||
|
||||
// First, check if we have a password cached for this alias.
|
||||
if numAttempts == 0 {
|
||||
if userEnteredTargetsSnapshotsPass && (alias == "snapshot" || alias == "targets") {
|
||||
return targetsSnapshotsPass, false, nil
|
||||
}
|
||||
if userEnteredRootsPass && (alias == "root") {
|
||||
return rootsPass, false, nil
|
||||
}
|
||||
}
|
||||
|
||||
if numAttempts > 3 && !createNew {
|
||||
return "", true, errors.New("Too many attempts")
|
||||
}
|
||||
|
||||
state, err := term.SaveState(0)
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
term.DisableEcho(0, state)
|
||||
defer term.RestoreTerminal(0, state)
|
||||
|
||||
stdin := bufio.NewReader(os.Stdin)
|
||||
|
||||
if createNew {
|
||||
fmt.Printf("Enter passphrase for new %s key with id %s: ", alias, keyID)
|
||||
} else {
|
||||
fmt.Printf("Enter key passphrase for %s key with id %s: ", alias, keyID)
|
||||
}
|
||||
|
||||
passphrase, err := stdin.ReadBytes('\n')
|
||||
fmt.Println()
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
|
||||
retPass := strings.TrimSpace(string(passphrase))
|
||||
|
||||
if !createNew {
|
||||
if alias == "snapshot" || alias == "targets" {
|
||||
userEnteredTargetsSnapshotsPass = true
|
||||
targetsSnapshotsPass = retPass
|
||||
}
|
||||
if alias == "root" {
|
||||
userEnteredRootsPass = true
|
||||
rootsPass = retPass
|
||||
}
|
||||
return retPass, false, nil
|
||||
}
|
||||
|
||||
if len(retPass) < 8 {
|
||||
fmt.Println("Please use a password manager to generate and store a good random passphrase.")
|
||||
return "", false, errors.New("Passphrase too short")
|
||||
}
|
||||
|
||||
fmt.Printf("Repeat passphrase for new %s key with id %s: ", alias, keyID)
|
||||
confirmation, err := stdin.ReadBytes('\n')
|
||||
fmt.Println()
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
confirmationStr := strings.TrimSpace(string(confirmation))
|
||||
|
||||
if retPass != confirmationStr {
|
||||
return "", false, errors.New("The entered passphrases do not match")
|
||||
}
|
||||
|
||||
if alias == "snapshot" || alias == "targets" {
|
||||
userEnteredTargetsSnapshotsPass = true
|
||||
targetsSnapshotsPass = retPass
|
||||
}
|
||||
if alias == "root" {
|
||||
userEnteredRootsPass = true
|
||||
rootsPass = retPass
|
||||
}
|
||||
|
||||
return retPass, false, nil
|
||||
}
|
||||
}
|
||||
|
||||
func getInsecureTransport() *http.Transport {
|
||||
return &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/notary/pkg/passphrase"
|
||||
"github.com/docker/notary/trustmanager"
|
||||
)
|
||||
|
||||
|
@ -135,7 +136,7 @@ func addKeysToArchive(zipWriter *zip.Writer, newKeyStore *trustmanager.KeyFileSt
|
|||
|
||||
// ExportAllKeys exports all keys to an io.Writer in zip format.
|
||||
// newPassphraseRetriever will be used to obtain passphrases to use to encrypt the existing keys.
|
||||
func (km *KeyStoreManager) ExportAllKeys(dest io.Writer, newPassphraseRetriever trustmanager.PassphraseRetriever) error {
|
||||
func (km *KeyStoreManager) ExportAllKeys(dest io.Writer, newPassphraseRetriever passphrase.Retriever) error {
|
||||
tempBaseDir, err := ioutil.TempDir("", "notary-key-export-")
|
||||
defer os.RemoveAll(tempBaseDir)
|
||||
|
||||
|
@ -280,7 +281,7 @@ func moveKeysByGUN(oldKeyStore, newKeyStore *trustmanager.KeyFileStore, gun stri
|
|||
// ExportKeysByGUN exports all keys associated with a specified GUN to an
|
||||
// io.Writer in zip format. passphraseRetriever is used to select new passphrases to use to
|
||||
// encrypt the keys.
|
||||
func (km *KeyStoreManager) ExportKeysByGUN(dest io.Writer, gun string, passphraseRetriever trustmanager.PassphraseRetriever) error {
|
||||
func (km *KeyStoreManager) ExportKeysByGUN(dest io.Writer, gun string, passphraseRetriever passphrase.Retriever) error {
|
||||
tempBaseDir, err := ioutil.TempDir("", "notary-key-export-")
|
||||
defer os.RemoveAll(tempBaseDir)
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/notary/cryptoservice"
|
||||
"github.com/docker/notary/pkg/passphrase"
|
||||
"github.com/docker/notary/trustmanager"
|
||||
"github.com/endophage/gotuf/data"
|
||||
"github.com/endophage/gotuf/signed"
|
||||
|
@ -60,7 +61,7 @@ func (err ErrRootRotationFail) Error() string {
|
|||
|
||||
// NewKeyStoreManager returns an initialized KeyStoreManager, or an error
|
||||
// if it fails to create the KeyFileStores or load certificates
|
||||
func NewKeyStoreManager(baseDir string, passphraseRetriever trustmanager.PassphraseRetriever) (*KeyStoreManager, error) {
|
||||
func NewKeyStoreManager(baseDir string, passphraseRetriever passphrase.Retriever) (*KeyStoreManager, error) {
|
||||
nonRootKeysPath := filepath.Join(baseDir, privDir, nonRootKeysSubdir)
|
||||
nonRootKeyStore, err := trustmanager.NewKeyFileStore(nonRootKeysPath, passphraseRetriever)
|
||||
if err != nil {
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
// Package passphrase is a utility function for managing passphrase
|
||||
// for TUF and Notary keys.
|
||||
package passphrase
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/pkg/term"
|
||||
)
|
||||
|
||||
// Retriever is a callback function that should retrieve a passphrase
|
||||
// for a given named key. If it should be treated as new passphrase (e.g. with
|
||||
// confirmation), createNew will be true. Attempts is passed in so that implementers
|
||||
// decide how many chances to give to a human, for example.
|
||||
type Retriever func(keyName, alias string, createNew bool, attempts int) (passphrase string, giveup bool, err error)
|
||||
|
||||
// PromptRetriever returns a new Retriever which will provide a terminal prompt
|
||||
// to retrieve a passphrase. The passphrase will be cached such that subsequent
|
||||
// prompts will produce the same passphrase.
|
||||
func PromptRetriever() Retriever {
|
||||
userEnteredTargetsSnapshotsPass := false
|
||||
targetsSnapshotsPass := ""
|
||||
userEnteredRootsPass := false
|
||||
rootsPass := ""
|
||||
|
||||
return func(keyName string, alias string, createNew bool, numAttempts int) (string, bool, error) {
|
||||
|
||||
// First, check if we have a password cached for this alias.
|
||||
if numAttempts == 0 {
|
||||
if userEnteredTargetsSnapshotsPass && (alias == "snapshot" || alias == "targets") {
|
||||
return targetsSnapshotsPass, false, nil
|
||||
}
|
||||
if userEnteredRootsPass && (alias == "root") {
|
||||
return rootsPass, false, nil
|
||||
}
|
||||
}
|
||||
|
||||
if numAttempts > 3 && !createNew {
|
||||
return "", true, errors.New("Too many attempts")
|
||||
}
|
||||
|
||||
state, err := term.SaveState(0)
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
term.DisableEcho(0, state)
|
||||
defer term.RestoreTerminal(0, state)
|
||||
|
||||
stdin := bufio.NewReader(os.Stdin)
|
||||
|
||||
if createNew {
|
||||
fmt.Printf("Enter passphrase for new %s key with id %s: ", alias, keyName)
|
||||
} else {
|
||||
fmt.Printf("Enter key passphrase for %s key with id %s: ", alias, keyName)
|
||||
}
|
||||
|
||||
passphrase, err := stdin.ReadBytes('\n')
|
||||
fmt.Println()
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
|
||||
retPass := strings.TrimSpace(string(passphrase))
|
||||
|
||||
if !createNew {
|
||||
if alias == "snapshot" || alias == "targets" {
|
||||
userEnteredTargetsSnapshotsPass = true
|
||||
targetsSnapshotsPass = retPass
|
||||
}
|
||||
if alias == "root" {
|
||||
userEnteredRootsPass = true
|
||||
rootsPass = retPass
|
||||
}
|
||||
return retPass, false, nil
|
||||
}
|
||||
|
||||
if len(retPass) < 8 {
|
||||
fmt.Println("Please use a password manager to generate and store a good random passphrase.")
|
||||
return "", false, errors.New("Passphrase too short")
|
||||
}
|
||||
|
||||
fmt.Printf("Repeat passphrase for new %s key with id %s: ", alias, keyName)
|
||||
confirmation, err := stdin.ReadBytes('\n')
|
||||
fmt.Println()
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
confirmationStr := strings.TrimSpace(string(confirmation))
|
||||
|
||||
if retPass != confirmationStr {
|
||||
return "", false, errors.New("The entered passphrases do not match")
|
||||
}
|
||||
|
||||
if alias == "snapshot" || alias == "targets" {
|
||||
userEnteredTargetsSnapshotsPass = true
|
||||
targetsSnapshotsPass = retPass
|
||||
}
|
||||
if alias == "root" {
|
||||
userEnteredRootsPass = true
|
||||
rootsPass = retPass
|
||||
}
|
||||
|
||||
return retPass, false, nil
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/docker/notary/cryptoservice"
|
||||
"github.com/docker/notary/pkg/passphrase"
|
||||
"github.com/docker/notary/signer"
|
||||
"github.com/docker/notary/signer/api"
|
||||
"github.com/docker/notary/trustmanager"
|
||||
|
@ -24,7 +25,7 @@ var (
|
|||
sClient pb.SignerClient
|
||||
grpcServer *grpc.Server
|
||||
void *pb.Void
|
||||
pr trustmanager.PassphraseRetriever
|
||||
pr passphrase.Retriever
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
|
|
@ -6,6 +6,8 @@ import (
|
|||
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/notary/pkg/passphrase"
|
||||
"github.com/endophage/gotuf/data"
|
||||
)
|
||||
|
||||
|
@ -24,27 +26,21 @@ type KeyStore interface {
|
|||
RemoveKey(name string) error
|
||||
}
|
||||
|
||||
// PassphraseRetriever is a callback function that should retrieve a passphrase
|
||||
// for a given named key. If it should be treated as new passphrase (e.g. with
|
||||
// confirmation), createNew will be true. Attempts is passed in so that implementers
|
||||
// decide how many chances to give to a human, for example.
|
||||
type PassphraseRetriever func(keyId, alias string, createNew bool, attempts int) (passphrase string, giveup bool, err error)
|
||||
|
||||
// KeyFileStore persists and manages private keys on disk
|
||||
type KeyFileStore struct {
|
||||
SimpleFileStore
|
||||
PassphraseRetriever
|
||||
PassphraseRetriever passphrase.Retriever
|
||||
}
|
||||
|
||||
// KeyMemoryStore manages private keys in memory
|
||||
type KeyMemoryStore struct {
|
||||
MemoryFileStore
|
||||
PassphraseRetriever
|
||||
PassphraseRetriever passphrase.Retriever
|
||||
}
|
||||
|
||||
// NewKeyFileStore returns a new KeyFileStore creating a private directory to
|
||||
// hold the keys.
|
||||
func NewKeyFileStore(baseDir string, passphraseRetriever PassphraseRetriever) (*KeyFileStore, error) {
|
||||
func NewKeyFileStore(baseDir string, passphraseRetriever passphrase.Retriever) (*KeyFileStore, error) {
|
||||
fileStore, err := NewPrivateSimpleFileStore(baseDir, keyExtension)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -81,7 +77,7 @@ func (s *KeyFileStore) RemoveKey(name string) error {
|
|||
}
|
||||
|
||||
// NewKeyMemoryStore returns a new KeyMemoryStore which holds keys in memory
|
||||
func NewKeyMemoryStore(passphraseRetriever PassphraseRetriever) *KeyMemoryStore {
|
||||
func NewKeyMemoryStore(passphraseRetriever passphrase.Retriever) *KeyMemoryStore {
|
||||
memStore := NewMemoryFileStore()
|
||||
|
||||
return &KeyMemoryStore{*memStore, passphraseRetriever}
|
||||
|
@ -114,7 +110,7 @@ func (s *KeyMemoryStore) RemoveKey(name string) error {
|
|||
return removeKey(s, name)
|
||||
}
|
||||
|
||||
func addKey(s LimitedFileStore, passphraseRetriever PassphraseRetriever, name, alias string, privKey data.PrivateKey) error {
|
||||
func addKey(s LimitedFileStore, passphraseRetriever passphrase.Retriever, name, alias string, privKey data.PrivateKey) error {
|
||||
pemPrivKey, err := KeyToPEM(privKey)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -166,7 +162,7 @@ func getKeyAlias(s LimitedFileStore, keyID string) (string, error) {
|
|||
}
|
||||
|
||||
// GetKey returns the PrivateKey given a KeyID
|
||||
func getKey(s LimitedFileStore, passphraseRetriever PassphraseRetriever, name string) (data.PrivateKey, error) {
|
||||
func getKey(s LimitedFileStore, passphraseRetriever passphrase.Retriever, name string) (data.PrivateKey, error) {
|
||||
keyAlias, err := getKeyAlias(s, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
Loading…
Reference in New Issue