config.go 15 KB

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