Files
Abhishek Kondur 58a0a12d15 Merge pull request #4142 from gravitl/NM-386
NM-386: allow Windows internet egress and reject virtual NAT on Windows
2026-09-17 17:07:20 +05:30

164 lines
5.9 KiB
Go

package controller
import (
"context"
"encoding/json"
"errors"
"net/http"
"github.com/gorilla/mux"
"github.com/gravitl/netmaker/db"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/middleware"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/mq"
"github.com/gravitl/netmaker/scope"
)
// Deprecated: internet gateway management is handled via /api/v1/egress with type=internet.
// These handlers remain as thin wrappers that create/update/delete internet-type egress resources.
func internetGatewayHandlers(r *mux.Router) {
r.HandleFunc("/api/nodes/{network}/{nodeid}/inet_gw", middleware.Scope(scope.TenantScope, logic.SecurityCheck(true, http.HandlerFunc(createInternetGw)))).
Methods(http.MethodPost)
r.HandleFunc("/api/nodes/{network}/{nodeid}/inet_gw", middleware.Scope(scope.TenantScope, logic.SecurityCheck(true, http.HandlerFunc(updateInternetGw)))).
Methods(http.MethodPut)
r.HandleFunc("/api/nodes/{network}/{nodeid}/inet_gw", middleware.Scope(scope.TenantScope, logic.SecurityCheck(true, http.HandlerFunc(deleteInternetGw)))).
Methods(http.MethodDelete)
}
func createInternetGw(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
}
if logic.IsInternetGw(node) || logic.NodeIsInternetEgressRouter(r.Context(), node.ID.String(), node.Network) {
logic.ReturnSuccessResponse(w, r, "node is already acting as internet gateway")
return
}
var request models.InetNodeReq
err = json.NewDecoder(r.Body).Decode(&request)
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
err = logic.ValidateInetGwReq(r.Context(), logic.ConvertModelsNodeToSchemaNode(&node), request, false)
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
e, err := logic.CreateInternetEgressForNode(r.Context(), &node, "", r.Header.Get("user"))
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
for _, clientNodeID := range request.InetNodeClientIDs {
clientNode, err := logic.GetNodeByID(clientNodeID)
if err != nil {
continue
}
_ = logic.SetNodeSelectedInternetEgress(&clientNode, e.ID, clientNode.UseTcpUplink)
}
node.IsInternetGateway = true
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"))
return
}
apiNode := node.ConvertToAPINode()
logger.Log(1, r.Header.Get("user"), "created internet egress on node", nodeid, "on network", netid)
w.WriteHeader(http.StatusOK)
_ = json.NewEncoder(w).Encode(apiNode)
ctx := scope.WithContext(db.WithContext(context.Background()), scope.Level(r.Context()), scope.ID(r.Context()))
go mq.PublishPeerUpdate(ctx, false)
}
func updateInternetGw(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.InetNodeReq
err = json.NewDecoder(r.Body).Decode(&request)
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
e, err := logic.FindInternetEgressByRoutingNode(r.Context(), netid, nodeid)
if err != nil {
if !node.IsInternetGateway {
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("node is not a internet gw"), "badrequest"))
return
}
e, err = logic.CreateInternetEgressForNode(r.Context(), &node, "", r.Header.Get("user"))
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
}
err = logic.ValidateInetGwReq(r.Context(), logic.ConvertModelsNodeToSchemaNode(&node), request, true)
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return
}
logic.ClearNodesSelectedInternetEgress(r.Context(), e.ID, netid)
for _, clientNodeID := range request.InetNodeClientIDs {
clientNode, err := logic.GetNodeByID(clientNodeID)
if err != nil {
continue
}
_ = logic.SetNodeSelectedInternetEgress(&clientNode, e.ID, clientNode.UseTcpUplink)
}
node.IsInternetGateway = true
err = logic.UpsertNode(&node)
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
apiNode := node.ConvertToAPINode()
logger.Log(1, r.Header.Get("user"), "updated internet egress on node", nodeid, "on network", netid)
w.WriteHeader(http.StatusOK)
_ = json.NewEncoder(w).Encode(apiNode)
ctx := scope.WithContext(db.WithContext(context.Background()), scope.Level(r.Context()), scope.ID(r.Context()))
go mq.PublishPeerUpdate(ctx, false)
}
func deleteInternetGw(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
}
logic.DeleteInternetEgressesForRoutingNode(r.Context(), netid, nodeid)
logic.UnsetInternetGw(r.Context(), &node)
err = logic.UpsertNode(&node)
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
apiNode := node.ConvertToAPINode()
logger.Log(1, r.Header.Get("user"), "deleted internet egress on node", nodeid, "on network", netid)
w.WriteHeader(http.StatusOK)
_ = json.NewEncoder(w).Encode(apiNode)
ctx := scope.WithContext(db.WithContext(context.Background()), scope.Level(r.Context()), scope.ID(r.Context()))
go mq.PublishPeerUpdate(ctx, false)
}