2020-06-21 10:32:08 +00:00
package headscale
import (
2022-01-16 13:16:59 +00:00
"database/sql/driver"
2020-06-21 10:32:08 +00:00
"encoding/json"
2021-11-04 22:11:38 +00:00
"errors"
2020-06-21 10:32:08 +00:00
"fmt"
2021-02-21 23:53:37 +00:00
"sort"
2021-02-22 22:27:33 +00:00
"strconv"
2021-10-02 21:03:34 +00:00
"strings"
2020-06-21 10:32:08 +00:00
"time"
2021-10-06 22:06:07 +00:00
"github.com/fatih/set"
2021-11-13 08:39:04 +00:00
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
2021-08-05 17:11:26 +00:00
"github.com/rs/zerolog/log"
2021-11-04 22:11:38 +00:00
"google.golang.org/protobuf/types/known/timestamppb"
2021-05-14 22:05:41 +00:00
"gorm.io/datatypes"
2021-02-20 21:43:07 +00:00
"inet.af/netaddr"
2020-06-21 10:32:08 +00:00
"tailscale.com/tailcfg"
2021-11-26 23:30:42 +00:00
"tailscale.com/types/key"
2020-06-21 10:32:08 +00:00
)
2021-11-15 19:18:14 +00:00
const (
errMachineNotFound = Error ( "machine not found" )
errMachineAlreadyRegistered = Error ( "machine already registered" )
errMachineRouteIsNotAvailable = Error ( "route is not available on machine" )
2022-01-16 13:16:59 +00:00
errMachineAddressesInvalid = Error ( "failed to parse machine addresses" )
2021-11-15 19:18:14 +00:00
)
2021-11-13 08:39:04 +00:00
// Machine is a Headscale client.
2020-06-21 10:32:08 +00:00
type Machine struct {
2021-02-27 23:58:09 +00:00
ID uint64 ` gorm:"primary_key" `
MachineKey string ` gorm:"type:varchar(64);unique_index" `
NodeKey string
DiscoKey string
2022-01-16 13:16:59 +00:00
IPAddresses MachineAddresses
2021-02-27 23:58:09 +00:00
Name string
NamespaceID uint
2021-06-25 16:57:08 +00:00
Namespace Namespace ` gorm:"foreignKey:NamespaceID" `
2020-06-21 10:32:08 +00:00
2021-05-05 22:08:36 +00:00
Registered bool // temp
RegisterMethod string
AuthKeyID uint
AuthKey * PreAuthKey
2021-08-18 22:17:38 +00:00
LastSeen * time . Time
LastSuccessfulUpdate * time . Time
Expiry * time . Time
2020-06-21 10:32:08 +00:00
2021-05-14 22:05:41 +00:00
HostInfo datatypes . JSON
Endpoints datatypes . JSON
EnabledRoutes datatypes . JSON
2020-06-21 10:32:08 +00:00
CreatedAt time . Time
UpdatedAt time . Time
DeletedAt * time . Time
}
2021-10-02 21:03:34 +00:00
type (
Machines [ ] Machine
MachinesP [ ] * Machine
)
2021-11-13 08:39:04 +00:00
// For the time being this method is rather naive.
2021-11-22 17:21:56 +00:00
func ( machine Machine ) isRegistered ( ) bool {
2021-11-14 19:32:03 +00:00
return machine . Registered
2020-06-21 10:32:08 +00:00
}
2022-01-16 13:16:59 +00:00
type MachineAddresses [ ] netaddr . IP
func ( ma MachineAddresses ) ToStringSlice ( ) [ ] string {
strSlice := make ( [ ] string , 0 , len ( ma ) )
for _ , addr := range ma {
strSlice = append ( strSlice , addr . String ( ) )
}
return strSlice
}
func ( ma * MachineAddresses ) Scan ( destination interface { } ) error {
switch value := destination . ( type ) {
case string :
addresses := strings . Split ( value , "," )
* ma = ( * ma ) [ : 0 ]
for _ , addr := range addresses {
if len ( addr ) < 1 {
continue
}
parsed , err := netaddr . ParseIP ( addr )
if err != nil {
return err
}
* ma = append ( * ma , parsed )
}
return nil
default :
return fmt . Errorf ( "%w: unexpected data type %T" , errMachineAddressesInvalid , destination )
}
}
// Value return json value, implement driver.Valuer interface.
func ( ma MachineAddresses ) Value ( ) ( driver . Value , error ) {
addresses := strings . Join ( ma . ToStringSlice ( ) , "," )
return addresses , nil
}
2021-11-13 08:39:04 +00:00
// isExpired returns whether the machine registration has expired.
2021-11-14 19:32:03 +00:00
func ( machine Machine ) isExpired ( ) bool {
2021-11-19 09:02:29 +00:00
// If Expiry is not set, the client has not indicated that
// it wants an expiry time, it is therefor considered
// to mean "not expired"
if machine . Expiry . IsZero ( ) {
return false
2021-10-10 09:22:42 +00:00
}
2021-11-19 09:02:29 +00:00
return time . Now ( ) . UTC ( ) . After ( * machine . Expiry )
2021-10-10 09:22:42 +00:00
}
2022-02-07 15:12:05 +00:00
func ( h * Headscale ) ListAllMachines ( ) ( [ ] Machine , error ) {
machines := [ ] Machine { }
if err := h . db . Preload ( "AuthKey" ) .
Preload ( "AuthKey.Namespace" ) .
Preload ( "Namespace" ) .
Where ( "registered" ) .
Find ( & machines ) . Error ; err != nil {
return nil , err
}
return machines , nil
}
2022-02-21 08:02:27 +00:00
func containsAddresses ( inputs [ ] string , addrs [ ] string ) bool {
for _ , addr := range addrs {
2022-02-03 18:53:11 +00:00
if containsString ( inputs , addr ) {
return true
}
}
2022-02-14 14:26:54 +00:00
2022-02-03 18:53:11 +00:00
return false
}
2022-02-21 08:02:27 +00:00
// matchSourceAndDestinationWithRule
func matchSourceAndDestinationWithRule ( ruleSources [ ] string , ruleDestinations [ ] string , source [ ] string , destination [ ] string ) bool {
return containsAddresses ( ruleSources , source ) && containsAddresses ( ruleDestinations , destination )
}
2022-02-03 18:53:11 +00:00
// getFilteredByACLPeerss should return the list of peers authorized to be accessed from machine.
2022-02-21 08:15:34 +00:00
func getFilteredByACLPeers ( machines [ ] Machine , rules [ ] tailcfg . FilterRule , machine * Machine ) ( Machines , error ) {
2021-12-29 09:58:10 +00:00
log . Trace ( ) .
Caller ( ) .
Str ( "machine" , machine . Name ) .
2022-02-03 18:53:11 +00:00
Msg ( "Finding peers filtered by ACLs" )
2021-12-29 09:58:10 +00:00
2022-02-20 20:24:02 +00:00
peers := make ( map [ uint64 ] Machine )
2022-02-03 18:53:11 +00:00
// Aclfilter peers here. We are itering through machines in all namespaces and search through the computed aclRules
// for match between rule SrcIPs and DstPorts. If the rule is a match we allow the machine to be viewable.
// FIXME: On official control plane if a rule allow user A to talk to user B but NO rule allows user B to talk to
// user A. The behaviour is the following
//
// On official tailscale control plane:
// on first `tailscale status`` on node A we can see node B. The `tailscale status` command on node B doesn't show node A
// We can successfully establish a communication from A to B. When it's done, if we run the `tailscale status` command
// on node B again we can now see node A. It's not possible to establish a communication from node B to node A.
// On this implementation of the feature
// on any `tailscale status` command on node A we can see node B. The `tailscale status` command on node B DOES show A.
//
// I couldn't find a way to not clutter the output of `tailscale status` with all nodes that we could be talking to.
// In order to do this we would need to be able to identify that node A want to talk to node B but that Node B doesn't know
// how to talk to node A and then add the peering resource.
2022-02-20 20:24:02 +00:00
for _ , peer := range machines {
2022-02-21 08:05:04 +00:00
if peer . ID == machine . ID {
continue
}
2022-02-21 08:15:34 +00:00
for _ , rule := range rules {
2022-02-20 22:47:04 +00:00
var dst [ ] string
for _ , d := range rule . DstPorts {
dst = append ( dst , d . IP )
}
2022-02-21 08:02:27 +00:00
if matchSourceAndDestinationWithRule ( rule . SrcIPs , dst , machine . IPAddresses . ToStringSlice ( ) , peer . IPAddresses . ToStringSlice ( ) ) || // match source and destination
matchSourceAndDestinationWithRule ( rule . SrcIPs , dst , machine . IPAddresses . ToStringSlice ( ) , [ ] string { "*" } ) || // match source and all destination
matchSourceAndDestinationWithRule ( rule . SrcIPs , dst , peer . IPAddresses . ToStringSlice ( ) , machine . IPAddresses . ToStringSlice ( ) ) { // match return path
2022-02-20 20:24:02 +00:00
peers [ peer . ID ] = peer
2022-02-03 18:53:11 +00:00
}
}
}
2021-12-29 09:58:10 +00:00
2022-02-20 22:50:08 +00:00
authorizedPeers := make ( [ ] Machine , 0 , len ( peers ) )
2022-02-20 20:24:02 +00:00
for _ , m := range peers {
2022-02-20 22:50:08 +00:00
authorizedPeers = append ( authorizedPeers , m )
2022-02-03 18:53:11 +00:00
}
2022-02-14 14:54:51 +00:00
sort . Slice (
2022-02-20 22:50:08 +00:00
authorizedPeers ,
func ( i , j int ) bool { return authorizedPeers [ i ] . ID < authorizedPeers [ j ] . ID } ,
2022-02-14 14:54:51 +00:00
)
2021-12-29 09:58:10 +00:00
log . Trace ( ) .
Caller ( ) .
Str ( "machine" , machine . Name ) .
2022-02-20 20:24:02 +00:00
Msgf ( "Found some machines: %v" , machines )
2021-12-29 09:58:10 +00:00
2022-02-20 22:50:08 +00:00
return authorizedPeers , nil
2021-12-29 09:58:10 +00:00
}
2021-11-14 19:32:03 +00:00
func ( h * Headscale ) getDirectPeers ( machine * Machine ) ( Machines , error ) {
2021-10-02 21:03:34 +00:00
log . Trace ( ) .
2021-11-04 22:11:38 +00:00
Caller ( ) .
2021-11-14 19:32:03 +00:00
Str ( "machine" , machine . Name ) .
2021-10-04 17:39:01 +00:00
Msg ( "Finding direct peers" )
2021-02-23 19:10:58 +00:00
2021-10-04 17:39:01 +00:00
machines := Machines { }
2021-10-05 15:47:21 +00:00
if err := h . db . Preload ( "Namespace" ) . Where ( "namespace_id = ? AND machine_key <> ? AND registered" ,
2021-11-14 19:32:03 +00:00
machine . NamespaceID , machine . MachineKey ) . Find ( & machines ) . Error ; err != nil {
2021-10-02 21:03:34 +00:00
log . Error ( ) . Err ( err ) . Msg ( "Error accessing db" )
2021-11-14 15:46:09 +00:00
2021-10-06 19:32:15 +00:00
return Machines { } , err
2020-06-21 10:32:08 +00:00
}
2021-10-04 17:39:01 +00:00
sort . Slice ( machines , func ( i , j int ) bool { return machines [ i ] . ID < machines [ j ] . ID } )
2020-06-21 10:32:08 +00:00
2021-10-02 21:03:34 +00:00
log . Trace ( ) .
2021-11-04 22:11:38 +00:00
Caller ( ) .
2021-11-14 19:32:03 +00:00
Str ( "machine" , machine . Name ) .
2021-10-04 17:39:01 +00:00
Msgf ( "Found direct machines: %s" , machines . String ( ) )
2021-11-14 15:46:09 +00:00
2021-10-04 17:39:01 +00:00
return machines , nil
2020-06-21 10:32:08 +00:00
}
2021-11-13 08:39:04 +00:00
// getShared fetches machines that are shared to the `Namespace` of the machine we are getting peers for.
2021-11-14 19:32:03 +00:00
func ( h * Headscale ) getShared ( machine * Machine ) ( Machines , error ) {
2021-08-13 09:33:19 +00:00
log . Trace ( ) .
2021-11-04 22:11:38 +00:00
Caller ( ) .
2021-11-14 19:32:03 +00:00
Str ( "machine" , machine . Name ) .
2021-10-02 21:03:34 +00:00
Msg ( "Finding shared peers" )
2020-06-21 10:32:08 +00:00
2021-09-09 22:26:46 +00:00
sharedMachines := [ ] SharedMachine { }
2021-10-17 09:59:08 +00:00
if err := h . db . Preload ( "Namespace" ) . Preload ( "Machine" ) . Preload ( "Machine.Namespace" ) . Where ( "namespace_id = ?" ,
2021-11-14 19:32:03 +00:00
machine . NamespaceID ) . Find ( & sharedMachines ) . Error ; err != nil {
2021-10-06 19:32:15 +00:00
return Machines { } , err
2021-09-02 14:59:03 +00:00
}
2021-10-04 17:39:01 +00:00
peers := make ( Machines , 0 )
2021-09-09 22:26:46 +00:00
for _ , sharedMachine := range sharedMachines {
2021-10-04 17:39:01 +00:00
peers = append ( peers , sharedMachine . Machine )
2020-06-21 10:32:08 +00:00
}
2021-10-02 21:03:34 +00:00
2021-02-21 23:53:37 +00:00
sort . Slice ( peers , func ( i , j int ) bool { return peers [ i ] . ID < peers [ j ] . ID } )
2021-08-13 09:33:19 +00:00
log . Trace ( ) .
2021-11-04 22:11:38 +00:00
Caller ( ) .
2021-11-14 19:32:03 +00:00
Str ( "machine" , machine . Name ) .
2021-10-02 21:03:34 +00:00
Msgf ( "Found shared peers: %s" , peers . String ( ) )
2021-11-14 15:46:09 +00:00
2021-10-02 21:03:34 +00:00
return peers , nil
}
2021-11-13 08:39:04 +00:00
// getSharedTo fetches the machines of the namespaces this machine is shared in.
2021-11-14 19:32:03 +00:00
func ( h * Headscale ) getSharedTo ( machine * Machine ) ( Machines , error ) {
2021-10-12 15:20:14 +00:00
log . Trace ( ) .
2021-11-04 22:11:38 +00:00
Caller ( ) .
2021-11-14 19:32:03 +00:00
Str ( "machine" , machine . Name ) .
2021-10-12 15:20:14 +00:00
Msg ( "Finding peers in namespaces this machine is shared with" )
sharedMachines := [ ] SharedMachine { }
2021-10-17 09:59:08 +00:00
if err := h . db . Preload ( "Namespace" ) . Preload ( "Machine" ) . Preload ( "Machine.Namespace" ) . Where ( "machine_id = ?" ,
2021-11-14 19:32:03 +00:00
machine . ID ) . Find ( & sharedMachines ) . Error ; err != nil {
2021-10-12 15:20:14 +00:00
return Machines { } , err
}
peers := make ( Machines , 0 )
for _ , sharedMachine := range sharedMachines {
2021-11-13 08:36:45 +00:00
namespaceMachines , err := h . ListMachinesInNamespace (
sharedMachine . Namespace . Name ,
)
2021-10-12 15:20:14 +00:00
if err != nil {
return Machines { } , err
}
2021-11-04 22:11:38 +00:00
peers = append ( peers , namespaceMachines ... )
2021-10-12 15:20:14 +00:00
}
sort . Slice ( peers , func ( i , j int ) bool { return peers [ i ] . ID < peers [ j ] . ID } )
log . Trace ( ) .
2021-11-04 22:11:38 +00:00
Caller ( ) .
2021-11-14 19:32:03 +00:00
Str ( "machine" , machine . Name ) .
2021-10-12 15:20:14 +00:00
Msgf ( "Found peers we are shared with: %s" , peers . String ( ) )
2021-11-14 15:46:09 +00:00
2021-10-12 15:20:14 +00:00
return peers , nil
}
2021-11-14 19:32:03 +00:00
func ( h * Headscale ) getPeers ( machine * Machine ) ( Machines , error ) {
2022-02-03 18:53:11 +00:00
var peers Machines
var err error
2022-02-21 08:15:34 +00:00
2022-02-03 18:53:11 +00:00
// If ACLs rules are defined, filter visible host list with the ACLs
// else use the classic namespace scope
if h . aclPolicy != nil {
2022-02-21 08:15:34 +00:00
var machines [ ] Machine
machines , err = h . ListAllMachines ( )
if err != nil {
log . Error ( ) . Err ( err ) . Msg ( "Error retrieving list of machines" )
return Machines { } , err
}
peers , err = getFilteredByACLPeers ( machines , h . aclRules , machine )
2022-02-03 18:53:11 +00:00
if err != nil {
log . Error ( ) .
Caller ( ) .
Err ( err ) .
Msg ( "Cannot fetch peers" )
2021-11-14 15:46:09 +00:00
2022-02-03 18:53:11 +00:00
return Machines { } , err
}
} else {
direct , err := h . getDirectPeers ( machine )
if err != nil {
log . Error ( ) .
Caller ( ) .
Err ( err ) .
Msg ( "Cannot fetch peers" )
return Machines { } , err
}
shared , err := h . getShared ( machine )
if err != nil {
log . Error ( ) .
Caller ( ) .
Err ( err ) .
Msg ( "Cannot fetch peers" )
return Machines { } , err
}
sharedTo , err := h . getSharedTo ( machine )
if err != nil {
log . Error ( ) .
Caller ( ) .
Err ( err ) .
Msg ( "Cannot fetch peers" )
return Machines { } , err
}
peers = append ( direct , shared ... )
peers = append ( peers , sharedTo ... )
2021-10-02 21:03:34 +00:00
}
2021-10-04 17:39:01 +00:00
sort . Slice ( peers , func ( i , j int ) bool { return peers [ i ] . ID < peers [ j ] . ID } )
log . Trace ( ) .
2021-11-04 22:11:38 +00:00
Caller ( ) .
2021-11-14 19:32:03 +00:00
Str ( "machine" , machine . Name ) .
2021-10-04 17:39:01 +00:00
Msgf ( "Found total peers: %s" , peers . String ( ) )
return peers , nil
2020-06-21 10:32:08 +00:00
}
2021-03-14 10:38:42 +00:00
2021-11-22 19:51:16 +00:00
func ( h * Headscale ) getValidPeers ( machine * Machine ) ( Machines , error ) {
validPeers := make ( Machines , 0 )
peers , err := h . getPeers ( machine )
if err != nil {
return Machines { } , err
}
for _ , peer := range peers {
if peer . isRegistered ( ) && ! peer . isExpired ( ) {
validPeers = append ( validPeers , peer )
}
}
return validPeers , nil
}
2021-11-04 22:11:38 +00:00
func ( h * Headscale ) ListMachines ( ) ( [ ] Machine , error ) {
machines := [ ] Machine { }
if err := h . db . Preload ( "AuthKey" ) . Preload ( "AuthKey.Namespace" ) . Preload ( "Namespace" ) . Find ( & machines ) . Error ; err != nil {
return nil , err
}
2021-11-14 15:46:09 +00:00
2021-11-04 22:11:38 +00:00
return machines , nil
}
2021-11-13 08:39:04 +00:00
// GetMachine finds a Machine by name and namespace and returns the Machine struct.
2021-03-14 10:38:42 +00:00
func ( h * Headscale ) GetMachine ( namespace string , name string ) ( * Machine , error ) {
machines , err := h . ListMachinesInNamespace ( namespace )
if err != nil {
return nil , err
}
2021-11-04 22:11:38 +00:00
for _ , m := range machines {
2021-03-14 10:38:42 +00:00
if m . Name == name {
return & m , nil
}
}
2021-11-14 15:46:09 +00:00
2021-11-15 19:18:14 +00:00
return nil , errMachineNotFound
2021-03-14 10:38:42 +00:00
}
2021-11-13 08:39:04 +00:00
// GetMachineByID finds a Machine by ID and returns the Machine struct.
2021-07-16 22:14:22 +00:00
func ( h * Headscale ) GetMachineByID ( id uint64 ) ( * Machine , error ) {
m := Machine { }
2021-09-03 08:23:26 +00:00
if result := h . db . Preload ( "Namespace" ) . Find ( & Machine { ID : id } ) . First ( & m ) ; result . Error != nil {
2021-07-16 22:14:22 +00:00
return nil , result . Error
}
2021-11-14 15:46:09 +00:00
2021-07-16 22:14:22 +00:00
return & m , nil
}
2021-11-13 08:39:04 +00:00
// GetMachineByMachineKey finds a Machine by ID and returns the Machine struct.
2021-11-26 23:30:42 +00:00
func ( h * Headscale ) GetMachineByMachineKey (
machineKey key . MachinePublic ,
) ( * Machine , error ) {
2021-10-02 20:58:28 +00:00
m := Machine { }
2021-11-26 23:30:42 +00:00
if result := h . db . Preload ( "Namespace" ) . First ( & m , "machine_key = ?" , MachinePublicKeyStripPrefix ( machineKey ) ) ; result . Error != nil {
2021-10-02 20:58:28 +00:00
return nil , result . Error
}
2021-11-14 15:46:09 +00:00
2021-10-02 20:58:28 +00:00
return & m , nil
}
2021-08-23 06:35:44 +00:00
// UpdateMachine takes a Machine struct pointer (typically already loaded from database
// and updates it with the latest data from the database.
2021-11-14 19:32:03 +00:00
func ( h * Headscale ) UpdateMachine ( machine * Machine ) error {
if result := h . db . Find ( machine ) . First ( & machine ) ; result . Error != nil {
2021-08-18 22:17:38 +00:00
return result . Error
}
2021-11-14 15:46:09 +00:00
2021-08-18 22:17:38 +00:00
return nil
}
2021-11-21 13:40:19 +00:00
// ExpireMachine takes a Machine struct and sets the expire field to now.
func ( h * Headscale ) ExpireMachine ( machine * Machine ) {
now := time . Now ( )
machine . Expiry = & now
2021-11-22 19:51:16 +00:00
h . setLastStateChangeToNow ( machine . Namespace . Name )
2021-11-21 13:40:19 +00:00
h . db . Save ( machine )
}
2021-11-22 19:32:11 +00:00
// RefreshMachine takes a Machine struct and sets the expire field to now.
func ( h * Headscale ) RefreshMachine ( machine * Machine , expiry time . Time ) {
now := time . Now ( )
machine . LastSuccessfulUpdate = & now
machine . Expiry = & expiry
2021-11-22 19:51:16 +00:00
h . setLastStateChangeToNow ( machine . Namespace . Name )
2021-11-22 19:32:11 +00:00
h . db . Save ( machine )
}
2021-11-13 08:39:04 +00:00
// DeleteMachine softs deletes a Machine from the database.
2021-11-14 19:32:03 +00:00
func ( h * Headscale ) DeleteMachine ( machine * Machine ) error {
err := h . RemoveSharedMachineFromAllNamespaces ( machine )
2021-11-15 16:33:16 +00:00
if err != nil && errors . Is ( err , errMachineNotShared ) {
2021-10-10 21:55:03 +00:00
return err
}
2021-11-14 19:32:03 +00:00
machine . Registered = false
h . db . Save ( & machine ) // we mark it as unregistered, just in case
if err := h . db . Delete ( & machine ) . Error ; err != nil {
2021-07-16 22:14:22 +00:00
return err
}
2021-07-25 15:59:48 +00:00
2022-02-12 21:04:00 +00:00
return nil
2021-07-16 22:14:22 +00:00
}
2022-01-16 10:59:03 +00:00
func ( h * Headscale ) TouchMachine ( machine * Machine ) error {
return h . db . Updates ( Machine {
ID : machine . ID ,
LastSeen : machine . LastSeen ,
LastSuccessfulUpdate : machine . LastSuccessfulUpdate ,
} ) . Error
}
2021-11-13 08:39:04 +00:00
// HardDeleteMachine hard deletes a Machine from the database.
2021-11-14 19:32:03 +00:00
func ( h * Headscale ) HardDeleteMachine ( machine * Machine ) error {
err := h . RemoveSharedMachineFromAllNamespaces ( machine )
2021-11-15 16:33:16 +00:00
if err != nil && errors . Is ( err , errMachineNotShared ) {
2021-10-10 21:55:03 +00:00
return err
}
2021-11-14 19:32:03 +00:00
if err := h . db . Unscoped ( ) . Delete ( & machine ) . Error ; err != nil {
2021-07-16 22:14:22 +00:00
return err
}
2021-10-10 21:55:03 +00:00
2022-02-12 21:04:00 +00:00
return nil
2021-07-16 22:14:22 +00:00
}
2021-11-13 08:39:04 +00:00
// GetHostInfo returns a Hostinfo struct for the machine.
2021-11-14 19:32:03 +00:00
func ( machine * Machine ) GetHostInfo ( ) ( * tailcfg . Hostinfo , error ) {
2021-03-14 10:38:42 +00:00
hostinfo := tailcfg . Hostinfo { }
2021-11-14 19:32:03 +00:00
if len ( machine . HostInfo ) != 0 {
hi , err := machine . HostInfo . MarshalJSON ( )
2021-03-14 10:38:42 +00:00
if err != nil {
return nil , err
}
err = json . Unmarshal ( hi , & hostinfo )
if err != nil {
return nil , err
}
}
2021-11-14 15:46:09 +00:00
2021-03-14 10:38:42 +00:00
return & hostinfo , nil
}
2021-08-12 19:53:37 +00:00
2021-11-14 19:32:03 +00:00
func ( h * Headscale ) isOutdated ( machine * Machine ) bool {
if err := h . UpdateMachine ( machine ) ; err != nil {
2021-10-07 14:45:45 +00:00
// It does not seem meaningful to propagate this error as the end result
// will have to be that the machine has to be considered outdated.
2021-08-19 17:05:33 +00:00
return true
}
2021-11-14 19:32:03 +00:00
sharedMachines , _ := h . getShared ( machine )
2021-10-06 19:32:15 +00:00
2021-10-06 22:06:07 +00:00
namespaceSet := set . New ( set . ThreadSafe )
2021-11-14 19:32:03 +00:00
namespaceSet . Add ( machine . Namespace . Name )
2021-10-06 22:06:07 +00:00
2021-10-06 19:32:15 +00:00
// Check if any of our shared namespaces has updates that we have
// not propagated.
for _ , sharedMachine := range sharedMachines {
2021-10-06 22:06:07 +00:00
namespaceSet . Add ( sharedMachine . Namespace . Name )
}
namespaces := make ( [ ] string , namespaceSet . Size ( ) )
for index , namespace := range namespaceSet . List ( ) {
2021-11-15 18:42:44 +00:00
if name , ok := namespace . ( string ) ; ok {
namespaces [ index ] = name
}
2021-10-06 19:32:15 +00:00
}
2021-10-06 22:06:07 +00:00
lastChange := h . getLastStateChange ( namespaces ... )
2022-01-15 15:20:14 +00:00
lastUpdate := machine . CreatedAt
if machine . LastSuccessfulUpdate != nil {
lastUpdate = * machine . LastSuccessfulUpdate
}
2021-08-18 22:17:38 +00:00
log . Trace ( ) .
2021-11-04 22:11:38 +00:00
Caller ( ) .
2021-11-14 19:32:03 +00:00
Str ( "machine" , machine . Name ) .
2022-01-15 15:20:14 +00:00
Time ( "last_successful_update" , lastChange ) .
Time ( "last_state_change" , lastUpdate ) .
2021-11-14 19:32:03 +00:00
Msgf ( "Checking if %s is missing updates" , machine . Name )
2021-11-14 15:46:09 +00:00
2022-01-15 15:20:14 +00:00
return lastUpdate . Before ( lastChange )
2021-08-18 22:17:38 +00:00
}
2021-10-02 21:03:34 +00:00
2021-11-14 19:32:03 +00:00
func ( machine Machine ) String ( ) string {
return machine . Name
2021-10-02 21:03:34 +00:00
}
2021-11-14 19:32:03 +00:00
func ( machines Machines ) String ( ) string {
temp := make ( [ ] string , len ( machines ) )
2021-10-02 21:03:34 +00:00
2021-11-14 19:32:03 +00:00
for index , machine := range machines {
2021-10-02 21:03:34 +00:00
temp [ index ] = machine . Name
}
return fmt . Sprintf ( "[ %s ](%d)" , strings . Join ( temp , ", " ) , len ( temp ) )
}
// TODO(kradalby): Remove when we have generics...
2021-11-14 19:32:03 +00:00
func ( machines MachinesP ) String ( ) string {
temp := make ( [ ] string , len ( machines ) )
2021-10-02 21:03:34 +00:00
2021-11-14 19:32:03 +00:00
for index , machine := range machines {
2021-10-02 21:03:34 +00:00
temp [ index ] = machine . Name
}
return fmt . Sprintf ( "[ %s ](%d)" , strings . Join ( temp , ", " ) , len ( temp ) )
}
2021-11-14 19:32:03 +00:00
func ( machines Machines ) toNodes (
2021-10-30 15:33:01 +00:00
baseDomain string ,
dnsConfig * tailcfg . DNSConfig ,
includeRoutes bool ,
) ( [ ] * tailcfg . Node , error ) {
2021-11-14 19:32:03 +00:00
nodes := make ( [ ] * tailcfg . Node , len ( machines ) )
2021-10-02 21:03:34 +00:00
2021-11-14 19:32:03 +00:00
for index , machine := range machines {
2021-10-04 21:49:16 +00:00
node , err := machine . toNode ( baseDomain , dnsConfig , includeRoutes )
2021-10-02 21:03:34 +00:00
if err != nil {
return nil , err
}
nodes [ index ] = node
}
return nodes , nil
2021-10-06 09:19:15 +00:00
}
2021-09-09 22:30:02 +00:00
// toNode converts a Machine into a Tailscale Node. includeRoutes is false for shared nodes
2021-11-13 08:39:04 +00:00
// as per the expected behaviour in the official SaaS.
2021-11-14 19:32:03 +00:00
func ( machine Machine ) toNode (
2021-11-13 08:36:45 +00:00
baseDomain string ,
dnsConfig * tailcfg . DNSConfig ,
includeRoutes bool ,
) ( * tailcfg . Node , error ) {
2021-11-26 23:50:42 +00:00
var nodeKey key . NodePublic
2021-11-27 20:25:12 +00:00
err := nodeKey . UnmarshalText ( [ ] byte ( NodePublicKeyEnsurePrefix ( machine . NodeKey ) ) )
2020-06-21 10:32:08 +00:00
if err != nil {
2021-11-26 23:30:42 +00:00
log . Trace ( ) .
Caller ( ) .
Str ( "node_key" , machine . NodeKey ) .
Msgf ( "Failed to parse node public key from hex" )
return nil , fmt . Errorf ( "failed to parse node public key: %w" , err )
2020-06-21 10:32:08 +00:00
}
2021-11-14 19:32:03 +00:00
2021-11-26 23:50:42 +00:00
var machineKey key . MachinePublic
2021-11-27 20:25:12 +00:00
err = machineKey . UnmarshalText (
[ ] byte ( MachinePublicKeyEnsurePrefix ( machine . MachineKey ) ) ,
)
2020-06-21 10:32:08 +00:00
if err != nil {
2021-11-26 23:30:42 +00:00
return nil , fmt . Errorf ( "failed to parse machine public key: %w" , err )
2020-06-21 10:32:08 +00:00
}
2021-02-23 19:10:58 +00:00
2021-11-26 23:30:42 +00:00
var discoKey key . DiscoPublic
2021-11-14 19:32:03 +00:00
if machine . DiscoKey != "" {
2021-11-27 20:25:12 +00:00
err := discoKey . UnmarshalText (
[ ] byte ( DiscoPublicKeyEnsurePrefix ( machine . DiscoKey ) ) ,
)
2021-02-23 19:10:58 +00:00
if err != nil {
2021-11-26 23:30:42 +00:00
return nil , fmt . Errorf ( "failed to parse disco public key: %w" , err )
2021-02-23 19:10:58 +00:00
}
} else {
2021-11-26 23:30:42 +00:00
discoKey = key . DiscoPublic { }
2021-02-21 20:34:28 +00:00
}
2021-02-23 19:10:58 +00:00
2021-02-20 21:43:07 +00:00
addrs := [ ] netaddr . IPPrefix { }
2022-01-16 13:16:59 +00:00
for _ , machineAddress := range machine . IPAddresses {
ip := netaddr . IPPrefixFrom ( machineAddress , machineAddress . BitLen ( ) )
addrs = append ( addrs , ip )
2020-06-21 10:32:08 +00:00
}
2021-03-14 10:38:42 +00:00
2022-02-12 21:04:00 +00:00
allowedIPs := append (
[ ] netaddr . IPPrefix { } ,
addrs ... ) // we append the node own IP, as it is required by the clients
2021-03-14 10:38:42 +00:00
2021-09-02 14:59:03 +00:00
if includeRoutes {
routesStr := [ ] string { }
2021-11-14 19:32:03 +00:00
if len ( machine . EnabledRoutes ) != 0 {
allwIps , err := machine . EnabledRoutes . MarshalJSON ( )
2021-09-02 14:59:03 +00:00
if err != nil {
return nil , err
}
err = json . Unmarshal ( allwIps , & routesStr )
if err != nil {
return nil , err
}
2021-03-14 10:38:42 +00:00
}
2021-09-09 22:26:46 +00:00
for _ , routeStr := range routesStr {
ip , err := netaddr . ParseIPPrefix ( routeStr )
2021-09-02 14:59:03 +00:00
if err != nil {
return nil , err
}
allowedIPs = append ( allowedIPs , ip )
2021-03-14 10:38:42 +00:00
}
}
2020-06-21 10:32:08 +00:00
endpoints := [ ] string { }
2021-11-14 19:32:03 +00:00
if len ( machine . Endpoints ) != 0 {
be , err := machine . Endpoints . MarshalJSON ( )
2021-02-23 19:10:58 +00:00
if err != nil {
return nil , err
}
err = json . Unmarshal ( be , & endpoints )
if err != nil {
return nil , err
}
2020-06-21 10:32:08 +00:00
}
hostinfo := tailcfg . Hostinfo { }
2021-11-14 19:32:03 +00:00
if len ( machine . HostInfo ) != 0 {
hi , err := machine . HostInfo . MarshalJSON ( )
2021-02-23 19:10:58 +00:00
if err != nil {
return nil , err
}
err = json . Unmarshal ( hi , & hostinfo )
if err != nil {
return nil , err
}
2020-06-21 10:32:08 +00:00
}
2021-02-24 22:45:27 +00:00
var derp string
if hostinfo . NetInfo != nil {
derp = fmt . Sprintf ( "127.3.3.40:%d" , hostinfo . NetInfo . PreferredDERP )
} else {
derp = "127.3.3.40:0" // Zero means disconnected or unknown.
}
2021-09-02 14:59:03 +00:00
var keyExpiry time . Time
2021-11-14 19:32:03 +00:00
if machine . Expiry != nil {
keyExpiry = * machine . Expiry
2021-09-02 14:59:03 +00:00
} else {
keyExpiry = time . Time { }
}
2021-10-04 21:49:16 +00:00
var hostname string
if dnsConfig != nil && dnsConfig . Proxied { // MagicDNS
2021-11-14 19:32:03 +00:00
hostname = fmt . Sprintf (
"%s.%s.%s" ,
machine . Name ,
2022-02-14 14:54:51 +00:00
strings . ReplaceAll (
machine . Namespace . Name ,
"@" ,
"." ,
) , // Replace @ with . for valid domain for machine
2021-11-14 19:32:03 +00:00
baseDomain ,
)
2021-10-04 21:49:16 +00:00
} else {
2021-11-14 19:32:03 +00:00
hostname = machine . Name
2021-10-04 21:49:16 +00:00
}
2021-11-15 16:15:50 +00:00
node := tailcfg . Node {
2021-11-14 19:32:03 +00:00
ID : tailcfg . NodeID ( machine . ID ) , // this is the actual ID
2021-10-30 15:33:01 +00:00
StableID : tailcfg . StableNodeID (
2021-11-15 17:24:24 +00:00
strconv . FormatUint ( machine . ID , Base10 ) ,
2021-10-30 15:33:01 +00:00
) , // in headscale, unlike tailcontrol server, IDs are permanent
2021-10-04 21:49:16 +00:00
Name : hostname ,
2021-11-14 19:32:03 +00:00
User : tailcfg . UserID ( machine . NamespaceID ) ,
2021-11-26 23:30:42 +00:00
Key : nodeKey ,
2021-09-02 14:59:03 +00:00
KeyExpiry : keyExpiry ,
2021-11-26 23:30:42 +00:00
Machine : machineKey ,
2021-02-23 19:10:58 +00:00
DiscoKey : discoKey ,
2020-06-21 10:32:08 +00:00
Addresses : addrs ,
AllowedIPs : allowedIPs ,
Endpoints : endpoints ,
2021-02-24 22:45:27 +00:00
DERP : derp ,
2020-06-21 10:32:08 +00:00
Hostinfo : hostinfo ,
2021-11-14 19:32:03 +00:00
Created : machine . CreatedAt ,
LastSeen : machine . LastSeen ,
2020-06-21 10:32:08 +00:00
2021-02-23 19:10:58 +00:00
KeepAlive : true ,
2021-11-14 19:32:03 +00:00
MachineAuthorized : machine . Registered ,
2021-09-24 07:49:29 +00:00
Capabilities : [ ] string { tailcfg . CapabilityFileSharing } ,
2020-06-21 10:32:08 +00:00
}
2021-11-14 15:46:09 +00:00
2021-11-15 16:15:50 +00:00
return & node , nil
2020-06-21 10:32:08 +00:00
}
2021-11-04 22:11:38 +00:00
2021-11-14 19:32:03 +00:00
func ( machine * Machine ) toProto ( ) * v1 . Machine {
machineProto := & v1 . Machine {
Id : machine . ID ,
MachineKey : machine . MachineKey ,
2021-11-04 22:11:38 +00:00
2022-01-16 13:16:59 +00:00
NodeKey : machine . NodeKey ,
DiscoKey : machine . DiscoKey ,
IpAddresses : machine . IPAddresses . ToStringSlice ( ) ,
Name : machine . Name ,
Namespace : machine . Namespace . toProto ( ) ,
2021-11-04 22:11:38 +00:00
2021-11-14 19:32:03 +00:00
Registered : machine . Registered ,
2021-11-04 22:11:38 +00:00
// TODO(kradalby): Implement register method enum converter
// RegisterMethod: ,
2021-11-14 19:32:03 +00:00
CreatedAt : timestamppb . New ( machine . CreatedAt ) ,
2021-11-04 22:11:38 +00:00
}
2021-11-14 19:32:03 +00:00
if machine . AuthKey != nil {
machineProto . PreAuthKey = machine . AuthKey . toProto ( )
2021-11-04 22:11:38 +00:00
}
2021-11-14 19:32:03 +00:00
if machine . LastSeen != nil {
machineProto . LastSeen = timestamppb . New ( * machine . LastSeen )
2021-11-04 22:11:38 +00:00
}
2021-11-14 19:32:03 +00:00
if machine . LastSuccessfulUpdate != nil {
machineProto . LastSuccessfulUpdate = timestamppb . New (
* machine . LastSuccessfulUpdate ,
)
2021-11-04 22:11:38 +00:00
}
2021-11-14 19:32:03 +00:00
if machine . Expiry != nil {
machineProto . Expiry = timestamppb . New ( * machine . Expiry )
2021-11-04 22:11:38 +00:00
}
2021-11-14 19:32:03 +00:00
return machineProto
2021-11-04 22:11:38 +00:00
}
2021-11-13 08:39:04 +00:00
// RegisterMachine is executed from the CLI to register a new Machine using its MachineKey.
2021-11-14 19:32:03 +00:00
func ( h * Headscale ) RegisterMachine (
2021-11-26 23:30:42 +00:00
machineKeyStr string ,
2021-11-14 19:32:03 +00:00
namespaceName string ,
) ( * Machine , error ) {
namespace , err := h . GetNamespace ( namespaceName )
2021-11-04 22:11:38 +00:00
if err != nil {
return nil , err
}
2021-11-26 23:30:42 +00:00
2021-11-26 23:50:42 +00:00
var machineKey key . MachinePublic
2021-11-27 20:25:12 +00:00
err = machineKey . UnmarshalText ( [ ] byte ( MachinePublicKeyEnsurePrefix ( machineKeyStr ) ) )
2021-11-04 22:11:38 +00:00
if err != nil {
return nil , err
}
2021-11-26 23:30:42 +00:00
log . Trace ( ) .
Caller ( ) .
Str ( "machine_key_str" , machineKeyStr ) .
Str ( "machine_key" , machineKey . String ( ) ) .
Msg ( "Registering machine" )
machine , err := h . GetMachineByMachineKey ( machineKey )
if err != nil {
return nil , err
2021-11-04 22:11:38 +00:00
}
2021-11-22 19:32:52 +00:00
// TODO(kradalby): Currently, if it fails to find a requested expiry, non will be set
// This means that if a user is to slow with register a machine, it will possibly not
// have the correct expiry.
requestedTime := time . Time { }
2021-11-26 23:30:42 +00:00
if requestedTimeIf , found := h . requestedExpiryCache . Get ( machineKey . String ( ) ) ; found {
2021-11-22 19:32:52 +00:00
log . Trace ( ) .
Caller ( ) .
Str ( "machine" , machine . Name ) .
Msg ( "Expiry time found in cache, assigning to node" )
if reqTime , ok := requestedTimeIf . ( time . Time ) ; ok {
requestedTime = reqTime
}
}
if machine . isRegistered ( ) {
log . Trace ( ) .
Caller ( ) .
Str ( "machine" , machine . Name ) .
Msg ( "machine already registered, reauthenticating" )
2021-11-26 23:30:42 +00:00
h . RefreshMachine ( machine , requestedTime )
2021-11-22 19:32:52 +00:00
2021-11-26 23:30:42 +00:00
return machine , nil
2021-11-22 19:32:52 +00:00
}
2021-11-04 22:11:38 +00:00
log . Trace ( ) .
Caller ( ) .
2021-11-14 19:32:03 +00:00
Str ( "machine" , machine . Name ) .
2021-11-04 22:11:38 +00:00
Msg ( "Attempting to register machine" )
2021-11-22 17:21:56 +00:00
if machine . isRegistered ( ) {
2021-11-15 19:18:14 +00:00
err := errMachineAlreadyRegistered
2021-11-04 22:11:38 +00:00
log . Error ( ) .
Caller ( ) .
Err ( err ) .
2021-11-14 19:32:03 +00:00
Str ( "machine" , machine . Name ) .
2021-11-04 22:11:38 +00:00
Msg ( "Attempting to register machine" )
return nil , err
}
2022-01-16 13:16:59 +00:00
ips , err := h . getAvailableIPs ( )
2021-11-04 22:11:38 +00:00
if err != nil {
log . Error ( ) .
Caller ( ) .
Err ( err ) .
2021-11-14 19:32:03 +00:00
Str ( "machine" , machine . Name ) .
2021-11-04 22:11:38 +00:00
Msg ( "Could not find IP for the new machine" )
2021-11-14 15:46:09 +00:00
2021-11-04 22:11:38 +00:00
return nil , err
}
log . Trace ( ) .
Caller ( ) .
2021-11-14 19:32:03 +00:00
Str ( "machine" , machine . Name ) .
2022-01-16 13:16:59 +00:00
Str ( "ip" , strings . Join ( ips . ToStringSlice ( ) , "," ) ) .
2021-11-04 22:11:38 +00:00
Msg ( "Found IP for host" )
2022-01-16 13:16:59 +00:00
machine . IPAddresses = ips
2021-11-14 19:32:03 +00:00
machine . NamespaceID = namespace . ID
machine . Registered = true
2021-11-18 17:51:54 +00:00
machine . RegisterMethod = RegisterMethodCLI
2021-11-22 19:32:11 +00:00
machine . Expiry = & requestedTime
2021-11-14 19:32:03 +00:00
h . db . Save ( & machine )
2021-11-04 22:11:38 +00:00
log . Trace ( ) .
Caller ( ) .
2021-11-14 19:32:03 +00:00
Str ( "machine" , machine . Name ) .
2022-01-16 13:16:59 +00:00
Str ( "ip" , strings . Join ( ips . ToStringSlice ( ) , "," ) ) .
2021-11-04 22:11:38 +00:00
Msg ( "Machine registered with the database" )
2021-11-26 23:30:42 +00:00
return machine , nil
2021-11-04 22:11:38 +00:00
}
2021-11-14 19:32:03 +00:00
func ( machine * Machine ) GetAdvertisedRoutes ( ) ( [ ] netaddr . IPPrefix , error ) {
hostInfo , err := machine . GetHostInfo ( )
2021-11-04 22:11:38 +00:00
if err != nil {
return nil , err
}
2021-11-14 15:46:09 +00:00
2021-11-04 22:11:38 +00:00
return hostInfo . RoutableIPs , nil
}
2021-11-14 19:32:03 +00:00
func ( machine * Machine ) GetEnabledRoutes ( ) ( [ ] netaddr . IPPrefix , error ) {
data , err := machine . EnabledRoutes . MarshalJSON ( )
2021-11-04 22:11:38 +00:00
if err != nil {
return nil , err
}
routesStr := [ ] string { }
err = json . Unmarshal ( data , & routesStr )
if err != nil {
return nil , err
}
routes := make ( [ ] netaddr . IPPrefix , len ( routesStr ) )
for index , routeStr := range routesStr {
route , err := netaddr . ParseIPPrefix ( routeStr )
if err != nil {
return nil , err
}
routes [ index ] = route
}
return routes , nil
}
2021-11-14 19:32:03 +00:00
func ( machine * Machine ) IsRoutesEnabled ( routeStr string ) bool {
2021-11-04 22:11:38 +00:00
route , err := netaddr . ParseIPPrefix ( routeStr )
if err != nil {
return false
}
2021-11-14 19:32:03 +00:00
enabledRoutes , err := machine . GetEnabledRoutes ( )
2021-11-04 22:11:38 +00:00
if err != nil {
return false
}
for _ , enabledRoute := range enabledRoutes {
if route == enabledRoute {
return true
}
}
2021-11-14 15:46:09 +00:00
2021-11-04 22:11:38 +00:00
return false
}
// EnableNodeRoute enables new routes based on a list of new routes. It will _replace_ the
// previous list of routes.
2021-11-14 19:32:03 +00:00
func ( h * Headscale ) EnableRoutes ( machine * Machine , routeStrs ... string ) error {
2021-11-04 22:11:38 +00:00
newRoutes := make ( [ ] netaddr . IPPrefix , len ( routeStrs ) )
for index , routeStr := range routeStrs {
route , err := netaddr . ParseIPPrefix ( routeStr )
if err != nil {
return err
}
newRoutes [ index ] = route
}
2021-11-14 19:32:03 +00:00
availableRoutes , err := machine . GetAdvertisedRoutes ( )
2021-11-04 22:11:38 +00:00
if err != nil {
return err
}
for _ , newRoute := range newRoutes {
2021-11-15 17:24:24 +00:00
if ! containsIPPrefix ( availableRoutes , newRoute ) {
2021-11-13 08:36:45 +00:00
return fmt . Errorf (
2021-11-15 19:18:14 +00:00
"route (%s) is not available on node %s: %w" ,
2021-11-14 19:32:03 +00:00
machine . Name ,
2021-11-15 19:18:14 +00:00
newRoute , errMachineRouteIsNotAvailable ,
2021-11-13 08:36:45 +00:00
)
2021-11-04 22:11:38 +00:00
}
}
routes , err := json . Marshal ( newRoutes )
if err != nil {
return err
}
2021-11-14 19:32:03 +00:00
machine . EnabledRoutes = datatypes . JSON ( routes )
h . db . Save ( & machine )
2021-11-04 22:11:38 +00:00
return nil
}
2021-11-14 19:32:03 +00:00
func ( machine * Machine ) RoutesToProto ( ) ( * v1 . Routes , error ) {
availableRoutes , err := machine . GetAdvertisedRoutes ( )
2021-11-04 22:11:38 +00:00
if err != nil {
return nil , err
}
2021-11-14 19:32:03 +00:00
enabledRoutes , err := machine . GetEnabledRoutes ( )
2021-11-04 22:11:38 +00:00
if err != nil {
return nil , err
}
return & v1 . Routes {
AdvertisedRoutes : ipPrefixToString ( availableRoutes ) ,
EnabledRoutes : ipPrefixToString ( enabledRoutes ) ,
} , nil
}