mirror of
				https://github.com/juanfont/headscale.git
				synced 2025-10-31 16:11:03 +01:00 
			
		
		
		
	* rename database only fields Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * use correct endpoint type over string list Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * remove HostInfo wrapper Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> * wrap errors in database hooks Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com> --------- Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
		
			
				
	
	
		
			158 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			158 lines
		
	
	
		
			3.6 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.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, err
 | |
| 	}
 | |
| 
 | |
| 	online := node.IsOnline()
 | |
| 
 | |
| 	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,
 | |
| 
 | |
| 		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,
 | |
| 
 | |
| 		Tags: tags,
 | |
| 
 | |
| 		PrimaryRoutes: primaryPrefixes,
 | |
| 
 | |
| 		LastSeen:          node.LastSeen,
 | |
| 		Online:            &online,
 | |
| 		MachineAuthorized: !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)
 | |
| 	}
 | |
| 
 | |
| 	return &tNode, nil
 | |
| }
 |