diff --git a/cli/config/config.go b/cli/config/config.go index 31ad117d41..b7c05c3f86 100644 --- a/cli/config/config.go +++ b/cli/config/config.go @@ -19,7 +19,7 @@ const ( // ConfigFileName is the name of config file ConfigFileName = "config.json" configFileDir = ".docker" - oldConfigfile = ".dockercfg" + oldConfigfile = ".dockercfg" // Deprecated: remove once we stop printing deprecation warning contextsDir = "contexts" ) @@ -84,16 +84,6 @@ func Path(p ...string) (string, error) { return path, nil } -// LegacyLoadFromReader is a convenience function that creates a ConfigFile object from -// a non-nested reader -func LegacyLoadFromReader(configData io.Reader) (*configfile.ConfigFile, error) { - configFile := configfile.ConfigFile{ - AuthConfigs: make(map[string]types.AuthConfig), - } - err := configFile.LegacyLoadFromReader(configData) - return &configFile, err -} - // LoadFromReader is a convenience function that creates a ConfigFile object from // a reader func LoadFromReader(configData io.Reader) (*configfile.ConfigFile, error) { @@ -140,12 +130,8 @@ func load(configDir string) (*configfile.ConfigFile, bool, error) { // Can't find latest config file so check for the old one filename = filepath.Join(getHomeDir(), oldConfigfile) - if file, err := os.Open(filename); err == nil { + if _, err := os.Stat(filename); err == nil { printLegacyFileWarning = true - defer file.Close() - if err := configFile.LegacyLoadFromReader(file); err != nil { - return configFile, printLegacyFileWarning, errors.Wrap(err, filename) - } } return configFile, printLegacyFileWarning, nil } @@ -158,7 +144,7 @@ func LoadDefaultConfigFile(stderr io.Writer) *configfile.ConfigFile { fmt.Fprintf(stderr, "WARNING: Error loading config file: %v\n", err) } 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") + _, _ = fmt.Fprintln(stderr, "WARNING: Support for the legacy ~/.dockercfg configuration file and file-format has been removed and the configuration file will be ignored") } if !configFile.ContainsAuth() { configFile.CredentialsStore = credentials.DetectDefaultStore(configFile.CredentialsStore) diff --git a/cli/config/config_test.go b/cli/config/config_test.go index c6858e9ffd..9bd5c15e92 100644 --- a/cli/config/config_test.go +++ b/cli/config/config_test.go @@ -96,115 +96,6 @@ func TestEmptyJSON(t *testing.T) { saveConfigAndValidateNewFormat(t, config, tmpHome) } -func TestOldInvalidsAuth(t *testing.T) { - invalids := map[string]string{ - `username = test`: "The Auth config file is empty", - `username -password`: "Invalid Auth config file", - `username = test -email`: "Invalid auth configuration file", - } - - resetHomeDir() - tmpHome := t.TempDir() - defer env.Patch(t, homeKey, tmpHome)() - - for content, expectedError := range invalids { - fn := filepath.Join(tmpHome, oldConfigfile) - err := os.WriteFile(fn, []byte(content), 0600) - assert.NilError(t, err) - - _, err = Load(tmpHome) - assert.ErrorContains(t, err, expectedError) - } -} - -func TestOldValidAuth(t *testing.T) { - resetHomeDir() - tmpHome := t.TempDir() - defer env.Patch(t, homeKey, tmpHome)() - - fn := filepath.Join(tmpHome, oldConfigfile) - js := `username = am9lam9lOmhlbGxv - email = user@example.com` - err := os.WriteFile(fn, []byte(js), 0600) - assert.NilError(t, err) - - config, err := Load(tmpHome) - assert.NilError(t, err) - - // defaultIndexserver is https://index.docker.io/v1/ - ac := config.AuthConfigs["https://index.docker.io/v1/"] - assert.Equal(t, ac.Username, "joejoe") - assert.Equal(t, ac.Password, "hello") - - // Now save it and make sure it shows up in new form - configStr := saveConfigAndValidateNewFormat(t, config, tmpHome) - - expConfStr := `{ - "auths": { - "https://index.docker.io/v1/": { - "auth": "am9lam9lOmhlbGxv" - } - } -}` - - assert.Check(t, is.Equal(expConfStr, configStr)) -} - -func TestOldJSONInvalid(t *testing.T) { - resetHomeDir() - tmpHome := t.TempDir() - defer env.Patch(t, homeKey, tmpHome)() - - fn := filepath.Join(tmpHome, oldConfigfile) - js := `{"https://index.docker.io/v1/":{"auth":"test","email":"user@example.com"}}` - if err := os.WriteFile(fn, []byte(js), 0600); err != nil { - t.Fatal(err) - } - - config, err := Load(tmpHome) - // Use Contains instead of == since the file name will change each time - if err == nil || !strings.Contains(err.Error(), "Invalid auth configuration file") { - t.Fatalf("Expected an error got : %v, %v", config, err) - } -} - -func TestOldJSON(t *testing.T) { - resetHomeDir() - tmpHome := t.TempDir() - defer env.Patch(t, homeKey, tmpHome)() - - fn := filepath.Join(tmpHome, oldConfigfile) - js := `{"https://index.docker.io/v1/":{"auth":"am9lam9lOmhlbGxv","email":"user@example.com"}}` - if err := os.WriteFile(fn, []byte(js), 0600); err != nil { - t.Fatal(err) - } - - config, err := Load(tmpHome) - assert.NilError(t, err) - - ac := config.AuthConfigs["https://index.docker.io/v1/"] - assert.Equal(t, ac.Username, "joejoe") - assert.Equal(t, ac.Password, "hello") - - // Now save it and make sure it shows up in new form - configStr := saveConfigAndValidateNewFormat(t, config, tmpHome) - - expConfStr := `{ - "auths": { - "https://index.docker.io/v1/": { - "auth": "am9lam9lOmhlbGxv", - "email": "user@example.com" - } - } -}` - - if configStr != expConfStr { - t.Fatalf("Should have save in new form: \n'%s'\n not \n'%s'\n", configStr, expConfStr) - } -} - 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)) @@ -218,15 +109,8 @@ func TestOldJSONFallbackDeprecationWarning(t *testing.T) { 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")) + expected.AuthConfigs = map[string]types.AuthConfig{} + assert.Assert(t, strings.Contains(buffer.String(), "WARNING: Support for the legacy ~/.dockercfg configuration file and file-format has been removed and the configuration file will be ignored")) assert.Check(t, is.DeepEqual(expected, configFile)) } @@ -418,17 +302,6 @@ func TestJSONReaderNoFile(t *testing.T) { assert.Equal(t, ac.Password, "hello") } -func TestOldJSONReaderNoFile(t *testing.T) { - js := `{"https://index.docker.io/v1/":{"auth":"am9lam9lOmhlbGxv","email":"user@example.com"}}` - - config, err := LegacyLoadFromReader(strings.NewReader(js)) - assert.NilError(t, err) - - ac := config.AuthConfigs["https://index.docker.io/v1/"] - assert.Equal(t, ac.Username, "joejoe") - assert.Equal(t, ac.Password, "hello") -} - func TestJSONWithPsFormatNoFile(t *testing.T) { js := `{ "auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv", "email": "user@example.com" } }, @@ -474,36 +347,6 @@ func TestJSONSaveWithNoFile(t *testing.T) { } } -func TestLegacyJSONSaveWithNoFile(t *testing.T) { - js := `{"https://index.docker.io/v1/":{"auth":"am9lam9lOmhlbGxv","email":"user@example.com"}}` - config, err := LegacyLoadFromReader(strings.NewReader(js)) - assert.NilError(t, err) - err = config.Save() - assert.ErrorContains(t, err, "with empty filename") - - tmpHome := t.TempDir() - fn := filepath.Join(tmpHome, ConfigFileName) - f, _ := os.OpenFile(fn, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) - defer f.Close() - - assert.NilError(t, config.SaveToWriter(f)) - buf, err := os.ReadFile(filepath.Join(tmpHome, ConfigFileName)) - assert.NilError(t, err) - - expConfStr := `{ - "auths": { - "https://index.docker.io/v1/": { - "auth": "am9lam9lOmhlbGxv", - "email": "user@example.com" - } - } -}` - - if string(buf) != expConfStr { - t.Fatalf("Should have save in new form: \n%s\n not \n%s", string(buf), expConfStr) - } -} - func TestLoadDefaultConfigFile(t *testing.T) { dir := setupConfigDir(t) buffer := new(bytes.Buffer) diff --git a/cli/config/configfile/file.go b/cli/config/configfile/file.go index 1ef85cdc98..4496059839 100644 --- a/cli/config/configfile/file.go +++ b/cli/config/configfile/file.go @@ -14,13 +14,6 @@ import ( "github.com/sirupsen/logrus" ) -const ( - // This constant is only used for really old config files when the - // URL wasn't saved as part of the config file and it was just - // assumed to be this value. - defaultIndexServer = "https://index.docker.io/v1/" -) - // ConfigFile ~/.docker/config.json file info type ConfigFile struct { AuthConfigs map[string]types.AuthConfig `json:"auths"` @@ -71,44 +64,6 @@ func New(fn string) *ConfigFile { } } -// LegacyLoadFromReader reads the non-nested configuration data given and sets up the -// auth config information with given directory and populates the receiver object -func (configFile *ConfigFile) LegacyLoadFromReader(configData io.Reader) error { - b, err := io.ReadAll(configData) - if err != nil { - return err - } - - if err := json.Unmarshal(b, &configFile.AuthConfigs); err != nil { - arr := strings.Split(string(b), "\n") - if len(arr) < 2 { - return errors.Errorf("The Auth config file is empty") - } - authConfig := types.AuthConfig{} - origAuth := strings.Split(arr[0], " = ") - if len(origAuth) != 2 { - return errors.Errorf("Invalid Auth config file") - } - authConfig.Username, authConfig.Password, err = decodeAuth(origAuth[1]) - if err != nil { - return err - } - authConfig.ServerAddress = defaultIndexServer - configFile.AuthConfigs[defaultIndexServer] = authConfig - } else { - for k, authConfig := range configFile.AuthConfigs { - authConfig.Username, authConfig.Password, err = decodeAuth(authConfig.Auth) - if err != nil { - return err - } - authConfig.Auth = "" - authConfig.ServerAddress = k - configFile.AuthConfigs[k] = authConfig - } - } - return nil -} - // LoadFromReader reads the configuration data given and sets up the auth config // information with given directory and populates the receiver object func (configFile *ConfigFile) LoadFromReader(configData io.Reader) error { diff --git a/docs/deprecated.md b/docs/deprecated.md index b7142d8740..59b5b303bb 100644 --- a/docs/deprecated.md +++ b/docs/deprecated.md @@ -59,7 +59,7 @@ Removed | [Linux containers on Windows (LCOW)](#linux-containers-on-windows-l Deprecated | [BLKIO weight options with cgroups v1](#blkio-weight-options-with-cgroups-v1) | v20.10 | - Deprecated | [Kernel memory limit](#kernel-memory-limit) | v20.10 | - Deprecated | [Classic Swarm and overlay networks using external key/value stores](#classic-swarm-and-overlay-networks-using-cluster-store) | v20.10 | - -Deprecated | [Support for the legacy `~/.dockercfg` configuration file for authentication](#support-for-legacy-dockercfg-configuration-files) | v20.10 | - +Removed | [Support for the legacy `~/.dockercfg` configuration file for authentication](#support-for-legacy-dockercfg-configuration-files) | v20.10 | v21.xx Deprecated | [CLI plugins support](#cli-plugins-support) | v20.10 | - Deprecated | [Dockerfile legacy `ENV name value` syntax](#dockerfile-legacy-env-name-value-syntax) | v20.10 | - Removed | [`docker build --stream` flag (experimental)](#docker-build---stream-flag-experimental) | v20.10 | v20.10 @@ -298,6 +298,7 @@ deprecated, and will be disabled or removed in a future release. ### Support for legacy `~/.dockercfg` configuration files **Deprecated in Release: v20.10** +**Removed in Release: v21.xx** 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 @@ -307,8 +308,11 @@ 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. +Docker v1.7.0 has created this file, support for this file, and its format has +been removed. + +A warning is printed in situations where the CLI would fall back to the old file, +notifying the user that the legacy file is present, but ignored. ### Configuration options for experimental CLI features