config: print deprecation warning when falling back to ~/.dockercfg

Relates to the deprecation, added in 3c0a167ed5

The docker CLI up until v1.7.0 used the `~/.dockercfg` file to store credentials
after authenticating to a registry (`docker login`). Docker v1.7.0 replaced this
file with a new CLI configuration file, located in `~/.docker/config.json`. When
implementing the new configuration file, the old file (and file-format) was kept
as a fall-back, to assist existing users with migrating to the new file.

Given that the old file format encourages insecure storage of credentials
(credentials are stored unencrypted), and that no version of the CLI since
Docker v1.7.0 has created this file, the file is marked deprecated, and support
for this file will be removed in a future release.

This patch adds a deprecation warning, which is printed if the CLI falls back
to using the deprecated ~/.dockercfg file.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit b83bc67136)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn 2020-07-16 12:01:54 +02:00
parent 55c4c88966
commit 4571d90f20
No known key found for this signature in database
GPG Key ID: 76698F39D527CE8C
3 changed files with 57 additions and 3 deletions

View File

@ -24,12 +24,12 @@ const (
) )
var ( var (
initConfigDir sync.Once initConfigDir = new(sync.Once)
configDir string configDir string
homeDir string homeDir string
) )
// resetHomeDir is used in testing to resets the "homeDir" package variable to // resetHomeDir is used in testing to reset the "homeDir" package variable to
// force re-lookup of the home directory between tests. // force re-lookup of the home directory between tests.
func resetHomeDir() { func resetHomeDir() {
homeDir = "" homeDir = ""
@ -42,6 +42,13 @@ func getHomeDir() string {
return homeDir return homeDir
} }
// resetConfigDir is used in testing to reset the "configDir" package variable
// and its sync.Once to force re-lookup between tests.
func resetConfigDir() {
configDir = ""
initConfigDir = new(sync.Once)
}
func setConfigDir() { func setConfigDir() {
if configDir != "" { if configDir != "" {
return return
@ -97,10 +104,20 @@ func LoadFromReader(configData io.Reader) (*configfile.ConfigFile, error) {
return &configFile, err return &configFile, err
} }
// TODO remove this temporary hack, which is used to warn about the deprecated ~/.dockercfg file
var (
mutex sync.RWMutex
printLegacyFileWarning bool
)
// Load reads the configuration files in the given directory, and sets up // Load reads the configuration files in the given directory, and sets up
// the auth config information and returns values. // the auth config information and returns values.
// FIXME: use the internal golang config parser // FIXME: use the internal golang config parser
func Load(configDir string) (*configfile.ConfigFile, error) { func Load(configDir string) (*configfile.ConfigFile, error) {
mutex.Lock()
printLegacyFileWarning = false
mutex.Unlock()
if configDir == "" { if configDir == "" {
configDir = Dir() configDir = Dir()
} }
@ -125,6 +142,9 @@ func Load(configDir string) (*configfile.ConfigFile, error) {
// Can't find latest config file so check for the old one // Can't find latest config file so check for the old one
filename = filepath.Join(getHomeDir(), oldConfigfile) filename = filepath.Join(getHomeDir(), oldConfigfile)
if file, err := os.Open(filename); err == nil { if file, err := os.Open(filename); err == nil {
mutex.Lock()
printLegacyFileWarning = true
mutex.Unlock()
defer file.Close() defer file.Close()
if err := configFile.LegacyLoadFromReader(file); err != nil { if err := configFile.LegacyLoadFromReader(file); err != nil {
return configFile, errors.Wrap(err, filename) return configFile, errors.Wrap(err, filename)
@ -140,6 +160,11 @@ func LoadDefaultConfigFile(stderr io.Writer) *configfile.ConfigFile {
if err != nil { if err != nil {
fmt.Fprintf(stderr, "WARNING: Error loading config file: %v\n", err) fmt.Fprintf(stderr, "WARNING: Error loading config file: %v\n", err)
} }
mutex.RLock()
defer mutex.RUnlock()
if printLegacyFileWarning {
_, _ = fmt.Fprintln(stderr, "WARNING: Support for the legacy ~/.dockercfg configuration file and file-format is deprecated and will be removed in an upcoming release")
}
if !configFile.ContainsAuth() { if !configFile.ContainsAuth() {
configFile.CredentialsStore = credentials.DetectDefaultStore(configFile.CredentialsStore) configFile.CredentialsStore = credentials.DetectDefaultStore(configFile.CredentialsStore)
} }

View File

@ -12,9 +12,11 @@ import (
"github.com/docker/cli/cli/config/configfile" "github.com/docker/cli/cli/config/configfile"
"github.com/docker/cli/cli/config/credentials" "github.com/docker/cli/cli/config/credentials"
"github.com/docker/cli/cli/config/types"
"gotest.tools/v3/assert" "gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp" is "gotest.tools/v3/assert/cmp"
"gotest.tools/v3/env" "gotest.tools/v3/env"
"gotest.tools/v3/fs"
) )
var homeKey = "HOME" var homeKey = "HOME"
@ -223,6 +225,31 @@ func TestOldJSON(t *testing.T) {
} }
} }
func TestOldJSONFallbackDeprecationWarning(t *testing.T) {
js := `{"https://index.docker.io/v1/":{"auth":"am9lam9lOmhlbGxv","email":"user@example.com"}}`
tmpHome := fs.NewDir(t, t.Name(), fs.WithFile(oldConfigfile, js))
defer tmpHome.Remove()
defer env.PatchAll(t, map[string]string{homeKey: tmpHome.Path(), "DOCKER_CONFIG": ""})()
// reset the homeDir, configDir, and its sync.Once, to force them being resolved again
resetHomeDir()
resetConfigDir()
buffer := new(bytes.Buffer)
configFile := LoadDefaultConfigFile(buffer)
expected := configfile.New(tmpHome.Join(configFileDir, ConfigFileName))
expected.AuthConfigs = map[string]types.AuthConfig{
"https://index.docker.io/v1/": {
Username: "joejoe",
Password: "hello",
Email: "user@example.com",
ServerAddress: "https://index.docker.io/v1/",
},
}
assert.Assert(t, strings.Contains(buffer.String(), "WARNING: Support for the legacy ~/.dockercfg configuration file and file-format is deprecated and will be removed in an upcoming release"))
assert.Check(t, is.DeepEqual(expected, configFile))
}
func TestNewJSON(t *testing.T) { func TestNewJSON(t *testing.T) {
tmpHome, err := ioutil.TempDir("", "config-test") tmpHome, err := ioutil.TempDir("", "config-test")
assert.NilError(t, err) assert.NilError(t, err)

View File

@ -17,7 +17,9 @@ import (
func TestClientDebugEnabled(t *testing.T) { func TestClientDebugEnabled(t *testing.T) {
defer debug.Disable() defer debug.Disable()
tcmd := newDockerCommand(&command.DockerCli{}) cli, err := command.NewDockerCli()
assert.NilError(t, err)
tcmd := newDockerCommand(cli)
tcmd.SetFlag("debug", "true") tcmd.SetFlag("debug", "true")
cmd, _, err := tcmd.HandleGlobalFlags() cmd, _, err := tcmd.HandleGlobalFlags()
assert.NilError(t, err) assert.NilError(t, err)