test
[platform/upstream/docker-engine.git] / registry / config.go
1 package registry
2
3 import (
4         "fmt"
5         "net"
6         "net/url"
7         "regexp"
8         "strconv"
9         "strings"
10
11         "github.com/Sirupsen/logrus"
12         "github.com/docker/distribution/reference"
13         registrytypes "github.com/docker/docker/api/types/registry"
14         "github.com/docker/docker/opts"
15         "github.com/pkg/errors"
16         "github.com/spf13/pflag"
17 )
18
19 // ServiceOptions holds command line options.
20 type ServiceOptions struct {
21         AllowNondistributableArtifacts []string `json:"allow-nondistributable-artifacts,omitempty"`
22         Mirrors                        []string `json:"registry-mirrors,omitempty"`
23         InsecureRegistries             []string `json:"insecure-registries,omitempty"`
24
25         // V2Only controls access to legacy registries.  If it is set to true via the
26         // command line flag the daemon will not attempt to contact v1 legacy registries
27         V2Only bool `json:"disable-legacy-registry,omitempty"`
28 }
29
30 // serviceConfig holds daemon configuration for the registry service.
31 type serviceConfig struct {
32         registrytypes.ServiceConfig
33         V2Only bool
34 }
35
36 var (
37         // DefaultNamespace is the default namespace
38         DefaultNamespace = "docker.io"
39         // DefaultRegistryVersionHeader is the name of the default HTTP header
40         // that carries Registry version info
41         DefaultRegistryVersionHeader = "Docker-Distribution-Api-Version"
42
43         // IndexHostname is the index hostname
44         IndexHostname = "index.docker.io"
45         // IndexServer is used for user auth and image search
46         IndexServer = "https://" + IndexHostname + "/v1/"
47         // IndexName is the name of the index
48         IndexName = "docker.io"
49
50         // NotaryServer is the endpoint serving the Notary trust server
51         NotaryServer = "https://notary.docker.io"
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 var (
69         validHostPortRegex = regexp.MustCompile(`^` + reference.DomainRegexp.String() + `$`)
70 )
71
72 // for mocking in unit tests
73 var lookupIP = net.LookupIP
74
75 // InstallCliFlags adds command-line options to the top-level flag parser for
76 // the current process.
77 func (options *ServiceOptions) InstallCliFlags(flags *pflag.FlagSet) {
78         ana := opts.NewNamedListOptsRef("allow-nondistributable-artifacts", &options.AllowNondistributableArtifacts, ValidateIndexName)
79         mirrors := opts.NewNamedListOptsRef("registry-mirrors", &options.Mirrors, ValidateMirror)
80         insecureRegistries := opts.NewNamedListOptsRef("insecure-registries", &options.InsecureRegistries, ValidateIndexName)
81
82         flags.Var(ana, "allow-nondistributable-artifacts", "Allow push of nondistributable artifacts to registry")
83         flags.Var(mirrors, "registry-mirror", "Preferred Docker registry mirror")
84         flags.Var(insecureRegistries, "insecure-registry", "Enable insecure registry communication")
85
86         options.installCliPlatformFlags(flags)
87 }
88
89 // newServiceConfig returns a new instance of ServiceConfig
90 func newServiceConfig(options ServiceOptions) *serviceConfig {
91         config := &serviceConfig{
92                 ServiceConfig: registrytypes.ServiceConfig{
93                         InsecureRegistryCIDRs: make([]*registrytypes.NetIPNet, 0),
94                         IndexConfigs:          make(map[string]*registrytypes.IndexInfo, 0),
95                         // Hack: Bypass setting the mirrors to IndexConfigs since they are going away
96                         // and Mirrors are only for the official registry anyways.
97                 },
98                 V2Only: options.V2Only,
99         }
100
101         config.LoadAllowNondistributableArtifacts(options.AllowNondistributableArtifacts)
102         config.LoadMirrors(options.Mirrors)
103         config.LoadInsecureRegistries(options.InsecureRegistries)
104
105         return config
106 }
107
108 // LoadAllowNondistributableArtifacts loads allow-nondistributable-artifacts registries into config.
109 func (config *serviceConfig) LoadAllowNondistributableArtifacts(registries []string) error {
110         cidrs := map[string]*registrytypes.NetIPNet{}
111         hostnames := map[string]bool{}
112
113         for _, r := range registries {
114                 if _, err := ValidateIndexName(r); err != nil {
115                         return err
116                 }
117                 if validateNoScheme(r) != nil {
118                         return fmt.Errorf("allow-nondistributable-artifacts registry %s should not contain '://'", r)
119                 }
120
121                 if _, ipnet, err := net.ParseCIDR(r); err == nil {
122                         // Valid CIDR.
123                         cidrs[ipnet.String()] = (*registrytypes.NetIPNet)(ipnet)
124                 } else if err := validateHostPort(r); err == nil {
125                         // Must be `host:port` if not CIDR.
126                         hostnames[r] = true
127                 } else {
128                         return fmt.Errorf("allow-nondistributable-artifacts registry %s is not valid: %v", r, err)
129                 }
130         }
131
132         config.AllowNondistributableArtifactsCIDRs = make([]*(registrytypes.NetIPNet), 0)
133         for _, c := range cidrs {
134                 config.AllowNondistributableArtifactsCIDRs = append(config.AllowNondistributableArtifactsCIDRs, c)
135         }
136
137         config.AllowNondistributableArtifactsHostnames = make([]string, 0)
138         for h := range hostnames {
139                 config.AllowNondistributableArtifactsHostnames = append(config.AllowNondistributableArtifactsHostnames, h)
140         }
141
142         return nil
143 }
144
145 // LoadMirrors loads mirrors to config, after removing duplicates.
146 // Returns an error if mirrors contains an invalid mirror.
147 func (config *serviceConfig) LoadMirrors(mirrors []string) error {
148         mMap := map[string]struct{}{}
149         unique := []string{}
150
151         for _, mirror := range mirrors {
152                 m, err := ValidateMirror(mirror)
153                 if err != nil {
154                         return err
155                 }
156                 if _, exist := mMap[m]; !exist {
157                         mMap[m] = struct{}{}
158                         unique = append(unique, m)
159                 }
160         }
161
162         config.Mirrors = unique
163
164         // Configure public registry since mirrors may have changed.
165         config.IndexConfigs[IndexName] = &registrytypes.IndexInfo{
166                 Name:     IndexName,
167                 Mirrors:  config.Mirrors,
168                 Secure:   true,
169                 Official: true,
170         }
171
172         return nil
173 }
174
175 // LoadInsecureRegistries loads insecure registries to config
176 func (config *serviceConfig) LoadInsecureRegistries(registries []string) error {
177         // Localhost is by default considered as an insecure registry
178         // This is a stop-gap for people who are running a private registry on localhost (especially on Boot2docker).
179         //
180         // TODO: should we deprecate this once it is easier for people to set up a TLS registry or change
181         // daemon flags on boot2docker?
182         registries = append(registries, "127.0.0.0/8")
183
184         // Store original InsecureRegistryCIDRs and IndexConfigs
185         // Clean InsecureRegistryCIDRs and IndexConfigs in config, as passed registries has all insecure registry info.
186         originalCIDRs := config.ServiceConfig.InsecureRegistryCIDRs
187         originalIndexInfos := config.ServiceConfig.IndexConfigs
188
189         config.ServiceConfig.InsecureRegistryCIDRs = make([]*registrytypes.NetIPNet, 0)
190         config.ServiceConfig.IndexConfigs = make(map[string]*registrytypes.IndexInfo, 0)
191
192 skip:
193         for _, r := range registries {
194                 // validate insecure registry
195                 if _, err := ValidateIndexName(r); err != nil {
196                         // before returning err, roll back to original data
197                         config.ServiceConfig.InsecureRegistryCIDRs = originalCIDRs
198                         config.ServiceConfig.IndexConfigs = originalIndexInfos
199                         return err
200                 }
201                 if strings.HasPrefix(strings.ToLower(r), "http://") {
202                         logrus.Warnf("insecure registry %s should not contain 'http://' and 'http://' has been removed from the insecure registry config", r)
203                         r = r[7:]
204                 } else if strings.HasPrefix(strings.ToLower(r), "https://") {
205                         logrus.Warnf("insecure registry %s should not contain 'https://' and 'https://' has been removed from the insecure registry config", r)
206                         r = r[8:]
207                 } else if validateNoScheme(r) != nil {
208                         // Insecure registry should not contain '://'
209                         // before returning err, roll back to original data
210                         config.ServiceConfig.InsecureRegistryCIDRs = originalCIDRs
211                         config.ServiceConfig.IndexConfigs = originalIndexInfos
212                         return fmt.Errorf("insecure registry %s should not contain '://'", r)
213                 }
214                 // Check if CIDR was passed to --insecure-registry
215                 _, ipnet, err := net.ParseCIDR(r)
216                 if err == nil {
217                         // Valid CIDR. If ipnet is already in config.InsecureRegistryCIDRs, skip.
218                         data := (*registrytypes.NetIPNet)(ipnet)
219                         for _, value := range config.InsecureRegistryCIDRs {
220                                 if value.IP.String() == data.IP.String() && value.Mask.String() == data.Mask.String() {
221                                         continue skip
222                                 }
223                         }
224                         // ipnet is not found, add it in config.InsecureRegistryCIDRs
225                         config.InsecureRegistryCIDRs = append(config.InsecureRegistryCIDRs, data)
226
227                 } else {
228                         if err := validateHostPort(r); err != nil {
229                                 config.ServiceConfig.InsecureRegistryCIDRs = originalCIDRs
230                                 config.ServiceConfig.IndexConfigs = originalIndexInfos
231                                 return fmt.Errorf("insecure registry %s is not valid: %v", r, err)
232
233                         }
234                         // Assume `host:port` if not CIDR.
235                         config.IndexConfigs[r] = &registrytypes.IndexInfo{
236                                 Name:     r,
237                                 Mirrors:  make([]string, 0),
238                                 Secure:   false,
239                                 Official: false,
240                         }
241                 }
242         }
243
244         // Configure public registry.
245         config.IndexConfigs[IndexName] = &registrytypes.IndexInfo{
246                 Name:     IndexName,
247                 Mirrors:  config.Mirrors,
248                 Secure:   true,
249                 Official: true,
250         }
251
252         return nil
253 }
254
255 // allowNondistributableArtifacts returns true if the provided hostname is part of the list of regsitries
256 // that allow push of nondistributable artifacts.
257 //
258 // The list can contain elements with CIDR notation to specify a whole subnet. If the subnet contains an IP
259 // of the registry specified by hostname, true is returned.
260 //
261 // hostname should be a URL.Host (`host:port` or `host`) where the `host` part can be either a domain name
262 // or an IP address. If it is a domain name, then it will be resolved to IP addresses for matching. If
263 // resolution fails, CIDR matching is not performed.
264 func allowNondistributableArtifacts(config *serviceConfig, hostname string) bool {
265         for _, h := range config.AllowNondistributableArtifactsHostnames {
266                 if h == hostname {
267                         return true
268                 }
269         }
270
271         return isCIDRMatch(config.AllowNondistributableArtifactsCIDRs, hostname)
272 }
273
274 // isSecureIndex returns false if the provided indexName is part of the list of insecure registries
275 // Insecure registries accept HTTP and/or accept HTTPS with certificates from unknown CAs.
276 //
277 // The list of insecure registries can contain an element with CIDR notation to specify a whole subnet.
278 // If the subnet contains one of the IPs of the registry specified by indexName, the latter is considered
279 // insecure.
280 //
281 // indexName should be a URL.Host (`host:port` or `host`) where the `host` part can be either a domain name
282 // or an IP address. If it is a domain name, then it will be resolved in order to check if the IP is contained
283 // in a subnet. If the resolving is not successful, isSecureIndex will only try to match hostname to any element
284 // of insecureRegistries.
285 func isSecureIndex(config *serviceConfig, indexName string) bool {
286         // Check for configured index, first.  This is needed in case isSecureIndex
287         // is called from anything besides newIndexInfo, in order to honor per-index configurations.
288         if index, ok := config.IndexConfigs[indexName]; ok {
289                 return index.Secure
290         }
291
292         return !isCIDRMatch(config.InsecureRegistryCIDRs, indexName)
293 }
294
295 // isCIDRMatch returns true if URLHost matches an element of cidrs. URLHost is a URL.Host (`host:port` or `host`)
296 // where the `host` part can be either a domain name or an IP address. If it is a domain name, then it will be
297 // resolved to IP addresses for matching. If resolution fails, false is returned.
298 func isCIDRMatch(cidrs []*registrytypes.NetIPNet, URLHost string) bool {
299         host, _, err := net.SplitHostPort(URLHost)
300         if err != nil {
301                 // Assume URLHost is of the form `host` without the port and go on.
302                 host = URLHost
303         }
304
305         addrs, err := lookupIP(host)
306         if err != nil {
307                 ip := net.ParseIP(host)
308                 if ip != nil {
309                         addrs = []net.IP{ip}
310                 }
311
312                 // if ip == nil, then `host` is neither an IP nor it could be looked up,
313                 // either because the index is unreachable, or because the index is behind an HTTP proxy.
314                 // So, len(addrs) == 0 and we're not aborting.
315         }
316
317         // Try CIDR notation only if addrs has any elements, i.e. if `host`'s IP could be determined.
318         for _, addr := range addrs {
319                 for _, ipnet := range cidrs {
320                         // check if the addr falls in the subnet
321                         if (*net.IPNet)(ipnet).Contains(addr) {
322                                 return true
323                         }
324                 }
325         }
326
327         return false
328 }
329
330 // ValidateMirror validates an HTTP(S) registry mirror
331 func ValidateMirror(val string) (string, error) {
332         uri, err := url.Parse(val)
333         if err != nil {
334                 return "", fmt.Errorf("invalid mirror: %q is not a valid URI", val)
335         }
336         if uri.Scheme != "http" && uri.Scheme != "https" {
337                 return "", fmt.Errorf("invalid mirror: unsupported scheme %q in %q", uri.Scheme, uri)
338         }
339         if (uri.Path != "" && uri.Path != "/") || uri.RawQuery != "" || uri.Fragment != "" {
340                 return "", fmt.Errorf("invalid mirror: path, query, or fragment at end of the URI %q", uri)
341         }
342         if uri.User != nil {
343                 // strip password from output
344                 uri.User = url.UserPassword(uri.User.Username(), "xxxxx")
345                 return "", fmt.Errorf("invalid mirror: username/password not allowed in URI %q", uri)
346         }
347         return strings.TrimSuffix(val, "/") + "/", nil
348 }
349
350 // ValidateIndexName validates an index name.
351 func ValidateIndexName(val string) (string, error) {
352         // TODO: upstream this to check to reference package
353         if val == "index.docker.io" {
354                 val = "docker.io"
355         }
356         if strings.HasPrefix(val, "-") || strings.HasSuffix(val, "-") {
357                 return "", fmt.Errorf("Invalid index name (%s). Cannot begin or end with a hyphen.", val)
358         }
359         return val, nil
360 }
361
362 func validateNoScheme(reposName string) error {
363         if strings.Contains(reposName, "://") {
364                 // It cannot contain a scheme!
365                 return ErrInvalidRepositoryName
366         }
367         return nil
368 }
369
370 func validateHostPort(s string) error {
371         // Split host and port, and in case s can not be splitted, assume host only
372         host, port, err := net.SplitHostPort(s)
373         if err != nil {
374                 host = s
375                 port = ""
376         }
377         // If match against the `host:port` pattern fails,
378         // it might be `IPv6:port`, which will be captured by net.ParseIP(host)
379         if !validHostPortRegex.MatchString(s) && net.ParseIP(host) == nil {
380                 return fmt.Errorf("invalid host %q", host)
381         }
382         if port != "" {
383                 v, err := strconv.Atoi(port)
384                 if err != nil {
385                         return err
386                 }
387                 if v < 0 || v > 65535 {
388                         return fmt.Errorf("invalid port %q", port)
389                 }
390         }
391         return nil
392 }
393
394 // newIndexInfo returns IndexInfo configuration from indexName
395 func newIndexInfo(config *serviceConfig, indexName string) (*registrytypes.IndexInfo, error) {
396         var err error
397         indexName, err = ValidateIndexName(indexName)
398         if err != nil {
399                 return nil, err
400         }
401
402         // Return any configured index info, first.
403         if index, ok := config.IndexConfigs[indexName]; ok {
404                 return index, nil
405         }
406
407         // Construct a non-configured index info.
408         index := &registrytypes.IndexInfo{
409                 Name:     indexName,
410                 Mirrors:  make([]string, 0),
411                 Official: false,
412         }
413         index.Secure = isSecureIndex(config, indexName)
414         return index, nil
415 }
416
417 // GetAuthConfigKey special-cases using the full index address of the official
418 // index as the AuthConfig key, and uses the (host)name[:port] for private indexes.
419 func GetAuthConfigKey(index *registrytypes.IndexInfo) string {
420         if index.Official {
421                 return IndexServer
422         }
423         return index.Name
424 }
425
426 // newRepositoryInfo validates and breaks down a repository name into a RepositoryInfo
427 func newRepositoryInfo(config *serviceConfig, name reference.Named) (*RepositoryInfo, error) {
428         index, err := newIndexInfo(config, reference.Domain(name))
429         if err != nil {
430                 return nil, err
431         }
432         official := !strings.ContainsRune(reference.FamiliarName(name), '/')
433
434         return &RepositoryInfo{
435                 Name:     reference.TrimNamed(name),
436                 Index:    index,
437                 Official: official,
438         }, nil
439 }
440
441 // ParseRepositoryInfo performs the breakdown of a repository name into a RepositoryInfo, but
442 // lacks registry configuration.
443 func ParseRepositoryInfo(reposName reference.Named) (*RepositoryInfo, error) {
444         return newRepositoryInfo(emptyServiceConfig, reposName)
445 }
446
447 // ParseSearchIndexInfo will use repository name to get back an indexInfo.
448 func ParseSearchIndexInfo(reposName string) (*registrytypes.IndexInfo, error) {
449         indexName, _ := splitReposSearchTerm(reposName)
450
451         indexInfo, err := newIndexInfo(emptyServiceConfig, indexName)
452         if err != nil {
453                 return nil, err
454         }
455         return indexInfo, nil
456 }