diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index 24a5b3429..81e79895a 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -31,6 +31,7 @@ body: label: Version description: What version are you running? options: + - v0.24.3 - v0.24.2 - v0.24.1 - v0.24.0 diff --git a/.github/workflows/deletedroplets.yml b/.github/workflows/deletedroplets.yml index d2fc1253b..040190384 100644 --- a/.github/workflows/deletedroplets.yml +++ b/.github/workflows/deletedroplets.yml @@ -12,7 +12,7 @@ jobs: if: ${{ github.event.workflow_run.conclusion == 'success' }} steps: - name: get logs - uses: dawidd6/action-download-artifact@v3 + uses: dawidd6/action-download-artifact@v6 with: run_id: ${{ github.event.workflow_run.id}} if_no_artifact_found: warn @@ -60,7 +60,7 @@ jobs: if: ${{ github.event.workflow_run.conclusion == 'failure' }} steps: - name: get logs - uses: dawidd6/action-download-artifact@v3 + uses: dawidd6/action-download-artifact@v6 with: run_id: ${{ github.event.workflow_run.id}} if_no_artifact_found: warn diff --git a/.github/workflows/docker-builder.yml b/.github/workflows/docker-builder.yml index 69373f882..fe2cd4aae 100644 --- a/.github/workflows/docker-builder.yml +++ b/.github/workflows/docker-builder.yml @@ -20,7 +20,7 @@ jobs: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build and push to docker hub - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . push: true diff --git a/.github/workflows/publish-docker.yml b/.github/workflows/publish-docker.yml index 889bcd8a3..61f42da9a 100644 --- a/.github/workflows/publish-docker.yml +++ b/.github/workflows/publish-docker.yml @@ -44,7 +44,7 @@ jobs: password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build and push - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . platforms: linux/amd64, linux/arm64, linux/arm/v7 @@ -84,7 +84,7 @@ jobs: password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build and push - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . platforms: linux/amd64, linux/arm64 diff --git a/Dockerfile b/Dockerfile index 4be29ea6d..c7c6369c9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ #first stage - builder -FROM gravitl/go-builder as builder +FROM gravitl/go-builder AS builder ARG tags WORKDIR /app COPY . . diff --git a/README.md b/README.md index 8e09b8530..6fb0fc6a3 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@

