From d3289dda4bd9339a053abc33ed0912a00a56ce4d Mon Sep 17 00:00:00 2001 From: Rob Murray Date: Tue, 22 Apr 2025 14:55:16 +0100 Subject: [PATCH] Add nftables NAT rules for internal DNS resolver Signed-off-by: Rob Murray --- libnetwork/resolver.go | 4 +-- libnetwork/resolver_unix.go | 49 +++++++++++++++++++++++++++++++++- libnetwork/resolver_windows.go | 4 ++- 3 files changed, 53 insertions(+), 4 deletions(-) diff --git a/libnetwork/resolver.go b/libnetwork/resolver.go index f75d70e7be..c6bdc6b63e 100644 --- a/libnetwork/resolver.go +++ b/libnetwork/resolver.go @@ -180,8 +180,8 @@ func (r *Resolver) Start() error { return r.err } - if err := r.setupIPTable(); err != nil { - return fmt.Errorf("setting up IP table rules failed: %v", err) + if err := r.setupNAT(context.TODO()); err != nil { + return fmt.Errorf("setting up DNAT/SNAT rules failed: %v", err) } s := &dns.Server{Handler: dns.HandlerFunc(r.serveDNS), PacketConn: r.conn} diff --git a/libnetwork/resolver_unix.go b/libnetwork/resolver_unix.go index d0f47ddeeb..7d007f1985 100644 --- a/libnetwork/resolver_unix.go +++ b/libnetwork/resolver_unix.go @@ -3,9 +3,11 @@ package libnetwork import ( + "context" "fmt" "net" + "github.com/docker/docker/libnetwork/internal/nftables" "github.com/docker/docker/libnetwork/iptables" ) @@ -16,7 +18,7 @@ const ( postroutingChain = "DOCKER_POSTROUTING" ) -func (r *Resolver) setupIPTable() error { +func (r *Resolver) setupNAT(ctx context.Context) error { if r.err != nil { return r.err } @@ -24,6 +26,14 @@ func (r *Resolver) setupIPTable() error { ltcpaddr := r.tcpListen.Addr().String() resolverIP, ipPort, _ := net.SplitHostPort(laddr) _, tcpPort, _ := net.SplitHostPort(ltcpaddr) + + if nftables.Enabled() { + return r.setupNftablesNAT(ctx, laddr, ltcpaddr, resolverIP, ipPort, tcpPort) + } + return r.setupIptablesNAT(laddr, ltcpaddr, resolverIP, ipPort, tcpPort) +} + +func (r *Resolver) setupIptablesNAT(laddr, ltcpaddr, resolverIP, ipPort, tcpPort string) error { rules := [][]string{ {"-t", "nat", "-I", outputChain, "-d", resolverIP, "-p", "udp", "--dport", dnsPort, "-j", "DNAT", "--to-destination", laddr}, {"-t", "nat", "-I", postroutingChain, "-s", resolverIP, "-p", "udp", "--sport", ipPort, "-j", "SNAT", "--to-source", ":" + dnsPort}, @@ -81,3 +91,40 @@ func (r *Resolver) setupIPTable() error { } return setupErr } + +func (r *Resolver) setupNftablesNAT(ctx context.Context, laddr, ltcpaddr, resolverIP, ipPort, tcpPort string) error { + table, err := nftables.NewTable(nftables.IPv4, "docker-dns") + if err != nil { + return err + } + + dnatChain, err := table.BaseChain("dns-dnat", nftables.BaseChainTypeNAT, nftables.BaseChainHookOutput, nftables.BaseChainPriorityDstNAT) + if err != nil { + return err + } + if err := dnatChain.AppendRule(0, "ip daddr %s udp dport %s counter dnat to %s", resolverIP, dnsPort, laddr); err != nil { + return err + } + if err := dnatChain.AppendRule(0, "ip daddr %s tcp dport %s counter dnat to %s", resolverIP, dnsPort, ltcpaddr); err != nil { + return err + } + + snatChain, err := table.BaseChain("dns-snat", nftables.BaseChainTypeNAT, nftables.BaseChainHookPostrouting, nftables.BaseChainPrioritySrcNAT) + if err != nil { + return err + } + if err := snatChain.AppendRule(0, "ip saddr %s udp sport %s counter snat to :%s", resolverIP, ipPort, dnsPort); err != nil { + return err + } + if err := snatChain.AppendRule(0, "ip saddr %s tcp sport %s counter snat to :%s", resolverIP, tcpPort, dnsPort); err != nil { + return err + } + + var setupErr error + if err := r.backend.ExecFunc(func() { + setupErr = table.Apply(ctx) + }); err != nil { + return err + } + return setupErr +} diff --git a/libnetwork/resolver_windows.go b/libnetwork/resolver_windows.go index df08ec3a75..c8faf55e39 100644 --- a/libnetwork/resolver_windows.go +++ b/libnetwork/resolver_windows.go @@ -2,6 +2,8 @@ package libnetwork -func (r *Resolver) setupIPTable() error { +import "context" + +func (r *Resolver) setupNAT(context.Context) error { return nil }