sandbox_dns_unix.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. //go:build !windows
  2. package libnetwork
  3. import (
  4. "context"
  5. "io/fs"
  6. "net/netip"
  7. "os"
  8. "path/filepath"
  9. "strings"
  10. "github.com/containerd/log"
  11. "github.com/docker/docker/errdefs"
  12. "github.com/docker/docker/libnetwork/etchosts"
  13. "github.com/docker/docker/libnetwork/internal/resolvconf"
  14. "github.com/docker/docker/libnetwork/types"
  15. "github.com/pkg/errors"
  16. )
  17. const (
  18. defaultPrefix = "/var/lib/docker/network/files"
  19. dirPerm = 0o755
  20. filePerm = 0o644
  21. resolverIPSandbox = "127.0.0.11"
  22. )
  23. // finishInitDNS is to be called after the container namespace has been created,
  24. // before it the user process is started. The container's support for IPv6 can be
  25. // determined at this point.
  26. func (sb *Sandbox) finishInitDNS() error {
  27. if err := sb.buildHostsFile(); err != nil {
  28. return errdefs.System(err)
  29. }
  30. for _, ep := range sb.Endpoints() {
  31. if err := sb.updateHostsFile(ep.getEtcHostsAddrs()); err != nil {
  32. return errdefs.System(err)
  33. }
  34. }
  35. return nil
  36. }
  37. func (sb *Sandbox) startResolver(restore bool) {
  38. sb.resolverOnce.Do(func() {
  39. var err error
  40. // The embedded resolver is always started with proxyDNS set as true, even when the sandbox is only attached to
  41. // an internal network. This way, it's the driver responsibility to make sure `connect` syscall fails fast when
  42. // no external connectivity is available (eg. by not setting a default gateway).
  43. sb.resolver = NewResolver(resolverIPSandbox, true, sb)
  44. defer func() {
  45. if err != nil {
  46. sb.resolver = nil
  47. }
  48. }()
  49. // In the case of live restore container is already running with
  50. // right resolv.conf contents created before. Just update the
  51. // external DNS servers from the restored sandbox for embedded
  52. // server to use.
  53. if !restore {
  54. err = sb.rebuildDNS()
  55. if err != nil {
  56. log.G(context.TODO()).Errorf("Updating resolv.conf failed for container %s, %q", sb.ContainerID(), err)
  57. return
  58. }
  59. }
  60. sb.resolver.SetExtServers(sb.extDNS)
  61. if err = sb.osSbox.InvokeFunc(sb.resolver.SetupFunc(0)); err != nil {
  62. log.G(context.TODO()).Errorf("Resolver Setup function failed for container %s, %q", sb.ContainerID(), err)
  63. return
  64. }
  65. if err = sb.resolver.Start(); err != nil {
  66. log.G(context.TODO()).Errorf("Resolver Start failed for container %s, %q", sb.ContainerID(), err)
  67. }
  68. })
  69. }
  70. func (sb *Sandbox) setupResolutionFiles() error {
  71. // Create a hosts file that can be mounted during container setup. For most
  72. // networking modes (not host networking) it will be re-created before the
  73. // container start, once its support for IPv6 is known.
  74. if sb.config.hostsPath == "" {
  75. sb.config.hostsPath = defaultPrefix + "/" + sb.id + "/hosts"
  76. }
  77. dir, _ := filepath.Split(sb.config.hostsPath)
  78. if err := createBasePath(dir); err != nil {
  79. return err
  80. }
  81. if err := sb.buildHostsFile(); err != nil {
  82. return err
  83. }
  84. return sb.setupDNS()
  85. }
  86. func (sb *Sandbox) buildHostsFile() error {
  87. sb.restoreHostsPath()
  88. dir, _ := filepath.Split(sb.config.hostsPath)
  89. if err := createBasePath(dir); err != nil {
  90. return err
  91. }
  92. // This is for the host mode networking
  93. if sb.config.useDefaultSandBox && len(sb.config.extraHosts) == 0 {
  94. // We are working under the assumption that the origin file option had been properly expressed by the upper layer
  95. // if not here we are going to error out
  96. if err := copyFile(sb.config.originHostsPath, sb.config.hostsPath); err != nil && !os.IsNotExist(err) {
  97. return types.InternalErrorf("could not copy source hosts file %s to %s: %v", sb.config.originHostsPath, sb.config.hostsPath, err)
  98. }
  99. return nil
  100. }
  101. extraContent := make([]etchosts.Record, 0, len(sb.config.extraHosts))
  102. for _, extraHost := range sb.config.extraHosts {
  103. extraContent = append(extraContent, etchosts.Record{Hosts: extraHost.name, IP: extraHost.IP})
  104. }
  105. // Assume IPv6 support, unless it's definitely disabled.
  106. buildf := etchosts.Build
  107. if en, ok := sb.ipv6Enabled(); ok && !en {
  108. buildf = etchosts.BuildNoIPv6
  109. }
  110. if err := buildf(sb.config.hostsPath, extraContent); err != nil {
  111. return err
  112. }
  113. return sb.updateParentHosts()
  114. }
  115. func (sb *Sandbox) updateHostsFile(ifaceIPs []string) error {
  116. if len(ifaceIPs) == 0 {
  117. return nil
  118. }
  119. if sb.config.originHostsPath != "" {
  120. return nil
  121. }
  122. // User might have provided a FQDN in hostname or split it across hostname
  123. // and domainname. We want the FQDN and the bare hostname.
  124. fqdn := sb.config.hostName
  125. if sb.config.domainName != "" {
  126. fqdn += "." + sb.config.domainName
  127. }
  128. hosts := fqdn
  129. if hostName, _, ok := strings.Cut(fqdn, "."); ok {
  130. hosts += " " + hostName
  131. }
  132. var extraContent []etchosts.Record
  133. for _, ip := range ifaceIPs {
  134. extraContent = append(extraContent, etchosts.Record{Hosts: hosts, IP: ip})
  135. }
  136. sb.addHostsEntries(extraContent)
  137. return nil
  138. }
  139. func (sb *Sandbox) addHostsEntries(recs []etchosts.Record) {
  140. // Assume IPv6 support, unless it's definitely disabled.
  141. if en, ok := sb.ipv6Enabled(); ok && !en {
  142. var filtered []etchosts.Record
  143. for _, rec := range recs {
  144. if addr, err := netip.ParseAddr(rec.IP); err == nil && !addr.Is6() {
  145. filtered = append(filtered, rec)
  146. }
  147. }
  148. recs = filtered
  149. }
  150. if err := etchosts.Add(sb.config.hostsPath, recs); err != nil {
  151. log.G(context.TODO()).Warnf("Failed adding service host entries to the running container: %v", err)
  152. }
  153. }
  154. func (sb *Sandbox) deleteHostsEntries(recs []etchosts.Record) {
  155. if err := etchosts.Delete(sb.config.hostsPath, recs); err != nil {
  156. log.G(context.TODO()).Warnf("Failed deleting service host entries to the running container: %v", err)
  157. }
  158. }
  159. func (sb *Sandbox) updateParentHosts() error {
  160. var pSb *Sandbox
  161. for _, update := range sb.config.parentUpdates {
  162. // TODO(thaJeztah): was it intentional for this loop to re-use prior results of pSB? If not, we should make pSb local and always replace here.
  163. if s, _ := sb.controller.GetSandbox(update.cid); s != nil {
  164. pSb = s
  165. }
  166. if pSb == nil {
  167. continue
  168. }
  169. // TODO(robmry) - filter out IPv6 addresses here if !sb.ipv6Enabled() but...
  170. // - this is part of the implementation of '--link', which will be removed along
  171. // with the rest of legacy networking.
  172. // - IPv6 addresses shouldn't be allocated if IPv6 is not available in a container,
  173. // and that change will come along later.
  174. // - I think this may be dead code, it's not possible to start a parent container with
  175. // '--link child' unless the child has already started ("Error response from daemon:
  176. // Cannot link to a non running container"). So, when the child starts and this method
  177. // is called with updates for parents, the parents aren't running and GetSandbox()
  178. // returns nil.)
  179. if err := etchosts.Update(pSb.config.hostsPath, update.ip, update.name); err != nil {
  180. return err
  181. }
  182. }
  183. return nil
  184. }
  185. func (sb *Sandbox) restoreResolvConfPath() {
  186. if sb.config.resolvConfPath == "" {
  187. sb.config.resolvConfPath = defaultPrefix + "/" + sb.id + "/resolv.conf"
  188. }
  189. sb.config.resolvConfHashFile = sb.config.resolvConfPath + ".hash"
  190. }
  191. func (sb *Sandbox) restoreHostsPath() {
  192. if sb.config.hostsPath == "" {
  193. sb.config.hostsPath = defaultPrefix + "/" + sb.id + "/hosts"
  194. }
  195. }
  196. func (sb *Sandbox) setExternalResolvers(entries []resolvconf.ExtDNSEntry) {
  197. sb.extDNS = make([]extDNSEntry, 0, len(entries))
  198. for _, entry := range entries {
  199. sb.extDNS = append(sb.extDNS, extDNSEntry{
  200. IPStr: entry.Addr.String(),
  201. HostLoopback: entry.HostLoopback,
  202. })
  203. }
  204. }
  205. func (c *containerConfig) getOriginResolvConfPath() string {
  206. if c.originResolvConfPath != "" {
  207. return c.originResolvConfPath
  208. }
  209. // Fallback if not specified.
  210. return resolvconf.Path()
  211. }
  212. // loadResolvConf reads the resolv.conf file at path, and merges in overrides for
  213. // nameservers, options, and search domains.
  214. func (sb *Sandbox) loadResolvConf(path string) (*resolvconf.ResolvConf, error) {
  215. rc, err := resolvconf.Load(path)
  216. if err != nil && !errors.Is(err, fs.ErrNotExist) {
  217. return nil, err
  218. }
  219. // Proceed with rc, which might be zero-valued if path does not exist.
  220. rc.SetHeader(`# Generated by Docker Engine.
  221. # This file can be edited; Docker Engine will not make further changes once it
  222. # has been modified.`)
  223. if len(sb.config.dnsList) > 0 {
  224. var dnsAddrs []netip.Addr
  225. for _, ns := range sb.config.dnsList {
  226. addr, err := netip.ParseAddr(ns)
  227. if err != nil {
  228. return nil, errors.Wrapf(err, "bad nameserver address %s", ns)
  229. }
  230. dnsAddrs = append(dnsAddrs, addr)
  231. }
  232. rc.OverrideNameServers(dnsAddrs)
  233. }
  234. if len(sb.config.dnsSearchList) > 0 {
  235. rc.OverrideSearch(sb.config.dnsSearchList)
  236. }
  237. if len(sb.config.dnsOptionsList) > 0 {
  238. rc.OverrideOptions(sb.config.dnsOptionsList)
  239. }
  240. return &rc, nil
  241. }
  242. // For a new sandbox, write an initial version of the container's resolv.conf. It'll
  243. // be a copy of the host's file, with overrides for nameservers, options and search
  244. // domains applied.
  245. func (sb *Sandbox) setupDNS() error {
  246. // Make sure the directory exists.
  247. sb.restoreResolvConfPath()
  248. dir, _ := filepath.Split(sb.config.resolvConfPath)
  249. if err := createBasePath(dir); err != nil {
  250. return err
  251. }
  252. rc, err := sb.loadResolvConf(sb.config.getOriginResolvConfPath())
  253. if err != nil {
  254. return err
  255. }
  256. return rc.WriteFile(sb.config.resolvConfPath, sb.config.resolvConfHashFile, filePerm)
  257. }
  258. // Called when an endpoint has joined the sandbox.
  259. func (sb *Sandbox) updateDNS(ipv6Enabled bool) error {
  260. if mod, err := resolvconf.UserModified(sb.config.resolvConfPath, sb.config.resolvConfHashFile); err != nil || mod {
  261. return err
  262. }
  263. // Load the host's resolv.conf as a starting point.
  264. rc, err := sb.loadResolvConf(sb.config.getOriginResolvConfPath())
  265. if err != nil {
  266. return err
  267. }
  268. // For host-networking, no further change is needed.
  269. if !sb.config.useDefaultSandBox {
  270. // The legacy bridge network has no internal nameserver. So, strip localhost
  271. // nameservers from the host's config, then add default nameservers if there
  272. // are none remaining.
  273. rc.TransformForLegacyNw(ipv6Enabled)
  274. }
  275. return rc.WriteFile(sb.config.resolvConfPath, sb.config.resolvConfHashFile, filePerm)
  276. }
  277. // Embedded DNS server has to be enabled for this sandbox. Rebuild the container's resolv.conf.
  278. func (sb *Sandbox) rebuildDNS() error {
  279. // Don't touch the file if the user has modified it.
  280. if mod, err := resolvconf.UserModified(sb.config.resolvConfPath, sb.config.resolvConfHashFile); err != nil || mod {
  281. return err
  282. }
  283. // Load the host's resolv.conf as a starting point.
  284. rc, err := sb.loadResolvConf(sb.config.getOriginResolvConfPath())
  285. if err != nil {
  286. return err
  287. }
  288. // Check for IPv6 endpoints in this sandbox. If there are any, and the container has
  289. // IPv6 enabled, upstream requests from the internal DNS resolver can be made from
  290. // the container's namespace.
  291. // TODO(robmry) - this can only check networks connected when the resolver is set up,
  292. // the configuration won't be updated if the container gets an IPv6 address later.
  293. ipv6 := false
  294. for _, ep := range sb.endpoints {
  295. if ep.network.enableIPv6 {
  296. if en, ok := sb.ipv6Enabled(); ok {
  297. ipv6 = en
  298. }
  299. break
  300. }
  301. }
  302. intNS, err := netip.ParseAddr(sb.resolver.NameServer())
  303. if err != nil {
  304. return err
  305. }
  306. // Work out whether ndots has been set from host config or overrides.
  307. _, sb.ndotsSet = rc.Option("ndots")
  308. // Swap nameservers for the internal one, and make sure the required options are set.
  309. var extNameServers []resolvconf.ExtDNSEntry
  310. extNameServers, err = rc.TransformForIntNS(ipv6, intNS, sb.resolver.ResolverOptions())
  311. if err != nil {
  312. return err
  313. }
  314. // Extract the list of nameservers that just got swapped out, and store them as
  315. // upstream nameservers.
  316. sb.setExternalResolvers(extNameServers)
  317. // Write the file for the container - preserving old behaviour, not updating the
  318. // hash file (so, no further updates will be made).
  319. // TODO(robmry) - I think that's probably accidental, I can't find a reason for it,
  320. // and the old resolvconf.Build() function wrote the file but not the hash, which
  321. // is surprising. But, before fixing it, a guard/flag needs to be added to
  322. // sb.updateDNS() to make sure that when an endpoint joins a sandbox that already
  323. // has an internal resolver, the container's resolv.conf is still (re)configured
  324. // for an internal resolver.
  325. return rc.WriteFile(sb.config.resolvConfPath, "", filePerm)
  326. }
  327. func createBasePath(dir string) error {
  328. return os.MkdirAll(dir, dirPerm)
  329. }
  330. func copyFile(src, dst string) error {
  331. sBytes, err := os.ReadFile(src)
  332. if err != nil {
  333. return err
  334. }
  335. return os.WriteFile(dst, sBytes, filePerm)
  336. }