- + diff --git a/auth/host_session.go b/auth/host_session.go index 0113351a3..d6869ed0c 100644 --- a/auth/host_session.go +++ b/auth/host_session.go @@ -164,10 +164,6 @@ func SessionHandler(conn *websocket.Conn) { logger.Log(0, "failed to create host credentials for EMQX: ", err.Error()) return } - if err := mq.GetEmqxHandler().CreateHostACL(result.Host.ID.String(), servercfg.GetServerInfo().Server); err != nil { - logger.Log(0, "failed to add host ACL rules to EMQX: ", err.Error()) - return - } } logic.CheckHostPorts(&result.Host) if err := logic.CreateHost(&result.Host); err != nil { diff --git a/compose/docker-compose.netclient.yml b/compose/docker-compose.netclient.yml index 4d42c5952..50af717a0 100644 --- a/compose/docker-compose.netclient.yml +++ b/compose/docker-compose.netclient.yml @@ -3,7 +3,7 @@ version: "3.4" services: netclient: container_name: netclient - image: 'gravitl/netclient:v0.24.2' + image: 'gravitl/netclient:v0.24.3' hostname: netmaker-1 network_mode: host restart: on-failure diff --git a/config/config.go b/config/config.go index 522372eff..591c8e3df 100644 --- a/config/config.go +++ b/config/config.go @@ -94,6 +94,7 @@ type ServerConfig struct { CacheEnabled string `yaml:"caching_enabled"` EndpointDetection bool `json:"endpoint_detection"` AllowedEmailDomains string `yaml:"allowed_email_domains"` + MetricInterval string `yaml:"metric_interval"` } // SQLConfig - Generic SQL Config diff --git a/controllers/docs.go b/controllers/docs.go index 05c769e47..9b5985b98 100644 --- a/controllers/docs.go +++ b/controllers/docs.go @@ -10,7 +10,7 @@ // // Schemes: https // BasePath: / -// Version: 0.24.2 +// Version: 0.24.3 // Host: api.demo.netmaker.io // // Consumes: diff --git a/controllers/enrollmentkeys.go b/controllers/enrollmentkeys.go index 2cfb13637..87a189a4a 100644 --- a/controllers/enrollmentkeys.go +++ b/controllers/enrollmentkeys.go @@ -315,10 +315,6 @@ func handleHostRegister(w http.ResponseWriter, r *http.Request) { logger.Log(0, "failed to create host credentials for EMQX: ", err.Error()) return } - if err := mq.GetEmqxHandler().CreateHostACL(newHost.ID.String(), servercfg.GetServerInfo().Server); err != nil { - logger.Log(0, "failed to add host ACL rules to EMQX: ", err.Error()) - return - } } if err = logic.CreateHost(&newHost); err != nil { logger.Log( diff --git a/controllers/ext_client.go b/controllers/ext_client.go index eb5308bba..1b1ec3dc7 100644 --- a/controllers/ext_client.go +++ b/controllers/ext_client.go @@ -386,6 +386,17 @@ func createExtClient(w http.ResponseWriter, r *http.Request) { logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) return } + + var gateway models.EgressGatewayRequest + gateway.NetID = params["network"] + gateway.Ranges = customExtClient.ExtraAllowedIPs + err := logic.ValidateEgressRange(gateway) + if err != nil { + logger.Log(0, r.Header.Get("user"), "error validating egress range: ", err.Error()) + logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) + return + } + node, err := logic.GetNodeByID(nodeid) if err != nil { logger.Log(0, r.Header.Get("user"), @@ -530,6 +541,17 @@ func updateExtClient(w http.ResponseWriter, r *http.Request) { return } } + + var gateway models.EgressGatewayRequest + gateway.NetID = params["network"] + gateway.Ranges = update.ExtraAllowedIPs + err = logic.ValidateEgressRange(gateway) + if err != nil { + logger.Log(0, r.Header.Get("user"), "error validating egress range: ", err.Error()) + logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) + return + } + var changedID = update.ClientID != oldExtClient.ClientID if !reflect.DeepEqual(update.DeniedACLs, oldExtClient.DeniedACLs) { diff --git a/controllers/hosts.go b/controllers/hosts.go index f6a88aa42..440fd74e9 100644 --- a/controllers/hosts.go +++ b/controllers/hosts.go @@ -233,7 +233,8 @@ func hostUpdateFallback(w http.ResponseWriter, r *http.Request) { logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) return } - + var sendPeerUpdate bool + var replacePeers bool var hostUpdate models.HostUpdate err = json.NewDecoder(r.Body).Decode(&hostUpdate) if err != nil { @@ -244,22 +245,32 @@ func hostUpdateFallback(w http.ResponseWriter, r *http.Request) { slog.Info("recieved host update", "name", hostUpdate.Host.Name, "id", hostUpdate.Host.ID) switch hostUpdate.Action { case models.CheckIn: - _ = mq.HandleHostCheckin(&hostUpdate.Host, currentHost) + sendPeerUpdate = mq.HandleHostCheckin(&hostUpdate.Host, currentHost) case models.UpdateHost: - - _ = logic.UpdateHostFromClient(&hostUpdate.Host, currentHost) + if hostUpdate.Host.PublicKey != currentHost.PublicKey { + //remove old peer entry + replacePeers = true + } + sendPeerUpdate = logic.UpdateHostFromClient(&hostUpdate.Host, currentHost) err := logic.UpsertHost(currentHost) if err != nil { slog.Error("failed to update host", "id", currentHost.ID, "error", err) logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) return } + case models.UpdateMetrics: mq.UpdateMetricsFallBack(hostUpdate.Node.ID.String(), hostUpdate.NewMetrics) } - logic.ReturnSuccessResponse(w, r, "updated host data") + if sendPeerUpdate { + err := mq.PublishPeerUpdate(replacePeers) + if err != nil { + slog.Error("failed to publish peer update", "error", err) + } + } + logic.ReturnSuccessResponse(w, r, "updated host data") } // swagger:route DELETE /api/hosts/{hostid} hosts deleteHost @@ -555,23 +566,10 @@ func authenticateHost(response http.ResponseWriter, request *http.Request) { return } go func() { - // Create EMQX creds and ACLs if not found + // Create EMQX creds if servercfg.GetBrokerType() == servercfg.EmqxBrokerType { if err := mq.GetEmqxHandler().CreateEmqxUser(host.ID.String(), authRequest.Password); err != nil { slog.Error("failed to create host credentials for EMQX: ", err.Error()) - } else { - if err := mq.GetEmqxHandler().CreateHostACL(host.ID.String(), servercfg.GetServerInfo().Server); err != nil { - slog.Error("failed to add host ACL rules to EMQX: ", err.Error()) - } - for _, nodeID := range host.Nodes { - if node, err := logic.GetNodeByID(nodeID); err == nil { - if err = mq.GetEmqxHandler().AppendNodeUpdateACL(host.ID.String(), node.Network, node.ID.String(), servercfg.GetServer()); err != nil { - slog.Error("failed to add ACLs for EMQX node", "error", err) - } - } else { - slog.Error("failed to get node", "nodeid", nodeID, "error", err) - } - } } } }() diff --git a/controllers/node.go b/controllers/node.go index ed104b354..b7027f2c5 100644 --- a/controllers/node.go +++ b/controllers/node.go @@ -415,6 +415,12 @@ func createEgressGateway(w http.ResponseWriter, r *http.Request) { } gateway.NetID = params["network"] gateway.NodeID = params["nodeid"] + err = logic.ValidateEgressRange(gateway) + if err != nil { + logger.Log(0, r.Header.Get("user"), "error validating egress range: ", err.Error()) + logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) + return + } node, err = logic.CreateEgressGateway(gateway) if err != nil { logger.Log(0, r.Header.Get("user"), @@ -631,10 +637,6 @@ func updateNode(w http.ResponseWriter, r *http.Request) { logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) return } - if len(newData.Metadata) > 255 { - logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("metadata cannot be longer than 255 characters"), "badrequest")) - return - } if !servercfg.IsPro { newData.AdditionalRagIps = []string{} } diff --git a/controllers/server.go b/controllers/server.go index 6e96688c4..6c8c121e0 100644 --- a/controllers/server.go +++ b/controllers/server.go @@ -117,6 +117,7 @@ func getStatus(w http.ResponseWriter, r *http.Request) { type status struct { DB bool `json:"db_connected"` Broker bool `json:"broker_connected"` + IsBrokerConnOpen bool `json:"is_broker_conn_open"` LicenseError string `json:"license_error"` IsPro bool `json:"is_pro"` TrialEndDate time.Time `json:"trial_end_date"` @@ -141,6 +142,7 @@ func getStatus(w http.ResponseWriter, r *http.Request) { currentServerStatus := status{ DB: database.IsConnected(), Broker: mq.IsConnected(), + IsBrokerConnOpen: mq.IsConnectionOpen(), LicenseError: licenseErr, IsPro: servercfg.IsPro, TrialEndDate: trialEndDate, diff --git a/go.mod b/go.mod index 6587dcacb..0dafb218f 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.19 require ( github.com/eclipse/paho.mqtt.golang v1.4.3 - github.com/go-playground/validator/v10 v10.20.0 + github.com/go-playground/validator/v10 v10.22.0 github.com/golang-jwt/jwt/v4 v4.5.0 github.com/google/uuid v1.6.0 github.com/gorilla/handlers v1.5.2 @@ -12,14 +12,15 @@ require ( github.com/lib/pq v1.10.9 github.com/mattn/go-sqlite3 v1.14.22 github.com/rqlite/gorqlite v0.0.0-20240122221808-a8a425b1a6aa + github.com/seancfoley/ipaddress-go v1.6.0 github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e github.com/stretchr/testify v1.9.0 github.com/txn2/txeh v1.5.5 golang.org/x/crypto v0.23.0 golang.org/x/net v0.22.0 // indirect - golang.org/x/oauth2 v0.20.0 - golang.org/x/sys v0.20.0 // indirect - golang.org/x/text v0.15.0 // indirect + golang.org/x/oauth2 v0.21.0 + golang.org/x/sys v0.21.0 // indirect + golang.org/x/text v0.16.0 // indirect golang.zx2c4.com/wireguard/wgctrl v0.0.0-20221104135756-97bc4ad4a1cb gopkg.in/yaml.v3 v3.0.1 ) @@ -32,7 +33,7 @@ require ( require ( github.com/coreos/go-oidc/v3 v3.9.0 - github.com/gorilla/websocket v1.5.1 + github.com/gorilla/websocket v1.5.3 golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 ) @@ -41,7 +42,7 @@ require ( github.com/guumaster/tablewriter v0.0.10 github.com/matryer/is v1.4.1 github.com/olekukonko/tablewriter v0.0.5 - github.com/spf13/cobra v1.8.0 + github.com/spf13/cobra v1.8.1 ) require ( @@ -49,6 +50,7 @@ require ( github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/rivo/uniseg v0.2.0 // indirect + github.com/seancfoley/bintree v1.3.1 // indirect github.com/spf13/pflag v1.0.5 // indirect ) @@ -62,5 +64,5 @@ require ( github.com/mattn/go-runewidth v0.0.13 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect - golang.org/x/sync v0.1.0 // indirect + golang.org/x/sync v0.7.0 // indirect ) diff --git a/go.sum b/go.sum index 1ec3a8eb1..cb78d0be7 100644 --- a/go.sum +++ b/go.sum @@ -8,7 +8,7 @@ github.com/c-robinson/iplib v1.0.8/go.mod h1:i3LuuFL1hRT5gFpBRnEydzw8R6yhGkF4szN github.com/coreos/go-oidc/v3 v3.9.0 h1:0J/ogVOd4y8P0f0xUh8l9t07xRP/d8tccvjHl2dcsSo= github.com/coreos/go-oidc/v3 v3.9.0/go.mod h1:rTKz2PYwftcrtoCzV5g5kvfJoWcm0Mk8AF8y1iAQro4= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -25,8 +25,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.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= -github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao= +github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= @@ -37,8 +37,8 @@ github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyE github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= -github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= -github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/guumaster/tablewriter v0.0.10 h1:A0HD94yMdt4usgxBjoEceNeE0XMJ027euoHAzsPqBQs= github.com/guumaster/tablewriter v0.0.10/go.mod h1:p4FRFhyfo0UD9ZLmMRbbJooTUsxo6b80qZTERVDWrH8= github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= @@ -70,11 +70,15 @@ github.com/rqlite/gorqlite v0.0.0-20240122221808-a8a425b1a6aa h1:hxMLFbj+F444JAS github.com/rqlite/gorqlite v0.0.0-20240122221808-a8a425b1a6aa/go.mod h1:xF/KoXmrRyahPfo5L7Szb5cAAUl53dMWBh9cMruGEZg= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/seancfoley/bintree v1.3.1 h1:cqmmQK7Jm4aw8gna0bP+huu5leVOgHGSJBEpUx3EXGI= +github.com/seancfoley/bintree v1.3.1/go.mod h1:hIUabL8OFYyFVTQ6azeajbopogQc2l5C/hiXMcemWNU= +github.com/seancfoley/ipaddress-go v1.6.0 h1:9z7yGmOnV4P2ML/dlR/kCJiv5tp8iHOOetJvxJh/R5w= +github.com/seancfoley/ipaddress-go v1.6.0/go.mod h1:TQRZgv+9jdvzHmKoPGBMxyiaVmoI0rYpfEk8Q/sL/Iw= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= -github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= -github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -103,12 +107,13 @@ 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.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo= -golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= +golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -117,8 +122,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.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.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= @@ -130,8 +135,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= diff --git a/k8s/client/netclient-daemonset.yaml b/k8s/client/netclient-daemonset.yaml index 2f39ce113..d75e1c19c 100644 --- a/k8s/client/netclient-daemonset.yaml +++ b/k8s/client/netclient-daemonset.yaml @@ -16,7 +16,7 @@ spec: hostNetwork: true containers: - name: netclient - image: gravitl/netclient:v0.24.2 + image: gravitl/netclient:v0.24.3 env: - name: TOKEN value: "TOKEN_VALUE" diff --git a/k8s/client/netclient.yaml b/k8s/client/netclient.yaml index 19c381697..8fa800731 100644 --- a/k8s/client/netclient.yaml +++ b/k8s/client/netclient.yaml @@ -28,7 +28,7 @@ spec: # - "" containers: - name: netclient - image: gravitl/netclient:v0.24.2 + image: gravitl/netclient:v0.24.3 env: - name: TOKEN value: "TOKEN_VALUE" diff --git a/k8s/server/netmaker-ui.yaml b/k8s/server/netmaker-ui.yaml index e137d3448..e1e60b547 100644 --- a/k8s/server/netmaker-ui.yaml +++ b/k8s/server/netmaker-ui.yaml @@ -15,7 +15,7 @@ spec: spec: containers: - name: netmaker-ui - image: gravitl/netmaker-ui:v0.24.2 + image: gravitl/netmaker-ui:v0.24.3 ports: - containerPort: 443 env: diff --git a/logic/gateway.go b/logic/gateway.go index 154a2a8d8..0d99af88e 100644 --- a/logic/gateway.go +++ b/logic/gateway.go @@ -74,7 +74,7 @@ func CreateEgressGateway(gateway models.EgressGatewayRequest) (models.Node, erro return models.Node{}, errors.New(host.OS + " is unsupported for egress gateways") } if host.FirewallInUse == models.FIREWALL_NONE { - return models.Node{}, errors.New("firewall is not supported for egress gateways") + return models.Node{}, errors.New("firewall is not supported for egress gateways. please install iptables or nftables on the device in order to use this feature") } for i := len(gateway.Ranges) - 1; i >= 0; i-- { // check if internet gateway IPv4 diff --git a/logic/nodes.go b/logic/nodes.go index 72f07836d..62f49557c 100644 --- a/logic/nodes.go +++ b/logic/nodes.go @@ -19,6 +19,7 @@ import ( "github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/servercfg" "github.com/gravitl/netmaker/validation" + "github.com/seancfoley/ipaddress-go/ipaddr" "golang.org/x/exp/slog" ) @@ -626,6 +627,39 @@ func ValidateParams(nodeid, netid string) (models.Node, error) { return node, nil } +func ValidateEgressRange(gateway models.EgressGatewayRequest) error { + network, err := GetNetworkSettings(gateway.NetID) + if err != nil { + slog.Error("error getting network with netid", "error", gateway.NetID, err.Error) + return errors.New("error getting network with netid: " + gateway.NetID + " " + err.Error()) + } + ipv4Net := network.AddressRange + ipv6Net := network.AddressRange6 + + for _, v := range gateway.Ranges { + if ipv4Net != "" { + if ContainsCIDR(ipv4Net, v) { + slog.Error("egress range should not be the same as or contained in the netmaker network address", "error", v, ipv4Net) + return errors.New("egress range should not be the same as or contained in the netmaker network address" + v + " " + ipv4Net) + } + } + if ipv6Net != "" { + if ContainsCIDR(ipv6Net, v) { + slog.Error("egress range should not be the same as or contained in the netmaker network address", "error", v, ipv6Net) + return errors.New("egress range should not be the same as or contained in the netmaker network address" + v + " " + ipv6Net) + } + } + } + + return nil +} + +func ContainsCIDR(net1, net2 string) bool { + one, two := ipaddr.NewIPAddressString(net1), + ipaddr.NewIPAddressString(net2) + return one.Contains(two) || two.Contains(one) +} + // GetAllFailOvers - gets all the nodes that are failovers func GetAllFailOvers() ([]models.Node, error) { nodes, err := GetAllNodes() diff --git a/logic/nodes_test.go b/logic/nodes_test.go new file mode 100644 index 000000000..e3331a6fd --- /dev/null +++ b/logic/nodes_test.go @@ -0,0 +1,33 @@ +package logic + +import ( + "testing" +) + +func TestContainsCIDR(t *testing.T) { + + b := ContainsCIDR("10.1.1.2/32", "10.1.1.0/24") + if !b { + t.Errorf("expected true, returned %v", b) + } + + b = ContainsCIDR("10.1.1.2/32", "10.5.1.0/24") + if b { + t.Errorf("expected false, returned %v", b) + } + + b = ContainsCIDR("fd52:65f5:d685:d11d::1/64", "fd52:65f5:d685:d11d::/64") + if !b { + t.Errorf("expected true, returned %v", b) + } + + b1 := ContainsCIDR("fd10:10::/64", "fd10::/16") + if !b1 { + t.Errorf("expected true, returned %v", b1) + } + + b1 = ContainsCIDR("fd10:10::/64", "fd10::/64") + if b1 { + t.Errorf("expected false, returned %v", b1) + } +} diff --git a/main.go b/main.go index 9b8f5eea6..0103cf349 100644 --- a/main.go +++ b/main.go @@ -27,7 +27,7 @@ import ( "golang.org/x/exp/slog" ) -var version = "v0.24.2" +var version = "v0.24.3" // Start DB Connection and start API Request Handler func main() { diff --git a/models/api_node.go b/models/api_node.go index 6015f4e86..e7005f327 100644 --- a/models/api_node.go +++ b/models/api_node.go @@ -36,7 +36,7 @@ type ApiNode struct { Server string `json:"server"` Connected bool `json:"connected"` PendingDelete bool `json:"pendingdelete"` - Metadata string `json:"metadata" validate:"max=256"` + Metadata string `json:"metadata"` // == PRO == DefaultACL string `json:"defaultacl,omitempty" validate:"checkyesornoorunset"` IsFailOver bool `json:"is_fail_over"` diff --git a/models/host.go b/models/host.go index 684cc7d1e..2781dee0e 100644 --- a/models/host.go +++ b/models/host.go @@ -116,6 +116,8 @@ const ( UpdateKeys HostMqAction = "UPDATE_KEYS" // RequestPull - request a pull from a host RequestPull HostMqAction = "REQ_PULL" + // SignalPull - request a pull from a host without restart + SignalPull HostMqAction = "SIGNAL_PULL" // UpdateMetrics - updates metrics data UpdateMetrics HostMqAction = "UPDATE_METRICS" ) diff --git a/models/structs.go b/models/structs.go index d4ea11711..594275333 100644 --- a/models/structs.go +++ b/models/structs.go @@ -273,19 +273,20 @@ 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"` + 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"` } // User.NameInCharset - returns if name is in charset below or not diff --git a/mq/emqx.go b/mq/emqx.go index 8b9b9f09f..43b8390b8 100644 --- a/mq/emqx.go +++ b/mq/emqx.go @@ -10,10 +10,7 @@ type Emqx interface { CreateEmqxUserforServer() error CreateEmqxDefaultAuthenticator() error CreateEmqxDefaultAuthorizer() error - CreateDefaultDenyRule() error - CreateHostACL(hostID, serverName string) error - AppendNodeUpdateACL(hostID, nodeNetwork, nodeID, serverName string) error - GetUserACL(username string) (*aclObject, error) + CreateDefaultAllowRule() error DeleteEmqxUser(username string) error } diff --git a/mq/emqx_cloud.go b/mq/emqx_cloud.go index 23bfb2448..aabac0dbd 100644 --- a/mq/emqx_cloud.go +++ b/mq/emqx_cloud.go @@ -89,21 +89,10 @@ func (e *EmqxCloud) CreateEmqxDefaultAuthenticator() error { return nil } // ign func (e *EmqxCloud) CreateEmqxDefaultAuthorizer() error { return nil } // ignore -func (e *EmqxCloud) CreateDefaultDenyRule() error { +func (e *EmqxCloud) CreateDefaultAllowRule() error { return nil } -func (e *EmqxCloud) CreateHostACL(hostID, serverName string) error { - return nil -} - -func (e *EmqxCloud) AppendNodeUpdateACL(hostID, nodeNetwork, nodeID, serverName string) error { - return nil - -} - -func (e *EmqxCloud) GetUserACL(username string) (*aclObject, error) { return nil, nil } // ununsed on cloud since it doesn't overwrite acls list - func (e *EmqxCloud) DeleteEmqxUser(username string) error { client := &http.Client{} diff --git a/mq/emqx_on_prem.go b/mq/emqx_on_prem.go index f116f0a86..d69067f3c 100644 --- a/mq/emqx_on_prem.go +++ b/mq/emqx_on_prem.go @@ -7,7 +7,6 @@ import ( "io" "net/http" "strings" - "sync" "github.com/gravitl/netmaker/servercfg" ) @@ -246,45 +245,14 @@ func (e *EmqxOnPrem) CreateEmqxDefaultAuthorizer() error { return nil } -// GetUserACL - returns ACL rules by username -func (e *EmqxOnPrem) GetUserACL(username string) (*aclObject, error) { - token, err := getEmqxAuthToken() - if err != nil { - return nil, err - } - req, err := http.NewRequest(http.MethodGet, servercfg.GetEmqxRestEndpoint()+"/api/v5/authorization/sources/built_in_database/username/"+username, nil) - if err != nil { - return nil, err - } - req.Header.Add("content-type", "application/json") - req.Header.Add("authorization", "Bearer "+token) - resp, err := (&http.Client{}).Do(req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - response, err := io.ReadAll(resp.Body) - if err != nil { - return nil, err - } - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("error fetching ACL rules %v", string(response)) - } - body := new(aclObject) - if err := json.Unmarshal(response, body); err != nil { - return nil, err - } - return body, nil -} - -// CreateDefaultDenyRule - creates a rule to deny access to all topics for all users by default +// CreateDefaultAllowRule - creates a rule to deny access to all topics for all users by default // to allow user access to topics use the `mq.CreateUserAccessRule` function -func (e *EmqxOnPrem) CreateDefaultDenyRule() error { +func (e *EmqxOnPrem) CreateDefaultAllowRule() error { token, err := getEmqxAuthToken() if err != nil { return err } - payload, err := json.Marshal(&aclObject{Rules: []aclRule{{Topic: "#", Permission: "deny", Action: "all"}}}) + payload, err := json.Marshal(&aclObject{Rules: []aclRule{{Topic: "#", Permission: "allow", Action: "all"}}}) if err != nil { return err } @@ -308,121 +276,3 @@ func (e *EmqxOnPrem) CreateDefaultDenyRule() error { } return nil } - -// CreateHostACL - create host ACL rules -func (e *EmqxOnPrem) CreateHostACL(hostID, serverName string) error { - token, err := getEmqxAuthToken() - if err != nil { - return err - } - payload, err := json.Marshal(&aclObject{ - Username: hostID, - Rules: []aclRule{ - { - Topic: fmt.Sprintf("peers/host/%s/%s", hostID, serverName), - Permission: "allow", - Action: "all", - }, - { - Topic: fmt.Sprintf("host/update/%s/%s", hostID, serverName), - Permission: "allow", - Action: "all", - }, - { - Topic: fmt.Sprintf("host/serverupdate/%s/%s", serverName, hostID), - Permission: "allow", - Action: "all", - }, - }, - }) - if err != nil { - return err - } - req, err := http.NewRequest(http.MethodPut, servercfg.GetEmqxRestEndpoint()+"/api/v5/authorization/sources/built_in_database/username/"+hostID, bytes.NewReader(payload)) - if err != nil { - return err - } - req.Header.Add("content-type", "application/json") - req.Header.Add("authorization", "Bearer "+token) - resp, err := (&http.Client{}).Do(req) - if err != nil { - return err - } - defer resp.Body.Close() - if resp.StatusCode != http.StatusNoContent { - msg, err := io.ReadAll(resp.Body) - if err != nil { - return err - } - return fmt.Errorf("error adding ACL Rules for user %s Error: %v", hostID, string(msg)) - } - return nil -} - -// a lock required for preventing simultaneous updates to the same ACL object leading to overwriting each other -// might occur when multiple nodes belonging to the same host are created at the same time -var nodeAclMux sync.Mutex - -// AppendNodeUpdateACL - adds ACL rule for subscribing to node updates for a node ID -func (e *EmqxOnPrem) AppendNodeUpdateACL(hostID, nodeNetwork, nodeID, serverName string) error { - nodeAclMux.Lock() - defer nodeAclMux.Unlock() - token, err := getEmqxAuthToken() - if err != nil { - return err - } - aclObject, err := emqx.GetUserACL(hostID) - if err != nil { - return err - } - aclObject.Rules = append(aclObject.Rules, []aclRule{ - { - Topic: fmt.Sprintf("node/update/%s/%s", nodeNetwork, nodeID), - Permission: "allow", - Action: "subscribe", - }, - { - Topic: fmt.Sprintf("ping/%s/%s", serverName, nodeID), - Permission: "allow", - Action: "all", - }, - { - Topic: fmt.Sprintf("update/%s/%s", serverName, nodeID), - Permission: "allow", - Action: "all", - }, - { - Topic: fmt.Sprintf("signal/%s/%s", serverName, nodeID), - Permission: "allow", - Action: "all", - }, - { - Topic: fmt.Sprintf("metrics/%s/%s", serverName, nodeID), - Permission: "allow", - Action: "all", - }, - }...) - payload, err := json.Marshal(aclObject) - if err != nil { - return err - } - req, err := http.NewRequest(http.MethodPut, servercfg.GetEmqxRestEndpoint()+"/api/v5/authorization/sources/built_in_database/username/"+hostID, bytes.NewReader(payload)) - if err != nil { - return err - } - req.Header.Add("content-type", "application/json") - req.Header.Add("authorization", "Bearer "+token) - resp, err := (&http.Client{}).Do(req) - if err != nil { - return err - } - defer resp.Body.Close() - if resp.StatusCode != http.StatusNoContent { - msg, err := io.ReadAll(resp.Body) - if err != nil { - return err - } - return fmt.Errorf("error adding ACL Rules for user %s Error: %v", hostID, string(msg)) - } - return nil -} diff --git a/mq/handlers.go b/mq/handlers.go index 1c087393a..8adb0744f 100644 --- a/mq/handlers.go +++ b/mq/handlers.go @@ -113,12 +113,6 @@ func UpdateHost(client mqtt.Client, msg mqtt.Message) { slog.Error("failed to send new node to host", "name", hostUpdate.Host.Name, "id", currentHost.ID, "error", err) return } else { - if servercfg.GetBrokerType() == servercfg.EmqxBrokerType { - if err = emqx.AppendNodeUpdateACL(hu.Host.ID.String(), hu.Node.Network, hu.Node.ID.String(), servercfg.GetServer()); err != nil { - slog.Error("failed to add ACLs for EMQX node", "error", err) - return - } - } nodes, err := logic.GetAllNodes() if err != nil { return diff --git a/mq/mq.go b/mq/mq.go index 92a6200b5..a143b1abd 100644 --- a/mq/mq.go +++ b/mq/mq.go @@ -58,7 +58,7 @@ func SetupMQTT(fatal bool) { logger.Log(0, err.Error()) } // create a default deny ACL to all topics for all users - if err := emqx.CreateDefaultDenyRule(); err != nil { + if err := emqx.CreateDefaultAllowRule(); err != nil { log.Fatal(err) } } else { @@ -142,6 +142,11 @@ func Keepalive(ctx context.Context) { // IsConnected - function for determining if the mqclient is connected or not func IsConnected() bool { + return mqclient != nil && mqclient.IsConnected() +} + +// IsConnectionOpen - function for determining if the mqclient is connected or not +func IsConnectionOpen() bool { return mqclient != nil && mqclient.IsConnectionOpen() } diff --git a/mq/publishers.go b/mq/publishers.go index 6c231b01d..b3f3efda5 100644 --- a/mq/publishers.go +++ b/mq/publishers.go @@ -35,7 +35,6 @@ func PublishPeerUpdate(replacePeers bool) error { logger.Log(1, "failed to publish peer update to host", host.ID.String(), ": ", err.Error()) } }(host) - } return err } @@ -217,30 +216,14 @@ func sendPeers() { if err != nil && len(hosts) > 0 { logger.Log(1, "error retrieving networks for keepalive", err.Error()) } - nodes, err := logic.GetAllNodes() - if err != nil { - return - } - var force bool + peer_force_send++ if peer_force_send == 5 { servercfg.SetHost() - force = true peer_force_send = 0 err := logic.TimerCheckpoint() // run telemetry & log dumps if 24 hours has passed.. if err != nil { logger.Log(3, "error occurred on timer,", err.Error()) } - - //collectServerMetrics(networks[:]) - } - if force { - for _, host := range hosts { - host := host - logger.Log(2, "sending scheduled peer update (5 min)") - if err = PublishSingleHostPeerUpdate(&host, nodes, nil, nil, false); err != nil { - logger.Log(1, "error publishing peer updates for host: ", host.ID.String(), " Err: ", err.Error()) - } - } } } diff --git a/pro/controllers/failover.go b/pro/controllers/failover.go index 04262165d..42c027340 100644 --- a/pro/controllers/failover.go +++ b/pro/controllers/failover.go @@ -19,12 +19,44 @@ import ( // FailOverHandlers - handlers for FailOver func FailOverHandlers(r *mux.Router) { + r.HandleFunc("/api/v1/node/{nodeid}/failover", http.HandlerFunc(getfailOver)).Methods(http.MethodGet) r.HandleFunc("/api/v1/node/{nodeid}/failover", logic.SecurityCheck(true, http.HandlerFunc(createfailOver))).Methods(http.MethodPost) r.HandleFunc("/api/v1/node/{nodeid}/failover", logic.SecurityCheck(true, http.HandlerFunc(deletefailOver))).Methods(http.MethodDelete) r.HandleFunc("/api/v1/node/{network}/failover/reset", logic.SecurityCheck(true, http.HandlerFunc(resetFailOver))).Methods(http.MethodPost) r.HandleFunc("/api/v1/node/{nodeid}/failover_me", controller.Authorize(true, false, "host", http.HandlerFunc(failOverME))).Methods(http.MethodPost) } +// swagger:route GET /api/v1/node/failover node getfailOver +// +// get failover node. +// +// Schemes: https +// +// Security: +// oauth +// +// Responses: +// 200: nodeResponse +func getfailOver(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 { + slog.Error("failed to get node:", "error", err.Error()) + logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) + return + } + + failOverNode, exists := proLogic.FailOverExists(node.Network) + if !exists { + logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("failover node not found"), "notfound")) + return + } + w.Header().Set("Content-Type", "application/json") + logic.ReturnSuccessResponseWithJson(w, r, failOverNode, "get failover node successfully") +} + // swagger:route POST /api/v1/node/failover node createfailOver // // Create a relay. diff --git a/pro/logic/nodes.go b/pro/logic/nodes.go index 9fbc49e9a..ca8ca94af 100644 --- a/pro/logic/nodes.go +++ b/pro/logic/nodes.go @@ -59,7 +59,7 @@ func ValidateInetGwReq(inetNode models.Node, req models.InetNodeReq, update bool ResetFailedOverPeer(&clientNode) } - if clientNode.IsRelayed { + if clientNode.IsRelayed && clientNode.RelayedBy != inetNode.ID.String() { return fmt.Errorf("node %s is being relayed", clientHost.Name) } diff --git a/pro/logic/relays.go b/pro/logic/relays.go index bc581def3..adb9bc6b4 100644 --- a/pro/logic/relays.go +++ b/pro/logic/relays.go @@ -123,6 +123,9 @@ func ValidateRelay(relay models.RelayRequest, update bool) error { 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 + ")") } diff --git a/release.md b/release.md index 09e6d7e32..32d783df1 100644 --- a/release.md +++ b/release.md @@ -1,18 +1,22 @@ -# Netmaker v0.24.2 +# Netmaker v0.24.3 ## Whats New ✨ -- Static Host Functionality With Separate Settings For Port and endpoint IP -- Network Info And Metadata Info Added To Remote-Access-Client +- Validation Checks For Egress Routes +- Network Change Detection System +- Removed Creation Of ACLs For EMQX ## What's Fixed/Improved 🛠 -- Improved FailOver Functionality -- Local Peer Routing In Dual-Stack Environment -- Stale Node Issue On Multinet With `netclient uninstall` -- IPv6 Internet Gateways Improvements -- Handled New Oauth User SignUp via Remote-Access-Client -- PeerUpdate Improvements Around Default Host and Multi-nets +- Removed RAG Metadata Length Restriction +- Scalability Improvements +- Optimised Traffic Flow Over MQ +- Improved Validation Checks For Internet GWS ## Known Issues 🐞 - Erratic Traffic Data In Metrics. -- Stale peer on the interface, when forced removed from multiple networks at once. +- Adding Custom Private/Public Key For Remote Access Gw Clients Doesn't Get Propagated To Other Peers. +- IPv6 DNS Entries Are Not Working. +- Stale Peer On The Interface, When Forced Removed From Multiple Networks At Once. +- Can Still Ping Domain Name Even When DNS Toggle Is Switched Off. +- WireGuard DNS issue on most flavors of Ubuntu 24.04 and some other newer Linux distributions. The issue is affecting the Remote Access Client (RAC) and the plain WireGuard external clients. Workaround can be found here https://help.netmaker.io/en/articles/9612016-extclient-rac-dns-issue-on-ubuntu-24-04. + diff --git a/servercfg/serverconf.go b/servercfg/serverconf.go index 56e00e340..1ab54b7fc 100644 --- a/servercfg/serverconf.go +++ b/servercfg/serverconf.go @@ -91,7 +91,7 @@ func GetServerConfig() config.ServerConfig { } cfg.JwtValidityDuration = GetJwtValidityDuration() cfg.RacAutoDisable = GetRacAutoDisable() - + cfg.MetricInterval = GetMetricInterval() return cfg } @@ -135,6 +135,7 @@ func GetServerInfo() models.ServerConfig { } cfg.Version = GetVersion() cfg.IsPro = IsPro + cfg.MetricInterval = GetMetricInterval() return cfg } @@ -586,6 +587,16 @@ func GetMqUserName() string { return password } +// GetMetricInterval - get the publish metric interval +func GetMetricInterval() string { + //default 15 minutes + mi := "15" + if os.Getenv("PUBLISH_METRIC_INTERVAL") != "" { + mi = os.Getenv("PUBLISH_METRIC_INTERVAL") + } + return mi +} + // GetEmqxRestEndpoint - returns the REST API Endpoint of EMQX func GetEmqxRestEndpoint() string { return os.Getenv("EMQX_REST_ENDPOINT") diff --git a/swagger.yml b/swagger.yml index f443861f0..4de02610f 100644 --- a/swagger.yml +++ b/swagger.yml @@ -1472,7 +1472,7 @@ info: API calls must be authenticated via a header of the format -H “Authorization: Bearer ” There are two methods to obtain YOUR_SECRET_KEY: 1. Using the masterkey. By default, this value is “secret key,” but you should change this on your instance and keep it secure. This value can be set via env var at startup or in a config file (config/environments/< env >.yaml). See the [Netmaker](https://docs.netmaker.org/index.html) documentation for more details. 2. Using a JWT received for a node. This can be retrieved by calling the /api/nodes//authenticate endpoint, as documented below. title: Netmaker - version: 0.24.2 + version: 0.24.3 paths: /api/dns: get: