file.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. package configfile
  2. import (
  3. "encoding/base64"
  4. "encoding/json"
  5. "fmt"
  6. "io"
  7. "io/ioutil"
  8. "os"
  9. "path/filepath"
  10. "strings"
  11. "github.com/docker/engine-api/types"
  12. )
  13. const (
  14. // This constant is only used for really old config files when the
  15. // URL wasn't saved as part of the config file and it was just
  16. // assumed to be this value.
  17. defaultIndexserver = "https://index.docker.io/v1/"
  18. )
  19. // ConfigFile ~/.docker/config.json file info
  20. type ConfigFile struct {
  21. AuthConfigs map[string]types.AuthConfig `json:"auths"`
  22. HTTPHeaders map[string]string `json:"HttpHeaders,omitempty"`
  23. PsFormat string `json:"psFormat,omitempty"`
  24. ImagesFormat string `json:"imagesFormat,omitempty"`
  25. DetachKeys string `json:"detachKeys,omitempty"`
  26. CredentialsStore string `json:"credsStore,omitempty"`
  27. Filename string `json:"-"` // Note: for internal use only
  28. }
  29. // LegacyLoadFromReader reads the non-nested configuration data given and sets up the
  30. // auth config information with given directory and populates the receiver object
  31. func (configFile *ConfigFile) LegacyLoadFromReader(configData io.Reader) error {
  32. b, err := ioutil.ReadAll(configData)
  33. if err != nil {
  34. return err
  35. }
  36. if err := json.Unmarshal(b, &configFile.AuthConfigs); err != nil {
  37. arr := strings.Split(string(b), "\n")
  38. if len(arr) < 2 {
  39. return fmt.Errorf("The Auth config file is empty")
  40. }
  41. authConfig := types.AuthConfig{}
  42. origAuth := strings.Split(arr[0], " = ")
  43. if len(origAuth) != 2 {
  44. return fmt.Errorf("Invalid Auth config file")
  45. }
  46. authConfig.Username, authConfig.Password, err = decodeAuth(origAuth[1])
  47. if err != nil {
  48. return err
  49. }
  50. authConfig.ServerAddress = defaultIndexserver
  51. configFile.AuthConfigs[defaultIndexserver] = authConfig
  52. } else {
  53. for k, authConfig := range configFile.AuthConfigs {
  54. authConfig.Username, authConfig.Password, err = decodeAuth(authConfig.Auth)
  55. if err != nil {
  56. return err
  57. }
  58. authConfig.Auth = ""
  59. authConfig.ServerAddress = k
  60. configFile.AuthConfigs[k] = authConfig
  61. }
  62. }
  63. return nil
  64. }
  65. // LoadFromReader reads the configuration data given and sets up the auth config
  66. // information with given directory and populates the receiver object
  67. func (configFile *ConfigFile) LoadFromReader(configData io.Reader) error {
  68. if err := json.NewDecoder(configData).Decode(&configFile); err != nil {
  69. return err
  70. }
  71. var err error
  72. for addr, ac := range configFile.AuthConfigs {
  73. ac.Username, ac.Password, err = decodeAuth(ac.Auth)
  74. if err != nil {
  75. return err
  76. }
  77. ac.Auth = ""
  78. ac.ServerAddress = addr
  79. configFile.AuthConfigs[addr] = ac
  80. }
  81. return nil
  82. }
  83. // ContainsAuth returns whether there is authentication configured
  84. // in this file or not.
  85. func (configFile *ConfigFile) ContainsAuth() bool {
  86. return configFile.CredentialsStore != "" ||
  87. (configFile.AuthConfigs != nil && len(configFile.AuthConfigs) > 0)
  88. }
  89. // SaveToWriter encodes and writes out all the authorization information to
  90. // the given writer
  91. func (configFile *ConfigFile) SaveToWriter(writer io.Writer) error {
  92. // Encode sensitive data into a new/temp struct
  93. tmpAuthConfigs := make(map[string]types.AuthConfig, len(configFile.AuthConfigs))
  94. for k, authConfig := range configFile.AuthConfigs {
  95. authCopy := authConfig
  96. // encode and save the authstring, while blanking out the original fields
  97. authCopy.Auth = encodeAuth(&authCopy)
  98. authCopy.Username = ""
  99. authCopy.Password = ""
  100. authCopy.ServerAddress = ""
  101. tmpAuthConfigs[k] = authCopy
  102. }
  103. saveAuthConfigs := configFile.AuthConfigs
  104. configFile.AuthConfigs = tmpAuthConfigs
  105. defer func() { configFile.AuthConfigs = saveAuthConfigs }()
  106. data, err := json.MarshalIndent(configFile, "", "\t")
  107. if err != nil {
  108. return err
  109. }
  110. _, err = writer.Write(data)
  111. return err
  112. }
  113. // Save encodes and writes out all the authorization information
  114. func (configFile *ConfigFile) Save() error {
  115. if configFile.Filename == "" {
  116. return fmt.Errorf("Can't save config with empty filename")
  117. }
  118. if err := os.MkdirAll(filepath.Dir(configFile.Filename), 0700); err != nil {
  119. return err
  120. }
  121. f, err := os.OpenFile(configFile.Filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
  122. if err != nil {
  123. return err
  124. }
  125. defer f.Close()
  126. return configFile.SaveToWriter(f)
  127. }
  128. // encodeAuth creates a base64 encoded string to containing authorization information
  129. func encodeAuth(authConfig *types.AuthConfig) string {
  130. if authConfig.Username == "" && authConfig.Password == "" {
  131. return ""
  132. }
  133. authStr := authConfig.Username + ":" + authConfig.Password
  134. msg := []byte(authStr)
  135. encoded := make([]byte, base64.StdEncoding.EncodedLen(len(msg)))
  136. base64.StdEncoding.Encode(encoded, msg)
  137. return string(encoded)
  138. }
  139. // decodeAuth decodes a base64 encoded string and returns username and password
  140. func decodeAuth(authStr string) (string, string, error) {
  141. if authStr == "" {
  142. return "", "", nil
  143. }
  144. decLen := base64.StdEncoding.DecodedLen(len(authStr))
  145. decoded := make([]byte, decLen)
  146. authByte := []byte(authStr)
  147. n, err := base64.StdEncoding.Decode(decoded, authByte)
  148. if err != nil {
  149. return "", "", err
  150. }
  151. if n > decLen {
  152. return "", "", fmt.Errorf("Something went wrong decoding auth config")
  153. }
  154. arr := strings.SplitN(string(decoded), ":", 2)
  155. if len(arr) != 2 {
  156. return "", "", fmt.Errorf("Invalid auth configuration file")
  157. }
  158. password := strings.Trim(arr[1], "\x00")
  159. return arr[0], password, nil
  160. }