Repository: cilium/cilium
Pull request #38020: proxy: Provide DefaultDNSProxy as a Hive Cell
Author: login: nathanjsweet
Labels: name: sig/policy
description: Impacts whether traffic is allowed or denied based on user-defined policies.
name: ready-to-merge
description: This PR has passed all tests and received consensus from code owners to merge.
name: release-note/misc
description: This PR makes changes that have no direct user impact.
Created: 2025-03-05T18:52:55Z | Merged: 2025-03-13T16:38:03Z

DefaultDNSProxy is a singleton that has been in the codebase for a while. It is get and set by various daemon methods without synchronization. Providing the singleton via a Cell allows for synchronization control and removes the ugliness of using a package singleton.



Diff:
diff --git a/daemon/cmd/cells.go b/daemon/cmd/cells.go
index a20c088b7cd4d..ce68a6e07580c 100644
--- a/daemon/cmd/cells.go
+++ b/daemon/cmd/cells.go
@@ -67,6 +67,7 @@ import (
 	policyK8s "github.com/cilium/cilium/pkg/policy/k8s"
 	"github.com/cilium/cilium/pkg/pprof"
 	"github.com/cilium/cilium/pkg/proxy"
+	"github.com/cilium/cilium/pkg/proxy/defaultdns"
 	"github.com/cilium/cilium/pkg/recorder"
 	"github.com/cilium/cilium/pkg/redirectpolicy"
 	"github.com/cilium/cilium/pkg/service"
@@ -140,6 +141,10 @@ var (
 
 		// Shell for inspecting the agent. Listens on the 'shell.sock' UNIX socket.
 		shellCell,
+
+		// DNSProxy provides the DefaultDNSProxy singleton which is used by different
+		// pacakges.
+		defaultdns.Cell,
 	)
 
 	// ControlPlane implement the per-node control functions. These are pure
diff --git a/daemon/cmd/daemon.go b/daemon/cmd/daemon.go
index 8836e67feded1..da1345a1ff4a1 100644
--- a/daemon/cmd/daemon.go
+++ b/daemon/cmd/daemon.go
@@ -71,6 +71,7 @@ import (
 	"github.com/cilium/cilium/pkg/policy"
 	policyAPI "github.com/cilium/cilium/pkg/policy/api"
 	"github.com/cilium/cilium/pkg/proxy"
+	"github.com/cilium/cilium/pkg/proxy/defaultdns"
 	"github.com/cilium/cilium/pkg/proxy/logger"
 	"github.com/cilium/cilium/pkg/rate"
 	"github.com/cilium/cilium/pkg/redirectpolicy"
@@ -196,6 +197,8 @@ type Daemon struct {
 	maglevConfig maglev.Config
 
 	explbConfig experimental.Config
+
+	dnsProxy defaultdns.Proxy
 }
 
 // GetPolicyRepository returns the policy repository of the daemon
@@ -416,6 +419,7 @@ func newDaemon(ctx context.Context, cleaner *daemonCleanup, params *daemonParams
 		maglevConfig:      params.MaglevConfig,
 		dnsNameManager:    params.NameManager,
 		explbConfig:       params.ExpLBConfig,
+		dnsProxy:          params.DNSProxy,
 	}
 
 	// initialize endpointRestoreComplete channel as soon as possible so that subsystems
@@ -572,10 +576,11 @@ func newDaemon(ctx context.Context, cleaner *daemonCleanup, params *daemonParams
 		bootstrapStats.fqdn.EndError(err)
 		return nil, restoredEndpoints, err
 	}
-	if proxy.DefaultDNSProxy != nil {
+
+	if dnsProxy := d.dnsProxy.Get(); dnsProxy != nil {
 		// This is done in preCleanup so that proxy stops serving DNS traffic before shutdown
 		cleaner.preCleanupFuncs.Add(func() {
-			proxy.DefaultDNSProxy.Cleanup()
+			dnsProxy.Cleanup()
 		})
 	}
 
diff --git a/daemon/cmd/daemon_main.go b/daemon/cmd/daemon_main.go
index 63eefcc6b4605..2ca153d049c81 100644
--- a/daemon/cmd/daemon_main.go
+++ b/daemon/cmd/daemon_main.go
@@ -99,6 +99,7 @@ import (
 	policyDirectory "github.com/cilium/cilium/pkg/policy/directory"
 	"github.com/cilium/cilium/pkg/promise"
 	"github.com/cilium/cilium/pkg/proxy"
+	"github.com/cilium/cilium/pkg/proxy/defaultdns"
 	"github.com/cilium/cilium/pkg/proxy/logger"
 	"github.com/cilium/cilium/pkg/rate"
 	"github.com/cilium/cilium/pkg/redirectpolicy"
@@ -1585,6 +1586,7 @@ type daemonParams struct {
 	MaglevConfig        maglev.Config
 	NameManager         namemanager.NameManager
 	ExpLBConfig         experimental.Config
+	DNSProxy            defaultdns.Proxy
 }
 
 func newDaemonPromise(params daemonParams) promise.Promise[*Daemon] {
diff --git a/daemon/cmd/daemon_test.go b/daemon/cmd/daemon_test.go
index 3900046642b4b..c50a84174dc76 100644
--- a/daemon/cmd/daemon_test.go
+++ b/daemon/cmd/daemon_test.go
@@ -47,7 +47,7 @@ import (
 	policycell "github.com/cilium/cilium/pkg/policy/cell"
 	policyTypes "github.com/cilium/cilium/pkg/policy/types"
 	"github.com/cilium/cilium/pkg/promise"
-	"github.com/cilium/cilium/pkg/proxy"
+	"github.com/cilium/cilium/pkg/proxy/defaultdns"
 	"github.com/cilium/cilium/pkg/testutils"
 	testidentity "github.com/cilium/cilium/pkg/testutils/identity"
 	"github.com/cilium/cilium/pkg/types"
@@ -101,8 +101,6 @@ func TestMain(m *testing.M) {
 		os.Exit(m.Run())
 	}
 
-	proxy.DefaultDNSProxy = fqdnproxy.MockFQDNProxy{}
-
 	time.Local = time.UTC
 
 	os.Exit(m.Run())
@@ -148,6 +146,7 @@ func setupDaemonSuite(tb testing.TB) *DaemonSuite {
 		ControlPlane,
 		metrics.Cell,
 		store.Cell,
+		defaultdns.Cell,
 		cell.Invoke(func(p promise.Promise[*Daemon]) {
 			daemonPromise = p
 		}),
@@ -171,6 +170,7 @@ func setupDaemonSuite(tb testing.TB) *DaemonSuite {
 	ds.d, err = daemonPromise.Await(ctx)
 	require.NoError(tb, err)
 
+	ds.d.dnsProxy.Set(fqdnproxy.MockFQDNProxy{})
 	kvstore.Client().DeletePrefix(ctx, kvstore.BaseKeyPrefix)
 
 	ds.d.policy.GetSelectorCache().SetLocalIdentityNotifier(testidentity.NewDummyIdentityNotifier())
diff --git a/daemon/cmd/endpoint.go b/daemon/cmd/endpoint.go
index 5e5b5b696b30f..858e21934dbe7 100644
--- a/daemon/cmd/endpoint.go
+++ b/daemon/cmd/endpoint.go
@@ -40,7 +40,6 @@ import (
 	"github.com/cilium/cilium/pkg/mac"
 	monitorAPI "github.com/cilium/cilium/pkg/monitor/api"
 	"github.com/cilium/cilium/pkg/option"
-	"github.com/cilium/cilium/pkg/proxy"
 	"github.com/cilium/cilium/pkg/resiliency"
 	"github.com/cilium/cilium/pkg/time"
 )
@@ -1179,14 +1178,15 @@ func (d *Daemon) QueueEndpointBuild(ctx context.Context, epID uint64) (func(), e
 }
 
 func (d *Daemon) GetDNSRules(epID uint16) restore.DNSRules {
-	if proxy.DefaultDNSProxy == nil {
+	dnsProxy := d.dnsProxy.Get()
+	if dnsProxy == nil {
 		return nil
 	}
 
 	// We get the latest consistent view on the DNS rules by getting handle to the latest
 	// coherent state of the selector cache
 	version := d.policy.GetSelectorCache().GetVersionHandle()
-	rules, err := proxy.DefaultDNSProxy.GetRules(version, epID)
+	rules, err := dnsProxy.GetRules(version, epID)
 	version.Close()
 
 	if err != nil {
@@ -1197,9 +1197,10 @@ func (d *Daemon) GetDNSRules(epID uint16) restore.DNSRules {
 }
 
 func (d *Daemon) RemoveRestoredDNSRules(epID uint16) {
-	if proxy.DefaultDNSProxy == nil {
+	dnsProxy := d.dnsProxy.Get()
+	if dnsProxy == nil {
 		return
 	}
 
-	proxy.DefaultDNSProxy.RemoveRestoredRules(epID)
+	dnsProxy.RemoveRestoredRules(epID)
 }
diff --git a/daemon/cmd/fqdn.go b/daemon/cmd/fqdn.go
index 7bb968df532eb..b397248d9c5a8 100644
--- a/daemon/cmd/fqdn.go
+++ b/daemon/cmd/fqdn.go
@@ -16,13 +16,13 @@ import (
 	"github.com/cilium/cilium/pkg/endpoint"
 	"github.com/cilium/cilium/pkg/fqdn"
 	"github.com/cilium/cilium/pkg/fqdn/dnsproxy"
+	fqdnproxy "github.com/cilium/cilium/pkg/fqdn/proxy"
 	"github.com/cilium/cilium/pkg/fqdn/re"
 	"github.com/cilium/cilium/pkg/identity"
 	"github.com/cilium/cilium/pkg/logging/logfields"
 	"github.com/cilium/cilium/pkg/metrics"
 	"github.com/cilium/cilium/pkg/node"
 	"github.com/cilium/cilium/pkg/option"
-	"github.com/cilium/cilium/pkg/proxy"
 	"github.com/cilium/cilium/pkg/proxy/accesslog"
 	"github.com/cilium/cilium/pkg/proxy/logger"
 	proxytypes "github.com/cilium/cilium/pkg/proxy/types"
@@ -95,20 +95,22 @@ func (d *Daemon) bootstrapFQDN(possibleEndpoints map[uint16]*endpoint.Endpoint,
 		ConcurrencyLimit:       option.Config.DNSProxyConcurrencyLimit,
 		ConcurrencyGracePeriod: option.Config.DNSProxyConcurrencyProcessingGracePeriod,
 	}
-	proxy.DefaultDNSProxy, err = dnsproxy.StartDNSProxy(dnsProxyConfig, d.lookupEPByIP, d.ipcache.LookupSecIDByIP, d.ipcache.LookupByIdentity,
+	var dnsProxy fqdnproxy.DNSProxier
+	dnsProxy, err = dnsproxy.StartDNSProxy(dnsProxyConfig, d.lookupEPByIP, d.ipcache.LookupSecIDByIP, d.ipcache.LookupByIdentity,
 		d.notifyOnDNSMsg)
+	d.dnsProxy.Set(dnsProxy)
 	if err == nil {
 		// Increase the ProxyPort reference count so that it will never get released.
-		err = d.l7Proxy.SetProxyPort(proxytypes.DNSProxyName, proxytypes.ProxyTypeDNS, proxy.DefaultDNSProxy.GetBindPort(), false)
-		if err == nil && port == proxy.DefaultDNSProxy.GetBindPort() {
+		err = d.l7Proxy.SetProxyPort(proxytypes.DNSProxyName, proxytypes.ProxyTypeDNS, dnsProxy.GetBindPort(), false)
+		if err == nil && port == dnsProxy.GetBindPort() {
 			log.Infof("Reusing previous DNS proxy port: %d", port)
 		}
-		proxy.DefaultDNSProxy.SetRejectReply(option.Config.FQDNRejectResponse)
+		dnsProxy.SetRejectReply(option.Config.FQDNRejectResponse)
 		// Restore old rules
 		for _, possibleEP := range possibleEndpoints {
 			// Upgrades from old ciliums have this nil
 			if possibleEP.DNSRules != nil || possibleEP.DNSRulesV2 != nil {
-				proxy.DefaultDNSProxy.RestoreRules(possibleEP)
+				dnsProxy.RestoreRules(possibleEP)
 			}
 		}
 	}
@@ -365,7 +367,11 @@ func (d *Daemon) notifyOnDNSMsg(
 
 	stat.ProcessingTime.End(true)
 
-	ep.UpdateProxyStatistics("fqdn", strings.ToUpper(protocol), serverAddrPort.Port(), proxy.DefaultDNSProxy.GetBindPort(), false, !msg.Response, verdict)
+	bindPort := uint16(0)
+	if dnsProxy := d.dnsProxy.Get(); dnsProxy != nil {
+		bindPort = dnsProxy.GetBindPort()
+	}
+	ep.UpdateProxyStatistics("fqdn", strings.ToUpper(protocol), serverAddrPort.Port(), bindPort, false, !msg.Response, verdict)
 
 	// Ensure that there are no early returns from this function before the
 	// code below, otherwise the log record will not be made.
diff --git a/pkg/proxy/cell.go b/pkg/proxy/cell.go
index fa4ec3111adda..ef2105c37c11a 100644
--- a/pkg/proxy/cell.go
+++ b/pkg/proxy/cell.go
@@ -13,6 +13,7 @@ import (
 	"github.com/cilium/cilium/pkg/envoy"
 	"github.com/cilium/cilium/pkg/logging/logfields"
 	"github.com/cilium/cilium/pkg/option"
+	"github.com/cilium/cilium/pkg/proxy/defaultdns"
 	"github.com/cilium/cilium/pkg/proxy/logger"
 	"github.com/cilium/cilium/pkg/proxy/logger/endpoint"
 	"github.com/cilium/cilium/pkg/proxy/proxyports"
@@ -115,10 +116,12 @@ func newEnvoyProxyIntegration(params envoyProxyIntegrationParams) *envoyProxyInt
 	}
 }
 
-func newDNSProxyIntegration() *dnsProxyIntegration {
+func newDNSProxyIntegration(dnsProxy defaultdns.Proxy) *dnsProxyIntegration {
 	if !option.Config.EnableL7Proxy {
 		return nil
 	}
 
-	return &dnsProxyIntegration{}
+	return &dnsProxyIntegration{
+		dnsProxy: dnsProxy,
+	}
 }
diff --git a/pkg/proxy/defaultdns/cell.go b/pkg/proxy/defaultdns/cell.go
new file mode 100644
index 0000000000000..1092758f2ecd6
--- /dev/null
+++ b/pkg/proxy/defaultdns/cell.go
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: Apache-2.0
+// Copyright Authors of Cilium
+
+package defaultdns
+
+import "github.com/cilium/hive/cell"
+
+// Cell provides the DefaultDNSProxy for the entire process.
+var Cell = cell.Module(
+	"default-dns-proxy",
+	"Provides the DefaultDNSProxy for the entire process",
+
+	cell.Provide(NewProxy),
+)
diff --git a/pkg/proxy/defaultdns/default_dns_proxy.go b/pkg/proxy/defaultdns/default_dns_proxy.go
new file mode 100644
index 0000000000000..aa58399f570bc
--- /dev/null
+++ b/pkg/proxy/defaultdns/default_dns_proxy.go
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: Apache-2.0
+// Copyright Authors of Cilium
+
+package defaultdns
+
+import (
+	"github.com/cilium/cilium/pkg/fqdn/proxy"
+	"github.com/cilium/cilium/pkg/lock"
+)
+
+// NewProxy instantiates the Proxy singleton.
+func NewProxy() Proxy {
+	return &defaultProxy{
+		mu: &lock.RWMutex{},
+	}
+}
+
+// Proxy represents the interface setting
+// and getting the default dns proxy. It
+// is an interface so that it can be mocked.
+type Proxy interface {
+	Get() proxy.DNSProxier
+	Set(proxy.DNSProxier)
+}
+
+// defaultProxy is the default dns proxy getter
+// and setter for the process.
+type defaultProxy struct {
+	mu    *lock.RWMutex
+	proxy proxy.DNSProxier
+}
+
+// Set sets the DefaultDNSProxy
+func (p *defaultProxy) Set(dnsProxy proxy.DNSProxier) {
+	p.mu.Lock()
+	defer p.mu.Unlock()
+	p.proxy = dnsProxy
+}
+
+// Get gets the DefaultDNSProxy
+func (p *defaultProxy) Get() proxy.DNSProxier {
+	p.mu.RLock()
+	defer p.mu.RUnlock()
+	return p.proxy
+}
diff --git a/pkg/proxy/dns.go b/pkg/proxy/dns.go
index 363bc8c58fb00..0f103196d4dc1 100644
--- a/pkg/proxy/dns.go
+++ b/pkg/proxy/dns.go
@@ -4,38 +4,26 @@
 package proxy
 
 import (
+	"errors"
+
 	"github.com/sirupsen/logrus"
 
-	"github.com/cilium/cilium/pkg/fqdn/proxy"
-	"github.com/cilium/cilium/pkg/fqdn/restore"
 	"github.com/cilium/cilium/pkg/logging/logfields"
 	"github.com/cilium/cilium/pkg/policy"
+	defaultdns "github.com/cilium/cilium/pkg/proxy/defaultdns"
 	"github.com/cilium/cilium/pkg/revert"
 )
 
-// DefaultDNSProxy is the global, shared, DNS Proxy singleton.
-var DefaultDNSProxy proxy.DNSProxier
-
 // dnsRedirect implements the Redirect interface for an l7 proxy
 type dnsRedirect struct {
 	Redirect
-	proxyRuleUpdater proxyRuleUpdater
+	dnsProxy defaultdns.Proxy
 }
 
 func (dr *dnsRedirect) GetRedirect() *Redirect {
 	return &dr.Redirect
 }
 
-// proxyRuleUpdater updates L7 proxy rules per endpoint.
-//
-// Note: Implementations must not take the IPcache lock, as the usage of this
-// interface is within the endpoint regeneration critical section.
-type proxyRuleUpdater interface {
-	// UpdateAllowed updates the rules in the DNS proxy with newRules for the
-	// endpointID and destPort.
-	UpdateAllowed(endpointID uint64, destPort restore.PortProto, newRules policy.L7DataMap) (revert.RevertFunc, error)
-}
-
 // setRules replaces old l7 rules of a redirect with new ones.
 func (dr *dnsRedirect) setRules(newRules policy.L7DataMap) (revert.RevertFunc, error) {
 	dr.logger.Debug(
@@ -43,7 +31,12 @@ func (dr *dnsRedirect) setRules(newRules policy.L7DataMap) (revert.RevertFunc, e
 		logfields.NewRules, newRules,
 		logfields.EndpointID, dr.endpointID,
 	)
-	return dr.proxyRuleUpdater.UpdateAllowed(uint64(dr.endpointID), dr.dstPortProto, newRules)
+	dnsProxy := dr.dnsProxy.Get()
+	if dnsProxy == nil {
+		return nil, errors.New("no dns proxy exists to update")
+	}
+	return dnsProxy.UpdateAllowed(uint64(dr.endpointID), dr.dstPortProto, newRules)
+
 }
 
 // UpdateRules atomically replaces the proxy rules in effect for this redirect.
@@ -58,14 +51,16 @@ func (dr *dnsRedirect) Close() {
 	dr.setRules(nil)
 }
 
-type dnsProxyIntegration struct{}
+type dnsProxyIntegration struct {
+	dnsProxy defaultdns.Proxy
+}
 
 // createRedirect creates a redirect to the dns proxy. The redirect structure passed
 // in is safe to access for reading and writing.
 func (p *dnsProxyIntegration) createRedirect(redirect Redirect) (RedirectImplementation, error) {
 	dr := &dnsRedirect{
-		Redirect:         redirect,
-		proxyRuleUpdater: DefaultDNSProxy,
+		Redirect: redirect,
+		dnsProxy: p.dnsProxy,
 	}
 
 	return dr, nil
diff --git a/test/controlplane/suite/agent.go b/test/controlplane/suite/agent.go
index 06d79fa471150..fccc172ebdacd 100644
--- a/test/controlplane/suite/agent.go
+++ b/test/controlplane/suite/agent.go
@@ -32,18 +32,19 @@ import (
 	monitorAgent "github.com/cilium/cilium/pkg/monitor/agent"
 	"github.com/cilium/cilium/pkg/option"
 	"github.com/cilium/cilium/pkg/promise"
-	"github.com/cilium/cilium/pkg/proxy"
+	"github.com/cilium/cilium/pkg/proxy/defaultdns"
 	"github.com/cilium/cilium/pkg/testutils/mockmaps"
 )
 
 type agentHandle struct {
-	t         *testing.T
-	db        *statedb.DB
-	nodeAddrs statedb.Table[datapathTables.NodeAddress]
-	d         *cmd.Daemon
-	p         promise.Promise[*cmd.Daemon]
-	fnh       *fakeTypes.FakeNodeHandler
-	flbMap    *mockmaps.LBMockMap
+	t            *testing.T
+	db           *statedb.DB
+	nodeAddrs    statedb.Table[datapathTables.NodeAddress]
+	d            *cmd.Daemon
+	p            promise.Promise[*cmd.Daemon]
+	fnh          *fakeTypes.FakeNodeHandler
+	flbMap       *mockmaps.LBMockMap
+	defaultProxy defaultdns.Proxy
 
 	hive *hive.Hive
 	log  *slog.Logger
@@ -87,6 +88,7 @@ func (h *agentHandle) setupCiliumAgentHive(clientset k8sClient.Clientset, extraC
 		metrics.Cell,
 		store.Cell,
 		cmd.ControlPlane,
+		defaultdns.Cell,
 		cell.Invoke(func(p promise.Promise[*cmd.Daemon], nh *fakeTypes.FakeNodeHandler, lbMap *mockmaps.LBMockMap) {
 			h.p = p
 			h.fnh = nh
@@ -136,7 +138,7 @@ func (h *agentHandle) populateCiliumAgentOptions(testDir string, modConfig func(
 	h.hive.Viper().Set(option.EndpointGCInterval, 0)
 
 	if option.Config.EnableL7Proxy {
-		proxy.DefaultDNSProxy = fqdnproxy.MockFQDNProxy{}
+		h.defaultProxy.Set(fqdnproxy.MockFQDNProxy{})
 	}
 }
 


Reviews:
user: login: christarazi
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-07T20:54:58Z
user: login: bimmlerd
body: I've left a few nits; but in general this looks good. The only blocking part is the infrastructure vs controlplane location of the cell.

The fact that we can't "just" provide a proxy instead of the whole getting/setting irks me a bit, but maybe we can clean that up in a future PR
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-03-13T09:07:26Z
user: login: nathanjsweet
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-13T13:53:31Z
user: login: bimmlerd
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-13T15:01:31Z
user: login: bimmlerd
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-13T15:01:41Z

Review comments:
diff_hunk: @@ -139,6 +140,10 @@ var (
 
 		// Shell for inspecting the agent. Listens on the 'shell.sock' UNIX socket.
 		shellCell,
+
+		// DNSProxy provides the DefaultDNSProxy singleton which is used by different
+		// pacakges.
+		defaultdns.Cell,
path: daemon/cmd/cells.go
user: login: bimmlerd
body: I'd probably move this into the `ControlPlane` unless there's a strong reason to keep it in `Infrastructure`?
created_at: 2025-03-11T08:25:48Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,36 @@
+// SPDX-License-Identifier: Apache-2.0
+// Copyright Authors of Cilium
+package defaultdns
+
+import (
+	"github.com/cilium/cilium/pkg/fqdn/proxy"
+	"github.com/cilium/cilium/pkg/lock"
+)
+
+// NewProxy instantiates the Proxy singleton.
+func NewProxy() *Proxy {
path: pkg/proxy/defaultdns/default_dns_proxy.go
user: login: bimmlerd
body: Unfortunately, `hive` doesn't really jive well with the whole `accept interfaces, return structs` Go advice - would it make sense to provide an interface here instead of the actual proxy? Could make it easier to inject a mock for testing
created_at: 2025-03-13T08:14:10Z
author_association: MEMBER
diff_hunk: @@ -4,46 +4,37 @@
 package proxy
 
 import (
+	"errors"
+
 	"github.com/sirupsen/logrus"
 
-	"github.com/cilium/cilium/pkg/fqdn/proxy"
-	"github.com/cilium/cilium/pkg/fqdn/restore"
 	"github.com/cilium/cilium/pkg/logging/logfields"
 	"github.com/cilium/cilium/pkg/policy"
+	defaultdns "github.com/cilium/cilium/pkg/proxy/defaultdns"
 	"github.com/cilium/cilium/pkg/revert"
 )
 
-// DefaultDNSProxy is the global, shared, DNS Proxy singleton.
-var DefaultDNSProxy proxy.DNSProxier
-
 // dnsRedirect implements the Redirect interface for an l7 proxy
 type dnsRedirect struct {
 	Redirect
-	proxyRuleUpdater proxyRuleUpdater
+	dnsProxy *defaultdns.Proxy
 }
 
 func (dr *dnsRedirect) GetRedirect() *Redirect {
 	return &dr.Redirect
 }
 
-// proxyRuleUpdater updates L7 proxy rules per endpoint.
-//
-// Note: Implementations must not take the IPcache lock, as the usage of this
-// interface is within the endpoint regeneration critical section.
-type proxyRuleUpdater interface {
-	// UpdateAllowed updates the rules in the DNS proxy with newRules for the
-	// endpointID and destPort.
-	UpdateAllowed(endpointID uint64, destPort restore.PortProto, newRules policy.L7DataMap) (revert.RevertFunc, error)
-}
-
 // setRules replaces old l7 rules of a redirect with new ones.
 func (dr *dnsRedirect) setRules(newRules policy.L7DataMap) (revert.RevertFunc, error) {
 	dr.logger.Debug(
 		"DNS Proxy updating matchNames in allowed list during UpdateRules",
 		logfields.NewRules, newRules,
 		logfields.EndpointID, dr.endpointID,
 	)
-	return dr.proxyRuleUpdater.UpdateAllowed(uint64(dr.endpointID), dr.dstPortProto, newRules)
+	if dnsProxy := dr.dnsProxy.Get(); dnsProxy != nil {
+		return dnsProxy.UpdateAllowed(uint64(dr.endpointID), dr.dstPortProto, newRules)
path: pkg/proxy/dns.go
user: login: bimmlerd
body: nit: go style generally prefers handling the error case in the indented branch of the if; https://go.dev/wiki/CodeReviewComments#indent-error-flow

created_at: 2025-03-13T09:05:16Z
author_association: MEMBER
diff_hunk: @@ -139,6 +140,10 @@ var (
 
 		// Shell for inspecting the agent. Listens on the 'shell.sock' UNIX socket.
 		shellCell,
+
+		// DNSProxy provides the DefaultDNSProxy singleton which is used by different
+		// pacakges.
+		defaultdns.Cell,
path: daemon/cmd/cells.go
user: login: nathanjsweet
body: It only works in the infrastructure section of the agent in the `test/controlplane/suite` tests. It also meets the requirements specified in the infrastructure comment:
> Infrastructure provides access and services to the outside.
> A cell should live here instead of ControlPlane if it is not needed by
> integrations tests, or needs to be mocked.

It can provide access to services outside the codebase, and it needs to be mocked to be tested.
created_at: 2025-03-13T13:53:31Z
author_association: MEMBER
diff_hunk: @@ -139,6 +140,10 @@ var (
 
 		// Shell for inspecting the agent. Listens on the 'shell.sock' UNIX socket.
 		shellCell,
+
+		// DNSProxy provides the DefaultDNSProxy singleton which is used by different
+		// pacakges.
+		defaultdns.Cell,
path: daemon/cmd/cells.go
user: login: bimmlerd
body: Right, fair enough. 
created_at: 2025-03-13T15:01:31Z
author_association: MEMBER

PR comments:
user: login: nathanjsweet
created_at: 2025-03-06T19:39:48Z
body: /test
author_association: MEMBER
user: login: nathanjsweet
created_at: 2025-03-07T20:16:56Z
body: /test
author_association: MEMBER
user: login: nathanjsweet
created_at: 2025-03-09T02:54:02Z
body: /test
author_association: MEMBER
user: login: nathanjsweet
created_at: 2025-03-12T14:10:07Z
body: /test
author_association: MEMBER
user: login: nathanjsweet
created_at: 2025-03-13T15:03:39Z
body: /test
author_association: MEMBER