resolve merge conflicts

This commit is contained in:
abhishek9686
2025-03-06 22:41:56 +04:00
22 changed files with 695 additions and 139 deletions

View File

@@ -17,6 +17,17 @@ var extClientConfigCmd = &cobra.Command{
}, },
} }
var extClientHAConfigCmd = &cobra.Command{
Use: "auto_config [NETWORK NAME]",
Args: cobra.ExactArgs(1),
Short: "Get an External Client Configuration",
Long: `Get an External Client Configuration`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println(functions.GetExtClientHAConfig(args[0]))
},
}
func init() { func init() {
rootCmd.AddCommand(extClientConfigCmd) rootCmd.AddCommand(extClientConfigCmd)
rootCmd.AddCommand(extClientHAConfigCmd)
} }

View File

@@ -27,6 +27,11 @@ func GetExtClientConfig(networkName, clientID string) string {
return get(fmt.Sprintf("/api/extclients/%s/%s/file", networkName, clientID)) return get(fmt.Sprintf("/api/extclients/%s/%s/file", networkName, clientID))
} }
// GetExtClientConfig - auto fetch a client config
func GetExtClientHAConfig(networkName string) string {
return get(fmt.Sprintf("/api/v1/client_conf/%s", networkName))
}
// CreateExtClient - create an external client // CreateExtClient - create an external client
func CreateExtClient(networkName, nodeID string, extClient models.CustomExtClient) { func CreateExtClient(networkName, nodeID string, extClient models.CustomExtClient) {
request[any](http.MethodPost, fmt.Sprintf("/api/extclients/%s/%s", networkName, nodeID), extClient) request[any](http.MethodPost, fmt.Sprintf("/api/extclients/%s/%s", networkName, nodeID), extClient)

View File

@@ -42,6 +42,7 @@ func extClientHandlers(r *mux.Router) {
Methods(http.MethodDelete) Methods(http.MethodDelete)
r.HandleFunc("/api/extclients/{network}/{nodeid}", logic.SecurityCheck(false, checkFreeTierLimits(limitChoiceMachines, http.HandlerFunc(createExtClient)))). r.HandleFunc("/api/extclients/{network}/{nodeid}", logic.SecurityCheck(false, checkFreeTierLimits(limitChoiceMachines, http.HandlerFunc(createExtClient)))).
Methods(http.MethodPost) Methods(http.MethodPost)
r.HandleFunc("/api/v1/client_conf/{network}", logic.SecurityCheck(false, http.HandlerFunc(getExtClientHAConf))).Methods(http.MethodGet)
} }
func checkIngressExists(nodeID string) bool { func checkIngressExists(nodeID string) bool {
@@ -387,6 +388,251 @@ Endpoint = %s
json.NewEncoder(w).Encode(client) json.NewEncoder(w).Encode(client)
} }
// @Summary Get an individual remote access client
// @Router /api/extclients/{network}/{clientid}/{type} [get]
// @Tags Remote Access Client
// @Security oauth2
// @Success 200 {object} models.ExtClient
// @Failure 500 {object} models.ErrorResponse
// @Failure 403 {object} models.ErrorResponse
func getExtClientHAConf(w http.ResponseWriter, r *http.Request) {
var params = mux.Vars(r)
networkid := params["network"]
network, err := logic.GetParentNetwork(networkid)
if err != nil {
logger.Log(
1,
r.Header.Get("user"),
"Could not retrieve Ingress Gateway Network",
networkid,
)
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
// fetch client based on availability
nodes, _ := logic.GetNetworkNodes(networkid)
defaultPolicy, _ := logic.GetDefaultPolicy(models.NetworkID(networkid), models.DevicePolicy)
var targetGwID string
var connectionCnt int = -1
for _, nodeI := range nodes {
if nodeI.IsGw {
// check health status
logic.GetNodeStatus(&nodeI, defaultPolicy.Enabled)
if nodeI.Status != models.OnlineSt {
continue
}
// Get Total connections on the gw
clients := logic.GetGwExtclients(nodeI.ID.String(), networkid)
if connectionCnt == -1 || len(clients) < connectionCnt {
connectionCnt = len(clients)
targetGwID = nodeI.ID.String()
}
}
}
gwnode, err := logic.GetNodeByID(targetGwID)
if err != nil {
logger.Log(
0,
r.Header.Get("user"),
fmt.Sprintf(
"failed to get ingress gateway node [%s] info: %v",
gwnode.ID,
err,
),
)
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
host, err := logic.GetHost(gwnode.HostID.String())
if err != nil {
logger.Log(0, r.Header.Get("user"),
fmt.Sprintf("failed to get ingress gateway host for node [%s] info: %v", gwnode.ID, err))
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
var userName string
if r.Header.Get("ismaster") == "yes" {
userName = logic.MasterUser
} else {
caller, err := logic.GetUser(r.Header.Get("user"))
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
userName = caller.UserName
}
// create client
var extclient models.ExtClient
extclient.OwnerID = userName
extclient.IngressGatewayID = targetGwID
extclient.Network = networkid
extclient.Tags = make(map[models.TagID]struct{})
// extclient.Tags[models.TagID(fmt.Sprintf("%s.%s", extclient.Network,
// models.RemoteAccessTagName))] = struct{}{}
// set extclient dns to ingressdns if extclient dns is not explicitly set
if (extclient.DNS == "") && (gwnode.IngressDNS != "") {
extclient.DNS = gwnode.IngressDNS
}
listenPort := logic.GetPeerListenPort(host)
extclient.IngressGatewayEndpoint = fmt.Sprintf("%s:%d", host.EndpointIP.String(), listenPort)
extclient.Enabled = true
if err = logic.CreateExtClient(&extclient); err != nil {
slog.Error(
"failed to create extclient",
"user",
r.Header.Get("user"),
"network",
networkid,
"error",
err,
)
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
client, err := logic.GetExtClient(extclient.ClientID, networkid)
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
addrString := client.Address
if addrString != "" {
addrString += "/32"
}
if client.Address6 != "" {
if addrString != "" {
addrString += ","
}
addrString += client.Address6 + "/128"
}
keepalive := ""
if network.DefaultKeepalive != 0 {
keepalive = "PersistentKeepalive = " + strconv.Itoa(int(network.DefaultKeepalive))
}
if gwnode.IngressPersistentKeepalive != 0 {
keepalive = "PersistentKeepalive = " + strconv.Itoa(int(gwnode.IngressPersistentKeepalive))
}
var newAllowedIPs string
if logic.IsInternetGw(gwnode) || gwnode.InternetGwID != "" {
egressrange := "0.0.0.0/0"
if gwnode.Address6.IP != nil && client.Address6 != "" {
egressrange += "," + "::/0"
}
newAllowedIPs = egressrange
} else {
newAllowedIPs = network.AddressRange
if newAllowedIPs != "" && network.AddressRange6 != "" {
newAllowedIPs += ","
}
if network.AddressRange6 != "" {
newAllowedIPs += network.AddressRange6
}
if egressGatewayRanges, err := logic.GetEgressRangesOnNetwork(&client); err == nil {
for _, egressGatewayRange := range egressGatewayRanges {
newAllowedIPs += "," + egressGatewayRange
}
}
}
gwendpoint := ""
if host.EndpointIP.To4() == nil {
gwendpoint = fmt.Sprintf("[%s]:%d", host.EndpointIPv6.String(), host.ListenPort)
} else {
gwendpoint = fmt.Sprintf("%s:%d", host.EndpointIP.String(), host.ListenPort)
}
defaultDNS := ""
if client.DNS != "" {
defaultDNS = "DNS = " + client.DNS
} else if gwnode.IngressDNS != "" {
defaultDNS = "DNS = " + gwnode.IngressDNS
}
defaultMTU := 1420
if host.MTU != 0 {
defaultMTU = host.MTU
}
if gwnode.IngressMTU != 0 {
defaultMTU = int(gwnode.IngressMTU)
}
postUp := strings.Builder{}
if client.PostUp != "" && params["type"] != "qr" {
for _, loc := range strings.Split(client.PostUp, "\n") {
postUp.WriteString(fmt.Sprintf("PostUp = %s\n", loc))
}
}
postDown := strings.Builder{}
if client.PostDown != "" && params["type"] != "qr" {
for _, loc := range strings.Split(client.PostDown, "\n") {
postDown.WriteString(fmt.Sprintf("PostDown = %s\n", loc))
}
}
config := fmt.Sprintf(`[Interface]
Address = %s
PrivateKey = %s
MTU = %d
%s
%s
%s
[Peer]
PublicKey = %s
AllowedIPs = %s
Endpoint = %s
%s
`, addrString,
client.PrivateKey,
defaultMTU,
defaultDNS,
postUp.String(),
postDown.String(),
host.PublicKey,
newAllowedIPs,
gwendpoint,
keepalive,
)
go func() {
if err := logic.SetClientDefaultACLs(&extclient); err != nil {
slog.Error(
"failed to set default acls for extclient",
"user",
r.Header.Get("user"),
"network",
networkid,
"error",
err,
)
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
if err := mq.PublishPeerUpdate(false); err != nil {
logger.Log(1, "error publishing peer update ", err.Error())
}
if servercfg.IsDNSMode() {
logic.SetDNS()
}
}()
name := client.ClientID + ".conf"
w.Header().Set("Content-Type", "application/config")
w.Header().Set("Content-Disposition", "attachment; filename=\""+name+"\"")
w.WriteHeader(http.StatusOK)
_, err = fmt.Fprint(w, config)
if err != nil {
logger.Log(1, r.Header.Get("user"), "response writer error (file) ", err.Error())
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
}
}
// @Summary Create an individual remote access client // @Summary Create an individual remote access client
// @Router /api/extclients/{network}/{nodeid} [post] // @Router /api/extclients/{network}/{nodeid} [post]
// @Tags Remote Access Client // @Tags Remote Access Client

View File

@@ -559,7 +559,7 @@ func createNetwork(w http.ResponseWriter, r *http.Request) {
logic.CreateDefaultNetworkRolesAndGroups(models.NetworkID(network.NetID)) logic.CreateDefaultNetworkRolesAndGroups(models.NetworkID(network.NetID))
logic.CreateDefaultAclNetworkPolicies(models.NetworkID(network.NetID)) logic.CreateDefaultAclNetworkPolicies(models.NetworkID(network.NetID))
logic.CreateDefaultTags(models.NetworkID(network.NetID)) logic.CreateDefaultTags(models.NetworkID(network.NetID))
//add new network to allocated ip map
go logic.AddNetworkToAllocatedIpMap(network.NetID) go logic.AddNetworkToAllocatedIpMap(network.NetID)
go func() { go func() {
@@ -640,6 +640,7 @@ func updateNetwork(w http.ResponseWriter, r *http.Request) {
return return
} }
netNew := netOld netNew := netOld
netNew.NameServers = payload.NameServers
netNew.DefaultACL = payload.DefaultACL netNew.DefaultACL = payload.DefaultACL
_, _, _, err = logic.UpdateNetwork(&netOld, &netNew) _, _, _, err = logic.UpdateNetwork(&netOld, &netNew)
if err != nil { if err != nil {
@@ -647,7 +648,7 @@ func updateNetwork(w http.ResponseWriter, r *http.Request) {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
return return
} }
go mq.PublishPeerUpdate(false)
slog.Info("updated network", "network", payload.NetID, "user", r.Header.Get("user")) slog.Info("updated network", "network", payload.NetID, "user", r.Header.Get("user"))
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(payload) json.NewEncoder(w).Encode(payload)

View File

@@ -21,6 +21,10 @@ var linuxHost models.Host
func TestCreateEgressGateway(t *testing.T) { func TestCreateEgressGateway(t *testing.T) {
var gateway models.EgressGatewayRequest var gateway models.EgressGatewayRequest
gateway.Ranges = []string{"10.100.100.0/24"} gateway.Ranges = []string{"10.100.100.0/24"}
gateway.RangesWithMetric = append(gateway.RangesWithMetric, models.EgressRangeMetric{
Network: "10.100.100.0/24",
RouteMetric: 256,
})
gateway.NetID = "skynet" gateway.NetID = "skynet"
deleteAllNetworks() deleteAllNetworks()
createNet() createNet()

View File

@@ -17,7 +17,6 @@ import (
var ( var (
aclCacheMutex = &sync.RWMutex{} aclCacheMutex = &sync.RWMutex{}
aclCacheMap = make(map[string]models.Acl) aclCacheMap = make(map[string]models.Acl)
aclTagsMutex = &sync.RWMutex{}
) )
func MigrateAclPolicies() { func MigrateAclPolicies() {
@@ -620,10 +619,22 @@ func IsPeerAllowed(node, peer models.Node, checkDefaultPolicy bool) bool {
} else { } else {
peerId = peer.ID.String() peerId = peer.ID.String()
} }
aclTagsMutex.RLock()
peerTags := maps.Clone(peer.Tags) var nodeTags, peerTags map[models.TagID]struct{}
nodeTags := maps.Clone(node.Tags) if node.Mutex != nil {
aclTagsMutex.RUnlock() node.Mutex.Lock()
nodeTags = maps.Clone(node.Tags)
node.Mutex.Unlock()
} else {
nodeTags = node.Tags
}
if peer.Mutex != nil {
peer.Mutex.Lock()
peerTags = maps.Clone(peer.Tags)
peer.Mutex.Unlock()
} else {
peerTags = peer.Tags
}
nodeTags[models.TagID(nodeId)] = struct{}{} nodeTags[models.TagID(nodeId)] = struct{}{}
peerTags[models.TagID(peerId)] = struct{}{} peerTags[models.TagID(peerId)] = struct{}{}
if checkDefaultPolicy { if checkDefaultPolicy {
@@ -854,10 +865,21 @@ func IsNodeAllowedToCommunicateV1(node, peer models.Node, checkDefaultPolicy boo
peerId = peer.ID.String() peerId = peer.ID.String()
} }
aclTagsMutex.RLock() var nodeTags, peerTags map[models.TagID]struct{}
peerTags := maps.Clone(peer.Tags) if node.Mutex != nil {
nodeTags := maps.Clone(node.Tags) node.Mutex.Lock()
aclTagsMutex.RUnlock() nodeTags = maps.Clone(node.Tags)
node.Mutex.Unlock()
} else {
nodeTags = node.Tags
}
if peer.Mutex != nil {
peer.Mutex.Lock()
peerTags = maps.Clone(peer.Tags)
peer.Mutex.Unlock()
} else {
peerTags = peer.Tags
}
nodeTags[models.TagID(nodeId)] = struct{}{} nodeTags[models.TagID(nodeId)] = struct{}{}
peerTags[models.TagID(peerId)] = struct{}{} peerTags[models.TagID(peerId)] = struct{}{}
if checkDefaultPolicy { if checkDefaultPolicy {
@@ -996,10 +1018,21 @@ func IsNodeAllowedToCommunicate(node, peer models.Node, checkDefaultPolicy bool)
peerId = peer.ID.String() peerId = peer.ID.String()
} }
aclTagsMutex.RLock() var nodeTags, peerTags map[models.TagID]struct{}
peerTags := maps.Clone(peer.Tags) if node.Mutex != nil {
nodeTags := maps.Clone(node.Tags) node.Mutex.Lock()
aclTagsMutex.RUnlock() nodeTags = maps.Clone(node.Tags)
node.Mutex.Unlock()
} else {
nodeTags = node.Tags
}
if peer.Mutex != nil {
peer.Mutex.Lock()
peerTags = maps.Clone(peer.Tags)
peer.Mutex.Unlock()
} else {
peerTags = peer.Tags
}
nodeTags[models.TagID(nodeId)] = struct{}{} nodeTags[models.TagID(nodeId)] = struct{}{}
peerTags[models.TagID(peerId)] = struct{}{} peerTags[models.TagID(peerId)] = struct{}{}
if checkDefaultPolicy { if checkDefaultPolicy {
@@ -1222,13 +1255,20 @@ func getUserAclRulesForNode(targetnode *models.Node,
userGrpMap := GetUserGrpMap() userGrpMap := GetUserGrpMap()
allowedUsers := make(map[string][]models.Acl) allowedUsers := make(map[string][]models.Acl)
acls := listUserPolicies(models.NetworkID(targetnode.Network)) acls := listUserPolicies(models.NetworkID(targetnode.Network))
var targetNodeTags = make(map[models.TagID]struct{})
if targetnode.Mutex != nil {
targetnode.Mutex.Lock()
targetNodeTags = maps.Clone(targetnode.Tags)
targetnode.Mutex.Unlock()
} else {
targetNodeTags = maps.Clone(targetnode.Tags)
}
for _, acl := range acls { for _, acl := range acls {
if !acl.Enabled { if !acl.Enabled {
continue continue
} }
dstTags := convAclTagToValueMap(acl.Dst) dstTags := convAclTagToValueMap(acl.Dst)
for nodeTag := range targetnode.Tags { for nodeTag := range targetNodeTags {
if _, ok := dstTags[nodeTag.String()]; !ok { if _, ok := dstTags[nodeTag.String()]; !ok {
if _, ok = dstTags[targetnode.ID.String()]; !ok { if _, ok = dstTags[targetnode.ID.String()]; !ok {
continue continue
@@ -1338,7 +1378,15 @@ func GetAclRulesForNode(targetnodeI *models.Node) (rules map[string]models.AclRu
} }
acls := listDevicePolicies(models.NetworkID(targetnode.Network)) acls := listDevicePolicies(models.NetworkID(targetnode.Network))
targetnode.Tags["*"] = struct{}{} var targetNodeTags = make(map[models.TagID]struct{})
if targetnode.Mutex != nil {
targetnode.Mutex.Lock()
targetNodeTags = maps.Clone(targetnode.Tags)
targetnode.Mutex.Unlock()
} else {
targetNodeTags = maps.Clone(targetnode.Tags)
}
targetNodeTags["*"] = struct{}{}
for _, acl := range acls { for _, acl := range acls {
if !acl.Enabled { if !acl.Enabled {
@@ -1355,7 +1403,7 @@ func GetAclRulesForNode(targetnodeI *models.Node) (rules map[string]models.AclRu
Direction: acl.AllowedDirection, Direction: acl.AllowedDirection,
Allowed: true, Allowed: true,
} }
for nodeTag := range targetnode.Tags { for nodeTag := range targetNodeTags {
if acl.AllowedDirection == models.TrafficDirectionBi { if acl.AllowedDirection == models.TrafficDirectionBi {
var existsInSrcTag bool var existsInSrcTag bool
var existsInDstTag bool var existsInDstTag bool

View File

@@ -28,6 +28,9 @@ var (
func getAllExtClientsFromCache() (extClients []models.ExtClient) { func getAllExtClientsFromCache() (extClients []models.ExtClient) {
extClientCacheMutex.RLock() extClientCacheMutex.RLock()
for _, extclient := range extClientCacheMap { for _, extclient := range extClientCacheMap {
if extclient.Mutex == nil {
extclient.Mutex = &sync.Mutex{}
}
extClients = append(extClients, extclient) extClients = append(extClients, extclient)
} }
extClientCacheMutex.RUnlock() extClientCacheMutex.RUnlock()
@@ -43,12 +46,18 @@ func deleteExtClientFromCache(key string) {
func getExtClientFromCache(key string) (extclient models.ExtClient, ok bool) { func getExtClientFromCache(key string) (extclient models.ExtClient, ok bool) {
extClientCacheMutex.RLock() extClientCacheMutex.RLock()
extclient, ok = extClientCacheMap[key] extclient, ok = extClientCacheMap[key]
if extclient.Mutex == nil {
extclient.Mutex = &sync.Mutex{}
}
extClientCacheMutex.RUnlock() extClientCacheMutex.RUnlock()
return return
} }
func storeExtClientInCache(key string, extclient models.ExtClient) { func storeExtClientInCache(key string, extclient models.ExtClient) {
extClientCacheMutex.Lock() extClientCacheMutex.Lock()
if extclient.Mutex == nil {
extclient.Mutex = &sync.Mutex{}
}
extClientCacheMap[key] = extclient extClientCacheMap[key] = extclient
extClientCacheMutex.Unlock() extClientCacheMutex.Unlock()
} }
@@ -96,14 +105,14 @@ func DeleteExtClient(network string, clientid string) error {
if err != nil { if err != nil {
return err return err
} }
//recycle ip address
if extClient.Address != "" {
RemoveIpFromAllocatedIpMap(network, extClient.Address)
}
if extClient.Address6 != "" {
RemoveIpFromAllocatedIpMap(network, extClient.Address6)
}
if servercfg.CacheEnabled() { if servercfg.CacheEnabled() {
// recycle ip address
if extClient.Address != "" {
RemoveIpFromAllocatedIpMap(network, extClient.Address)
}
if extClient.Address6 != "" {
RemoveIpFromAllocatedIpMap(network, extClient.Address6)
}
deleteExtClientFromCache(key) deleteExtClientFromCache(key)
} }
go RemoveNodeFromAclPolicy(extClient.ConvertToStaticNode()) go RemoveNodeFromAclPolicy(extClient.ConvertToStaticNode())
@@ -334,15 +343,16 @@ func SaveExtClient(extclient *models.ExtClient) error {
} }
if servercfg.CacheEnabled() { if servercfg.CacheEnabled() {
storeExtClientInCache(key, *extclient) storeExtClientInCache(key, *extclient)
} if _, ok := allocatedIpMap[extclient.Network]; ok {
if _, ok := allocatedIpMap[extclient.Network]; ok { if extclient.Address != "" {
if extclient.Address != "" { AddIpToAllocatedIpMap(extclient.Network, net.ParseIP(extclient.Address))
AddIpToAllocatedIpMap(extclient.Network, net.ParseIP(extclient.Address)) }
} if extclient.Address6 != "" {
if extclient.Address6 != "" { AddIpToAllocatedIpMap(extclient.Network, net.ParseIP(extclient.Address6))
AddIpToAllocatedIpMap(extclient.Network, net.ParseIP(extclient.Address6)) }
} }
} }
return SetNetworkNodesLastModified(extclient.Network) return SetNetworkNodesLastModified(extclient.Network)
} }

View File

@@ -3,6 +3,8 @@ package logic
import ( import (
"errors" "errors"
"fmt" "fmt"
"slices"
"sort"
"time" "time"
"github.com/gravitl/netmaker/database" "github.com/gravitl/netmaker/database"
@@ -77,6 +79,14 @@ func CreateEgressGateway(gateway models.EgressGatewayRequest) (models.Node, erro
if host.FirewallInUse == models.FIREWALL_NONE { if host.FirewallInUse == models.FIREWALL_NONE {
return models.Node{}, errors.New("please install iptables or nftables on the device") return models.Node{}, errors.New("please install iptables or nftables on the device")
} }
if len(gateway.RangesWithMetric) == 0 && len(gateway.Ranges) > 0 {
for _, rangeI := range gateway.Ranges {
gateway.RangesWithMetric = append(gateway.RangesWithMetric, models.EgressRangeMetric{
Network: rangeI,
RouteMetric: 256,
})
}
}
for i := len(gateway.Ranges) - 1; i >= 0; i-- { for i := len(gateway.Ranges) - 1; i >= 0; i-- {
// check if internet gateway IPv4 // check if internet gateway IPv4
if gateway.Ranges[i] == "0.0.0.0/0" || gateway.Ranges[i] == "::/0" { if gateway.Ranges[i] == "0.0.0.0/0" || gateway.Ranges[i] == "::/0" {
@@ -91,6 +101,28 @@ func CreateEgressGateway(gateway models.EgressGatewayRequest) (models.Node, erro
gateway.Ranges[i] = normalized gateway.Ranges[i] = normalized
} }
rangesWithMetric := []string{}
for i := len(gateway.RangesWithMetric) - 1; i >= 0; i-- {
if gateway.RangesWithMetric[i].Network == "0.0.0.0/0" || gateway.RangesWithMetric[i].Network == "::/0" {
// remove inet range
gateway.RangesWithMetric = append(gateway.RangesWithMetric[:i], gateway.RangesWithMetric[i+1:]...)
continue
}
normalized, err := NormalizeCIDR(gateway.RangesWithMetric[i].Network)
if err != nil {
return models.Node{}, err
}
gateway.RangesWithMetric[i].Network = normalized
rangesWithMetric = append(rangesWithMetric, gateway.RangesWithMetric[i].Network)
if gateway.RangesWithMetric[i].RouteMetric <= 0 || gateway.RangesWithMetric[i].RouteMetric > 999 {
gateway.RangesWithMetric[i].RouteMetric = 256
}
}
sort.Strings(gateway.Ranges)
sort.Strings(rangesWithMetric)
if !slices.Equal(gateway.Ranges, rangesWithMetric) {
return models.Node{}, errors.New("invalid ranges")
}
if gateway.NatEnabled == "" { if gateway.NatEnabled == "" {
gateway.NatEnabled = "yes" gateway.NatEnabled = "yes"
} }
@@ -104,6 +136,7 @@ func CreateEgressGateway(gateway models.EgressGatewayRequest) (models.Node, erro
node.IsEgressGateway = true node.IsEgressGateway = true
node.EgressGatewayRanges = gateway.Ranges node.EgressGatewayRanges = gateway.Ranges
node.EgressGatewayNatEnabled = models.ParseBool(gateway.NatEnabled) node.EgressGatewayNatEnabled = models.ParseBool(gateway.NatEnabled)
node.EgressGatewayRequest = gateway // store entire request for use when preserving the egress gateway node.EgressGatewayRequest = gateway // store entire request for use when preserving the egress gateway
node.SetLastModified() node.SetLastModified()
if err = UpsertNode(&node); err != nil { if err = UpsertNode(&node); err != nil {

View File

@@ -30,6 +30,9 @@ var (
// SetAllocatedIpMap - set allocated ip map for networks // SetAllocatedIpMap - set allocated ip map for networks
func SetAllocatedIpMap() error { func SetAllocatedIpMap() error {
if !servercfg.CacheEnabled() {
return nil
}
logger.Log(0, "start setting up allocated ip map") logger.Log(0, "start setting up allocated ip map")
if allocatedIpMap == nil { if allocatedIpMap == nil {
allocatedIpMap = map[string]map[string]net.IP{} allocatedIpMap = map[string]map[string]net.IP{}
@@ -84,16 +87,25 @@ func SetAllocatedIpMap() error {
// ClearAllocatedIpMap - set allocatedIpMap to nil // ClearAllocatedIpMap - set allocatedIpMap to nil
func ClearAllocatedIpMap() { func ClearAllocatedIpMap() {
if !servercfg.CacheEnabled() {
return
}
allocatedIpMap = nil allocatedIpMap = nil
} }
func AddIpToAllocatedIpMap(networkName string, ip net.IP) { func AddIpToAllocatedIpMap(networkName string, ip net.IP) {
if !servercfg.CacheEnabled() {
return
}
networkCacheMutex.Lock() networkCacheMutex.Lock()
allocatedIpMap[networkName][ip.String()] = ip allocatedIpMap[networkName][ip.String()] = ip
networkCacheMutex.Unlock() networkCacheMutex.Unlock()
} }
func RemoveIpFromAllocatedIpMap(networkName string, ip string) { func RemoveIpFromAllocatedIpMap(networkName string, ip string) {
if !servercfg.CacheEnabled() {
return
}
networkCacheMutex.Lock() networkCacheMutex.Lock()
delete(allocatedIpMap[networkName], ip) delete(allocatedIpMap[networkName], ip)
networkCacheMutex.Unlock() networkCacheMutex.Unlock()
@@ -101,6 +113,10 @@ func RemoveIpFromAllocatedIpMap(networkName string, ip string) {
// AddNetworkToAllocatedIpMap - add network to allocated ip map when network is added // AddNetworkToAllocatedIpMap - add network to allocated ip map when network is added
func AddNetworkToAllocatedIpMap(networkName string) { func AddNetworkToAllocatedIpMap(networkName string) {
//add new network to allocated ip map
if !servercfg.CacheEnabled() {
return
}
networkCacheMutex.Lock() networkCacheMutex.Lock()
allocatedIpMap[networkName] = make(map[string]net.IP) allocatedIpMap[networkName] = make(map[string]net.IP)
networkCacheMutex.Unlock() networkCacheMutex.Unlock()
@@ -108,6 +124,9 @@ func AddNetworkToAllocatedIpMap(networkName string) {
// RemoveNetworkFromAllocatedIpMap - remove network from allocated ip map when network is deleted // RemoveNetworkFromAllocatedIpMap - remove network from allocated ip map when network is deleted
func RemoveNetworkFromAllocatedIpMap(networkName string) { func RemoveNetworkFromAllocatedIpMap(networkName string) {
if !servercfg.CacheEnabled() {
return
}
networkCacheMutex.Lock() networkCacheMutex.Lock()
delete(allocatedIpMap, networkName) delete(allocatedIpMap, networkName)
networkCacheMutex.Unlock() networkCacheMutex.Unlock()
@@ -354,7 +373,7 @@ func GetNetworkSettings(networkname string) (models.Network, error) {
} }
// UniqueAddress - get a unique ipv4 address // UniqueAddress - get a unique ipv4 address
func UniqueAddress(networkName string, reverse bool) (net.IP, error) { func UniqueAddressCache(networkName string, reverse bool) (net.IP, error) {
add := net.IP{} add := net.IP{}
var network models.Network var network models.Network
network, err := GetParentNetwork(networkName) network, err := GetParentNetwork(networkName)
@@ -396,6 +415,49 @@ func UniqueAddress(networkName string, reverse bool) (net.IP, error) {
return add, errors.New("ERROR: No unique addresses available. Check network subnet") return add, errors.New("ERROR: No unique addresses available. Check network subnet")
} }
// UniqueAddress - get a unique ipv4 address
func UniqueAddressDB(networkName string, reverse bool) (net.IP, error) {
add := net.IP{}
var network models.Network
network, err := GetParentNetwork(networkName)
if err != nil {
logger.Log(0, "UniqueAddressServer encountered an error")
return add, err
}
if network.IsIPv4 == "no" {
return add, fmt.Errorf("IPv4 not active on network " + networkName)
}
//ensure AddressRange is valid
if _, _, err := net.ParseCIDR(network.AddressRange); err != nil {
logger.Log(0, "UniqueAddress encountered an error")
return add, err
}
net4 := iplib.Net4FromStr(network.AddressRange)
newAddrs := net4.FirstAddress()
if reverse {
newAddrs = net4.LastAddress()
}
for {
if IsIPUnique(networkName, newAddrs.String(), database.NODES_TABLE_NAME, false) &&
IsIPUnique(networkName, newAddrs.String(), database.EXT_CLIENT_TABLE_NAME, false) {
return newAddrs, nil
}
if reverse {
newAddrs, err = net4.PreviousIP(newAddrs)
} else {
newAddrs, err = net4.NextIP(newAddrs)
}
if err != nil {
break
}
}
return add, errors.New("ERROR: No unique addresses available. Check network subnet")
}
// IsIPUnique - checks if an IP is unique // IsIPUnique - checks if an IP is unique
func IsIPUnique(network string, ip string, tableName string, isIpv6 bool) bool { func IsIPUnique(network string, ip string, tableName string, isIpv6 bool) bool {
@@ -439,9 +501,67 @@ func IsIPUnique(network string, ip string, tableName string, isIpv6 bool) bool {
return isunique return isunique
} }
func UniqueAddress(networkName string, reverse bool) (net.IP, error) {
if servercfg.CacheEnabled() {
return UniqueAddressCache(networkName, reverse)
}
return UniqueAddressDB(networkName, reverse)
}
// UniqueAddress6 - see if ipv6 address is unique
func UniqueAddress6(networkName string, reverse bool) (net.IP, error) { func UniqueAddress6(networkName string, reverse bool) (net.IP, error) {
if servercfg.CacheEnabled() {
return UniqueAddress6Cache(networkName, reverse)
}
return UniqueAddress6DB(networkName, reverse)
}
// UniqueAddress6DB - see if ipv6 address is unique
func UniqueAddress6DB(networkName string, reverse bool) (net.IP, error) {
add := net.IP{}
var network models.Network
network, err := GetParentNetwork(networkName)
if err != nil {
fmt.Println("Network Not Found")
return add, err
}
if network.IsIPv6 == "no" {
return add, fmt.Errorf("IPv6 not active on network " + networkName)
}
//ensure AddressRange is valid
if _, _, err := net.ParseCIDR(network.AddressRange6); err != nil {
return add, err
}
net6 := iplib.Net6FromStr(network.AddressRange6)
newAddrs, err := net6.NextIP(net6.FirstAddress())
if reverse {
newAddrs, err = net6.PreviousIP(net6.LastAddress())
}
if err != nil {
return add, err
}
for {
if IsIPUnique(networkName, newAddrs.String(), database.NODES_TABLE_NAME, true) &&
IsIPUnique(networkName, newAddrs.String(), database.EXT_CLIENT_TABLE_NAME, true) {
return newAddrs, nil
}
if reverse {
newAddrs, err = net6.PreviousIP(newAddrs)
} else {
newAddrs, err = net6.NextIP(newAddrs)
}
if err != nil {
break
}
}
return add, errors.New("ERROR: No unique IPv6 addresses available. Check network subnet")
}
// UniqueAddress6Cache - see if ipv6 address is unique using cache
func UniqueAddress6Cache(networkName string, reverse bool) (net.IP, error) {
add := net.IP{} add := net.IP{}
var network models.Network var network models.Network
network, err := GetParentNetwork(networkName) network, err := GetParentNetwork(networkName)

View File

@@ -35,12 +35,20 @@ var (
func getNodeFromCache(nodeID string) (node models.Node, ok bool) { func getNodeFromCache(nodeID string) (node models.Node, ok bool) {
nodeCacheMutex.RLock() nodeCacheMutex.RLock()
node, ok = nodesCacheMap[nodeID] node, ok = nodesCacheMap[nodeID]
if node.Mutex == nil {
node.Mutex = &sync.Mutex{}
}
nodeCacheMutex.RUnlock() nodeCacheMutex.RUnlock()
return return
} }
func getNodesFromCache() (nodes []models.Node) { func getNodesFromCache() (nodes []models.Node) {
nodeCacheMutex.RLock() nodeCacheMutex.RLock()
nodes = slices.Collect(maps.Values(nodesCacheMap)) for _, node := range nodesCacheMap {
if node.Mutex == nil {
node.Mutex = &sync.Mutex{}
}
nodes = append(nodes, node)
}
nodeCacheMutex.RUnlock() nodeCacheMutex.RUnlock()
return return
} }
@@ -357,12 +365,15 @@ func DeleteNodeByID(node *models.Node) error {
logger.Log(1, "unable to remove metrics from DB for node", node.ID.String(), err.Error()) logger.Log(1, "unable to remove metrics from DB for node", node.ID.String(), err.Error())
} }
//recycle ip address //recycle ip address
if node.Address.IP != nil { if servercfg.CacheEnabled() {
RemoveIpFromAllocatedIpMap(node.Network, node.Address.IP.String()) if node.Address.IP != nil {
} RemoveIpFromAllocatedIpMap(node.Network, node.Address.IP.String())
if node.Address6.IP != nil { }
RemoveIpFromAllocatedIpMap(node.Network, node.Address6.IP.String()) if node.Address6.IP != nil {
RemoveIpFromAllocatedIpMap(node.Network, node.Address6.IP.String())
}
} }
return nil return nil
} }
@@ -424,6 +435,9 @@ func GetAllNodes() ([]models.Node, error) {
} }
// add node to our array // add node to our array
nodes = append(nodes, node) nodes = append(nodes, node)
if node.Mutex == nil {
node.Mutex = &sync.Mutex{}
}
nodesMap[node.ID.String()] = node nodesMap[node.ID.String()] = node
} }
@@ -699,15 +713,16 @@ func createNode(node *models.Node) error {
if servercfg.CacheEnabled() { if servercfg.CacheEnabled() {
storeNodeInCache(*node) storeNodeInCache(*node)
storeNodeInNetworkCache(*node, node.Network) storeNodeInNetworkCache(*node, node.Network)
} if _, ok := allocatedIpMap[node.Network]; ok {
if _, ok := allocatedIpMap[node.Network]; ok { if node.Address.IP != nil {
if node.Address.IP != nil { AddIpToAllocatedIpMap(node.Network, node.Address.IP)
AddIpToAllocatedIpMap(node.Network, node.Address.IP) }
} if node.Address6.IP != nil {
if node.Address6.IP != nil { AddIpToAllocatedIpMap(node.Network, node.Address6.IP)
AddIpToAllocatedIpMap(node.Network, node.Address6.IP) }
} }
} }
_, err = nodeacls.CreateNodeACL(nodeacls.NetworkID(node.Network), nodeacls.NodeID(node.ID.String()), defaultACLVal) _, err = nodeacls.CreateNodeACL(nodeacls.NetworkID(node.Network), nodeacls.NodeID(node.ID.String()), defaultACLVal)
if err != nil { if err != nil {
logger.Log(1, "failed to create node ACL for node,", node.ID.String(), "err:", err.Error()) logger.Log(1, "failed to create node ACL for node,", node.ID.String(), "err:", err.Error())
@@ -753,16 +768,14 @@ func ValidateParams(nodeid, netid string) (models.Node, error) {
func ValidateNodeIp(currentNode *models.Node, newNode *models.ApiNode) error { func ValidateNodeIp(currentNode *models.Node, newNode *models.ApiNode) error {
if currentNode.Address.IP != nil && currentNode.Address.String() != newNode.Address { if currentNode.Address.IP != nil && currentNode.Address.String() != newNode.Address {
newIp, _, _ := net.ParseCIDR(newNode.Address) if !IsIPUnique(newNode.Network, newNode.Address, database.NODES_TABLE_NAME, false) ||
ipAllocated := allocatedIpMap[currentNode.Network] !IsIPUnique(newNode.Network, newNode.Address, database.EXT_CLIENT_TABLE_NAME, false) {
if _, ok := ipAllocated[newIp.String()]; ok {
return errors.New("ip specified is already allocated: " + newNode.Address) return errors.New("ip specified is already allocated: " + newNode.Address)
} }
} }
if currentNode.Address6.IP != nil && currentNode.Address6.String() != newNode.Address6 { if currentNode.Address6.IP != nil && currentNode.Address6.String() != newNode.Address6 {
newIp, _, _ := net.ParseCIDR(newNode.Address6) if !IsIPUnique(newNode.Network, newNode.Address6, database.NODES_TABLE_NAME, false) ||
ipAllocated := allocatedIpMap[currentNode.Network] !IsIPUnique(newNode.Network, newNode.Address6, database.EXT_CLIENT_TABLE_NAME, false) {
if _, ok := ipAllocated[newIp.String()]; ok {
return errors.New("ip specified is already allocated: " + newNode.Address6) return errors.New("ip specified is already allocated: " + newNode.Address6)
} }
} }
@@ -825,9 +838,16 @@ func GetTagMapWithNodes() (tagNodesMap map[models.TagID][]models.Node) {
if nodeI.Tags == nil { if nodeI.Tags == nil {
continue continue
} }
if nodeI.Mutex != nil {
nodeI.Mutex.Lock()
}
for nodeTagID := range nodeI.Tags { for nodeTagID := range nodeI.Tags {
tagNodesMap[nodeTagID] = append(tagNodesMap[nodeTagID], nodeI) tagNodesMap[nodeTagID] = append(tagNodesMap[nodeTagID], nodeI)
} }
if nodeI.Mutex != nil {
nodeI.Mutex.Unlock()
}
} }
return return
} }
@@ -842,9 +862,15 @@ func GetTagMapWithNodesByNetwork(netID models.NetworkID, withStaticNodes bool) (
if nodeI.Tags == nil { if nodeI.Tags == nil {
continue continue
} }
if nodeI.Mutex != nil {
nodeI.Mutex.Lock()
}
for nodeTagID := range nodeI.Tags { for nodeTagID := range nodeI.Tags {
tagNodesMap[nodeTagID] = append(tagNodesMap[nodeTagID], nodeI) tagNodesMap[nodeTagID] = append(tagNodesMap[nodeTagID], nodeI)
} }
if nodeI.Mutex != nil {
nodeI.Mutex.Unlock()
}
} }
tagNodesMap["*"] = nodes tagNodesMap["*"] = nodes
if !withStaticNodes { if !withStaticNodes {
@@ -873,17 +899,16 @@ func AddTagMapWithStaticNodes(netID models.NetworkID,
continue continue
} }
for tagID := range extclient.Tags { if extclient.Mutex != nil {
tagNodesMap[tagID] = append(tagNodesMap[tagID], models.Node{ extclient.Mutex.Lock()
IsStatic: true, }
StaticNode: extclient, for tagID := range extclient.Tags {
}) tagNodesMap[tagID] = append(tagNodesMap[tagID], extclient.ConvertToStaticNode())
tagNodesMap["*"] = append(tagNodesMap["*"], models.Node{ tagNodesMap["*"] = append(tagNodesMap["*"], extclient.ConvertToStaticNode())
IsStatic: true, }
StaticNode: extclient, if extclient.Mutex != nil {
}) extclient.Mutex.Unlock()
} }
} }
return tagNodesMap return tagNodesMap
} }
@@ -904,11 +929,14 @@ func AddTagMapWithStaticNodesWithUsers(netID models.NetworkID,
if extclient.Tags == nil { if extclient.Tags == nil {
continue continue
} }
if extclient.Mutex != nil {
extclient.Mutex.Lock()
}
for tagID := range extclient.Tags { for tagID := range extclient.Tags {
tagNodesMap[tagID] = append(tagNodesMap[tagID], models.Node{ tagNodesMap[tagID] = append(tagNodesMap[tagID], extclient.ConvertToStaticNode())
IsStatic: true, }
StaticNode: extclient, if extclient.Mutex != nil {
}) extclient.Mutex.Unlock()
} }
} }
@@ -926,9 +954,15 @@ func GetNodesWithTag(tagID models.TagID) map[string]models.Node {
if nodeI.Tags == nil { if nodeI.Tags == nil {
continue continue
} }
if nodeI.Mutex != nil {
nodeI.Mutex.Lock()
}
if _, ok := nodeI.Tags[tagID]; ok { if _, ok := nodeI.Tags[tagID]; ok {
nMap[nodeI.ID.String()] = nodeI nMap[nodeI.ID.String()] = nodeI
} }
if nodeI.Mutex != nil {
nodeI.Mutex.Unlock()
}
} }
return AddStaticNodesWithTag(tag, nMap) return AddStaticNodesWithTag(tag, nMap)
} }
@@ -942,13 +976,15 @@ func AddStaticNodesWithTag(tag models.Tag, nMap map[string]models.Node) map[stri
if extclient.RemoteAccessClientID != "" { if extclient.RemoteAccessClientID != "" {
continue continue
} }
if _, ok := extclient.Tags[tag.ID]; ok { if extclient.Mutex != nil {
nMap[extclient.ClientID] = models.Node{ extclient.Mutex.Lock()
IsStatic: true, }
StaticNode: extclient, if _, ok := extclient.Tags[tag.ID]; ok {
} nMap[extclient.ClientID] = extclient.ConvertToStaticNode()
}
if extclient.Mutex != nil {
extclient.Mutex.Unlock()
} }
} }
return nMap return nMap
} }
@@ -964,10 +1000,7 @@ func GetStaticNodeWithTag(tagID models.TagID) map[string]models.Node {
return nMap return nMap
} }
for _, extclient := range extclients { for _, extclient := range extclients {
nMap[extclient.ClientID] = models.Node{ nMap[extclient.ClientID] = extclient.ConvertToStaticNode()
IsStatic: true,
StaticNode: extclient,
}
} }
return nMap return nMap
} }

View File

@@ -269,7 +269,11 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
} }
} }
} }
networkSettings, err := GetNetwork(node.Network)
if err != nil {
continue
}
hostPeerUpdate.NameServers = append(hostPeerUpdate.NameServers, networkSettings.NameServers...)
currentPeers := GetNetworkNodesMemory(allNodes, node.Network) currentPeers := GetNetworkNodesMemory(allNodes, node.Network)
for _, peer := range currentPeers { for _, peer := range currentPeers {
peer := peer peer := peer
@@ -291,11 +295,12 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
} }
if peer.IsEgressGateway { if peer.IsEgressGateway {
hostPeerUpdate.EgressRoutes = append(hostPeerUpdate.EgressRoutes, models.EgressNetworkRoutes{ hostPeerUpdate.EgressRoutes = append(hostPeerUpdate.EgressRoutes, models.EgressNetworkRoutes{
EgressGwAddr: peer.Address, EgressGwAddr: peer.Address,
EgressGwAddr6: peer.Address6, EgressGwAddr6: peer.Address6,
NodeAddr: node.Address, NodeAddr: node.Address,
NodeAddr6: node.Address6, NodeAddr6: node.Address6,
EgressRanges: peer.EgressGatewayRanges, EgressRanges: peer.EgressGatewayRanges,
EgressRangesWithMetric: peer.EgressGatewayRequest.RangesWithMetric,
}) })
} }
if peer.IsIngressGateway { if peer.IsIngressGateway {

View File

@@ -225,6 +225,16 @@ func updateNodes() {
node.EgressGatewayRanges = egressRanges node.EgressGatewayRanges = egressRanges
logic.UpsertNode(&node) logic.UpsertNode(&node)
} }
if len(node.EgressGatewayRequest.Ranges) > 0 && len(node.EgressGatewayRequest.RangesWithMetric) == 0 {
for _, egressRangeI := range node.EgressGatewayRequest.Ranges {
node.EgressGatewayRequest.RangesWithMetric = append(node.EgressGatewayRequest.RangesWithMetric, models.EgressRangeMetric{
Network: egressRangeI,
RouteMetric: 256,
})
}
logic.UpsertNode(&node)
}
} }
} }
} }

View File

@@ -17,35 +17,36 @@ type ApiNodeStatus struct {
// ApiNode is a stripped down Node DTO that exposes only required fields to external systems // ApiNode is a stripped down Node DTO that exposes only required fields to external systems
type ApiNode struct { type ApiNode struct {
ID string `json:"id,omitempty" validate:"required,min=5,id_unique"` ID string `json:"id,omitempty" validate:"required,min=5,id_unique"`
HostID string `json:"hostid,omitempty" validate:"required,min=5,id_unique"` HostID string `json:"hostid,omitempty" validate:"required,min=5,id_unique"`
Address string `json:"address" validate:"omitempty,cidrv4"` Address string `json:"address" validate:"omitempty,cidrv4"`
Address6 string `json:"address6" validate:"omitempty,cidrv6"` Address6 string `json:"address6" validate:"omitempty,cidrv6"`
LocalAddress string `json:"localaddress" validate:"omitempty,cidr"` LocalAddress string `json:"localaddress" validate:"omitempty,cidr"`
AllowedIPs []string `json:"allowedips"` AllowedIPs []string `json:"allowedips"`
LastModified int64 `json:"lastmodified" swaggertype:"primitive,integer" format:"int64"` LastModified int64 `json:"lastmodified" swaggertype:"primitive,integer" format:"int64"`
ExpirationDateTime int64 `json:"expdatetime" swaggertype:"primitive,integer" format:"int64"` ExpirationDateTime int64 `json:"expdatetime" swaggertype:"primitive,integer" format:"int64"`
LastCheckIn int64 `json:"lastcheckin" swaggertype:"primitive,integer" format:"int64"` LastCheckIn int64 `json:"lastcheckin" swaggertype:"primitive,integer" format:"int64"`
LastPeerUpdate int64 `json:"lastpeerupdate" swaggertype:"primitive,integer" format:"int64"` LastPeerUpdate int64 `json:"lastpeerupdate" swaggertype:"primitive,integer" format:"int64"`
Network string `json:"network"` Network string `json:"network"`
NetworkRange string `json:"networkrange"` NetworkRange string `json:"networkrange"`
NetworkRange6 string `json:"networkrange6"` NetworkRange6 string `json:"networkrange6"`
IsRelayed bool `json:"isrelayed"` IsRelayed bool `json:"isrelayed"`
IsRelay bool `json:"isrelay"` IsRelay bool `json:"isrelay"`
RelayedBy string `json:"relayedby" bson:"relayedby" yaml:"relayedby"` RelayedBy string `json:"relayedby" bson:"relayedby" yaml:"relayedby"`
RelayedNodes []string `json:"relaynodes" yaml:"relayedNodes"` RelayedNodes []string `json:"relaynodes" yaml:"relayedNodes"`
IsEgressGateway bool `json:"isegressgateway"` IsEgressGateway bool `json:"isegressgateway"`
IsIngressGateway bool `json:"isingressgateway"` IsIngressGateway bool `json:"isingressgateway"`
EgressGatewayRanges []string `json:"egressgatewayranges"` EgressGatewayRanges []string `json:"egressgatewayranges"`
EgressGatewayNatEnabled bool `json:"egressgatewaynatenabled"` EgressGatewayNatEnabled bool `json:"egressgatewaynatenabled"`
DNSOn bool `json:"dnson"` EgressGatewayRangesWithMetric []EgressRangeMetric `json:"egressgatewayranges_with_metric"`
IngressDns string `json:"ingressdns"` DNSOn bool `json:"dnson"`
IngressPersistentKeepalive int32 `json:"ingresspersistentkeepalive"` IngressDns string `json:"ingressdns"`
IngressMTU int32 `json:"ingressmtu"` IngressPersistentKeepalive int32 `json:"ingresspersistentkeepalive"`
Server string `json:"server"` IngressMTU int32 `json:"ingressmtu"`
Connected bool `json:"connected"` Server string `json:"server"`
PendingDelete bool `json:"pendingdelete"` Connected bool `json:"connected"`
Metadata string `json:"metadata"` PendingDelete bool `json:"pendingdelete"`
Metadata string `json:"metadata"`
// == PRO == // == PRO ==
DefaultACL string `json:"defaultacl,omitempty" validate:"checkyesornoorunset"` DefaultACL string `json:"defaultacl,omitempty" validate:"checkyesornoorunset"`
IsFailOver bool `json:"is_fail_over"` IsFailOver bool `json:"is_fail_over"`
@@ -189,6 +190,7 @@ func (nm *Node) ConvertToAPINode() *ApiNode {
apiNode.IsEgressGateway = nm.IsEgressGateway apiNode.IsEgressGateway = nm.IsEgressGateway
apiNode.IsIngressGateway = nm.IsIngressGateway apiNode.IsIngressGateway = nm.IsIngressGateway
apiNode.EgressGatewayRanges = nm.EgressGatewayRanges apiNode.EgressGatewayRanges = nm.EgressGatewayRanges
apiNode.EgressGatewayRangesWithMetric = nm.EgressGatewayRequest.RangesWithMetric
apiNode.EgressGatewayNatEnabled = nm.EgressGatewayNatEnabled apiNode.EgressGatewayNatEnabled = nm.EgressGatewayNatEnabled
apiNode.DNSOn = nm.DNSOn apiNode.DNSOn = nm.DNSOn
apiNode.IngressDns = nm.IngressDNS apiNode.IngressDns = nm.IngressDNS

View File

@@ -1,5 +1,7 @@
package models package models
import "sync"
// ExtClient - struct for external clients // ExtClient - struct for external clients
type ExtClient struct { type ExtClient struct {
ClientID string `json:"clientid" bson:"clientid"` ClientID string `json:"clientid" bson:"clientid"`
@@ -25,6 +27,7 @@ type ExtClient struct {
DeviceName string `json:"device_name"` DeviceName string `json:"device_name"`
PublicEndpoint string `json:"public_endpoint"` PublicEndpoint string `json:"public_endpoint"`
Country string `json:"country"` Country string `json:"country"`
Mutex *sync.Mutex `json:"-"`
} }
// CustomExtClient - struct for CustomExtClient params // CustomExtClient - struct for CustomExtClient params
@@ -57,5 +60,6 @@ func (ext *ExtClient) ConvertToStaticNode() Node {
Tags: ext.Tags, Tags: ext.Tags,
IsStatic: true, IsStatic: true,
StaticNode: *ext, StaticNode: *ext,
Mutex: ext.Mutex,
} }
} }

View File

@@ -27,6 +27,7 @@ type HostPeerUpdate struct {
EgressRoutes []EgressNetworkRoutes `json:"egress_network_routes"` EgressRoutes []EgressNetworkRoutes `json:"egress_network_routes"`
FwUpdate FwUpdate `json:"fw_update"` FwUpdate FwUpdate `json:"fw_update"`
ReplacePeers bool `json:"replace_peers"` ReplacePeers bool `json:"replace_peers"`
NameServers []string `json:"name_servers"`
ServerConfig ServerConfig
OldPeerUpdateFields OldPeerUpdateFields
} }
@@ -69,11 +70,12 @@ type EgressInfo struct {
// EgressNetworkRoutes - struct for egress network routes for adding routes to peer's interface // EgressNetworkRoutes - struct for egress network routes for adding routes to peer's interface
type EgressNetworkRoutes struct { type EgressNetworkRoutes struct {
EgressGwAddr net.IPNet `json:"egress_gw_addr" yaml:"egress_gw_addr"` EgressGwAddr net.IPNet `json:"egress_gw_addr" yaml:"egress_gw_addr"`
EgressGwAddr6 net.IPNet `json:"egress_gw_addr6" yaml:"egress_gw_addr6"` EgressGwAddr6 net.IPNet `json:"egress_gw_addr6" yaml:"egress_gw_addr6"`
NodeAddr net.IPNet `json:"node_addr"` NodeAddr net.IPNet `json:"node_addr"`
NodeAddr6 net.IPNet `json:"node_addr6"` NodeAddr6 net.IPNet `json:"node_addr6"`
EgressRanges []string `json:"egress_ranges"` EgressRanges []string `json:"egress_ranges"`
EgressRangesWithMetric []EgressRangeMetric `json:"egress_ranges_metric"`
} }
// PeerRouteInfo - struct for peer info for an ext. client // PeerRouteInfo - struct for peer info for an ext. client

View File

@@ -8,22 +8,23 @@ import (
// Network Struct - contains info for a given unique network // Network Struct - contains info for a given unique network
// At some point, need to replace all instances of Name with something else like Identifier // At some point, need to replace all instances of Name with something else like Identifier
type Network struct { type Network struct {
AddressRange string `json:"addressrange" bson:"addressrange" validate:"omitempty,cidrv4"` AddressRange string `json:"addressrange" bson:"addressrange" validate:"omitempty,cidrv4"`
AddressRange6 string `json:"addressrange6" bson:"addressrange6" validate:"omitempty,cidrv6"` AddressRange6 string `json:"addressrange6" bson:"addressrange6" validate:"omitempty,cidrv6"`
NetID string `json:"netid" bson:"netid" validate:"required,min=1,max=32,netid_valid"` NetID string `json:"netid" bson:"netid" validate:"required,min=1,max=32,netid_valid"`
NodesLastModified int64 `json:"nodeslastmodified" bson:"nodeslastmodified" swaggertype:"primitive,integer" format:"int64"` NodesLastModified int64 `json:"nodeslastmodified" bson:"nodeslastmodified" swaggertype:"primitive,integer" format:"int64"`
NetworkLastModified int64 `json:"networklastmodified" bson:"networklastmodified" swaggertype:"primitive,integer" format:"int64"` NetworkLastModified int64 `json:"networklastmodified" bson:"networklastmodified" swaggertype:"primitive,integer" format:"int64"`
DefaultInterface string `json:"defaultinterface" bson:"defaultinterface" validate:"min=1,max=35"` DefaultInterface string `json:"defaultinterface" bson:"defaultinterface" validate:"min=1,max=35"`
DefaultListenPort int32 `json:"defaultlistenport,omitempty" bson:"defaultlistenport,omitempty" validate:"omitempty,min=1024,max=65535"` DefaultListenPort int32 `json:"defaultlistenport,omitempty" bson:"defaultlistenport,omitempty" validate:"omitempty,min=1024,max=65535"`
NodeLimit int32 `json:"nodelimit" bson:"nodelimit"` NodeLimit int32 `json:"nodelimit" bson:"nodelimit"`
DefaultPostDown string `json:"defaultpostdown" bson:"defaultpostdown"` DefaultPostDown string `json:"defaultpostdown" bson:"defaultpostdown"`
DefaultKeepalive int32 `json:"defaultkeepalive" bson:"defaultkeepalive" validate:"omitempty,max=1000"` DefaultKeepalive int32 `json:"defaultkeepalive" bson:"defaultkeepalive" validate:"omitempty,max=1000"`
AllowManualSignUp string `json:"allowmanualsignup" bson:"allowmanualsignup" validate:"checkyesorno"` AllowManualSignUp string `json:"allowmanualsignup" bson:"allowmanualsignup" validate:"checkyesorno"`
IsIPv4 string `json:"isipv4" bson:"isipv4" validate:"checkyesorno"` IsIPv4 string `json:"isipv4" bson:"isipv4" validate:"checkyesorno"`
IsIPv6 string `json:"isipv6" bson:"isipv6" validate:"checkyesorno"` IsIPv6 string `json:"isipv6" bson:"isipv6" validate:"checkyesorno"`
DefaultUDPHolePunch string `json:"defaultudpholepunch" bson:"defaultudpholepunch" validate:"checkyesorno"` DefaultUDPHolePunch string `json:"defaultudpholepunch" bson:"defaultudpholepunch" validate:"checkyesorno"`
DefaultMTU int32 `json:"defaultmtu" bson:"defaultmtu"` DefaultMTU int32 `json:"defaultmtu" bson:"defaultmtu"`
DefaultACL string `json:"defaultacl" bson:"defaultacl" yaml:"defaultacl" validate:"checkyesorno"` DefaultACL string `json:"defaultacl" bson:"defaultacl" yaml:"defaultacl" validate:"checkyesorno"`
NameServers []string `json:"dns_nameservers"`
} }
// SaveData - sensitive fields of a network that should be kept the same // SaveData - sensitive fields of a network that should be kept the same

View File

@@ -5,6 +5,7 @@ import (
"math/rand" "math/rand"
"net" "net"
"strings" "strings"
"sync"
"time" "time"
"github.com/google/uuid" "github.com/google/uuid"
@@ -119,6 +120,7 @@ type Node struct {
IsUserNode bool `json:"is_user_node"` IsUserNode bool `json:"is_user_node"`
StaticNode ExtClient `json:"static_node"` StaticNode ExtClient `json:"static_node"`
Status NodeStatus `json:"node_status"` Status NodeStatus `json:"node_status"`
Mutex *sync.Mutex `json:"-"`
} }
// LegacyNode - legacy struct for node model // LegacyNode - legacy struct for node model

View File

@@ -151,12 +151,18 @@ type ExtPeersResponse struct {
KeepAlive int32 `json:"persistentkeepalive" bson:"persistentkeepalive"` KeepAlive int32 `json:"persistentkeepalive" bson:"persistentkeepalive"`
} }
type EgressRangeMetric struct {
Network string `json:"network"`
RouteMetric uint32 `json:"route_metric"` // preffered range 1-999
}
// EgressGatewayRequest - egress gateway request // EgressGatewayRequest - egress gateway request
type EgressGatewayRequest struct { type EgressGatewayRequest struct {
NodeID string `json:"nodeid" bson:"nodeid"` NodeID string `json:"nodeid" bson:"nodeid"`
NetID string `json:"netid" bson:"netid"` NetID string `json:"netid" bson:"netid"`
NatEnabled string `json:"natenabled" bson:"natenabled"` NatEnabled string `json:"natenabled" bson:"natenabled"`
Ranges []string `json:"ranges" bson:"ranges"` Ranges []string `json:"ranges" bson:"ranges"`
RangesWithMetric []EgressRangeMetric `json:"ranges_with_metric"`
} }
// RelayRequest - relay request struct // RelayRequest - relay request struct

View File

@@ -245,6 +245,12 @@ func getUserEmailFromClaims(token string) string {
return "" return ""
} }
claims, _ := accessToken.Claims.(jwt.MapClaims) claims, _ := accessToken.Claims.(jwt.MapClaims)
if claims == nil {
return ""
}
if claims["email"] == nil {
return ""
}
return claims["email"].(string) return claims["email"].(string)
} }

View File

@@ -199,6 +199,10 @@ func getAzureUserInfo(state string, code string) (*OAuthUser, error) {
if userInfo.Email == "" { if userInfo.Email == "" {
userInfo.Email = getUserEmailFromClaims(token.AccessToken) userInfo.Email = getUserEmailFromClaims(token.AccessToken)
} }
if userInfo.Email == "" && userInfo.UserPrincipalName != "" {
userInfo.Email = userInfo.UserPrincipalName
}
if userInfo.Email == "" { if userInfo.Email == "" {
err = errors.New("failed to fetch user email from SSO state") err = errors.New("failed to fetch user email from SSO state")
return userInfo, err return userInfo, err

View File

@@ -1108,6 +1108,9 @@ func getUserRemoteAccessGwsV1(w http.ResponseWriter, r *http.Request) {
} }
gws := userGws[node.Network] gws := userGws[node.Network]
if extClient.DNS == "" {
extClient.DNS = node.IngressDNS
}
extClient.AllowedIPs = logic.GetExtclientAllowedIPs(extClient) extClient.AllowedIPs = logic.GetExtclientAllowedIPs(extClient)
gws = append(gws, models.UserRemoteGws{ gws = append(gws, models.UserRemoteGws{
GwID: node.ID.String(), GwID: node.ID.String(),

View File

@@ -55,6 +55,6 @@ func GetClient() (e EmailSender) {
} }
func IsValid(email string) bool { func IsValid(email string) bool {
emailRegex := regexp.MustCompile(`^[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,4}$`) emailRegex := regexp.MustCompile(`^[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,}$`)
return emailRegex.MatchString(email) return emailRegex.MatchString(email)
} }