config.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. package registry // import "github.com/docker/docker/registry"
  2. import (
  3. "net"
  4. "net/url"
  5. "regexp"
  6. "strconv"
  7. "strings"
  8. "github.com/docker/distribution/reference"
  9. "github.com/docker/docker/api/types/registry"
  10. "github.com/sirupsen/logrus"
  11. )
  12. // ServiceOptions holds command line options.
  13. type ServiceOptions struct {
  14. AllowNondistributableArtifacts []string `json:"allow-nondistributable-artifacts,omitempty"`
  15. Mirrors []string `json:"registry-mirrors,omitempty"`
  16. InsecureRegistries []string `json:"insecure-registries,omitempty"`
  17. }
  18. // serviceConfig holds daemon configuration for the registry service.
  19. type serviceConfig registry.ServiceConfig
  20. // TODO(thaJeztah) both the "index.docker.io" and "registry-1.docker.io" domains
  21. // are here for historic reasons and backward-compatibility. These domains
  22. // are still supported by Docker Hub (and will continue to be supported), but
  23. // there are new domains already in use, and plans to consolidate all legacy
  24. // domains to new "canonical" domains. Once those domains are decided on, we
  25. // should update these consts (but making sure to preserve compatibility with
  26. // existing installs, clients, and user configuration).
  27. const (
  28. // DefaultNamespace is the default namespace
  29. DefaultNamespace = "docker.io"
  30. // DefaultRegistryHost is the hostname for the default (Docker Hub) registry
  31. // used for pushing and pulling images. This hostname is hard-coded to handle
  32. // the conversion from image references without registry name (e.g. "ubuntu",
  33. // or "ubuntu:latest"), as well as references using the "docker.io" domain
  34. // name, which is used as canonical reference for images on Docker Hub, but
  35. // does not match the domain-name of Docker Hub's registry.
  36. DefaultRegistryHost = "registry-1.docker.io"
  37. // IndexHostname is the index hostname, used for authentication and image search.
  38. IndexHostname = "index.docker.io"
  39. // IndexServer is used for user auth and image search
  40. IndexServer = "https://" + IndexHostname + "/v1/"
  41. // IndexName is the name of the index
  42. IndexName = "docker.io"
  43. )
  44. var (
  45. // DefaultV2Registry is the URI of the default (Docker Hub) registry.
  46. DefaultV2Registry = &url.URL{
  47. Scheme: "https",
  48. Host: DefaultRegistryHost,
  49. }
  50. emptyServiceConfig, _ = newServiceConfig(ServiceOptions{})
  51. validHostPortRegex = regexp.MustCompile(`^` + reference.DomainRegexp.String() + `$`)
  52. // for mocking in unit tests
  53. lookupIP = net.LookupIP
  54. // certsDir is used to override defaultCertsDir.
  55. certsDir string
  56. )
  57. // SetCertsDir allows the default certs directory to be changed. This function
  58. // is used at daemon startup to set the correct location when running in
  59. // rootless mode.
  60. func SetCertsDir(path string) {
  61. certsDir = path
  62. }
  63. // CertsDir is the directory where certificates are stored.
  64. func CertsDir() string {
  65. if certsDir != "" {
  66. return certsDir
  67. }
  68. return defaultCertsDir
  69. }
  70. // newServiceConfig returns a new instance of ServiceConfig
  71. func newServiceConfig(options ServiceOptions) (*serviceConfig, error) {
  72. config := &serviceConfig{}
  73. if err := config.loadAllowNondistributableArtifacts(options.AllowNondistributableArtifacts); err != nil {
  74. return nil, err
  75. }
  76. if err := config.loadMirrors(options.Mirrors); err != nil {
  77. return nil, err
  78. }
  79. if err := config.loadInsecureRegistries(options.InsecureRegistries); err != nil {
  80. return nil, err
  81. }
  82. return config, nil
  83. }
  84. // copy constructs a new ServiceConfig with a copy of the configuration in config.
  85. func (config *serviceConfig) copy() *registry.ServiceConfig {
  86. ic := make(map[string]*registry.IndexInfo)
  87. for key, value := range config.IndexConfigs {
  88. ic[key] = value
  89. }
  90. return &registry.ServiceConfig{
  91. AllowNondistributableArtifactsCIDRs: append([]*registry.NetIPNet(nil), config.AllowNondistributableArtifactsCIDRs...),
  92. AllowNondistributableArtifactsHostnames: append([]string(nil), config.AllowNondistributableArtifactsHostnames...),
  93. InsecureRegistryCIDRs: append([]*registry.NetIPNet(nil), config.InsecureRegistryCIDRs...),
  94. IndexConfigs: ic,
  95. Mirrors: append([]string(nil), config.Mirrors...),
  96. }
  97. }
  98. // loadAllowNondistributableArtifacts loads allow-nondistributable-artifacts registries into config.
  99. func (config *serviceConfig) loadAllowNondistributableArtifacts(registries []string) error {
  100. cidrs := map[string]*registry.NetIPNet{}
  101. hostnames := map[string]bool{}
  102. for _, r := range registries {
  103. if _, err := ValidateIndexName(r); err != nil {
  104. return err
  105. }
  106. if hasScheme(r) {
  107. return invalidParamf("allow-nondistributable-artifacts registry %s should not contain '://'", r)
  108. }
  109. if _, ipnet, err := net.ParseCIDR(r); err == nil {
  110. // Valid CIDR.
  111. cidrs[ipnet.String()] = (*registry.NetIPNet)(ipnet)
  112. } else if err = validateHostPort(r); err == nil {
  113. // Must be `host:port` if not CIDR.
  114. hostnames[r] = true
  115. } else {
  116. return invalidParamWrapf(err, "allow-nondistributable-artifacts registry %s is not valid", r)
  117. }
  118. }
  119. config.AllowNondistributableArtifactsCIDRs = make([]*registry.NetIPNet, 0, len(cidrs))
  120. for _, c := range cidrs {
  121. config.AllowNondistributableArtifactsCIDRs = append(config.AllowNondistributableArtifactsCIDRs, c)
  122. }
  123. config.AllowNondistributableArtifactsHostnames = make([]string, 0, len(hostnames))
  124. for h := range hostnames {
  125. config.AllowNondistributableArtifactsHostnames = append(config.AllowNondistributableArtifactsHostnames, h)
  126. }
  127. return nil
  128. }
  129. // loadMirrors loads mirrors to config, after removing duplicates.
  130. // Returns an error if mirrors contains an invalid mirror.
  131. func (config *serviceConfig) loadMirrors(mirrors []string) error {
  132. mMap := map[string]struct{}{}
  133. unique := []string{}
  134. for _, mirror := range mirrors {
  135. m, err := ValidateMirror(mirror)
  136. if err != nil {
  137. return err
  138. }
  139. if _, exist := mMap[m]; !exist {
  140. mMap[m] = struct{}{}
  141. unique = append(unique, m)
  142. }
  143. }
  144. config.Mirrors = unique
  145. // Configure public registry since mirrors may have changed.
  146. config.IndexConfigs = map[string]*registry.IndexInfo{
  147. IndexName: {
  148. Name: IndexName,
  149. Mirrors: unique,
  150. Secure: true,
  151. Official: true,
  152. },
  153. }
  154. return nil
  155. }
  156. // loadInsecureRegistries loads insecure registries to config
  157. func (config *serviceConfig) loadInsecureRegistries(registries []string) error {
  158. // Localhost is by default considered as an insecure registry. This is a
  159. // stop-gap for people who are running a private registry on localhost.
  160. registries = append(registries, "127.0.0.0/8")
  161. var (
  162. insecureRegistryCIDRs = make([]*registry.NetIPNet, 0)
  163. indexConfigs = make(map[string]*registry.IndexInfo)
  164. )
  165. skip:
  166. for _, r := range registries {
  167. // validate insecure registry
  168. if _, err := ValidateIndexName(r); err != nil {
  169. return err
  170. }
  171. if strings.HasPrefix(strings.ToLower(r), "http://") {
  172. logrus.Warnf("insecure registry %s should not contain 'http://' and 'http://' has been removed from the insecure registry config", r)
  173. r = r[7:]
  174. } else if strings.HasPrefix(strings.ToLower(r), "https://") {
  175. logrus.Warnf("insecure registry %s should not contain 'https://' and 'https://' has been removed from the insecure registry config", r)
  176. r = r[8:]
  177. } else if hasScheme(r) {
  178. return invalidParamf("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 := (*registry.NetIPNet)(ipnet)
  185. for _, value := range 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. insecureRegistryCIDRs = append(insecureRegistryCIDRs, data)
  192. } else {
  193. if err := validateHostPort(r); err != nil {
  194. return invalidParamWrapf(err, "insecure registry %s is not valid", r)
  195. }
  196. // Assume `host:port` if not CIDR.
  197. indexConfigs[r] = &registry.IndexInfo{
  198. Name: r,
  199. Mirrors: make([]string, 0),
  200. Secure: false,
  201. Official: false,
  202. }
  203. }
  204. }
  205. // Configure public registry.
  206. indexConfigs[IndexName] = &registry.IndexInfo{
  207. Name: IndexName,
  208. Mirrors: config.Mirrors,
  209. Secure: true,
  210. Official: true,
  211. }
  212. config.InsecureRegistryCIDRs = insecureRegistryCIDRs
  213. config.IndexConfigs = indexConfigs
  214. return nil
  215. }
  216. // allowNondistributableArtifacts returns true if the provided hostname is part of the list of registries
  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 (config *serviceConfig) allowNondistributableArtifacts(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 (config *serviceConfig) isSecureIndex(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 []*registry.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 "", invalidParamWrapf(err, "invalid mirror: %q is not a valid URI", val)
  287. }
  288. if uri.Scheme != "http" && uri.Scheme != "https" {
  289. return "", invalidParamf("invalid mirror: unsupported scheme %q in %q", uri.Scheme, uri)
  290. }
  291. if (uri.Path != "" && uri.Path != "/") || uri.RawQuery != "" || uri.Fragment != "" {
  292. return "", invalidParamf("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 "", invalidParamf("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 "", invalidParamf("invalid index name (%s). Cannot begin or end with a hyphen", val)
  309. }
  310. return val, nil
  311. }
  312. func hasScheme(reposName string) bool {
  313. return strings.Contains(reposName, "://")
  314. }
  315. func validateHostPort(s string) error {
  316. // Split host and port, and in case s can not be splitted, assume host only
  317. host, port, err := net.SplitHostPort(s)
  318. if err != nil {
  319. host = s
  320. port = ""
  321. }
  322. // If match against the `host:port` pattern fails,
  323. // it might be `IPv6:port`, which will be captured by net.ParseIP(host)
  324. if !validHostPortRegex.MatchString(s) && net.ParseIP(host) == nil {
  325. return invalidParamf("invalid host %q", host)
  326. }
  327. if port != "" {
  328. v, err := strconv.Atoi(port)
  329. if err != nil {
  330. return err
  331. }
  332. if v < 0 || v > 65535 {
  333. return invalidParamf("invalid port %q", port)
  334. }
  335. }
  336. return nil
  337. }
  338. // newIndexInfo returns IndexInfo configuration from indexName
  339. func newIndexInfo(config *serviceConfig, indexName string) (*registry.IndexInfo, error) {
  340. var err error
  341. indexName, err = ValidateIndexName(indexName)
  342. if err != nil {
  343. return nil, err
  344. }
  345. // Return any configured index info, first.
  346. if index, ok := config.IndexConfigs[indexName]; ok {
  347. return index, nil
  348. }
  349. // Construct a non-configured index info.
  350. return &registry.IndexInfo{
  351. Name: indexName,
  352. Mirrors: make([]string, 0),
  353. Secure: config.isSecureIndex(indexName),
  354. Official: false,
  355. }, nil
  356. }
  357. // GetAuthConfigKey special-cases using the full index address of the official
  358. // index as the AuthConfig key, and uses the (host)name[:port] for private indexes.
  359. func GetAuthConfigKey(index *registry.IndexInfo) string {
  360. if index.Official {
  361. return IndexServer
  362. }
  363. return index.Name
  364. }
  365. // newRepositoryInfo validates and breaks down a repository name into a RepositoryInfo
  366. func newRepositoryInfo(config *serviceConfig, name reference.Named) (*RepositoryInfo, error) {
  367. index, err := newIndexInfo(config, reference.Domain(name))
  368. if err != nil {
  369. return nil, err
  370. }
  371. official := !strings.ContainsRune(reference.FamiliarName(name), '/')
  372. return &RepositoryInfo{
  373. Name: reference.TrimNamed(name),
  374. Index: index,
  375. Official: official,
  376. }, nil
  377. }
  378. // ParseRepositoryInfo performs the breakdown of a repository name into a RepositoryInfo, but
  379. // lacks registry configuration.
  380. func ParseRepositoryInfo(reposName reference.Named) (*RepositoryInfo, error) {
  381. return newRepositoryInfo(emptyServiceConfig, reposName)
  382. }
  383. // ParseSearchIndexInfo will use repository name to get back an indexInfo.
  384. //
  385. // TODO(thaJeztah) this function is only used by the CLI, and used to get
  386. // information of the registry (to provide credentials if needed). We should
  387. // move this function (or equivalent) to the CLI, as it's doing too much just
  388. // for that.
  389. func ParseSearchIndexInfo(reposName string) (*registry.IndexInfo, error) {
  390. indexName, _ := splitReposSearchTerm(reposName)
  391. return newIndexInfo(emptyServiceConfig, indexName)
  392. }