github.com/portworx/docker@v1.12.1/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("Disable contacting 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] = ®istrytypes.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] = ®istrytypes.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 := ®istrytypes.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 }