github.com/containerd/Containerd@v1.4.13/remotes/docker/registry.go (about)

     1  /*
     2     Copyright The containerd Authors.
     3  
     4     Licensed under the Apache License, Version 2.0 (the "License");
     5     you may not use this file except in compliance with the License.
     6     You may obtain a copy of the License at
     7  
     8         http://www.apache.org/licenses/LICENSE-2.0
     9  
    10     Unless required by applicable law or agreed to in writing, software
    11     distributed under the License is distributed on an "AS IS" BASIS,
    12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13     See the License for the specific language governing permissions and
    14     limitations under the License.
    15  */
    16  
    17  package docker
    18  
    19  import (
    20  	"net/http"
    21  )
    22  
    23  // HostCapabilities represent the capabilities of the registry
    24  // host. This also represents the set of operations for which
    25  // the registry host may be trusted to perform.
    26  //
    27  // For example pushing is a capability which should only be
    28  // performed on an upstream source, not a mirror.
    29  // Resolving (the process of converting a name into a digest)
    30  // must be considered a trusted operation and only done by
    31  // a host which is trusted (or more preferably by secure process
    32  // which can prove the provenance of the mapping). A public
    33  // mirror should never be trusted to do a resolve action.
    34  //
    35  // | Registry Type    | Pull | Resolve | Push |
    36  // |------------------|------|---------|------|
    37  // | Public Registry  | yes  | yes     | yes  |
    38  // | Private Registry | yes  | yes     | yes  |
    39  // | Public Mirror    | yes  | no      | no   |
    40  // | Private Mirror   | yes  | yes     | no   |
    41  type HostCapabilities uint8
    42  
    43  const (
    44  	// HostCapabilityPull represents the capability to fetch manifests
    45  	// and blobs by digest
    46  	HostCapabilityPull HostCapabilities = 1 << iota
    47  
    48  	// HostCapabilityResolve represents the capability to fetch manifests
    49  	// by name
    50  	HostCapabilityResolve
    51  
    52  	// HostCapabilityPush represents the capability to push blobs and
    53  	// manifests
    54  	HostCapabilityPush
    55  
    56  	// Reserved for future capabilities (i.e. search, catalog, remove)
    57  )
    58  
    59  func (c HostCapabilities) Has(t HostCapabilities) bool {
    60  	return c&t == t
    61  }
    62  
    63  // RegistryHost represents a complete configuration for a registry
    64  // host, representing the capabilities, authorizations, connection
    65  // configuration, and location.
    66  type RegistryHost struct {
    67  	Client       *http.Client
    68  	Authorizer   Authorizer
    69  	Host         string
    70  	Scheme       string
    71  	Path         string
    72  	Capabilities HostCapabilities
    73  	Header       http.Header
    74  }
    75  
    76  func (h RegistryHost) isProxy(refhost string) bool {
    77  	if refhost != h.Host {
    78  		if refhost != "docker.io" || h.Host != "registry-1.docker.io" {
    79  			return true
    80  		}
    81  	}
    82  	return false
    83  }
    84  
    85  // RegistryHosts fetches the registry hosts for a given namespace,
    86  // provided by the host component of an distribution image reference.
    87  type RegistryHosts func(string) ([]RegistryHost, error)
    88  
    89  // Registries joins multiple registry configuration functions, using the same
    90  // order as provided within the arguments. When an empty registry configuration
    91  // is returned with a nil error, the next function will be called.
    92  // NOTE: This function will not join configurations, as soon as a non-empty
    93  // configuration is returned from a configuration function, it will be returned
    94  // to the caller.
    95  func Registries(registries ...RegistryHosts) RegistryHosts {
    96  	return func(host string) ([]RegistryHost, error) {
    97  		for _, registry := range registries {
    98  			config, err := registry(host)
    99  			if err != nil {
   100  				return config, err
   101  			}
   102  			if len(config) > 0 {
   103  				return config, nil
   104  			}
   105  		}
   106  		return nil, nil
   107  	}
   108  }
   109  
   110  type registryOpts struct {
   111  	authorizer Authorizer
   112  	plainHTTP  func(string) (bool, error)
   113  	host       func(string) (string, error)
   114  	client     *http.Client
   115  }
   116  
   117  // RegistryOpt defines a registry default option
   118  type RegistryOpt func(*registryOpts)
   119  
   120  // WithPlainHTTP configures registries to use plaintext http scheme
   121  // for the provided host match function.
   122  func WithPlainHTTP(f func(string) (bool, error)) RegistryOpt {
   123  	return func(opts *registryOpts) {
   124  		opts.plainHTTP = f
   125  	}
   126  }
   127  
   128  // WithAuthorizer configures the default authorizer for a registry
   129  func WithAuthorizer(a Authorizer) RegistryOpt {
   130  	return func(opts *registryOpts) {
   131  		opts.authorizer = a
   132  	}
   133  }
   134  
   135  // WithHostTranslator defines the default translator to use for registry hosts
   136  func WithHostTranslator(h func(string) (string, error)) RegistryOpt {
   137  	return func(opts *registryOpts) {
   138  		opts.host = h
   139  	}
   140  }
   141  
   142  // WithClient configures the default http client for a registry
   143  func WithClient(c *http.Client) RegistryOpt {
   144  	return func(opts *registryOpts) {
   145  		opts.client = c
   146  	}
   147  }
   148  
   149  // ConfigureDefaultRegistries is used to create a default configuration for
   150  // registries. For more advanced configurations or per-domain setups,
   151  // the RegistryHosts interface should be used directly.
   152  // NOTE: This function will always return a non-empty value or error
   153  func ConfigureDefaultRegistries(ropts ...RegistryOpt) RegistryHosts {
   154  	var opts registryOpts
   155  	for _, opt := range ropts {
   156  		opt(&opts)
   157  	}
   158  
   159  	return func(host string) ([]RegistryHost, error) {
   160  		config := RegistryHost{
   161  			Client:       opts.client,
   162  			Authorizer:   opts.authorizer,
   163  			Host:         host,
   164  			Scheme:       "https",
   165  			Path:         "/v2",
   166  			Capabilities: HostCapabilityPull | HostCapabilityResolve | HostCapabilityPush,
   167  		}
   168  
   169  		if config.Client == nil {
   170  			config.Client = http.DefaultClient
   171  		}
   172  
   173  		if opts.plainHTTP != nil {
   174  			match, err := opts.plainHTTP(host)
   175  			if err != nil {
   176  				return nil, err
   177  			}
   178  			if match {
   179  				config.Scheme = "http"
   180  			}
   181  		}
   182  
   183  		if opts.host != nil {
   184  			var err error
   185  			config.Host, err = opts.host(config.Host)
   186  			if err != nil {
   187  				return nil, err
   188  			}
   189  		} else if host == "docker.io" {
   190  			config.Host = "registry-1.docker.io"
   191  		}
   192  
   193  		return []RegistryHost{config}, nil
   194  	}
   195  }
   196  
   197  // MatchAllHosts is a host match function which is always true.
   198  func MatchAllHosts(string) (bool, error) {
   199  	return true, nil
   200  }
   201  
   202  // MatchLocalhost is a host match function which returns true for
   203  // localhost.
   204  func MatchLocalhost(host string) (bool, error) {
   205  	for _, s := range []string{"localhost", "127.0.0.1", "[::1]"} {
   206  		if len(host) >= len(s) && host[0:len(s)] == s && (len(host) == len(s) || host[len(s)] == ':') {
   207  			return true, nil
   208  		}
   209  	}
   210  	return host == "::1", nil
   211  
   212  }