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 <rob.murray@docker.com>
| ... | ... |
@@ -237,7 +237,7 @@ func (rc *ResolvConf) TransformForLegacyNw(ipv6 bool) {
|
| 237 | 237 |
// use in a network sandbox that has an internal DNS resolver. |
| 238 | 238 |
// - Add internalNS as a nameserver. |
| 239 | 239 |
// - Remove other nameservers, stashing them as ExtNameServers for the |
| 240 |
-// internal resolver to use. (Apart from IPv6 nameservers, if keepIPv6.) |
|
| 240 |
+// internal resolver to use. |
|
| 241 | 241 |
// - Mark ExtNameServers that must be used in the host namespace. |
| 242 | 242 |
// - If no ExtNameServer addresses are found, use the defaults. |
| 243 | 243 |
// - Return an error if an "ndots" option inherited from the host's config, or |
| ... | ... |
@@ -246,7 +246,7 @@ func (rc *ResolvConf) TransformForLegacyNw(ipv6 bool) {
|
| 246 | 246 |
// option includes a ':', and an option with a matching prefix exists, it |
| 247 | 247 |
// is not modified. |
| 248 | 248 |
func (rc *ResolvConf) TransformForIntNS( |
| 249 |
- keepIPv6 bool, |
|
| 249 |
+ ipv6 bool, |
|
| 250 | 250 |
internalNS netip.Addr, |
| 251 | 251 |
reqdOptions []string, |
| 252 | 252 |
) ([]ExtDNSEntry, error) {
|
| ... | ... |
@@ -256,30 +256,16 @@ func (rc *ResolvConf) TransformForIntNS( |
| 256 | 256 |
// internal nameserver. |
| 257 | 257 |
rc.md.ExtNameServers = nil |
| 258 | 258 |
for _, addr := range rc.nameServers {
|
| 259 |
- // The internal resolver only uses IPv4 addresses so, keep IPv6 nameservers in |
|
| 260 |
- // the container's file if keepIPv6, else drop them. |
|
| 261 |
- if addr.Is6() {
|
|
| 262 |
- if keepIPv6 {
|
|
| 263 |
- newNSs = append(newNSs, addr) |
|
| 264 |
- } |
|
| 265 |
- } else {
|
|
| 266 |
- // Extract this NS. Mark loopback addresses that did not come from an override as |
|
| 267 |
- // 'HostLoopback'. Upstream requests for these servers will be made in the host's |
|
| 268 |
- // network namespace. (So, '--dns 127.0.0.53' means use a nameserver listening on |
|
| 269 |
- // the container's loopback interface. But, if the host's resolv.conf contains |
|
| 270 |
- // 'nameserver 127.0.0.53', the host's resolver will be used.) |
|
| 271 |
- // |
|
| 272 |
- // TODO(robmry) - why only loopback addresses? |
|
| 273 |
- // Addresses from the host's resolv.conf must be usable in the host's namespace, |
|
| 274 |
- // and a lookup from the container's namespace is more expensive? And, for |
|
| 275 |
- // example, if the host has a nameserver with an IPv6 LL address with a zone-id, |
|
| 276 |
- // it won't work from the container's namespace (now, while the address is left in |
|
| 277 |
- // the container's resolv.conf, or in future for the internal resolver). |
|
| 278 |
- rc.md.ExtNameServers = append(rc.md.ExtNameServers, ExtDNSEntry{
|
|
| 279 |
- Addr: addr, |
|
| 280 |
- HostLoopback: addr.IsLoopback() && !rc.md.NSOverride, |
|
| 281 |
- }) |
|
| 282 |
- } |
|
| 259 |
+ // Extract this NS. Mark addresses that did not come from an override, but will |
|
| 260 |
+ // definitely not work in the container's namespace as 'HostLoopback'. Upstream |
|
| 261 |
+ // requests for these servers will be made in the host's network namespace. (So, |
|
| 262 |
+ // '--dns 127.0.0.53' means use a nameserver listening on the container's |
|
| 263 |
+ // loopback interface. But, if the host's resolv.conf contains 'nameserver |
|
| 264 |
+ // 127.0.0.53', the host's resolver will be used.) |
|
| 265 |
+ rc.md.ExtNameServers = append(rc.md.ExtNameServers, ExtDNSEntry{
|
|
| 266 |
+ Addr: addr, |
|
| 267 |
+ HostLoopback: !rc.md.NSOverride && (addr.IsLoopback() || (addr.Is6() && !ipv6) || addr.Zone() != ""), |
|
| 268 |
+ }) |
|
| 283 | 269 |
} |
| 284 | 270 |
rc.nameServers = newNSs |
| 285 | 271 |
|
| ... | ... |
@@ -287,7 +273,7 @@ func (rc *ResolvConf) TransformForIntNS( |
| 287 | 287 |
// internal resolver, use the defaults as ext nameservers. |
| 288 | 288 |
if len(rc.md.ExtNameServers) == 0 && len(rc.nameServers) == 1 {
|
| 289 | 289 |
log.G(context.TODO()).Info("No non-localhost DNS nameservers are left in resolv.conf. Using default external servers")
|
| 290 |
- for _, addr := range defaultNSAddrs(keepIPv6) {
|
|
| 290 |
+ for _, addr := range defaultNSAddrs(ipv6) {
|
|
| 291 | 291 |
rc.md.ExtNameServers = append(rc.md.ExtNameServers, ExtDNSEntry{Addr: addr})
|
| 292 | 292 |
} |
| 293 | 293 |
rc.md.UsedDefaultNS = true |
| ... | ... |
@@ -351,16 +351,22 @@ func TestRCTransformForIntNS(t *testing.T) {
|
| 351 | 351 |
expExtServers: []ExtDNSEntry{mke("10.0.0.1", false)},
|
| 352 | 352 |
}, |
| 353 | 353 |
{
|
| 354 |
- name: "IPv4 and IPv6, ipv6 enabled", |
|
| 355 |
- input: "nameserver 10.0.0.1\nnameserver fdb6:b8fe:b528::1", |
|
| 356 |
- ipv6: true, |
|
| 357 |
- expExtServers: []ExtDNSEntry{mke("10.0.0.1", false)},
|
|
| 354 |
+ name: "IPv4 and IPv6, ipv6 enabled", |
|
| 355 |
+ input: "nameserver 10.0.0.1\nnameserver fdb6:b8fe:b528::1", |
|
| 356 |
+ ipv6: true, |
|
| 357 |
+ expExtServers: []ExtDNSEntry{
|
|
| 358 |
+ mke("10.0.0.1", false),
|
|
| 359 |
+ mke("fdb6:b8fe:b528::1", false),
|
|
| 360 |
+ }, |
|
| 358 | 361 |
}, |
| 359 | 362 |
{
|
| 360 |
- name: "IPv4 and IPv6, ipv6 disabled", |
|
| 361 |
- input: "nameserver 10.0.0.1\nnameserver fdb6:b8fe:b528::1", |
|
| 362 |
- ipv6: false, |
|
| 363 |
- expExtServers: []ExtDNSEntry{mke("10.0.0.1", false)},
|
|
| 363 |
+ name: "IPv4 and IPv6, ipv6 disabled", |
|
| 364 |
+ input: "nameserver 10.0.0.1\nnameserver fdb6:b8fe:b528::1", |
|
| 365 |
+ ipv6: false, |
|
| 366 |
+ expExtServers: []ExtDNSEntry{
|
|
| 367 |
+ mke("10.0.0.1", false),
|
|
| 368 |
+ mke("fdb6:b8fe:b528::1", true),
|
|
| 369 |
+ }, |
|
| 364 | 370 |
}, |
| 365 | 371 |
{
|
| 366 | 372 |
name: "IPv4 localhost", |
| ... | ... |
@@ -384,37 +390,46 @@ func TestRCTransformForIntNS(t *testing.T) {
|
| 384 | 384 |
expExtServers: []ExtDNSEntry{mke("127.0.0.53", true)},
|
| 385 | 385 |
}, |
| 386 | 386 |
{
|
| 387 |
- name: "IPv6 addr, IPv6 enabled", |
|
| 388 |
- input: "nameserver fd14:6e0e:f855::1", |
|
| 389 |
- ipv6: true, |
|
| 390 |
- // Note that there are no ext servers in this case, the internal resolver |
|
| 391 |
- // will only look up container names. The default nameservers aren't added |
|
| 392 |
- // because the host's IPv6 nameserver remains in the container's resolv.conf, |
|
| 393 |
- // (because only IPv4 ext servers are currently allowed). |
|
| 387 |
+ name: "IPv6 addr, IPv6 enabled", |
|
| 388 |
+ input: "nameserver fd14:6e0e:f855::1", |
|
| 389 |
+ ipv6: true, |
|
| 390 |
+ expExtServers: []ExtDNSEntry{mke("fd14:6e0e:f855::1", false)},
|
|
| 394 | 391 |
}, |
| 395 | 392 |
{
|
| 396 |
- name: "IPv4 and IPv6 localhost, IPv6 disabled", |
|
| 397 |
- input: "nameserver 127.0.0.53\nnameserver ::1", |
|
| 398 |
- ipv6: false, |
|
| 399 |
- expExtServers: []ExtDNSEntry{mke("127.0.0.53", true)},
|
|
| 393 |
+ name: "IPv4 and IPv6 localhost, IPv6 disabled", |
|
| 394 |
+ input: "nameserver 127.0.0.53\nnameserver ::1", |
|
| 395 |
+ ipv6: false, |
|
| 396 |
+ expExtServers: []ExtDNSEntry{
|
|
| 397 |
+ mke("127.0.0.53", true),
|
|
| 398 |
+ mke("::1", true),
|
|
| 399 |
+ }, |
|
| 400 | 400 |
}, |
| 401 | 401 |
{
|
| 402 |
- name: "IPv4 and IPv6 localhost, ipv6 enabled", |
|
| 403 |
- input: "nameserver 127.0.0.53\nnameserver ::1", |
|
| 404 |
- ipv6: true, |
|
| 405 |
- expExtServers: []ExtDNSEntry{mke("127.0.0.53", true)},
|
|
| 402 |
+ name: "IPv4 and IPv6 localhost, ipv6 enabled", |
|
| 403 |
+ input: "nameserver 127.0.0.53\nnameserver ::1", |
|
| 404 |
+ ipv6: true, |
|
| 405 |
+ expExtServers: []ExtDNSEntry{
|
|
| 406 |
+ mke("127.0.0.53", true),
|
|
| 407 |
+ mke("::1", true),
|
|
| 408 |
+ }, |
|
| 406 | 409 |
}, |
| 407 | 410 |
{
|
| 408 |
- name: "IPv4 localhost, IPv6 private, IPv6 enabled", |
|
| 409 |
- input: "nameserver 127.0.0.53\nnameserver fd3e:2d1a:1f5a::1", |
|
| 410 |
- ipv6: true, |
|
| 411 |
- expExtServers: []ExtDNSEntry{mke("127.0.0.53", true)},
|
|
| 411 |
+ name: "IPv4 localhost, IPv6 private, IPv6 enabled", |
|
| 412 |
+ input: "nameserver 127.0.0.53\nnameserver fd3e:2d1a:1f5a::1", |
|
| 413 |
+ ipv6: true, |
|
| 414 |
+ expExtServers: []ExtDNSEntry{
|
|
| 415 |
+ mke("127.0.0.53", true),
|
|
| 416 |
+ mke("fd3e:2d1a:1f5a::1", false),
|
|
| 417 |
+ }, |
|
| 412 | 418 |
}, |
| 413 | 419 |
{
|
| 414 |
- name: "IPv4 localhost, IPv6 private, IPv6 disabled", |
|
| 415 |
- input: "nameserver 127.0.0.53\nnameserver fd3e:2d1a:1f5a::1", |
|
| 416 |
- ipv6: false, |
|
| 417 |
- expExtServers: []ExtDNSEntry{mke("127.0.0.53", true)},
|
|
| 420 |
+ name: "IPv4 localhost, IPv6 private, IPv6 disabled", |
|
| 421 |
+ input: "nameserver 127.0.0.53\nnameserver fd3e:2d1a:1f5a::1", |
|
| 422 |
+ ipv6: false, |
|
| 423 |
+ expExtServers: []ExtDNSEntry{
|
|
| 424 |
+ mke("127.0.0.53", true),
|
|
| 425 |
+ mke("fd3e:2d1a:1f5a::1", true),
|
|
| 426 |
+ }, |
|
| 418 | 427 |
}, |
| 419 | 428 |
{
|
| 420 | 429 |
name: "No host nameserver, no iv6", |
| ... | ... |
@@ -1838,7 +1838,7 @@ func TestResolvConf(t *testing.T) {
|
| 1838 | 1838 |
makeNet: makeTestIPv6Network, |
| 1839 | 1839 |
delNet: true, |
| 1840 | 1840 |
originResolvConf: "search pommesfrites.fr\nnameserver 12.34.56.78\nnameserver 2001:4860:4860::8888\n", |
| 1841 |
- expResolvConf: "nameserver 127.0.0.11\nnameserver 2001:4860:4860::8888\nsearch pommesfrites.fr\noptions ndots:0", |
|
| 1841 |
+ expResolvConf: "nameserver 127.0.0.11\nsearch pommesfrites.fr\noptions ndots:0", |
|
| 1842 | 1842 |
}, |
| 1843 | 1843 |
{
|
| 1844 | 1844 |
name: "host network", |
| ... | ... |
@@ -327,20 +327,17 @@ func (sb *Sandbox) rebuildDNS() error {
|
| 327 | 327 |
return err |
| 328 | 328 |
} |
| 329 | 329 |
|
| 330 |
- // Check for IPv6 endpoints in this sandbox. If there are any, IPv6 nameservers |
|
| 331 |
- // will be left in the container's 'resolv.conf'. |
|
| 332 |
- // TODO(robmry) - preserving old behaviour, but ... |
|
| 333 |
- // IPv6 nameservers should be treated like IPv4 ones, and used as upstream |
|
| 334 |
- // servers for the internal resolver (if it has IPv6 connectivity). This |
|
| 335 |
- // doesn't need to depend on whether there are currently any IPv6 endpoints. |
|
| 336 |
- // Removing IPv6 nameservers from the container's resolv.conf will avoid the |
|
| 337 |
- // problem that musl-libc's resolver tries all nameservers in parallel, so an |
|
| 338 |
- // external IPv6 resolver can return NXDOMAIN before the internal resolver |
|
| 339 |
- // returns the address of a container. |
|
| 330 |
+ // Check for IPv6 endpoints in this sandbox. If there are any, and the container has |
|
| 331 |
+ // IPv6 enabled, upstream requests from the internal DNS resolver can be made from |
|
| 332 |
+ // the container's namespace. |
|
| 333 |
+ // TODO(robmry) - this can only check networks connected when the resolver is set up, |
|
| 334 |
+ // the configuration won't be updated if the container gets an IPv6 address later. |
|
| 340 | 335 |
ipv6 := false |
| 341 | 336 |
for _, ep := range sb.endpoints {
|
| 342 | 337 |
if ep.network.enableIPv6 {
|
| 343 |
- ipv6 = true |
|
| 338 |
+ if en, ok := sb.ipv6Enabled(); ok {
|
|
| 339 |
+ ipv6 = en |
|
| 340 |
+ } |
|
| 344 | 341 |
break |
| 345 | 342 |
} |
| 346 | 343 |
} |