config.go 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. package registry
  2. import (
  3. "errors"
  4. "fmt"
  5. "net"
  6. "net/url"
  7. "strings"
  8. "github.com/docker/docker/opts"
  9. flag "github.com/docker/docker/pkg/mflag"
  10. "github.com/docker/docker/reference"
  11. registrytypes "github.com/docker/engine-api/types/registry"
  12. )
  13. // ServiceOptions holds command line options.
  14. type ServiceOptions struct {
  15. Mirrors []string `json:"registry-mirrors,omitempty"`
  16. InsecureRegistries []string `json:"insecure-registries,omitempty"`
  17. // V2Only controls access to legacy registries. If it is set to true via the
  18. // command line flag the daemon will not attempt to contact v1 legacy registries
  19. V2Only bool `json:"disable-legacy-registry,omitempty"`
  20. }
  21. // serviceConfig holds daemon configuration for the registry service.
  22. type serviceConfig struct {
  23. registrytypes.ServiceConfig
  24. V2Only bool
  25. }
  26. var (
  27. // DefaultNamespace is the default namespace
  28. DefaultNamespace = "docker.io"
  29. // DefaultRegistryVersionHeader is the name of the default HTTP header
  30. // that carries Registry version info
  31. DefaultRegistryVersionHeader = "Docker-Distribution-Api-Version"
  32. // IndexServer is the v1 registry server used for user auth + account creation
  33. IndexServer = DefaultV1Registry.String() + "/v1/"
  34. // IndexName is the name of the index
  35. IndexName = "docker.io"
  36. // NotaryServer is the endpoint serving the Notary trust server
  37. NotaryServer = "https://notary.docker.io"
  38. // DefaultV1Registry is the URI of the default v1 registry
  39. DefaultV1Registry = &url.URL{
  40. Scheme: "https",
  41. Host: "index.docker.io",
  42. }
  43. // DefaultV2Registry is the URI of the default v2 registry
  44. DefaultV2Registry = &url.URL{
  45. Scheme: "https",
  46. Host: "registry-1.docker.io",
  47. }
  48. )
  49. var (
  50. // ErrInvalidRepositoryName is an error returned if the repository name did
  51. // not have the correct form
  52. ErrInvalidRepositoryName = errors.New("Invalid repository name (ex: \"registry.domain.tld/myrepos\")")
  53. emptyServiceConfig = newServiceConfig(ServiceOptions{})
  54. )
  55. // for mocking in unit tests
  56. var lookupIP = net.LookupIP
  57. // InstallCliFlags adds command-line options to the top-level flag parser for
  58. // the current process.
  59. func (options *ServiceOptions) InstallCliFlags(cmd *flag.FlagSet, usageFn func(string) string) {
  60. mirrors := opts.NewNamedListOptsRef("registry-mirrors", &options.Mirrors, ValidateMirror)
  61. cmd.Var(mirrors, []string{"-registry-mirror"}, usageFn("Preferred Docker registry mirror"))
  62. insecureRegistries := opts.NewNamedListOptsRef("insecure-registries", &options.InsecureRegistries, ValidateIndexName)
  63. cmd.Var(insecureRegistries, []string{"-insecure-registry"}, usageFn("Enable insecure registry communication"))
  64. options.installCliPlatformFlags(cmd, usageFn)
  65. }
  66. // newServiceConfig returns a new instance of ServiceConfig
  67. func newServiceConfig(options ServiceOptions) *serviceConfig {
  68. // Localhost is by default considered as an insecure registry
  69. // This is a stop-gap for people who are running a private registry on localhost (especially on Boot2docker).
  70. //
  71. // TODO: should we deprecate this once it is easier for people to set up a TLS registry or change
  72. // daemon flags on boot2docker?
  73. options.InsecureRegistries = append(options.InsecureRegistries, "127.0.0.0/8")
  74. config := &serviceConfig{
  75. ServiceConfig: registrytypes.ServiceConfig{
  76. InsecureRegistryCIDRs: make([]*registrytypes.NetIPNet, 0),
  77. IndexConfigs: make(map[string]*registrytypes.IndexInfo, 0),
  78. // Hack: Bypass setting the mirrors to IndexConfigs since they are going away
  79. // and Mirrors are only for the official registry anyways.
  80. Mirrors: options.Mirrors,
  81. },
  82. V2Only: options.V2Only,
  83. }
  84. // Split --insecure-registry into CIDR and registry-specific settings.
  85. for _, r := range options.InsecureRegistries {
  86. // Check if CIDR was passed to --insecure-registry
  87. _, ipnet, err := net.ParseCIDR(r)
  88. if err == nil {
  89. // Valid CIDR.
  90. config.InsecureRegistryCIDRs = append(config.InsecureRegistryCIDRs, (*registrytypes.NetIPNet)(ipnet))
  91. } else {
  92. // Assume `host:port` if not CIDR.
  93. config.IndexConfigs[r] = &registrytypes.IndexInfo{
  94. Name: r,
  95. Mirrors: make([]string, 0),
  96. Secure: false,
  97. Official: false,
  98. }
  99. }
  100. }
  101. // Configure public registry.
  102. config.IndexConfigs[IndexName] = &registrytypes.IndexInfo{
  103. Name: IndexName,
  104. Mirrors: config.Mirrors,
  105. Secure: true,
  106. Official: true,
  107. }
  108. return config
  109. }
  110. // isSecureIndex returns false if the provided indexName is part of the list of insecure registries
  111. // Insecure registries accept HTTP and/or accept HTTPS with certificates from unknown CAs.
  112. //
  113. // The list of insecure registries can contain an element with CIDR notation to specify a whole subnet.
  114. // If the subnet contains one of the IPs of the registry specified by indexName, the latter is considered
  115. // insecure.
  116. //
  117. // indexName should be a URL.Host (`host:port` or `host`) where the `host` part can be either a domain name
  118. // or an IP address. If it is a domain name, then it will be resolved in order to check if the IP is contained
  119. // in a subnet. If the resolving is not successful, isSecureIndex will only try to match hostname to any element
  120. // of insecureRegistries.
  121. func isSecureIndex(config *serviceConfig, indexName string) bool {
  122. // Check for configured index, first. This is needed in case isSecureIndex
  123. // is called from anything besides newIndexInfo, in order to honor per-index configurations.
  124. if index, ok := config.IndexConfigs[indexName]; ok {
  125. return index.Secure
  126. }
  127. host, _, err := net.SplitHostPort(indexName)
  128. if err != nil {
  129. // assume indexName is of the form `host` without the port and go on.
  130. host = indexName
  131. }
  132. addrs, err := lookupIP(host)
  133. if err != nil {
  134. ip := net.ParseIP(host)
  135. if ip != nil {
  136. addrs = []net.IP{ip}
  137. }
  138. // if ip == nil, then `host` is neither an IP nor it could be looked up,
  139. // either because the index is unreachable, or because the index is behind an HTTP proxy.
  140. // So, len(addrs) == 0 and we're not aborting.
  141. }
  142. // Try CIDR notation only if addrs has any elements, i.e. if `host`'s IP could be determined.
  143. for _, addr := range addrs {
  144. for _, ipnet := range config.InsecureRegistryCIDRs {
  145. // check if the addr falls in the subnet
  146. if (*net.IPNet)(ipnet).Contains(addr) {
  147. return false
  148. }
  149. }
  150. }
  151. return true
  152. }
  153. // ValidateMirror validates an HTTP(S) registry mirror
  154. func ValidateMirror(val string) (string, error) {
  155. uri, err := url.Parse(val)
  156. if err != nil {
  157. return "", fmt.Errorf("%s is not a valid URI", val)
  158. }
  159. if uri.Scheme != "http" && uri.Scheme != "https" {
  160. return "", fmt.Errorf("Unsupported scheme %s", uri.Scheme)
  161. }
  162. if uri.Path != "" || uri.RawQuery != "" || uri.Fragment != "" {
  163. return "", fmt.Errorf("Unsupported path/query/fragment at end of the URI")
  164. }
  165. return fmt.Sprintf("%s://%s/", uri.Scheme, uri.Host), nil
  166. }
  167. // ValidateIndexName validates an index name.
  168. func ValidateIndexName(val string) (string, error) {
  169. if val == reference.LegacyDefaultHostname {
  170. val = reference.DefaultHostname
  171. }
  172. if strings.HasPrefix(val, "-") || strings.HasSuffix(val, "-") {
  173. return "", fmt.Errorf("Invalid index name (%s). Cannot begin or end with a hyphen.", val)
  174. }
  175. return val, nil
  176. }
  177. func validateNoScheme(reposName string) error {
  178. if strings.Contains(reposName, "://") {
  179. // It cannot contain a scheme!
  180. return ErrInvalidRepositoryName
  181. }
  182. return nil
  183. }
  184. // newIndexInfo returns IndexInfo configuration from indexName
  185. func newIndexInfo(config *serviceConfig, indexName string) (*registrytypes.IndexInfo, error) {
  186. var err error
  187. indexName, err = ValidateIndexName(indexName)
  188. if err != nil {
  189. return nil, err
  190. }
  191. // Return any configured index info, first.
  192. if index, ok := config.IndexConfigs[indexName]; ok {
  193. return index, nil
  194. }
  195. // Construct a non-configured index info.
  196. index := &registrytypes.IndexInfo{
  197. Name: indexName,
  198. Mirrors: make([]string, 0),
  199. Official: false,
  200. }
  201. index.Secure = isSecureIndex(config, indexName)
  202. return index, nil
  203. }
  204. // GetAuthConfigKey special-cases using the full index address of the official
  205. // index as the AuthConfig key, and uses the (host)name[:port] for private indexes.
  206. func GetAuthConfigKey(index *registrytypes.IndexInfo) string {
  207. if index.Official {
  208. return IndexServer
  209. }
  210. return index.Name
  211. }
  212. // newRepositoryInfo validates and breaks down a repository name into a RepositoryInfo
  213. func newRepositoryInfo(config *serviceConfig, name reference.Named) (*RepositoryInfo, error) {
  214. index, err := newIndexInfo(config, name.Hostname())
  215. if err != nil {
  216. return nil, err
  217. }
  218. official := !strings.ContainsRune(name.Name(), '/')
  219. return &RepositoryInfo{name, index, official}, nil
  220. }
  221. // ParseRepositoryInfo performs the breakdown of a repository name into a RepositoryInfo, but
  222. // lacks registry configuration.
  223. func ParseRepositoryInfo(reposName reference.Named) (*RepositoryInfo, error) {
  224. return newRepositoryInfo(emptyServiceConfig, reposName)
  225. }
  226. // ParseSearchIndexInfo will use repository name to get back an indexInfo.
  227. func ParseSearchIndexInfo(reposName string) (*registrytypes.IndexInfo, error) {
  228. indexName, _ := splitReposSearchTerm(reposName)
  229. indexInfo, err := newIndexInfo(emptyServiceConfig, indexName)
  230. if err != nil {
  231. return nil, err
  232. }
  233. return indexInfo, nil
  234. }