Files
abhishek9686 881c9d120d NM-295: publish WSS uplink endpoints for selfsigned and external termination
Add TLS mode, listen addr, optional public hostname, and proxy-mode client
port (443 / TCP_PROXY_PUBLIC_PORT) when building tcp_proxy_endpoint.
2026-08-27 23:50:50 +05:30

348 lines
15 KiB
Go

package models
import (
"bytes"
"net"
"sync"
"time"
"github.com/google/uuid"
"github.com/gravitl/netmaker/schema"
"gorm.io/datatypes"
)
// LastCheckInThreshold - if node's checkin more than this threshold,then node is declared as offline
const LastCheckInThreshold = time.Minute * 10
// NodeCheckin - struct for node checkins with server
type NodeCheckin struct {
Version string
Connected bool
Ifaces []schema.Iface
}
// CommonNode - represents a commonn node data elements shared by netmaker and netclient
type CommonNode struct {
ID uuid.UUID `json:"id" yaml:"id"`
TenantID string `json:"tenant_id"`
HostID uuid.UUID `json:"hostid" yaml:"hostid"`
Network string `json:"network" yaml:"network"`
NetworkRange net.IPNet `json:"networkrange" yaml:"networkrange" swaggertype:"primitive,integer"`
NetworkRange6 net.IPNet `json:"networkrange6" yaml:"networkrange6" swaggertype:"primitive,number"`
Server string `json:"server" yaml:"server"`
Connected bool `json:"connected" yaml:"connected"`
Address net.IPNet `json:"address" yaml:"address"`
Address6 net.IPNet `json:"address6" yaml:"address6"`
Action string `json:"action" yaml:"action"`
LocalAddress net.IPNet `json:"localaddress" yaml:"localaddress"`
IsEgressGateway bool `json:"isegressgateway" yaml:"isegressgateway"`
EgressGatewayRanges []string `json:"egressgatewayranges" yaml:"egressgatewayranges"`
IsIngressGateway bool `json:"isingressgateway" yaml:"isingressgateway"`
IsRelayed bool `json:"isrelayed" yaml:"isrelayed"`
RelayedBy string `json:"relayedby" yaml:"relayedby"`
IsRelay bool `json:"isrelay" yaml:"isrelay"`
IsGw bool `json:"is_gw" yaml:"is_gw"`
RelayedNodes []string `json:"relaynodes" yaml:"relayedNodes"`
IngressDNS string `json:"ingressdns" yaml:"ingressdns"`
AutoAssignGateway bool `json:"auto_assign_gw"`
TcpProxyEnabled bool `json:"tcp_proxy_enabled"`
TcpProxyListenPort int `json:"tcp_proxy_listen_port"`
TcpProxyTLSMode string `json:"tcp_proxy_tls_mode"`
TcpProxyListenAddr string `json:"tcp_proxy_listen_addr,omitempty"`
TcpProxyPublicHostname string `json:"tcp_proxy_public_hostname,omitempty"`
UseTcpUplink bool `json:"use_tcp_uplink"`
}
// Node - a model of a network node
type Node struct {
CommonNode
PendingDelete bool `json:"pendingdelete"`
LastModified time.Time `json:"lastmodified"`
LastCheckIn time.Time `json:"lastcheckin"`
LastPeerUpdate time.Time `json:"lastpeerupdate"`
ExpirationDateTime time.Time `json:"expdatetime"`
EgressGatewayNatEnabled bool `json:"egressgatewaynatenabled"`
EgressGatewayRequest EgressGatewayRequest `json:"egressgatewayrequest"`
IngressGatewayRange string `json:"ingressgatewayrange"`
IngressGatewayRange6 string `json:"ingressgatewayrange6"`
IngressPersistentKeepalive int32 `json:"ingresspersistentkeepalive"`
IngressMTU int32 `json:"ingressmtu"`
Metadata string `json:"metadata"`
// == PRO ==
OwnerID string `json:"ownerid,omitempty"`
IsFailOver bool `json:"is_fail_over"`
IsAutoRelay bool `json:"is_auto_relay"`
//AutoRelayedPeers map[string]struct{} `json:"auto_relayed_peers"`
AutoRelayedPeers map[string]string `json:"auto_relayed_peers_v1"`
//AutoRelayedBy uuid.UUID `json:"auto_relayed_by"`
FailOverPeers map[string]struct{} `json:"fail_over_peers"`
FailedOverBy uuid.UUID `json:"failed_over_by"`
IsInternetGateway bool `json:"isinternetgateway"`
InetNodeReq InetNodeReq `json:"inet_node_req"`
InternetGwID string `json:"internetgw_node_id"`
// SelectedInternetEgressID is the internet-type egress this node uses as its exit node (empty = none).
SelectedInternetEgressID string `json:"selected_internet_egress_id"`
AdditionalRagIps []net.IP `json:"additional_rag_ips" swaggertype:"array,number"`
Tags map[TagID]struct{} `json:"tags"`
IsStatic bool `json:"is_static"`
IsUserNode bool `json:"is_user_node"`
StaticNode ExtClient `json:"static_node"`
Status schema.NodeStatus `json:"node_status"`
Mutex *sync.Mutex `json:"-"`
EgressDetails EgressDetails `json:"-"`
PostureChecksViolations []Violation `json:"posture_check_violations"`
PostureCheckViolationSeverityLevel schema.Severity `json:"posture_check_violation_severity_level"`
LastEvaluationCycleID string `json:"last_evaluation_cycle_id"`
LastEvaluatedAt time.Time `json:"last_evaluated_at"`
Location string `json:"location"` // Format: "lat,lon"
CountryCode string `json:"country_code"`
}
type EgressDetails struct {
EgressGatewayNatEnabled bool
EgressGatewayRequest EgressGatewayRequest
IsEgressGateway bool
EgressGatewayRanges []string
// IsInternetGateway bool `json:"isinternetgateway" yaml:"isinternetgateway"`
// InetNodeReq InetNodeReq `json:"inet_node_req" yaml:"inet_node_req"`
// InternetGwID string `json:"internetgw_node_id" yaml:"internetgw_node_id"`
}
// NodesArray - used for node sorting
type NodesArray []Node
// NodesArray.Len - gets length of node array
func (a NodesArray) Len() int { return len(a) }
// NodesArray.Less - gets returns lower rank of two node addressesFill
func (a NodesArray) Less(i, j int) bool {
return isLess(a[i].Address.IP.String(), a[j].Address.IP.String())
}
// NodesArray.Swap - swaps two nodes in array
func (a NodesArray) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func isLess(ipA string, ipB string) bool {
ipNetA := net.ParseIP(ipA)
ipNetB := net.ParseIP(ipB)
return bytes.Compare(ipNetA, ipNetB) < 0
}
// Node.PrimaryAddress - return ipv4 address if present, else return ipv6
func (node *Node) PrimaryAddressIPNet() net.IPNet {
if node.Address.IP != nil {
return node.Address
}
return node.Address6
}
// Node.PrimaryAddress - return ipv4 address if present, else return ipv6
func (node *Node) PrimaryAddress() string {
if node.Address.IP != nil {
return node.Address.IP.String()
}
return node.Address6.IP.String()
}
func (node *Node) AddressIPNet4() net.IPNet {
return net.IPNet{
IP: node.Address.IP,
Mask: net.CIDRMask(32, 32),
}
}
func (node *Node) AddressIPNet6() net.IPNet {
return net.IPNet{
IP: node.Address6.IP,
Mask: net.CIDRMask(128, 128),
}
}
// Node.PrimaryNetworkRange - returns node's parent network, returns ipv4 address if present, else return ipv6
func (node *Node) PrimaryNetworkRange() net.IPNet {
if node.NetworkRange.IP != nil {
return node.NetworkRange
}
return node.NetworkRange6
}
// Node.SetDefaultConnected
func (node *Node) SetDefaultConnected() {
node.Connected = true
}
// Node.SetLastModified - set last modified initial time
func (node *Node) SetLastModified() {
node.LastModified = time.Now().UTC()
}
// Node.SetLastCheckIn - set checkin time of node
func (node *Node) SetLastCheckIn() {
node.LastCheckIn = time.Now().UTC()
}
// Node.SetLastPeerUpdate - sets last peer update time
func (node *Node) SetLastPeerUpdate() {
node.LastPeerUpdate = time.Now().UTC()
}
// Node.SetExpirationDateTime - sets node expiry time
func (node *Node) SetExpirationDateTime() {
if node.ExpirationDateTime.IsZero() {
node.ExpirationDateTime = time.Now().AddDate(100, 1, 0)
}
}
// Node.Fill - fills other node data into calling node data if not set on calling node (skips DNSOn)
func (newNode *Node) Fill(
currentNode *Node,
isPro bool,
) { // TODO add new field for nftables present
newNode.ID = currentNode.ID
newNode.HostID = currentNode.HostID
// Revisit the logic for boolean values
// TODO ---- !!!!!!!!!!!!!!!!!!!!!!!!!!!!
// TODO ---- !!!!!!!!!!!!!!!!!!!!!!!!!!
if newNode.Address.String() == "" {
newNode.Address = currentNode.Address
}
if newNode.Address6.String() == "" {
newNode.Address6 = currentNode.Address6
}
if newNode.LastModified != currentNode.LastModified {
newNode.LastModified = currentNode.LastModified
}
if newNode.ExpirationDateTime.IsZero() {
newNode.ExpirationDateTime = currentNode.ExpirationDateTime
}
if newNode.LastPeerUpdate.IsZero() || currentNode.LastPeerUpdate.After(newNode.LastPeerUpdate) {
newNode.LastPeerUpdate = currentNode.LastPeerUpdate
}
if newNode.LastCheckIn.IsZero() || currentNode.LastCheckIn.After(newNode.LastCheckIn) {
newNode.LastCheckIn = currentNode.LastCheckIn
}
if newNode.Network == "" {
newNode.Network = currentNode.Network
}
if newNode.IsIngressGateway != currentNode.IsIngressGateway {
newNode.IsIngressGateway = currentNode.IsIngressGateway
}
if newNode.IngressGatewayRange == "" {
newNode.IngressGatewayRange = currentNode.IngressGatewayRange
}
if newNode.IngressGatewayRange6 == "" {
newNode.IngressGatewayRange6 = currentNode.IngressGatewayRange6
}
if newNode.Action == "" {
newNode.Action = currentNode.Action
}
if newNode.RelayedNodes == nil {
newNode.RelayedNodes = currentNode.RelayedNodes
}
if newNode.IsRelay != currentNode.IsRelay && isPro {
newNode.IsRelay = currentNode.IsRelay
}
if newNode.IsRelayed == currentNode.IsRelayed && isPro {
newNode.IsRelayed = currentNode.IsRelayed
}
if newNode.Server == "" {
newNode.Server = currentNode.Server
}
if newNode.IsFailOver != currentNode.IsFailOver {
newNode.IsFailOver = currentNode.IsFailOver
}
newNode.FailOverPeers = currentNode.FailOverPeers
if newNode.Tags == nil {
if currentNode.Tags == nil {
currentNode.Tags = make(map[TagID]struct{})
}
newNode.Tags = currentNode.Tags
}
if newNode.PostureCheckViolationSeverityLevel == 0 {
newNode.PostureCheckViolationSeverityLevel = currentNode.PostureCheckViolationSeverityLevel
}
if len(newNode.PostureChecksViolations) == 0 {
newNode.PostureChecksViolations = currentNode.PostureChecksViolations
}
if newNode.LastEvaluationCycleID == "" {
newNode.LastEvaluationCycleID = currentNode.LastEvaluationCycleID
}
if newNode.LastEvaluatedAt.IsZero() {
newNode.LastEvaluatedAt = currentNode.LastEvaluatedAt
}
}
// Node.NetworkSettings updates a node with network settings
func (node *Node) NetworkSettings(n Network) {
_, cidr, err := net.ParseCIDR(n.AddressRange)
if err == nil {
node.NetworkRange = *cidr
}
_, cidr, err = net.ParseCIDR(n.AddressRange6)
if err == nil {
node.NetworkRange6 = *cidr
}
}
type NodeWithHost struct {
ID string `json:"id"`
TenantID string `json:"tenant_id"`
HostID string `json:"host_id"`
Host *ApiHost `json:"host,omitempty"`
NetworkID string `json:"network_id"`
Address string `json:"address"`
Address6 string `json:"address6"`
Connected bool `json:"connected"`
Action string `json:"action"`
Status schema.NodeStatus `json:"status"`
PendingDelete bool `json:"pending_delete"`
AutoAssignGateway bool `json:"auto_assign_gateway"`
IsGateway bool `json:"is_gateway"`
IsAutoRelay bool `json:"is_auto_relay"`
IsInternetGateway bool `json:"is_internet_gateway"`
RelayedClients datatypes.JSONMap `json:"relayed_clients"`
RelayedIGWClients datatypes.JSONMap `json:"relayed_igw_clients"`
RelayedByNodeID *string `json:"relayed_by_node_id"`
IsIGWClient bool `json:"is_igw_client"`
SelectedInternetEgressID string `json:"selected_internet_egress_id"`
AutoRelayedPeers datatypes.JSONType[map[string]string] `json:"auto_relayed_peers"`
Tags datatypes.JSONMap `json:"tags"`
PostureCheckSeverity schema.Severity `json:"posture_check_severity"`
PostureCheckLastEvaluationCycleID string `json:"posture_check_last_evaluation_cycle_id"`
Metadata string `json:"metadata"`
LastCheckIn time.Time `json:"last_check_in"`
ExpirationDateTime time.Time `json:"expiration_date_time"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (n *NodeWithHost) Fill(_node *schema.Node) {
n.ID = _node.ID
n.TenantID = _node.TenantID
n.HostID = _node.HostID
n.Host = NewApiHostFromSchemaHost(_node.Host)
n.NetworkID = _node.NetworkID
n.Address = _node.Address
n.Address6 = _node.Address6
n.Connected = _node.Connected
n.Action = _node.Action
n.Status = _node.Status
n.PendingDelete = _node.PendingDelete
n.AutoAssignGateway = _node.AutoAssignGateway
n.IsGateway = _node.IsGateway
n.IsAutoRelay = _node.IsAutoRelay == "yes"
n.IsInternetGateway = _node.IsInternetGateway
n.RelayedClients = _node.RelayedClients
n.RelayedIGWClients = _node.RelayedIGWClients
n.RelayedByNodeID = _node.RelayedByNodeID
n.IsIGWClient = _node.IsIGWClient
n.SelectedInternetEgressID = _node.SelectedInternetEgressID
n.AutoRelayedPeers = _node.AutoRelayedPeers
n.Tags = _node.Tags
n.PostureCheckSeverity = _node.PostureCheckSeverity
n.PostureCheckLastEvaluationCycleID = _node.PostureCheckLastEvaluationCycleID
n.Metadata = _node.Metadata
n.LastCheckIn = _node.LastCheckIn
n.ExpirationDateTime = _node.ExpirationDateTime
n.CreatedAt = _node.CreatedAt
n.UpdatedAt = _node.UpdatedAt
}