Merge branch 'develop' into master

This commit is contained in:
Abhishek K 2025-02-19 01:14:06 +04:00 committed by GitHub
commit ee6bea117f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
46 changed files with 1215 additions and 727 deletions

View file

@ -12,7 +12,7 @@ jobs:
if: ${{ github.event.workflow_run.conclusion == 'success' }}
steps:
- name: get logs
uses: dawidd6/action-download-artifact@v7
uses: dawidd6/action-download-artifact@v8
with:
run_id: ${{ github.event.workflow_run.id}}
if_no_artifact_found: warn
@ -75,7 +75,7 @@ jobs:
if: ${{ github.event.workflow_run.conclusion == 'failure' }}
steps:
- name: get logs
uses: dawidd6/action-download-artifact@v7
uses: dawidd6/action-download-artifact@v8
with:
run_id: ${{ github.event.workflow_run.id}}
if_no_artifact_found: warn

View file

@ -6,11 +6,13 @@ COPY . .
RUN GOOS=linux CGO_ENABLED=1 go build -ldflags="-s -w " -tags ${tags} .
# RUN go build -tags=ee . -o netmaker main.go
FROM alpine:3.21.0
FROM alpine:3.21.2
# add a c lib
# set the working directory
WORKDIR /root/
RUN apk update && apk upgrade
RUN apk add --no-cache sqlite
RUN mkdir -p /etc/netclient/config
COPY --from=builder /app/netmaker .
COPY --from=builder /app/config config

View file

@ -1,5 +1,5 @@
#first stage - builder
FROM alpine:3.21.0
FROM alpine:3.21.2
ARG version
WORKDIR /app
COPY ./netmaker /root/netmaker

View file

@ -52,8 +52,8 @@ services:
- caddy_data:/data
- caddy_conf:/config
ports:
- "80:80"
- "443:443"
- "80:80/tcp"
- "443:443/tcp"
coredns:
#network_mode: host

View file

