Deprecation: config: remove support for old ~/.dockercfg

The `~/.dockercfg` file was replaced by `~/.docker/config.json` in 2015
(github.com/docker/docker/commit/18c9b6c6455f116ae59cde8544413b3d7d294a5e),
but the CLI still falls back to checking if this file exists if no current
(`~/.docker/config.json`) file was found.

Given that no version of the CLI since Docker v1.7.0 has created this file,
and if such a file exists, it means someone hasn't re-authenticated for
5 years, it's probably safe to remove this fallback.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn 2020-05-07 19:37:16 +02:00
parent 59449a57f8
commit ee218fa89e
No known key found for this signature in database
GPG Key ID: 76698F39D527CE8C
4 changed files with 12 additions and 224 deletions

View File

@ -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)

View File

@ -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)

View File

@ -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 {

View File

@ -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