diff options
| author | Adriano Sela Aviles <adriano.selaviles@gmail.com> | 2026-04-13 08:48:02 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-04-13 08:48:02 -0700 |
| commit | 4fcce6000d3d3f79d1ac1fca571a50efb059cbf2 (patch) | |
| tree | 25a8a8a2df5dd5aae4678f119621167a87c1de02 | |
| parent | 674f866eccf727b59d24cdb09a990dc403892e4c (diff) | |
| download | tailscale-4fcce6000d3d3f79d1ac1fca571a50efb059cbf2.tar.xz tailscale-4fcce6000d3d3f79d1ac1fca571a50efb059cbf2.zip | |
tailcfg,types/netmap: add (visible) Services to SelfNode Caps (#19335)
Updates #40052
Signed-off-by: Adriano Sela Aviles <adriano@tailscale.com>
| -rw-r--r-- | tailcfg/tailcfg.go | 33 | ||||
| -rw-r--r-- | types/netmap/netmap.go | 21 |
2 files changed, 54 insertions, 0 deletions
diff --git a/tailcfg/tailcfg.go b/tailcfg/tailcfg.go index 3d7921d75..bc3693dab 100644 --- a/tailcfg/tailcfg.go +++ b/tailcfg/tailcfg.go @@ -2447,6 +2447,18 @@ type Oauth2Token struct { // These are also referred to as "Node Attributes" in the ACL policy file. type NodeCapability string +// NodeCapabilityPrefix is a prefix for [NodeCapMap] keys that share a common +// namespace, where each entry represents a distinct named instance (e.g. one +// per service). The full key is formed by concatenating the prefix with the +// instance name. +type NodeCapabilityPrefix string + +// ToAttribute returns the full [NodeCapability] key for the given value under +// this prefix, of the form prefix+value. +func (p NodeCapabilityPrefix) ToAttribute(value string) NodeCapability { + return NodeCapability(string(p) + value) +} + const ( CapabilityFileSharing NodeCapability = "https://tailscale.com/cap/file-sharing" CapabilityAdmin NodeCapability = "https://tailscale.com/cap/is-admin" @@ -2780,6 +2792,14 @@ const ( NodeAttrCacheNetworkMaps NodeCapability = "cache-network-maps" ) +const ( + // NodeAttrPrefixServices is the prefix for per-service [NodeCapMap] + // entries describing Services visible (accessible) to this node. The full + // key for a service named "svc:foo" is NodeAttrPrefixServices+"foo". + // Each value under such a key is of type [ServiceDetails]. + NodeAttrPrefixServices NodeCapabilityPrefix = "services/" +) + // SetDNSRequest is a request to add a DNS record. // // This is used to let tailscaled clients complete their ACME DNS-01 challenges @@ -3318,6 +3338,19 @@ const LBHeader = "Ts-Lb" // this client is hosting can be ignored. type ServiceIPMappings map[ServiceName][]netip.Addr +// ServiceDetails describes a Service visible to this node. +// It is the value type stored under [NodeAttrPrefixServices]+serviceName keys in [NodeCapMap]. +type ServiceDetails struct { + // Name is the name of the Service, of the form "svc:dns-label". + Name ServiceName + + // Addrs are the IP addresses (IPv4 and IPv6) assigned to this Service. + Addrs []netip.Addr `json:",omitempty"` + + // Ports are the protocol/port combinations the Service accepts. + Ports []ProtoPortRange `json:",omitempty"` +} + // ClientAuditAction represents an auditable action that a client can report to the // control plane. These actions must correspond to the supported actions // in the control plane. diff --git a/types/netmap/netmap.go b/types/netmap/netmap.go index ac95254da..90ed9b3fc 100644 --- a/types/netmap/netmap.go +++ b/types/netmap/netmap.go @@ -146,6 +146,27 @@ func (nm *NetworkMap) GetIPVIPServiceMap() IPServiceMappings { return res } +// Services returns the Services visible (accessible) to this node, +// decoded from [tailcfg.NodeAttrPrefixServices]+serviceName entries in the +// self node's CapMap. Returns nil if nm is nil or SelfNode is invalid. +func (nm *NetworkMap) Services() map[tailcfg.ServiceName]tailcfg.ServiceDetails { + if nm == nil || !nm.SelfNode.Valid() { + return nil + } + result := make(map[tailcfg.ServiceName]tailcfg.ServiceDetails) + for cap := range nm.SelfNode.CapMap().All() { + if !strings.HasPrefix(string(cap), string(tailcfg.NodeAttrPrefixServices)) { + continue + } + svcs, err := tailcfg.UnmarshalNodeCapViewJSON[tailcfg.ServiceDetails](nm.SelfNode.CapMap(), cap) + if err != nil || len(svcs) < 1 { + continue + } + result[svcs[0].Name] = svcs[0] + } + return result +} + // SelfNodeOrZero returns the self node, or a zero value if nm is nil. func (nm *NetworkMap) SelfNodeOrZero() tailcfg.NodeView { if nm == nil { |
