Rename Machine to Node (#1553)
This commit is contained in:
parent
096ac31bb3
commit
0030af3fa4
57 changed files with 5222 additions and 5238 deletions
|
@ -28,7 +28,7 @@ func (hi *HostInfo) Scan(destination interface{}) error {
|
|||
return json.Unmarshal([]byte(value), hi)
|
||||
|
||||
default:
|
||||
return fmt.Errorf("%w: unexpected data type %T", ErrMachineAddressesInvalid, destination)
|
||||
return fmt.Errorf("%w: unexpected data type %T", ErrNodeAddressesInvalid, destination)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -74,7 +74,7 @@ func (i *IPPrefixes) Scan(destination interface{}) error {
|
|||
return json.Unmarshal([]byte(value), i)
|
||||
|
||||
default:
|
||||
return fmt.Errorf("%w: unexpected data type %T", ErrMachineAddressesInvalid, destination)
|
||||
return fmt.Errorf("%w: unexpected data type %T", ErrNodeAddressesInvalid, destination)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -96,7 +96,7 @@ func (i *StringList) Scan(destination interface{}) error {
|
|||
return json.Unmarshal([]byte(value), i)
|
||||
|
||||
default:
|
||||
return fmt.Errorf("%w: unexpected data type %T", ErrMachineAddressesInvalid, destination)
|
||||
return fmt.Errorf("%w: unexpected data type %T", ErrNodeAddressesInvalid, destination)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -123,8 +123,8 @@ type StateUpdate struct {
|
|||
Type StateUpdateType
|
||||
|
||||
// Changed must be set when Type is StatePeerChanged and
|
||||
// contain the Machine IDs of machines that has changed.
|
||||
Changed Machines
|
||||
// contain the Node IDs of nodes that have changed.
|
||||
Changed Nodes
|
||||
|
||||
// Removed must be set when Type is StatePeerRemoved and
|
||||
// contain a list of the nodes that has been removed from
|
||||
|
|
|
@ -1,346 +0,0 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/netip"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
|
||||
"github.com/juanfont/headscale/hscontrol/policy/matcher"
|
||||
"github.com/juanfont/headscale/hscontrol/util"
|
||||
"go4.org/netipx"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/types/key"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrMachineAddressesInvalid = errors.New("failed to parse machine addresses")
|
||||
ErrHostnameTooLong = errors.New("hostname too long")
|
||||
)
|
||||
|
||||
// Machine is a Headscale client.
|
||||
type Machine struct {
|
||||
ID uint64 `gorm:"primary_key"`
|
||||
MachineKey string `gorm:"type:varchar(64);unique_index"`
|
||||
NodeKey string
|
||||
DiscoKey string
|
||||
IPAddresses MachineAddresses
|
||||
|
||||
// Hostname represents the name given by the Tailscale
|
||||
// client during registration
|
||||
Hostname string
|
||||
|
||||
// Givenname represents either:
|
||||
// a DNS normalized version of Hostname
|
||||
// a valid name set by the User
|
||||
//
|
||||
// GivenName is the name used in all DNS related
|
||||
// parts of headscale.
|
||||
GivenName string `gorm:"type:varchar(63);unique_index"`
|
||||
UserID uint
|
||||
User User `gorm:"foreignKey:UserID"`
|
||||
|
||||
RegisterMethod string
|
||||
|
||||
ForcedTags StringList
|
||||
|
||||
// TODO(kradalby): This seems like irrelevant information?
|
||||
AuthKeyID uint
|
||||
AuthKey *PreAuthKey
|
||||
|
||||
LastSeen *time.Time
|
||||
Expiry *time.Time
|
||||
|
||||
HostInfo HostInfo
|
||||
Endpoints StringList
|
||||
|
||||
Routes []Route
|
||||
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
DeletedAt *time.Time
|
||||
}
|
||||
|
||||
type (
|
||||
Machines []*Machine
|
||||
)
|
||||
|
||||
type MachineAddresses []netip.Addr
|
||||
|
||||
func (ma MachineAddresses) Sort() {
|
||||
sort.Slice(ma, func(index1, index2 int) bool {
|
||||
if ma[index1].Is4() && ma[index2].Is6() {
|
||||
return true
|
||||
}
|
||||
if ma[index1].Is6() && ma[index2].Is4() {
|
||||
return false
|
||||
}
|
||||
|
||||
return ma[index1].Compare(ma[index2]) < 0
|
||||
})
|
||||
}
|
||||
|
||||
func (ma MachineAddresses) StringSlice() []string {
|
||||
ma.Sort()
|
||||
strSlice := make([]string, 0, len(ma))
|
||||
for _, addr := range ma {
|
||||
strSlice = append(strSlice, addr.String())
|
||||
}
|
||||
|
||||
return strSlice
|
||||
}
|
||||
|
||||
func (ma MachineAddresses) Prefixes() []netip.Prefix {
|
||||
addrs := []netip.Prefix{}
|
||||
for _, machineAddress := range ma {
|
||||
ip := netip.PrefixFrom(machineAddress, machineAddress.BitLen())
|
||||
addrs = append(addrs, ip)
|
||||
}
|
||||
|
||||
return addrs
|
||||
}
|
||||
|
||||
func (ma MachineAddresses) InIPSet(set *netipx.IPSet) bool {
|
||||
for _, machineAddr := range ma {
|
||||
if set.Contains(machineAddr) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// AppendToIPSet adds the individual ips in MachineAddresses to a
|
||||
// given netipx.IPSetBuilder.
|
||||
func (ma MachineAddresses) AppendToIPSet(build *netipx.IPSetBuilder) {
|
||||
for _, ip := range ma {
|
||||
build.Add(ip)
|
||||
}
|
||||
}
|
||||
|
||||
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 := netip.ParseAddr(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.StringSlice(), ",")
|
||||
|
||||
return addresses, nil
|
||||
}
|
||||
|
||||
// IsExpired returns whether the machine registration has expired.
|
||||
func (machine Machine) IsExpired() bool {
|
||||
// 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 == nil || machine.Expiry.IsZero() {
|
||||
return false
|
||||
}
|
||||
|
||||
return time.Now().UTC().After(*machine.Expiry)
|
||||
}
|
||||
|
||||
// IsOnline returns if the machine is connected to Headscale.
|
||||
// This is really a naive implementation, as we don't really see
|
||||
// if there is a working connection between the client and the server.
|
||||
func (machine *Machine) IsOnline() bool {
|
||||
if machine.LastSeen == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if machine.IsExpired() {
|
||||
return false
|
||||
}
|
||||
|
||||
return machine.LastSeen.After(time.Now().Add(-KeepAliveInterval))
|
||||
}
|
||||
|
||||
// IsEphemeral returns if the machine is registered as an Ephemeral node.
|
||||
// https://tailscale.com/kb/1111/ephemeral-nodes/
|
||||
func (machine *Machine) IsEphemeral() bool {
|
||||
return machine.AuthKey != nil && machine.AuthKey.Ephemeral
|
||||
}
|
||||
|
||||
func (machine *Machine) CanAccess(filter []tailcfg.FilterRule, machine2 *Machine) bool {
|
||||
for _, rule := range filter {
|
||||
// TODO(kradalby): Cache or pregen this
|
||||
matcher := matcher.MatchFromFilterRule(rule)
|
||||
|
||||
if !matcher.SrcsContainsIPs([]netip.Addr(machine.IPAddresses)) {
|
||||
continue
|
||||
}
|
||||
|
||||
if matcher.DestsContainsIP([]netip.Addr(machine2.IPAddresses)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (machines Machines) FilterByIP(ip netip.Addr) Machines {
|
||||
found := make(Machines, 0)
|
||||
|
||||
for _, machine := range machines {
|
||||
for _, mIP := range machine.IPAddresses {
|
||||
if ip == mIP {
|
||||
found = append(found, machine)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return found
|
||||
}
|
||||
|
||||
func (machine *Machine) Proto() *v1.Machine {
|
||||
machineProto := &v1.Machine{
|
||||
Id: machine.ID,
|
||||
MachineKey: machine.MachineKey,
|
||||
|
||||
NodeKey: machine.NodeKey,
|
||||
DiscoKey: machine.DiscoKey,
|
||||
IpAddresses: machine.IPAddresses.StringSlice(),
|
||||
Name: machine.Hostname,
|
||||
GivenName: machine.GivenName,
|
||||
User: machine.User.Proto(),
|
||||
ForcedTags: machine.ForcedTags,
|
||||
Online: machine.IsOnline(),
|
||||
|
||||
// TODO(kradalby): Implement register method enum converter
|
||||
// RegisterMethod: ,
|
||||
|
||||
CreatedAt: timestamppb.New(machine.CreatedAt),
|
||||
}
|
||||
|
||||
if machine.AuthKey != nil {
|
||||
machineProto.PreAuthKey = machine.AuthKey.Proto()
|
||||
}
|
||||
|
||||
if machine.LastSeen != nil {
|
||||
machineProto.LastSeen = timestamppb.New(*machine.LastSeen)
|
||||
}
|
||||
|
||||
if machine.Expiry != nil {
|
||||
machineProto.Expiry = timestamppb.New(*machine.Expiry)
|
||||
}
|
||||
|
||||
return machineProto
|
||||
}
|
||||
|
||||
// GetHostInfo returns a Hostinfo struct for the machine.
|
||||
func (machine *Machine) GetHostInfo() tailcfg.Hostinfo {
|
||||
return tailcfg.Hostinfo(machine.HostInfo)
|
||||
}
|
||||
|
||||
func (machine *Machine) GetFQDN(dnsConfig *tailcfg.DNSConfig, baseDomain string) (string, error) {
|
||||
var hostname string
|
||||
if dnsConfig != nil && dnsConfig.Proxied { // MagicDNS
|
||||
hostname = fmt.Sprintf(
|
||||
"%s.%s.%s",
|
||||
machine.GivenName,
|
||||
machine.User.Name,
|
||||
baseDomain,
|
||||
)
|
||||
if len(hostname) > MaxHostnameLength {
|
||||
return "", fmt.Errorf(
|
||||
"hostname %q is too long it cannot except 255 ASCII chars: %w",
|
||||
hostname,
|
||||
ErrHostnameTooLong,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
hostname = machine.GivenName
|
||||
}
|
||||
|
||||
return hostname, nil
|
||||
}
|
||||
|
||||
func (machine *Machine) MachinePublicKey() (key.MachinePublic, error) {
|
||||
var machineKey key.MachinePublic
|
||||
|
||||
if machine.MachineKey != "" {
|
||||
err := machineKey.UnmarshalText(
|
||||
[]byte(util.MachinePublicKeyEnsurePrefix(machine.MachineKey)),
|
||||
)
|
||||
if err != nil {
|
||||
return key.MachinePublic{}, fmt.Errorf("failed to parse machine public key: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return machineKey, nil
|
||||
}
|
||||
|
||||
func (machine *Machine) DiscoPublicKey() (key.DiscoPublic, error) {
|
||||
var discoKey key.DiscoPublic
|
||||
if machine.DiscoKey != "" {
|
||||
err := discoKey.UnmarshalText(
|
||||
[]byte(util.DiscoPublicKeyEnsurePrefix(machine.DiscoKey)),
|
||||
)
|
||||
if err != nil {
|
||||
return key.DiscoPublic{}, fmt.Errorf("failed to parse disco public key: %w", err)
|
||||
}
|
||||
} else {
|
||||
discoKey = key.DiscoPublic{}
|
||||
}
|
||||
|
||||
return discoKey, nil
|
||||
}
|
||||
|
||||
func (machine *Machine) NodePublicKey() (key.NodePublic, error) {
|
||||
var nodeKey key.NodePublic
|
||||
err := nodeKey.UnmarshalText([]byte(util.NodePublicKeyEnsurePrefix(machine.NodeKey)))
|
||||
if err != nil {
|
||||
return key.NodePublic{}, fmt.Errorf("failed to parse node public key: %w", err)
|
||||
}
|
||||
|
||||
return nodeKey, nil
|
||||
}
|
||||
|
||||
func (machine Machine) String() string {
|
||||
return machine.Hostname
|
||||
}
|
||||
|
||||
func (machines Machines) String() string {
|
||||
temp := make([]string, len(machines))
|
||||
|
||||
for index, machine := range machines {
|
||||
temp[index] = machine.Hostname
|
||||
}
|
||||
|
||||
return fmt.Sprintf("[ %s ](%d)", strings.Join(temp, ", "), len(temp))
|
||||
}
|
||||
|
||||
func (machines Machines) IDMap() map[uint64]*Machine {
|
||||
ret := map[uint64]*Machine{}
|
||||
|
||||
for _, machine := range machines {
|
||||
ret[machine.ID] = machine
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
346
hscontrol/types/node.go
Normal file
346
hscontrol/types/node.go
Normal file
|
@ -0,0 +1,346 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/netip"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
|
||||
"github.com/juanfont/headscale/hscontrol/policy/matcher"
|
||||
"github.com/juanfont/headscale/hscontrol/util"
|
||||
"go4.org/netipx"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/types/key"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNodeAddressesInvalid = errors.New("failed to parse node addresses")
|
||||
ErrHostnameTooLong = errors.New("hostname too long")
|
||||
)
|
||||
|
||||
// Node is a Headscale client.
|
||||
type Node struct {
|
||||
ID uint64 `gorm:"primary_key"`
|
||||
MachineKey string `gorm:"type:varchar(64);unique_index"`
|
||||
NodeKey string
|
||||
DiscoKey string
|
||||
IPAddresses NodeAddresses
|
||||
|
||||
// Hostname represents the name given by the Tailscale
|
||||
// client during registration
|
||||
Hostname string
|
||||
|
||||
// Givenname represents either:
|
||||
// a DNS normalized version of Hostname
|
||||
// a valid name set by the User
|
||||
//
|
||||
// GivenName is the name used in all DNS related
|
||||
// parts of headscale.
|
||||
GivenName string `gorm:"type:varchar(63);unique_index"`
|
||||
UserID uint
|
||||
User User `gorm:"foreignKey:UserID"`
|
||||
|
||||
RegisterMethod string
|
||||
|
||||
ForcedTags StringList
|
||||
|
||||
// TODO(kradalby): This seems like irrelevant information?
|
||||
AuthKeyID uint
|
||||
AuthKey *PreAuthKey
|
||||
|
||||
LastSeen *time.Time
|
||||
Expiry *time.Time
|
||||
|
||||
HostInfo HostInfo
|
||||
Endpoints StringList
|
||||
|
||||
Routes []Route
|
||||
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
DeletedAt *time.Time
|
||||
}
|
||||
|
||||
type (
|
||||
Nodes []*Node
|
||||
)
|
||||
|
||||
type NodeAddresses []netip.Addr
|
||||
|
||||
func (na NodeAddresses) Sort() {
|
||||
sort.Slice(na, func(index1, index2 int) bool {
|
||||
if na[index1].Is4() && na[index2].Is6() {
|
||||
return true
|
||||
}
|
||||
if na[index1].Is6() && na[index2].Is4() {
|
||||
return false
|
||||
}
|
||||
|
||||
return na[index1].Compare(na[index2]) < 0
|
||||
})
|
||||
}
|
||||
|
||||
func (na NodeAddresses) StringSlice() []string {
|
||||
na.Sort()
|
||||
strSlice := make([]string, 0, len(na))
|
||||
for _, addr := range na {
|
||||
strSlice = append(strSlice, addr.String())
|
||||
}
|
||||
|
||||
return strSlice
|
||||
}
|
||||
|
||||
func (na NodeAddresses) Prefixes() []netip.Prefix {
|
||||
addrs := []netip.Prefix{}
|
||||
for _, nodeAddress := range na {
|
||||
ip := netip.PrefixFrom(nodeAddress, nodeAddress.BitLen())
|
||||
addrs = append(addrs, ip)
|
||||
}
|
||||
|
||||
return addrs
|
||||
}
|
||||
|
||||
func (na NodeAddresses) InIPSet(set *netipx.IPSet) bool {
|
||||
for _, nodeAddr := range na {
|
||||
if set.Contains(nodeAddr) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// AppendToIPSet adds the individual ips in NodeAddresses to a
|
||||
// given netipx.IPSetBuilder.
|
||||
func (na NodeAddresses) AppendToIPSet(build *netipx.IPSetBuilder) {
|
||||
for _, ip := range na {
|
||||
build.Add(ip)
|
||||
}
|
||||
}
|
||||
|
||||
func (na *NodeAddresses) Scan(destination interface{}) error {
|
||||
switch value := destination.(type) {
|
||||
case string:
|
||||
addresses := strings.Split(value, ",")
|
||||
*na = (*na)[:0]
|
||||
for _, addr := range addresses {
|
||||
if len(addr) < 1 {
|
||||
continue
|
||||
}
|
||||
parsed, err := netip.ParseAddr(addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*na = append(*na, parsed)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
default:
|
||||
return fmt.Errorf("%w: unexpected data type %T", ErrNodeAddressesInvalid, destination)
|
||||
}
|
||||
}
|
||||
|
||||
// Value return json value, implement driver.Valuer interface.
|
||||
func (na NodeAddresses) Value() (driver.Value, error) {
|
||||
addresses := strings.Join(na.StringSlice(), ",")
|
||||
|
||||
return addresses, nil
|
||||
}
|
||||
|
||||
// IsExpired returns whether the node registration has expired.
|
||||
func (node Node) IsExpired() bool {
|
||||
// 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 node.Expiry == nil || node.Expiry.IsZero() {
|
||||
return false
|
||||
}
|
||||
|
||||
return time.Now().UTC().After(*node.Expiry)
|
||||
}
|
||||
|
||||
// IsOnline returns if the node is connected to Headscale.
|
||||
// This is really a naive implementation, as we don't really see
|
||||
// if there is a working connection between the client and the server.
|
||||
func (node *Node) IsOnline() bool {
|
||||
if node.LastSeen == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if node.IsExpired() {
|
||||
return false
|
||||
}
|
||||
|
||||
return node.LastSeen.After(time.Now().Add(-KeepAliveInterval))
|
||||
}
|
||||
|
||||
// IsEphemeral returns if the node is registered as an Ephemeral node.
|
||||
// https://tailscale.com/kb/1111/ephemeral-nodes/
|
||||
func (node *Node) IsEphemeral() bool {
|
||||
return node.AuthKey != nil && node.AuthKey.Ephemeral
|
||||
}
|
||||
|
||||
func (node *Node) CanAccess(filter []tailcfg.FilterRule, node2 *Node) bool {
|
||||
for _, rule := range filter {
|
||||
// TODO(kradalby): Cache or pregen this
|
||||
matcher := matcher.MatchFromFilterRule(rule)
|
||||
|
||||
if !matcher.SrcsContainsIPs([]netip.Addr(node.IPAddresses)) {
|
||||
continue
|
||||
}
|
||||
|
||||
if matcher.DestsContainsIP([]netip.Addr(node2.IPAddresses)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (nodes Nodes) FilterByIP(ip netip.Addr) Nodes {
|
||||
found := make(Nodes, 0)
|
||||
|
||||
for _, node := range nodes {
|
||||
for _, mIP := range node.IPAddresses {
|
||||
if ip == mIP {
|
||||
found = append(found, node)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return found
|
||||
}
|
||||
|
||||
func (node *Node) Proto() *v1.Node {
|
||||
nodeProto := &v1.Node{
|
||||
Id: node.ID,
|
||||
MachineKey: node.MachineKey,
|
||||
|
||||
NodeKey: node.NodeKey,
|
||||
DiscoKey: node.DiscoKey,
|
||||
IpAddresses: node.IPAddresses.StringSlice(),
|
||||
Name: node.Hostname,
|
||||
GivenName: node.GivenName,
|
||||
User: node.User.Proto(),
|
||||
ForcedTags: node.ForcedTags,
|
||||
Online: node.IsOnline(),
|
||||
|
||||
// TODO(kradalby): Implement register method enum converter
|
||||
// RegisterMethod: ,
|
||||
|
||||
CreatedAt: timestamppb.New(node.CreatedAt),
|
||||
}
|
||||
|
||||
if node.AuthKey != nil {
|
||||
nodeProto.PreAuthKey = node.AuthKey.Proto()
|
||||
}
|
||||
|
||||
if node.LastSeen != nil {
|
||||
nodeProto.LastSeen = timestamppb.New(*node.LastSeen)
|
||||
}
|
||||
|
||||
if node.Expiry != nil {
|
||||
nodeProto.Expiry = timestamppb.New(*node.Expiry)
|
||||
}
|
||||
|
||||
return nodeProto
|
||||
}
|
||||
|
||||
// GetHostInfo returns a Hostinfo struct for the node.
|
||||
func (node *Node) GetHostInfo() tailcfg.Hostinfo {
|
||||
return tailcfg.Hostinfo(node.HostInfo)
|
||||
}
|
||||
|
||||
func (node *Node) GetFQDN(dnsConfig *tailcfg.DNSConfig, baseDomain string) (string, error) {
|
||||
var hostname string
|
||||
if dnsConfig != nil && dnsConfig.Proxied { // MagicDNS
|
||||
hostname = fmt.Sprintf(
|
||||
"%s.%s.%s",
|
||||
node.GivenName,
|
||||
node.User.Name,
|
||||
baseDomain,
|
||||
)
|
||||
if len(hostname) > MaxHostnameLength {
|
||||
return "", fmt.Errorf(
|
||||
"hostname %q is too long it cannot except 255 ASCII chars: %w",
|
||||
hostname,
|
||||
ErrHostnameTooLong,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
hostname = node.GivenName
|
||||
}
|
||||
|
||||
return hostname, nil
|
||||
}
|
||||
|
||||
func (node *Node) MachinePublicKey() (key.MachinePublic, error) {
|
||||
var machineKey key.MachinePublic
|
||||
|
||||
if node.MachineKey != "" {
|
||||
err := machineKey.UnmarshalText(
|
||||
[]byte(util.MachinePublicKeyEnsurePrefix(node.MachineKey)),
|
||||
)
|
||||
if err != nil {
|
||||
return key.MachinePublic{}, fmt.Errorf("failed to parse machine public key: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return machineKey, nil
|
||||
}
|
||||
|
||||
func (node *Node) DiscoPublicKey() (key.DiscoPublic, error) {
|
||||
var discoKey key.DiscoPublic
|
||||
if node.DiscoKey != "" {
|
||||
err := discoKey.UnmarshalText(
|
||||
[]byte(util.DiscoPublicKeyEnsurePrefix(node.DiscoKey)),
|
||||
)
|
||||
if err != nil {
|
||||
return key.DiscoPublic{}, fmt.Errorf("failed to parse disco public key: %w", err)
|
||||
}
|
||||
} else {
|
||||
discoKey = key.DiscoPublic{}
|
||||
}
|
||||
|
||||
return discoKey, nil
|
||||
}
|
||||
|
||||
func (node *Node) NodePublicKey() (key.NodePublic, error) {
|
||||
var nodeKey key.NodePublic
|
||||
err := nodeKey.UnmarshalText([]byte(util.NodePublicKeyEnsurePrefix(node.NodeKey)))
|
||||
if err != nil {
|
||||
return key.NodePublic{}, fmt.Errorf("failed to parse node public key: %w", err)
|
||||
}
|
||||
|
||||
return nodeKey, nil
|
||||
}
|
||||
|
||||
func (node Node) String() string {
|
||||
return node.Hostname
|
||||
}
|
||||
|
||||
func (nodes Nodes) String() string {
|
||||
temp := make([]string, len(nodes))
|
||||
|
||||
for index, node := range nodes {
|
||||
temp[index] = node.Hostname
|
||||
}
|
||||
|
||||
return fmt.Sprintf("[ %s ](%d)", strings.Join(temp, ", "), len(temp))
|
||||
}
|
||||
|
||||
func (nodes Nodes) IDMap() map[uint64]*Node {
|
||||
ret := map[uint64]*Node{}
|
||||
|
||||
for _, node := range nodes {
|
||||
ret[node.ID] = node
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
|
@ -7,20 +7,20 @@ import (
|
|||
"tailscale.com/tailcfg"
|
||||
)
|
||||
|
||||
func Test_MachineCanAccess(t *testing.T) {
|
||||
func Test_NodeCanAccess(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
machine1 Machine
|
||||
machine2 Machine
|
||||
rules []tailcfg.FilterRule
|
||||
want bool
|
||||
name string
|
||||
node1 Node
|
||||
node2 Node
|
||||
rules []tailcfg.FilterRule
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "no-rules",
|
||||
machine1: Machine{
|
||||
node1: Node{
|
||||
IPAddresses: []netip.Addr{netip.MustParseAddr("10.0.0.1")},
|
||||
},
|
||||
machine2: Machine{
|
||||
node2: Node{
|
||||
IPAddresses: []netip.Addr{netip.MustParseAddr("10.0.0.2")},
|
||||
},
|
||||
rules: []tailcfg.FilterRule{},
|
||||
|
@ -28,10 +28,10 @@ func Test_MachineCanAccess(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "wildcard",
|
||||
machine1: Machine{
|
||||
node1: Node{
|
||||
IPAddresses: []netip.Addr{netip.MustParseAddr("10.0.0.1")},
|
||||
},
|
||||
machine2: Machine{
|
||||
node2: Node{
|
||||
IPAddresses: []netip.Addr{netip.MustParseAddr("10.0.0.2")},
|
||||
},
|
||||
rules: []tailcfg.FilterRule{
|
||||
|
@ -49,10 +49,10 @@ func Test_MachineCanAccess(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "other-cant-access-src",
|
||||
machine1: Machine{
|
||||
node1: Node{
|
||||
IPAddresses: []netip.Addr{netip.MustParseAddr("100.64.0.1")},
|
||||
},
|
||||
machine2: Machine{
|
||||
node2: Node{
|
||||
IPAddresses: []netip.Addr{netip.MustParseAddr("100.64.0.3")},
|
||||
},
|
||||
rules: []tailcfg.FilterRule{
|
||||
|
@ -67,10 +67,10 @@ func Test_MachineCanAccess(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "dest-cant-access-src",
|
||||
machine1: Machine{
|
||||
node1: Node{
|
||||
IPAddresses: []netip.Addr{netip.MustParseAddr("100.64.0.3")},
|
||||
},
|
||||
machine2: Machine{
|
||||
node2: Node{
|
||||
IPAddresses: []netip.Addr{netip.MustParseAddr("100.64.0.2")},
|
||||
},
|
||||
rules: []tailcfg.FilterRule{
|
||||
|
@ -85,10 +85,10 @@ func Test_MachineCanAccess(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "src-can-access-dest",
|
||||
machine1: Machine{
|
||||
node1: Node{
|
||||
IPAddresses: []netip.Addr{netip.MustParseAddr("100.64.0.2")},
|
||||
},
|
||||
machine2: Machine{
|
||||
node2: Node{
|
||||
IPAddresses: []netip.Addr{netip.MustParseAddr("100.64.0.3")},
|
||||
},
|
||||
rules: []tailcfg.FilterRule{
|
||||
|
@ -105,7 +105,7 @@ func Test_MachineCanAccess(t *testing.T) {
|
|||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := tt.machine1.CanAccess(tt.rules, &tt.machine2)
|
||||
got := tt.node1.CanAccess(tt.rules, &tt.node2)
|
||||
|
||||
if got != tt.want {
|
||||
t.Errorf("canAccess() failed: want (%t), got (%t)", tt.want, got)
|
||||
|
@ -114,8 +114,8 @@ func Test_MachineCanAccess(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestMachineAddressesOrder(t *testing.T) {
|
||||
machineAddresses := MachineAddresses{
|
||||
func TestNodeAddressesOrder(t *testing.T) {
|
||||
machineAddresses := NodeAddresses{
|
||||
netip.MustParseAddr("2001:db8::2"),
|
||||
netip.MustParseAddr("100.64.0.2"),
|
||||
netip.MustParseAddr("2001:db8::1"),
|
|
@ -17,9 +17,9 @@ var (
|
|||
type Route struct {
|
||||
gorm.Model
|
||||
|
||||
MachineID uint64
|
||||
Machine Machine
|
||||
Prefix IPPrefix
|
||||
NodeID uint64
|
||||
Node Node
|
||||
Prefix IPPrefix
|
||||
|
||||
Advertised bool
|
||||
Enabled bool
|
||||
|
@ -29,7 +29,7 @@ type Route struct {
|
|||
type Routes []Route
|
||||
|
||||
func (r *Route) String() string {
|
||||
return fmt.Sprintf("%s:%s", r.Machine, netip.Prefix(r.Prefix).String())
|
||||
return fmt.Sprintf("%s:%s", r.Node, netip.Prefix(r.Prefix).String())
|
||||
}
|
||||
|
||||
func (r *Route) IsExitRoute() bool {
|
||||
|
@ -51,7 +51,7 @@ func (rs Routes) Proto() []*v1.Route {
|
|||
for _, route := range rs {
|
||||
protoRoute := v1.Route{
|
||||
Id: uint64(route.ID),
|
||||
Machine: route.Machine.Proto(),
|
||||
Node: route.Node.Proto(),
|
||||
Prefix: netip.Prefix(route.Prefix).String(),
|
||||
Advertised: route.Advertised,
|
||||
Enabled: route.Enabled,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue