tailcfg: add CapabilityDebug

Updates tailscale/corp#7948

Signed-off-by: Maisem Ali <maisem@tailscale.com>
pull/6357/head
Maisem Ali 2 years ago committed by Maisem Ali
parent 1e78fc462c
commit 296e712591

@ -778,6 +778,17 @@ func (h *peerAPIHandler) canPutFile() bool {
// canDebug reports whether h can debug this node (goroutines, metrics, // canDebug reports whether h can debug this node (goroutines, metrics,
// magicsock internal state, etc). // magicsock internal state, etc).
func (h *peerAPIHandler) canDebug() bool { func (h *peerAPIHandler) canDebug() bool {
// Reread the selfNode as it may have changed since the peerAPIServer
// was created.
// TODO(maisem): handle this in other places too.
nm := h.ps.b.NetMap()
if nm == nil || nm.SelfNode == nil {
return false
}
if !slices.Contains(nm.SelfNode.Capabilities, tailcfg.CapabilityDebug) {
// This node does not expose debug info.
return false
}
return h.isSelf || h.peerHasCap(tailcfg.CapabilityDebugPeer) return h.isSelf || h.peerHasCap(tailcfg.CapabilityDebugPeer)
} }

@ -25,6 +25,7 @@ import (
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/tstest" "tailscale.com/tstest"
"tailscale.com/types/logger" "tailscale.com/types/logger"
"tailscale.com/types/netmap"
"tailscale.com/util/must" "tailscale.com/util/must"
"tailscale.com/wgengine" "tailscale.com/wgengine"
"tailscale.com/wgengine/filter" "tailscale.com/wgengine/filter"
@ -113,6 +114,7 @@ func TestHandlePeerAPI(t *testing.T) {
name string name string
isSelf bool // the peer sending the request is owned by us isSelf bool // the peer sending the request is owned by us
capSharing bool // self node has file sharing capability capSharing bool // self node has file sharing capability
debugCap bool // self node has debug capability
omitRoot bool // don't configure omitRoot bool // don't configure
req *http.Request req *http.Request
checks []check checks []check
@ -140,15 +142,24 @@ func TestHandlePeerAPI(t *testing.T) {
), ),
}, },
{ {
name: "peer_api_goroutines_deny", name: "goroutines/deny-self-no-cap",
isSelf: false, isSelf: true,
req: httptest.NewRequest("GET", "/v0/goroutines", nil), debugCap: false,
checks: checks(httpStatus(403)), req: httptest.NewRequest("GET", "/v0/goroutines", nil),
checks: checks(httpStatus(403)),
}, },
{ {
name: "peer_api_goroutines", name: "goroutines/deny-nonself",
isSelf: true, isSelf: false,
req: httptest.NewRequest("GET", "/v0/goroutines", nil), debugCap: true,
req: httptest.NewRequest("GET", "/v0/goroutines", nil),
checks: checks(httpStatus(403)),
},
{
name: "goroutines/accept-self",
isSelf: true,
debugCap: true,
req: httptest.NewRequest("GET", "/v0/goroutines", nil),
checks: checks( checks: checks(
httpStatus(200), httpStatus(200),
bodyContains("ServeHTTP"), bodyContains("ServeHTTP"),
@ -404,25 +415,28 @@ func TestHandlePeerAPI(t *testing.T) {
), ),
}, },
{ {
name: "host-val/bad-ip", name: "host-val/bad-ip",
isSelf: true, isSelf: true,
req: httptest.NewRequest("GET", "http://12.23.45.66:1234/v0/env", nil), debugCap: true,
req: httptest.NewRequest("GET", "http://12.23.45.66:1234/v0/env", nil),
checks: checks( checks: checks(
httpStatus(403), httpStatus(403),
), ),
}, },
{ {
name: "host-val/no-port", name: "host-val/no-port",
isSelf: true, isSelf: true,
req: httptest.NewRequest("GET", "http://100.100.100.101/v0/env", nil), debugCap: true,
req: httptest.NewRequest("GET", "http://100.100.100.101/v0/env", nil),
checks: checks( checks: checks(
httpStatus(403), httpStatus(403),
), ),
}, },
{ {
name: "host-val/peer", name: "host-val/peer",
isSelf: true, isSelf: true,
req: httptest.NewRequest("GET", "http://peer/v0/env", nil), debugCap: true,
req: httptest.NewRequest("GET", "http://peer/v0/env", nil),
checks: checks( checks: checks(
httpStatus(200), httpStatus(200),
), ),
@ -430,10 +444,16 @@ func TestHandlePeerAPI(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
selfNode := &tailcfg.Node{
Addresses: []netip.Prefix{
netip.MustParsePrefix("100.100.100.101/32"),
},
}
var e peerAPITestEnv var e peerAPITestEnv
lb := &LocalBackend{ lb := &LocalBackend{
logf: e.logBuf.Logf, logf: e.logBuf.Logf,
capFileSharing: tt.capSharing, capFileSharing: tt.capSharing,
netMap: &netmap.NetworkMap{SelfNode: selfNode},
} }
e.ph = &peerAPIHandler{ e.ph = &peerAPIHandler{
isSelf: tt.isSelf, isSelf: tt.isSelf,
@ -441,14 +461,13 @@ func TestHandlePeerAPI(t *testing.T) {
ComputedName: "some-peer-name", ComputedName: "some-peer-name",
}, },
ps: &peerAPIServer{ ps: &peerAPIServer{
b: lb, b: lb,
selfNode: &tailcfg.Node{ selfNode: selfNode,
Addresses: []netip.Prefix{
netip.MustParsePrefix("100.100.100.101/32"),
},
},
}, },
} }
if tt.debugCap {
e.ph.ps.selfNode.Capabilities = append(e.ph.ps.selfNode.Capabilities, tailcfg.CapabilityDebug)
}
var rootDir string var rootDir string
if !tt.omitRoot { if !tt.omitRoot {
rootDir = t.TempDir() rootDir = t.TempDir()

@ -1654,12 +1654,16 @@ type Oauth2Token struct {
const ( const (
// These are the capabilities that the self node has as listed in // These are the capabilities that the self node has as listed in
// MapResponse.Node.Capabilities. // MapResponse.Node.Capabilities.
//
// We've since started referring to these as "Node Attributes" ("nodeAttrs"
// in the ACL policy file).
CapabilityFileSharing = "https://tailscale.com/cap/file-sharing" CapabilityFileSharing = "https://tailscale.com/cap/file-sharing"
CapabilityAdmin = "https://tailscale.com/cap/is-admin" CapabilityAdmin = "https://tailscale.com/cap/is-admin"
CapabilitySSH = "https://tailscale.com/cap/ssh" // feature enabled/available CapabilitySSH = "https://tailscale.com/cap/ssh" // feature enabled/available
CapabilitySSHRuleIn = "https://tailscale.com/cap/ssh-rule-in" // some SSH rule reach this node CapabilitySSHRuleIn = "https://tailscale.com/cap/ssh-rule-in" // some SSH rule reach this node
CapabilityDataPlaneAuditLogs = "https://tailscale.com/cap/data-plane-audit-logs" // feature enabled CapabilityDataPlaneAuditLogs = "https://tailscale.com/cap/data-plane-audit-logs" // feature enabled
CapabilityDebug = "https://tailscale.com/cap/debug" // exposes debug endpoints over the PeerAPI
// Inter-node capabilities as specified in the MapResponse.PacketFilter[].CapGrants. // Inter-node capabilities as specified in the MapResponse.PacketFilter[].CapGrants.

Loading…
Cancel
Save