config.go 15 KB

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