registry.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. package distribution // import "github.com/docker/docker/distribution"
  2. import (
  3. "context"
  4. "fmt"
  5. "net"
  6. "net/http"
  7. "time"
  8. "github.com/distribution/reference"
  9. "github.com/docker/distribution"
  10. "github.com/docker/distribution/manifest/schema2"
  11. "github.com/docker/distribution/registry/client"
  12. "github.com/docker/distribution/registry/client/auth"
  13. "github.com/docker/distribution/registry/client/transport"
  14. registrytypes "github.com/docker/docker/api/types/registry"
  15. "github.com/docker/docker/dockerversion"
  16. "github.com/docker/docker/registry"
  17. ocispec "github.com/opencontainers/image-spec/specs-go/v1"
  18. )
  19. var (
  20. // supportedMediaTypes represents acceptable media-type(-prefixes)
  21. // we use this list to prevent obscure errors when trying to pull
  22. // OCI artifacts.
  23. supportedMediaTypes = []string{
  24. // valid prefixes
  25. "application/vnd.oci.image",
  26. "application/vnd.docker",
  27. // these types may occur on old images, and are copied from
  28. // defaultImageTypes below.
  29. "application/octet-stream",
  30. "application/json",
  31. "text/html",
  32. "",
  33. }
  34. // defaultImageTypes represents the schema2 config types for images
  35. defaultImageTypes = []string{
  36. schema2.MediaTypeImageConfig,
  37. ocispec.MediaTypeImageConfig,
  38. // Handle unexpected values from https://github.com/docker/distribution/issues/1621
  39. // (see also https://github.com/docker/docker/issues/22378,
  40. // https://github.com/docker/docker/issues/30083)
  41. "application/octet-stream",
  42. "application/json",
  43. "text/html",
  44. // Treat defaulted values as images, newer types cannot be implied
  45. "",
  46. }
  47. // pluginTypes represents the schema2 config types for plugins
  48. pluginTypes = []string{
  49. schema2.MediaTypePluginConfig,
  50. }
  51. mediaTypeClasses map[string]string
  52. )
  53. func init() {
  54. // initialize media type classes with all know types for images and plugins.
  55. mediaTypeClasses = map[string]string{}
  56. for _, t := range defaultImageTypes {
  57. mediaTypeClasses[t] = "image"
  58. }
  59. for _, t := range pluginTypes {
  60. mediaTypeClasses[t] = "plugin"
  61. }
  62. }
  63. // newRepository returns a repository (v2 only). It creates an HTTP transport
  64. // providing timeout settings and authentication support, and also verifies the
  65. // remote API version.
  66. func newRepository(
  67. ctx context.Context, repoInfo *registry.RepositoryInfo, endpoint registry.APIEndpoint,
  68. metaHeaders http.Header, authConfig *registrytypes.AuthConfig, actions ...string,
  69. ) (distribution.Repository, error) {
  70. repoName := repoInfo.Name.Name()
  71. // If endpoint does not support CanonicalName, use the RemoteName instead
  72. if endpoint.TrimHostname {
  73. repoName = reference.Path(repoInfo.Name)
  74. }
  75. direct := &net.Dialer{
  76. Timeout: 30 * time.Second,
  77. KeepAlive: 30 * time.Second,
  78. }
  79. // TODO(dmcgowan): Call close idle connections when complete, use keep alive
  80. base := &http.Transport{
  81. Proxy: http.ProxyFromEnvironment,
  82. DialContext: direct.DialContext,
  83. TLSHandshakeTimeout: 10 * time.Second,
  84. TLSClientConfig: endpoint.TLSConfig,
  85. // TODO(dmcgowan): Call close idle connections when complete and use keep alive
  86. DisableKeepAlives: true,
  87. }
  88. modifiers := registry.Headers(dockerversion.DockerUserAgent(ctx), metaHeaders)
  89. authTransport := transport.NewTransport(base, modifiers...)
  90. challengeManager, err := registry.PingV2Registry(endpoint.URL, authTransport)
  91. if err != nil {
  92. transportOK := false
  93. if responseErr, ok := err.(registry.PingResponseError); ok {
  94. transportOK = true
  95. err = responseErr.Err
  96. }
  97. return nil, fallbackError{
  98. err: err,
  99. transportOK: transportOK,
  100. }
  101. }
  102. if authConfig.RegistryToken != "" {
  103. modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, &passThruTokenHandler{token: authConfig.RegistryToken}))
  104. } else {
  105. creds := registry.NewStaticCredentialStore(authConfig)
  106. tokenHandler := auth.NewTokenHandlerWithOptions(auth.TokenHandlerOptions{
  107. Transport: authTransport,
  108. Credentials: creds,
  109. Scopes: []auth.Scope{auth.RepositoryScope{
  110. Repository: repoName,
  111. Actions: actions,
  112. Class: repoInfo.Class,
  113. }},
  114. ClientID: registry.AuthClientID,
  115. })
  116. basicHandler := auth.NewBasicHandler(creds)
  117. modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, tokenHandler, basicHandler))
  118. }
  119. tr := transport.NewTransport(base, modifiers...)
  120. repoNameRef, err := reference.WithName(repoName)
  121. if err != nil {
  122. return nil, fallbackError{
  123. err: err,
  124. transportOK: true,
  125. }
  126. }
  127. repo, err := client.NewRepository(repoNameRef, endpoint.URL.String(), tr)
  128. if err != nil {
  129. return nil, fallbackError{
  130. err: err,
  131. transportOK: true,
  132. }
  133. }
  134. return repo, nil
  135. }
  136. type passThruTokenHandler struct {
  137. token string
  138. }
  139. func (th *passThruTokenHandler) Scheme() string {
  140. return "bearer"
  141. }
  142. func (th *passThruTokenHandler) AuthorizeRequest(req *http.Request, params map[string]string) error {
  143. req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", th.token))
  144. return nil
  145. }