github.com/sijibomii/docker@v0.0.0-20231230191044-5cf6ca554647/registry/config.go (about)

     1  package registry
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"net"
     7  	"net/url"
     8  	"strings"
     9  
    10  	"github.com/docker/docker/opts"
    11  	flag "github.com/docker/docker/pkg/mflag"
    12  	"github.com/docker/docker/reference"
    13  	registrytypes "github.com/docker/engine-api/types/registry"
    14  )
    15  
    16  // ServiceOptions holds command line options.
    17  type ServiceOptions struct {
    18  	Mirrors            []string `json:"registry-mirrors,omitempty"`
    19  	InsecureRegistries []string `json:"insecure-registries,omitempty"`
    20  
    21  	// V2Only controls access to legacy registries.  If it is set to true via the
    22  	// command line flag the daemon will not attempt to contact v1 legacy registries
    23  	V2Only bool `json:"disable-legacy-registry,omitempty"`
    24  }
    25  
    26  // serviceConfig holds daemon configuration for the registry service.
    27  type serviceConfig struct {
    28  	registrytypes.ServiceConfig
    29  	V2Only bool
    30  }
    31  
    32  var (
    33  	// DefaultNamespace is the default namespace
    34  	DefaultNamespace = "docker.io"
    35  	// DefaultRegistryVersionHeader is the name of the default HTTP header
    36  	// that carries Registry version info
    37  	DefaultRegistryVersionHeader = "Docker-Distribution-Api-Version"
    38  
    39  	// IndexServer is the v1 registry server used for user auth + account creation
    40  	IndexServer = DefaultV1Registry.String() + "/v1/"
    41  	// IndexName is the name of the index
    42  	IndexName = "docker.io"
    43  
    44  	// NotaryServer is the endpoint serving the Notary trust server
    45  	NotaryServer = "https://notary.docker.io"
    46  
    47  	// DefaultV1Registry is the URI of the default v1 registry
    48  	DefaultV1Registry = &url.URL{
    49  		Scheme: "https",
    50  		Host:   "index.docker.io",
    51  	}
    52  
    53  	// DefaultV2Registry is the URI of the default v2 registry
    54  	DefaultV2Registry = &url.URL{
    55  		Scheme: "https",
    56  		Host:   "registry-1.docker.io",
    57  	}
    58  )
    59  
    60  var (
    61  	// ErrInvalidRepositoryName is an error returned if the repository name did
    62  	// not have the correct form
    63  	ErrInvalidRepositoryName = errors.New("Invalid repository name (ex: \"registry.domain.tld/myrepos\")")
    64  
    65  	emptyServiceConfig = newServiceConfig(ServiceOptions{})
    66  )
    67  
    68  // for mocking in unit tests
    69  var lookupIP = net.LookupIP
    70  
    71  // InstallCliFlags adds command-line options to the top-level flag parser for
    72  // the current process.
    73  func (options *ServiceOptions) InstallCliFlags(cmd *flag.FlagSet, usageFn func(string) string) {
    74  	mirrors := opts.NewNamedListOptsRef("registry-mirrors", &options.Mirrors, ValidateMirror)
    75  	cmd.Var(mirrors, []string{"-registry-mirror"}, usageFn("Preferred Docker registry mirror"))
    76  
    77  	insecureRegistries := opts.NewNamedListOptsRef("insecure-registries", &options.InsecureRegistries, ValidateIndexName)
    78  	cmd.Var(insecureRegistries, []string{"-insecure-registry"}, usageFn("Enable insecure registry communication"))
    79  
    80  	cmd.BoolVar(&options.V2Only, []string{"-disable-legacy-registry"}, false, usageFn("Do not contact legacy registries"))
    81  }
    82  
    83  // newServiceConfig returns a new instance of ServiceConfig
    84  func newServiceConfig(options ServiceOptions) *serviceConfig {
    85  	// Localhost is by default considered as an insecure registry
    86  	// This is a stop-gap for people who are running a private registry on localhost (especially on Boot2docker).
    87  	//
    88  	// TODO: should we deprecate this once it is easier for people to set up a TLS registry or change
    89  	// daemon flags on boot2docker?
    90  	options.InsecureRegistries = append(options.InsecureRegistries, "127.0.0.0/8")
    91  
    92  	config := &serviceConfig{
    93  		ServiceConfig: registrytypes.ServiceConfig{
    94  			InsecureRegistryCIDRs: make([]*registrytypes.NetIPNet, 0),
    95  			IndexConfigs:          make(map[string]*registrytypes.IndexInfo, 0),
    96  			// Hack: Bypass setting the mirrors to IndexConfigs since they are going away
    97  			// and Mirrors are only for the official registry anyways.
    98  			Mirrors: options.Mirrors,
    99  		},
   100  		V2Only: options.V2Only,
   101  	}
   102  	// Split --insecure-registry into CIDR and registry-specific settings.
   103  	for _, r := range options.InsecureRegistries {
   104  		// Check if CIDR was passed to --insecure-registry
   105  		_, ipnet, err := net.ParseCIDR(r)
   106  		if err == nil {
   107  			// Valid CIDR.
   108  			config.InsecureRegistryCIDRs = append(config.InsecureRegistryCIDRs, (*registrytypes.NetIPNet)(ipnet))
   109  		} else {
   110  			// Assume `host:port` if not CIDR.
   111  			config.IndexConfigs[r] = &registrytypes.IndexInfo{
   112  				Name:     r,
   113  				Mirrors:  make([]string, 0),
   114  				Secure:   false,
   115  				Official: false,
   116  			}
   117  		}
   118  	}
   119  
   120  	// Configure public registry.
   121  	config.IndexConfigs[IndexName] = &registrytypes.IndexInfo{
   122  		Name:     IndexName,
   123  		Mirrors:  config.Mirrors,
   124  		Secure:   true,
   125  		Official: true,
   126  	}
   127  
   128  	return config
   129  }
   130  
   131  // isSecureIndex returns false if the provided indexName is part of the list of insecure registries
   132  // Insecure registries accept HTTP and/or accept HTTPS with certificates from unknown CAs.
   133  //
   134  // The list of insecure registries can contain an element with CIDR notation to specify a whole subnet.
   135  // If the subnet contains one of the IPs of the registry specified by indexName, the latter is considered
   136  // insecure.
   137  //
   138  // indexName should be a URL.Host (`host:port` or `host`) where the `host` part can be either a domain name
   139  // or an IP address. If it is a domain name, then it will be resolved in order to check if the IP is contained
   140  // in a subnet. If the resolving is not successful, isSecureIndex will only try to match hostname to any element
   141  // of insecureRegistries.
   142  func isSecureIndex(config *serviceConfig, indexName string) bool {
   143  	// Check for configured index, first.  This is needed in case isSecureIndex
   144  	// is called from anything besides newIndexInfo, in order to honor per-index configurations.
   145  	if index, ok := config.IndexConfigs[indexName]; ok {
   146  		return index.Secure
   147  	}
   148  
   149  	host, _, err := net.SplitHostPort(indexName)
   150  	if err != nil {
   151  		// assume indexName is of the form `host` without the port and go on.
   152  		host = indexName
   153  	}
   154  
   155  	addrs, err := lookupIP(host)
   156  	if err != nil {
   157  		ip := net.ParseIP(host)
   158  		if ip != nil {
   159  			addrs = []net.IP{ip}
   160  		}
   161  
   162  		// if ip == nil, then `host` is neither an IP nor it could be looked up,
   163  		// either because the index is unreachable, or because the index is behind an HTTP proxy.
   164  		// So, len(addrs) == 0 and we're not aborting.
   165  	}
   166  
   167  	// Try CIDR notation only if addrs has any elements, i.e. if `host`'s IP could be determined.
   168  	for _, addr := range addrs {
   169  		for _, ipnet := range config.InsecureRegistryCIDRs {
   170  			// check if the addr falls in the subnet
   171  			if (*net.IPNet)(ipnet).Contains(addr) {
   172  				return false
   173  			}
   174  		}
   175  	}
   176  
   177  	return true
   178  }
   179  
   180  // ValidateMirror validates an HTTP(S) registry mirror
   181  func ValidateMirror(val string) (string, error) {
   182  	uri, err := url.Parse(val)
   183  	if err != nil {
   184  		return "", fmt.Errorf("%s is not a valid URI", val)
   185  	}
   186  
   187  	if uri.Scheme != "http" && uri.Scheme != "https" {
   188  		return "", fmt.Errorf("Unsupported scheme %s", uri.Scheme)
   189  	}
   190  
   191  	if uri.Path != "" || uri.RawQuery != "" || uri.Fragment != "" {
   192  		return "", fmt.Errorf("Unsupported path/query/fragment at end of the URI")
   193  	}
   194  
   195  	return fmt.Sprintf("%s://%s/", uri.Scheme, uri.Host), nil
   196  }
   197  
   198  // ValidateIndexName validates an index name.
   199  func ValidateIndexName(val string) (string, error) {
   200  	if val == reference.LegacyDefaultHostname {
   201  		val = reference.DefaultHostname
   202  	}
   203  	if strings.HasPrefix(val, "-") || strings.HasSuffix(val, "-") {
   204  		return "", fmt.Errorf("Invalid index name (%s). Cannot begin or end with a hyphen.", val)
   205  	}
   206  	return val, nil
   207  }
   208  
   209  func validateNoScheme(reposName string) error {
   210  	if strings.Contains(reposName, "://") {
   211  		// It cannot contain a scheme!
   212  		return ErrInvalidRepositoryName
   213  	}
   214  	return nil
   215  }
   216  
   217  // newIndexInfo returns IndexInfo configuration from indexName
   218  func newIndexInfo(config *serviceConfig, indexName string) (*registrytypes.IndexInfo, error) {
   219  	var err error
   220  	indexName, err = ValidateIndexName(indexName)
   221  	if err != nil {
   222  		return nil, err
   223  	}
   224  
   225  	// Return any configured index info, first.
   226  	if index, ok := config.IndexConfigs[indexName]; ok {
   227  		return index, nil
   228  	}
   229  
   230  	// Construct a non-configured index info.
   231  	index := &registrytypes.IndexInfo{
   232  		Name:     indexName,
   233  		Mirrors:  make([]string, 0),
   234  		Official: false,
   235  	}
   236  	index.Secure = isSecureIndex(config, indexName)
   237  	return index, nil
   238  }
   239  
   240  // GetAuthConfigKey special-cases using the full index address of the official
   241  // index as the AuthConfig key, and uses the (host)name[:port] for private indexes.
   242  func GetAuthConfigKey(index *registrytypes.IndexInfo) string {
   243  	if index.Official {
   244  		return IndexServer
   245  	}
   246  	return index.Name
   247  }
   248  
   249  // newRepositoryInfo validates and breaks down a repository name into a RepositoryInfo
   250  func newRepositoryInfo(config *serviceConfig, name reference.Named) (*RepositoryInfo, error) {
   251  	index, err := newIndexInfo(config, name.Hostname())
   252  	if err != nil {
   253  		return nil, err
   254  	}
   255  	official := !strings.ContainsRune(name.Name(), '/')
   256  	return &RepositoryInfo{name, index, official}, nil
   257  }
   258  
   259  // ParseRepositoryInfo performs the breakdown of a repository name into a RepositoryInfo, but
   260  // lacks registry configuration.
   261  func ParseRepositoryInfo(reposName reference.Named) (*RepositoryInfo, error) {
   262  	return newRepositoryInfo(emptyServiceConfig, reposName)
   263  }
   264  
   265  // ParseSearchIndexInfo will use repository name to get back an indexInfo.
   266  func ParseSearchIndexInfo(reposName string) (*registrytypes.IndexInfo, error) {
   267  	indexName, _ := splitReposSearchTerm(reposName)
   268  
   269  	indexInfo, err := newIndexInfo(emptyServiceConfig, indexName)
   270  	if err != nil {
   271  		return nil, err
   272  	}
   273  	return indexInfo, nil
   274  }