dns.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. /*
  2. Copyright 2017 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package dns
  14. import (
  15. "fmt"
  16. "io"
  17. "io/ioutil"
  18. "net"
  19. "os"
  20. "path/filepath"
  21. "strings"
  22. "k8s.io/api/core/v1"
  23. utilerrors "k8s.io/apimachinery/pkg/util/errors"
  24. "k8s.io/client-go/tools/record"
  25. runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
  26. "k8s.io/kubernetes/pkg/apis/core/validation"
  27. kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
  28. "k8s.io/kubernetes/pkg/kubelet/util/format"
  29. "k8s.io/klog"
  30. utilio "k8s.io/utils/io"
  31. )
  32. var (
  33. // The default dns opt strings.
  34. defaultDNSOptions = []string{"ndots:5"}
  35. )
  36. type podDNSType int
  37. const (
  38. podDNSCluster podDNSType = iota
  39. podDNSHost
  40. podDNSNone
  41. )
  42. const (
  43. maxResolveConfLength = 10 * 1 << 20 // 10MB
  44. )
  45. // Configurer is used for setting up DNS resolver configuration when launching pods.
  46. type Configurer struct {
  47. recorder record.EventRecorder
  48. nodeRef *v1.ObjectReference
  49. nodeIP net.IP
  50. // If non-nil, use this for container DNS server.
  51. clusterDNS []net.IP
  52. // If non-empty, use this for container DNS search.
  53. ClusterDomain string
  54. // The path to the DNS resolver configuration file used as the base to generate
  55. // the container's DNS resolver configuration file. This can be used in
  56. // conjunction with clusterDomain and clusterDNS.
  57. ResolverConfig string
  58. }
  59. // NewConfigurer returns a DNS configurer for launching pods.
  60. func NewConfigurer(recorder record.EventRecorder, nodeRef *v1.ObjectReference, nodeIP net.IP, clusterDNS []net.IP, clusterDomain, resolverConfig string) *Configurer {
  61. return &Configurer{
  62. recorder: recorder,
  63. nodeRef: nodeRef,
  64. nodeIP: nodeIP,
  65. clusterDNS: clusterDNS,
  66. ClusterDomain: clusterDomain,
  67. ResolverConfig: resolverConfig,
  68. }
  69. }
  70. func omitDuplicates(strs []string) []string {
  71. uniqueStrs := make(map[string]bool)
  72. var ret []string
  73. for _, str := range strs {
  74. if !uniqueStrs[str] {
  75. ret = append(ret, str)
  76. uniqueStrs[str] = true
  77. }
  78. }
  79. return ret
  80. }
  81. func (c *Configurer) formDNSSearchFitsLimits(composedSearch []string, pod *v1.Pod) []string {
  82. limitsExceeded := false
  83. if len(composedSearch) > validation.MaxDNSSearchPaths {
  84. composedSearch = composedSearch[:validation.MaxDNSSearchPaths]
  85. limitsExceeded = true
  86. }
  87. if resolvSearchLineStrLen := len(strings.Join(composedSearch, " ")); resolvSearchLineStrLen > validation.MaxDNSSearchListChars {
  88. cutDomainsNum := 0
  89. cutDomainsLen := 0
  90. for i := len(composedSearch) - 1; i >= 0; i-- {
  91. cutDomainsLen += len(composedSearch[i]) + 1
  92. cutDomainsNum++
  93. if (resolvSearchLineStrLen - cutDomainsLen) <= validation.MaxDNSSearchListChars {
  94. break
  95. }
  96. }
  97. composedSearch = composedSearch[:(len(composedSearch) - cutDomainsNum)]
  98. limitsExceeded = true
  99. }
  100. if limitsExceeded {
  101. log := fmt.Sprintf("Search Line limits were exceeded, some search paths have been omitted, the applied search line is: %s", strings.Join(composedSearch, " "))
  102. c.recorder.Event(pod, v1.EventTypeWarning, "DNSConfigForming", log)
  103. klog.Error(log)
  104. }
  105. return composedSearch
  106. }
  107. func (c *Configurer) formDNSNameserversFitsLimits(nameservers []string, pod *v1.Pod) []string {
  108. if len(nameservers) > validation.MaxDNSNameservers {
  109. nameservers = nameservers[0:validation.MaxDNSNameservers]
  110. log := fmt.Sprintf("Nameserver limits were exceeded, some nameservers have been omitted, the applied nameserver line is: %s", strings.Join(nameservers, " "))
  111. c.recorder.Event(pod, v1.EventTypeWarning, "DNSConfigForming", log)
  112. klog.Error(log)
  113. }
  114. return nameservers
  115. }
  116. func (c *Configurer) formDNSConfigFitsLimits(dnsConfig *runtimeapi.DNSConfig, pod *v1.Pod) *runtimeapi.DNSConfig {
  117. dnsConfig.Servers = c.formDNSNameserversFitsLimits(dnsConfig.Servers, pod)
  118. dnsConfig.Searches = c.formDNSSearchFitsLimits(dnsConfig.Searches, pod)
  119. return dnsConfig
  120. }
  121. func (c *Configurer) generateSearchesForDNSClusterFirst(hostSearch []string, pod *v1.Pod) []string {
  122. if c.ClusterDomain == "" {
  123. return hostSearch
  124. }
  125. nsSvcDomain := fmt.Sprintf("%s.svc.%s", pod.Namespace, c.ClusterDomain)
  126. svcDomain := fmt.Sprintf("svc.%s", c.ClusterDomain)
  127. clusterSearch := []string{nsSvcDomain, svcDomain, c.ClusterDomain}
  128. return omitDuplicates(append(clusterSearch, hostSearch...))
  129. }
  130. // CheckLimitsForResolvConf checks limits in resolv.conf.
  131. func (c *Configurer) CheckLimitsForResolvConf() {
  132. f, err := os.Open(c.ResolverConfig)
  133. if err != nil {
  134. c.recorder.Event(c.nodeRef, v1.EventTypeWarning, "CheckLimitsForResolvConf", err.Error())
  135. klog.V(4).Infof("CheckLimitsForResolvConf: " + err.Error())
  136. return
  137. }
  138. defer f.Close()
  139. _, hostSearch, _, err := parseResolvConf(f)
  140. if err != nil {
  141. c.recorder.Event(c.nodeRef, v1.EventTypeWarning, "CheckLimitsForResolvConf", err.Error())
  142. klog.V(4).Infof("CheckLimitsForResolvConf: " + err.Error())
  143. return
  144. }
  145. domainCountLimit := validation.MaxDNSSearchPaths
  146. if c.ClusterDomain != "" {
  147. domainCountLimit -= 3
  148. }
  149. if len(hostSearch) > domainCountLimit {
  150. log := fmt.Sprintf("Resolv.conf file '%s' contains search line consisting of more than %d domains!", c.ResolverConfig, domainCountLimit)
  151. c.recorder.Event(c.nodeRef, v1.EventTypeWarning, "CheckLimitsForResolvConf", log)
  152. klog.V(4).Infof("CheckLimitsForResolvConf: " + log)
  153. return
  154. }
  155. if len(strings.Join(hostSearch, " ")) > validation.MaxDNSSearchListChars {
  156. log := fmt.Sprintf("Resolv.conf file '%s' contains search line which length is more than allowed %d chars!", c.ResolverConfig, validation.MaxDNSSearchListChars)
  157. c.recorder.Event(c.nodeRef, v1.EventTypeWarning, "CheckLimitsForResolvConf", log)
  158. klog.V(4).Infof("CheckLimitsForResolvConf: " + log)
  159. return
  160. }
  161. }
  162. // parseResolvConf reads a resolv.conf file from the given reader, and parses
  163. // it into nameservers, searches and options, possibly returning an error.
  164. func parseResolvConf(reader io.Reader) (nameservers []string, searches []string, options []string, err error) {
  165. file, err := utilio.ReadAtMost(reader, maxResolveConfLength)
  166. if err != nil {
  167. return nil, nil, nil, err
  168. }
  169. // Lines of the form "nameserver 1.2.3.4" accumulate.
  170. nameservers = []string{}
  171. // Lines of the form "search example.com" overrule - last one wins.
  172. searches = []string{}
  173. // Lines of the form "option ndots:5 attempts:2" overrule - last one wins.
  174. // Each option is recorded as an element in the array.
  175. options = []string{}
  176. var allErrors []error
  177. lines := strings.Split(string(file), "\n")
  178. for l := range lines {
  179. trimmed := strings.TrimSpace(lines[l])
  180. if strings.HasPrefix(trimmed, "#") {
  181. continue
  182. }
  183. fields := strings.Fields(trimmed)
  184. if len(fields) == 0 {
  185. continue
  186. }
  187. if fields[0] == "nameserver" {
  188. if len(fields) >= 2 {
  189. nameservers = append(nameservers, fields[1])
  190. } else {
  191. allErrors = append(allErrors, fmt.Errorf("nameserver list is empty "))
  192. }
  193. }
  194. if fields[0] == "search" {
  195. // Normalise search fields so the same domain with and without trailing dot will only count once, to avoid hitting search validation limits.
  196. searches = []string{}
  197. for _, s := range fields[1:] {
  198. searches = append(searches, strings.TrimSuffix(s, "."))
  199. }
  200. }
  201. if fields[0] == "options" {
  202. options = fields[1:]
  203. }
  204. }
  205. return nameservers, searches, options, utilerrors.NewAggregate(allErrors)
  206. }
  207. func (c *Configurer) getHostDNSConfig() (*runtimeapi.DNSConfig, error) {
  208. var hostDNS, hostSearch, hostOptions []string
  209. // Get host DNS settings
  210. if c.ResolverConfig != "" {
  211. f, err := os.Open(c.ResolverConfig)
  212. if err != nil {
  213. return nil, err
  214. }
  215. defer f.Close()
  216. hostDNS, hostSearch, hostOptions, err = parseResolvConf(f)
  217. if err != nil {
  218. return nil, err
  219. }
  220. }
  221. return &runtimeapi.DNSConfig{
  222. Servers: hostDNS,
  223. Searches: hostSearch,
  224. Options: hostOptions,
  225. }, nil
  226. }
  227. func getPodDNSType(pod *v1.Pod) (podDNSType, error) {
  228. dnsPolicy := pod.Spec.DNSPolicy
  229. switch dnsPolicy {
  230. case v1.DNSNone:
  231. return podDNSNone, nil
  232. case v1.DNSClusterFirstWithHostNet:
  233. return podDNSCluster, nil
  234. case v1.DNSClusterFirst:
  235. if !kubecontainer.IsHostNetworkPod(pod) {
  236. return podDNSCluster, nil
  237. }
  238. // Fallback to DNSDefault for pod on hostnetowrk.
  239. fallthrough
  240. case v1.DNSDefault:
  241. return podDNSHost, nil
  242. }
  243. // This should not happen as kube-apiserver should have rejected
  244. // invalid dnsPolicy.
  245. return podDNSCluster, fmt.Errorf(fmt.Sprintf("invalid DNSPolicy=%v", dnsPolicy))
  246. }
  247. // mergeDNSOptions merges DNS options. If duplicated, entries given by PodDNSConfigOption will
  248. // overwrite the existing ones.
  249. func mergeDNSOptions(existingDNSConfigOptions []string, dnsConfigOptions []v1.PodDNSConfigOption) []string {
  250. optionsMap := make(map[string]string)
  251. for _, op := range existingDNSConfigOptions {
  252. if index := strings.Index(op, ":"); index != -1 {
  253. optionsMap[op[:index]] = op[index+1:]
  254. } else {
  255. optionsMap[op] = ""
  256. }
  257. }
  258. for _, op := range dnsConfigOptions {
  259. if op.Value != nil {
  260. optionsMap[op.Name] = *op.Value
  261. } else {
  262. optionsMap[op.Name] = ""
  263. }
  264. }
  265. // Reconvert DNS options into a string array.
  266. options := []string{}
  267. for opName, opValue := range optionsMap {
  268. op := opName
  269. if opValue != "" {
  270. op = op + ":" + opValue
  271. }
  272. options = append(options, op)
  273. }
  274. return options
  275. }
  276. // appendDNSConfig appends DNS servers, search paths and options given by
  277. // PodDNSConfig to the existing DNS config. Duplicated entries will be merged.
  278. // This assumes existingDNSConfig and dnsConfig are not nil.
  279. func appendDNSConfig(existingDNSConfig *runtimeapi.DNSConfig, dnsConfig *v1.PodDNSConfig) *runtimeapi.DNSConfig {
  280. existingDNSConfig.Servers = omitDuplicates(append(existingDNSConfig.Servers, dnsConfig.Nameservers...))
  281. existingDNSConfig.Searches = omitDuplicates(append(existingDNSConfig.Searches, dnsConfig.Searches...))
  282. existingDNSConfig.Options = mergeDNSOptions(existingDNSConfig.Options, dnsConfig.Options)
  283. return existingDNSConfig
  284. }
  285. // GetPodDNS returns DNS settings for the pod.
  286. func (c *Configurer) GetPodDNS(pod *v1.Pod) (*runtimeapi.DNSConfig, error) {
  287. dnsConfig, err := c.getHostDNSConfig()
  288. if err != nil {
  289. return nil, err
  290. }
  291. dnsType, err := getPodDNSType(pod)
  292. if err != nil {
  293. klog.Errorf("Failed to get DNS type for pod %q: %v. Falling back to DNSClusterFirst policy.", format.Pod(pod), err)
  294. dnsType = podDNSCluster
  295. }
  296. switch dnsType {
  297. case podDNSNone:
  298. // DNSNone should use empty DNS settings as the base.
  299. dnsConfig = &runtimeapi.DNSConfig{}
  300. case podDNSCluster:
  301. if len(c.clusterDNS) != 0 {
  302. // For a pod with DNSClusterFirst policy, the cluster DNS server is
  303. // the only nameserver configured for the pod. The cluster DNS server
  304. // itself will forward queries to other nameservers that is configured
  305. // to use, in case the cluster DNS server cannot resolve the DNS query
  306. // itself.
  307. dnsConfig.Servers = []string{}
  308. for _, ip := range c.clusterDNS {
  309. dnsConfig.Servers = append(dnsConfig.Servers, ip.String())
  310. }
  311. dnsConfig.Searches = c.generateSearchesForDNSClusterFirst(dnsConfig.Searches, pod)
  312. dnsConfig.Options = defaultDNSOptions
  313. break
  314. }
  315. // clusterDNS is not known. Pod with ClusterDNSFirst Policy cannot be created.
  316. nodeErrorMsg := fmt.Sprintf("kubelet does not have ClusterDNS IP configured and cannot create Pod using %q policy. Falling back to %q policy.", v1.DNSClusterFirst, v1.DNSDefault)
  317. c.recorder.Eventf(c.nodeRef, v1.EventTypeWarning, "MissingClusterDNS", nodeErrorMsg)
  318. c.recorder.Eventf(pod, v1.EventTypeWarning, "MissingClusterDNS", "pod: %q. %s", format.Pod(pod), nodeErrorMsg)
  319. // Fallback to DNSDefault.
  320. fallthrough
  321. case podDNSHost:
  322. // When the kubelet --resolv-conf flag is set to the empty string, use
  323. // DNS settings that override the docker default (which is to use
  324. // /etc/resolv.conf) and effectively disable DNS lookups. According to
  325. // the bind documentation, the behavior of the DNS client library when
  326. // "nameservers" are not specified is to "use the nameserver on the
  327. // local machine". A nameserver setting of localhost is equivalent to
  328. // this documented behavior.
  329. if c.ResolverConfig == "" {
  330. switch {
  331. case c.nodeIP == nil || c.nodeIP.To4() != nil:
  332. dnsConfig.Servers = []string{"127.0.0.1"}
  333. case c.nodeIP.To16() != nil:
  334. dnsConfig.Servers = []string{"::1"}
  335. }
  336. dnsConfig.Searches = []string{"."}
  337. }
  338. }
  339. if pod.Spec.DNSConfig != nil {
  340. dnsConfig = appendDNSConfig(dnsConfig, pod.Spec.DNSConfig)
  341. }
  342. return c.formDNSConfigFitsLimits(dnsConfig, pod), nil
  343. }
  344. // SetupDNSinContainerizedMounter replaces the nameserver in containerized-mounter's rootfs/etc/resolve.conf with kubelet.ClusterDNS
  345. func (c *Configurer) SetupDNSinContainerizedMounter(mounterPath string) {
  346. resolvePath := filepath.Join(strings.TrimSuffix(mounterPath, "/mounter"), "rootfs", "etc", "resolv.conf")
  347. dnsString := ""
  348. for _, dns := range c.clusterDNS {
  349. dnsString = dnsString + fmt.Sprintf("nameserver %s\n", dns)
  350. }
  351. if c.ResolverConfig != "" {
  352. f, err := os.Open(c.ResolverConfig)
  353. if err != nil {
  354. klog.Error("Could not open resolverConf file")
  355. } else {
  356. defer f.Close()
  357. _, hostSearch, _, err := parseResolvConf(f)
  358. if err != nil {
  359. klog.Errorf("Error for parsing the resolv.conf file: %v", err)
  360. } else {
  361. dnsString = dnsString + "search"
  362. for _, search := range hostSearch {
  363. dnsString = dnsString + fmt.Sprintf(" %s", search)
  364. }
  365. dnsString = dnsString + "\n"
  366. }
  367. }
  368. }
  369. if err := ioutil.WriteFile(resolvePath, []byte(dnsString), 0600); err != nil {
  370. klog.Errorf("Could not write dns nameserver in file %s, with error %v", resolvePath, err)
  371. }
  372. }