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