83769ba715
This commits removes the locks used to guard data integrity for the database and replaces them with Transactions, turns out that SQL had a way to deal with this all along. This reduces the complexity we had with multiple locks that might stack or recurse (database, nofitifer, mapper). All notifications and state updates are now triggered _after_ a database change. Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
163 lines
3.9 KiB
Go
163 lines
3.9 KiB
Go
package mapper
|
|
|
|
import (
|
|
"fmt"
|
|
"net/netip"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/juanfont/headscale/hscontrol/policy"
|
|
"github.com/juanfont/headscale/hscontrol/types"
|
|
"github.com/juanfont/headscale/hscontrol/util"
|
|
"github.com/samber/lo"
|
|
"tailscale.com/tailcfg"
|
|
)
|
|
|
|
func tailNodes(
|
|
nodes types.Nodes,
|
|
capVer tailcfg.CapabilityVersion,
|
|
pol *policy.ACLPolicy,
|
|
dnsConfig *tailcfg.DNSConfig,
|
|
baseDomain string,
|
|
randomClientPort bool,
|
|
) ([]*tailcfg.Node, error) {
|
|
tNodes := make([]*tailcfg.Node, len(nodes))
|
|
|
|
for index, node := range nodes {
|
|
node, err := tailNode(
|
|
node,
|
|
capVer,
|
|
pol,
|
|
dnsConfig,
|
|
baseDomain,
|
|
randomClientPort,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
tNodes[index] = node
|
|
}
|
|
|
|
return tNodes, nil
|
|
}
|
|
|
|
// tailNode converts a Node into a Tailscale Node. includeRoutes is false for shared nodes
|
|
// as per the expected behaviour in the official SaaS.
|
|
func tailNode(
|
|
node *types.Node,
|
|
capVer tailcfg.CapabilityVersion,
|
|
pol *policy.ACLPolicy,
|
|
dnsConfig *tailcfg.DNSConfig,
|
|
baseDomain string,
|
|
randomClientPort bool,
|
|
) (*tailcfg.Node, error) {
|
|
addrs := node.IPAddresses.Prefixes()
|
|
|
|
allowedIPs := append(
|
|
[]netip.Prefix{},
|
|
addrs...) // we append the node own IP, as it is required by the clients
|
|
|
|
primaryPrefixes := []netip.Prefix{}
|
|
|
|
for _, route := range node.Routes {
|
|
if route.Enabled {
|
|
if route.IsPrimary {
|
|
allowedIPs = append(allowedIPs, netip.Prefix(route.Prefix))
|
|
primaryPrefixes = append(primaryPrefixes, netip.Prefix(route.Prefix))
|
|
} else if route.IsExitRoute() {
|
|
allowedIPs = append(allowedIPs, netip.Prefix(route.Prefix))
|
|
}
|
|
}
|
|
}
|
|
|
|
var derp string
|
|
if node.Hostinfo != nil && node.Hostinfo.NetInfo != nil {
|
|
derp = fmt.Sprintf("127.3.3.40:%d", node.Hostinfo.NetInfo.PreferredDERP)
|
|
} else {
|
|
derp = "127.3.3.40:0" // Zero means disconnected or unknown.
|
|
}
|
|
|
|
var keyExpiry time.Time
|
|
if node.Expiry != nil {
|
|
keyExpiry = *node.Expiry
|
|
} else {
|
|
keyExpiry = time.Time{}
|
|
}
|
|
|
|
hostname, err := node.GetFQDN(dnsConfig, baseDomain)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("tailNode, failed to create FQDN: %s", err)
|
|
}
|
|
|
|
tags, _ := pol.TagsOfNode(node)
|
|
tags = lo.Uniq(append(tags, node.ForcedTags...))
|
|
|
|
tNode := tailcfg.Node{
|
|
ID: tailcfg.NodeID(node.ID), // this is the actual ID
|
|
StableID: tailcfg.StableNodeID(
|
|
strconv.FormatUint(node.ID, util.Base10),
|
|
), // in headscale, unlike tailcontrol server, IDs are permanent
|
|
Name: hostname,
|
|
Cap: capVer,
|
|
|
|
User: tailcfg.UserID(node.UserID),
|
|
|
|
Key: node.NodeKey,
|
|
KeyExpiry: keyExpiry,
|
|
|
|
Machine: node.MachineKey,
|
|
DiscoKey: node.DiscoKey,
|
|
Addresses: addrs,
|
|
AllowedIPs: allowedIPs,
|
|
Endpoints: node.Endpoints,
|
|
DERP: derp,
|
|
Hostinfo: node.Hostinfo.View(),
|
|
Created: node.CreatedAt,
|
|
|
|
Online: node.IsOnline,
|
|
|
|
Tags: tags,
|
|
|
|
PrimaryRoutes: primaryPrefixes,
|
|
|
|
MachineAuthorized: !node.IsExpired(),
|
|
Expired: node.IsExpired(),
|
|
}
|
|
|
|
// - 74: 2023-09-18: Client understands NodeCapMap
|
|
if capVer >= 74 {
|
|
tNode.CapMap = tailcfg.NodeCapMap{
|
|
tailcfg.CapabilityFileSharing: []tailcfg.RawMessage{},
|
|
tailcfg.CapabilityAdmin: []tailcfg.RawMessage{},
|
|
tailcfg.CapabilitySSH: []tailcfg.RawMessage{},
|
|
}
|
|
|
|
if randomClientPort {
|
|
tNode.CapMap[tailcfg.NodeAttrRandomizeClientPort] = []tailcfg.RawMessage{}
|
|
}
|
|
} else {
|
|
tNode.Capabilities = []tailcfg.NodeCapability{
|
|
tailcfg.CapabilityFileSharing,
|
|
tailcfg.CapabilityAdmin,
|
|
tailcfg.CapabilitySSH,
|
|
}
|
|
|
|
if randomClientPort {
|
|
tNode.Capabilities = append(tNode.Capabilities, tailcfg.NodeAttrRandomizeClientPort)
|
|
}
|
|
}
|
|
|
|
// - 72: 2023-08-23: TS-2023-006 UPnP issue fixed; UPnP can now be used again
|
|
if capVer < 72 {
|
|
tNode.Capabilities = append(tNode.Capabilities, tailcfg.NodeAttrDisableUPnP)
|
|
}
|
|
|
|
if node.IsOnline == nil || !*node.IsOnline {
|
|
// LastSeen is only set when node is
|
|
// not connected to the control server.
|
|
tNode.LastSeen = node.LastSeen
|
|
}
|
|
|
|
return &tNode, nil
|
|
}
|