Signed-off-by: haoyun <yun.hao@daocloud.io> Co-authored-by: zounengren <zouyee1989@gmail.com>
		
			
				
	
	
		
			245 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			245 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
/*
 | 
						|
   Copyright The containerd Authors.
 | 
						|
 | 
						|
   Licensed under the Apache License, Version 2.0 (the "License");
 | 
						|
   you may not use this file except in compliance with the License.
 | 
						|
   You may obtain a copy of the License at
 | 
						|
 | 
						|
       http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
 | 
						|
   Unless required by applicable law or agreed to in writing, software
 | 
						|
   distributed under the License is distributed on an "AS IS" BASIS,
 | 
						|
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
						|
   See the License for the specific language governing permissions and
 | 
						|
   limitations under the License.
 | 
						|
*/
 | 
						|
 | 
						|
package docker
 | 
						|
 | 
						|
import (
 | 
						|
	"errors"
 | 
						|
	"net"
 | 
						|
	"net/http"
 | 
						|
)
 | 
						|
 | 
						|
// HostCapabilities represent the capabilities of the registry
 | 
						|
// host. This also represents the set of operations for which
 | 
						|
// the registry host may be trusted to perform.
 | 
						|
//
 | 
						|
// For example pushing is a capability which should only be
 | 
						|
// performed on an upstream source, not a mirror.
 | 
						|
// Resolving (the process of converting a name into a digest)
 | 
						|
// must be considered a trusted operation and only done by
 | 
						|
// a host which is trusted (or more preferably by secure process
 | 
						|
// which can prove the provenance of the mapping). A public
 | 
						|
// mirror should never be trusted to do a resolve action.
 | 
						|
//
 | 
						|
// | Registry Type    | Pull | Resolve | Push |
 | 
						|
// |------------------|------|---------|------|
 | 
						|
// | Public Registry  | yes  | yes     | yes  |
 | 
						|
// | Private Registry | yes  | yes     | yes  |
 | 
						|
// | Public Mirror    | yes  | no      | no   |
 | 
						|
// | Private Mirror   | yes  | yes     | no   |
 | 
						|
type HostCapabilities uint8
 | 
						|
 | 
						|
const (
 | 
						|
	// HostCapabilityPull represents the capability to fetch manifests
 | 
						|
	// and blobs by digest
 | 
						|
	HostCapabilityPull HostCapabilities = 1 << iota
 | 
						|
 | 
						|
	// HostCapabilityResolve represents the capability to fetch manifests
 | 
						|
	// by name
 | 
						|
	HostCapabilityResolve
 | 
						|
 | 
						|
	// HostCapabilityPush represents the capability to push blobs and
 | 
						|
	// manifests
 | 
						|
	HostCapabilityPush
 | 
						|
 | 
						|
	// Reserved for future capabilities (i.e. search, catalog, remove)
 | 
						|
)
 | 
						|
 | 
						|
// Has checks whether the capabilities list has the provide capability
 | 
						|
func (c HostCapabilities) Has(t HostCapabilities) bool {
 | 
						|
	return c&t == t
 | 
						|
}
 | 
						|
 | 
						|
// RegistryHost represents a complete configuration for a registry
 | 
						|
// host, representing the capabilities, authorizations, connection
 | 
						|
// configuration, and location.
 | 
						|
type RegistryHost struct {
 | 
						|
	Client       *http.Client
 | 
						|
	Authorizer   Authorizer
 | 
						|
	Host         string
 | 
						|
	Scheme       string
 | 
						|
	Path         string
 | 
						|
	Capabilities HostCapabilities
 | 
						|
	Header       http.Header
 | 
						|
}
 | 
						|
 | 
						|
