mirror of
https://github.com/juanfont/headscale.git
synced 2026-01-23 02:24:10 +00:00
Some checks failed
Build / build-nix (push) Has been cancelled
Build / build-cross (GOARCH=amd64 GOOS=darwin) (push) Has been cancelled
Build / build-cross (GOARCH=amd64 GOOS=linux) (push) Has been cancelled
Build / build-cross (GOARCH=arm64 GOOS=darwin) (push) Has been cancelled
Build / build-cross (GOARCH=arm64 GOOS=linux) (push) Has been cancelled
Check Generated Files / check-generated (push) Has been cancelled
NixOS Module Tests / nix-module-check (push) Has been cancelled
Tests / test (push) Has been cancelled
This PR changes tags to be something that exists on nodes in addition to users, to being its own thing. It is part of moving our tags support towards the correct tailscale compatible implementation. There are probably rough edges in this PR, but the intention is to get it in, and then start fixing bugs from 0.28.0 milestone (long standing tags issue) to discover what works and what doesnt. Updates #2417 Closes #2619
146 lines
3.6 KiB
Go
146 lines
3.6 KiB
Go
package mapper
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/juanfont/headscale/hscontrol/types"
|
|
"github.com/samber/lo"
|
|
"tailscale.com/net/tsaddr"
|
|
"tailscale.com/tailcfg"
|
|
"tailscale.com/types/views"
|
|
)
|
|
|
|
// NodeCanHaveTagChecker is an interface for checking if a node can have a tag.
|
|
type NodeCanHaveTagChecker interface {
|
|
NodeCanHaveTag(node types.NodeView, tag string) bool
|
|
}
|
|
|
|
func tailNodes(
|
|
nodes views.Slice[types.NodeView],
|
|
capVer tailcfg.CapabilityVersion,
|
|
checker NodeCanHaveTagChecker,
|
|
primaryRouteFunc routeFilterFunc,
|
|
cfg *types.Config,
|
|
) ([]*tailcfg.Node, error) {
|
|
tNodes := make([]*tailcfg.Node, 0, nodes.Len())
|
|
|
|
for _, node := range nodes.All() {
|
|
tNode, err := tailNode(
|
|
node,
|
|
capVer,
|
|
checker,
|
|
primaryRouteFunc,
|
|
cfg,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
tNodes = append(tNodes, tNode)
|
|
}
|
|
|
|
return tNodes, nil
|
|
}
|
|
|
|
// tailNode converts a Node into a Tailscale Node.
|
|
func tailNode(
|
|
node types.NodeView,
|
|
capVer tailcfg.CapabilityVersion,
|
|
checker NodeCanHaveTagChecker,
|
|
primaryRouteFunc routeFilterFunc,
|
|
cfg *types.Config,
|
|
) (*tailcfg.Node, error) {
|
|
addrs := node.Prefixes()
|
|
|
|
var derp int
|
|
|
|
// TODO(kradalby): legacyDERP was removed in tailscale/tailscale@2fc4455e6dd9ab7f879d4e2f7cffc2be81f14077
|
|
// and should be removed after 111 is the minimum capver.
|
|
var legacyDERP string
|
|
if node.Hostinfo().Valid() && node.Hostinfo().NetInfo().Valid() {
|
|
legacyDERP = fmt.Sprintf("127.3.3.40:%d", node.Hostinfo().NetInfo().PreferredDERP())
|
|
derp = node.Hostinfo().NetInfo().PreferredDERP()
|
|
} else {
|
|
legacyDERP = "127.3.3.40:0" // Zero means disconnected or unknown.
|
|
}
|
|
|
|
var keyExpiry time.Time
|
|
if node.Expiry().Valid() {
|
|
keyExpiry = node.Expiry().Get()
|
|
} else {
|
|
keyExpiry = time.Time{}
|
|
}
|
|
|
|
hostname, err := node.GetFQDN(cfg.BaseDomain)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var tags []string
|
|
for _, tag := range node.RequestTagsSlice().All() {
|
|
if checker.NodeCanHaveTag(node, tag) {
|
|
tags = append(tags, tag)
|
|
}
|
|
}
|
|
|
|
for _, tag := range node.Tags().All() {
|
|
tags = append(tags, tag)
|
|
}
|
|
tags = lo.Uniq(tags)
|
|
|
|
routes := primaryRouteFunc(node.ID())
|
|
allowed := append(addrs, routes...)
|
|
allowed = append(allowed, node.ExitRoutes()...)
|
|
tsaddr.SortPrefixes(allowed)
|
|
|
|
tNode := tailcfg.Node{
|
|
ID: tailcfg.NodeID(node.ID()), // this is the actual ID
|
|
StableID: node.ID().StableID(),
|
|
Name: hostname,
|
|
Cap: capVer,
|
|
|
|
User: node.TailscaleUserID(),
|
|
|
|
Key: node.NodeKey(),
|
|
KeyExpiry: keyExpiry.UTC(),
|
|
|
|
Machine: node.MachineKey(),
|
|
DiscoKey: node.DiscoKey(),
|
|
Addresses: addrs,
|
|
PrimaryRoutes: routes,
|
|
AllowedIPs: allowed,
|
|
Endpoints: node.Endpoints().AsSlice(),
|
|
HomeDERP: derp,
|
|
LegacyDERPString: legacyDERP,
|
|
Hostinfo: node.Hostinfo(),
|
|
Created: node.CreatedAt().UTC(),
|
|
|
|
Online: node.IsOnline().Clone(),
|
|
|
|
Tags: tags,
|
|
|
|
MachineAuthorized: !node.IsExpired(),
|
|
Expired: node.IsExpired(),
|
|
}
|
|
|
|
tNode.CapMap = tailcfg.NodeCapMap{
|
|
tailcfg.CapabilityFileSharing: []tailcfg.RawMessage{},
|
|
tailcfg.CapabilityAdmin: []tailcfg.RawMessage{},
|
|
tailcfg.CapabilitySSH: []tailcfg.RawMessage{},
|
|
}
|
|
|
|
if cfg.RandomizeClientPort {
|
|
tNode.CapMap[tailcfg.NodeAttrRandomizeClientPort] = []tailcfg.RawMessage{}
|
|
}
|
|
|
|
// Set LastSeen only for offline nodes to avoid confusing Tailscale clients
|
|
// during rapid reconnection cycles. Online nodes should not have LastSeen set
|
|
// as this can make clients interpret them as "not online" despite Online=true.
|
|
if node.LastSeen().Valid() && node.IsOnline().Valid() && !node.IsOnline().Get() {
|
|
lastSeen := node.LastSeen().Get()
|
|
tNode.LastSeen = &lastSeen
|
|
}
|
|
|
|
return &tNode, nil
|
|
}
|