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