config.go 15 KB

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