func (h RegistryHost) isProxy(refhost string) bool {
 | 
						|
	if refhost != h.Host {
 | 
						|
		if refhost != "docker.io" || h.Host != "registry-1.docker.io" {
 | 
						|
			return true
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return false
 | 
						|
}
 | 
						|
 | 
						|
// RegistryHosts fetches the registry hosts for a given namespace,
 | 
						|
// provided by the host component of an distribution image reference.
 | 
						|
type RegistryHosts func(string) ([]RegistryHost, error)
 | 
						|
 | 
						|
// Registries joins multiple registry configuration functions, using the same
 | 
						|
// order as provided within the arguments. When an empty registry configuration
 | 
						|
// is returned with a nil error, the next function will be called.
 | 
						|
// NOTE: This function will not join configurations, as soon as a non-empty
 | 
						|
// configuration is returned from a configuration function, it will be returned
 | 
						|
// to the caller.
 | 
						|
func Registries(registries ...RegistryHosts) RegistryHosts {
 | 
						|
	return func(host string) ([]RegistryHost, error) {
 | 
						|
		for _, registry := range registries {
 | 
						|
			config, err := registry(host)
 | 
						|
			if err != nil {
 | 
						|
				return config, err
 | 
						|
			}
 | 
						|
			if len(config) > 0 {
 | 
						|
				return config, nil
 | 
						|
			}
 | 
						|
		}
 | 
						|
		return nil, nil
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
type registryOpts struct {
 | 
						|
	authorizer Authorizer
 | 
						|
	plainHTTP  func(string) (bool, error)
 | 
						|
	host       func(string) (string, error)
 | 
						|
	client     *http.Client
 | 
						|
}
 | 
						|
 | 
						|
// RegistryOpt defines a registry default option
 | 
						|
type RegistryOpt func(*registryOpts)
 | 
						|
 | 
						|
// WithPlainHTTP configures registries to use plaintext http scheme
 | 
						|
// for the provided host match function.
 | 
						|
func WithPlainHTTP(f func(string) (bool, error)) RegistryOpt {
 | 
						|
	return func(opts *registryOpts) {
 | 
						|
		opts.plainHTTP = f
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// WithAuthorizer configures the default authorizer for a registry
 | 
						|
func WithAuthorizer(a Authorizer) RegistryOpt {
 | 
						|
	return func(opts *registryOpts) {
 | 
						|
		opts.authorizer = a
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// WithHostTranslator defines the default translator to use for registry hosts
 | 
						|
func WithHostTranslator(h func(string) (string, error)) RegistryOpt {
 | 
						|
	return func(opts *registryOpts) {
 | 
						|
		opts.host = h
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// WithClient configures the default http client for a registry
 | 
						|
func WithClient(c *http.Client) RegistryOpt {
 | 
						|
	return func(opts *registryOpts) {
 | 
						|
		opts.client = c
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// ConfigureDefaultRegistries is used to create a default configuration for
 | 
						|
// registries. For more advanced configurations or per-domain setups,
 | 
						|
// the RegistryHosts interface should be used directly.
 | 
						|
// NOTE: This function will always return a non-empty value or error
 | 
						|
func ConfigureDefaultRegistries(ropts ...RegistryOpt) RegistryHosts {
 | 
						|
	var opts registryOpts
 | 
						|
	for _, opt := range ropts {
 | 
						|
		opt(&opts)
 | 
						|
	}
 | 
						|
 | 
						|
	return func(host string) ([]RegistryHost, error) {
 | 
						|
		config := RegistryHost{
 | 
						|
			Client:       opts.client,
 | 
						|
			Authorizer:   opts.authorizer,
 | 
						|
			Host:         host,
 | 
						|
			Scheme:       "https",
 | 
						|
			Path:         "/v2",
 | 
						|
			Capabilities: HostCapabilityPull | HostCapabilityResolve | HostCapabilityPush,
 | 
						|
		}
 | 
						|
 | 
						|
		if config.Client == nil {
 | 
						|
			config.Client = http.DefaultClient
 | 
						|
		}
 | 
						|
 | 
						|
		if opts.plainHTTP != nil {
 | 
						|
			match, err := opts.plainHTTP(host)
 | 
						|
			if err != nil {
 | 
						|
				return nil, err
 | 
						|
			}
 | 
						|
			if match {
 | 
						|
				config.Scheme = "http"
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		if opts.host != nil {
 | 
						|
			var err error
 | 
						|
			config.Host, err = opts.host(config.Host)
 | 
						|
			if err != nil {
 | 
						|
				return nil, err
 | 
						|
			}
 | 
						|
		} else if host == "docker.io" {
 | 
						|
			config.Host = "registry-1.docker.io"
 | 
						|
		}
 | 
						|
 | 
						|
		return []RegistryHost{config}, nil
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// MatchAllHosts is a host match function which is always true.
 | 
						|
func MatchAllHosts(string) (bool, error) {
 | 
						|
	return true, nil
 | 
						|
}
 | 
						|
 | 
						|
// MatchLocalhost is a host match function which returns true for
 | 
						|
// localhost.
 | 
						|
//
 | 
						|
// Note: this does not handle matching of ip addresses in octal,
 | 
						|
// decimal or hex form.
 | 
						|
func MatchLocalhost(host string) (bool, error) {
 | 
						|
	switch {
 | 
						|
	case host == "::1":
 | 
						|
		return true, nil
 | 
						|
	case host == "[::1]":
 | 
						|
		return true, nil
 | 
						|
	}
 | 
						|
	h, p, err := net.SplitHostPort(host)
 | 
						|
 | 
						|
	// addrError helps distinguish between errors of form
 | 
						|
	// "no colon in address" and "too many colons in address".
 | 
						|
	// The former is fine as the host string need not have a
 | 
						|
	// port. Latter needs to be handled.
 | 
						|
	addrError := &net.AddrError{
 | 
						|
		Err:  "missing port in address",
 | 
						|
		Addr: host,
 | 
						|
	}
 | 
						|
	if err != nil {
 | 
						|
		if err.Error() != addrError.Error() {
 | 
						|
			return false, err
 | 
						|
		}
 | 
						|
		// host string without any port specified
 | 
						|
		h = host
 | 
						|
	} else if len(p) == 0 {
 | 
						|
		return false, errors.New("invalid host name format")
 | 
						|
	}
 | 
						|
 | 
						|
	// use ipv4 dotted decimal for further checking
 | 
						|
	if h == "localhost" {
 | 
						|
		h = "127.0.0.1"
 | 
						|
	}
 | 
						|
	ip := net.ParseIP(h)
 | 
						|
 | 
						|
	return ip.IsLoopback(), nil
 | 
						|
}
 |