feature/featuretags: add option to turn off DNS

Saves 328 KB (2.5%) off the minimal binary.

For IoT devices that don't need MagicDNS (e.g. they don't make
outbound connections), this provides a knob to disable all the DNS
functionality.

Rather than a massive refactor today, this uses constant false values
as a deadcode sledgehammer, guided by shotizam to find the largest DNS
functions which survived deadcode.

A future refactor could make it so that the net/dns/resolver and
publicdns packages don't even show up in the import graph (along with
their imports) but really it's already pretty good looking with just
these consts, so it's not at the top of my list to refactor it more
soon.

Also do the same in a few places with the ACME (cert) functionality,
as I saw those while searching for DNS stuff.

Updates #12614

Change-Id: I8e459f595c2fde68ca16503ff61c8ab339871f97
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick
2025-09-29 22:10:28 -07:00
committed by Brad Fitzpatrick
parent a45473c4c5
commit bcd79b161a
16 changed files with 144 additions and 4 deletions
+9
View File
@@ -729,6 +729,9 @@ func (b *LocalBackend) SetComponentDebugLogging(component string, until time.Tim
// GetDNSOSConfig returns the base OS DNS configuration, as seen by the DNS manager.
func (b *LocalBackend) GetDNSOSConfig() (dns.OSConfig, error) {
if !buildfeatures.HasDNS {
panic("unreachable")
}
manager, ok := b.sys.DNSManager.GetOK()
if !ok {
return dns.OSConfig{}, errors.New("DNS manager not available")
@@ -740,6 +743,9 @@ func (b *LocalBackend) GetDNSOSConfig() (dns.OSConfig, error) {
// the raw DNS response and the resolvers that are were able to handle the query (the internal forwarder
// may race multiple resolvers).
func (b *LocalBackend) QueryDNS(name string, queryType dnsmessage.Type) (res []byte, resolvers []*dnstype.Resolver, err error) {
if !buildfeatures.HasDNS {
return nil, nil, feature.ErrUnavailable
}
manager, ok := b.sys.DNSManager.GetOK()
if !ok {
return nil, nil, errors.New("DNS manager not available")
@@ -6189,6 +6195,9 @@ func (b *LocalBackend) TestOnlyPublicKeys() (machineKey key.MachinePublic, nodeK
// This is the low-level interface. Other layers will provide more
// friendly options to get HTTPS certs.
func (b *LocalBackend) SetDNS(ctx context.Context, name, value string) error {
if !buildfeatures.HasACME {
return feature.ErrUnavailable
}
req := &tailcfg.SetDNSRequest{
Version: 1, // TODO(bradfitz,maisem): use tailcfg.CurrentCapabilityVersion when using the Noise transport
Type: "TXT",
+4
View File
@@ -12,6 +12,7 @@ import (
"sync/atomic"
"go4.org/netipx"
"tailscale.com/feature/buildfeatures"
"tailscale.com/ipn"
"tailscale.com/net/dns"
"tailscale.com/net/tsaddr"
@@ -630,6 +631,9 @@ func dnsConfigForNetmap(nm *netmap.NetworkMap, peers map[tailcfg.NodeID]tailcfg.
if nm == nil {
return nil
}
if !buildfeatures.HasDNS {
return &dns.Config{}
}
// If the current node's key is expired, then we don't program any DNS
// configuration into the operating system. This ensures that if the
+9 -1
View File
@@ -26,6 +26,7 @@ import (
"golang.org/x/net/dns/dnsmessage"
"golang.org/x/net/http/httpguts"
"tailscale.com/envknob"
"tailscale.com/feature/buildfeatures"
"tailscale.com/health"
"tailscale.com/hostinfo"
"tailscale.com/net/netaddr"
@@ -636,6 +637,10 @@ func (h *peerAPIHandler) handleServeMetrics(w http.ResponseWriter, r *http.Reque
}
func (h *peerAPIHandler) handleServeDNSFwd(w http.ResponseWriter, r *http.Request) {
if !buildfeatures.HasDNS {
http.NotFound(w, r)
return
}
if !h.canDebug() {
http.Error(w, "denied; no debug access", http.StatusForbidden)
return
@@ -649,6 +654,9 @@ func (h *peerAPIHandler) handleServeDNSFwd(w http.ResponseWriter, r *http.Reques
}
func (h *peerAPIHandler) replyToDNSQueries() bool {
if !buildfeatures.HasDNS {
return false
}
if h.isSelf {
// If the peer is owned by the same user, just allow it
// without further checks.
@@ -700,7 +708,7 @@ func (h *peerAPIHandler) replyToDNSQueries() bool {
// handleDNSQuery implements a DoH server (RFC 8484) over the peerapi.
// It's not over HTTPS as the spec dictates, but rather HTTP-over-WireGuard.
func (h *peerAPIHandler) handleDNSQuery(w http.ResponseWriter, r *http.Request) {
if h.ps.resolver == nil {
if !buildfeatures.HasDNS || h.ps.resolver == nil {
http.Error(w, "DNS not wired up", http.StatusNotImplemented)
return
}