mirror of
https://github.com/gravitl/netmaker.git
synced 2025-09-07 21:54:54 +08:00
Merge branch 'develop' into master
This commit is contained in:
commit
ee6bea117f
46 changed files with 1215 additions and 727 deletions
4
.github/workflows/deletedroplets.yml
vendored
4
.github/workflows/deletedroplets.yml
vendored
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"`
|
||||
|
|
|
@ -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
207
controllers/gateway.go
Normal 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)
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
10
go.mod
|
@ -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
20
go.sum
|
@ -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=
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
122
logic/peers.go
122
logic/peers.go
|
@ -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
|
||||
}
|
||||
|
||||
|
|
238
logic/relay.go
238
logic/relay.go
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
9
models/gateway.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
package models
|
||||
|
||||
type CreateGwReq struct {
|
||||
IngressRequest
|
||||
RelayRequest
|
||||
}
|
||||
|
||||
type DeleteGw struct {
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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"`
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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"))
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue