config.go 10.0 KB

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