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>
173 lines
4.7 KiB
Go
173 lines
4.7 KiB
Go
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"sync"
|
|
|
|
"github.com/docker/cli/cli/config/configfile"
|
|
"github.com/docker/cli/cli/config/credentials"
|
|
"github.com/docker/cli/cli/config/types"
|
|
"github.com/docker/docker/pkg/homedir"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
const (
|
|
// ConfigFileName is the name of config file
|
|
ConfigFileName = "config.json"
|
|
configFileDir = ".docker"
|
|
oldConfigfile = ".dockercfg"
|
|
contextsDir = "contexts"
|
|
)
|
|
|
|
var (
|
|
initConfigDir = new(sync.Once)
|
|
configDir string
|
|
homeDir string
|
|
)
|
|
|
|
// resetHomeDir is used in testing to reset the "homeDir" package variable to
|
|
// force re-lookup of the home directory between tests.
|
|
func resetHomeDir() {
|
|
homeDir = ""
|
|
}
|
|
|
|
func getHomeDir() string {
|
|
if homeDir == "" {
|
|
homeDir = homedir.Get()
|
|
}
|
|
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() {
|
|
if configDir != "" {
|
|
return
|
|
}
|
|
configDir = os.Getenv("DOCKER_CONFIG")
|
|
if configDir == "" {
|
|
configDir = filepath.Join(getHomeDir(), configFileDir)
|
|
}
|
|
}
|
|
|
|
// Dir returns the directory the configuration file is stored in
|
|
func Dir() string {
|
|
initConfigDir.Do(setConfigDir)
|
|
return configDir
|
|
}
|
|
|
|
// ContextStoreDir returns the directory the docker contexts are stored in
|
|
func ContextStoreDir() string {
|
|
return filepath.Join(Dir(), contextsDir)
|
|
}
|
|
|
|
// SetDir sets the directory the configuration file is stored in
|
|
func SetDir(dir string) {
|
|
configDir = filepath.Clean(dir)
|
|
}
|
|
|
|
// Path returns the path to a file relative to the config dir
|
|
func Path(p ...string) (string, error) {
|
|
path := filepath.Join(append([]string{Dir()}, p...)...)
|
|
if !strings.HasPrefix(path, Dir()+string(filepath.Separator)) {
|
|
return "", errors.Errorf("path %q is outside of root config directory %q", path, Dir())
|
|
}
|
|
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) {
|
|
configFile := configfile.ConfigFile{
|
|
AuthConfigs: make(map[string]types.AuthConfig),
|
|
}
|
|
err := configFile.LoadFromReader(configData)
|
|
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
|
|
// the auth config information and returns values.
|
|
// FIXME: use the internal golang config parser
|
|
func Load(configDir string) (*configfile.ConfigFile, error) {
|
|
mutex.Lock()
|
|
printLegacyFileWarning = false
|
|
mutex.Unlock()
|
|
|
|
if configDir == "" {
|
|
configDir = Dir()
|
|
}
|
|
|
|
filename := filepath.Join(configDir, ConfigFileName)
|
|
configFile := configfile.New(filename)
|
|
|
|
// Try happy path first - latest config file
|
|
if file, err := os.Open(filename); err == nil {
|
|
defer file.Close()
|
|
err = configFile.LoadFromReader(file)
|
|
if err != nil {
|
|
err = errors.Wrap(err, filename)
|
|
}
|
|
return configFile, err
|
|
} else if !os.IsNotExist(err) {
|
|
// if file is there but we can't stat it for any reason other
|
|
// than it doesn't exist then stop
|
|
return configFile, errors.Wrap(err, filename)
|
|
}
|
|
|
|
// 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 {
|
|
mutex.Lock()
|
|
printLegacyFileWarning = true
|
|
mutex.Unlock()
|
|
defer file.Close()
|
|
if err := configFile.LegacyLoadFromReader(file); err != nil {
|
|
return configFile, errors.Wrap(err, filename)
|
|
}
|
|
}
|
|
return configFile, nil
|
|
}
|
|
|
|
// LoadDefaultConfigFile attempts to load the default config file and returns
|
|
// an initialized ConfigFile struct if none is found.
|
|
func LoadDefaultConfigFile(stderr io.Writer) *configfile.ConfigFile {
|
|
configFile, err := Load(Dir())
|
|
if err != nil {
|
|
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() {
|
|
configFile.CredentialsStore = credentials.DetectDefaultStore(configFile.CredentialsStore)
|
|
}
|
|
return configFile
|
|
}
|