github.com/endophage/docker@v1.4.2-0.20161027011718-242853499895/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 // 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(flags *pflag.FlagSet) { 74 mirrors := opts.NewNamedListOptsRef("registry-mirrors", &options.Mirrors, ValidateMirror) 75 insecureRegistries := opts.NewNamedListOptsRef("insecure-registries", &options.InsecureRegistries, ValidateIndexName) 76 77 flags.Var(mirrors, "registry-mirror", "Preferred Docker registry mirror") 78 flags.Var(insecureRegistries, "insecure-registry", "Enable insecure registry communication") 79 80 options.installCliPlatformFlags(flags) 81 } 82 83 // newServiceConfig returns a new instance of ServiceConfig 84 func newServiceConfig(options ServiceOptions) *serviceConfig { 85 config := &serviceConfig{ 86 ServiceConfig: registrytypes.ServiceConfig{ 87 InsecureRegistryCIDRs: make([]*registrytypes.NetIPNet, 0), 88 IndexConfigs: make(map[string]*registrytypes.IndexInfo, 0), 89 // Hack: Bypass setting the mirrors to IndexConfigs since they are going away 90 // and Mirrors are only for the official registry anyways. 91 Mirrors: options.Mirrors, 92 }, 93 V2Only: options.V2Only, 94 } 95 96 config.LoadInsecureRegistries(options.InsecureRegistries) 97 98 return config 99 } 100 101 // LoadInsecureRegistries loads insecure registries to config 102 func (config *serviceConfig) LoadInsecureRegistries(registries []string) error { 103 // Localhost is by default considered as an insecure registry 104 // This is a stop-gap for people who are running a private registry on localhost (especially on Boot2docker). 105 // 106 // TODO: should we deprecate this once it is easier for people to set up a TLS registry or change 107 // daemon flags on boot2docker? 108 registries = append(registries, "127.0.0.0/8") 109 110 // Store original InsecureRegistryCIDRs and IndexConfigs 111 // Clean InsecureRegistryCIDRs and IndexConfigs in config, as passed registries has all insecure registry info. 112 originalCIDRs := config.ServiceConfig.InsecureRegistryCIDRs 113 originalIndexInfos := config.ServiceConfig.IndexConfigs 114 115 config.ServiceConfig.InsecureRegistryCIDRs = make([]*registrytypes.NetIPNet, 0) 116 config.ServiceConfig.IndexConfigs = make(map[string]*registrytypes.IndexInfo, 0) 117 118 skip: 119 for _, r := range registries { 120 // validate insecure registry 121 if _, err := ValidateIndexName(r); err != nil { 122 // before returning err, roll back to original data 123 config.ServiceConfig.InsecureRegistryCIDRs = originalCIDRs 124 config.ServiceConfig.IndexConfigs = originalIndexInfos 125 return err 126 } 127 // Check if CIDR was passed to --insecure-registry 128 _, ipnet, err := net.ParseCIDR(r) 129 if err == nil { 130 // Valid CIDR. If ipnet is already in config.InsecureRegistryCIDRs, skip. 131 data := (*registrytypes.NetIPNet)(ipnet) 132 for _, value := range config.InsecureRegistryCIDRs { 133 if value.IP.String() == data.IP.String() && value.Mask.String() == data.Mask.String() { 134 continue skip 135 } 136 } 137 // ipnet is not found, add it in config.InsecureRegistryCIDRs 138 config.InsecureRegistryCIDRs = append(config.InsecureRegistryCIDRs, data) 139 140 } else { 141 // Assume `host:port` if not CIDR. 142 config.IndexConfigs[r] = ®istrytypes.IndexInfo{ 143 Name: r, 144 Mirrors: make([]string, 0), 145 Secure: false, 146 Official: false, 147 } 148 } 149 } 150 151 // Configure public registry. 152 config.IndexConfigs[IndexName] = ®istrytypes.IndexInfo{ 153 Name: IndexName, 154 Mirrors: config.Mirrors, 155 Secure: true, 156 Official: true, 157 } 158 159 return nil 160 } 161 162 // isSecureIndex returns false if the provided indexName is part of the list of insecure registries 163 // Insecure registries accept HTTP and/or accept HTTPS with certificates from unknown CAs. 164 // 165 // The list of insecure registries can contain an element with CIDR notation to specify a whole subnet. 166 // If the subnet contains one of the IPs of the registry specified by indexName, the latter is considered 167 // insecure. 168 // 169 // indexName should be a URL.Host (`host:port` or `host`) where the `host` part can be either a domain name 170 // or an IP address. If it is a domain name, then it will be resolved in order to check if the IP is contained 171 // in a subnet. If the resolving is not successful, isSecureIndex will only try to match hostname to any element 172 // of insecureRegistries. 173 func isSecureIndex(config *serviceConfig, indexName string) bool { 174 // Check for configured index, first. This is needed in case isSecureIndex 175 // is called from anything besides newIndexInfo, in order to honor per-index configurations. 176 if index, ok := config.IndexConfigs[indexName]; ok { 177 return index.Secure 178 } 179 180 host, _, err := net.SplitHostPort(indexName) 181 if err != nil { 182 // assume indexName is of the form `host` without the port and go on. 183 host = indexName 184 } 185 186 addrs, err := lookupIP(host) 187 if err != nil { 188 ip := net.ParseIP(host) 189 if ip != nil { 190 addrs = []net.IP{ip} 191 } 192 193 // if ip == nil, then `host` is neither an IP nor it could be looked up, 194 // either because the index is unreachable, or because the index is behind an HTTP proxy. 195 // So, len(addrs) == 0 and we're not aborting. 196 } 197 198 // Try CIDR notation only if addrs has any elements, i.e. if `host`'s IP could be determined. 199 for _, addr := range addrs { 200 for _, ipnet := range config.InsecureRegistryCIDRs { 201 // check if the addr falls in the subnet 202 if (*net.IPNet)(ipnet).Contains(addr) { 203 return false 204 } 205 } 206 } 207 208 return true 209 } 210 211 // ValidateMirror validates an HTTP(S) registry mirror 212 func ValidateMirror(val string) (string, error) { 213 uri, err := url.Parse(val) 214 if err != nil { 215 return "", fmt.Errorf("%s is not a valid URI", val) 216 } 217 218 if uri.Scheme != "http" && uri.Scheme != "https" { 219 return "", fmt.Errorf("Unsupported scheme %s", uri.Scheme) 220 } 221 222 if uri.Path != "" || uri.RawQuery != "" || uri.Fragment != "" { 223 return "", fmt.Errorf("Unsupported path/query/fragment at end of the URI") 224 } 225 226 return fmt.Sprintf("%s://%s/", uri.Scheme, uri.Host), nil 227 } 228 229 // ValidateIndexName validates an index name. 230 func ValidateIndexName(val string) (string, error) { 231 if val == reference.LegacyDefaultHostname { 232 val = reference.DefaultHostname 233 } 234 if strings.HasPrefix(val, "-") || strings.HasSuffix(val, "-") { 235 return "", fmt.Errorf("Invalid index name (%s). Cannot begin or end with a hyphen.", val) 236 } 237 return val, nil 238 } 239 240 func validateNoScheme(reposName string) error { 241 if strings.Contains(reposName, "://") { 242 // It cannot contain a scheme! 243 return ErrInvalidRepositoryName 244 } 245 return nil 246 } 247 248 // newIndexInfo returns IndexInfo configuration from indexName 249 func newIndexInfo(config *serviceConfig, indexName string) (*registrytypes.IndexInfo, error) { 250 var err error 251 indexName, err = ValidateIndexName(indexName) 252 if err != nil { 253 return nil, err 254 } 255 256 // Return any configured index info, first. 257 if index, ok := config.IndexConfigs[indexName]; ok { 258 return index, nil 259 } 260 261 // Construct a non-configured index info. 262 index := ®istrytypes.IndexInfo{ 263 Name: indexName, 264 Mirrors: make([]string, 0), 265 Official: false, 266 } 267 index.Secure = isSecureIndex(config, indexName) 268 return index, nil 269 } 270 271 // GetAuthConfigKey special-cases using the full index address of the official 272 // index as the AuthConfig key, and uses the (host)name[:port] for private indexes. 273 func GetAuthConfigKey(index *registrytypes.IndexInfo) string { 274 if index.Official { 275 return IndexServer 276 } 277 return index.Name 278 } 279 280 // newRepositoryInfo validates and breaks down a repository name into a RepositoryInfo 281 func newRepositoryInfo(config *serviceConfig, name reference.Named) (*RepositoryInfo, error) { 282 index, err := newIndexInfo(config, name.Hostname()) 283 if err != nil { 284 return nil, err 285 } 286 official := !strings.ContainsRune(name.Name(), '/') 287 return &RepositoryInfo{name, index, official}, nil 288 } 289 290 // ParseRepositoryInfo performs the breakdown of a repository name into a RepositoryInfo, but 291 // lacks registry configuration. 292 func ParseRepositoryInfo(reposName reference.Named) (*RepositoryInfo, error) { 293 return newRepositoryInfo(emptyServiceConfig, reposName) 294 } 295 296 // ParseSearchIndexInfo will use repository name to get back an indexInfo. 297 func ParseSearchIndexInfo(reposName string) (*registrytypes.IndexInfo, error) { 298 indexName, _ := splitReposSearchTerm(reposName) 299 300 indexInfo, err := newIndexInfo(emptyServiceConfig, indexName) 301 if err != nil { 302 return nil, err 303 } 304 return indexInfo, nil 305 }