config.go 12 KB

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