ipn/localapi: add localapi debug endpoints for packet filter/matches

For debugging #6423. This is easier than TS_DEBUG_MAP, as this means I
can pipe things into jq, etc.

Updates #6423

Change-Id: Ib3e7496b2eb3f47d4bed42e9b8045a441424b23c
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick 2023-01-03 15:39:32 -08:00 committed by Brad Fitzpatrick
parent b2b8e62476
commit eafbf8886d
3 changed files with 93 additions and 51 deletions

View File

@ -16,6 +16,7 @@ import (
"tailscale.com/types/logger" "tailscale.com/types/logger"
"tailscale.com/types/netmap" "tailscale.com/types/netmap"
"tailscale.com/types/opt" "tailscale.com/types/opt"
"tailscale.com/types/views"
"tailscale.com/wgengine/filter" "tailscale.com/wgengine/filter"
) )
@ -40,6 +41,7 @@ type mapSession struct {
lastDNSConfig *tailcfg.DNSConfig lastDNSConfig *tailcfg.DNSConfig
lastDERPMap *tailcfg.DERPMap lastDERPMap *tailcfg.DERPMap
lastUserProfile map[tailcfg.UserID]tailcfg.UserProfile lastUserProfile map[tailcfg.UserID]tailcfg.UserProfile
lastPacketFilterRules views.Slice[tailcfg.FilterRule]
lastParsedPacketFilter []filter.Match lastParsedPacketFilter []filter.Match
lastSSHPolicy *tailcfg.SSHPolicy lastSSHPolicy *tailcfg.SSHPolicy
collectServices bool collectServices bool
@ -96,6 +98,7 @@ func (ms *mapSession) netmapForResponse(resp *tailcfg.MapResponse) *netmap.Netwo
if pf := resp.PacketFilter; pf != nil { if pf := resp.PacketFilter; pf != nil {
var err error var err error
ms.lastPacketFilterRules = views.SliceOf(pf)
ms.lastParsedPacketFilter, err = filter.MatchesFromFilterRules(pf) ms.lastParsedPacketFilter, err = filter.MatchesFromFilterRules(pf)
if err != nil { if err != nil {
ms.logf("parsePacketFilter: %v", err) ms.logf("parsePacketFilter: %v", err)
@ -156,6 +159,7 @@ func (ms *mapSession) netmapForResponse(resp *tailcfg.MapResponse) *netmap.Netwo
DomainAuditLogID: ms.lastDomainAuditLogID, DomainAuditLogID: ms.lastDomainAuditLogID,
DNS: *ms.lastDNSConfig, DNS: *ms.lastDNSConfig,
PacketFilter: ms.lastParsedPacketFilter, PacketFilter: ms.lastParsedPacketFilter,
PacketFilterRules: ms.lastPacketFilterRules,
SSHPolicy: ms.lastSSHPolicy, SSHPolicy: ms.lastSSHPolicy,
CollectServices: ms.collectServices, CollectServices: ms.collectServices,
DERPMap: ms.lastDERPMap, DERPMap: ms.lastDERPMap,

View File

@ -67,6 +67,8 @@ var handler = map[string]localAPIHandler{
"component-debug-logging": (*Handler).serveComponentDebugLogging, "component-debug-logging": (*Handler).serveComponentDebugLogging,
"debug": (*Handler).serveDebug, "debug": (*Handler).serveDebug,
"debug-derp-region": (*Handler).serveDebugDERPRegion, "debug-derp-region": (*Handler).serveDebugDERPRegion,
"debug-packet-filter-matches": (*Handler).serveDebugPacketFilterMatches,
"debug-packet-filter-rules": (*Handler).serveDebugPacketFilterRules,
"derpmap": (*Handler).serveDERPMap, "derpmap": (*Handler).serveDERPMap,
"dev-set-state-store": (*Handler).serveDevSetStateStore, "dev-set-state-store": (*Handler).serveDevSetStateStore,
"dial": (*Handler).serveDial, "dial": (*Handler).serveDial,
@ -506,6 +508,40 @@ func (h *Handler) serveDevSetStateStore(w http.ResponseWriter, r *http.Request)
io.WriteString(w, "done\n") io.WriteString(w, "done\n")
} }
func (h *Handler) serveDebugPacketFilterRules(w http.ResponseWriter, r *http.Request) {
if !h.PermitWrite {
http.Error(w, "debug access denied", http.StatusForbidden)
return
}
nm := h.b.NetMap()
if nm == nil {
http.Error(w, "no netmap", http.StatusNotFound)
return
}
w.Header().Set("Content-Type", "application/json")
enc := json.NewEncoder(w)
enc.SetIndent("", "\t")
enc.Encode(nm.PacketFilterRules)
}
func (h *Handler) serveDebugPacketFilterMatches(w http.ResponseWriter, r *http.Request) {
if !h.PermitWrite {
http.Error(w, "debug access denied", http.StatusForbidden)
return
}
nm := h.b.NetMap()
if nm == nil {
http.Error(w, "no netmap", http.StatusNotFound)
return
}
w.Header().Set("Content-Type", "application/json")
enc := json.NewEncoder(w)
enc.SetIndent("", "\t")
enc.Encode(nm.PacketFilter)
}
func (h *Handler) serveComponentDebugLogging(w http.ResponseWriter, r *http.Request) { func (h *Handler) serveComponentDebugLogging(w http.ResponseWriter, r *http.Request) {
if !h.PermitWrite { if !h.PermitWrite {
http.Error(w, "debug access denied", http.StatusForbidden) http.Error(w, "debug access denied", http.StatusForbidden)

View File

@ -16,6 +16,7 @@ import (
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/tka" "tailscale.com/tka"
"tailscale.com/types/key" "tailscale.com/types/key"
"tailscale.com/types/views"
"tailscale.com/wgengine/filter" "tailscale.com/wgengine/filter"
) )
@ -40,6 +41,7 @@ type NetworkMap struct {
// TODO(maisem) : replace with View. // TODO(maisem) : replace with View.
Hostinfo tailcfg.Hostinfo Hostinfo tailcfg.Hostinfo
PacketFilter []filter.Match PacketFilter []filter.Match
PacketFilterRules views.Slice[tailcfg.FilterRule]
SSHPolicy *tailcfg.SSHPolicy // or nil, if not enabled/allowed SSHPolicy *tailcfg.SSHPolicy // or nil, if not enabled/allowed
// CollectServices reports whether this node's Tailnet has // CollectServices reports whether this node's Tailnet has