mirror of
https://github.com/moby/moby.git
synced 2026-01-11 18:51:37 +00:00
Forward DNS requests into --internal networks
A recent change to prevent containers only connected to --internal
networks from communicating with external DNS servers inadvertently
prevented the daemon's internal DNS server from forwarding requests
within an internal network to a containerised DNS server.
Relax the check, so that only requests that need to be forwarded
from the host's network namespace are dropped.
External DNS servers remain unreachable from the internal network.
Signed-off-by: Rob Murray <rob.murray@docker.com>
(cherry picked from commit 77a47dba3b)
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
This commit is contained in:
committed by
Paweł Gronowski
parent
86af4eddb3
commit
c21fe3efa3
@@ -2,11 +2,14 @@ package networking
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
containertypes "github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/mount"
|
||||
"github.com/docker/docker/integration/internal/container"
|
||||
"github.com/docker/docker/integration/internal/network"
|
||||
"github.com/docker/docker/testutil/daemon"
|
||||
@@ -134,6 +137,59 @@ func TestInternalNetworkDNS(t *testing.T) {
|
||||
assert.Check(t, is.Contains(res.Stdout(), network.DNSRespAddr))
|
||||
}
|
||||
|
||||
// Check that '--dns' can be used to name a server inside a '--internal' network.
|
||||
// Regression test for https://github.com/moby/moby/issues/47822
|
||||
func TestInternalNetworkLocalDNS(t *testing.T) {
|
||||
skip.If(t, testEnv.DaemonInfo.OSType == "windows", "No internal networks on Windows")
|
||||
skip.If(t, testEnv.IsRootless, "Can't write an accessible dnsd.conf in rootless mode")
|
||||
ctx := setupTest(t)
|
||||
|
||||
d := daemon.New(t)
|
||||
d.StartWithBusybox(ctx, t)
|
||||
defer d.Stop(t)
|
||||
|
||||
c := d.NewClientT(t)
|
||||
defer c.Close()
|
||||
|
||||
intNetName := "intnet"
|
||||
network.CreateNoError(ctx, t, c, intNetName,
|
||||
network.WithDriver("bridge"),
|
||||
network.WithInternal(),
|
||||
)
|
||||
defer network.RemoveNoError(ctx, t, c, intNetName)
|
||||
|
||||
// Write a config file for busybox's dnsd.
|
||||
td := t.TempDir()
|
||||
fname := path.Join(td, "dnsd.conf")
|
||||
err := os.WriteFile(fname, []byte("foo.example 192.0.2.42\n"), 0644)
|
||||
assert.NilError(t, err)
|
||||
|
||||
// Start a DNS server on the internal network.
|
||||
serverId := container.Run(ctx, t, c,
|
||||
container.WithNetworkMode(intNetName),
|
||||
container.WithMount(mount.Mount{
|
||||
Type: mount.TypeBind,
|
||||
Source: fname,
|
||||
Target: "/etc/dnsd.conf",
|
||||
}),
|
||||
container.WithCmd("dnsd"),
|
||||
)
|
||||
defer c.ContainerRemove(ctx, serverId, containertypes.RemoveOptions{Force: true})
|
||||
|
||||
// Get the DNS server's address.
|
||||
inspect := container.Inspect(ctx, t, c, serverId)
|
||||
serverIP := inspect.NetworkSettings.Networks[intNetName].IPAddress
|
||||
|
||||
// Query the internal network's DNS server (via the daemon's internal DNS server).
|
||||
res := container.RunAttach(ctx, t, c,
|
||||
container.WithNetworkMode(intNetName),
|
||||
container.WithDNS([]string{serverIP}),
|
||||
container.WithCmd("nslookup", "-type=A", "foo.example"),
|
||||
)
|
||||
defer c.ContainerRemove(ctx, res.ContainerID, containertypes.RemoveOptions{Force: true})
|
||||
assert.Check(t, is.Contains(res.Stdout.String(), "192.0.2.42"))
|
||||
}
|
||||
|
||||
// TestNslookupWindows checks that nslookup gets results from external DNS.
|
||||
// Regression test for https://github.com/moby/moby/issues/46792
|
||||
func TestNslookupWindows(t *testing.T) {
|
||||
|
||||
@@ -486,17 +486,15 @@ func (r *Resolver) serveDNS(w dns.ResponseWriter, query *dns.Msg) {
|
||||
return
|
||||
}
|
||||
|
||||
if r.proxyDNS.Load() {
|
||||
// If the user sets ndots > 0 explicitly and the query is
|
||||
// in the root domain don't forward it out. We will return
|
||||
// failure and let the client retry with the search domain
|
||||
// attached.
|
||||
if (queryType == dns.TypeA || queryType == dns.TypeAAAA) && r.backend.NdotsSet() &&
|
||||
!strings.Contains(strings.TrimSuffix(queryName, "."), ".") {
|
||||
resp = createRespMsg(query)
|
||||
} else {
|
||||
resp = r.forwardExtDNS(ctx, w.LocalAddr().Network(), w.RemoteAddr(), query)
|
||||
}
|
||||
// If the user sets ndots > 0 explicitly and the query is
|
||||
// in the root domain don't forward it out. We will return
|
||||
// failure and let the client retry with the search domain
|
||||
// attached.
|
||||
if (queryType == dns.TypeA || queryType == dns.TypeAAAA) && r.backend.NdotsSet() &&
|
||||
!strings.Contains(strings.TrimSuffix(queryName, "."), ".") {
|
||||
resp = createRespMsg(query)
|
||||
} else {
|
||||
resp = r.forwardExtDNS(ctx, w.LocalAddr().Network(), w.RemoteAddr(), query)
|
||||
}
|
||||
|
||||
if resp == nil {
|
||||
@@ -541,10 +539,18 @@ func (r *Resolver) forwardExtDNS(ctx context.Context, proto string, remoteAddr n
|
||||
ctx, span := otel.Tracer("").Start(ctx, "resolver.forwardExtDNS")
|
||||
defer span.End()
|
||||
|
||||
proxyDNS := r.proxyDNS.Load()
|
||||
for _, extDNS := range r.extDNS(netiputil.AddrPortFromNet(remoteAddr)) {
|
||||
if extDNS.IPStr == "" {
|
||||
break
|
||||
}
|
||||
// If proxyDNS is false, do not forward the request from the host's namespace
|
||||
// (don't access an external DNS server from an internal network). But, it is
|
||||
// safe to make the request from the container's network namespace - it'll fail
|
||||
// if the DNS server is not accessible, but the server may be on-net.
|
||||
if !proxyDNS && extDNS.HostLoopback {
|
||||
continue
|
||||
}
|
||||
|
||||
// limits the number of outstanding concurrent queries.
|
||||
ctx, cancel := context.WithTimeout(ctx, extIOTimeout)
|
||||
|
||||
Reference in New Issue
Block a user