ipn/{ipnlocal,localapi},client/local: add per-dst cap resolution for services

Adds two new cap resolution methods alongside the existing PeerCaps:

PeerCapsForService(src netip.Addr, svcName tailcfg.ServiceName) resolves
the service name to its VIP addresses via the node's service IP mappings
and returns caps scoped to that service. Exposed on /v0/whois via the
svc_name query parameter and on client/local.Client as WhoIsForService.

PeerCapsForIP(src, dst netip.Addr) resolves caps against an arbitrary
destination IP. Exposed on /v0/whois via the svc_addr query parameter
and on client/local.Client as WhoIsForIP.

svc_name takes priority over svc_addr when both are present. Invalid
values for either return 400. The existing PeerCaps/WhoIs path is
unchanged: without a service parameter, WhoIs returns only host-level
caps.

Updates tailscale/corp#41632

Signed-off-by: Adriano Sela Aviles <adriano@tailscale.com>
This commit is contained in:
Adriano Sela Aviles
2026-05-11 14:48:25 -07:00
committed by Adriano Sela Aviles
parent ad8ead9c94
commit 72578de033
5 changed files with 298 additions and 4 deletions
+12
View File
@@ -1621,6 +1621,18 @@ func (b *LocalBackend) PeerCaps(src netip.Addr) tailcfg.PeerCapMap {
return b.currentNode().PeerCaps(src)
}
// PeerCapsForIP returns the capabilities that remote src IP has when
// talking to the given destination IP on this node.
func (b *LocalBackend) PeerCapsForIP(src, dst netip.Addr) tailcfg.PeerCapMap {
return b.currentNode().PeerCapsForIP(src, dst)
}
// PeerCapsForService returns the capabilities that remote src IP has when
// talking to the named VIP service on this node.
func (b *LocalBackend) PeerCapsForService(src netip.Addr, svcName tailcfg.ServiceName) tailcfg.PeerCapMap {
return b.currentNode().PeerCapsForService(src, svcName)
}
// PeerByID returns the current full [tailcfg.Node] for the peer with the
// given NodeID, in O(1) time. It returns ok=false if no such peer is in
// the current netmap.