From a9b119d4858a40936583262cb8d878036a0171dd Mon Sep 17 00:00:00 2001
From: root <root@localhost.localdomain>
Date: Sun, 14 Apr 2024 19:46:56 +0800
Subject: [PATCH] fix cve-2024-29018

---
 integration/networking/bridge_test.go | 20 +++++++++++++++-----
 libnetwork/endpoint.go                |  8 +++++++-
 libnetwork/resolver.go                | 21 +++++++++++++++++----
 libnetwork/sandbox_dns_unix.go        |  5 +----
 4 files changed, 40 insertions(+), 14 deletions(-)

diff --git a/integration/networking/bridge_test.go b/integration/networking/bridge_test.go
index e3d1fe2..4be3e55 100644
--- a/integration/networking/bridge_test.go
+++ b/integration/networking/bridge_test.go
@@ -36,7 +36,8 @@ func TestBridgeICC(t *testing.T) {
 		name           string
 		bridgeOpts     []func(*types.NetworkCreate)
 		ctr1MacAddress string
-		linkLocal      bool
+		isIPv6         bool
+		isLinkLocal    bool
 		pingHost       string
 	}{
 		{
@@ -55,6 +56,7 @@ func TestBridgeICC(t *testing.T) {
 				network.WithIPv6(),
 				network.WithIPAM("fdf1:a844:380c:b200::/64", "fdf1:a844:380c:b200::1"),
 			},
+			isIPv6: true,
 		},
 		{
 			name: "IPv6 ULA on internal network",
@@ -74,7 +76,8 @@ func TestBridgeICC(t *testing.T) {
 				// 2. the one dynamically assigned by the IPAM driver.
 				network.WithIPAM("fe80::/64", "fe80::1"),
 			},
-			linkLocal: true,
+			isLinkLocal: true,
+			isIPv6:      true,
 		},
 		{
 			name: "IPv6 link-local address on internal network",
@@ -84,7 +87,8 @@ func TestBridgeICC(t *testing.T) {
 				// See the note above about link-local addresses.
 				network.WithIPAM("fe80::/64", "fe80::1"),
 			},
-			linkLocal: true,
+			isLinkLocal: true,
+			isIPv6:      true,
 		},
 		{
 			// As for 'LL non-internal', but ping the container by name instead of by address
@@ -122,6 +126,7 @@ func TestBridgeICC(t *testing.T) {
 			// specify one here to hardcode the SLAAC LL address below.
 			ctr1MacAddress: "02:42:ac:11:00:02",
 			pingHost:       "fe80::42:acff:fe11:2%eth0",
+			isIPv6:         true,
 		},
 		{
 			name: "IPv6 internal network with SLAAC LL address",
@@ -133,6 +138,7 @@ func TestBridgeICC(t *testing.T) {
 			// specify one here to hardcode the SLAAC LL address below.
 			ctr1MacAddress: "02:42:ac:11:00:02",
 			pingHost:       "fe80::42:acff:fe11:2%eth0",
+			isIPv6:         true,
 		},
 	}
 
@@ -162,7 +168,7 @@ func TestBridgeICC(t *testing.T) {
 
 			pingHost := tc.pingHost
 			if pingHost == "" {
-				if tc.linkLocal {
+				if tc.isLinkLocal {
 					inspect := container.Inspect(ctx, t, c, id1)
 					pingHost = inspect.NetworkSettings.Networks[bridgeName].GlobalIPv6Address + "%eth0"
 				} else {
@@ -170,7 +176,11 @@ func TestBridgeICC(t *testing.T) {
 				}
 			}
 
-			pingCmd := []string{"ping", "-c1", "-W3", pingHost}
+			pingCmd := []string{"ping", "-c1", "-W3"}
+			if tc.isIPv6 {
+				pingCmd = append(pingCmd, "-6")
+			}
+			pingCmd = append(pingCmd, pingHost)
 
 			ctr2Name := fmt.Sprintf("ctr-icc-%d-2", tcID)
 			attachCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
diff --git a/libnetwork/endpoint.go b/libnetwork/endpoint.go
index d9c257d..93ddbc9 100644
--- a/libnetwork/endpoint.go
+++ b/libnetwork/endpoint.go
@@ -538,8 +538,11 @@ func (ep *Endpoint) sbJoin(sb *Sandbox, options ...EndpointOption) (err error) {
 		return sb.setupDefaultGW()
 	}
 
-	moveExtConn := sb.getGatewayEndpoint() != extEp
+	currentExtEp := sb.getGatewayEndpoint()
+	// Enable upstream forwarding if the sandbox gained external connectivity.
+	sb.resolver.SetForwardingPolicy(currentExtEp != nil)
 
+	moveExtConn := currentExtEp != extEp
 	if moveExtConn {
 		if extEp != nil {
 			log.G(context.TODO()).Debugf("Revoking external connectivity on endpoint %s (%s)", extEp.Name(), extEp.ID())
@@ -735,6 +738,9 @@ func (ep *Endpoint) sbLeave(sb *Sandbox, force bool, options ...EndpointOption)
 
 	// New endpoint providing external connectivity for the sandbox
 	extEp = sb.getGatewayEndpoint()
+	// Disable upstream forwarding if the sandbox lost external connectivity.
+	sb.resolver.SetForwardingPolicy(extEp != nil)
+
 	if moveExtConn && extEp != nil {
 		log.G(context.TODO()).Debugf("Programming external connectivity on endpoint %s (%s)", extEp.Name(), extEp.ID())
 		extN, err := extEp.getNetworkFromStore()
diff --git a/libnetwork/resolver.go b/libnetwork/resolver.go
index 9df2154..6da595c 100644
--- a/libnetwork/resolver.go
+++ b/libnetwork/resolver.go
@@ -9,6 +9,7 @@ import (
 	"strconv"
 	"strings"
 	"sync"
+	"sync/atomic"
 	"time"
 
 	"github.com/containerd/log"
@@ -75,7 +76,7 @@ type Resolver struct {
 	tcpListen     *net.TCPListener
 	err           error
 	listenAddress string
-	proxyDNS      bool
+	proxyDNS      atomic.Bool
 	startCh       chan struct{}
 	logger        *log.Entry
 
@@ -85,15 +86,17 @@ type Resolver struct {
 
 // NewResolver creates a new instance of the Resolver
 func NewResolver(address string, proxyDNS bool, backend DNSBackend) *Resolver {
-	return &Resolver{
+	r := &Resolver{
 		backend:       backend,
-		proxyDNS:      proxyDNS,
 		listenAddress: address,
 		err:           fmt.Errorf("setup not done yet"),
 		startCh:       make(chan struct{}, 1),
 		fwdSem:        semaphore.NewWeighted(maxConcurrent),
 		logInverval:   rate.Sometimes{Interval: logInterval},
 	}
+	r.proxyDNS.Store(proxyDNS)
+
+	return r
 }
 
 func (r *Resolver) log(ctx context.Context) *log.Entry {
@@ -103,6 +106,8 @@ func (r *Resolver) log(ctx context.Context) *log.Entry {
 	return r.logger
 }
 
+
+
 // SetupFunc returns the setup function that should be run in the container's
 // network namespace.
 func (r *Resolver) SetupFunc(port int) func() {
@@ -194,6 +199,14 @@ func (r *Resolver) SetExtServers(extDNS []extDNSEntry) {
 	}
 }
 
+// SetForwardingPolicy re-configures the embedded DNS resolver to either enable or disable forwarding DNS queries to
+// external servers.
+func (r *Resolver) SetForwardingPolicy(policy bool) {
+	if r != nil {
+		r.proxyDNS.Store(policy)
+	}
+}
+
 // NameServer returns the IP of the DNS resolver for the containers.
 func (r *Resolver) NameServer() string {
 	return r.listenAddress
@@ -421,7 +434,7 @@ func (r *Resolver) serveDNS(w dns.ResponseWriter, query *dns.Msg) {
 		return
 	}
 
-	if r.proxyDNS {
+	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
diff --git a/libnetwork/sandbox_dns_unix.go b/libnetwork/sandbox_dns_unix.go
index e30f394..505c5f5 100644
--- a/libnetwork/sandbox_dns_unix.go
+++ b/libnetwork/sandbox_dns_unix.go
@@ -30,10 +30,7 @@ const (
 func (sb *Sandbox) startResolver(restore bool) {
 	sb.resolverOnce.Do(func() {
 		var err error
-		// The embedded resolver is always started with proxyDNS set as true, even when the sandbox is only attached to
-		// an internal network. This way, it's the driver responsibility to make sure `connect` syscall fails fast when
-		// no external connectivity is available (eg. by not setting a default gateway).
-		sb.resolver = NewResolver(resolverIPSandbox, true, sb)
+		sb.resolver = NewResolver(resolverIPSandbox, sb.getGatewayEndpoint() != nil, sb)
 		defer func() {
 			if err != nil {
 				sb.resolver = nil
-- 
2.27.0

