NET-1914: add gw apis, move relays into CE (#3309)

* add gw apis, move relays into CE

* set gw field on relay and ingress creation

* add gw handlers to relay and ingress apis

* if node is inetgw and gw add dns

* remove pro check on relays

* fetch node before updating
This commit is contained in:
Abhishek K 2025-01-28 11:28:31 +05:30 committed by GitHub
parent 6509da9b61
commit 8297642b90
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 485 additions and 556 deletions

View file

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

207
controllers/gateway.go Normal file
View file

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

View file

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

View file

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

View file

@ -222,21 +222,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

0
logic/pro/failover Normal file
View file

View file

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

View file

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

9
models/gateway.go Normal file
View file

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

View file

@ -78,11 +78,12 @@ type CommonNode struct {
Action string `json:"action" yaml:"action"`
LocalAddress net.IPNet `json:"localaddress" yaml:"localaddress"`
IsEgressGateway bool `json:"isegressgateway" yaml:"isegressgateway"`
EgressGatewayRanges []string `json:"egressgatewayranges" yaml:"egressgatewayranges" bson:"egressgatewayranges"`
EgressGatewayRanges []string `json:"egressgatewayranges" yaml:"egressgatewayranges"`
IsIngressGateway bool `json:"isingressgateway" yaml:"isingressgateway"`
IsRelayed bool `json:"isrelayed" yaml:"isrelayed" bson:"isrelayed"`
RelayedBy string `json:"relayedby" yaml:"relayedby" bson:"relayedby"`
IsRelay bool `json:"isrelay" yaml:"isrelay" bson:"isrelay"`
IsRelayed bool `json:"isrelayed" yaml:"isrelayed"`
RelayedBy string `json:"relayedby" yaml:"relayedby"`
IsRelay bool `json:"isrelay" yaml:"isrelay"`
IsGw bool `json:"is_gw" yaml:"is_gw"`
RelayedNodes []string `json:"relaynodes" yaml:"relayedNodes"`
IngressDNS string `json:"ingressdns" yaml:"ingressdns"`
DNSOn bool `json:"dnson" yaml:"dnson"`

View file

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

View file

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

View file

@ -29,7 +29,6 @@ func InitPro() {
controller.HttpHandlers = append(
controller.HttpHandlers,
proControllers.MetricHandlers,
proControllers.RelayHandlers,
proControllers.UserHandlers,
proControllers.FailOverHandlers,
proControllers.InetHandlers,
@ -106,13 +105,6 @@ func InitPro() {
logic.GetMetrics = proLogic.GetMetrics
logic.UpdateMetrics = proLogic.UpdateMetrics
logic.DeleteMetrics = proLogic.DeleteMetrics
logic.GetRelays = proLogic.GetRelays
logic.GetAllowedIpsForRelayed = proLogic.GetAllowedIpsForRelayed
logic.RelayedAllowedIPs = proLogic.RelayedAllowedIPs
logic.UpdateRelayed = proLogic.UpdateRelayed
logic.SetRelayedNodes = proLogic.SetRelayedNodes
logic.RelayUpdates = proLogic.RelayUpdates
logic.ValidateRelay = proLogic.ValidateRelay
logic.GetTrialEndDate = getTrialEndDate
logic.SetDefaultGw = proLogic.SetDefaultGw
logic.SetDefaultGwForRelayedUpdate = proLogic.SetDefaultGwForRelayedUpdate

View file

@ -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
}