Browse Source

[NET-562] Persistent Keep Alive from node to host (#2604)

* Move PKA field from models node to host level

* Move PKA field from api models node to host level

* Adapt logic package to node->host PKA

* Adapt migration-related code to node->host PKA

* Adapt cli code to node->host PKA

* Change host PKA default to 20s

* On IfaceDelta, check for PKA on host

* On handleHostRegister, set default PKA

* Use a default PKA

* Use int64 for api host pka

* Reorder imports

* Don't use host pka in iface delta

* Fix ConvertAPIHostToNMHost

* Add swagger doc for host PKA field

* Fix swagger.yml

* Set default PKA only for new hosts

* Remove TODO comment

* Remove redundant check

* Have api-host pka be specified in seconds
Gabriel de Souza Seibel 1 year ago
parent
commit
cb4b99ffcb

+ 5 - 1
cli/cmd/host/update.go

@@ -5,9 +5,10 @@ import (
 	"log"
 	"os"
 
+	"github.com/spf13/cobra"
+
 	"github.com/gravitl/netmaker/cli/functions"
 	"github.com/gravitl/netmaker/models"
-	"github.com/spf13/cobra"
 )
 
 var (
@@ -18,6 +19,7 @@ var (
 	mtu             int
 	isStatic        bool
 	isDefault       bool
+	keepAlive       int
 )
 
 var hostUpdateCmd = &cobra.Command{
@@ -43,6 +45,7 @@ var hostUpdateCmd = &cobra.Command{
 			apiHost.MTU = mtu
 			apiHost.IsStatic = isStatic
 			apiHost.IsDefault = isDefault
+			apiHost.PersistentKeepalive = keepAlive
 		}
 		functions.PrettyPrint(functions.UpdateHost(args[0], apiHost))
 	},
@@ -54,6 +57,7 @@ func init() {
 	hostUpdateCmd.Flags().StringVar(&name, "name", "", "Host name")
 	hostUpdateCmd.Flags().IntVar(&listenPort, "listen_port", 0, "Listen port of the host")
 	hostUpdateCmd.Flags().IntVar(&mtu, "mtu", 0, "Host MTU size")
+	hostUpdateCmd.Flags().IntVar(&keepAlive, "keep_alive", 0, "Interval (seconds) in which packets are sent to keep connections open with peers")
 	hostUpdateCmd.Flags().BoolVar(&isStatic, "static", false, "Make Host Static ?")
 	hostUpdateCmd.Flags().BoolVar(&isDefault, "default", false, "Make Host Default ?")
 	rootCmd.AddCommand(hostUpdateCmd)

+ 0 - 1
cli/cmd/node/flags.go

@@ -11,7 +11,6 @@ var (
 	name                   string
 	postUp                 string
 	postDown               string
-	keepAlive              int
 	relayedNodes           string
 	egressGatewayRanges    string
 	expirationDateTime     int

+ 0 - 2
cli/cmd/node/update.go

@@ -34,7 +34,6 @@ var nodeUpdateCmd = &cobra.Command{
 			node.Address = address
 			node.Address6 = address6
 			node.LocalAddress = localAddress
-			node.PersistentKeepalive = int32(keepAlive)
 			if relayedNodes != "" {
 				node.RelayedNodes = strings.Split(relayedNodes, ",")
 			}
@@ -61,7 +60,6 @@ func init() {
 	nodeUpdateCmd.Flags().StringVar(&name, "name", "", "Node name")
 	nodeUpdateCmd.Flags().StringVar(&postUp, "post_up", "", "Commands to run after node is up `;` separated")
 	nodeUpdateCmd.Flags().StringVar(&postDown, "post_down", "", "Commands to run after node is down `;` separated")
-	nodeUpdateCmd.Flags().IntVar(&keepAlive, "keep_alive", 0, "Interval in which packets are sent to keep connections open with peers")
 	nodeUpdateCmd.Flags().StringVar(&relayedNodes, "relayed_nodes", "", "relayed nodes if node acts as a relay")
 	nodeUpdateCmd.Flags().StringVar(&egressGatewayRanges, "egress_addrs", "", "Addresses for egressing traffic if node acts as an egress")
 	nodeUpdateCmd.Flags().IntVar(&expirationDateTime, "expiry", 0, "UNIX timestamp after which node will lose access to the network")

+ 46 - 14
controllers/enrollmentkeys.go

@@ -7,6 +7,7 @@ import (
 	"time"
 
 	"github.com/gorilla/mux"
+
 	"github.com/gravitl/netmaker/auth"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic"
@@ -17,10 +18,14 @@ import (
 )
 
 func enrollmentKeyHandlers(r *mux.Router) {
-	r.HandleFunc("/api/v1/enrollment-keys", logic.SecurityCheck(true, http.HandlerFunc(createEnrollmentKey))).Methods(http.MethodPost)
-	r.HandleFunc("/api/v1/enrollment-keys", logic.SecurityCheck(true, http.HandlerFunc(getEnrollmentKeys))).Methods(http.MethodGet)
-	r.HandleFunc("/api/v1/enrollment-keys/{keyID}", logic.SecurityCheck(true, http.HandlerFunc(deleteEnrollmentKey))).Methods(http.MethodDelete)
-	r.HandleFunc("/api/v1/host/register/{token}", http.HandlerFunc(handleHostRegister)).Methods(http.MethodPost)
+	r.HandleFunc("/api/v1/enrollment-keys", logic.SecurityCheck(true, http.HandlerFunc(createEnrollmentKey))).
+		Methods(http.MethodPost)
+	r.HandleFunc("/api/v1/enrollment-keys", logic.SecurityCheck(true, http.HandlerFunc(getEnrollmentKeys))).
+		Methods(http.MethodGet)
+	r.HandleFunc("/api/v1/enrollment-keys/{keyID}", logic.SecurityCheck(true, http.HandlerFunc(deleteEnrollmentKey))).
+		Methods(http.MethodDelete)
+	r.HandleFunc("/api/v1/host/register/{token}", http.HandlerFunc(handleHostRegister)).
+		Methods(http.MethodPost)
 }
 
 // swagger:route GET /api/v1/enrollment-keys enrollmentKeys getEnrollmentKeys
@@ -70,7 +75,7 @@ func getEnrollmentKeys(w http.ResponseWriter, r *http.Request) {
 //			Responses:
 //				200: okResponse
 func deleteEnrollmentKey(w http.ResponseWriter, r *http.Request) {
-	var params = mux.Vars(r)
+	params := mux.Vars(r)
 	keyID := params["keyID"]
 	err := logic.DeleteEnrollmentKey(keyID)
 	if err != nil {
@@ -94,7 +99,6 @@ func deleteEnrollmentKey(w http.ResponseWriter, r *http.Request) {
 //			Responses:
 //				200: EnrollmentKey
 func createEnrollmentKey(w http.ResponseWriter, r *http.Request) {
-
 	var enrollmentKeyBody models.APIEnrollmentKey
 
 	err := json.NewDecoder(r.Body).Decode(&enrollmentKeyBody)
@@ -109,7 +113,13 @@ func createEnrollmentKey(w http.ResponseWriter, r *http.Request) {
 		newTime = time.Unix(enrollmentKeyBody.Expiration, 0)
 	}
 
-	newEnrollmentKey, err := logic.CreateEnrollmentKey(enrollmentKeyBody.UsesRemaining, newTime, enrollmentKeyBody.Networks, enrollmentKeyBody.Tags, enrollmentKeyBody.Unlimited)
+	newEnrollmentKey, err := logic.CreateEnrollmentKey(
+		enrollmentKeyBody.UsesRemaining,
+		newTime,
+		enrollmentKeyBody.Networks,
+		enrollmentKeyBody.Tags,
+		enrollmentKeyBody.Unlimited,
+	)
 	if err != nil {
 		logger.Log(0, r.Header.Get("user"), "failed to create enrollment key:", err.Error())
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
@@ -138,7 +148,7 @@ func createEnrollmentKey(w http.ResponseWriter, r *http.Request) {
 //			Responses:
 //				200: RegisterResponse
 func handleHostRegister(w http.ResponseWriter, r *http.Request) {
-	var params = mux.Vars(r)
+	params := mux.Vars(r)
 	token := params["token"]
 	logger.Log(0, "received registration attempt with token", token)
 	// check if token exists
@@ -156,7 +166,6 @@ func handleHostRegister(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		return
 	}
-	hostExists := false
 	// re-register host with turn just in case.
 	if servercfg.IsUsingTurn() {
 		err = logic.RegisterHostWithTurn(newHost.ID.String(), newHost.HostPass)
@@ -165,9 +174,20 @@ func handleHostRegister(w http.ResponseWriter, r *http.Request) {
 		}
 	}
 	// check if host already exists
+	hostExists := false
 	if hostExists = logic.HostExists(&newHost); hostExists && len(enrollmentKey.Networks) == 0 {
-		logger.Log(0, "host", newHost.ID.String(), newHost.Name, "attempted to re-register with no networks")
-		logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("host already exists"), "badrequest"))
+		logger.Log(
+			0,
+			"host",
+			newHost.ID.String(),
+			newHost.Name,
+			"attempted to re-register with no networks",
+		)
+		logic.ReturnErrorResponse(
+			w,
+			r,
+			logic.FormatError(fmt.Errorf("host already exists"), "badrequest"),
+		)
 		return
 	}
 	// version check
@@ -190,11 +210,16 @@ func handleHostRegister(w http.ResponseWriter, r *http.Request) {
 	// use the token
 	if ok := logic.TryToUseEnrollmentKey(enrollmentKey); !ok {
 		logger.Log(0, "host", newHost.ID.String(), newHost.Name, "failed registration")
-		logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("invalid enrollment key"), "badrequest"))
+		logic.ReturnErrorResponse(
+			w,
+			r,
+			logic.FormatError(fmt.Errorf("invalid enrollment key"), "badrequest"),
+		)
 		return
 	}
 	hostPass := newHost.HostPass
 	if !hostExists {
+		newHost.PersistentKeepalive = models.DefaultPersistentKeepAlive
 		// register host
 		logic.CheckHostPorts(&newHost)
 		// create EMQX credentials and ACLs for host
@@ -209,14 +234,21 @@ func handleHostRegister(w http.ResponseWriter, r *http.Request) {
 			}
 		}
 		if err = logic.CreateHost(&newHost); err != nil {
-			logger.Log(0, "host", newHost.ID.String(), newHost.Name, "failed registration -", err.Error())
+			logger.Log(
+				0,
+				"host",
+				newHost.ID.String(),
+				newHost.Name,
+				"failed registration -",
+				err.Error(),
+			)
 			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 			return
 		}
 	} else {
 		// need to revise the list of networks from key
 		// based on the ones host currently has
-		var networksToAdd = []string{}
+		networksToAdd := []string{}
 		currentNets := logic.GetHostNetworks(newHost.ID.String())
 		for _, newNet := range enrollmentKey.Networks {
 			if !logic.StringSliceContains(currentNets, newNet) {

+ 1 - 1
controllers/migrate.go

@@ -65,6 +65,7 @@ func migrate(w http.ResponseWriter, r *http.Request) {
 			host.Name = data.HostName
 			host.HostPass = data.Password
 			host.OS = data.OS
+			host.PersistentKeepalive = time.Duration(legacy.PersistentKeepalive)
 			if err := logic.CreateHost(&host); err != nil {
 				slog.Error("create host", "error", err)
 				logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
@@ -202,7 +203,6 @@ func convertLegacyNode(legacy models.LegacyNode, hostID uuid.UUID) models.Node {
 	node.IsRelay = false
 	node.RelayedNodes = []string{}
 	node.DNSOn = models.ParseBool(legacy.DNSOn)
-	node.PersistentKeepalive = time.Duration(int64(time.Second) * int64(legacy.PersistentKeepalive))
 	node.LastModified = time.Now()
 	node.ExpirationDateTime = time.Unix(legacy.ExpirationDateTime, 0)
 	node.EgressGatewayNatEnabled = models.ParseBool(legacy.EgressGatewayNatEnabled)

+ 9 - 7
logic/hosts.go

@@ -13,11 +13,12 @@ import (
 
 	"github.com/devilcove/httpclient"
 	"github.com/google/uuid"
+	"golang.org/x/crypto/bcrypt"
+
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/servercfg"
-	"golang.org/x/crypto/bcrypt"
 )
 
 var (
@@ -66,6 +67,7 @@ func deleteHostFromCache(hostID string) {
 	delete(hostsCacheMap, hostID)
 	hostCacheMutex.Unlock()
 }
+
 func loadHostsIntoCache(hMap map[string]models.Host) {
 	hostCacheMutex.Lock()
 	hostsCacheMap = hMap
@@ -79,7 +81,6 @@ const (
 
 // GetAllHosts - returns all hosts in flat list or error
 func GetAllHosts() ([]models.Host, error) {
-
 	currHosts := getHostsFromCache()
 	if len(currHosts) != 0 {
 		return currHosts, nil
@@ -139,7 +140,6 @@ func GetHostsMap() (map[string]models.Host, error) {
 
 // GetHost - gets a host from db given id
 func GetHost(hostid string) (*models.Host, error) {
-
 	if host, ok := getHostFromCache(hostid); ok {
 		return &host, nil
 	}
@@ -217,11 +217,13 @@ func UpdateHost(newHost, currentHost *models.Host) {
 		newHost.ListenPort = currentHost.ListenPort
 	}
 
+	if newHost.PersistentKeepalive == 0 {
+		newHost.PersistentKeepalive = currentHost.PersistentKeepalive
+	}
 }
 
 // UpdateHostFromClient - used for updating host on server with update recieved from client
 func UpdateHostFromClient(newHost, currHost *models.Host) (sendPeerUpdate bool) {
-
 	if newHost.PublicKey != currHost.PublicKey {
 		currHost.PublicKey = newHost.PublicKey
 		sendPeerUpdate = true
@@ -230,7 +232,8 @@ func UpdateHostFromClient(newHost, currHost *models.Host) (sendPeerUpdate bool)
 		currHost.ListenPort = newHost.ListenPort
 		sendPeerUpdate = true
 	}
-	if newHost.WgPublicListenPort != 0 && currHost.WgPublicListenPort != newHost.WgPublicListenPort {
+	if newHost.WgPublicListenPort != 0 &&
+		currHost.WgPublicListenPort != newHost.WgPublicListenPort {
 		currHost.WgPublicListenPort = newHost.WgPublicListenPort
 		sendPeerUpdate = true
 	}
@@ -488,7 +491,7 @@ func CheckHostPorts(h *models.Host) {
 	}
 	for _, host := range hosts {
 		if host.ID.String() == h.ID.String() {
-			//skip self
+			// skip self
 			continue
 		}
 		if !host.EndpointIP.Equal(h.EndpointIP) {
@@ -503,7 +506,6 @@ func CheckHostPorts(h *models.Host) {
 			h.ListenPort = minPort
 		}
 	}
-
 }
 
 // HostExists - checks if given host already exists

+ 0 - 3
logic/nodes.go

@@ -350,9 +350,6 @@ func SetNodeDefaults(node *models.Node) {
 		node.DefaultACL = parentNetwork.DefaultACL
 	}
 
-	if node.PersistentKeepalive == 0 {
-		node.PersistentKeepalive = time.Second * time.Duration(parentNetwork.DefaultKeepalive)
-	}
 	node.SetLastModified()
 	node.SetLastCheckIn()
 	node.SetDefaultConnected()

+ 5 - 5
logic/peers.go

@@ -64,7 +64,7 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
 				}
 				relayPeer := wgtypes.PeerConfig{
 					PublicKey:                   relayHost.PublicKey,
-					PersistentKeepaliveInterval: &relayNode.PersistentKeepalive,
+					PersistentKeepaliveInterval: &relayHost.PersistentKeepalive,
 					ReplaceAllowedIPs:           true,
 					AllowedIPs:                  GetAllowedIPs(&node, &relayNode, nil),
 				}
@@ -111,7 +111,7 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
 			peer := peer
 			if peer.ID.String() == node.ID.String() {
 				logger.Log(2, "peer update, skipping self")
-				//skip yourself
+				// skip yourself
 				continue
 			}
 
@@ -122,7 +122,7 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
 			}
 			peerConfig := wgtypes.PeerConfig{
 				PublicKey:                   peerHost.PublicKey,
-				PersistentKeepaliveInterval: &peer.PersistentKeepalive,
+				PersistentKeepaliveInterval: &peerHost.PersistentKeepalive,
 				ReplaceAllowedIPs:           true,
 			}
 			if peer.IsEgressGateway {
@@ -390,7 +390,7 @@ func GetEgressIPs(peer *models.Node) []net.IPNet {
 		logger.Log(0, "error retrieving host for peer", peer.ID.String(), err.Error())
 	}
 
-	//check for internet gateway
+	// check for internet gateway
 	internetGateway := false
 	if slices.Contains(peer.EgressGatewayRanges, "0.0.0.0/0") || slices.Contains(peer.EgressGatewayRanges, "::/0") {
 		internetGateway = true
@@ -439,7 +439,7 @@ func getNodeAllowedIPs(peer, node *models.Node) []net.IPNet {
 	}
 	// handle egress gateway peers
 	if peer.IsEgressGateway {
-		//hasGateway = true
+		// hasGateway = true
 		egressIPs := GetEgressIPs(peer)
 		allowedips = append(allowedips, egressIPs...)
 	}

+ 0 - 1
logic/wireguard.go

@@ -12,7 +12,6 @@ func IfaceDelta(currentNode *models.Node, newNode *models.Node) bool {
 		newNode.IsEgressGateway != currentNode.IsEgressGateway ||
 		newNode.IsIngressGateway != currentNode.IsIngressGateway ||
 		newNode.IsRelay != currentNode.IsRelay ||
-		newNode.PersistentKeepalive != currentNode.PersistentKeepalive ||
 		newNode.DNSOn != currentNode.DNSOn ||
 		newNode.Connected != currentNode.Connected {
 		return true

+ 31 - 3
migrate/migrate.go

@@ -3,17 +3,19 @@ package migrate
 import (
 	"encoding/json"
 
+	"golang.org/x/exp/slog"
+
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
-	"golang.org/x/exp/slog"
 )
 
 // Run - runs all migrations
 func Run() {
 	updateEnrollmentKeys()
 	assignSuperAdmin()
+	updateHosts()
 }
 
 func assignSuperAdmin() {
@@ -37,7 +39,13 @@ func assignSuperAdmin() {
 			user.IsAdmin = false
 			err = logic.UpsertUser(*user)
 			if err != nil {
-				slog.Error("error updating user to superadmin", "user", user.UserName, "error", err.Error())
+				slog.Error(
+					"error updating user to superadmin",
+					"user",
+					user.UserName,
+					"error",
+					err.Error(),
+				)
 				continue
 			} else {
 				createdSuperAdmin = true
@@ -49,7 +57,6 @@ func assignSuperAdmin() {
 	if !createdSuperAdmin {
 		slog.Error("failed to create superadmin!!")
 	}
-
 }
 
 func updateEnrollmentKeys() {
@@ -87,3 +94,24 @@ func updateEnrollmentKeys() {
 
 	}
 }
+
+func updateHosts() {
+	rows, err := database.FetchRecords(database.HOSTS_TABLE_NAME)
+	if err != nil {
+		logger.Log(0, "failed to fetch database records for hosts")
+	}
+	for _, row := range rows {
+		var host models.Host
+		if err := json.Unmarshal([]byte(row), &host); err != nil {
+			logger.Log(0, "failed to unmarshal database row to host", "row", row)
+			continue
+		}
+		if host.PersistentKeepalive == 0 {
+			host.PersistentKeepalive = models.DefaultPersistentKeepAlive
+			if err := logic.UpsertHost(&host); err != nil {
+				logger.Log(0, "failed to upsert host", host.ID.String())
+				continue
+			}
+		}
+	}
+}

+ 27 - 24
models/api_host.go

@@ -3,33 +3,35 @@ package models
 import (
 	"net"
 	"strings"
+	"time"
 )
 
 // ApiHost - the host struct for API usage
 type ApiHost struct {
-	ID                 string   `json:"id"`
-	Verbosity          int      `json:"verbosity"`
-	FirewallInUse      string   `json:"firewallinuse"`
-	Version            string   `json:"version"`
-	Name               string   `json:"name"`
-	OS                 string   `json:"os"`
-	Debug              bool     `json:"debug"`
-	IsStatic           bool     `json:"isstatic"`
-	ListenPort         int      `json:"listenport"`
-	WgPublicListenPort int      `json:"wg_public_listen_port" yaml:"wg_public_listen_port"`
-	MTU                int      `json:"mtu" yaml:"mtu"`
-	Interfaces         []Iface  `json:"interfaces" yaml:"interfaces"`
-	DefaultInterface   string   `json:"defaultinterface" yaml:"defautlinterface"`
-	EndpointIP         string   `json:"endpointip" yaml:"endpointip"`
-	PublicKey          string   `json:"publickey"`
-	MacAddress         string   `json:"macaddress"`
-	Nodes              []string `json:"nodes"`
-	IsDefault          bool     `json:"isdefault" yaml:"isdefault"`
-	IsRelayed          bool     `json:"isrelayed" bson:"isrelayed" yaml:"isrelayed"`
-	RelayedBy          string   `json:"relayed_by" bson:"relayed_by" yaml:"relayed_by"`
-	IsRelay            bool     `json:"isrelay" bson:"isrelay" yaml:"isrelay"`
-	RelayedHosts       []string `json:"relay_hosts" bson:"relay_hosts" yaml:"relay_hosts"`
-	NatType            string   `json:"nat_type" yaml:"nat_type"`
+	ID                  string   `json:"id"`
+	Verbosity           int      `json:"verbosity"`
+	FirewallInUse       string   `json:"firewallinuse"`
+	Version             string   `json:"version"`
+	Name                string   `json:"name"`
+	OS                  string   `json:"os"`
+	Debug               bool     `json:"debug"`
+	IsStatic            bool     `json:"isstatic"`
+	ListenPort          int      `json:"listenport"`
+	WgPublicListenPort  int      `json:"wg_public_listen_port" yaml:"wg_public_listen_port"`
+	MTU                 int      `json:"mtu"                   yaml:"mtu"`
+	Interfaces          []Iface  `json:"interfaces"            yaml:"interfaces"`
+	DefaultInterface    string   `json:"defaultinterface"      yaml:"defautlinterface"`
+	EndpointIP          string   `json:"endpointip"            yaml:"endpointip"`
+	PublicKey           string   `json:"publickey"`
+	MacAddress          string   `json:"macaddress"`
+	Nodes               []string `json:"nodes"`
+	IsDefault           bool     `json:"isdefault"             yaml:"isdefault"`
+	IsRelayed           bool     `json:"isrelayed"             yaml:"isrelayed"             bson:"isrelayed"`
+	RelayedBy           string   `json:"relayed_by"            yaml:"relayed_by"            bson:"relayed_by"`
+	IsRelay             bool     `json:"isrelay"               yaml:"isrelay"               bson:"isrelay"`
+	RelayedHosts        []string `json:"relay_hosts"           yaml:"relay_hosts"           bson:"relay_hosts"`
+	NatType             string   `json:"nat_type"              yaml:"nat_type"`
+	PersistentKeepalive int      `json:"persistentkeepalive"   yaml:"persistentkeepalive"`
 }
 
 // Host.ConvertNMHostToAPI - converts a Netmaker host to an API editable host
@@ -57,6 +59,7 @@ func (h *Host) ConvertNMHostToAPI() *ApiHost {
 	a.Version = h.Version
 	a.IsDefault = h.IsDefault
 	a.NatType = h.NatType
+	a.PersistentKeepalive = int(h.PersistentKeepalive.Seconds())
 	return &a
 }
 
@@ -94,6 +97,6 @@ func (a *ApiHost) ConvertAPIHostToNMHost(currentHost *Host) *Host {
 	h.IsDefault = a.IsDefault
 	h.NatType = currentHost.NatType
 	h.TurnEndpoint = currentHost.TurnEndpoint
-
+	h.PersistentKeepalive = time.Duration(a.PersistentKeepalive) * time.Second
 	return &h
 }

+ 0 - 3
models/api_node.go

@@ -15,7 +15,6 @@ type ApiNode struct {
 	Address6                string   `json:"address6" validate:"omitempty,ipv6"`
 	LocalAddress            string   `json:"localaddress" validate:"omitempty,ipv4"`
 	AllowedIPs              []string `json:"allowedips"`
-	PersistentKeepalive     int32    `json:"persistentkeepalive"`
 	LastModified            int64    `json:"lastmodified"`
 	ExpirationDateTime      int64    `json:"expdatetime"`
 	LastCheckIn             int64    `json:"lastcheckin"`
@@ -68,7 +67,6 @@ func (a *ApiNode) ConvertToServerNode(currentNode *Node) *Node {
 	convertedNode.IngressDNS = a.IngressDns
 	convertedNode.EgressGatewayRequest = currentNode.EgressGatewayRequest
 	convertedNode.EgressGatewayNatEnabled = currentNode.EgressGatewayNatEnabled
-	convertedNode.PersistentKeepalive = time.Second * time.Duration(a.PersistentKeepalive)
 	convertedNode.RelayedNodes = a.RelayedNodes
 	convertedNode.DefaultACL = a.DefaultACL
 	convertedNode.OwnerID = currentNode.OwnerID
@@ -127,7 +125,6 @@ func (nm *Node) ConvertToAPINode() *ApiNode {
 	if isEmptyAddr(apiNode.LocalAddress) {
 		apiNode.LocalAddress = ""
 	}
-	apiNode.PersistentKeepalive = int32(nm.PersistentKeepalive.Seconds())
 	apiNode.LastModified = nm.LastModified.Unix()
 	apiNode.LastCheckIn = nm.LastCheckIn.Unix()
 	apiNode.LastPeerUpdate = nm.LastPeerUpdate.Unix()

+ 34 - 29
models/host.go

@@ -3,6 +3,7 @@ package models
 import (
 	"net"
 	"net/netip"
+	"time"
 
 	"github.com/google/uuid"
 	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
@@ -33,38 +34,42 @@ var NAT_Types = struct {
 }
 
 // WIREGUARD_INTERFACE name of wireguard interface
-const WIREGUARD_INTERFACE = "netmaker"
+const (
+	WIREGUARD_INTERFACE        = "netmaker"
+	DefaultPersistentKeepAlive = 20 * time.Second
+)
 
 // Host - represents a host on the network
 type Host struct {
-	ID                 uuid.UUID        `json:"id" yaml:"id"`
-	Verbosity          int              `json:"verbosity" yaml:"verbosity"`
-	FirewallInUse      string           `json:"firewallinuse" yaml:"firewallinuse"`
-	Version            string           `json:"version" yaml:"version"`
-	IPForwarding       bool             `json:"ipforwarding" yaml:"ipforwarding"`
-	DaemonInstalled    bool             `json:"daemoninstalled" yaml:"daemoninstalled"`
-	AutoUpdate         bool             `json:"autoupdate" yaml:"autoupdate"`
-	HostPass           string           `json:"hostpass" yaml:"hostpass"`
-	Name               string           `json:"name" yaml:"name"`
-	OS                 string           `json:"os" yaml:"os"`
-	Interface          string           `json:"interface" yaml:"interface"`
-	Debug              bool             `json:"debug" yaml:"debug"`
-	ListenPort         int              `json:"listenport" yaml:"listenport"`
-	WgPublicListenPort int              `json:"wg_public_listen_port" yaml:"wg_public_listen_port"`
-	MTU                int              `json:"mtu" yaml:"mtu"`
-	PublicKey          wgtypes.Key      `json:"publickey" yaml:"publickey"`
-	MacAddress         net.HardwareAddr `json:"macaddress" yaml:"macaddress"`
-	TrafficKeyPublic   []byte           `json:"traffickeypublic" yaml:"traffickeypublic"`
-	Nodes              []string         `json:"nodes" yaml:"nodes"`
-	Interfaces         []Iface          `json:"interfaces" yaml:"interfaces"`
-	DefaultInterface   string           `json:"defaultinterface" yaml:"defaultinterface"`
-	EndpointIP         net.IP           `json:"endpointip" yaml:"endpointip"`
-	IsDocker           bool             `json:"isdocker" yaml:"isdocker"`
-	IsK8S              bool             `json:"isk8s" yaml:"isk8s"`
-	IsStatic           bool             `json:"isstatic" yaml:"isstatic"`
-	IsDefault          bool             `json:"isdefault" yaml:"isdefault"`
-	NatType            string           `json:"nat_type,omitempty" yaml:"nat_type,omitempty"`
-	TurnEndpoint       *netip.AddrPort  `json:"turn_endpoint,omitempty" yaml:"turn_endpoint,omitempty"`
+	ID                  uuid.UUID        `json:"id"                      yaml:"id"`
+	Verbosity           int              `json:"verbosity"               yaml:"verbosity"`
+	FirewallInUse       string           `json:"firewallinuse"           yaml:"firewallinuse"`
+	Version             string           `json:"version"                 yaml:"version"`
+	IPForwarding        bool             `json:"ipforwarding"            yaml:"ipforwarding"`
+	DaemonInstalled     bool             `json:"daemoninstalled"         yaml:"daemoninstalled"`
+	AutoUpdate          bool             `json:"autoupdate"              yaml:"autoupdate"`
+	HostPass            string           `json:"hostpass"                yaml:"hostpass"`
+	Name                string           `json:"name"                    yaml:"name"`
+	OS                  string           `json:"os"                      yaml:"os"`
+	Interface           string           `json:"interface"               yaml:"interface"`
+	Debug               bool             `json:"debug"                   yaml:"debug"`
+	ListenPort          int              `json:"listenport"              yaml:"listenport"`
+	WgPublicListenPort  int              `json:"wg_public_listen_port"   yaml:"wg_public_listen_port"`
+	MTU                 int              `json:"mtu"                     yaml:"mtu"`
+	PublicKey           wgtypes.Key      `json:"publickey"               yaml:"publickey"`
+	MacAddress          net.HardwareAddr `json:"macaddress"              yaml:"macaddress"`
+	TrafficKeyPublic    []byte           `json:"traffickeypublic"        yaml:"traffickeypublic"`
+	Nodes               []string         `json:"nodes"                   yaml:"nodes"`
+	Interfaces          []Iface          `json:"interfaces"              yaml:"interfaces"`
+	DefaultInterface    string           `json:"defaultinterface"        yaml:"defaultinterface"`
+	EndpointIP          net.IP           `json:"endpointip"              yaml:"endpointip"`
+	IsDocker            bool             `json:"isdocker"                yaml:"isdocker"`
+	IsK8S               bool             `json:"isk8s"                   yaml:"isk8s"`
+	IsStatic            bool             `json:"isstatic"                yaml:"isstatic"`
+	IsDefault           bool             `json:"isdefault"               yaml:"isdefault"`
+	NatType             string           `json:"nat_type,omitempty"      yaml:"nat_type,omitempty"`
+	TurnEndpoint        *netip.AddrPort  `json:"turn_endpoint,omitempty" yaml:"turn_endpoint,omitempty"`
+	PersistentKeepalive time.Duration    `json:"persistentkeepalive"     yaml:"persistentkeepalive"`
 }
 
 // FormatBool converts a boolean to a [yes|no] string

+ 21 - 25
models/node.go

@@ -54,28 +54,27 @@ type Iface struct {
 
 // CommonNode - represents a commonn node data elements shared by netmaker and netclient
 type CommonNode struct {
-	ID                  uuid.UUID     `json:"id" yaml:"id"`
-	HostID              uuid.UUID     `json:"hostid" yaml:"hostid"`
-	Network             string        `json:"network" yaml:"network"`
-	NetworkRange        net.IPNet     `json:"networkrange" yaml:"networkrange"`
-	NetworkRange6       net.IPNet     `json:"networkrange6" yaml:"networkrange6"`
-	InternetGateway     *net.UDPAddr  `json:"internetgateway" yaml:"internetgateway"`
-	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" bson:"egressgatewayranges" yaml:"egressgatewayranges"`
-	IsIngressGateway    bool          `json:"isingressgateway" yaml:"isingressgateway"`
-	IsRelayed           bool          `json:"isrelayed" bson:"isrelayed" yaml:"isrelayed"`
-	RelayedBy           string        `json:"relayedby" bson:"relayedby" yaml:"relayedby"`
-	IsRelay             bool          `json:"isrelay" bson:"isrelay" yaml:"isrelay"`
-	RelayedNodes        []string      `json:"relaynodes" yaml:"relayedNodes"`
-	IngressDNS          string        `json:"ingressdns" yaml:"ingressdns"`
-	DNSOn               bool          `json:"dnson" yaml:"dnson"`
-	PersistentKeepalive time.Duration `json:"persistentkeepalive" yaml:"persistentkeepalive"`
+	ID                  uuid.UUID    `json:"id" yaml:"id"`
+	HostID              uuid.UUID    `json:"hostid" yaml:"hostid"`
+	Network             string       `json:"network" yaml:"network"`
+	NetworkRange        net.IPNet    `json:"networkrange" yaml:"networkrange"`
+	NetworkRange6       net.IPNet    `json:"networkrange6" yaml:"networkrange6"`
+	InternetGateway     *net.UDPAddr `json:"internetgateway" yaml:"internetgateway"`
+	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" bson:"egressgatewayranges" yaml:"egressgatewayranges"`
+	IsIngressGateway    bool         `json:"isingressgateway" yaml:"isingressgateway"`
+	IsRelayed           bool         `json:"isrelayed" bson:"isrelayed" yaml:"isrelayed"`
+	RelayedBy           string       `json:"relayedby" bson:"relayedby" yaml:"relayedby"`
+	IsRelay             bool         `json:"isrelay" bson:"isrelay" yaml:"isrelay"`
+	RelayedNodes        []string     `json:"relaynodes" yaml:"relayedNodes"`
+	IngressDNS          string       `json:"ingressdns" yaml:"ingressdns"`
+	DNSOn               bool         `json:"dnson" yaml:"dnson"`
 }
 
 // Node - a model of a network node
@@ -369,9 +368,6 @@ func (newNode *Node) Fill(currentNode *Node, isPro bool) { // TODO add new field
 	if newNode.Address6.String() == "" {
 		newNode.Address6 = currentNode.Address6
 	}
-	if newNode.PersistentKeepalive < 0 {
-		newNode.PersistentKeepalive = currentNode.PersistentKeepalive
-	}
 	if newNode.LastModified != currentNode.LastModified {
 		newNode.LastModified = currentNode.LastModified
 	}