github.com/damirazo/docker@v1.9.0/cliconfig/config.go (about) 1 package cliconfig 2 3 import ( 4 "encoding/base64" 5 "encoding/json" 6 "fmt" 7 "io" 8 "io/ioutil" 9 "os" 10 "path/filepath" 11 "strings" 12 13 "github.com/docker/docker/pkg/homedir" 14 "github.com/docker/docker/pkg/system" 15 ) 16 17 const ( 18 // ConfigFileName is the name of config file 19 ConfigFileName = "config.json" 20 oldConfigfile = ".dockercfg" 21 22 // This constant is only used for really old config files when the 23 // URL wasn't saved as part of the config file and it was just 24 // assumed to be this value. 25 defaultIndexserver = "https://index.docker.io/v1/" 26 ) 27 28 var ( 29 configDir = os.Getenv("DOCKER_CONFIG") 30 ) 31 32 func init() { 33 if configDir == "" { 34 configDir = filepath.Join(homedir.Get(), ".docker") 35 } 36 } 37 38 // ConfigDir returns the directory the configuration file is stored in 39 func ConfigDir() string { 40 return configDir 41 } 42 43 // SetConfigDir sets the directory the configuration file is stored in 44 func SetConfigDir(dir string) { 45 configDir = dir 46 } 47 48 // AuthConfig contains authorization information for connecting to a Registry 49 type AuthConfig struct { 50 Username string `json:"username,omitempty"` 51 Password string `json:"password,omitempty"` 52 Auth string `json:"auth"` 53 Email string `json:"email"` 54 ServerAddress string `json:"serveraddress,omitempty"` 55 } 56 57 // ConfigFile ~/.docker/config.json file info 58 type ConfigFile struct { 59 AuthConfigs map[string]AuthConfig `json:"auths"` 60 HTTPHeaders map[string]string `json:"HttpHeaders,omitempty"` 61 PsFormat string `json:"psFormat,omitempty"` 62 filename string // Note: not serialized - for internal use only 63 } 64 65 // NewConfigFile initilizes an empty configuration file for the given filename 'fn' 66 func NewConfigFile(fn string) *ConfigFile { 67 return &ConfigFile{ 68 AuthConfigs: make(map[string]AuthConfig), 69 HTTPHeaders: make(map[string]string), 70 filename: fn, 71 } 72 } 73 74 // LegacyLoadFromReader reads the non-nested configuration data given and sets up the 75 // auth config information with given directory and populates the receiver object 76 func (configFile *ConfigFile) LegacyLoadFromReader(configData io.Reader) error { 77 b, err := ioutil.ReadAll(configData) 78 if err != nil { 79 return err 80 } 81 82 if err := json.Unmarshal(b, &configFile.AuthConfigs); err != nil { 83 arr := strings.Split(string(b), "\n") 84 if len(arr) < 2 { 85 return fmt.Errorf("The Auth config file is empty") 86 } 87 authConfig := AuthConfig{} 88 origAuth := strings.Split(arr[0], " = ") 89 if len(origAuth) != 2 { 90 return fmt.Errorf("Invalid Auth config file") 91 } 92 authConfig.Username, authConfig.Password, err = DecodeAuth(origAuth[1]) 93 if err != nil { 94 return err 95 } 96 origEmail := strings.Split(arr[1], " = ") 97 if len(origEmail) != 2 { 98 return fmt.Errorf("Invalid Auth config file") 99 } 100 authConfig.Email = origEmail[1] 101 authConfig.ServerAddress = defaultIndexserver 102 configFile.AuthConfigs[defaultIndexserver] = authConfig 103 } else { 104 for k, authConfig := range configFile.AuthConfigs { 105 authConfig.Username, authConfig.Password, err = DecodeAuth(authConfig.Auth) 106 if err != nil { 107 return err 108 } 109 authConfig.Auth = "" 110 authConfig.ServerAddress = k 111 configFile.AuthConfigs[k] = authConfig 112 } 113 } 114 return nil 115 } 116 117 // LoadFromReader reads the configuration data given and sets up the auth config 118 // information with given directory and populates the receiver object 119 func (configFile *ConfigFile) LoadFromReader(configData io.Reader) error { 120 if err := json.NewDecoder(configData).Decode(&configFile); err != nil { 121 return err 122 } 123 var err error 124 for addr, ac := range configFile.AuthConfigs { 125 ac.Username, ac.Password, err = DecodeAuth(ac.Auth) 126 if err != nil { 127 return err 128 } 129 ac.Auth = "" 130 ac.ServerAddress = addr 131 configFile.AuthConfigs[addr] = ac 132 } 133 return nil 134 } 135 136 // LegacyLoadFromReader is a convenience function that creates a ConfigFile object from 137 // a non-nested reader 138 func LegacyLoadFromReader(configData io.Reader) (*ConfigFile, error) { 139 configFile := ConfigFile{ 140 AuthConfigs: make(map[string]AuthConfig), 141 } 142 err := configFile.LegacyLoadFromReader(configData) 143 return &configFile, err 144 } 145 146 // LoadFromReader is a convenience function that creates a ConfigFile object from 147 // a reader 148 func LoadFromReader(configData io.Reader) (*ConfigFile, error) { 149 configFile := ConfigFile{ 150 AuthConfigs: make(map[string]AuthConfig), 151 } 152 err := configFile.LoadFromReader(configData) 153 return &configFile, err 154 } 155 156 // Load reads the configuration files in the given directory, and sets up 157 // the auth config information and return values. 158 // FIXME: use the internal golang config parser 159 func Load(configDir string) (*ConfigFile, error) { 160 if configDir == "" { 161 configDir = ConfigDir() 162 } 163 164 configFile := ConfigFile{ 165 AuthConfigs: make(map[string]AuthConfig), 166 filename: filepath.Join(configDir, ConfigFileName), 167 } 168 169 // Try happy path first - latest config file 170 if _, err := os.Stat(configFile.filename); err == nil { 171 file, err := os.Open(configFile.filename) 172 if err != nil { 173 return &configFile, err 174 } 175 defer file.Close() 176 err = configFile.LoadFromReader(file) 177 return &configFile, err 178 } else if !os.IsNotExist(err) { 179 // if file is there but we can't stat it for any reason other 180 // than it doesn't exist then stop 181 return &configFile, err 182 } 183 184 // Can't find latest config file so check for the old one 185 confFile := filepath.Join(homedir.Get(), oldConfigfile) 186 if _, err := os.Stat(confFile); err != nil { 187 return &configFile, nil //missing file is not an error 188 } 189 file, err := os.Open(confFile) 190 if err != nil { 191 return &configFile, err 192 } 193 defer file.Close() 194 err = configFile.LegacyLoadFromReader(file) 195 return &configFile, err 196 } 197 198 // SaveToWriter encodes and writes out all the authorization information to 199 // the given writer 200 func (configFile *ConfigFile) SaveToWriter(writer io.Writer) error { 201 // Encode sensitive data into a new/temp struct 202 tmpAuthConfigs := make(map[string]AuthConfig, len(configFile.AuthConfigs)) 203 for k, authConfig := range configFile.AuthConfigs { 204 authCopy := authConfig 205 // encode and save the authstring, while blanking out the original fields 206 authCopy.Auth = EncodeAuth(&authCopy) 207 authCopy.Username = "" 208 authCopy.Password = "" 209 authCopy.ServerAddress = "" 210 tmpAuthConfigs[k] = authCopy 211 } 212 213 saveAuthConfigs := configFile.AuthConfigs 214 configFile.AuthConfigs = tmpAuthConfigs 215 defer func() { configFile.AuthConfigs = saveAuthConfigs }() 216 217 data, err := json.MarshalIndent(configFile, "", "\t") 218 if err != nil { 219 return err 220 } 221 _, err = writer.Write(data) 222 return err 223 } 224 225 // Save encodes and writes out all the authorization information 226 func (configFile *ConfigFile) Save() error { 227 if configFile.Filename() == "" { 228 return fmt.Errorf("Can't save config with empty filename") 229 } 230 231 if err := system.MkdirAll(filepath.Dir(configFile.filename), 0700); err != nil { 232 return err 233 } 234 f, err := os.OpenFile(configFile.filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) 235 if err != nil { 236 return err 237 } 238 defer f.Close() 239 return configFile.SaveToWriter(f) 240 } 241 242 // Filename returns the name of the configuration file 243 func (configFile *ConfigFile) Filename() string { 244 return configFile.filename 245 } 246 247 // EncodeAuth creates a base64 encoded string to containing authorization information 248 func EncodeAuth(authConfig *AuthConfig) string { 249 authStr := authConfig.Username + ":" + authConfig.Password 250 msg := []byte(authStr) 251 encoded := make([]byte, base64.StdEncoding.EncodedLen(len(msg))) 252 base64.StdEncoding.Encode(encoded, msg) 253 return string(encoded) 254 } 255 256 // DecodeAuth decodes a base64 encoded string and returns username and password 257 func DecodeAuth(authStr string) (string, string, error) { 258 decLen := base64.StdEncoding.DecodedLen(len(authStr)) 259 decoded := make([]byte, decLen) 260 authByte := []byte(authStr) 261 n, err := base64.StdEncoding.Decode(decoded, authByte) 262 if err != nil { 263 return "", "", err 264 } 265 if n > decLen { 266 return "", "", fmt.Errorf("Something went wrong decoding auth config") 267 } 268 arr := strings.SplitN(string(decoded), ":", 2) 269 if len(arr) != 2 { 270 return "", "", fmt.Errorf("Invalid auth configuration file") 271 } 272 password := strings.Trim(arr[1], "\x00") 273 return arr[0], password, nil 274 }