5bad48a24e
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
151 lines
3.3 KiB
Go
151 lines
3.3 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(
|
|
machines types.Machines,
|
|
pol *policy.ACLPolicy,
|
|
dnsConfig *tailcfg.DNSConfig,
|
|
baseDomain string,
|
|
stripEmailDomain bool,
|
|
) ([]*tailcfg.Node, error) {
|
|
nodes := make([]*tailcfg.Node, len(machines))
|
|
|
|
for index, machine := range machines {
|
|
node, err := tailNode(
|
|
machine,
|
|
pol,
|
|
dnsConfig,
|
|
baseDomain,
|
|
stripEmailDomain,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
nodes[index] = node
|
|
}
|
|
|
|
return nodes, nil
|
|
}
|
|
|
|
// tailNode converts a Machine into a Tailscale Node. includeRoutes is false for shared nodes
|
|
// as per the expected behaviour in the official SaaS.
|
|
func tailNode(
|
|
machine types.Machine,
|
|
pol *policy.ACLPolicy,
|
|
dnsConfig *tailcfg.DNSConfig,
|
|
baseDomain string,
|
|
stripEmailDomain bool,
|
|
) (*tailcfg.Node, error) {
|
|
nodeKey, err := machine.NodePublicKey()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// MachineKey is only used in the legacy protocol
|
|
machineKey, err := machine.MachinePublicKey()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
discoKey, err := machine.DiscoPublicKey()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
addrs := machine.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 machine.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 machine.HostInfo.NetInfo != nil {
|
|
derp = fmt.Sprintf("127.3.3.40:%d", machine.HostInfo.NetInfo.PreferredDERP)
|
|
} else {
|
|
derp = "127.3.3.40:0" // Zero means disconnected or unknown.
|
|
}
|
|
|
|
var keyExpiry time.Time
|
|
if machine.Expiry != nil {
|
|
keyExpiry = *machine.Expiry
|
|
} else {
|
|
keyExpiry = time.Time{}
|
|
}
|
|
|
|
hostname, err := machine.GetFQDN(dnsConfig, baseDomain)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
hostInfo := machine.GetHostInfo()
|
|
|
|
online := machine.IsOnline()
|
|
|
|
tags, _ := pol.GetTagsOfMachine(machine, stripEmailDomain)
|
|
tags = lo.Uniq(append(tags, machine.ForcedTags...))
|
|
|
|
node := tailcfg.Node{
|
|
ID: tailcfg.NodeID(machine.ID), // this is the actual ID
|
|
StableID: tailcfg.StableNodeID(
|
|
strconv.FormatUint(machine.ID, util.Base10),
|
|
), // in headscale, unlike tailcontrol server, IDs are permanent
|
|
Name: hostname,
|
|
|
|
User: tailcfg.UserID(machine.UserID),
|
|
|
|
Key: nodeKey,
|
|
KeyExpiry: keyExpiry,
|
|
|
|
Machine: machineKey,
|
|
DiscoKey: discoKey,
|
|
Addresses: addrs,
|
|
AllowedIPs: allowedIPs,
|
|
Endpoints: machine.Endpoints,
|
|
DERP: derp,
|
|
Hostinfo: hostInfo.View(),
|
|
Created: machine.CreatedAt,
|
|
|
|
Tags: tags,
|
|
|
|
PrimaryRoutes: primaryPrefixes,
|
|
|
|
LastSeen: machine.LastSeen,
|
|
Online: &online,
|
|
KeepAlive: true,
|
|
MachineAuthorized: !machine.IsExpired(),
|
|
|
|
Capabilities: []string{
|
|
tailcfg.CapabilityFileSharing,
|
|
tailcfg.CapabilityAdmin,
|
|
tailcfg.CapabilitySSH,
|
|
},
|
|
}
|
|
|
|
return &node, nil
|
|
}
|