service.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. package registry // import "github.com/docker/docker/registry"
  2. import (
  3. "context"
  4. "crypto/tls"
  5. "net/url"
  6. "strings"
  7. "sync"
  8. "github.com/containerd/containerd/log"
  9. "github.com/docker/distribution/reference"
  10. "github.com/docker/docker/api/types/registry"
  11. "github.com/docker/docker/errdefs"
  12. )
  13. // Service is a registry service. It tracks configuration data such as a list
  14. // of mirrors.
  15. type Service struct {
  16. config *serviceConfig
  17. mu sync.RWMutex
  18. }
  19. // NewService returns a new instance of defaultService ready to be
  20. // installed into an engine.
  21. func NewService(options ServiceOptions) (*Service, error) {
  22. config, err := newServiceConfig(options)
  23. return &Service{config: config}, err
  24. }
  25. // ServiceConfig returns a copy of the public registry service's configuration.
  26. func (s *Service) ServiceConfig() *registry.ServiceConfig {
  27. s.mu.RLock()
  28. defer s.mu.RUnlock()
  29. return s.config.copy()
  30. }
  31. // ReplaceConfig prepares a transaction which will atomically replace the
  32. // registry service's configuration when the returned commit function is called.
  33. func (s *Service) ReplaceConfig(options ServiceOptions) (commit func(), err error) {
  34. config, err := newServiceConfig(options)
  35. if err != nil {
  36. return nil, err
  37. }
  38. return func() {
  39. s.mu.Lock()
  40. defer s.mu.Unlock()
  41. s.config = config
  42. }, nil
  43. }
  44. // Auth contacts the public registry with the provided credentials,
  45. // and returns OK if authentication was successful.
  46. // It can be used to verify the validity of a client's credentials.
  47. func (s *Service) Auth(ctx context.Context, authConfig *registry.AuthConfig, userAgent string) (status, token string, err error) {
  48. // TODO Use ctx when searching for repositories
  49. registryHostName := IndexHostname
  50. if authConfig.ServerAddress != "" {
  51. serverAddress := authConfig.ServerAddress
  52. if !strings.HasPrefix(serverAddress, "https://") && !strings.HasPrefix(serverAddress, "http://") {
  53. serverAddress = "https://" + serverAddress
  54. }
  55. u, err := url.Parse(serverAddress)
  56. if err != nil {
  57. return "", "", invalidParamWrapf(err, "unable to parse server address")
  58. }
  59. registryHostName = u.Host
  60. }
  61. // Lookup endpoints for authentication using "LookupPushEndpoints", which
  62. // excludes mirrors to prevent sending credentials of the upstream registry
  63. // to a mirror.
  64. endpoints, err := s.LookupPushEndpoints(registryHostName)
  65. if err != nil {
  66. return "", "", invalidParam(err)
  67. }
  68. for _, endpoint := range endpoints {
  69. status, token, err = loginV2(authConfig, endpoint, userAgent)
  70. if err == nil {
  71. return
  72. }
  73. if errdefs.IsUnauthorized(err) {
  74. // Failed to authenticate; don't continue with (non-TLS) endpoints.
  75. return status, token, err
  76. }
  77. log.G(ctx).WithError(err).Infof("Error logging in to endpoint, trying next endpoint")
  78. }
  79. return "", "", err
  80. }
  81. // splitReposSearchTerm breaks a search term into an index name and remote name
  82. func splitReposSearchTerm(reposName string) (string, string) {
  83. nameParts := strings.SplitN(reposName, "/", 2)
  84. if len(nameParts) == 1 || (!strings.Contains(nameParts[0], ".") &&
  85. !strings.Contains(nameParts[0], ":") && nameParts[0] != "localhost") {
  86. // This is a Docker Hub repository (ex: samalba/hipache or ubuntu),
  87. // use the default Docker Hub registry (docker.io)
  88. return IndexName, reposName
  89. }
  90. return nameParts[0], nameParts[1]
  91. }
  92. // ResolveRepository splits a repository name into its components
  93. // and configuration of the associated registry.
  94. func (s *Service) ResolveRepository(name reference.Named) (*RepositoryInfo, error) {
  95. s.mu.RLock()
  96. defer s.mu.RUnlock()
  97. return newRepositoryInfo(s.config, name)
  98. }
  99. // APIEndpoint represents a remote API endpoint
  100. type APIEndpoint struct {
  101. Mirror bool
  102. URL *url.URL
  103. Version APIVersion
  104. AllowNondistributableArtifacts bool
  105. Official bool
  106. TrimHostname bool
  107. TLSConfig *tls.Config
  108. }
  109. // LookupPullEndpoints creates a list of v2 endpoints to try to pull from, in order of preference.
  110. // It gives preference to mirrors over the actual registry, and HTTPS over plain HTTP.
  111. func (s *Service) LookupPullEndpoints(hostname string) (endpoints []APIEndpoint, err error) {
  112. s.mu.RLock()
  113. defer s.mu.RUnlock()
  114. return s.lookupV2Endpoints(hostname)
  115. }
  116. // LookupPushEndpoints creates a list of v2 endpoints to try to push to, in order of preference.
  117. // It gives preference to HTTPS over plain HTTP. Mirrors are not included.
  118. func (s *Service) LookupPushEndpoints(hostname string) (endpoints []APIEndpoint, err error) {
  119. s.mu.RLock()
  120. defer s.mu.RUnlock()
  121. allEndpoints, err := s.lookupV2Endpoints(hostname)
  122. if err == nil {
  123. for _, endpoint := range allEndpoints {
  124. if !endpoint.Mirror {
  125. endpoints = append(endpoints, endpoint)
  126. }
  127. }
  128. }
  129. return endpoints, err
  130. }
  131. // IsInsecureRegistry returns true if the registry at given host is configured as
  132. // insecure registry.
  133. func (s *Service) IsInsecureRegistry(host string) bool {
  134. s.mu.RLock()
  135. defer s.mu.RUnlock()
  136. return !s.config.isSecureIndex(host)
  137. }