config.go 16 KB

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