diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml
index 107322995..2e8cbe2c5 100644
--- a/.github/ISSUE_TEMPLATE/bug-report.yml
+++ b/.github/ISSUE_TEMPLATE/bug-report.yml
@@ -31,6 +31,7 @@ body:
label: Version
description: What version are you running?
options:
+ - v0.21.2
- v0.21.1
- v0.21.0
- v0.20.6
diff --git a/README.md b/README.md
index 7976720b4..5079df10a 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,7 @@
-
+
@@ -57,7 +57,7 @@ These are the instructions for deploying a Netmaker server on your own cloud VM
1. Get a cloud VM with Ubuntu 22.04 and a public IP.
2. Open ports 443, 80, 3479, 8089 and 51821-51830/udp on the VM firewall and in cloud security settings.
-3. (recommended) Prepare DNS - Set a wildcard subdomain in your DNS settings for Netmaker, e.g. *.netmaker.example.com, which points to your VM's pubic IP.
+3. (recommended) Prepare DNS - Set a wildcard subdomain in your DNS settings for Netmaker, e.g. *.netmaker.example.com, which points to your VM's public IP.
4. Run the script:
`sudo wget -qO /root/nm-quick.sh https://raw.githubusercontent.com/gravitl/netmaker/master/scripts/nm-quick.sh && sudo chmod +x /root/nm-quick.sh && sudo /root/nm-quick.sh`
diff --git a/auth/host_session.go b/auth/host_session.go
index 4db9917c2..60cf963dc 100644
--- a/auth/host_session.go
+++ b/auth/host_session.go
@@ -15,6 +15,7 @@ import (
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/mq"
"github.com/gravitl/netmaker/servercfg"
+ "golang.org/x/exp/slog"
)
// SessionHandler - called by the HTTP router when user
@@ -202,7 +203,7 @@ func SessionHandler(conn *websocket.Conn) {
if err = conn.WriteMessage(messageType, reponseData); err != nil {
logger.Log(0, "error during message writing:", err.Error())
}
- go CheckNetRegAndHostUpdate(netsToAdd[:], &result.Host)
+ go CheckNetRegAndHostUpdate(netsToAdd[:], &result.Host, uuid.Nil)
case <-timeout: // the read from req.answerCh has timed out
if err = conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")); err != nil {
logger.Log(0, "error during timeout message writing:", err.Error())
@@ -221,7 +222,7 @@ func SessionHandler(conn *websocket.Conn) {
}
// CheckNetRegAndHostUpdate - run through networks and send a host update
-func CheckNetRegAndHostUpdate(networks []string, h *models.Host) {
+func CheckNetRegAndHostUpdate(networks []string, h *models.Host, relayNodeId uuid.UUID) {
// publish host update through MQ
for i := range networks {
network := networks[i]
@@ -231,6 +232,14 @@ func CheckNetRegAndHostUpdate(networks []string, h *models.Host) {
logger.Log(0, "failed to add host to network:", h.ID.String(), h.Name, network, err.Error())
continue
}
+ if relayNodeId != uuid.Nil && !newNode.IsRelayed {
+ newNode.IsRelayed = true
+ newNode.RelayedBy = relayNodeId.String()
+ slog.Info(fmt.Sprintf("adding relayed node %s to relay %s on network %s", newNode.ID.String(), relayNodeId.String(), network))
+ if err := logic.UpsertNode(newNode); err != nil {
+ slog.Error("failed to update node", "nodeid", relayNodeId.String())
+ }
+ }
logger.Log(1, "added new node", newNode.ID.String(), "to host", h.Name)
hostactions.AddAction(models.HostUpdate{
Action: models.JoinHostToNetwork,
diff --git a/compose/docker-compose.netclient.yml b/compose/docker-compose.netclient.yml
index e0901f84b..5b7425f24 100644
--- a/compose/docker-compose.netclient.yml
+++ b/compose/docker-compose.netclient.yml
@@ -3,7 +3,7 @@ version: "3.4"
services:
netclient:
container_name: netclient
- image: 'gravitl/netclient:v0.21.1'
+ image: 'gravitl/netclient:v0.21.2'
hostname: netmaker-1
network_mode: host
restart: on-failure
diff --git a/controllers/docs.go b/controllers/docs.go
index 491e09039..365708bea 100644
--- a/controllers/docs.go
+++ b/controllers/docs.go
@@ -10,7 +10,7 @@
//
// Schemes: https
// BasePath: /
-// Version: 0.21.1
+// Version: 0.21.2
// Host: api.demo.netmaker.io
//
// Consumes:
diff --git a/controllers/enrollmentkeys.go b/controllers/enrollmentkeys.go
index 33b2c7209..c7de4f92a 100644
--- a/controllers/enrollmentkeys.go
+++ b/controllers/enrollmentkeys.go
@@ -6,6 +6,7 @@ import (
"net/http"
"time"
+ "github.com/google/uuid"
"github.com/gorilla/mux"
"github.com/gravitl/netmaker/auth"
@@ -26,6 +27,8 @@ func enrollmentKeyHandlers(r *mux.Router) {
Methods(http.MethodDelete)
r.HandleFunc("/api/v1/host/register/{token}", http.HandlerFunc(handleHostRegister)).
Methods(http.MethodPost)
+ r.HandleFunc("/api/v1/enrollment-keys/{keyID}", logic.SecurityCheck(true, http.HandlerFunc(updateEnrollmentKey))).
+ Methods(http.MethodPut)
}
// swagger:route GET /api/v1/enrollment-keys enrollmentKeys getEnrollmentKeys
@@ -113,12 +116,23 @@ func createEnrollmentKey(w http.ResponseWriter, r *http.Request) {
newTime = time.Unix(enrollmentKeyBody.Expiration, 0)
}
+ relayId := uuid.Nil
+ if enrollmentKeyBody.Relay != "" {
+ relayId, err = uuid.Parse(enrollmentKeyBody.Relay)
+ if err != nil {
+ logger.Log(0, r.Header.Get("user"), "error parsing relay id: ", err.Error())
+ logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
+ return
+ }
+ }
+
newEnrollmentKey, err := logic.CreateEnrollmentKey(
enrollmentKeyBody.UsesRemaining,
newTime,
enrollmentKeyBody.Networks,
enrollmentKeyBody.Tags,
enrollmentKeyBody.Unlimited,
+ relayId,
)
if err != nil {
logger.Log(0, r.Header.Get("user"), "failed to create enrollment key:", err.Error())
@@ -136,6 +150,57 @@ func createEnrollmentKey(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(newEnrollmentKey)
}
+// swagger:route PUT /api/v1/enrollment-keys/:id enrollmentKeys updateEnrollmentKey
+//
+// Updates an EnrollmentKey for hosts to use on Netmaker server. Updates only the relay to use.
+//
+// Schemes: https
+//
+// Security:
+// oauth
+//
+// Responses:
+// 200: EnrollmentKey
+func updateEnrollmentKey(w http.ResponseWriter, r *http.Request) {
+ var enrollmentKeyBody models.APIEnrollmentKey
+ params := mux.Vars(r)
+ keyId := params["keyID"]
+
+ err := json.NewDecoder(r.Body).Decode(&enrollmentKeyBody)
+ if err != nil {
+ slog.Error("error decoding request body", "error", err)
+ logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
+ return
+ }
+
+ relayId := uuid.Nil
+ if enrollmentKeyBody.Relay != "" {
+ relayId, err = uuid.Parse(enrollmentKeyBody.Relay)
+ if err != nil {
+ slog.Error("error parsing relay id", "error", err)
+ logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
+ return
+ }
+ }
+
+ newEnrollmentKey, err := logic.UpdateEnrollmentKey(keyId, relayId)
+ if err != nil {
+ slog.Error("failed to update enrollment key", "error", err)
+ logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+ return
+ }
+
+ if err = logic.Tokenize(newEnrollmentKey, servercfg.GetAPIHost()); err != nil {
+ slog.Error("failed to update enrollment key", "error", err)
+ logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+ return
+ }
+
+ slog.Info("updated enrollment key", "id", keyId)
+ w.WriteHeader(http.StatusOK)
+ json.NewEncoder(w).Encode(newEnrollmentKey)
+}
+
// swagger:route POST /api/v1/enrollment-keys/{token} enrollmentKeys handleHostRegister
//
// Handles a Netclient registration with server and add nodes accordingly.
@@ -286,5 +351,5 @@ func handleHostRegister(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(&response)
// notify host of changes, peer and node updates
- go auth.CheckNetRegAndHostUpdate(enrollmentKey.Networks, &newHost)
+ go auth.CheckNetRegAndHostUpdate(enrollmentKey.Networks, &newHost, enrollmentKey.Relay)
}
diff --git a/controllers/hosts.go b/controllers/hosts.go
index 6c6bbeccb..4e3cf0726 100644
--- a/controllers/hosts.go
+++ b/controllers/hosts.go
@@ -7,6 +7,7 @@ import (
"net/http"
"github.com/gorilla/mux"
+ "github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/models"
@@ -123,11 +124,12 @@ func pull(w http.ResponseWriter, r *http.Request) {
serverConf.TrafficKey = key
response := models.HostPull{
- Host: *host,
- Nodes: logic.GetHostNodes(host),
- ServerConfig: serverConf,
- Peers: hPU.Peers,
- PeerIDs: hPU.PeerIDs,
+ Host: *host,
+ Nodes: logic.GetHostNodes(host),
+ ServerConfig: serverConf,
+ Peers: hPU.Peers,
+ PeerIDs: hPU.PeerIDs,
+ HostNetworkInfo: hPU.HostNetworkInfo,
}
logger.Log(1, hostID, "completed a pull")
@@ -226,6 +228,19 @@ func deleteHost(w http.ResponseWriter, r *http.Request) {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
+ for _, nodeID := range currHost.Nodes {
+ node, err := logic.GetNodeByID(nodeID)
+ if err != nil {
+ slog.Error("failed to get node", "nodeid", nodeID, "error", err)
+ continue
+ }
+ var gwClients []models.ExtClient
+ if node.IsIngressGateway {
+ gwClients = logic.GetGwExtclients(node.ID.String(), node.Network)
+ }
+ go mq.PublishMqUpdatesForDeletedNode(node, false, gwClients)
+
+ }
if err = logic.RemoveHost(currHost, forceDelete); err != nil {
logger.Log(0, r.Header.Get("user"), "failed to delete a host:", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
@@ -285,6 +300,7 @@ func addHostToNetwork(w http.ResponseWriter, r *http.Request) {
Node: *newNode,
})
mq.PublishPeerUpdate()
+ mq.HandleNewNodeDNS(currHost, newNode)
}()
logger.Log(2, r.Header.Get("user"), fmt.Sprintf("added host %s to network %s", currHost.Name, network))
w.WriteHeader(http.StatusOK)
@@ -314,6 +330,24 @@ func deleteHostFromNetwork(w http.ResponseWriter, r *http.Request) {
// confirm host exists
currHost, err := logic.GetHost(hostid)
if err != nil {
+ if database.IsEmptyRecord(err) {
+ // check if there is any daemon nodes that needs to be deleted
+ node, err := logic.GetNodeByHostRef(hostid, network)
+ if err != nil {
+ slog.Error("couldn't get node for host", "hostid", hostid, "network", network, "error", err)
+ logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
+ return
+ }
+ if err = logic.DeleteNodeByID(&node); err != nil {
+ slog.Error("failed to force delete daemon node",
+ "nodeid", node.ID.String(), "hostid", hostid, "network", network, "error", err)
+ logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to force delete daemon node: "+err.Error()), "internal"))
+ return
+ }
+ logic.ReturnSuccessResponse(w, r, "force deleted daemon node successfully")
+ return
+ }
+
logger.Log(0, r.Header.Get("user"), "failed to find host:", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
@@ -321,58 +355,37 @@ func deleteHostFromNetwork(w http.ResponseWriter, r *http.Request) {
node, err := logic.UpdateHostNetwork(currHost, network, false)
if err != nil {
+ if node == nil && forceDelete {
+ // force cleanup the node
+ node, err := logic.GetNodeByHostRef(hostid, network)
+ if err != nil {
+ slog.Error("couldn't get node for host", "hostid", hostid, "network", network, "error", err)
+ logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
+ return
+ }
+ if err = logic.DeleteNodeByID(&node); err != nil {
+ slog.Error("failed to force delete daemon node",
+ "nodeid", node.ID.String(), "hostid", hostid, "network", network, "error", err)
+ logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to force delete daemon node: "+err.Error()), "internal"))
+ return
+ }
+ logic.ReturnSuccessResponse(w, r, "force deleted daemon node successfully")
+ return
+ }
logger.Log(0, r.Header.Get("user"), "failed to remove host from network:", hostid, network, err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
- if node.IsRelayed {
- // cleanup node from relayednodes on relay node
- relayNode, err := logic.GetNodeByID(node.RelayedBy)
- if err == nil {
- relayedNodes := []string{}
- for _, relayedNodeID := range relayNode.RelayedNodes {
- if relayedNodeID == node.ID.String() {
- continue
- }
- relayedNodes = append(relayedNodes, relayedNodeID)
- }
- relayNode.RelayedNodes = relayedNodes
- logic.UpsertNode(&relayNode)
- }
- }
- if node.IsRelay {
- // unset all the relayed nodes
- logic.SetRelayedNodes(false, node.ID.String(), node.RelayedNodes)
- }
+ var gwClients []models.ExtClient
if node.IsIngressGateway {
- // delete ext clients belonging to ingress gateway
- go func(node models.Node) {
- if err = logic.DeleteGatewayExtClients(node.ID.String(), node.Network); err != nil {
- slog.Error("failed to delete extclients", "gatewayid", node.ID.String(), "network", node.Network, "error", err.Error())
- }
- }(*node)
+ gwClients = logic.GetGwExtclients(node.ID.String(), node.Network)
}
logger.Log(1, "deleting node", node.ID.String(), "from host", currHost.Name)
if err := logic.DeleteNode(node, forceDelete); err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to delete node"), "internal"))
return
}
- node.Action = models.NODE_DELETE
- node.PendingDelete = true
- go func() {
- // notify node change
- if err := mq.NodeUpdate(node); err != nil {
- slog.Error("error publishing node update to node", "node", node.ID, "error", err)
- }
- // notify of peer change
- err = mq.PublishDeletedNodePeerUpdate(node)
- if err != nil {
- logger.Log(1, "error publishing peer update ", err.Error())
- }
- if err := mq.PublishDNSDelete(node, currHost); err != nil {
- logger.Log(1, "error publishing dns update", err.Error())
- }
- }()
+ go mq.PublishMqUpdatesForDeletedNode(*node, true, gwClients)
logger.Log(2, r.Header.Get("user"), fmt.Sprintf("removed host %s from network %s", currHost.Name, network))
w.WriteHeader(http.StatusOK)
}
diff --git a/controllers/node.go b/controllers/node.go
index 4b66b158d..4acfd4f7f 100644
--- a/controllers/node.go
+++ b/controllers/node.go
@@ -614,6 +614,7 @@ func deleteIngressGateway(w http.ResponseWriter, r *http.Request) {
if err := mq.NodeUpdate(&node); err != nil {
slog.Error("error publishing node update to node", "node", node.ID, "error", err)
}
+ mq.PublishDeleteAllExtclientsDNS(node.Network, removedClients)
}()
}
}
@@ -725,35 +726,10 @@ func deleteNode(w http.ResponseWriter, r *http.Request) {
}
forceDelete := r.URL.Query().Get("force") == "true"
fromNode := r.Header.Get("requestfrom") == "node"
- if node.IsRelayed {
- // cleanup node from relayednodes on relay node
- relayNode, err := logic.GetNodeByID(node.RelayedBy)
- if err == nil {
- relayedNodes := []string{}
- for _, relayedNodeID := range relayNode.RelayedNodes {
- if relayedNodeID == node.ID.String() {
- continue
- }
- relayedNodes = append(relayedNodes, relayedNodeID)
- }
- relayNode.RelayedNodes = relayedNodes
- logic.UpsertNode(&relayNode)
- }
- }
- if node.IsRelay {
- // unset all the relayed nodes
- logic.SetRelayedNodes(false, node.ID.String(), node.RelayedNodes)
- }
+ var gwClients []models.ExtClient
if node.IsIngressGateway {
- // delete ext clients belonging to ingress gatewa
- go func(node models.Node) {
- if err = logic.DeleteGatewayExtClients(node.ID.String(), node.Network); err != nil {
- slog.Error("failed to delete extclients", "gatewayid", node.ID.String(), "network", node.Network, "error", err.Error())
- }
- }(node)
-
+ gwClients = logic.GetGwExtclients(node.ID.String(), node.Network)
}
-
purge := forceDelete || fromNode
if err := logic.DeleteNode(&node, purge); err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to delete node"), "internal"))
@@ -762,23 +738,7 @@ func deleteNode(w http.ResponseWriter, r *http.Request) {
logic.ReturnSuccessResponse(w, r, nodeid+" deleted.")
logger.Log(1, r.Header.Get("user"), "Deleted node", nodeid, "from network", params["network"])
- go func() { // notify of peer change
- if !fromNode {
- if err := mq.NodeUpdate(&node); err != nil {
- slog.Error("error publishing node update to node", "node", node.ID, "error", err)
- }
- }
- if err := mq.PublishDeletedNodePeerUpdate(&node); err != nil {
- logger.Log(1, "error publishing peer update ", err.Error())
- }
- host, err := logic.GetHost(node.HostID.String())
- if err != nil {
- logger.Log(1, "failed to retrieve host for node", node.ID.String(), err.Error())
- }
- if err := mq.PublishDNSDelete(&node, host); err != nil {
- logger.Log(1, "error publishing dns update", err.Error())
- }
- }()
+ go mq.PublishMqUpdatesForDeletedNode(node, !fromNode, gwClients)
}
func validateParams(nodeid, netid string) (models.Node, error) {
diff --git a/go.mod b/go.mod
index 46423f016..ee9ca7773 100644
--- a/go.mod
+++ b/go.mod
@@ -6,7 +6,7 @@ require (
github.com/eclipse/paho.mqtt.golang v1.4.3
github.com/go-playground/validator/v10 v10.15.5
github.com/golang-jwt/jwt/v4 v4.5.0
- github.com/google/uuid v1.3.1
+ github.com/google/uuid v1.4.0
github.com/gorilla/handlers v1.5.1
github.com/gorilla/mux v1.8.0
github.com/lib/pq v1.10.9
@@ -15,10 +15,10 @@ require (
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
github.com/stretchr/testify v1.8.4
github.com/txn2/txeh v1.5.5
- golang.org/x/crypto v0.13.0
- golang.org/x/net v0.15.0 // indirect
- golang.org/x/oauth2 v0.12.0
- golang.org/x/sys v0.12.0 // indirect
+ golang.org/x/crypto v0.14.0
+ golang.org/x/net v0.17.0 // indirect
+ golang.org/x/oauth2 v0.13.0
+ golang.org/x/sys v0.13.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20221104135756-97bc4ad4a1cb
google.golang.org/protobuf v1.31.0 // indirect
@@ -32,7 +32,7 @@ require (
)
require (
- github.com/coreos/go-oidc/v3 v3.6.0
+ github.com/coreos/go-oidc/v3 v3.7.0
github.com/gorilla/websocket v1.5.0
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1
)
@@ -67,5 +67,5 @@ require (
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect
golang.org/x/sync v0.1.0 // indirect
- google.golang.org/appengine v1.6.7 // indirect
+ google.golang.org/appengine v1.6.8 // indirect
)
diff --git a/go.sum b/go.sum
index 8addbbd63..d0ac66086 100644
--- a/go.sum
+++ b/go.sum
@@ -7,8 +7,8 @@ filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5E
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/c-robinson/iplib v1.0.7 h1:Dh9AINAlkc+NsNzZuFiVs+pi3AjN+0B7mu01KHdJKHU=
github.com/c-robinson/iplib v1.0.7/go.mod h1:i3LuuFL1hRT5gFpBRnEydzw8R6yhGkF4szNDIbF8pgo=
-github.com/coreos/go-oidc/v3 v3.6.0 h1:AKVxfYw1Gmkn/w96z0DbT/B/xFnzTd3MkZvWLjF4n/o=
-github.com/coreos/go-oidc/v3 v3.6.0/go.mod h1:ZpHUsHBucTUj6WOkrP4E20UPynbLZzhTQ1XKCXkxyPc=
+github.com/coreos/go-oidc/v3 v3.7.0 h1:FTdj0uexT4diYIPlF4yoFVI5MRO1r5+SEcIpEw9vC0o=
+github.com/coreos/go-oidc/v3 v3.7.0/go.mod h1:yQzSCqBnK3e6Fs5l+f5i0F8Kwf0zpH9bPEsbY00KanM=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -34,15 +34,15 @@ github.com/go-playground/validator/v10 v10.15.5 h1:LEBecTWb/1j5TNY1YYG2RcOUN3R7N
github.com/go-playground/validator/v10 v10.15.5/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
-github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
-github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
-github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
+github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
@@ -101,34 +101,52 @@ github.com/txn2/txeh v1.5.5/go.mod h1:qYzGG9kCzeVEI12geK4IlanHWY8X4uy/I3NcW7mk8g
github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c h1:3lbZUMbMiGUW/LMkfsEABsc5zNT9+b1CvsJx47JzJ8g=
github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c/go.mod h1:UrdRz5enIKZ63MEE3IF9l2/ebyx59GyGgPi+tICQdmM=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck=
-golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
+golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
-golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
-golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
-golang.org/x/oauth2 v0.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4=
-golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
+golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
+golang.org/x/oauth2 v0.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY=
+golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
-golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
+golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20221104135756-97bc4ad4a1cb h1:9aqVcYEDHmSNb0uOWukxV5lHV09WqiSiCuhEgWNETLY=
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20221104135756-97bc4ad4a1cb/go.mod h1:mQqgjkW8GQQcJQsbBvK890TKqUK1DfKWkuBGbOkuMHQ=
-google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
-google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
+google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
diff --git a/k8s/client/netclient-daemonset.yaml b/k8s/client/netclient-daemonset.yaml
index d01612917..c8d5ea5be 100644
--- a/k8s/client/netclient-daemonset.yaml
+++ b/k8s/client/netclient-daemonset.yaml
@@ -16,7 +16,7 @@ spec:
hostNetwork: true
containers:
- name: netclient
- image: gravitl/netclient:v0.21.1
+ image: gravitl/netclient:v0.21.2
env:
- name: TOKEN
value: "TOKEN_VALUE"
diff --git a/k8s/client/netclient.yaml b/k8s/client/netclient.yaml
index 0152ff960..9fe01dac7 100644
--- a/k8s/client/netclient.yaml
+++ b/k8s/client/netclient.yaml
@@ -28,7 +28,7 @@ spec:
# - ""
containers:
- name: netclient
- image: gravitl/netclient:v0.21.1
+ image: gravitl/netclient:v0.21.2
env:
- name: TOKEN
value: "TOKEN_VALUE"
diff --git a/k8s/server/netmaker-ui.yaml b/k8s/server/netmaker-ui.yaml
index bf02d1b01..09f1b87ce 100644
--- a/k8s/server/netmaker-ui.yaml
+++ b/k8s/server/netmaker-ui.yaml
@@ -15,7 +15,7 @@ spec:
spec:
containers:
- name: netmaker-ui
- image: gravitl/netmaker-ui:v0.21.1
+ image: gravitl/netmaker-ui:v0.21.2
ports:
- containerPort: 443
env:
diff --git a/logic/dns.go b/logic/dns.go
index af244c9dd..54c97f393 100644
--- a/logic/dns.go
+++ b/logic/dns.go
@@ -2,6 +2,7 @@ package logic
import (
"encoding/json"
+ "fmt"
"os"
"regexp"
"sort"
@@ -87,7 +88,7 @@ func GetNodeDNS(network string) ([]models.DNSEntry, error) {
continue
}
var entry = models.DNSEntry{}
- entry.Name = host.Name
+ entry.Name = fmt.Sprintf("%s.%s", host.Name, network)
entry.Network = network
if node.Address.IP != nil {
entry.Address = node.Address.IP.String()
diff --git a/logic/enrollmentkey.go b/logic/enrollmentkey.go
index a1b2ecbea..5605bdac9 100644
--- a/logic/enrollmentkey.go
+++ b/logic/enrollmentkey.go
@@ -7,8 +7,10 @@ import (
"fmt"
"time"
+ "github.com/google/uuid"
"github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/models"
+ "golang.org/x/exp/slices"
)
// EnrollmentErrors - struct for holding EnrollmentKey error messages
@@ -29,12 +31,12 @@ var EnrollmentErrors = struct {
}
// CreateEnrollmentKey - creates a new enrollment key in db
-func CreateEnrollmentKey(uses int, expiration time.Time, networks, tags []string, unlimited bool) (k *models.EnrollmentKey, err error) {
+func CreateEnrollmentKey(uses int, expiration time.Time, networks, tags []string, unlimited bool, relay uuid.UUID) (*models.EnrollmentKey, error) {
newKeyID, err := getUniqueEnrollmentID()
if err != nil {
return nil, err
}
- k = &models.EnrollmentKey{
+ k := &models.EnrollmentKey{
Value: newKeyID,
Expiration: time.Time{},
UsesRemaining: 0,
@@ -42,6 +44,7 @@ func CreateEnrollmentKey(uses int, expiration time.Time, networks, tags []string
Networks: []string{},
Tags: []string{},
Type: models.Undefined,
+ Relay: relay,
}
if uses > 0 {
k.UsesRemaining = uses
@@ -61,10 +64,51 @@ func CreateEnrollmentKey(uses int, expiration time.Time, networks, tags []string
if ok := k.Validate(); !ok {
return nil, EnrollmentErrors.InvalidCreate
}
+ if relay != uuid.Nil {
+ relayNode, err := GetNodeByID(relay.String())
+ if err != nil {
+ return nil, err
+ }
+ if !slices.Contains(k.Networks, relayNode.Network) {
+ return nil, errors.New("relay node not in key's networks")
+ }
+ if !relayNode.IsRelay {
+ return nil, errors.New("relay node is not a relay")
+ }
+ }
if err = upsertEnrollmentKey(k); err != nil {
return nil, err
}
- return
+ return k, nil
+}
+
+// UpdateEnrollmentKey - updates an existing enrollment key's associated relay
+func UpdateEnrollmentKey(keyId string, relayId uuid.UUID) (*models.EnrollmentKey, error) {
+ key, err := GetEnrollmentKey(keyId)
+ if err != nil {
+ return nil, err
+ }
+
+ if relayId != uuid.Nil {
+ relayNode, err := GetNodeByID(relayId.String())
+ if err != nil {
+ return nil, err
+ }
+ if !slices.Contains(key.Networks, relayNode.Network) {
+ return nil, errors.New("relay node not in key's networks")
+ }
+ if !relayNode.IsRelay {
+ return nil, errors.New("relay node is not a relay")
+ }
+ }
+
+ key.Relay = relayId
+
+ if err = upsertEnrollmentKey(key); err != nil {
+ return nil, err
+ }
+
+ return key, nil
}
// GetAllEnrollmentKeys - fetches all enrollment keys from DB
diff --git a/logic/enrollmentkey_test.go b/logic/enrollmentkey_test.go
index ace8ef9a8..3812b08a2 100644
--- a/logic/enrollmentkey_test.go
+++ b/logic/enrollmentkey_test.go
@@ -4,6 +4,7 @@ import (
"testing"
"time"
+ "github.com/google/uuid"
"github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/models"
"github.com/stretchr/testify/assert"
@@ -13,35 +14,35 @@ func TestCreateEnrollmentKey(t *testing.T) {
database.InitializeDatabase()
defer database.CloseDB()
t.Run("Can_Not_Create_Key", func(t *testing.T) {
- newKey, err := CreateEnrollmentKey(0, time.Time{}, nil, nil, false)
+ newKey, err := CreateEnrollmentKey(0, time.Time{}, nil, nil, false, uuid.Nil)
assert.Nil(t, newKey)
assert.NotNil(t, err)
assert.Equal(t, err, EnrollmentErrors.InvalidCreate)
})
t.Run("Can_Create_Key_Uses", func(t *testing.T) {
- newKey, err := CreateEnrollmentKey(1, time.Time{}, nil, nil, false)
+ newKey, err := CreateEnrollmentKey(1, time.Time{}, nil, nil, false, uuid.Nil)
assert.Nil(t, err)
assert.Equal(t, 1, newKey.UsesRemaining)
assert.True(t, newKey.IsValid())
})
t.Run("Can_Create_Key_Time", func(t *testing.T) {
- newKey, err := CreateEnrollmentKey(0, time.Now().Add(time.Minute), nil, nil, false)
+ newKey, err := CreateEnrollmentKey(0, time.Now().Add(time.Minute), nil, nil, false, uuid.Nil)
assert.Nil(t, err)
assert.True(t, newKey.IsValid())
})
t.Run("Can_Create_Key_Unlimited", func(t *testing.T) {
- newKey, err := CreateEnrollmentKey(0, time.Time{}, nil, nil, true)
+ newKey, err := CreateEnrollmentKey(0, time.Time{}, nil, nil, true, uuid.Nil)
assert.Nil(t, err)
assert.True(t, newKey.IsValid())
})
t.Run("Can_Create_Key_WithNetworks", func(t *testing.T) {
- newKey, err := CreateEnrollmentKey(0, time.Time{}, []string{"mynet", "skynet"}, nil, true)
+ newKey, err := CreateEnrollmentKey(0, time.Time{}, []string{"mynet", "skynet"}, nil, true, uuid.Nil)
assert.Nil(t, err)
assert.True(t, newKey.IsValid())
assert.True(t, len(newKey.Networks) == 2)
})
t.Run("Can_Create_Key_WithTags", func(t *testing.T) {
- newKey, err := CreateEnrollmentKey(0, time.Time{}, nil, []string{"tag1", "tag2"}, true)
+ newKey, err := CreateEnrollmentKey(0, time.Time{}, nil, []string{"tag1", "tag2"}, true, uuid.Nil)
assert.Nil(t, err)
assert.True(t, newKey.IsValid())
assert.True(t, len(newKey.Tags) == 2)
@@ -61,7 +62,7 @@ func TestCreateEnrollmentKey(t *testing.T) {
func TestDelete_EnrollmentKey(t *testing.T) {
database.InitializeDatabase()
defer database.CloseDB()
- newKey, _ := CreateEnrollmentKey(0, time.Time{}, []string{"mynet", "skynet"}, nil, true)
+ newKey, _ := CreateEnrollmentKey(0, time.Time{}, []string{"mynet", "skynet"}, nil, true, uuid.Nil)
t.Run("Can_Delete_Key", func(t *testing.T) {
assert.True(t, newKey.IsValid())
err := DeleteEnrollmentKey(newKey.Value)
@@ -82,7 +83,7 @@ func TestDelete_EnrollmentKey(t *testing.T) {
func TestDecrement_EnrollmentKey(t *testing.T) {
database.InitializeDatabase()
defer database.CloseDB()
- newKey, _ := CreateEnrollmentKey(1, time.Time{}, nil, nil, false)
+ newKey, _ := CreateEnrollmentKey(1, time.Time{}, nil, nil, false, uuid.Nil)
t.Run("Check_initial_uses", func(t *testing.T) {
assert.True(t, newKey.IsValid())
assert.Equal(t, newKey.UsesRemaining, 1)
@@ -106,9 +107,9 @@ func TestDecrement_EnrollmentKey(t *testing.T) {
func TestUsability_EnrollmentKey(t *testing.T) {
database.InitializeDatabase()
defer database.CloseDB()
- key1, _ := CreateEnrollmentKey(1, time.Time{}, nil, nil, false)
- key2, _ := CreateEnrollmentKey(0, time.Now().Add(time.Minute<<4), nil, nil, false)
- key3, _ := CreateEnrollmentKey(0, time.Time{}, nil, nil, true)
+ key1, _ := CreateEnrollmentKey(1, time.Time{}, nil, nil, false, uuid.Nil)
+ key2, _ := CreateEnrollmentKey(0, time.Now().Add(time.Minute<<4), nil, nil, false, uuid.Nil)
+ key3, _ := CreateEnrollmentKey(0, time.Time{}, nil, nil, true, uuid.Nil)
t.Run("Check if valid use key can be used", func(t *testing.T) {
assert.Equal(t, key1.UsesRemaining, 1)
ok := TryToUseEnrollmentKey(key1)
@@ -144,7 +145,7 @@ func removeAllEnrollments() {
func TestTokenize_EnrollmentKeys(t *testing.T) {
database.InitializeDatabase()
defer database.CloseDB()
- newKey, _ := CreateEnrollmentKey(0, time.Time{}, []string{"mynet", "skynet"}, nil, true)
+ newKey, _ := CreateEnrollmentKey(0, time.Time{}, []string{"mynet", "skynet"}, nil, true, uuid.Nil)
const defaultValue = "MwE5MwE5MwE5MwE5MwE5MwE5MwE5MwE5"
const b64value = "eyJzZXJ2ZXIiOiJhcGkubXlzZXJ2ZXIuY29tIiwidmFsdWUiOiJNd0U1TXdFNU13RTVNd0U1TXdFNU13RTVNd0U1TXdFNSJ9"
const serverAddr = "api.myserver.com"
@@ -177,7 +178,7 @@ func TestTokenize_EnrollmentKeys(t *testing.T) {
func TestDeTokenize_EnrollmentKeys(t *testing.T) {
database.InitializeDatabase()
defer database.CloseDB()
- newKey, _ := CreateEnrollmentKey(0, time.Time{}, []string{"mynet", "skynet"}, nil, true)
+ newKey, _ := CreateEnrollmentKey(0, time.Time{}, []string{"mynet", "skynet"}, nil, true, uuid.Nil)
const b64Value = "eyJzZXJ2ZXIiOiJhcGkubXlzZXJ2ZXIuY29tIiwidmFsdWUiOiJNd0U1TXdFNU13RTVNd0U1TXdFNU13RTVNd0U1TXdFNSJ9"
const serverAddr = "api.myserver.com"
diff --git a/logic/extpeers.go b/logic/extpeers.go
index 7d301c5a0..e19b2edc8 100644
--- a/logic/extpeers.go
+++ b/logic/extpeers.go
@@ -107,7 +107,7 @@ func GetNetworkExtClients(network string) ([]models.ExtClient, error) {
if err != nil {
continue
}
- key, err := GetRecordKey(extclient.ClientID, network)
+ key, err := GetRecordKey(extclient.ClientID, extclient.Network)
if err == nil {
storeExtClientInCache(key, extclient)
}
@@ -137,6 +137,21 @@ func GetExtClient(clientid string, network string) (models.ExtClient, error) {
return extclient, err
}
+// GetGwExtclients - return all ext clients attached to the passed gw id
+func GetGwExtclients(nodeID, network string) []models.ExtClient {
+ gwClients := []models.ExtClient{}
+ clients, err := GetNetworkExtClients(network)
+ if err != nil {
+ return gwClients
+ }
+ for _, client := range clients {
+ if client.IngressGatewayID == nodeID {
+ gwClients = append(gwClients, client)
+ }
+ }
+ return gwClients
+}
+
// GetExtClient - gets a single ext client on a network
func GetExtClientByPubKey(publicKey string, network string) (*models.ExtClient, error) {
netClients, err := GetNetworkExtClients(network)
diff --git a/logic/hosts.go b/logic/hosts.go
index 2137706ff..9778ddf81 100644
--- a/logic/hosts.go
+++ b/logic/hosts.go
@@ -199,7 +199,6 @@ func UpdateHost(newHost, currentHost *models.Host) {
newHost.Nodes = currentHost.Nodes
newHost.PublicKey = currentHost.PublicKey
newHost.TrafficKeyPublic = currentHost.TrafficKeyPublic
-
// changeable fields
if len(newHost.Version) == 0 {
newHost.Version = currentHost.Version
@@ -393,7 +392,7 @@ func DissasociateNodeFromHost(n *models.Node, h *models.Host) error {
}
}
}()
- if err := deleteNodeByID(n); err != nil {
+ if err := DeleteNodeByID(n); err != nil {
return err
}
diff --git a/logic/networks.go b/logic/networks.go
index a6203ab47..5ba0e84d6 100644
--- a/logic/networks.go
+++ b/logic/networks.go
@@ -23,7 +23,6 @@ func GetNetworks() ([]models.Network, error) {
var networks []models.Network
collection, err := database.FetchRecords(database.NETWORKS_TABLE_NAME)
-
if err != nil {
return networks, err
}
@@ -72,6 +71,9 @@ func CreateNetwork(network models.Network) (models.Network, error) {
}
network.AddressRange6 = normalizedRange
}
+ if !IsNetworkCIDRUnique(network.GetNetworkNetworkCIDR4(), network.GetNetworkNetworkCIDR6()) {
+ return models.Network{}, errors.New("network cidr already in use")
+ }
network.SetDefaults()
network.SetNodesLastModified()
@@ -101,6 +103,27 @@ func GetNetworkNonServerNodeCount(networkName string) (int, error) {
return len(nodes), err
}
+func IsNetworkCIDRUnique(cidr4 *net.IPNet, cidr6 *net.IPNet) bool {
+ networks, err := GetNetworks()
+ if err != nil {
+ return database.IsEmptyRecord(err)
+ }
+ for _, network := range networks {
+ if intersect(network.GetNetworkNetworkCIDR4(), cidr4) ||
+ intersect(network.GetNetworkNetworkCIDR6(), cidr6) {
+ return false
+ }
+ }
+ return true
+}
+
+func intersect(n1, n2 *net.IPNet) bool {
+ if n1 == nil || n2 == nil {
+ return false
+ }
+ return n2.Contains(n1.IP) || n1.Contains(n2.IP)
+}
+
// GetParentNetwork - get parent network
func GetParentNetwork(networkname string) (models.Network, error) {
diff --git a/logic/nodes.go b/logic/nodes.go
index c9cf8da01..f5889da2b 100644
--- a/logic/nodes.go
+++ b/logic/nodes.go
@@ -183,6 +183,33 @@ func UpdateNode(currentNode *models.Node, newNode *models.Node) error {
func DeleteNode(node *models.Node, purge bool) error {
alreadyDeleted := node.PendingDelete || node.Action == models.NODE_DELETE
node.Action = models.NODE_DELETE
+
+ //delete ext clients if node is ingress gw
+ if node.IsIngressGateway {
+ if err := DeleteGatewayExtClients(node.ID.String(), node.Network); err != nil {
+ slog.Error("failed to delete ext clients", "nodeid", node.ID.String(), "error", err.Error())
+ }
+ }
+ if node.IsRelayed {
+ // cleanup node from relayednodes on relay node
+ relayNode, err := GetNodeByID(node.RelayedBy)
+ if err == nil {
+ relayedNodes := []string{}
+ for _, relayedNodeID := range relayNode.RelayedNodes {
+ if relayedNodeID == node.ID.String() {
+ continue
+ }
+ relayedNodes = append(relayedNodes, relayedNodeID)
+ }
+ relayNode.RelayedNodes = relayedNodes
+ UpsertNode(&relayNode)
+ }
+ }
+ if node.IsRelay {
+ // unset all the relayed nodes
+ SetRelayedNodes(false, node.ID.String(), node.RelayedNodes)
+ }
+
if !purge && !alreadyDeleted {
newnode := *node
newnode.PendingDelete = true
@@ -198,7 +225,7 @@ func DeleteNode(node *models.Node, purge bool) error {
host, err := GetHost(node.HostID.String())
if err != nil {
logger.Log(1, "no host found for node", node.ID.String(), "deleting..")
- if delErr := deleteNodeByID(node); delErr != nil {
+ if delErr := DeleteNodeByID(node); delErr != nil {
logger.Log(0, "failed to delete node", node.ID.String(), delErr.Error())
}
return err
@@ -215,16 +242,25 @@ func DeleteNode(node *models.Node, purge bool) error {
return nil
}
-// deleteNodeByID - deletes a node from database
-func deleteNodeByID(node *models.Node) error {
- var err error
- var key = node.ID.String()
- //delete any ext clients as required
- if node.IsIngressGateway {
- if err := DeleteGatewayExtClients(node.ID.String(), node.Network); err != nil {
- logger.Log(0, "failed to deleted ext clients", err.Error())
+// GetNodeByHostRef - gets the node by host id and network
+func GetNodeByHostRef(hostid, network string) (node models.Node, err error) {
+ nodes, err := GetNetworkNodes(network)
+ if err != nil {
+ return models.Node{}, err
+ }
+ for _, node := range nodes {
+ if node.HostID.String() == hostid && node.Network == network {
+ return node, nil
}
}
+ return models.Node{}, errors.New("node not found")
+}
+
+// DeleteNodeByID - deletes a node from database
+func DeleteNodeByID(node *models.Node) error {
+ var err error
+ var key = node.ID.String()
+
if err = database.DeleteRecord(database.NODES_TABLE_NAME, key); err != nil {
if !database.IsEmptyRecord(err) {
return err
diff --git a/logic/peers.go b/logic/peers.go
index aa3475765..9f86fd2d0 100644
--- a/logic/peers.go
+++ b/logic/peers.go
@@ -38,8 +38,6 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
HostNetworkInfo: models.HostInfoMap{},
}
- // endpoint detection always comes from the server
- hostPeerUpdate.EndpointDetection = servercfg.EndpointDetectionEnabled()
slog.Debug("peer update for host", "hostId", host.ID.String())
peerIndexMap := make(map[string]int)
for _, nodeID := range host.Nodes {
diff --git a/logic/zombie.go b/logic/zombie.go
index d191aaa3d..2e1692edd 100644
--- a/logic/zombie.go
+++ b/logic/zombie.go
@@ -107,6 +107,7 @@ func ManageZombies(ctx context.Context, peerUpdate chan *models.Node) {
logger.Log(1, "error deleting zombie node", zombies[i].String(), err.Error())
continue
}
+ node.PendingDelete = true
node.Action = models.NODE_DELETE
peerUpdate <- &node
logger.Log(1, "deleting zombie node", node.ID.String())
@@ -120,14 +121,17 @@ func ManageZombies(ctx context.Context, peerUpdate chan *models.Node) {
host, err := GetHost(hostZombies[i].String())
if err != nil {
logger.Log(1, "error retrieving zombie host", err.Error())
- logger.Log(1, "deleting ", host.ID.String(), " from zombie list")
- zombies = append(zombies[:i], zombies[i+1:]...)
+ if host != nil {
+ logger.Log(1, "deleting ", host.ID.String(), " from zombie list")
+ }
+ hostZombies = append(hostZombies[:i], hostZombies[i+1:]...)
continue
}
if len(host.Nodes) == 0 {
if err := RemoveHost(host, true); err != nil {
logger.Log(0, "error deleting zombie host", host.ID.String(), err.Error())
}
+ hostZombies = append(hostZombies[:i], hostZombies[i+1:]...)
}
}
}
diff --git a/main.go b/main.go
index c93fcb495..63d946fb2 100644
--- a/main.go
+++ b/main.go
@@ -28,7 +28,8 @@ import (
"golang.org/x/exp/slog"
)
-var version = "v0.21.1"
+
+var version = "v0.21.2"
// Start DB Connection and start API Request Handler
func main() {
@@ -48,6 +49,7 @@ func main() {
defer stop()
var waitGroup sync.WaitGroup
startControllers(&waitGroup, ctx) // start the api endpoint and mq and stun
+ startHooks()
<-ctx.Done()
waitGroup.Wait()
}
@@ -63,6 +65,14 @@ func setupConfig(absoluteConfigPath string) {
}
}
+func startHooks() {
+ err := logic.TimerCheckpoint()
+ if err != nil {
+ logger.Log(1, "Timer error occurred: ", err.Error())
+ }
+ logic.EnterpriseCheck()
+}
+
func initialize() { // Client Mode Prereq Check
var err error
@@ -82,12 +92,6 @@ func initialize() { // Client Mode Prereq Check
logic.SetJWTSecret()
- err = logic.TimerCheckpoint()
- if err != nil {
- logger.Log(1, "Timer error occurred: ", err.Error())
- }
- logic.EnterpriseCheck()
-
var authProvider = auth.InitializeAuthProvider()
if authProvider != "" {
logger.Log(0, "OAuth provider,", authProvider+",", "initialized")
diff --git a/models/api_host.go b/models/api_host.go
index 7dd9fdf9e..8cfe79af7 100644
--- a/models/api_host.go
+++ b/models/api_host.go
@@ -32,6 +32,7 @@ type ApiHost struct {
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"`
+ AutoUpdate bool `json:"autoupdate" yaml:"autoupdate"`
}
// Host.ConvertNMHostToAPI - converts a Netmaker host to an API editable host
@@ -60,6 +61,7 @@ func (h *Host) ConvertNMHostToAPI() *ApiHost {
a.IsDefault = h.IsDefault
a.NatType = h.NatType
a.PersistentKeepalive = int(h.PersistentKeepalive.Seconds())
+ a.AutoUpdate = h.AutoUpdate
return &a
}
@@ -98,5 +100,6 @@ func (a *ApiHost) ConvertAPIHostToNMHost(currentHost *Host) *Host {
h.NatType = currentHost.NatType
h.TurnEndpoint = currentHost.TurnEndpoint
h.PersistentKeepalive = time.Duration(a.PersistentKeepalive) * time.Second
+ h.AutoUpdate = a.AutoUpdate
return &h
}
diff --git a/models/enrollment_key.go b/models/enrollment_key.go
index 581ff6a3a..982c5463b 100644
--- a/models/enrollment_key.go
+++ b/models/enrollment_key.go
@@ -2,6 +2,8 @@ package models
import (
"time"
+
+ "github.com/google/uuid"
)
const (
@@ -39,6 +41,7 @@ type EnrollmentKey struct {
Tags []string `json:"tags"`
Token string `json:"token,omitempty"` // B64 value of EnrollmentToken
Type KeyType `json:"type"`
+ Relay uuid.UUID `json:"relay"`
}
// APIEnrollmentKey - used to create enrollment keys via API
@@ -49,6 +52,7 @@ type APIEnrollmentKey struct {
Unlimited bool `json:"unlimited"`
Tags []string `json:"tags"`
Type KeyType `json:"type"`
+ Relay string `json:"relay"`
}
// RegisterResponse - the response to a successful enrollment register
diff --git a/models/host.go b/models/host.go
index 4a2715b50..de55c531c 100644
--- a/models/host.go
+++ b/models/host.go
@@ -160,4 +160,5 @@ type RegisterMsg struct {
User string `json:"user,omitempty"`
Password string `json:"password,omitempty"`
JoinAll bool `json:"join_all,omitempty"`
+ Relay string `json:"relay,omitempty"`
}
diff --git a/models/mqtt.go b/models/mqtt.go
index b50e8cd22..7c6072af7 100644
--- a/models/mqtt.go
+++ b/models/mqtt.go
@@ -8,18 +8,17 @@ import (
// HostPeerUpdate - struct for host peer updates
type HostPeerUpdate struct {
- Host Host `json:"host" bson:"host" yaml:"host"`
- NodeAddrs []net.IPNet `json:"nodes_addrs" yaml:"nodes_addrs"`
- Server string `json:"server" bson:"server" yaml:"server"`
- ServerVersion string `json:"serverversion" bson:"serverversion" yaml:"serverversion"`
- ServerAddrs []ServerAddr `json:"serveraddrs" bson:"serveraddrs" yaml:"serveraddrs"`
- NodePeers []wgtypes.PeerConfig `json:"peers" bson:"peers" yaml:"peers"`
- Peers []wgtypes.PeerConfig
- PeerIDs PeerMap `json:"peerids" bson:"peerids" yaml:"peerids"`
- EndpointDetection bool `json:"endpointdetection" yaml:"endpointdetection"`
- HostNetworkInfo HostInfoMap `json:"host_network_info,omitempty" bson:"host_network_info,omitempty" yaml:"host_network_info,omitempty"`
- EgressRoutes []EgressNetworkRoutes `json:"egress_network_routes"`
- FwUpdate FwUpdate `json:"fw_update"`
+ Host Host `json:"host" bson:"host" yaml:"host"`
+ NodeAddrs []net.IPNet `json:"nodes_addrs" yaml:"nodes_addrs"`
+ Server string `json:"server" bson:"server" yaml:"server"`
+ ServerVersion string `json:"serverversion" bson:"serverversion" yaml:"serverversion"`
+ ServerAddrs []ServerAddr `json:"serveraddrs" bson:"serveraddrs" yaml:"serveraddrs"`
+ NodePeers []wgtypes.PeerConfig `json:"peers" bson:"peers" yaml:"peers"`
+ Peers []wgtypes.PeerConfig
+ PeerIDs PeerMap `json:"peerids" bson:"peerids" yaml:"peerids"`
+ HostNetworkInfo HostInfoMap `json:"host_network_info,omitempty" bson:"host_network_info,omitempty" yaml:"host_network_info,omitempty"`
+ EgressRoutes []EgressNetworkRoutes `json:"egress_network_routes"`
+ FwUpdate FwUpdate `json:"fw_update"`
}
// IngressInfo - struct for ingress info
diff --git a/models/network.go b/models/network.go
index 89fb44d4d..c29b45d74 100644
--- a/models/network.go
+++ b/models/network.go
@@ -1,6 +1,7 @@
package models
import (
+ "net"
"time"
)
@@ -81,3 +82,18 @@ func (network *Network) SetDefaults() {
network.DefaultACL = "yes"
}
}
+
+func (network *Network) GetNetworkNetworkCIDR4() *net.IPNet {
+ if network.AddressRange == "" {
+ return nil
+ }
+ _, netCidr, _ := net.ParseCIDR(network.AddressRange)
+ return netCidr
+}
+func (network *Network) GetNetworkNetworkCIDR6() *net.IPNet {
+ if network.AddressRange6 == "" {
+ return nil
+ }
+ _, netCidr, _ := net.ParseCIDR(network.AddressRange6)
+ return netCidr
+}
diff --git a/models/structs.go b/models/structs.go
index c67d5eeea..906d16cf8 100644
--- a/models/structs.go
+++ b/models/structs.go
@@ -223,11 +223,12 @@ type TrafficKeys struct {
// HostPull - response of a host's pull
type HostPull struct {
- Host Host `json:"host" yaml:"host"`
- Nodes []Node `json:"nodes" yaml:"nodes"`
- Peers []wgtypes.PeerConfig `json:"peers" yaml:"peers"`
- ServerConfig ServerConfig `json:"server_config" yaml:"server_config"`
- PeerIDs PeerMap `json:"peer_ids,omitempty" yaml:"peer_ids,omitempty"`
+ Host Host `json:"host" yaml:"host"`
+ Nodes []Node `json:"nodes" yaml:"nodes"`
+ Peers []wgtypes.PeerConfig `json:"peers" yaml:"peers"`
+ ServerConfig ServerConfig `json:"server_config" yaml:"server_config"`
+ PeerIDs PeerMap `json:"peer_ids,omitempty" yaml:"peer_ids,omitempty"`
+ HostNetworkInfo HostInfoMap `json:"host_network_info,omitempty" yaml:"host_network_info,omitempty"`
}
// NodeGet - struct for a single node get response
diff --git a/mq/handlers.go b/mq/handlers.go
index 84ffc81e4..a6ab628e7 100644
--- a/mq/handlers.go
+++ b/mq/handlers.go
@@ -60,7 +60,21 @@ func UpdateNode(client mqtt.Client, msg mqtt.Message) {
return
}
if ifaceDelta { // reduce number of unneeded updates, by only sending on iface changes
- if err = PublishPeerUpdate(); err != nil {
+ if !newNode.Connected {
+ err = PublishDeletedNodePeerUpdate(&newNode)
+ host, err := logic.GetHost(newNode.HostID.String())
+ if err != nil {
+ slog.Error("failed to get host for the node", "nodeid", newNode.ID.String(), "error", err)
+ return
+ }
+ allNodes, err := logic.GetAllNodes()
+ if err == nil {
+ PublishSingleHostPeerUpdate(host, allNodes, nil, nil)
+ }
+ } else {
+ err = PublishPeerUpdate()
+ }
+ if err != nil {
slog.Warn("error updating peers when node informed the server of an interface change", "nodeid", currentNode.ID, "error", err)
}
}
@@ -116,7 +130,7 @@ func UpdateHost(client mqtt.Client, msg mqtt.Message) {
slog.Error("failed peers publish after join acknowledged", "name", hostUpdate.Host.Name, "id", currentHost.ID, "error", err)
return
}
- if err = handleNewNodeDNS(&hu.Host, &hu.Node); err != nil {
+ if err = HandleNewNodeDNS(&hu.Host, &hu.Node); err != nil {
slog.Error("failed to send dns update after node added to host", "name", hostUpdate.Host.Name, "id", currentHost.ID, "error", err)
return
}
@@ -159,6 +173,22 @@ func UpdateHost(client mqtt.Client, msg mqtt.Message) {
return
}
}
+
+ // notify of deleted peer change
+ go func(host models.Host) {
+ for _, nodeID := range host.Nodes {
+ node, err := logic.GetNodeByID(nodeID)
+ if err == nil {
+ var gwClients []models.ExtClient
+ if node.IsIngressGateway {
+ gwClients = logic.GetGwExtclients(node.ID.String(), node.Network)
+ }
+ go PublishMqUpdatesForDeletedNode(node, false, gwClients)
+ }
+
+ }
+ }(*currentHost)
+
if err := logic.DisassociateAllNodesFromHost(currentHost.ID.String()); err != nil {
slog.Error("failed to delete all nodes of host", "id", currentHost.ID, "error", err)
return
@@ -217,7 +247,7 @@ func ClientPeerUpdate(client mqtt.Client, msg mqtt.Message) {
slog.Info("sent peer updates after signal received from", "id", id)
}
-func handleNewNodeDNS(host *models.Host, node *models.Node) error {
+func HandleNewNodeDNS(host *models.Host, node *models.Node) error {
dns := models.DNSUpdate{
Action: models.DNSInsert,
Name: host.Name + "." + node.Network,
diff --git a/mq/publishers.go b/mq/publishers.go
index 83de5e63f..5ffef67be 100644
--- a/mq/publishers.go
+++ b/mq/publishers.go
@@ -10,6 +10,7 @@ import (
"github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/servercfg"
+ "golang.org/x/exp/slog"
)
// PublishPeerUpdate --- determines and publishes a peer update to all the hosts
@@ -95,10 +96,6 @@ func PublishSingleHostPeerUpdate(host *models.Host, allNodes []models.Node, dele
if err != nil {
return err
}
- if len(peerUpdate.Peers) == 0 { // no peers to send
- return nil
- }
-
data, err := json.Marshal(&peerUpdate)
if err != nil {
return err
@@ -169,6 +166,27 @@ func ServerStartNotify() error {
return nil
}
+// PublishDNSUpdatev1 - published dns updates to all nodes passed
+func PublishDNSUpdatev1(network string, dns models.DNSUpdate, nodes []models.Node) error {
+ for _, node := range nodes {
+ host, err := logic.GetHost(node.HostID.String())
+ if err != nil {
+ logger.Log(0, "error retrieving host for dns update", node.HostID.String(), err.Error())
+ continue
+ }
+ data, err := json.Marshal(dns)
+ if err != nil {
+ logger.Log(0, "failed to encode dns data for node", node.ID.String(), err.Error())
+ }
+ if err := publish(host, "dns/update/"+host.ID.String()+"/"+servercfg.GetServer(), data); err != nil {
+ logger.Log(0, "error publishing dns update to host", host.ID.String(), err.Error())
+ continue
+ }
+ logger.Log(3, "published dns update to host", host.ID.String())
+ }
+ return nil
+}
+
// PublishDNSUpdate publishes a dns update to all nodes on a network
func PublishDNSUpdate(network string, dns models.DNSUpdate) error {
nodes, err := logic.GetNetworkNodes(network)
@@ -215,6 +233,32 @@ func PublishAllDNS(newnode *models.Node) error {
return nil
}
+// PublishMqUpdatesForDeletedNode - published all the required updates for deleted node
+func PublishMqUpdatesForDeletedNode(node models.Node, sendNodeUpdate bool, gwClients []models.ExtClient) {
+ // notify of peer change
+ node.PendingDelete = true
+ node.Action = models.NODE_DELETE
+ if sendNodeUpdate {
+ if err := NodeUpdate(&node); err != nil {
+ slog.Error("error publishing node update to node", "node", node.ID, "error", err)
+ }
+ }
+ if err := PublishDeletedNodePeerUpdate(&node); err != nil {
+ logger.Log(1, "error publishing peer update ", err.Error())
+ }
+ host, err := logic.GetHost(node.HostID.String())
+ if err != nil {
+ logger.Log(1, "failed to retrieve host for node", node.ID.String(), err.Error())
+ }
+ if err := PublishDNSDelete(&node, host); err != nil {
+ logger.Log(1, "error publishing dns update", err.Error())
+ }
+ if err := PublishDeleteAllExtclientsDNS(node.Network, gwClients); err != nil {
+ logger.Log(1, "error publishing ext dns update", err.Error())
+ }
+
+}
+
// PublishDNSDelete publish a dns update deleting a node to all hosts on a network
func PublishDNSDelete(node *models.Node, host *models.Host) error {
dns := models.DNSUpdate{
@@ -299,6 +343,22 @@ func PublishExtClientDNSUpdate(old, new models.ExtClient, network string) error
return nil
}
+// PublishDeleteAllExtclientsDNS - publish to delete all passed ext clients dns entries
+func PublishDeleteAllExtclientsDNS(network string, clients []models.ExtClient) error {
+ nodes, err := logic.GetNetworkNodes(network)
+ if err != nil {
+ return err
+ }
+ for _, client := range clients {
+ dns := models.DNSUpdate{
+ Action: models.DNSDeleteByName,
+ Name: client.ClientID + "." + client.Network,
+ }
+ go PublishDNSUpdatev1(client.Network, dns, nodes)
+ }
+ return nil
+}
+
// PublishDeleteExtClientDNS publish dns update to delete extclient entry
func PublishDeleteExtClientDNS(client *models.ExtClient) error {
dns := models.DNSUpdate{
diff --git a/mq/util.go b/mq/util.go
index 8d7819935..4bdc4194a 100644
--- a/mq/util.go
+++ b/mq/util.go
@@ -34,7 +34,7 @@ func decryptMsgWithHost(host *models.Host, msg []byte) ([]byte, error) {
func DecryptMsg(node *models.Node, msg []byte) ([]byte, error) {
if len(msg) <= 24 { // make sure message is of appropriate length
- return nil, fmt.Errorf("recieved invalid message from broker %v", msg)
+ return nil, fmt.Errorf("received invalid message from broker %v", msg)
}
host, err := logic.GetHost(node.HostID.String())
if err != nil {
diff --git a/pro/license.go b/pro/license.go
index c76505348..52d373809 100644
--- a/pro/license.go
+++ b/pro/license.go
@@ -213,28 +213,43 @@ func validateLicenseKey(encryptedData []byte, publicKey *[32]byte) ([]byte, erro
req.Header.Add("Content-Type", "application/json")
req.Header.Add("Accept", "application/json")
client := &http.Client{}
- var body []byte
validateResponse, err := client.Do(req)
if err != nil { // check cache
- body, err = getCachedResponse()
- if err != nil {
- return nil, err
- }
slog.Warn("proceeding with cached response, Netmaker API may be down")
- } else {
- defer validateResponse.Body.Close()
- if validateResponse.StatusCode != 200 {
- return nil, fmt.Errorf("could not validate license, got status code %d", validateResponse.StatusCode)
- } // if you received a 200 cache the response locally
+ return getCachedResponse()
+ }
+ defer validateResponse.Body.Close()
+ code := validateResponse.StatusCode
- body, err = io.ReadAll(validateResponse.Body)
+ // if we received a 200, cache the response locally
+ if code == http.StatusOK {
+ body, err := io.ReadAll(validateResponse.Body)
if err != nil {
+ slog.Warn("failed to parse response", "error", err)
return nil, err
}
- cacheResponse(body)
+ if err := cacheResponse(body); err != nil {
+ slog.Warn("failed to cache response", "error", err)
+ }
+ return body, nil
+ }
+
+ // at this point the backend returned some undesired state
+
+ // inform failure via logs
+ body, _ := io.ReadAll(validateResponse.Body)
+ err = fmt.Errorf("could not validate license with validation backend (status={%d}, body={%s})",
+ validateResponse.StatusCode, string(body))
+ slog.Warn(err.Error())
+
+ // try to use cache if we had a temporary error
+ if code == http.StatusServiceUnavailable || code == http.StatusGatewayTimeout {
+ slog.Warn("proceeding with cached response, Netmaker API may be down")
+ return getCachedResponse()
}
- return body, err
+ // at this point the error is irreversible, return it
+ return nil, err
}
func getAccountsHost() string {
diff --git a/pro/logic/relays.go b/pro/logic/relays.go
index 9cb5e836d..fb0660c10 100644
--- a/pro/logic/relays.go
+++ b/pro/logic/relays.go
@@ -149,6 +149,7 @@ func RelayUpdates(currentNode, newNode *models.Node) bool {
return relayUpdates
}
+// UpdateRelayed - updates a relay's relayed nodes, and sends updates to the relayed nodes over MQ
func UpdateRelayed(currentNode, newNode *models.Node) {
updatenodes := updateRelayNodes(currentNode.ID.String(), currentNode.RelayedNodes, newNode.RelayedNodes)
if len(updatenodes) > 0 {
diff --git a/release.md b/release.md
index 1f4e1b7b5..4df6f364d 100644
--- a/release.md
+++ b/release.md
@@ -1,20 +1,19 @@
-# Netmaker v0.21.1
+# Netmaker v0.21.2
## Whats New
-- Remote access client session management, refer users section in docs for more details
-- Can now create generic DNS entries
-- Upgrade client version to match server version from UI
-- Moved PersistentKeepAlive setting from node to host level
+- Auto Relay via Enrollment key
+- Local Routing Improvements
## What's Fixed
-- Extclients DNS now properly set from ingress dns value provided
-- Allow role update of OAuth user
-- Fixed zombie node issue
+- Inconsistency in DNS Entries for Network has been fixed
+- Unique network CIDR validation
+- Fixed extclient caching decrepancies
+- Deleted node peer update fixes when disconnected from network
+- Force Deletion of Daemon Nodes that are stuck in removing state
## known issues
- Windows installer does not install WireGuard
- netclient-gui will continously display error dialog if netmaker server is offline
- Mac IPv6 addresses/route issues
-- Docker client can not re-join after complete deletion
- netclient-gui network tab blank after disconnect
diff --git a/scripts/netmaker.default.env b/scripts/netmaker.default.env
index fcacaeaae..247791d47 100644
--- a/scripts/netmaker.default.env
+++ b/scripts/netmaker.default.env
@@ -19,7 +19,6 @@ NETMAKER_TENANT_ID=
LICENSE_KEY=
SERVER_IMAGE_TAG=
UI_IMAGE_TAG=
-NETCLIENT_ENDPOINT_DETECTION=disabled
# used for HA - identifies this server vs other servers
NODE_ID=netmaker-server-1
METRICS_EXPORTER=off
diff --git a/scripts/nm-quick.sh b/scripts/nm-quick.sh
index 714bb360c..7e24696ed 100755
--- a/scripts/nm-quick.sh
+++ b/scripts/nm-quick.sh
@@ -309,7 +309,7 @@ save_config() { (
"INSTALL_TYPE" "NODE_ID" "DNS_MODE" "NETCLIENT_AUTO_UPDATE" "API_PORT"
"CORS_ALLOWED_ORIGIN" "DISPLAY_KEYS" "DATABASE" "SERVER_BROKER_ENDPOINT" "STUN_PORT" "VERBOSITY"
"TURN_PORT" "USE_TURN" "DEBUG_MODE" "TURN_API_PORT" "REST_BACKEND"
- "DISABLE_REMOTE_IP_CHECK" "NETCLIENT_ENDPOINT_DETECTION" "TELEMETRY" "AUTH_PROVIDER" "CLIENT_ID" "CLIENT_SECRET"
+ "DISABLE_REMOTE_IP_CHECK" "TELEMETRY" "AUTH_PROVIDER" "CLIENT_ID" "CLIENT_SECRET"
"FRONTEND_URL" "AZURE_TENANT" "OIDC_ISSUER" "EXPORTER_API_PORT" "JWT_VALIDITY_DURATION" "RAC_AUTO_DISABLE")
for name in "${toCopy[@]}"; do
save_config_item $name "${!name}"
@@ -370,7 +370,6 @@ local_install_setup() { (
else
cp docker/Caddyfile "$SCRIPT_DIR/Caddyfile"
fi
- cp scripts/nm-certs.sh "$SCRIPT_DIR/nm-certs.sh"
cp scripts/netmaker.default.env "$SCRIPT_DIR/netmaker.default.env"
cp docker/mosquitto.conf "$SCRIPT_DIR/mosquitto.conf"
cp docker/wait.sh "$SCRIPT_DIR/wait.sh"
@@ -776,8 +775,9 @@ install_netmaker() {
export COMPOSE_HTTP_TIMEOUT=120
# start docker and rebuild containers / networks
- docker-compose -f "$SCRIPT_DIR"/docker-compose.yml up -d --force-recreate
-
+ cd "${SCRIPT_DIR}"
+ docker-compose up -d --force-recreate
+ cd -
wait_seconds 2
}
diff --git a/scripts/nm-upgrade-0-17-1-to-0-19-0.sh b/scripts/nm-upgrade-0-17-1-to-0-19-0.sh
index 79a78bd5a..b2f090883 100644
--- a/scripts/nm-upgrade-0-17-1-to-0-19-0.sh
+++ b/scripts/nm-upgrade-0-17-1-to-0-19-0.sh
@@ -1,6 +1,6 @@
#!/bin/bash
-LATEST="v0.21.1"
+LATEST="v0.21.2"
INSTALL_PATH="/root"
trap restore_old_netmaker_instructions
diff --git a/scripts/nm-upgrade.sh b/scripts/nm-upgrade.sh
index 3a62bc4f6..8dcb7bcdc 100755
--- a/scripts/nm-upgrade.sh
+++ b/scripts/nm-upgrade.sh
@@ -179,7 +179,7 @@ save_config() { (
"INSTALL_TYPE" "NODE_ID" "DNS_MODE" "NETCLIENT_AUTO_UPDATE" "API_PORT"
"CORS_ALLOWED_ORIGIN" "DISPLAY_KEYS" "DATABASE" "SERVER_BROKER_ENDPOINT" "STUN_PORT" "VERBOSITY"
"TURN_PORT" "USE_TURN" "DEBUG_MODE" "TURN_API_PORT" "REST_BACKEND"
- "DISABLE_REMOTE_IP_CHECK" "NETCLIENT_ENDPOINT_DETECTION" "TELEMETRY" "AUTH_PROVIDER" "CLIENT_ID" "CLIENT_SECRET"
+ "DISABLE_REMOTE_IP_CHECK" "TELEMETRY" "AUTH_PROVIDER" "CLIENT_ID" "CLIENT_SECRET"
"FRONTEND_URL" "AZURE_TENANT" "OIDC_ISSUER" "EXPORTER_API_PORT" "JWT_VALIDITY_DURATION" "RAC_AUTO_DISABLE")
for name in "${toCopy[@]}"; do
save_config_item $name "${!name}"
diff --git a/servercfg/serverconf.go b/servercfg/serverconf.go
index a7347aeb2..7bac8ede3 100644
--- a/servercfg/serverconf.go
+++ b/servercfg/serverconf.go
@@ -53,11 +53,6 @@ func GetServerConfig() config.ServerConfig {
} else {
cfg.NetclientAutoUpdate = "disabled"
}
- if EndpointDetectionEnabled() {
- cfg.NetclientEndpointDetection = "enabled"
- } else {
- cfg.NetclientEndpointDetection = "disabled"
- }
if IsRestBackend() {
cfg.RestBackend = "on"
}
@@ -405,17 +400,6 @@ func AutoUpdateEnabled() bool {
return true
}
-// EndpointDetectionEnabled returns a boolean indicating whether netclient endpoint detection is enabled or disabled
-// default is enabled
-func EndpointDetectionEnabled() bool {
- if os.Getenv("NETCLIENT_ENDPOINT_DETECTION") == "disabled" {
- return false
- } else if config.Config.Server.NetclientEndpointDetection == "disabled" {
- return false
- }
- return true
-}
-
// IsDNSMode - should it run with DNS
func IsDNSMode() bool {
isdns := true
diff --git a/swagger.yml b/swagger.yml
index 2637cdb10..01cbf17d4 100644
--- a/swagger.yml
+++ b/swagger.yml
@@ -1149,7 +1149,7 @@ info:
API calls must be authenticated via a header of the format -H “Authorization: Bearer ” There are two methods to obtain YOUR_SECRET_KEY: 1. Using the masterkey. By default, this value is “secret key,” but you should change this on your instance and keep it secure. This value can be set via env var at startup or in a config file (config/environments/< env >.yaml). See the [Netmaker](https://docs.netmaker.org/index.html) documentation for more details. 2. Using a JWT received for a node. This can be retrieved by calling the /api/nodes//authenticate endpoint, as documented below.
title: Netmaker
- version: 0.21.1
+ version: 0.21.2
paths:
/api/dns:
get: