[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
This commit is contained in:
Gabriel de Souza Seibel
2023-10-06 03:09:19 -03:00
committed by GitHub
parent 234f226f89
commit cb4b99ffcb
14 changed files with 179 additions and 119 deletions

View File

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

View File

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

View File

@@ -34,7 +34,6 @@ var nodeUpdateCmd = &cobra.Command{
node.Address = address node.Address = address
node.Address6 = address6 node.Address6 = address6
node.LocalAddress = localAddress node.LocalAddress = localAddress
node.PersistentKeepalive = int32(keepAlive)
if relayedNodes != "" { if relayedNodes != "" {
node.RelayedNodes = strings.Split(relayedNodes, ",") node.RelayedNodes = strings.Split(relayedNodes, ",")
} }
@@ -61,7 +60,6 @@ func init() {
nodeUpdateCmd.Flags().StringVar(&name, "name", "", "Node name") nodeUpdateCmd.Flags().StringVar(&name, "name", "", "Node name")
nodeUpdateCmd.Flags().StringVar(&postUp, "post_up", "", "Commands to run after node is up `;` separated") 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().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(&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().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") nodeUpdateCmd.Flags().IntVar(&expirationDateTime, "expiry", 0, "UNIX timestamp after which node will lose access to the network")

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -3,17 +3,19 @@ package migrate
import ( import (
"encoding/json" "encoding/json"
"golang.org/x/exp/slog"
"github.com/gravitl/netmaker/database" "github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/logger" "github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/logic" "github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/models"
"golang.org/x/exp/slog"
) )
// Run - runs all migrations // Run - runs all migrations
func Run() { func Run() {
updateEnrollmentKeys() updateEnrollmentKeys()
assignSuperAdmin() assignSuperAdmin()
updateHosts()
} }
func assignSuperAdmin() { func assignSuperAdmin() {
@@ -37,7 +39,13 @@ func assignSuperAdmin() {
user.IsAdmin = false user.IsAdmin = false
err = logic.UpsertUser(*user) err = logic.UpsertUser(*user)
if err != nil { 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 continue
} else { } else {
createdSuperAdmin = true createdSuperAdmin = true
@@ -49,7 +57,6 @@ func assignSuperAdmin() {
if !createdSuperAdmin { if !createdSuperAdmin {
slog.Error("failed to create superadmin!!") slog.Error("failed to create superadmin!!")
} }
} }
func updateEnrollmentKeys() { 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
}
}
}
}

View File

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

View File

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

View File

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

View File

@@ -54,28 +54,27 @@ type Iface struct {
// CommonNode - represents a commonn node data elements shared by netmaker and netclient // CommonNode - represents a commonn node data elements shared by netmaker and netclient
type CommonNode struct { type CommonNode struct {
ID uuid.UUID `json:"id" yaml:"id"` ID uuid.UUID `json:"id" yaml:"id"`
HostID uuid.UUID `json:"hostid" yaml:"hostid"` HostID uuid.UUID `json:"hostid" yaml:"hostid"`
Network string `json:"network" yaml:"network"` Network string `json:"network" yaml:"network"`
NetworkRange net.IPNet `json:"networkrange" yaml:"networkrange"` NetworkRange net.IPNet `json:"networkrange" yaml:"networkrange"`
NetworkRange6 net.IPNet `json:"networkrange6" yaml:"networkrange6"` NetworkRange6 net.IPNet `json:"networkrange6" yaml:"networkrange6"`
InternetGateway *net.UDPAddr `json:"internetgateway" yaml:"internetgateway"` InternetGateway *net.UDPAddr `json:"internetgateway" yaml:"internetgateway"`
Server string `json:"server" yaml:"server"` Server string `json:"server" yaml:"server"`
Connected bool `json:"connected" yaml:"connected"` Connected bool `json:"connected" yaml:"connected"`
Address net.IPNet `json:"address" yaml:"address"` Address net.IPNet `json:"address" yaml:"address"`
Address6 net.IPNet `json:"address6" yaml:"address6"` Address6 net.IPNet `json:"address6" yaml:"address6"`
Action string `json:"action" yaml:"action"` Action string `json:"action" yaml:"action"`
LocalAddress net.IPNet `json:"localaddress" yaml:"localaddress"` LocalAddress net.IPNet `json:"localaddress" yaml:"localaddress"`
IsEgressGateway bool `json:"isegressgateway" yaml:"isegressgateway"` IsEgressGateway bool `json:"isegressgateway" yaml:"isegressgateway"`
EgressGatewayRanges []string `json:"egressgatewayranges" bson:"egressgatewayranges" yaml:"egressgatewayranges"` EgressGatewayRanges []string `json:"egressgatewayranges" bson:"egressgatewayranges" yaml:"egressgatewayranges"`
IsIngressGateway bool `json:"isingressgateway" yaml:"isingressgateway"` IsIngressGateway bool `json:"isingressgateway" yaml:"isingressgateway"`
IsRelayed bool `json:"isrelayed" bson:"isrelayed" yaml:"isrelayed"` IsRelayed bool `json:"isrelayed" bson:"isrelayed" yaml:"isrelayed"`
RelayedBy string `json:"relayedby" bson:"relayedby" yaml:"relayedby"` RelayedBy string `json:"relayedby" bson:"relayedby" yaml:"relayedby"`
IsRelay bool `json:"isrelay" bson:"isrelay" yaml:"isrelay"` IsRelay bool `json:"isrelay" bson:"isrelay" yaml:"isrelay"`
RelayedNodes []string `json:"relaynodes" yaml:"relayedNodes"` RelayedNodes []string `json:"relaynodes" yaml:"relayedNodes"`
IngressDNS string `json:"ingressdns" yaml:"ingressdns"` IngressDNS string `json:"ingressdns" yaml:"ingressdns"`
DNSOn bool `json:"dnson" yaml:"dnson"` DNSOn bool `json:"dnson" yaml:"dnson"`
PersistentKeepalive time.Duration `json:"persistentkeepalive" yaml:"persistentkeepalive"`
} }
// Node - a model of a network node // 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() == "" { if newNode.Address6.String() == "" {
newNode.Address6 = currentNode.Address6 newNode.Address6 = currentNode.Address6
} }
if newNode.PersistentKeepalive < 0 {
newNode.PersistentKeepalive = currentNode.PersistentKeepalive
}
if newNode.LastModified != currentNode.LastModified { if newNode.LastModified != currentNode.LastModified {
newNode.LastModified = currentNode.LastModified newNode.LastModified = currentNode.LastModified
} }