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