mirror of https://github.com/docker/cli.git
print a big warning about cleartext passwords
Signed-off-by: Tycho Andersen <tycho@docker.com>
This commit is contained in:
parent
db11cffe37
commit
4290df3958
|
@ -17,6 +17,11 @@ import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const unencryptedWarning = `WARNING! Your password will be stored unencrypted in %s.
|
||||||
|
Configure a credential helper to remove this warning. See
|
||||||
|
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
|
||||||
|
`
|
||||||
|
|
||||||
type loginOptions struct {
|
type loginOptions struct {
|
||||||
serverAddress string
|
serverAddress string
|
||||||
user string
|
user string
|
||||||
|
@ -50,6 +55,29 @@ func NewLoginCommand(dockerCli command.Cli) *cobra.Command {
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// unencryptedPrompt prompts the user to find out whether they want to continue
|
||||||
|
// with insecure credential storage. If stdin is not a terminal, we assume they
|
||||||
|
// want it (sadly), because people may have been scripting insecure logins and
|
||||||
|
// we don't want to break them. Maybe they'll see the warning in their logs and
|
||||||
|
// fix things.
|
||||||
|
func unencryptedPrompt(dockerCli command.Streams, filename string) error {
|
||||||
|
fmt.Fprintln(dockerCli.Err(), fmt.Sprintf(unencryptedWarning, filename))
|
||||||
|
|
||||||
|
if dockerCli.In().IsTerminal() {
|
||||||
|
if command.PromptForConfirmation(dockerCli.In(), dockerCli.Out(), "") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return errors.Errorf("User refused unencrypted credentials storage.")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type isFileStore interface {
|
||||||
|
IsFileStore() bool
|
||||||
|
GetFilename() string
|
||||||
|
}
|
||||||
|
|
||||||
func verifyloginOptions(dockerCli command.Cli, opts *loginOptions) error {
|
func verifyloginOptions(dockerCli command.Cli, opts *loginOptions) error {
|
||||||
if opts.password != "" {
|
if opts.password != "" {
|
||||||
fmt.Fprintln(dockerCli.Err(), "WARNING! Using --password via the CLI is insecure. Use --password-stdin.")
|
fmt.Fprintln(dockerCli.Err(), "WARNING! Using --password via the CLI is insecure. Use --password-stdin.")
|
||||||
|
@ -74,7 +102,7 @@ func verifyloginOptions(dockerCli command.Cli, opts *loginOptions) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func runLogin(dockerCli command.Cli, opts loginOptions) error {
|
func runLogin(dockerCli command.Cli, opts loginOptions) error { //nolint: gocyclo
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
clnt := dockerCli.Client()
|
clnt := dockerCli.Client()
|
||||||
if err := verifyloginOptions(dockerCli, &opts); err != nil {
|
if err := verifyloginOptions(dockerCli, &opts); err != nil {
|
||||||
|
@ -113,7 +141,18 @@ func runLogin(dockerCli command.Cli, opts loginOptions) error {
|
||||||
authConfig.Password = ""
|
authConfig.Password = ""
|
||||||
authConfig.IdentityToken = response.IdentityToken
|
authConfig.IdentityToken = response.IdentityToken
|
||||||
}
|
}
|
||||||
if err := dockerCli.ConfigFile().GetCredentialsStore(serverAddress).Store(*authConfig); err != nil {
|
|
||||||
|
creds := dockerCli.ConfigFile().GetCredentialsStore(serverAddress)
|
||||||
|
|
||||||
|
store, isDefault := creds.(isFileStore)
|
||||||
|
if isDefault {
|
||||||
|
err = unencryptedPrompt(dockerCli, store.GetFilename())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := creds.Store(*authConfig); err != nil {
|
||||||
return errors.Errorf("Error saving credentials: %v", err)
|
return errors.Errorf("Error saving credentials: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -307,3 +307,8 @@ func (configFile *ConfigFile) GetAllCredentials() (map[string]types.AuthConfig,
|
||||||
}
|
}
|
||||||
return auths, nil
|
return auths, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetFilename returns the file name that this config file is based on.
|
||||||
|
func (configFile *ConfigFile) GetFilename() string {
|
||||||
|
return configFile.Filename
|
||||||
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
type store interface {
|
type store interface {
|
||||||
Save() error
|
Save() error
|
||||||
GetAuthConfigs() map[string]types.AuthConfig
|
GetAuthConfigs() map[string]types.AuthConfig
|
||||||
|
GetFilename() string
|
||||||
}
|
}
|
||||||
|
|
||||||
// fileStore implements a credentials store using
|
// fileStore implements a credentials store using
|
||||||
|
@ -53,3 +54,11 @@ func (c *fileStore) Store(authConfig types.AuthConfig) error {
|
||||||
c.file.GetAuthConfigs()[authConfig.ServerAddress] = authConfig
|
c.file.GetAuthConfigs()[authConfig.ServerAddress] = authConfig
|
||||||
return c.file.Save()
|
return c.file.Save()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *fileStore) GetFilename() string {
|
||||||
|
return c.file.GetFilename()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *fileStore) IsFileStore() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
|
@ -20,6 +20,10 @@ func (f *fakeStore) GetAuthConfigs() map[string]types.AuthConfig {
|
||||||
return f.configs
|
return f.configs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *fakeStore) GetFilename() string {
|
||||||
|
return "/tmp/docker-fakestore"
|
||||||
|
}
|
||||||
|
|
||||||
func newStore(auths map[string]types.AuthConfig) store {
|
func newStore(auths map[string]types.AuthConfig) store {
|
||||||
return &fakeStore{configs: auths}
|
return &fakeStore{configs: auths}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue