tsweb: add TS_DEBUG_TRUSTED_CIDRS envknob to debug (#19283)

Add a new envknob that allows connections from trusted CIDR ranges
to access debug endpoints without Tailscale authentication. This is
useful for in-cluster scrapers like Prometheus that are not on a
tailnet, do not have static IP addresses and cannot use debug keys.

Fixes #19282

Signed-off-by: Jason O'Donnell <2160810+jasonodonnell@users.noreply.github.com>
This commit is contained in:
Jason O'Donnell
2026-04-08 18:47:52 -04:00
committed by GitHub
parent 647deed2d9
commit d948b78b23
2 changed files with 129 additions and 0 deletions
+48
View File
@@ -13,6 +13,7 @@ import (
"expvar"
"fmt"
"io"
"log"
"maps"
"net"
"net/http"
@@ -54,6 +55,50 @@ func IsProd443(addr string) bool {
return port == "443" || port == "https"
}
// debugTrustedCIDRs is the envknob for TS_DEBUG_TRUSTED_CIDRS, a
// comma-separated list of CIDR ranges (e.g. "10.0.0.0/8,172.16.0.0/12")
// whose source IPs are allowed to access debug endpoints without Tailscale
// authentication. This will supersede both IsTailscaleIP() and
// TS_ALLOW_DEBUG_IP.
var debugTrustedCIDRs = envknob.RegisterString("TS_DEBUG_TRUSTED_CIDRS")
// trustedCIDRs returns the parsed CIDR prefixes from TS_DEBUG_TRUSTED_CIDRS.
var trustedCIDRs = sync.OnceValue(func() []netip.Prefix {
return parseTrustedCIDRs(debugTrustedCIDRs())
})
// parseTrustedCIDRs parses a comma-separated list of CIDR prefixes.
// It fatals on invalid entries, consistent with other envknob parsing.
func parseTrustedCIDRs(raw string) []netip.Prefix {
if raw == "" {
return nil
}
var prefixes []netip.Prefix
for _, s := range strings.Split(raw, ",") {
s = strings.TrimSpace(s)
if s == "" {
continue
}
pfx, err := netip.ParsePrefix(s)
if err != nil {
log.Fatalf("invalid CIDR in TS_DEBUG_TRUSTED_CIDRS: %q: %v", s, err)
}
prefixes = append(prefixes, pfx)
}
return prefixes
}
// cidrsContain checks if the source IP is associated with one of the
// provided cidrs.
func cidrsContain(cidrs []netip.Prefix, ip netip.Addr) bool {
for _, pfx := range cidrs {
if pfx.Contains(ip) {
return true
}
}
return false
}
// AllowDebugAccess reports whether r should be permitted to access
// various debug endpoints.
func AllowDebugAccess(r *http.Request) bool {
@@ -75,6 +120,9 @@ func AllowDebugAccess(r *http.Request) bool {
if tsaddr.IsTailscaleIP(ip) || ip.IsLoopback() || ipStr == envknob.String("TS_ALLOW_DEBUG_IP") {
return true
}
if cidrsContain(trustedCIDRs(), ip) {
return true
}
return false
}