GitOps for k8s
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

credentials.go 4.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. package registry
  2. import (
  3. "encoding/base64"
  4. "encoding/json"
  5. "fmt"
  6. "io/ioutil"
  7. "net/url"
  8. "strings"
  9. "github.com/pkg/errors"
  10. )
  11. // Registry Credentials
  12. type creds struct {
  13. username, password string
  14. registry, provenance string
  15. }
  16. func (c creds) String() string {
  17. if (creds{}) == c {
  18. return "<zero creds>"
  19. }
  20. return fmt.Sprintf("<registry creds for %s@%s, from %s>", c.username, c.registry, c.provenance)
  21. }
  22. // Credentials to a (Docker) registry.
  23. type Credentials struct {
  24. m map[string]creds
  25. }
  26. // NoCredentials returns a usable but empty credentials object.
  27. func NoCredentials() Credentials {
  28. return Credentials{
  29. m: map[string]creds{},
  30. }
  31. }
  32. func parseAuth(auth string) (creds, error) {
  33. decodedAuth, err := base64.StdEncoding.DecodeString(auth)
  34. if err != nil {
  35. return creds{}, err
  36. }
  37. authParts := strings.SplitN(string(decodedAuth), ":", 2)
  38. if len(authParts) != 2 {
  39. return creds{},
  40. fmt.Errorf("decoded credential has wrong number of fields (expected 2, got %d)", len(authParts))
  41. }
  42. return creds{
  43. username: authParts[0],
  44. password: authParts[1],
  45. }, nil
  46. }
  47. func ParseCredentials(from string, b []byte) (Credentials, error) {
  48. var config struct {
  49. Auths map[string]struct {
  50. Auth string
  51. }
  52. }
  53. if err := json.Unmarshal(b, &config); err != nil {
  54. return Credentials{}, err
  55. }
  56. // If it's in k8s format, it won't have the surrounding "Auth". Try that too.
  57. if len(config.Auths) == 0 {
  58. if err := json.Unmarshal(b, &config.Auths); err != nil {
  59. return Credentials{}, err
  60. }
  61. }
  62. m := map[string]creds{}
  63. for host, entry := range config.Auths {
  64. creds, err := parseAuth(entry.Auth)
  65. if err != nil {
  66. return Credentials{}, err
  67. }
  68. // Some users were passing in credentials in the form of
  69. // http://docker.io and http://docker.io/v1/, etc.
  70. // So strip everything down to the host.
  71. // Also, the registry might be local and on a different port.
  72. // So we need to check for that because url.Parse won't parse the ip:port format very well.
  73. u, err := url.Parse(host)
  74. if err != nil {
  75. return Credentials{}, err
  76. }
  77. if u.Host == "" && u.Path == "" && !strings.Contains(host, ":") || host == "http://" || host == "https://" {
  78. return Credentials{}, errors.New("Empty registry auth url")
  79. }
  80. if u.Host == "" { // If there's no https:// prefix, it won't parse the host.
  81. u, err = url.Parse(fmt.Sprintf("https://%s/", host))
  82. if err != nil {
  83. return Credentials{}, err
  84. }
  85. // If the host is still empty, then there's probably a rogue /
  86. if u.Host == "" {
  87. return Credentials{}, errors.New("Invalid registry auth url. Must be a valid http address (e.g. https://gcr.io/v1/)")
  88. }
  89. }
  90. host = u.Host
  91. creds.registry = host
  92. creds.provenance = from
  93. m[host] = creds
  94. }
  95. return Credentials{m: m}, nil
  96. }
  97. func ImageCredsWithDefaults(lookup func() ImageCreds, configPath string) (func() ImageCreds, error) {
  98. // pre-flight check
  99. bs, err := ioutil.ReadFile(configPath)
  100. if err == nil {
  101. _, err = ParseCredentials(configPath, bs)
  102. }
  103. if err != nil {
  104. return nil, err
  105. }
  106. return func() ImageCreds {
  107. var defaults Credentials
  108. bs, err := ioutil.ReadFile(configPath)
  109. if err == nil {
  110. defaults, _ = ParseCredentials(configPath, bs)
  111. }
  112. imageCreds := lookup()
  113. for k, v := range imageCreds {
  114. newCreds := NoCredentials()
  115. newCreds.Merge(defaults)
  116. newCreds.Merge(v)
  117. imageCreds[k] = newCreds
  118. }
  119. return imageCreds
  120. }, nil
  121. }
  122. // ---
  123. // credsFor yields an authenticator for a specific host.
  124. func (cs Credentials) credsFor(host string) creds {
  125. if cred, found := cs.m[host]; found {
  126. return cred
  127. }
  128. if host == "gcr.io" || strings.HasSuffix(host, ".gcr.io") {
  129. if cred, err := GetGCPOauthToken(host); err == nil {
  130. return cred
  131. }
  132. }
  133. if hostIsAzureContainerRegistry(host) {
  134. if cred, err := getAzureCloudConfigAADToken(host); err == nil {
  135. return cred
  136. }
  137. }
  138. return creds{}
  139. }
  140. // Hosts returns all of the hosts available in these credentials.
  141. func (cs Credentials) Hosts() []string {
  142. hosts := []string{}
  143. for host := range cs.m {
  144. hosts = append(hosts, host)
  145. }
  146. return hosts
  147. }
  148. func (cs Credentials) Merge(c Credentials) {
  149. for k, v := range c.m {
  150. cs.m[k] = v
  151. }
  152. }
  153. func (cs Credentials) String() string {
  154. return fmt.Sprintf("{%v}", cs.m)
  155. }