diff --git a/cli/config/credentials/default_store_linux.go b/cli/config/credentials/default_store_linux.go index 9b619c0279..a9012c6d4a 100644 --- a/cli/config/credentials/default_store_linux.go +++ b/cli/config/credentials/default_store_linux.go @@ -1,11 +1,11 @@ package credentials import ( - "github.com/docker/docker-credential-helpers/pass" + "os/exec" ) func defaultCredentialsStore() string { - if pass.PassInitialized { + if _, err := exec.LookPath("pass"); err == nil { return "pass" } diff --git a/vendor.conf b/vendor.conf index b09b875606..2bc9751237 100755 --- a/vendor.conf +++ b/vendor.conf @@ -7,7 +7,7 @@ github.com/cpuguy83/go-md2man v1.0.8 github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76 github.com/docker/distribution 83389a148052d74ac602f5f1d62f86ff2f3c4aa5 github.com/docker/docker c752b0991e31ba9869ab6a0661af57e9423874fb -github.com/docker/docker-credential-helpers 3c90bd29a46b943b2a9842987b58fb91a7c1819b +github.com/docker/docker-credential-helpers 5241b46610f2491efdf9d1c85f1ddf5b02f6d962 # the docker/go package contains a customized version of canonical/json # and is used by Notary. The package is periodically rebased on current Go versions. github.com/docker/go d30aec9fd63c35133f8f79c3412ad91a3b08be06 diff --git a/vendor/github.com/docker/docker-credential-helpers/README.md b/vendor/github.com/docker/docker-credential-helpers/README.md index e795f7c5e4..f9cbc3fb5f 100644 --- a/vendor/github.com/docker/docker-credential-helpers/README.md +++ b/vendor/github.com/docker/docker-credential-helpers/README.md @@ -55,6 +55,12 @@ You can see examples of each function in the [client](https://godoc.org/github.c 1. osxkeychain: Provides a helper to use the OS X keychain as credentials store. 2. secretservice: Provides a helper to use the D-Bus secret service as credentials store. 3. wincred: Provides a helper to use Windows credentials manager as store. +4. pass: Provides a helper to use `pass` as credentials store. + +#### Note + +`pass` needs to be configured for `docker-credential-pass` to work properly. +It must be initialized with a `gpg2` key ID. Make sure your GPG key exists is in `gpg2` keyring as `pass` uses `gpg2` instead of the regular `gpg`. ## Development diff --git a/vendor/github.com/docker/docker-credential-helpers/credentials/version.go b/vendor/github.com/docker/docker-credential-helpers/credentials/version.go index a4834dd41a..033a5fee55 100644 --- a/vendor/github.com/docker/docker-credential-helpers/credentials/version.go +++ b/vendor/github.com/docker/docker-credential-helpers/credentials/version.go @@ -1,4 +1,4 @@ package credentials // Version holds a string describing the current version -const Version = "0.5.2" +const Version = "0.6.0" diff --git a/vendor/github.com/docker/docker-credential-helpers/pass/pass_linux.go b/vendor/github.com/docker/docker-credential-helpers/pass/pass_linux.go deleted file mode 100644 index 2ddfa4a6ec..0000000000 --- a/vendor/github.com/docker/docker-credential-helpers/pass/pass_linux.go +++ /dev/null @@ -1,208 +0,0 @@ -// A `pass` based credential helper. Passwords are stored as arguments to pass -// of the form: "$PASS_FOLDER/base64-url(serverURL)/username". We base64-url -// encode the serverURL, because under the hood pass uses files and folders, so -// /s will get translated into additional folders. -package pass - -import ( - "encoding/base64" - "errors" - "fmt" - "io/ioutil" - "os" - "os/exec" - "path" - "strings" - - "github.com/docker/docker-credential-helpers/credentials" -) - -const PASS_FOLDER = "docker-credential-helpers" - -var ( - PassInitialized bool -) - -func init() { - PassInitialized = exec.Command("pass").Run() == nil -} - -func runPass(stdinContent string, args ...string) (string, error) { - cmd := exec.Command("pass", args...) - - stdin, err := cmd.StdinPipe() - if err != nil { - return "", err - } - defer stdin.Close() - - stderr, err := cmd.StderrPipe() - if err != nil { - return "", err - } - defer stderr.Close() - - stdout, err := cmd.StdoutPipe() - if err != nil { - return "", err - } - defer stdout.Close() - - err = cmd.Start() - if err != nil { - return "", err - } - - _, err = stdin.Write([]byte(stdinContent)) - if err != nil { - return "", err - } - stdin.Close() - - errContent, err := ioutil.ReadAll(stderr) - if err != nil { - return "", fmt.Errorf("error reading stderr: %s", err) - } - - result, err := ioutil.ReadAll(stdout) - if err != nil { - return "", fmt.Errorf("Error reading stdout: %s", err) - } - - cmdErr := cmd.Wait() - if cmdErr != nil { - return "", fmt.Errorf("%s: %s", cmdErr, errContent) - } - - return string(result), nil -} - -// Pass handles secrets using Linux secret-service as a store. -type Pass struct{} - -// Add adds new credentials to the keychain. -func (h Pass) Add(creds *credentials.Credentials) error { - if !PassInitialized { - return errors.New("pass store is uninitialized") - } - - if creds == nil { - return errors.New("missing credentials") - } - - encoded := base64.URLEncoding.EncodeToString([]byte(creds.ServerURL)) - - _, err := runPass(creds.Secret, "insert", "-f", "-m", path.Join(PASS_FOLDER, encoded, creds.Username)) - return err -} - -// Delete removes credentials from the store. -func (h Pass) Delete(serverURL string) error { - if !PassInitialized { - return errors.New("pass store is uninitialized") - } - - if serverURL == "" { - return errors.New("missing server url") - } - - encoded := base64.URLEncoding.EncodeToString([]byte(serverURL)) - _, err := runPass("", "rm", "-rf", path.Join(PASS_FOLDER, encoded)) - return err -} - -// listPassDir lists all the contents of a directory in the password store. -// Pass uses fancy unicode to emit stuff to stdout, so rather than try -// and parse this, let's just look at the directory structure instead. -func listPassDir(args ...string) ([]os.FileInfo, error) { - passDir := os.ExpandEnv("$HOME/.password-store") - for _, e := range os.Environ() { - parts := strings.SplitN(e, "=", 2) - if len(parts) < 2 { - continue - } - - if parts[0] != "PASSWORD_STORE_DIR" { - continue - } - - passDir = parts[1] - break - } - - p := path.Join(append([]string{passDir, PASS_FOLDER}, args...)...) - contents, err := ioutil.ReadDir(p) - if err != nil { - if os.IsNotExist(err) { - return []os.FileInfo{}, nil - } - - return nil, err - } - - return contents, nil -} - -// Get returns the username and secret to use for a given registry server URL. -func (h Pass) Get(serverURL string) (string, string, error) { - if !PassInitialized { - return "", "", errors.New("pass store is uninitialized") - } - - if serverURL == "" { - return "", "", errors.New("missing server url") - } - - encoded := base64.URLEncoding.EncodeToString([]byte(serverURL)) - - usernames, err := listPassDir(encoded) - if err != nil { - return "", "", err - } - - if len(usernames) < 1 { - return "", "", fmt.Errorf("no usernames for %s", serverURL) - } - - actual := strings.TrimSuffix(usernames[0].Name(), ".gpg") - secret, err := runPass("", "show", path.Join(PASS_FOLDER, encoded, actual)) - return actual, secret, err -} - -// List returns the stored URLs and corresponding usernames for a given credentials label -func (h Pass) List() (map[string]string, error) { - if !PassInitialized { - return nil, errors.New("pass store is uninitialized") - } - - servers, err := listPassDir() - if err != nil { - return nil, err - } - - resp := map[string]string{} - - for _, server := range servers { - if !server.IsDir() { - continue - } - - serverURL, err := base64.URLEncoding.DecodeString(server.Name()) - if err != nil { - return nil, err - } - - usernames, err := listPassDir(server.Name()) - if err != nil { - return nil, err - } - - if len(usernames) < 1 { - return nil, fmt.Errorf("no usernames for %s", serverURL) - } - - resp[string(serverURL)] = strings.TrimSuffix(usernames[0].Name(), ".gpg") - } - - return resp, nil -}