@ -37,7 +37,7 @@ type ServerConfig struct {
APIConnString string `yaml:"apiconn"`
APIHost string `yaml:"apihost"`
APIPort string `yaml:"apiport"`
Broker string `yam:"broker"`
Broker string `yaml:"broker"`
ServerBrokerEndpoint string `yaml:"serverbrokerendpoint"`
BrokerType string `yaml:"brokertype"`
EmqxRestEndpoint string `yaml:"emqxrestendpoint"`
@ -92,14 +92,15 @@ type ServerConfig struct {
JwtValidityDuration time.Duration `yaml:"jwt_validity_duration" swaggertype:"primitive,integer" format:"int64"`
RacAutoDisable bool `yaml:"rac_auto_disable"`
CacheEnabled string `yaml:"caching_enabled"`
EndpointDetection bool `json:"endpoint_detection"`
EndpointDetection bool `yaml:"endpoint_detection"`
AllowedEmailDomains string `yaml:"allowed_email_domains"`
EmailSenderAddr string `json:"email_sender_addr"`
EmailSenderUser string `json:"email_sender_user"`
EmailSenderPassword string `json:"email_sender_password"`
SmtpHost string `json:"smtp_host"`
SmtpPort int `json:"smtp_port"`
EmailSenderAddr string `yaml:"email_sender_addr"`
EmailSenderUser string `yaml:"email_sender_user"`
EmailSenderPassword string `yaml:"email_sender_password"`
SmtpHost string `yaml:"smtp_host"`
SmtpPort int `yaml:"smtp_port"`
MetricInterval string `yaml:"metric_interval"`
MetricsPort int `yaml:"metrics_port"`
ManageDNS bool `yaml:"manage_dns"`
Stun bool `yaml:"stun"`
StunServers string `yaml:"stun_servers"`

View file

@ -24,6 +24,7 @@ var HttpMiddlewares = []mux.MiddlewareFunc{
// HttpHandlers - handler functions for REST interactions
var HttpHandlers = []interface{}{
nodeHandlers,
gwHandlers,
userHandlers,
networkHandlers,
dnsHandlers,

207
controllers/gateway.go Normal file
View file

@ -0,0 +1,207 @@
package controller
import (
"encoding/json"
"fmt"
"net/http"
"github.com/google/uuid"
"github.com/gorilla/mux"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/mq"
"github.com/gravitl/netmaker/servercfg"
"golang.org/x/exp/slog"
)
func gwHandlers(r *mux.Router) {
r.HandleFunc("/api/nodes/{network}/{nodeid}/gateway", logic.SecurityCheck(true, checkFreeTierLimits(limitChoiceIngress, http.HandlerFunc(createGateway)))).Methods(http.MethodPost)
r.HandleFunc("/api/nodes/{network}/{nodeid}/gateway", logic.SecurityCheck(true, http.HandlerFunc(deleteGateway))).Methods(http.MethodDelete)
// old relay handlers
r.HandleFunc("/api/nodes/{network}/{nodeid}/createrelay", logic.SecurityCheck(true, http.HandlerFunc(createGateway))).Methods(http.MethodPost)
r.HandleFunc("/api/nodes/{network}/{nodeid}/deleterelay", logic.SecurityCheck(true, http.HandlerFunc(deleteGateway))).Methods(http.MethodDelete)
}
// @Summary Create a gateway
// @Router /api/nodes/{network}/{nodeid}/gateway [post]
// @Tags Nodes
// @Security oauth2
// @Success 200 {object} models.ApiNode
// @Failure 500 {object} models.ErrorResponse
func createGateway(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var params = mux.Vars(r)
nodeid := params["nodeid"]
netid := params["network"]
node, err := logic.ValidateParams(nodeid, netid)
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
var req models.CreateGwReq
err = json.NewDecoder(r.Body).Decode(&req)
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
node, err = logic.CreateIngressGateway(netid, nodeid, req.IngressRequest)
if err != nil {
logger.Log(0, r.Header.Get("user"),
fmt.Sprintf("failed to create gateway on node [%s] on network [%s]: %v",
nodeid, netid, err))
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
req.RelayRequest.NetID = netid
req.RelayRequest.NodeID = nodeid
_, relayNode, err := logic.CreateRelay(req.RelayRequest)
if err != nil {
logger.Log(
0,
r.Header.Get("user"),
fmt.Sprintf(
"failed to create relay on node [%s] on network [%s]: %v",
req.RelayRequest.NodeID,
req.RelayRequest.NetID,
err,
),
)
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
for _, relayedNodeID := range relayNode.RelayedNodes {
relayedNode, err := logic.GetNodeByID(relayedNodeID)
if err == nil {
if relayedNode.FailedOverBy != uuid.Nil {
go logic.ResetFailedOverPeer(&relayedNode)
}
}
}
logger.Log(
1,
r.Header.Get("user"),
"created gw node",
req.RelayRequest.NodeID,
"on network",
req.RelayRequest.NetID,
)
apiNode := relayNode.ConvertToAPINode()
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(apiNode)
go func() {
if err := mq.NodeUpdate(&node); err != nil {
slog.Error("error publishing node update to node", "node", node.ID, "error", err)
}
mq.PublishPeerUpdate(false)
}()
}
// @Summary Delete a gateway
// @Router /api/nodes/{network}/{nodeid}/gateway [delete]
// @Tags Nodes
// @Security oauth2
// @Success 200 {object} models.ApiNode
// @Failure 500 {object} models.ErrorResponse
func deleteGateway(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var params = mux.Vars(r)
nodeid := params["nodeid"]
netid := params["network"]
node, err := logic.ValidateParams(nodeid, netid)
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
node, removedClients, err := logic.DeleteIngressGateway(nodeid)
if err != nil {
logger.Log(0, r.Header.Get("user"),
fmt.Sprintf("failed to delete ingress gateway on node [%s] on network [%s]: %v",
nodeid, netid, err))
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
updateNodes, node, err := logic.DeleteRelay(netid, nodeid)
if err != nil {
logger.Log(0, r.Header.Get("user"), "error decoding request body: ", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
node, err = logic.GetNodeByID(node.ID.String())
if err != nil {
logger.Log(0, r.Header.Get("user"), "failed to get node", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
node.IsGw = false
logic.UpsertNode(&node)
logger.Log(1, r.Header.Get("user"), "deleted gw", nodeid, "on network", netid)
go func() {
host, err := logic.GetHost(node.HostID.String())
if err == nil {
allNodes, err := logic.GetAllNodes()
if err != nil {
return
}
for _, relayedNode := range updateNodes {
err = mq.NodeUpdate(&relayedNode)
if err != nil {
logger.Log(
1,
"relayed node update ",
relayedNode.ID.String(),
"on network",
relayedNode.Network,
": ",
err.Error(),
)
}
h, err := logic.GetHost(relayedNode.HostID.String())
if err == nil {
if h.OS == models.OS_Types.IoT {
nodes, err := logic.GetAllNodes()
if err != nil {
return
}
node.IsRelay = true // for iot update to recognise that it has to delete relay peer
if err = mq.PublishSingleHostPeerUpdate(h, nodes, &node, nil, false, nil); err != nil {
logger.Log(1, "failed to publish peer update to host", h.ID.String(), ": ", err.Error())
}
}
}
}
if len(removedClients) > 0 {
if err := mq.PublishSingleHostPeerUpdate(host, allNodes, nil, removedClients[:], false, nil); err != nil {
slog.Error("publishSingleHostUpdate", "host", host.Name, "error", err)
}
}
mq.PublishPeerUpdate(false)
if err := mq.NodeUpdate(&node); err != nil {
slog.Error(
"error publishing node update to node",
"node",
node.ID,
"error",
err,
)
}
if servercfg.IsDNSMode() {
logic.SetDNS()
}
}
}()
apiNode := node.ConvertToAPINode()
logger.Log(1, r.Header.Get("user"), "deleted ingress gateway", nodeid)
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(apiNode)
}

View file

@ -5,7 +5,7 @@ import (
"errors"
"fmt"
"net/http"
"os"
"time"
"github.com/google/uuid"
"github.com/gorilla/mux"
@ -24,6 +24,10 @@ func hostHandlers(r *mux.Router) {
Methods(http.MethodGet)
r.HandleFunc("/api/hosts/keys", logic.SecurityCheck(true, http.HandlerFunc(updateAllKeys))).
Methods(http.MethodPut)
r.HandleFunc("/api/hosts/sync", logic.SecurityCheck(true, http.HandlerFunc(syncHosts))).
Methods(http.MethodPost)
r.HandleFunc("/api/hosts/upgrade", logic.SecurityCheck(true, http.HandlerFunc(upgradeHosts))).
Methods(http.MethodPost)
r.HandleFunc("/api/hosts/{hostid}/keys", logic.SecurityCheck(true, http.HandlerFunc(updateKeys))).
Methods(http.MethodPut)
r.HandleFunc("/api/hosts/{hostid}/sync", logic.SecurityCheck(true, http.HandlerFunc(syncHost))).
@ -45,16 +49,64 @@ func hostHandlers(r *mux.Router) {
Methods(http.MethodPost)
r.HandleFunc("/api/v1/fallback/host/{hostid}", Authorize(true, false, "host", http.HandlerFunc(hostUpdateFallback))).
Methods(http.MethodPut)
r.HandleFunc("/api/v1/host/{hostid}/peer_info", Authorize(true, false, "host", http.HandlerFunc(getHostPeerInfo))).
Methods(http.MethodGet)
r.HandleFunc("/api/emqx/hosts", logic.SecurityCheck(true, http.HandlerFunc(delEmqxHosts))).
Methods(http.MethodDelete)
r.HandleFunc("/api/v1/auth-register/host", socketHandler)
}
// @Summary Requests all the hosts to upgrade their version
// @Router /api/hosts/upgrade [post]
// @Tags Hosts
// @Security oauth
// @Param force query bool false "Force upgrade"
// @Success 200 {string} string "upgrade all hosts request received"
func upgradeHosts(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
action := models.Upgrade
if r.URL.Query().Get("force") == "true" {
action = models.ForceUpgrade
}
user := r.Header.Get("user")
go func() {
slog.Info("requesting all hosts to upgrade", "user", user)
hosts, err := logic.GetAllHosts()
if err != nil {
slog.Error("failed to retrieve all hosts", "user", user, "error", err)
return
}
for _, host := range hosts {
go func(host models.Host) {
hostUpdate := models.HostUpdate{
Action: action,
Host: host,
}
if err = mq.HostUpdate(&hostUpdate); err != nil {
slog.Error("failed to request host to upgrade", "user", user, "host", host.ID.String(), "error", err)
} else {
slog.Info("host upgrade requested", "user", user, "host", host.ID.String())
}
}(host)
}
}()
slog.Info("upgrade all hosts request received", "user", user)
logic.ReturnSuccessResponse(w, r, "upgrade all hosts request received")
}
// @Summary Upgrade a host
// @Router /api/hosts/{hostid}/upgrade [put]
// @Tags Hosts
// @Security oauth
// @Param hostid path string true "Host ID"
// @Param force query bool false "Force upgrade"
// @Success 200 {string} string "passed message to upgrade host"
// @Failure 500 {object} models.ErrorResponse
// upgrade host is a handler to send upgrade message to a host
@ -65,7 +117,14 @@ func upgradeHost(w http.ResponseWriter, r *http.Request) {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "notfound"))
return
}
if err := mq.HostUpdate(&models.HostUpdate{Action: models.Upgrade, Host: *host}); err != nil {
action := models.Upgrade
if r.URL.Query().Get("force") == "true" {
action = models.ForceUpgrade
}
if err := mq.HostUpdate(&models.HostUpdate{Action: action, Host: *host}); err != nil {
slog.Error("failed to upgrade host", "error", err)
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
@ -176,17 +235,13 @@ func pull(w http.ResponseWriter, r *http.Request) {
slog.Error("failed to get node:", "id", node.ID, "error", err)
continue
}
if node.FailedOverBy != uuid.Nil {
if node.FailedOverBy != uuid.Nil && r.URL.Query().Get("reset_failovered") == "true" {
logic.ResetFailedOverPeer(&node)
sendPeerUpdate = true
}
}
if sendPeerUpdate {
reset := true
if os.Getenv("RESET_PEER_UPDATE") != "" {
reset = os.Getenv("RESET_PEER_UPDATE") == "true"
}
if err := mq.PublishPeerUpdate(reset); err != nil {
if err := mq.PublishPeerUpdate(false); err != nil {
logger.Log(0, "fail to publish peer update: ", err.Error())
}
}
@ -320,7 +375,7 @@ func hostUpdateFallback(w http.ResponseWriter, r *http.Request) {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
slog.Info("recieved host update", "name", hostUpdate.Host.Name, "id", hostUpdate.Host.ID)
slog.Info("recieved host update", "name", hostUpdate.Host.Name, "id", hostUpdate.Host.ID, "action", hostUpdate.Action)
switch hostUpdate.Action {
case models.CheckIn:
sendPeerUpdate = mq.HandleHostCheckin(&hostUpdate.Host, currentHost)
@ -537,7 +592,7 @@ func deleteHostFromNetwork(w http.ResponseWriter, r *http.Request) {
w,
r,
logic.FormatError(
fmt.Errorf("failed to force delete daemon node: "+err.Error()),
fmt.Errorf("failed to force delete daemon node: %s", err.Error()),
"internal",
),
)
@ -577,7 +632,7 @@ func deleteHostFromNetwork(w http.ResponseWriter, r *http.Request) {
w,
r,
logic.FormatError(
fmt.Errorf("failed to force delete daemon node: "+err.Error()),
fmt.Errorf("failed to force delete daemon node: %s", err.Error()),
"internal",
),
)
@ -860,6 +915,45 @@ func updateKeys(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
}
// @Summary Requests all the hosts to pull
// @Router /api/hosts/sync [post]
// @Tags Hosts
// @Security oauth
// @Success 200 {string} string "sync all hosts request received"
func syncHosts(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
user := r.Header.Get("user")
go func() {
slog.Info("requesting all hosts to sync", "user", user)
hosts, err := logic.GetAllHosts()
if err != nil {
slog.Error("failed to retrieve all hosts", "user", user, "error", err)
return
}
for _, host := range hosts {
go func(host models.Host) {
hostUpdate := models.HostUpdate{
Action: models.RequestPull,
Host: host,
}
if err = mq.HostUpdate(&hostUpdate); err != nil {
slog.Error("failed to request host to sync", "user", user, "host", host.ID.String(), "error", err)
} else {
slog.Info("host sync requested", "user", user, "host", host.ID.String())
}
}(host)
time.Sleep(time.Millisecond * 100)
}
}()
slog.Info("sync all hosts request received", "user", user)
logic.ReturnSuccessResponse(w, r, "sync all hosts request received")
}
// @Summary Requests a host to pull
// @Router /api/hosts/{hostid}/sync [post]
// @Tags Hosts
@ -892,7 +986,7 @@ func syncHost(w http.ResponseWriter, r *http.Request) {
}
}()
slog.Info("requested host pull", "user", r.Header.Get("user"), "host", host.ID)
slog.Info("requested host pull", "user", r.Header.Get("user"), "host", host.ID.String())
w.WriteHeader(http.StatusOK)
}
@ -927,3 +1021,33 @@ func delEmqxHosts(w http.ResponseWriter, r *http.Request) {
}
logic.ReturnSuccessResponse(w, r, "deleted hosts data on emqx")
}
// @Summary Fetches host peerinfo
// @Router /api/host/{hostid}/peer_info [get]
// @Tags Hosts
// @Security oauth
// @Param hostid path string true "Host ID"
// @Success 200 {object} models.SuccessResponse
// @Failure 500 {object} models.ErrorResponse
func getHostPeerInfo(w http.ResponseWriter, r *http.Request) {
hostId := mux.Vars(r)["hostid"]
var errorResponse = models.ErrorResponse{}
host, err := logic.GetHost(hostId)
if err != nil {
slog.Error("failed to retrieve host", "error", err)
errorResponse.Code = http.StatusBadRequest
errorResponse.Message = err.Error()
logic.ReturnErrorResponse(w, r, errorResponse)
return
}
peerInfo, err := logic.GetHostPeerInfo(host)
if err != nil {
slog.Error("failed to retrieve host peerinfo", "error", err)
errorResponse.Code = http.StatusBadRequest
errorResponse.Message = err.Error()
logic.ReturnErrorResponse(w, r, errorResponse)
return
}
logic.ReturnSuccessResponseWithJson(w, r, peerInfo, "fetched host peer info")
}

View file

@ -434,6 +434,7 @@ func getNetworkACL(w http.ResponseWriter, r *http.Request) {
// @Tags Networks
// @Security oauth
// @Param networkname path string true "Network name"
// @Param force query bool false "Force Delete"
// @Produce json
// @Success 200 {object} models.SuccessResponse
// @Failure 400 {object} models.ErrorResponse
@ -441,10 +442,18 @@ func getNetworkACL(w http.ResponseWriter, r *http.Request) {
func deleteNetwork(w http.ResponseWriter, r *http.Request) {
// Set header
w.Header().Set("Content-Type", "application/json")
force := r.URL.Query().Get("force") == "true"
var params = mux.Vars(r)
network := params["networkname"]
err := logic.DeleteNetwork(network)
doneCh := make(chan struct{}, 1)
networkNodes, err := logic.GetNetworkNodes(network)
if err != nil {
logger.Log(0, r.Header.Get("user"),
fmt.Sprintf("failed to get network nodes [%s]: %v", network, err))
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
err = logic.DeleteNetwork(network, force, doneCh)
if err != nil {
errtype := "badrequest"
if strings.Contains(err.Error(), "Node check failed") {
@ -459,7 +468,22 @@ func deleteNetwork(w http.ResponseWriter, r *http.Request) {
go logic.DeleteDefaultNetworkPolicies(models.NetworkID(network))
//delete network from allocated ip map
go logic.RemoveNetworkFromAllocatedIpMap(network)
go func() {
<-doneCh
mq.PublishPeerUpdate(true)
// send node update to clean up locally
for _, node := range networkNodes {
node := node
node.PendingDelete = true
node.Action = models.NODE_DELETE
if err := mq.NodeUpdate(&node); err != nil {
slog.Error("error publishing node update to node", "node", node.ID, "error", err)
}
}
if servercfg.IsDNSMode() {
logic.SetDNS()
}
}()
logger.Log(1, r.Header.Get("user"), "deleted network", network)
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode("success")

View file

@ -75,11 +75,19 @@ func TestDeleteNetwork(t *testing.T) {
t.Run("NetworkwithNodes", func(t *testing.T) {
})
t.Run("DeleteExistingNetwork", func(t *testing.T) {
err := logic.DeleteNetwork("skynet")
doneCh := make(chan struct{}, 1)
err := logic.DeleteNetwork("skynet", false, doneCh)
assert.Nil(t, err)
})
t.Run("NonExistentNetwork", func(t *testing.T) {
err := logic.DeleteNetwork("skynet")
doneCh := make(chan struct{}, 1)
err := logic.DeleteNetwork("skynet", false, doneCh)
assert.Nil(t, err)
})
createNetv1("test")
t.Run("ForceDeleteNetwork", func(t *testing.T) {
doneCh := make(chan struct{}, 1)
err := logic.DeleteNetwork("test", true, doneCh)
assert.Nil(t, err)
})
}
@ -214,6 +222,15 @@ func createNet() {
logic.CreateNetwork(network)
}
}
func createNetv1(netId string) {
var network models.Network
network.NetID = netId
network.AddressRange = "100.0.0.1/24"
_, err := logic.GetNetwork(netId)
if err != nil {
logic.CreateNetwork(network)
}
}
func createNetDualStack() {
var network models.Network

View file

@ -28,8 +28,8 @@ func nodeHandlers(r *mux.Router) {
r.HandleFunc("/api/nodes/{network}/{nodeid}", Authorize(true, true, "node", http.HandlerFunc(deleteNode))).Methods(http.MethodDelete)
r.HandleFunc("/api/nodes/{network}/{nodeid}/creategateway", logic.SecurityCheck(true, checkFreeTierLimits(limitChoiceEgress, http.HandlerFunc(createEgressGateway)))).Methods(http.MethodPost)
r.HandleFunc("/api/nodes/{network}/{nodeid}/deletegateway", logic.SecurityCheck(true, http.HandlerFunc(deleteEgressGateway))).Methods(http.MethodDelete)
r.HandleFunc("/api/nodes/{network}/{nodeid}/createingress", logic.SecurityCheck(true, checkFreeTierLimits(limitChoiceIngress, http.HandlerFunc(createIngressGateway)))).Methods(http.MethodPost)
r.HandleFunc("/api/nodes/{network}/{nodeid}/deleteingress", logic.SecurityCheck(true, http.HandlerFunc(deleteIngressGateway))).Methods(http.MethodDelete)
r.HandleFunc("/api/nodes/{network}/{nodeid}/createingress", logic.SecurityCheck(true, checkFreeTierLimits(limitChoiceIngress, http.HandlerFunc(createGateway)))).Methods(http.MethodPost)
r.HandleFunc("/api/nodes/{network}/{nodeid}/deleteingress", logic.SecurityCheck(true, http.HandlerFunc(deleteGateway))).Methods(http.MethodDelete)
r.HandleFunc("/api/nodes/adm/{network}/authenticate", authenticate).Methods(http.MethodPost)
r.HandleFunc("/api/v1/nodes/migrate", migrate).Methods(http.MethodPost)
}
@ -548,113 +548,6 @@ func deleteEgressGateway(w http.ResponseWriter, r *http.Request) {
}()
}
// == INGRESS ==
// @Summary Create an remote access gateway
// @Router /api/nodes/{network}/{nodeid}/createingress [post]
// @Tags Nodes
// @Security oauth2
// @Success 200 {object} models.ApiNode
// @Failure 500 {object} models.ErrorResponse
func createIngressGateway(w http.ResponseWriter, r *http.Request) {
var params = mux.Vars(r)
w.Header().Set("Content-Type", "application/json")
nodeid := params["nodeid"]
netid := params["network"]
node, err := logic.ValidateParams(nodeid, netid)
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
var request models.IngressRequest
json.NewDecoder(r.Body).Decode(&request)
node, err = logic.CreateIngressGateway(netid, nodeid, request)
if err != nil {
logger.Log(0, r.Header.Get("user"),
fmt.Sprintf("failed to create ingress gateway on node [%s] on network [%s]: %v",
nodeid, netid, err))
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
apiNode := node.ConvertToAPINode()
logger.Log(
1,
r.Header.Get("user"),
"created ingress gateway on node",
nodeid,
"on network",
netid,
)
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(apiNode)
go func() {
if err := mq.NodeUpdate(&node); err != nil {
slog.Error("error publishing node update to node", "node", node.ID, "error", err)
}
mq.PublishPeerUpdate(false)
}()
}
// @Summary Delete an remote access gateway
// @Router /api/nodes/{network}/{nodeid}/deleteingress [delete]
// @Tags Nodes
// @Security oauth2
// @Success 200 {object} models.ApiNode
// @Failure 500 {object} models.ErrorResponse
func deleteIngressGateway(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var params = mux.Vars(r)
nodeid := params["nodeid"]
netid := params["network"]
node, err := logic.ValidateParams(nodeid, netid)
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
node, removedClients, err := logic.DeleteIngressGateway(nodeid)
if err != nil {
logger.Log(0, r.Header.Get("user"),
fmt.Sprintf("failed to delete ingress gateway on node [%s] on network [%s]: %v",
nodeid, netid, err))
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
apiNode := node.ConvertToAPINode()
logger.Log(1, r.Header.Get("user"), "deleted ingress gateway", nodeid)
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(apiNode)
if len(removedClients) > 0 {
host, err := logic.GetHost(node.HostID.String())
if err == nil {
allNodes, err := logic.GetAllNodes()
if err != nil {
return
}
go func() {
if err := mq.PublishSingleHostPeerUpdate(host, allNodes, nil, removedClients[:], false, nil); err != nil {
slog.Error("publishSingleHostUpdate", "host", host.Name, "error", err)
}
mq.PublishPeerUpdate(false)
if err := mq.NodeUpdate(&node); err != nil {
slog.Error(
"error publishing node update to node",
"node",
node.ID,
"error",
err,
)
}
if servercfg.IsDNSMode() {
logic.SetDNS()
}
}()
}
}
}
// @Summary Update an individual node
// @Router /api/nodes/{network}/{nodeid} [put]
// @Tags Nodes

View file

@ -71,6 +71,8 @@ const (
USER_INVITES_TABLE_NAME = "user_invites"
// TAG_TABLE_NAME - table for tags
TAG_TABLE_NAME = "tags"
// PEER_ACK_TABLE - table for failover peer ack
PEER_ACK_TABLE = "peer_ack"
// == ERROR CONSTS ==
// NO_RECORD - no singular result found
NO_RECORD = "no result found"
@ -158,6 +160,7 @@ func createTables() {
CreateTable(USER_INVITES_TABLE_NAME)
CreateTable(TAG_TABLE_NAME)
CreateTable(ACLS_TABLE_NAME)
CreateTable(PEER_ACK_TABLE)
}
func CreateTable(tableName string) error {

10
go.mod
View file

@ -5,7 +5,7 @@ go 1.23
require (
github.com/blang/semver v3.5.1+incompatible
github.com/eclipse/paho.mqtt.golang v1.4.3
github.com/go-playground/validator/v10 v10.23.0
github.com/go-playground/validator/v10 v10.24.0
github.com/golang-jwt/jwt/v4 v4.5.1
github.com/google/uuid v1.6.0
github.com/gorilla/handlers v1.5.2
@ -18,10 +18,10 @@ require (
github.com/stretchr/testify v1.10.0
github.com/txn2/txeh v1.5.5
go.uber.org/automaxprocs v1.6.0
golang.org/x/crypto v0.30.0
golang.org/x/net v0.27.0 // indirect
golang.org/x/crypto v0.32.0
golang.org/x/net v0.34.0 // indirect
golang.org/x/oauth2 v0.24.0
golang.org/x/sys v0.28.0 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20221104135756-97bc4ad4a1cb
gopkg.in/yaml.v3 v3.0.1
@ -50,7 +50,7 @@ require (
require (
cloud.google.com/go/compute/metadata v0.3.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
github.com/go-jose/go-jose/v3 v3.0.3 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/kr/text v0.2.0 // indirect

20
go.sum
View file

@ -17,8 +17,8 @@ github.com/eclipse/paho.mqtt.golang v1.4.3 h1:2kwcUGn8seMUfWndX0hGbvH8r7crgcJguQ
github.com/eclipse/paho.mqtt.golang v1.4.3/go.mod h1:CSYvoAlsMkhYOXh/oKyxa8EcBci6dVkLCbo5tTC1RIE=
github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k=
github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
@ -27,8 +27,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.23.0 h1:/PwmTwZhS0dPkav3cdK9kV1FsAmrL8sThn8IHr/sO+o=
github.com/go-playground/validator/v10 v10.23.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/go-playground/validator/v10 v10.24.0 h1:KHQckvo8G6hlWnrPX4NJJ+aBfWNAE/HH+qdL2cBpCmg=
github.com/go-playground/validator/v10 v10.24.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus=
github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo=
github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
@ -101,8 +101,8 @@ go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwE
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.30.0 h1:RwoQn3GkWiMkzlX562cLB7OxWvjH1L8xutO2WoJcRoY=
golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
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=
@ -112,8 +112,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE=
golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -129,8 +129,8 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
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/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=

View file

@ -165,6 +165,7 @@ func storeAclInCache(a models.Acl) {
aclCacheMutex.Lock()
defer aclCacheMutex.Unlock()
aclCacheMap[a.ID] = a
}
func removeAclFromCache(a models.Acl) {
@ -588,6 +589,7 @@ func IsPeerAllowed(node, peer models.Node, checkDefaultPolicy bool) bool {
return true
}
}
}
// list device policies
policies := listDevicePolicies(models.NetworkID(peer.Network))

View file

@ -461,9 +461,7 @@ func GetFwRulesOnIngressGateway(node models.Node) (rules []models.FwRule) {
defaultDevicePolicy, _ := GetDefaultPolicy(models.NetworkID(node.Network), models.DevicePolicy)
nodes, _ := GetNetworkNodes(node.Network)
nodes = append(nodes, GetStaticNodesByNetwork(models.NetworkID(node.Network), true)...)
//fmt.Printf("=====> NODES: %+v \n\n", nodes)
userNodes := GetStaticUserNodesByNetwork(models.NetworkID(node.Network))
//fmt.Printf("=====> USER NODES %+v \n\n", userNodes)
for _, userNodeI := range userNodes {
for _, peer := range nodes {
if peer.IsUserNode {

View file

@ -141,14 +141,14 @@ func CreateIngressGateway(netid string, nodeid string, ingress models.IngressReq
return models.Node{}, err
}
if node.IsRelayed {
return models.Node{}, errors.New("ingress cannot be created on a relayed node")
return models.Node{}, errors.New("gateway cannot be created on a relayed node")
}
host, err := GetHost(node.HostID.String())
if err != nil {
return models.Node{}, err
}
if host.OS != "linux" {
return models.Node{}, errors.New("ingress can only be created on linux based node")
return models.Node{}, errors.New("gateway can only be created on linux based node")
}
network, err := GetParentNetwork(netid)
@ -156,12 +156,16 @@ func CreateIngressGateway(netid string, nodeid string, ingress models.IngressReq
return models.Node{}, err
}
node.IsIngressGateway = true
node.IsGw = true
if !servercfg.IsPro {
node.IsInternetGateway = ingress.IsInternetGateway
}
node.IngressGatewayRange = network.AddressRange
node.IngressGatewayRange6 = network.AddressRange6
node.IngressDNS = ingress.ExtclientDNS
if node.IsInternetGateway && node.IngressDNS == "" {
node.IngressDNS = "1.1.1.1"
}
node.IngressPersistentKeepalive = 20
if ingress.PersistentKeepalive != 0 {
node.IngressPersistentKeepalive = ingress.PersistentKeepalive

View file

@ -102,7 +102,7 @@ func RemoveIpFromAllocatedIpMap(networkName string, ip string) {
// AddNetworkToAllocatedIpMap - add network to allocated ip map when network is added
func AddNetworkToAllocatedIpMap(networkName string) {
networkCacheMutex.Lock()
allocatedIpMap[networkName] = map[string]net.IP{}
allocatedIpMap[networkName] = make(map[string]net.IP)
networkCacheMutex.Unlock()
}
@ -171,23 +171,8 @@ func GetNetworks() ([]models.Network, error) {
}
// DeleteNetwork - deletes a network
func DeleteNetwork(network string) error {
// remove ACL for network
err := nodeacls.DeleteACLContainer(nodeacls.NetworkID(network))
if err != nil {
logger.Log(1, "failed to remove the node acls during network delete for network,", network)
}
// Delete default network enrollment key
keys, _ := GetAllEnrollmentKeys()
for _, key := range keys {
if key.Tags[0] == network {
if key.Default {
DeleteEnrollmentKey(key.Value, true)
break
}
func DeleteNetwork(network string, force bool, done chan struct{}) error {
}
}
nodeCount, err := GetNetworkNonServerNodeCount(network)
if nodeCount == 0 || database.IsEmptyRecord(err) {
// delete server nodes first then db records
@ -200,7 +185,50 @@ func DeleteNetwork(network string) error {
}
return nil
}
return errors.New("node check failed. All nodes must be deleted before deleting network")
// Remove All Nodes
go func() {
nodes, err := GetNetworkNodes(network)
if err == nil {
for _, node := range nodes {
node := node
host, err := GetHost(node.HostID.String())
if err != nil {
continue
}
DissasociateNodeFromHost(&node, host)
}
}
// remove ACL for network
err = nodeacls.DeleteACLContainer(nodeacls.NetworkID(network))
if err != nil {
logger.Log(1, "failed to remove the node acls during network delete for network,", network)
}
// delete server nodes first then db records
err = database.DeleteRecord(database.NETWORKS_TABLE_NAME, network)
if err != nil {
return
}
if servercfg.CacheEnabled() {
deleteNetworkFromCache(network)
}
done <- struct{}{}
close(done)
}()
// Delete default network enrollment key
keys, _ := GetAllEnrollmentKeys()
for _, key := range keys {
if key.Tags[0] == network {
if key.Default {
DeleteEnrollmentKey(key.Value, true)
break
}
}
}
return nil
}
// CreateNetwork - creates a network in database

View file

@ -40,9 +40,7 @@ func getNodeFromCache(nodeID string) (node models.Node, ok bool) {
}
func getNodesFromCache() (nodes []models.Node) {
nodeCacheMutex.RLock()
for _, node := range nodesCacheMap {
nodes = append(nodes, node)
}
nodes = slices.Collect(maps.Values(nodesCacheMap))
nodeCacheMutex.RUnlock()
return
}
@ -141,7 +139,7 @@ func GetNetworkNodesMemory(allNodes []models.Node, network string) []models.Node
defer nodeNetworkCacheMutex.Unlock()
return slices.Collect(maps.Values(networkNodes))
}
var nodes = []models.Node{}
var nodes = make([]models.Node, 0, len(allNodes))
for i := range allNodes {
node := allNodes[i]
if node.Network == network {
@ -239,7 +237,7 @@ func UpdateNode(currentNode *models.Node, newNode *models.Node) error {
}
}
return fmt.Errorf("failed to update node " + currentNode.ID.String() + ", cannot change ID.")
return fmt.Errorf("failed to update node %s, cannot change ID", currentNode.ID.String())
}
// DeleteNode - marks node for deletion (and adds to zombie list) if called by UI or deletes node if called by node

View file

@ -59,6 +59,80 @@ var (
}
)
// GetHostPeerInfo - fetches required peer info per network
func GetHostPeerInfo(host *models.Host) (models.HostPeerInfo, error) {
peerInfo := models.HostPeerInfo{
NetworkPeerIDs: make(map[models.NetworkID]models.PeerMap),
}
allNodes, err := GetAllNodes()
if err != nil {
return peerInfo, err
}
for _, nodeID := range host.Nodes {
nodeID := nodeID
node, err := GetNodeByID(nodeID)
if err != nil {
continue
}
if !node.Connected || node.PendingDelete || node.Action == models.NODE_DELETE {
continue
}
networkPeersInfo := make(models.PeerMap)
defaultDevicePolicy, _ := GetDefaultPolicy(models.NetworkID(node.Network), models.DevicePolicy)
currentPeers := GetNetworkNodesMemory(allNodes, node.Network)
for _, peer := range currentPeers {
peer := peer
if peer.ID.String() == node.ID.String() {
logger.Log(2, "peer update, skipping self")
// skip yourself
continue
}
peerHost, err := GetHost(peer.HostID.String())
if err != nil {
logger.Log(1, "no peer host", peer.HostID.String(), err.Error())
continue
}
var allowedToComm bool
if defaultDevicePolicy.Enabled {
allowedToComm = true
} else {
allowedToComm = IsPeerAllowed(node, peer, false)
}
if peer.Action != models.NODE_DELETE &&
!peer.PendingDelete &&
peer.Connected &&
nodeacls.AreNodesAllowed(nodeacls.NetworkID(node.Network), nodeacls.NodeID(node.ID.String()), nodeacls.NodeID(peer.ID.String())) &&
(defaultDevicePolicy.Enabled || allowedToComm) {
networkPeersInfo[peerHost.PublicKey.String()] = models.IDandAddr{
ID: peer.ID.String(),
HostID: peerHost.ID.String(),
Address: peer.PrimaryAddress(),
Name: peerHost.Name,
Network: peer.Network,
ListenPort: peerHost.ListenPort,
}
}
}
var extPeerIDAndAddrs []models.IDandAddr
if node.IsIngressGateway {
_, extPeerIDAndAddrs, _, err = GetExtPeers(&node, &node)
if err == nil {
for _, extPeerIdAndAddr := range extPeerIDAndAddrs {
networkPeersInfo[extPeerIdAndAddr.ID] = extPeerIdAndAddr
}
}
}
peerInfo.NetworkPeerIDs[models.NetworkID(node.Network)] = networkPeersInfo
}
return peerInfo, nil
}
// GetPeerUpdateForHost - gets the consolidated peer update for the host from all networks
func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.Node,
deletedNode *models.Node, deletedClients []models.ExtClient) (models.HostPeerUpdate, error) {
@ -79,11 +153,11 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
IngressInfo: make(map[string]models.IngressInfo),
AclRules: make(map[string]models.AclRule),
},
PeerIDs: make(models.PeerMap, 0),
Peers: []wgtypes.PeerConfig{},
NodePeers: []wgtypes.PeerConfig{},
HostNetworkInfo: models.HostInfoMap{},
EndpointDetection: servercfg.IsEndpointDetectionEnabled(),
PeerIDs: make(models.PeerMap, 0),
Peers: []wgtypes.PeerConfig{},
NodePeers: []wgtypes.PeerConfig{},
HostNetworkInfo: models.HostInfoMap{},
ServerConfig: servercfg.ServerInfo,
}
defer func() {
if !hostPeerUpdate.FwUpdate.AllowAll {
@ -229,21 +303,19 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
hostPeerUpdate.EgressRoutes = append(hostPeerUpdate.EgressRoutes, getExtpeersExtraRoutes(node)...)
}
_, isFailOverPeer := node.FailOverPeers[peer.ID.String()]
if servercfg.IsPro {
if (node.IsRelayed && node.RelayedBy != peer.ID.String()) ||
(peer.IsRelayed && peer.RelayedBy != node.ID.String()) || isFailOverPeer {
// if node is relayed and peer is not the relay, set remove to true
if _, ok := peerIndexMap[peerHost.PublicKey.String()]; ok {
continue
}
peerConfig.Remove = true
hostPeerUpdate.Peers = append(hostPeerUpdate.Peers, peerConfig)
peerIndexMap[peerHost.PublicKey.String()] = len(hostPeerUpdate.Peers) - 1
if (node.IsRelayed && node.RelayedBy != peer.ID.String()) ||
(peer.IsRelayed && peer.RelayedBy != node.ID.String()) || isFailOverPeer {
// if node is relayed and peer is not the relay, set remove to true
if _, ok := peerIndexMap[peerHost.PublicKey.String()]; ok {
continue
}
if node.IsRelayed && node.RelayedBy == peer.ID.String() {
hostPeerUpdate = SetDefaultGwForRelayedUpdate(node, peer, hostPeerUpdate)
}
peerConfig.Remove = true
hostPeerUpdate.Peers = append(hostPeerUpdate.Peers, peerConfig)
peerIndexMap[peerHost.PublicKey.String()] = len(hostPeerUpdate.Peers) - 1
continue
}
if node.IsRelayed && node.RelayedBy == peer.ID.String() {
hostPeerUpdate = SetDefaultGwForRelayedUpdate(node, peer, hostPeerUpdate)
}
uselocal := false
@ -297,15 +369,19 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
peerConfig.Endpoint.IP = peer.LocalAddress.IP
peerConfig.Endpoint.Port = peerHost.ListenPort
}
allowedips := GetAllowedIPs(&node, &peer, nil)
allowedToComm := IsPeerAllowed(node, peer, false)
var allowedToComm bool
if defaultDevicePolicy.Enabled {
allowedToComm = true
} else {
allowedToComm = IsPeerAllowed(node, peer, false)
}
if peer.Action != models.NODE_DELETE &&
!peer.PendingDelete &&
peer.Connected &&
nodeacls.AreNodesAllowed(nodeacls.NetworkID(node.Network), nodeacls.NodeID(node.ID.String()), nodeacls.NodeID(peer.ID.String())) &&
(defaultDevicePolicy.Enabled || allowedToComm) &&
(deletedNode == nil || (deletedNode != nil && peer.ID.String() != deletedNode.ID.String())) {
peerConfig.AllowedIPs = allowedips // only append allowed IPs if valid connection
peerConfig.AllowedIPs = GetAllowedIPs(&node, &peer, nil) // only append allowed IPs if valid connection
}
var nodePeer wgtypes.PeerConfig
@ -466,10 +542,6 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
}
}
}
hostPeerUpdate.ManageDNS = servercfg.GetManageDNS()
hostPeerUpdate.Stun = servercfg.IsStunEnabled()
hostPeerUpdate.StunServers = servercfg.GetStunServers()
return hostPeerUpdate, nil
}

View file

@ -1,34 +1,246 @@
package logic
import (
"errors"
"fmt"
"net"
"github.com/google/uuid"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/logic/acls/nodeacls"
"github.com/gravitl/netmaker/models"
)
var GetRelays = func() ([]models.Node, error) {
return []models.Node{}, nil
// GetRelays - gets all the nodes that are relays
func GetRelays() ([]models.Node, error) {
nodes, err := GetAllNodes()
if err != nil {
return nil, err
}
relays := make([]models.Node, 0)
for _, node := range nodes {
if node.IsRelay {
relays = append(relays, node)
}
}
return relays, nil
}
var RelayedAllowedIPs = func(peer, node *models.Node) []net.IPNet {
return []net.IPNet{}
// CreateRelay - creates a relay
func CreateRelay(relay models.RelayRequest) ([]models.Node, models.Node, error) {
var returnnodes []models.Node
node, err := GetNodeByID(relay.NodeID)
if err != nil {
return returnnodes, models.Node{}, err
}
host, err := GetHost(node.HostID.String())
if err != nil {
return returnnodes, models.Node{}, err
}
if host.OS != "linux" {
return returnnodes, models.Node{}, fmt.Errorf("only linux machines can be gateway nodes")
}
err = ValidateRelay(relay, false)
if err != nil {
return returnnodes, models.Node{}, err
}
node.IsRelay = true
node.IsGw = true
node.RelayedNodes = relay.RelayedNodes
node.SetLastModified()
err = UpsertNode(&node)
if err != nil {
return returnnodes, node, err
}
returnnodes = SetRelayedNodes(true, relay.NodeID, relay.RelayedNodes)
return returnnodes, node, nil
}
var GetAllowedIpsForRelayed = func(relayed, relay *models.Node) []net.IPNet {
return []net.IPNet{}
// SetRelayedNodes- sets and saves node as relayed
func SetRelayedNodes(setRelayed bool, relay string, relayed []string) []models.Node {
var returnnodes []models.Node
for _, id := range relayed {
node, err := GetNodeByID(id)
if err != nil {
logger.Log(0, "setRelayedNodes.GetNodebyID", err.Error())
continue
}
node.IsRelayed = setRelayed
if setRelayed {
node.RelayedBy = relay
} else {
node.RelayedBy = ""
}
node.SetLastModified()
if err := UpsertNode(&node); err != nil {
logger.Log(0, "setRelayedNodes.Insert", err.Error())
continue
}
returnnodes = append(returnnodes, node)
}
return returnnodes
}
var UpdateRelayed = func(currentNode, newNode *models.Node) {
// func GetRelayedNodes(relayNode *models.Node) (models.Node, error) {
// var returnnodes []models.Node
// networkNodes, err := GetNetworkNodes(relayNode.Network)
// if err != nil {
// return returnnodes, err
// }
// for _, node := range networkNodes {
// for _, addr := range relayNode.RelayAddrs {
// if addr == node.Address.IP.String() || addr == node.Address6.IP.String() {
// returnnodes = append(returnnodes, node)
// }
// }
// }
// return returnnodes, nil
// }
// ValidateRelay - checks if relay is valid
func ValidateRelay(relay models.RelayRequest, update bool) error {
var err error
node, err := GetNodeByID(relay.NodeID)
if err != nil {
return err
}
if !update && node.IsRelay {
return errors.New("node is already acting as a relay")
}
for _, relayedNodeID := range relay.RelayedNodes {
relayedNode, err := GetNodeByID(relayedNodeID)
if err != nil {
return err
}
if relayedNode.IsIngressGateway {
return errors.New("cannot relay an ingress gateway (" + relayedNodeID + ")")
}
if relayedNode.IsInternetGateway {
return errors.New("cannot relay an internet gateway (" + relayedNodeID + ")")
}
if relayedNode.InternetGwID != "" && relayedNode.InternetGwID != relay.NodeID {
return errors.New("cannot relay an internet client (" + relayedNodeID + ")")
}
if relayedNode.IsFailOver {
return errors.New("cannot relay a failOver (" + relayedNodeID + ")")
}
if relayedNode.FailedOverBy != uuid.Nil {
ResetFailedOverPeer(&relayedNode)
}
}
return err
}
var SetRelayedNodes = func(setRelayed bool, relay string, relayed []string) []models.Node {
return []models.Node{}
// UpdateRelayNodes - updates relay nodes
func updateRelayNodes(relay string, oldNodes []string, newNodes []string) []models.Node {
_ = SetRelayedNodes(false, relay, oldNodes)
return SetRelayedNodes(true, relay, newNodes)
}
var RelayUpdates = func(currentNode, newNode *models.Node) bool {
return false
func RelayUpdates(currentNode, newNode *models.Node) bool {
relayUpdates := false
if newNode.IsRelay {
if len(newNode.RelayedNodes) != len(currentNode.RelayedNodes) {
relayUpdates = true
} else {
for i, node := range newNode.RelayedNodes {
if node != currentNode.RelayedNodes[i] {
relayUpdates = true
}
}
}
}
return relayUpdates
}
var ValidateRelay = func(relay models.RelayRequest, update bool) error {
return nil
// 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 {
for _, relayedNode := range updatenodes {
node := relayedNode
ResetFailedOverPeer(&node)
}
}
}
// DeleteRelay - deletes a relay
func DeleteRelay(network, nodeid string) ([]models.Node, models.Node, error) {
var returnnodes []models.Node
node, err := GetNodeByID(nodeid)
if err != nil {
return returnnodes, models.Node{}, err
}
returnnodes = SetRelayedNodes(false, nodeid, node.RelayedNodes)
node.IsRelay = false
node.RelayedNodes = []string{}
node.SetLastModified()
if err = UpsertNode(&node); err != nil {
return returnnodes, models.Node{}, err
}
return returnnodes, node, nil
}
func RelayedAllowedIPs(peer, node *models.Node) []net.IPNet {
var allowedIPs = []net.IPNet{}
for _, relayedNodeID := range peer.RelayedNodes {
if node.ID.String() == relayedNodeID {
continue
}
relayedNode, err := GetNodeByID(relayedNodeID)
if err != nil {
continue
}
allowed := getRelayedAddresses(relayedNodeID)
if relayedNode.IsEgressGateway {
allowed = append(allowed, GetEgressIPs(&relayedNode)...)
}
allowedIPs = append(allowedIPs, allowed...)
}
return allowedIPs
}
// GetAllowedIpsForRelayed - returns the peerConfig for a node relayed by relay
func GetAllowedIpsForRelayed(relayed, relay *models.Node) (allowedIPs []net.IPNet) {
if relayed.RelayedBy != relay.ID.String() {
logger.Log(0, "RelayedByRelay called with invalid parameters")
return
}
if relay.InternetGwID != "" {
return GetAllowedIpForInetNodeClient(relayed, relay)
}
peers, err := GetNetworkNodes(relay.Network)
if err != nil {
logger.Log(0, "error getting network clients", err.Error())
return
}
for _, peer := range peers {
if peer.ID == relayed.ID || peer.ID == relay.ID {
continue
}
if nodeacls.AreNodesAllowed(nodeacls.NetworkID(relayed.Network), nodeacls.NodeID(relayed.ID.String()), nodeacls.NodeID(peer.ID.String())) {
allowedIPs = append(allowedIPs, GetAllowedIPs(relayed, &peer, nil)...)
}
}
return
}
func getRelayedAddresses(id string) []net.IPNet {
addrs := []net.IPNet{}
node, err := GetNodeByID(id)
if err != nil {
logger.Log(0, "getRelayedAddresses: "+err.Error())
return addrs
}
if node.Address.IP != nil {
node.Address.Mask = net.CIDRMask(32, 32)
addrs = append(addrs, node.Address)
}
if node.Address6.IP != nil {
node.Address6.Mask = net.CIDRMask(128, 128)
addrs = append(addrs, node.Address6)
}
return addrs
}

View file

@ -18,6 +18,10 @@ func getNodeStatus(node *models.Node, t bool) {
node.Status = models.OnlineSt
return
}
if !node.Connected {
node.Status = models.Disconnected
return
}
if time.Since(node.LastCheckIn) > time.Minute*10 {
node.Status = models.OfflineSt
return

View file

@ -259,7 +259,7 @@ func CheckIDSyntax(id string) error {
if len(id) < 3 {
return errors.New("name should have min 3 characters")
}
reg, err := regexp.Compile("^[a-zA-Z-]+$")
reg, err := regexp.Compile("^[a-zA-Z0-9- ]+$")
if err != nil {
return err
}

View file

@ -7,6 +7,7 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
"log/slog"
"net"
"os"
"strings"
@ -91,6 +92,30 @@ func StringSliceContains(slice []string, item string) bool {
}
return false
}
func SetVerbosity(logLevel int) {
var level slog.Level
switch logLevel {
case 0:
level = slog.LevelInfo
case 1:
level = slog.LevelError
case 2:
level = slog.LevelWarn
case 3:
level = slog.LevelDebug
default:
level = slog.LevelInfo
}
// Create the logger with the chosen level
handler := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
Level: level,
})
logger := slog.New(handler)
slog.SetDefault(logger)
}
// NormalizeCIDR - returns the first address of CIDR
func NormalizeCIDR(address string) (string, error) {

View file

@ -28,6 +28,7 @@ func Run() {
updateHosts()
updateNodes()
updateAcls()
migrateToGws()
}
func assignSuperAdmin() {
@ -441,3 +442,18 @@ func createDefaultTagsAndPolicies() {
}
logic.MigrateAclPolicies()
}
func migrateToGws() {
nodes, err := logic.GetAllNodes()
if err != nil {
return
}
for _, node := range nodes {
if node.IsIngressGateway || node.IsRelay {
node.IsGw = true
node.IsIngressGateway = true
node.IsRelay = true
logic.UpsertNode(&node)
}
}
}

9
models/gateway.go Normal file
View file

@ -0,0 +1,9 @@
package models
type CreateGwReq struct {
IngressRequest
RelayRequest
}
type DeleteGw struct {
}

View file

@ -98,6 +98,8 @@ type HostMqAction string
const (
// Upgrade - const to request host to update it's client
Upgrade HostMqAction = "UPGRADE"
// ForceUpgrade - const for forcing a host to upgrade its client binary
ForceUpgrade HostMqAction = "FORCE_UPGRADE"
// SignalHost - const for host signal action
SignalHost HostMqAction = "SIGNAL_HOST"
// UpdateHost - constant for host update action

View file

@ -48,6 +48,7 @@ type HostNetworkInfo struct {
ListenPort int `json:"listen_port" yaml:"listen_port"`
IsStaticPort bool `json:"is_static_port"`
IsStatic bool `json:"is_static"`
Version string `json:"version"`
}
// PeerMap - peer map for ids and addresses in metrics

View file

@ -6,27 +6,35 @@ import (
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
type HostPeerInfo struct {
NetworkPeerIDs map[NetworkID]PeerMap `json:"network_peers"`
}
// HostPeerUpdate - struct for host peer updates
type HostPeerUpdate struct {
Host Host `json:"host" bson:"host" yaml:"host"`
ChangeDefaultGw bool `json:"change_default_gw"`
DefaultGwIp net.IP `json:"default_gw_ip"`
IsInternetGw bool `json:"is_inet_gw"`
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"`
Host Host `json:"host"`
ChangeDefaultGw bool `json:"change_default_gw"`
DefaultGwIp net.IP `json:"default_gw_ip"`
IsInternetGw bool `json:"is_inet_gw"`
NodeAddrs []net.IPNet `json:"nodes_addrs"`
Server string `json:"server"`
ServerVersion string `json:"serverversion"`
ServerAddrs []ServerAddr `json:"serveraddrs"`
NodePeers []wgtypes.PeerConfig `json:"node_peers"`
Peers []wgtypes.PeerConfig `json:"host_peers"`
PeerIDs PeerMap `json:"peerids"`
HostNetworkInfo HostInfoMap `json:"host_network_info,omitempty"`
EgressRoutes []EgressNetworkRoutes `json:"egress_network_routes"`
FwUpdate FwUpdate `json:"fw_update"`
ReplacePeers bool `json:"replace_peers"`
ServerConfig
OldPeerUpdateFields
}
type OldPeerUpdateFields struct {
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"`
ReplacePeers bool `json:"replace_peers"`
EndpointDetection bool `json:"endpoint_detection"`
ManageDNS bool `yaml:"manage_dns"`
Stun bool `yaml:"stun"`
StunServers string `yaml:"stun_servers"`
OldPeers []wgtypes.PeerConfig `json:"Peers"`
EndpointDetection bool `json:"endpoint_detection"`
}
type FwRule struct {

View file

@ -42,9 +42,10 @@ func (network *Network) SetNetworkLastModified() {
}
// Network.SetDefaults - sets default values for a network struct
func (network *Network) SetDefaults() {
func (network *Network) SetDefaults() (upsert bool) {
if network.DefaultUDPHolePunch == "" {
network.DefaultUDPHolePunch = "no"
upsert = true
}
if network.DefaultInterface == "" {
if len(network.NetID) < 33 {
@ -52,35 +53,45 @@ func (network *Network) SetDefaults() {
} else {
network.DefaultInterface = network.NetID
}
upsert = true
}
if network.DefaultListenPort == 0 {
network.DefaultListenPort = 51821
upsert = true
}
if network.NodeLimit == 0 {
network.NodeLimit = 999999999
upsert = true
}
if network.DefaultKeepalive == 0 {
network.DefaultKeepalive = 20
upsert = true
}
if network.AllowManualSignUp == "" {
network.AllowManualSignUp = "no"
upsert = true
}
if network.IsIPv4 == "" {
network.IsIPv4 = "yes"
upsert = true
}
if network.IsIPv6 == "" {
network.IsIPv6 = "no"
upsert = true
}
if network.DefaultMTU == 0 {
network.DefaultMTU = 1280
upsert = true
}
if network.DefaultACL == "" {
network.DefaultACL = "yes"
upsert = true
}
return
}
func (network *Network) GetNetworkNetworkCIDR4() *net.IPNet {

View file

@ -14,11 +14,12 @@ import (
type NodeStatus string
const (
OnlineSt NodeStatus = "online"
OfflineSt NodeStatus = "offline"
WarningSt NodeStatus = "warning"
ErrorSt NodeStatus = "error"
UnKnown NodeStatus = "unknown"
OnlineSt NodeStatus = "online"
OfflineSt NodeStatus = "offline"
WarningSt NodeStatus = "warning"
ErrorSt NodeStatus = "error"
UnKnown NodeStatus = "unknown"
Disconnected NodeStatus = "disconnected"
)
// LastCheckInThreshold - if node's checkin more than this threshold,then node is declared as offline
@ -77,11 +78,12 @@ type CommonNode struct {
Action string `json:"action" yaml:"action"`
LocalAddress net.IPNet `json:"localaddress" yaml:"localaddress"`
IsEgressGateway bool `json:"isegressgateway" yaml:"isegressgateway"`
EgressGatewayRanges []string `json:"egressgatewayranges" yaml:"egressgatewayranges" bson:"egressgatewayranges"`
EgressGatewayRanges []string `json:"egressgatewayranges" yaml:"egressgatewayranges"`
IsIngressGateway bool `json:"isingressgateway" yaml:"isingressgateway"`
IsRelayed bool `json:"isrelayed" yaml:"isrelayed" bson:"isrelayed"`
RelayedBy string `json:"relayedby" yaml:"relayedby" bson:"relayedby"`
IsRelay bool `json:"isrelay" yaml:"isrelay" bson:"isrelay"`
IsRelayed bool `json:"isrelayed" yaml:"isrelayed"`
RelayedBy string `json:"relayedby" yaml:"relayedby"`
IsRelay bool `json:"isrelay" yaml:"isrelay"`
IsGw bool `json:"is_gw" yaml:"is_gw"`
RelayedNodes []string `json:"relaynodes" yaml:"relayedNodes"`
IngressDNS string `json:"ingressdns" yaml:"ingressdns"`
DNSOn bool `json:"dnson" yaml:"dnson"`

View file

@ -252,24 +252,26 @@ type NodeJoinResponse struct {
// ServerConfig - struct for dealing with the server information for a netclient
type ServerConfig struct {
CoreDNSAddr string `yaml:"corednsaddr"`
API string `yaml:"api"`
APIPort string `yaml:"apiport"`
DNSMode string `yaml:"dnsmode"`
Version string `yaml:"version"`
MQPort string `yaml:"mqport"`
MQUserName string `yaml:"mq_username"`
MQPassword string `yaml:"mq_password"`
BrokerType string `yaml:"broker_type"`
Server string `yaml:"server"`
Broker string `yaml:"broker"`
IsPro bool `yaml:"isee" json:"Is_EE"`
TrafficKey []byte `yaml:"traffickey"`
MetricInterval string `yaml:"metric_interval"`
ManageDNS bool `yaml:"manage_dns"`
Stun bool `yaml:"stun"`
StunServers string `yaml:"stun_servers"`
DefaultDomain string `yaml:"default_domain"`
CoreDNSAddr string `yaml:"corednsaddr"`
API string `yaml:"api"`
APIPort string `yaml:"apiport"`
DNSMode string `yaml:"dnsmode"`
Version string `yaml:"version"`
MQPort string `yaml:"mqport"`
MQUserName string `yaml:"mq_username"`
MQPassword string `yaml:"mq_password"`
BrokerType string `yaml:"broker_type"`
Server string `yaml:"server"`
Broker string `yaml:"broker"`
IsPro bool `yaml:"isee" json:"Is_EE"`
TrafficKey []byte `yaml:"traffickey"`
MetricInterval string `yaml:"metric_interval"`
MetricsPort int `yaml:"metrics_port"`
ManageDNS bool `yaml:"manage_dns"`
Stun bool `yaml:"stun"`
StunServers string `yaml:"stun_servers"`
EndpointDetection bool `yaml:"endpoint_detection"`
DefaultDomain string `yaml:"default_domain"`
}
// User.NameInCharset - returns if name is in charset below or not

View file

@ -280,6 +280,7 @@ func HandleHostCheckin(h, currentHost *models.Host) bool {
(h.ListenPort != 0 && h.ListenPort != currentHost.ListenPort) ||
(h.WgPublicListenPort != 0 && h.WgPublicListenPort != currentHost.WgPublicListenPort) || (!h.EndpointIPv6.Equal(currentHost.EndpointIPv6))
if ifaceDelta { // only save if something changes
currentHost.EndpointIP = h.EndpointIP
currentHost.EndpointIPv6 = h.EndpointIPv6
currentHost.Interfaces = h.Interfaces

View file

@ -17,7 +17,6 @@ import (
// PublishPeerUpdate --- determines and publishes a peer update to all the hosts
func PublishPeerUpdate(replacePeers bool) error {
if !servercfg.IsMessageQueueBackend() {
return nil
}
@ -114,6 +113,11 @@ func PublishSingleHostPeerUpdate(host *models.Host, allNodes []models.Node, dele
if err != nil {
return err
}
peerUpdate.OldPeerUpdateFields = models.OldPeerUpdateFields{
NodePeers: peerUpdate.NodePeers,
OldPeers: peerUpdate.Peers,
EndpointDetection: peerUpdate.ServerConfig.EndpointDetection,
}
peerUpdate.ReplacePeers = replacePeers
data, err := json.Marshal(&peerUpdate)
if err != nil {

View file

@ -19,7 +19,7 @@ import (
// FailOverHandlers - handlers for FailOver
func FailOverHandlers(r *mux.Router) {
r.HandleFunc("/api/v1/node/{nodeid}/failover", http.HandlerFunc(getfailOver)).
r.HandleFunc("/api/v1/node/{nodeid}/failover", controller.Authorize(true, false, "host", http.HandlerFunc(getfailOver))).
Methods(http.MethodGet)
r.HandleFunc("/api/v1/node/{nodeid}/failover", logic.SecurityCheck(true, http.HandlerFunc(createfailOver))).
Methods(http.MethodPost)
@ -29,6 +29,8 @@ func FailOverHandlers(r *mux.Router) {
Methods(http.MethodPost)
r.HandleFunc("/api/v1/node/{nodeid}/failover_me", controller.Authorize(true, false, "host", http.HandlerFunc(failOverME))).
Methods(http.MethodPost)
r.HandleFunc("/api/v1/node/{nodeid}/failover_check", controller.Authorize(true, false, "host", http.HandlerFunc(checkfailOverCtx))).
Methods(http.MethodGet)
}
// @Summary Get failover node
@ -44,7 +46,6 @@ func getfailOver(w http.ResponseWriter, r *http.Request) {
// confirm host exists
node, err := logic.GetNodeByID(nodeid)
if err != nil {
slog.Error("failed to get node:", "node", nodeid, "error", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
@ -140,6 +141,7 @@ func deletefailOver(w http.ResponseWriter, r *http.Request) {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
proLogic.RemoveFailOverFromCache(node.Network)
go func() {
proLogic.ResetFailOver(&node)
mq.PublishPeerUpdate(false)
@ -265,10 +267,9 @@ func failOverME(w http.ResponseWriter, r *http.Request) {
)
return
}
err = proLogic.SetFailOverCtx(failOverNode, node, peerNode)
if err != nil {
slog.Error("failed to create failover", "id", node.ID.String(),
slog.Debug("failed to create failover", "id", node.ID.String(),
"network", node.Network, "error", err)
logic.ReturnErrorResponse(
w,
@ -293,3 +294,135 @@ func failOverME(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
logic.ReturnSuccessResponse(w, r, "relayed successfully")
}
// @Summary checkfailOverCtx
// @Router /api/v1/node/{nodeid}/failover_check [get]
// @Tags PRO
// @Param nodeid path string true "Node ID"
// @Accept json
// @Param body body models.FailOverMeReq true "Failover request"
// @Success 200 {object} models.SuccessResponse
// @Failure 400 {object} models.ErrorResponse
// @Failure 500 {object} models.ErrorResponse
func checkfailOverCtx(w http.ResponseWriter, r *http.Request) {
var params = mux.Vars(r)
nodeid := params["nodeid"]
// confirm host exists
node, err := logic.GetNodeByID(nodeid)
if err != nil {
logger.Log(0, r.Header.Get("user"), "failed to get node:", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
host, err := logic.GetHost(node.HostID.String())
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
failOverNode, exists := proLogic.FailOverExists(node.Network)
if !exists {
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(
fmt.Errorf("req-from: %s, failover node doesn't exist in the network", host.Name),
"badrequest",
),
)
return
}
var failOverReq models.FailOverMeReq
err = json.NewDecoder(r.Body).Decode(&failOverReq)
if err != nil {
logger.Log(0, r.Header.Get("user"), "error decoding request body: ", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
peerNode, err := logic.GetNodeByID(failOverReq.NodeID)
if err != nil {
slog.Error("peer not found: ", "nodeid", failOverReq.NodeID, "error", err)
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(errors.New("peer not found"), "badrequest"),
)
return
}
if peerNode.IsFailOver {
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(errors.New("peer is acting as failover"), "badrequest"),
)
return
}
if node.IsFailOver {
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(errors.New("node is acting as failover"), "badrequest"),
)
return
}
if peerNode.IsFailOver {
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(errors.New("peer is acting as failover"), "badrequest"),
)
return
}
if node.IsRelayed && node.RelayedBy == peerNode.ID.String() {
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(errors.New("node is relayed by peer node"), "badrequest"),
)
return
}
if node.IsRelay && peerNode.RelayedBy == node.ID.String() {
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(errors.New("node acting as relay for the peer node"), "badrequest"),
)
return
}
if node.IsInternetGateway && peerNode.InternetGwID == node.ID.String() {
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(
errors.New("node acting as internet gw for the peer node"),
"badrequest",
),
)
return
}
if node.InternetGwID != "" && node.InternetGwID == peerNode.ID.String() {
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(
errors.New("node using a internet gw by the peer node"),
"badrequest",
),
)
return
}
err = proLogic.CheckFailOverCtx(failOverNode, node, peerNode)
if err != nil {
slog.Error("failover ctx cannot be set ", "error", err)
logic.ReturnErrorResponse(
w,
r,
logic.FormatError(fmt.Errorf("failover ctx cannot be set: %v", err), "internal"),
)
return
}
w.Header().Set("Content-Type", "application/json")
logic.ReturnSuccessResponse(w, r, "failover can be set")
}

View file

@ -84,6 +84,9 @@ func createInternetGw(w http.ResponseWriter, r *http.Request) {
}()
}
}
if node.IsGw && node.IngressDNS == "" {
node.IngressDNS = "1.1.1.1"
}
err = logic.UpsertNode(&node)
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))

View file

@ -1,152 +0,0 @@
package controllers
import (
"encoding/json"
"fmt"
"net/http"
"github.com/google/uuid"
proLogic "github.com/gravitl/netmaker/pro/logic"
"github.com/gorilla/mux"
controller "github.com/gravitl/netmaker/controllers"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/mq"
)
// RelayHandlers - handle Pro Relays
func RelayHandlers(r *mux.Router) {
r.HandleFunc("/api/nodes/{network}/{nodeid}/createrelay", logic.SecurityCheck(true, http.HandlerFunc(createRelay))).Methods(http.MethodPost)
r.HandleFunc("/api/nodes/{network}/{nodeid}/deleterelay", logic.SecurityCheck(true, http.HandlerFunc(deleteRelay))).Methods(http.MethodDelete)
r.HandleFunc("/api/v1/host/{hostid}/failoverme", controller.Authorize(true, false, "host", http.HandlerFunc(failOverME))).Methods(http.MethodPost)
}
// @Summary Create a relay
// @Router /api/nodes/{network}/{nodeid}/createrelay [post]
// @Tags PRO
// @Accept json
// @Produce json
// @Param network path string true "Network ID"
// @Param nodeid path string true "Node ID"
// @Param body body models.RelayRequest true "Relay request parameters"
// @Success 200 {object} models.ApiNode
// @Failure 400 {object} models.ErrorResponse
// @Failure 500 {object} models.ErrorResponse
func createRelay(w http.ResponseWriter, r *http.Request) {
var relayRequest models.RelayRequest
var params = mux.Vars(r)
w.Header().Set("Content-Type", "application/json")
err := json.NewDecoder(r.Body).Decode(&relayRequest)
if err != nil {
logger.Log(0, r.Header.Get("user"), "error decoding request body: ", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
relayRequest.NetID = params["network"]
relayRequest.NodeID = params["nodeid"]
_, relayNode, err := proLogic.CreateRelay(relayRequest)
if err != nil {
logger.Log(
0,
r.Header.Get("user"),
fmt.Sprintf(
"failed to create relay on node [%s] on network [%s]: %v",
relayRequest.NodeID,
relayRequest.NetID,
err,
),
)
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
for _, relayedNodeID := range relayNode.RelayedNodes {
relayedNode, err := logic.GetNodeByID(relayedNodeID)
if err == nil {
if relayedNode.FailedOverBy != uuid.Nil {
go logic.ResetFailedOverPeer(&relayedNode)
}
}
}
go mq.PublishPeerUpdate(false)
logger.Log(
1,
r.Header.Get("user"),
"created relay on node",
relayRequest.NodeID,
"on network",
relayRequest.NetID,
)
apiNode := relayNode.ConvertToAPINode()
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(apiNode)
}
// @Summary Remove a relay
// @Router /api/nodes/{network}/{nodeid}/deleterelay [delete]
// @Tags PRO
// @Accept json
// @Produce json
// @Param network path string true "Network ID"
// @Param nodeid path string true "Node ID"
// @Success 200 {object} models.ApiNode
// @Failure 400 {object} models.ErrorResponse
// @Failure 500 {object} models.ErrorResponse
func deleteRelay(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var params = mux.Vars(r)
nodeid := params["nodeid"]
netid := params["network"]
updateNodes, node, err := proLogic.DeleteRelay(netid, nodeid)
if err != nil {
logger.Log(0, r.Header.Get("user"), "error decoding request body: ", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
logger.Log(1, r.Header.Get("user"), "deleted relay server", nodeid, "on network", netid)
go func() {
for _, relayedNode := range updateNodes {
err = mq.NodeUpdate(&relayedNode)
if err != nil {
logger.Log(
1,
"relayed node update ",
relayedNode.ID.String(),
"on network",
relayedNode.Network,
": ",
err.Error(),
)
}
h, err := logic.GetHost(relayedNode.HostID.String())
if err == nil {
if h.OS == models.OS_Types.IoT {
nodes, err := logic.GetAllNodes()
if err != nil {
return
}
node.IsRelay = true // for iot update to recognise that it has to delete relay peer
if err = mq.PublishSingleHostPeerUpdate(h, nodes, &node, nil, false, nil); err != nil {
logger.Log(1, "failed to publish peer update to host", h.ID.String(), ": ", err.Error())
}
}
}
}
mq.PublishPeerUpdate(false)
}()
logger.Log(
1,
r.Header.Get("user"),
"deleted relay on node",
node.ID.String(),
"on network",
node.Network,
)
apiNode := node.ConvertToAPINode()
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(apiNode)
}

View file

@ -29,7 +29,6 @@ func InitPro() {
controller.HttpHandlers = append(
controller.HttpHandlers,
proControllers.MetricHandlers,
proControllers.RelayHandlers,
proControllers.UserHandlers,
proControllers.FailOverHandlers,
proControllers.InetHandlers,
@ -91,6 +90,7 @@ func InitPro() {
slog.Error("no OAuth provider found or not configured, continuing without OAuth")
}
proLogic.LoadNodeMetricsToCache()
proLogic.InitFailOverCache()
})
logic.ResetFailOver = proLogic.ResetFailOver
logic.ResetFailedOverPeer = proLogic.ResetFailedOverPeer
@ -106,13 +106,6 @@ func InitPro() {
logic.GetMetrics = proLogic.GetMetrics
logic.UpdateMetrics = proLogic.UpdateMetrics
logic.DeleteMetrics = proLogic.DeleteMetrics
logic.GetRelays = proLogic.GetRelays
logic.GetAllowedIpsForRelayed = proLogic.GetAllowedIpsForRelayed
logic.RelayedAllowedIPs = proLogic.RelayedAllowedIPs
logic.UpdateRelayed = proLogic.UpdateRelayed
logic.SetRelayedNodes = proLogic.SetRelayedNodes
logic.RelayUpdates = proLogic.RelayUpdates
logic.ValidateRelay = proLogic.ValidateRelay
logic.GetTrialEndDate = getTrialEndDate
logic.SetDefaultGw = proLogic.SetDefaultGw
logic.SetDefaultGwForRelayedUpdate = proLogic.SetDefaultGwForRelayedUpdate

View file

@ -13,7 +13,49 @@ import (
)
var failOverCtxMutex = &sync.RWMutex{}
var failOverCacheMutex = &sync.RWMutex{}
var failOverCache = make(map[models.NetworkID]string)
func InitFailOverCache() {
failOverCacheMutex.Lock()
defer failOverCacheMutex.Unlock()
networks, err := logic.GetNetworks()
if err != nil {
return
}
allNodes, err := logic.GetAllNodes()
if err != nil {
return
}
for _, network := range networks {
networkNodes := logic.GetNetworkNodesMemory(allNodes, network.NetID)
for _, node := range networkNodes {
if node.IsFailOver {
failOverCache[models.NetworkID(network.NetID)] = node.ID.String()
break
}
}
}
}
func CheckFailOverCtx(failOverNode, victimNode, peerNode models.Node) error {
failOverCtxMutex.RLock()
defer failOverCtxMutex.RUnlock()
if peerNode.FailOverPeers == nil {
return nil
}
if victimNode.FailOverPeers == nil {
return nil
}
_, peerHasFailovered := peerNode.FailOverPeers[victimNode.ID.String()]
_, victimHasFailovered := victimNode.FailOverPeers[peerNode.ID.String()]
if peerHasFailovered && victimHasFailovered &&
victimNode.FailedOverBy == failOverNode.ID && peerNode.FailedOverBy == failOverNode.ID {
return errors.New("failover ctx is already set")
}
return nil
}
func SetFailOverCtx(failOverNode, victimNode, peerNode models.Node) error {
failOverCtxMutex.Lock()
defer failOverCtxMutex.Unlock()
@ -23,13 +65,16 @@ func SetFailOverCtx(failOverNode, victimNode, peerNode models.Node) error {
if victimNode.FailOverPeers == nil {
victimNode.FailOverPeers = make(map[string]struct{})
}
_, peerHasFailovered := peerNode.FailOverPeers[victimNode.ID.String()]
_, victimHasFailovered := victimNode.FailOverPeers[peerNode.ID.String()]
if peerHasFailovered && victimHasFailovered &&
victimNode.FailedOverBy == failOverNode.ID && peerNode.FailedOverBy == failOverNode.ID {
return errors.New("failover ctx is already set")
}
peerNode.FailOverPeers[victimNode.ID.String()] = struct{}{}
victimNode.FailOverPeers[peerNode.ID.String()] = struct{}{}
victimNode.FailedOverBy = failOverNode.ID
peerNode.FailedOverBy = failOverNode.ID
if err := logic.UpsertNode(&failOverNode); err != nil {
return err
}
if err := logic.UpsertNode(&victimNode); err != nil {
return err
}
@ -50,17 +95,26 @@ func GetFailOverNode(network string, allNodes []models.Node) (models.Node, error
return models.Node{}, errors.New("auto relay not found")
}
func RemoveFailOverFromCache(network string) {
failOverCacheMutex.Lock()
defer failOverCacheMutex.Unlock()
delete(failOverCache, models.NetworkID(network))
}
func SetFailOverInCache(node models.Node) {
failOverCacheMutex.Lock()
defer failOverCacheMutex.Unlock()
failOverCache[models.NetworkID(node.Network)] = node.ID.String()
}
// FailOverExists - checks if failOver exists already in the network
func FailOverExists(network string) (failOverNode models.Node, exists bool) {
nodes, err := logic.GetNetworkNodes(network)
if err != nil {
return
}
for _, node := range nodes {
if node.IsFailOver {
exists = true
failOverNode = node
return
failOverCacheMutex.RLock()
defer failOverCacheMutex.RUnlock()
if nodeID, ok := failOverCache[models.NetworkID(network)]; ok {
failOverNode, err := logic.GetNodeByID(nodeID)
if err == nil {
return failOverNode, true
}
}
return
@ -185,5 +239,6 @@ func CreateFailOver(node models.Node) error {
slog.Error("failed to upsert node", "node", node.ID.String(), "error", err)
return err
}
SetFailOverInCache(node)
return nil
}

View file

@ -1,255 +0,0 @@
package logic
import (
"errors"
"fmt"
"net"
"github.com/google/uuid"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/logic/acls/nodeacls"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/mq"
"github.com/gravitl/netmaker/servercfg"
"golang.org/x/exp/slog"
)
// GetRelays - gets all the nodes that are relays
func GetRelays() ([]models.Node, error) {
nodes, err := logic.GetAllNodes()
if err != nil {
return nil, err
}
relays := make([]models.Node, 0)
for _, node := range nodes {
if node.IsRelay {
relays = append(relays, node)
}
}
return relays, nil
}
// CreateRelay - creates a relay
func CreateRelay(relay models.RelayRequest) ([]models.Node, models.Node, error) {
var returnnodes []models.Node
node, err := logic.GetNodeByID(relay.NodeID)
if err != nil {
return returnnodes, models.Node{}, err
}
host, err := logic.GetHost(node.HostID.String())
if err != nil {
return returnnodes, models.Node{}, err
}
if host.OS != "linux" {
return returnnodes, models.Node{}, fmt.Errorf("only linux machines can be relay nodes")
}
err = ValidateRelay(relay, false)
if err != nil {
return returnnodes, models.Node{}, err
}
node.IsRelay = true
node.RelayedNodes = relay.RelayedNodes
node.SetLastModified()
err = logic.UpsertNode(&node)
if err != nil {
return returnnodes, node, err
}
returnnodes = SetRelayedNodes(true, relay.NodeID, relay.RelayedNodes)
return returnnodes, node, nil
}
// SetRelayedNodes- sets and saves node as relayed
func SetRelayedNodes(setRelayed bool, relay string, relayed []string) []models.Node {
var returnnodes []models.Node
for _, id := range relayed {
node, err := logic.GetNodeByID(id)
if err != nil {
logger.Log(0, "setRelayedNodes.GetNodebyID", err.Error())
continue
}
node.IsRelayed = setRelayed
if setRelayed {
node.RelayedBy = relay
} else {
node.RelayedBy = ""
}
node.SetLastModified()
if err := logic.UpsertNode(&node); err != nil {
logger.Log(0, "setRelayedNodes.Insert", err.Error())
continue
}
returnnodes = append(returnnodes, node)
}
return returnnodes
}
// func GetRelayedNodes(relayNode *models.Node) (models.Node, error) {
// var returnnodes []models.Node
// networkNodes, err := GetNetworkNodes(relayNode.Network)
// if err != nil {
// return returnnodes, err
// }
// for _, node := range networkNodes {
// for _, addr := range relayNode.RelayAddrs {
// if addr == node.Address.IP.String() || addr == node.Address6.IP.String() {
// returnnodes = append(returnnodes, node)
// }
// }
// }
// return returnnodes, nil
// }
// ValidateRelay - checks if relay is valid
func ValidateRelay(relay models.RelayRequest, update bool) error {
var err error
node, err := logic.GetNodeByID(relay.NodeID)
if err != nil {
return err
}
if !update && node.IsRelay {
return errors.New("node is already acting as a relay")
}
for _, relayedNodeID := range relay.RelayedNodes {
relayedNode, err := logic.GetNodeByID(relayedNodeID)
if err != nil {
return err
}
if relayedNode.IsIngressGateway {
return errors.New("cannot relay an ingress gateway (" + relayedNodeID + ")")
}
if relayedNode.IsInternetGateway {
return errors.New("cannot relay an internet gateway (" + relayedNodeID + ")")
}
if relayedNode.InternetGwID != "" && relayedNode.InternetGwID != relay.NodeID {
return errors.New("cannot relay an internet client (" + relayedNodeID + ")")
}
if relayedNode.IsFailOver {
return errors.New("cannot relay a failOver (" + relayedNodeID + ")")
}
if relayedNode.FailedOverBy != uuid.Nil {
ResetFailedOverPeer(&relayedNode)
}
}
return err
}
// UpdateRelayNodes - updates relay nodes
func updateRelayNodes(relay string, oldNodes []string, newNodes []string) []models.Node {
_ = SetRelayedNodes(false, relay, oldNodes)
return SetRelayedNodes(true, relay, newNodes)
}
func RelayUpdates(currentNode, newNode *models.Node) bool {
relayUpdates := false
if servercfg.IsPro && newNode.IsRelay {
if len(newNode.RelayedNodes) != len(currentNode.RelayedNodes) {
relayUpdates = true
} else {
for i, node := range newNode.RelayedNodes {
if node != currentNode.RelayedNodes[i] {
relayUpdates = true
}
}
}
}
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 {
for _, relayedNode := range updatenodes {
node := relayedNode
ResetFailedOverPeer(&node)
go func() {
if err := mq.NodeUpdate(&node); err != nil {
slog.Error("error publishing node update to node", "node", node.ID, "error", err)
}
}()
}
}
}
// DeleteRelay - deletes a relay
func DeleteRelay(network, nodeid string) ([]models.Node, models.Node, error) {
var returnnodes []models.Node
node, err := logic.GetNodeByID(nodeid)
if err != nil {
return returnnodes, models.Node{}, err
}
returnnodes = SetRelayedNodes(false, nodeid, node.RelayedNodes)
node.IsRelay = false
node.RelayedNodes = []string{}
node.SetLastModified()
if err = logic.UpsertNode(&node); err != nil {
return returnnodes, models.Node{}, err
}
return returnnodes, node, nil
}
func RelayedAllowedIPs(peer, node *models.Node) []net.IPNet {
var allowedIPs = []net.IPNet{}
for _, relayedNodeID := range peer.RelayedNodes {
if node.ID.String() == relayedNodeID {
continue
}
relayedNode, err := logic.GetNodeByID(relayedNodeID)
if err != nil {
continue
}
allowed := getRelayedAddresses(relayedNodeID)
if relayedNode.IsEgressGateway {
allowed = append(allowed, logic.GetEgressIPs(&relayedNode)...)
}
allowedIPs = append(allowedIPs, allowed...)
}
return allowedIPs
}
// GetAllowedIpsForRelayed - returns the peerConfig for a node relayed by relay
func GetAllowedIpsForRelayed(relayed, relay *models.Node) (allowedIPs []net.IPNet) {
if relayed.RelayedBy != relay.ID.String() {
logger.Log(0, "RelayedByRelay called with invalid parameters")
return
}
if relay.InternetGwID != "" {
return GetAllowedIpForInetNodeClient(relayed, relay)
}
peers, err := logic.GetNetworkNodes(relay.Network)
if err != nil {
logger.Log(0, "error getting network clients", err.Error())
return
}
for _, peer := range peers {
if peer.ID == relayed.ID || peer.ID == relay.ID {
continue
}
if nodeacls.AreNodesAllowed(nodeacls.NetworkID(relayed.Network), nodeacls.NodeID(relayed.ID.String()), nodeacls.NodeID(peer.ID.String())) {
allowedIPs = append(allowedIPs, logic.GetAllowedIPs(relayed, &peer, nil)...)
}
}
return
}
func getRelayedAddresses(id string) []net.IPNet {
addrs := []net.IPNet{}
node, err := logic.GetNodeByID(id)
if err != nil {
logger.Log(0, "getRelayedAddresses: "+err.Error())
return addrs
}
if node.Address.IP != nil {
node.Address.Mask = net.CIDRMask(32, 32)
addrs = append(addrs, node.Address)
}
if node.Address6.IP != nil {
node.Address6.Mask = net.CIDRMask(128, 128)
addrs = append(addrs, node.Address6)
}
return addrs
}

View file

@ -17,6 +17,10 @@ func getNodeStatusOld(node *models.Node) {
node.Status = models.OnlineSt
return
}
if !node.Connected {
node.Status = models.Disconnected
return
}
if time.Since(node.LastCheckIn) > time.Minute*10 {
node.Status = models.OfflineSt
return
@ -31,12 +35,25 @@ func GetNodeStatus(node *models.Node, defaultEnabledPolicy bool) {
node.Status = models.OfflineSt
return
}
ingNode, err := logic.GetNodeByID(node.StaticNode.IngressGatewayID)
if err != nil {
node.Status = models.OfflineSt
return
}
if !defaultEnabledPolicy {
allowed, _ := logic.IsNodeAllowedToCommunicate(*node, ingNode, false)
if !allowed {
node.Status = models.OnlineSt
return
}
}
// check extclient connection from metrics
ingressMetrics, err := GetMetrics(node.StaticNode.IngressGatewayID)
if err != nil || ingressMetrics == nil || ingressMetrics.Connectivity == nil {
node.Status = models.UnKnown
return
}
if metric, ok := ingressMetrics.Connectivity[node.StaticNode.ClientID]; ok {
if metric.Connected {
node.Status = models.OnlineSt
@ -46,9 +63,14 @@ func GetNodeStatus(node *models.Node, defaultEnabledPolicy bool) {
return
}
}
node.Status = models.UnKnown
return
}
if !node.Connected {
node.Status = models.Disconnected
return
}
if time.Since(node.LastCheckIn) > models.LastCheckInThreshold {
node.Status = models.OfflineSt
return
@ -197,6 +219,7 @@ func checkPeerConnectivity(node *models.Node, metrics *models.Metrics, defaultAc
peerNotConnectedCnt++
}
if peerNotConnectedCnt > len(metrics.Connectivity)/2 {
node.Status = models.WarningSt
return

View file

@ -96,3 +96,7 @@ MANAGE_DNS=false
OLD_ACL_SUPPORT=true
# if STUN is set to true, hole punch is called
STUN=true
# Metrics Collection Port
METRICS_PORT=51821
# Metrics Collection interval in minutes
PUBLISH_METRIC_INTERVAL=15

View file

@ -6,7 +6,7 @@ SCRIPT_DIR=$(dirname "$(realpath "$0")")
CONFIG_PATH="$SCRIPT_DIR/$CONFIG_FILE"
NM_QUICK_VERSION="0.1.1"
LATEST=$(curl -s https://api.github.com/repos/gravitl/netmaker/releases/latest | grep "tag_name" | cut -d : -f 2,3 | tr -d [:space:],\")
BRANCH=master
if [ $(id -u) -ne 0 ]; then
echo "This script must be run as root"
exit 1
@ -617,7 +617,7 @@ install_netmaker() {
echo "Pulling config files..."
local BASE_URL="https://raw.githubusercontent.com/gravitl/netmaker/master"
local BASE_URL="https://raw.githubusercontent.com/gravitl/netmaker/$BRANCH"
local COMPOSE_URL="$BASE_URL/compose/docker-compose.yml"
local CADDY_URL="$BASE_URL/docker/Caddyfile"
if [ "$INSTALL_TYPE" = "pro" ]; then

View file

@ -14,6 +14,8 @@ import (
"github.com/gravitl/netmaker/models"
)
var ServerInfo = GetServerInfo()
// EmqxBrokerType denotes the broker type for EMQX MQTT
const EmqxBrokerType = "emqx"
@ -141,10 +143,12 @@ func GetServerInfo() models.ServerConfig {
cfg.Version = GetVersion()
cfg.IsPro = IsPro
cfg.MetricInterval = GetMetricInterval()
cfg.MetricsPort = GetMetricsPort()
cfg.ManageDNS = GetManageDNS()
cfg.Stun = IsStunEnabled()
cfg.StunServers = GetStunServers()
cfg.DefaultDomain = GetDefaultDomain()
cfg.EndpointDetection = IsEndpointDetectionEnabled()
return cfg
}
@ -654,6 +658,19 @@ func GetMqUserName() string {
return password
}
// GetMetricsPort - get metrics port
func GetMetricsPort() int {
p := 51821
if os.Getenv("METRICS_PORT") != "" {
pStr := os.Getenv("METRICS_PORT")
pInt, err := strconv.Atoi(pStr)
if err == nil && pInt != 0 {
p = pInt
}
}
return p
}
// GetMetricInterval - get the publish metric interval
func GetMetricIntervalInMinutes() time.Duration {
//default 15 minutes

View file

@ -59,32 +59,8 @@ func setNetworkDefaults() error {
return err
}
for _, network := range networks {
update := false
newNet := network
if strings.Contains(network.NetID, ".") {
newNet.NetID = strings.ReplaceAll(network.NetID, ".", "")
newNet.DefaultInterface = strings.ReplaceAll(network.DefaultInterface, ".", "")
update = true
}
if strings.ContainsAny(network.NetID, "ABCDEFGHIJKLMNOPQRSTUVWXYZ") {
newNet.NetID = strings.ToLower(network.NetID)
newNet.DefaultInterface = strings.ToLower(network.DefaultInterface)
update = true
}
if update {
newNet.SetDefaults()
if err := logic.SaveNetwork(&newNet); err != nil {
logger.Log(0, "error saving networks during initial update:", err.Error())
}
if err := logic.DeleteNetwork(network.NetID); err != nil {
logger.Log(0, "error deleting old network:", err.Error())
}
} else {
network.SetDefaults()
_, _, _, err = logic.UpdateNetwork(&network, &network)
if err != nil {
logger.Log(0, "could not set defaults on network", network.NetID)
}
if network.SetDefaults() {
logic.SaveNetwork(&network)
}
}
return nil

View file

@ -1,6 +1,10 @@
package utils
import "time"
import (
"log/slog"
"runtime"
"time"
)
// RetryStrategy specifies a strategy to retry an operation after waiting a while,
// with hooks for successful and unsuccessful (>=max) tries.
@ -39,3 +43,19 @@ func (rs RetryStrategy) DoStrategy() {
return
}
}
func TraceCaller() {
// Skip 1 frame to get the caller of this function
pc, file, line, ok := runtime.Caller(2)
if !ok {
slog.Debug("Unable to get caller information")
return
}
// Get function name from the program counter (pc)
funcName := runtime.FuncForPC(pc).Name()
// Print trace details
slog.Debug("Called from function: %s\n", "func-name", funcName)
slog.Debug("File: %s, Line: %d\n", "file", file, "line-no", line)
}