From 4e8d9a452220c4db27dd608abc73317cbc38329a Mon Sep 17 00:00:00 2001 From: Rob Murray Date: Wed, 6 Mar 2024 10:47:18 +0000 Subject: [PATCH] Add IPv6 nameserver to the internal DNS's upstreams. When configuring the internal DNS resolver - rather than keep IPv6 nameservers read from the host's resolv.conf in the container's resolv.conf, treat them like IPv4 addresses and use them as upstream resolvers. For IPv6 nameservers, if there's a zone identifier in the address or the container itself doesn't have IPv6 support, mark the upstream addresses for use in the host's network namespace. Signed-off-by: Rob Murray --- libnetwork/internal/resolvconf/resolvconf.go | 40 +++------ .../internal/resolvconf/resolvconf_test.go | 83 +++++++++++-------- .../IPv4_and_IPv6,_ipv6_disabled.golden | 2 +- .../IPv4_and_IPv6,_ipv6_enabled.golden | 3 +- ...4_and_IPv6_localhost,_IPv6_disabled.golden | 2 +- ...v4_and_IPv6_localhost,_ipv6_enabled.golden | 3 +- ...alhost,_IPv6_private,_IPv6_disabled.golden | 2 +- ...calhost,_IPv6_private,_IPv6_enabled.golden | 3 +- .../IPv6_addr,_IPv6_enabled.golden | 2 +- libnetwork/libnetwork_linux_test.go | 2 +- libnetwork/sandbox_dns_unix.go | 19 ++--- 11 files changed, 78 insertions(+), 83 deletions(-) diff --git a/libnetwork/internal/resolvconf/resolvconf.go b/libnetwork/internal/resolvconf/resolvconf.go index 6e49c37927..499b9497d9 100644 --- a/libnetwork/internal/resolvconf/resolvconf.go +++ b/libnetwork/internal/resolvconf/resolvconf.go @@ -237,7 +237,7 @@ func (rc *ResolvConf) TransformForLegacyNw(ipv6 bool) { // use in a network sandbox that has an internal DNS resolver. // - Add internalNS as a nameserver. // - Remove other nameservers, stashing them as ExtNameServers for the -// internal resolver to use. (Apart from IPv6 nameservers, if keepIPv6.) +// internal resolver to use. // - Mark ExtNameServers that must be used in the host namespace. // - If no ExtNameServer addresses are found, use the defaults. // - Return an error if an "ndots" option inherited from the host's config, or @@ -246,7 +246,7 @@ func (rc *ResolvConf) TransformForLegacyNw(ipv6 bool) { // option includes a ':', and an option with a matching prefix exists, it // is not modified. func (rc *ResolvConf) TransformForIntNS( - keepIPv6 bool, + ipv6 bool, internalNS netip.Addr, reqdOptions []string, ) ([]ExtDNSEntry, error) { @@ -256,30 +256,16 @@ func (rc *ResolvConf) TransformForIntNS( // internal nameserver. rc.md.ExtNameServers = nil for _, addr := range rc.nameServers { - // The internal resolver only uses IPv4 addresses so, keep IPv6 nameservers in - // the container's file if keepIPv6, else drop them. - if addr.Is6() { - if keepIPv6 { - newNSs = append(newNSs, addr) - } - } else { - // Extract this NS. Mark loopback addresses that did not come from an override as - // 'HostLoopback'. Upstream requests for these servers will be made in the host's - // network namespace. (So, '--dns 127.0.0.53' means use a nameserver listening on - // the container's loopback interface. But, if the host's resolv.conf contains - // 'nameserver 127.0.0.53', the host's resolver will be used.) - // - // TODO(robmry) - why only loopback addresses? - // Addresses from the host's resolv.conf must be usable in the host's namespace, - // and a lookup from the container's namespace is more expensive? And, for - // example, if the host has a nameserver with an IPv6 LL address with a zone-id, - // it won't work from the container's namespace (now, while the address is left in - // the container's resolv.conf, or in future for the internal resolver). - rc.md.ExtNameServers = append(rc.md.ExtNameServers, ExtDNSEntry{ - Addr: addr, - HostLoopback: addr.IsLoopback() && !rc.md.NSOverride, - }) - } + // Extract this NS. Mark addresses that did not come from an override, but will + // definitely not work in the container's namespace as 'HostLoopback'. Upstream + // requests for these servers will be made in the host's network namespace. (So, + // '--dns 127.0.0.53' means use a nameserver listening on the container's + // loopback interface. But, if the host's resolv.conf contains 'nameserver + // 127.0.0.53', the host's resolver will be used.) + rc.md.ExtNameServers = append(rc.md.ExtNameServers, ExtDNSEntry{ + Addr: addr, + HostLoopback: !rc.md.NSOverride && (addr.IsLoopback() || (addr.Is6() && !ipv6) || addr.Zone() != ""), + }) } rc.nameServers = newNSs @@ -287,7 +273,7 @@ func (rc *ResolvConf) TransformForIntNS( // internal resolver, use the defaults as ext nameservers. if len(rc.md.ExtNameServers) == 0 && len(rc.nameServers) == 1 { log.G(context.TODO()).Info("No non-localhost DNS nameservers are left in resolv.conf. Using default external servers") - for _, addr := range defaultNSAddrs(keepIPv6) { + for _, addr := range defaultNSAddrs(ipv6) { rc.md.ExtNameServers = append(rc.md.ExtNameServers, ExtDNSEntry{Addr: addr}) } rc.md.UsedDefaultNS = true diff --git a/libnetwork/internal/resolvconf/resolvconf_test.go b/libnetwork/internal/resolvconf/resolvconf_test.go index 21605178df..87fc520ac2 100644 --- a/libnetwork/internal/resolvconf/resolvconf_test.go +++ b/libnetwork/internal/resolvconf/resolvconf_test.go @@ -351,16 +351,22 @@ func TestRCTransformForIntNS(t *testing.T) { expExtServers: []ExtDNSEntry{mke("10.0.0.1", false)}, }, { - name: "IPv4 and IPv6, ipv6 enabled", - input: "nameserver 10.0.0.1\nnameserver fdb6:b8fe:b528::1", - ipv6: true, - expExtServers: []ExtDNSEntry{mke("10.0.0.1", false)}, + name: "IPv4 and IPv6, ipv6 enabled", + input: "nameserver 10.0.0.1\nnameserver fdb6:b8fe:b528::1", + ipv6: true, + expExtServers: []ExtDNSEntry{ + mke("10.0.0.1", false), + mke("fdb6:b8fe:b528::1", false), + }, }, { - name: "IPv4 and IPv6, ipv6 disabled", - input: "nameserver 10.0.0.1\nnameserver fdb6:b8fe:b528::1", - ipv6: false, - expExtServers: []ExtDNSEntry{mke("10.0.0.1", false)}, + name: "IPv4 and IPv6, ipv6 disabled", + input: "nameserver 10.0.0.1\nnameserver fdb6:b8fe:b528::1", + ipv6: false, + expExtServers: []ExtDNSEntry{ + mke("10.0.0.1", false), + mke("fdb6:b8fe:b528::1", true), + }, }, { name: "IPv4 localhost", @@ -384,37 +390,46 @@ func TestRCTransformForIntNS(t *testing.T) { expExtServers: []ExtDNSEntry{mke("127.0.0.53", true)}, }, { - name: "IPv6 addr, IPv6 enabled", - input: "nameserver fd14:6e0e:f855::1", + name: "IPv6 addr, IPv6 enabled", + input: "nameserver fd14:6e0e:f855::1", + ipv6: true, + expExtServers: []ExtDNSEntry{mke("fd14:6e0e:f855::1", false)}, + }, + { + name: "IPv4 and IPv6 localhost, IPv6 disabled", + input: "nameserver 127.0.0.53\nnameserver ::1", + ipv6: false, + expExtServers: []ExtDNSEntry{ + mke("127.0.0.53", true), + mke("::1", true), + }, + }, + { + name: "IPv4 and IPv6 localhost, ipv6 enabled", + input: "nameserver 127.0.0.53\nnameserver ::1", ipv6: true, - // Note that there are no ext servers in this case, the internal resolver - // will only look up container names. The default nameservers aren't added - // because the host's IPv6 nameserver remains in the container's resolv.conf, - // (because only IPv4 ext servers are currently allowed). + expExtServers: []ExtDNSEntry{ + mke("127.0.0.53", true), + mke("::1", true), + }, }, { - name: "IPv4 and IPv6 localhost, IPv6 disabled", - input: "nameserver 127.0.0.53\nnameserver ::1", - ipv6: false, - expExtServers: []ExtDNSEntry{mke("127.0.0.53", true)}, + name: "IPv4 localhost, IPv6 private, IPv6 enabled", + input: "nameserver 127.0.0.53\nnameserver fd3e:2d1a:1f5a::1", + ipv6: true, + expExtServers: []ExtDNSEntry{ + mke("127.0.0.53", true), + mke("fd3e:2d1a:1f5a::1", false), + }, }, { - name: "IPv4 and IPv6 localhost, ipv6 enabled", - input: "nameserver 127.0.0.53\nnameserver ::1", - ipv6: true, - expExtServers: []ExtDNSEntry{mke("127.0.0.53", true)}, - }, - { - name: "IPv4 localhost, IPv6 private, IPv6 enabled", - input: "nameserver 127.0.0.53\nnameserver fd3e:2d1a:1f5a::1", - ipv6: true, - expExtServers: []ExtDNSEntry{mke("127.0.0.53", true)}, - }, - { - name: "IPv4 localhost, IPv6 private, IPv6 disabled", - input: "nameserver 127.0.0.53\nnameserver fd3e:2d1a:1f5a::1", - ipv6: false, - expExtServers: []ExtDNSEntry{mke("127.0.0.53", true)}, + name: "IPv4 localhost, IPv6 private, IPv6 disabled", + input: "nameserver 127.0.0.53\nnameserver fd3e:2d1a:1f5a::1", + ipv6: false, + expExtServers: []ExtDNSEntry{ + mke("127.0.0.53", true), + mke("fd3e:2d1a:1f5a::1", true), + }, }, { name: "No host nameserver, no iv6", diff --git a/libnetwork/internal/resolvconf/testdata/TestRCTransformForIntNS/IPv4_and_IPv6,_ipv6_disabled.golden b/libnetwork/internal/resolvconf/testdata/TestRCTransformForIntNS/IPv4_and_IPv6,_ipv6_disabled.golden index 4479c59369..23dec24ad2 100644 --- a/libnetwork/internal/resolvconf/testdata/TestRCTransformForIntNS/IPv4_and_IPv6,_ipv6_disabled.golden +++ b/libnetwork/internal/resolvconf/testdata/TestRCTransformForIntNS/IPv4_and_IPv6,_ipv6_disabled.golden @@ -1,5 +1,5 @@ nameserver 127.0.0.11 # Based on host file: '/etc/resolv.conf' (internal resolver) -# ExtServers: [10.0.0.1] +# ExtServers: [10.0.0.1 host(fdb6:b8fe:b528::1)] # Overrides: [] diff --git a/libnetwork/internal/resolvconf/testdata/TestRCTransformForIntNS/IPv4_and_IPv6,_ipv6_enabled.golden b/libnetwork/internal/resolvconf/testdata/TestRCTransformForIntNS/IPv4_and_IPv6,_ipv6_enabled.golden index 578b5babbe..f1f5ea0bca 100644 --- a/libnetwork/internal/resolvconf/testdata/TestRCTransformForIntNS/IPv4_and_IPv6,_ipv6_enabled.golden +++ b/libnetwork/internal/resolvconf/testdata/TestRCTransformForIntNS/IPv4_and_IPv6,_ipv6_enabled.golden @@ -1,6 +1,5 @@ nameserver 127.0.0.11 -nameserver fdb6:b8fe:b528::1 # Based on host file: '/etc/resolv.conf' (internal resolver) -# ExtServers: [10.0.0.1] +# ExtServers: [10.0.0.1 fdb6:b8fe:b528::1] # Overrides: [] diff --git a/libnetwork/internal/resolvconf/testdata/TestRCTransformForIntNS/IPv4_and_IPv6_localhost,_IPv6_disabled.golden b/libnetwork/internal/resolvconf/testdata/TestRCTransformForIntNS/IPv4_and_IPv6_localhost,_IPv6_disabled.golden index 926d44d49a..387cc713b7 100644 --- a/libnetwork/internal/resolvconf/testdata/TestRCTransformForIntNS/IPv4_and_IPv6_localhost,_IPv6_disabled.golden +++ b/libnetwork/internal/resolvconf/testdata/TestRCTransformForIntNS/IPv4_and_IPv6_localhost,_IPv6_disabled.golden @@ -1,5 +1,5 @@ nameserver 127.0.0.11 # Based on host file: '/etc/resolv.conf' (internal resolver) -# ExtServers: [host(127.0.0.53)] +# ExtServers: [host(127.0.0.53) host(::1)] # Overrides: [] diff --git a/libnetwork/internal/resolvconf/testdata/TestRCTransformForIntNS/IPv4_and_IPv6_localhost,_ipv6_enabled.golden b/libnetwork/internal/resolvconf/testdata/TestRCTransformForIntNS/IPv4_and_IPv6_localhost,_ipv6_enabled.golden index b64148d845..387cc713b7 100644 --- a/libnetwork/internal/resolvconf/testdata/TestRCTransformForIntNS/IPv4_and_IPv6_localhost,_ipv6_enabled.golden +++ b/libnetwork/internal/resolvconf/testdata/TestRCTransformForIntNS/IPv4_and_IPv6_localhost,_ipv6_enabled.golden @@ -1,6 +1,5 @@ nameserver 127.0.0.11 -nameserver ::1 # Based on host file: '/etc/resolv.conf' (internal resolver) -# ExtServers: [host(127.0.0.53)] +# ExtServers: [host(127.0.0.53) host(::1)] # Overrides: [] diff --git a/libnetwork/internal/resolvconf/testdata/TestRCTransformForIntNS/IPv4_localhost,_IPv6_private,_IPv6_disabled.golden b/libnetwork/internal/resolvconf/testdata/TestRCTransformForIntNS/IPv4_localhost,_IPv6_private,_IPv6_disabled.golden index 926d44d49a..2f58161ae6 100644 --- a/libnetwork/internal/resolvconf/testdata/TestRCTransformForIntNS/IPv4_localhost,_IPv6_private,_IPv6_disabled.golden +++ b/libnetwork/internal/resolvconf/testdata/TestRCTransformForIntNS/IPv4_localhost,_IPv6_private,_IPv6_disabled.golden @@ -1,5 +1,5 @@ nameserver 127.0.0.11 # Based on host file: '/etc/resolv.conf' (internal resolver) -# ExtServers: [host(127.0.0.53)] +# ExtServers: [host(127.0.0.53) host(fd3e:2d1a:1f5a::1)] # Overrides: [] diff --git a/libnetwork/internal/resolvconf/testdata/TestRCTransformForIntNS/IPv4_localhost,_IPv6_private,_IPv6_enabled.golden b/libnetwork/internal/resolvconf/testdata/TestRCTransformForIntNS/IPv4_localhost,_IPv6_private,_IPv6_enabled.golden index 6c1c311c1b..4d7ac5e73e 100644 --- a/libnetwork/internal/resolvconf/testdata/TestRCTransformForIntNS/IPv4_localhost,_IPv6_private,_IPv6_enabled.golden +++ b/libnetwork/internal/resolvconf/testdata/TestRCTransformForIntNS/IPv4_localhost,_IPv6_private,_IPv6_enabled.golden @@ -1,6 +1,5 @@ nameserver 127.0.0.11 -nameserver fd3e:2d1a:1f5a::1 # Based on host file: '/etc/resolv.conf' (internal resolver) -# ExtServers: [host(127.0.0.53)] +# ExtServers: [host(127.0.0.53) fd3e:2d1a:1f5a::1] # Overrides: [] diff --git a/libnetwork/internal/resolvconf/testdata/TestRCTransformForIntNS/IPv6_addr,_IPv6_enabled.golden b/libnetwork/internal/resolvconf/testdata/TestRCTransformForIntNS/IPv6_addr,_IPv6_enabled.golden index 0a412defbf..36cbb47099 100644 --- a/libnetwork/internal/resolvconf/testdata/TestRCTransformForIntNS/IPv6_addr,_IPv6_enabled.golden +++ b/libnetwork/internal/resolvconf/testdata/TestRCTransformForIntNS/IPv6_addr,_IPv6_enabled.golden @@ -1,5 +1,5 @@ nameserver 127.0.0.11 -nameserver fd14:6e0e:f855::1 # Based on host file: '/etc/resolv.conf' (internal resolver) +# ExtServers: [fd14:6e0e:f855::1] # Overrides: [] diff --git a/libnetwork/libnetwork_linux_test.go b/libnetwork/libnetwork_linux_test.go index f69e70f213..7128e0fccc 100644 --- a/libnetwork/libnetwork_linux_test.go +++ b/libnetwork/libnetwork_linux_test.go @@ -1838,7 +1838,7 @@ func TestResolvConf(t *testing.T) { makeNet: makeTestIPv6Network, delNet: true, originResolvConf: "search pommesfrites.fr\nnameserver 12.34.56.78\nnameserver 2001:4860:4860::8888\n", - expResolvConf: "nameserver 127.0.0.11\nnameserver 2001:4860:4860::8888\nsearch pommesfrites.fr\noptions ndots:0", + expResolvConf: "nameserver 127.0.0.11\nsearch pommesfrites.fr\noptions ndots:0", }, { name: "host network", diff --git a/libnetwork/sandbox_dns_unix.go b/libnetwork/sandbox_dns_unix.go index fb1827e4e3..2c146a406a 100644 --- a/libnetwork/sandbox_dns_unix.go +++ b/libnetwork/sandbox_dns_unix.go @@ -327,20 +327,17 @@ func (sb *Sandbox) rebuildDNS() error { return err } - // Check for IPv6 endpoints in this sandbox. If there are any, IPv6 nameservers - // will be left in the container's 'resolv.conf'. - // TODO(robmry) - preserving old behaviour, but ... - // IPv6 nameservers should be treated like IPv4 ones, and used as upstream - // servers for the internal resolver (if it has IPv6 connectivity). This - // doesn't need to depend on whether there are currently any IPv6 endpoints. - // Removing IPv6 nameservers from the container's resolv.conf will avoid the - // problem that musl-libc's resolver tries all nameservers in parallel, so an - // external IPv6 resolver can return NXDOMAIN before the internal resolver - // returns the address of a container. + // Check for IPv6 endpoints in this sandbox. If there are any, and the container has + // IPv6 enabled, upstream requests from the internal DNS resolver can be made from + // the container's namespace. + // TODO(robmry) - this can only check networks connected when the resolver is set up, + // the configuration won't be updated if the container gets an IPv6 address later. ipv6 := false for _, ep := range sb.endpoints { if ep.network.enableIPv6 { - ipv6 = true + if en, ok := sb.ipv6Enabled(); ok { + ipv6 = en + } break } }