mirror of https://github.com/docker/cli.git
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:
parent
59449a57f8
commit
ee218fa89e
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue