github.com/flavio/docker@v0.1.3-0.20170117145210-f63d1a6eec47/registry/config.go (about) 1 package registry 2 3 import ( 4 "errors" 5 "fmt" 6 "net" 7 "net/url" 8 "strings" 9 10 registrytypes "github.com/docker/docker/api/types/registry" 11 "github.com/docker/docker/opts" 12 "github.com/docker/docker/reference" 13 "github.com/spf13/pflag" 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 // IndexHostname is the index hostname 40 IndexHostname = "index.docker.io" 41 // IndexServer is used for user auth and image search 42 IndexServer = "https://" + IndexHostname + "/v1/" 43 // IndexName is the name of the index 44 IndexName = "docker.io" 45 46 // NotaryServer is the endpoint serving the Notary trust server 47 NotaryServer = "https://notary.docker.io" 48 49 // DefaultV2Registry is the URI of the default v2 registry 50 DefaultV2Registry = &url.URL{ 51 Scheme: "https", 52 Host: "registry-1.docker.io", 53 } 54 ) 55 56 var ( 57 // ErrInvalidRepositoryName is an error returned if the repository name did 58 // not have the correct form 59 ErrInvalidRepositoryName = errors.New("Invalid repository name (ex: \"registry.domain.tld/myrepos\")") 60 61 emptyServiceConfig = newServiceConfig(ServiceOptions{}) 62 ) 63 64 // for mocking in unit tests 65 var lookupIP = net.LookupIP 66 67 // InstallCliFlags adds command-line options to the top-level flag parser for 68 // the current process. 69 func (options *ServiceOptions) InstallCliFlags(flags *pflag.FlagSet) { 70 mirrors := opts.NewNamedListOptsRef("registry-mirrors", &options.Mirrors, ValidateMirror) 71 insecureRegistries := opts.NewNamedListOptsRef("insecure-registries", &options.InsecureRegistries, ValidateIndexName) 72 73 flags.Var(mirrors, "registry-mirror", "Preferred Docker registry mirror") 74 flags.Var(insecureRegistries, "insecure-registry", "Enable insecure registry communication") 75 76 options.installCliPlatformFlags(flags) 77 } 78 79 // newServiceConfig returns a new instance of ServiceConfig 80 func newServiceConfig(options ServiceOptions) *serviceConfig { 81 config := &serviceConfig{ 82 ServiceConfig: registrytypes.ServiceConfig{ 83 InsecureRegistryCIDRs: make([]*registrytypes.NetIPNet, 0), 84 IndexConfigs: make(map[string]*registrytypes.IndexInfo, 0), 85 // Hack: Bypass setting the mirrors to IndexConfigs since they are going away 86 // and Mirrors are only for the official registry anyways. 87 }, 88 V2Only: options.V2Only, 89 } 90 91 config.LoadMirrors(options.Mirrors) 92 config.LoadInsecureRegistries(options.InsecureRegistries) 93 94 return config 95 } 96 97 // LoadMirrors loads mirrors to config, after removing duplicates. 98 // Returns an error if mirrors contains an invalid mirror. 99 func (config *serviceConfig) LoadMirrors(mirrors []string) error { 100 mMap := map[string]struct{}{} 101 unique := []string{} 102 103 for _, mirror := range mirrors { 104 m, err := ValidateMirror(mirror) 105 if err != nil { 106 return err 107 } 108 if _, exist := mMap[m]; !exist { 109 mMap[m] = struct{}{} 110 unique = append(unique, m) 111 } 112 } 113 114 config.Mirrors = unique 115 116 // Configure public registry since mirrors may have changed. 117 config.IndexConfigs[IndexName] = ®istrytypes.IndexInfo{ 118 Name: IndexName, 119 Mirrors: config.Mirrors, 120 Secure: true, 121 Official: true, 122 } 123 124 return nil 125 } 126 127 // LoadInsecureRegistries loads insecure registries to config 128 func (config *serviceConfig) LoadInsecureRegistries(registries []string) error { 129 // Localhost is by default considered as an insecure registry 130 // This is a stop-gap for people who are running a private registry on localhost (especially on Boot2docker). 131 // 132 // TODO: should we deprecate this once it is easier for people to set up a TLS registry or change 133 // daemon flags on boot2docker? 134 registries = append(registries, "127.0.0.0/8") 135 136 // Store original InsecureRegistryCIDRs and IndexConfigs 137 // Clean InsecureRegistryCIDRs and IndexConfigs in config, as passed registries has all insecure registry info. 138 originalCIDRs := config.ServiceConfig.InsecureRegistryCIDRs 139 originalIndexInfos := config.ServiceConfig.IndexConfigs 140 141 config.ServiceConfig.InsecureRegistryCIDRs = make([]*registrytypes.NetIPNet, 0) 142 config.ServiceConfig.IndexConfigs = make(map[string]*registrytypes.IndexInfo, 0) 143 144 skip: 145 for _, r := range registries { 146 // validate insecure registry 147 if _, err := ValidateIndexName(r); err != nil { 148 // before returning err, roll back to original data 149 config.ServiceConfig.InsecureRegistryCIDRs = originalCIDRs 150 config.ServiceConfig.IndexConfigs = originalIndexInfos 151 return err 152 } 153 // Check if CIDR was passed to --insecure-registry 154 _, ipnet, err := net.ParseCIDR(r) 155 if err == nil { 156 // Valid CIDR. If ipnet is already in config.InsecureRegistryCIDRs, skip. 157 data := (*registrytypes.NetIPNet)(ipnet) 158 for _, value := range config.InsecureRegistryCIDRs { 159 if value.IP.String() == data.IP.String() && value.Mask.String() == data.Mask.String() { 160 continue skip 161 } 162 } 163 // ipnet is not found, add it in config.InsecureRegistryCIDRs 164 config.InsecureRegistryCIDRs = append(config.InsecureRegistryCIDRs, data) 165 166 } else { 167 // Assume `host:port` if not CIDR. 168 config.IndexConfigs[r] = ®istrytypes.IndexInfo{ 169 Name: r, 170 Mirrors: make([]string, 0), 171 Secure: false, 172 Official: false, 173 } 174 } 175 } 176 177 // Configure public registry. 178 config.IndexConfigs[IndexName] = ®istrytypes.IndexInfo{ 179 Name: IndexName, 180 Mirrors: config.Mirrors, 181 Secure: true, 182 Official: true, 183 } 184 185 return nil 186 } 187 188 // isSecureIndex returns false if the provided indexName is part of the list of insecure registries 189 // Insecure registries accept HTTP and/or accept HTTPS with certificates from unknown CAs. 190 // 191 // The list of insecure registries can contain an element with CIDR notation to specify a whole subnet. 192 // If the subnet contains one of the IPs of the registry specified by indexName, the latter is considered 193 // insecure. 194 // 195 // indexName should be a URL.Host (`host:port` or `host`) where the `host` part can be either a domain name 196 // or an IP address. If it is a domain name, then it will be resolved in order to check if the IP is contained 197 // in a subnet. If the resolving is not successful, isSecureIndex will only try to match hostname to any element 198 // of insecureRegistries. 199 func isSecureIndex(config *serviceConfig, indexName string) bool { 200 // Check for configured index, first. This is needed in case isSecureIndex 201 // is called from anything besides newIndexInfo, in order to honor per-index configurations. 202 if index, ok := config.IndexConfigs[indexName]; ok { 203 return index.Secure 204 } 205 206 host, _, err := net.SplitHostPort(indexName) 207 if err != nil { 208 // assume indexName is of the form `host` without the port and go on. 209 host = indexName 210 } 211 212 addrs, err := lookupIP(host) 213 if err != nil { 214 ip := net.ParseIP(host) 215 if ip != nil { 216 addrs = []net.IP{ip} 217 } 218 219 // if ip == nil, then `host` is neither an IP nor it could be looked up, 220 // either because the index is unreachable, or because the index is behind an HTTP proxy. 221 // So, len(addrs) == 0 and we're not aborting. 222 } 223 224 // Try CIDR notation only if addrs has any elements, i.e. if `host`'s IP could be determined. 225 for _, addr := range addrs { 226 for _, ipnet := range config.InsecureRegistryCIDRs { 227 // check if the addr falls in the subnet 228 if (*net.IPNet)(ipnet).Contains(addr) { 229 return false 230 } 231 } 232 } 233 234 return true 235 } 236 237 // ValidateMirror validates an HTTP(S) registry mirror 238 func ValidateMirror(val string) (string, error) { 239 uri, err := url.Parse(val) 240 if err != nil { 241 return "", fmt.Errorf("invalid mirror: %q is not a valid URI", val) 242 } 243 if uri.Scheme != "http" && uri.Scheme != "https" { 244 return "", fmt.Errorf("invalid mirror: unsupported scheme %q in %q", uri.Scheme, uri) 245 } 246 if (uri.Path != "" && uri.Path != "/") || uri.RawQuery != "" || uri.Fragment != "" { 247 return "", fmt.Errorf("invalid mirror: path, query, or fragment at end of the URI %q", uri) 248 } 249 if uri.User != nil { 250 // strip password from output 251 uri.User = url.UserPassword(uri.User.Username(), "xxxxx") 252 return "", fmt.Errorf("invalid mirror: username/password not allowed in URI %q", uri) 253 } 254 return strings.TrimSuffix(val, "/") + "/", nil 255 } 256 257 // ValidateIndexName validates an index name. 258 func ValidateIndexName(val string) (string, error) { 259 if val == reference.LegacyDefaultHostname { 260 val = reference.DefaultHostname 261 } 262 if strings.HasPrefix(val, "-") || strings.HasSuffix(val, "-") { 263 return "", fmt.Errorf("Invalid index name (%s). Cannot begin or end with a hyphen.", val) 264 } 265 return val, nil 266 } 267 268 func validateNoScheme(reposName string) error { 269 if strings.Contains(reposName, "://") { 270 // It cannot contain a scheme! 271 return ErrInvalidRepositoryName 272 } 273 return nil 274 } 275 276 // newIndexInfo returns IndexInfo configuration from indexName 277 func newIndexInfo(config *serviceConfig, indexName string) (*registrytypes.IndexInfo, error) { 278 var err error 279 indexName, err = ValidateIndexName(indexName) 280 if err != nil { 281 return nil, err 282 } 283 284 // Return any configured index info, first. 285 if index, ok := config.IndexConfigs[indexName]; ok { 286 return index, nil 287 } 288 289 // Construct a non-configured index info. 290 index := ®istrytypes.IndexInfo{ 291 Name: indexName, 292 Mirrors: make([]string, 0), 293 Official: false, 294 } 295 index.Secure = isSecureIndex(config, indexName) 296 return index, nil 297 } 298 299 // GetAuthConfigKey special-cases using the full index address of the official 300 // index as the AuthConfig key, and uses the (host)name[:port] for private indexes. 301 func GetAuthConfigKey(index *registrytypes.IndexInfo) string { 302 if index.Official { 303 return IndexServer 304 } 305 return index.Name 306 } 307 308 // newRepositoryInfo validates and breaks down a repository name into a RepositoryInfo 309 func newRepositoryInfo(config *serviceConfig, name reference.Named) (*RepositoryInfo, error) { 310 index, err := newIndexInfo(config, name.Hostname()) 311 if err != nil { 312 return nil, err 313 } 314 official := !strings.ContainsRune(name.Name(), '/') 315 return &RepositoryInfo{ 316 Named: name, 317 Index: index, 318 Official: official, 319 }, nil 320 } 321 322 // ParseRepositoryInfo performs the breakdown of a repository name into a RepositoryInfo, but 323 // lacks registry configuration. 324 func ParseRepositoryInfo(reposName reference.Named) (*RepositoryInfo, error) { 325 return newRepositoryInfo(emptyServiceConfig, reposName) 326 } 327 328 // ParseSearchIndexInfo will use repository name to get back an indexInfo. 329 func ParseSearchIndexInfo(reposName string) (*registrytypes.IndexInfo, error) { 330 indexName, _ := splitReposSearchTerm(reposName) 331 332 indexInfo, err := newIndexInfo(emptyServiceConfig, indexName) 333 if err != nil { 334 return nil, err 335 } 336 return indexInfo, nil 337 }