|
@@ -1,24 +1,20 @@
|
|
|
package logic
|
|
|
|
|
|
import (
|
|
|
- "context"
|
|
|
"encoding/json"
|
|
|
"errors"
|
|
|
"fmt"
|
|
|
"net"
|
|
|
"reflect"
|
|
|
- "sort"
|
|
|
"strings"
|
|
|
"sync"
|
|
|
"time"
|
|
|
|
|
|
"github.com/goombaio/namegenerator"
|
|
|
"github.com/gravitl/netmaker/database"
|
|
|
- "github.com/gravitl/netmaker/db"
|
|
|
"github.com/gravitl/netmaker/logger"
|
|
|
"github.com/gravitl/netmaker/logic/acls"
|
|
|
"github.com/gravitl/netmaker/models"
|
|
|
- "github.com/gravitl/netmaker/schema"
|
|
|
"github.com/gravitl/netmaker/servercfg"
|
|
|
"golang.org/x/exp/slog"
|
|
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
|
@@ -493,362 +489,6 @@ func ToggleExtClientConnectivity(client *models.ExtClient, enable bool) (models.
|
|
|
return newClient, nil
|
|
|
}
|
|
|
|
|
|
-// Sort a slice of net.IP addresses
|
|
|
-func sortIPs(ips []net.IP) {
|
|
|
- sort.Slice(ips, func(i, j int) bool {
|
|
|
- ip1, ip2 := ips[i].To16(), ips[j].To16()
|
|
|
- return string(ip1) < string(ip2) // Compare as byte slices
|
|
|
- })
|
|
|
-}
|
|
|
-
|
|
|
-func GetStaticNodeIps(node models.Node) (ips []net.IP) {
|
|
|
- defer func() {
|
|
|
- sortIPs(ips)
|
|
|
- }()
|
|
|
- defaultUserPolicy, _ := GetDefaultPolicy(models.NetworkID(node.Network), models.UserPolicy)
|
|
|
- defaultDevicePolicy, _ := GetDefaultPolicy(models.NetworkID(node.Network), models.DevicePolicy)
|
|
|
-
|
|
|
- extclients := GetStaticNodesByNetwork(models.NetworkID(node.Network), false)
|
|
|
- for _, extclient := range extclients {
|
|
|
- if extclient.IsUserNode && defaultUserPolicy.Enabled {
|
|
|
- continue
|
|
|
- }
|
|
|
- if !extclient.IsUserNode && defaultDevicePolicy.Enabled {
|
|
|
- continue
|
|
|
- }
|
|
|
- if extclient.StaticNode.Address != "" {
|
|
|
- ips = append(ips, extclient.StaticNode.AddressIPNet4().IP)
|
|
|
- }
|
|
|
- if extclient.StaticNode.Address6 != "" {
|
|
|
- ips = append(ips, extclient.StaticNode.AddressIPNet6().IP)
|
|
|
- }
|
|
|
- }
|
|
|
- return
|
|
|
-}
|
|
|
-
|
|
|
-func getFwRulesForNodeAndPeerOnGw(node, peer models.Node, allowedPolicies []models.Acl) (rules []models.FwRule) {
|
|
|
-
|
|
|
- for _, policy := range allowedPolicies {
|
|
|
- // if static peer dst rule not for ingress node -> skip
|
|
|
- if node.Address.IP != nil {
|
|
|
- rules = append(rules, models.FwRule{
|
|
|
- SrcIP: net.IPNet{
|
|
|
- IP: node.Address.IP,
|
|
|
- Mask: net.CIDRMask(32, 32),
|
|
|
- },
|
|
|
- DstIP: net.IPNet{
|
|
|
- IP: peer.Address.IP,
|
|
|
- Mask: net.CIDRMask(32, 32),
|
|
|
- },
|
|
|
- AllowedProtocol: policy.Proto,
|
|
|
- AllowedPorts: policy.Port,
|
|
|
- Allow: true,
|
|
|
- })
|
|
|
- }
|
|
|
-
|
|
|
- if node.Address6.IP != nil {
|
|
|
- rules = append(rules, models.FwRule{
|
|
|
- SrcIP: net.IPNet{
|
|
|
- IP: node.Address6.IP,
|
|
|
- Mask: net.CIDRMask(128, 128),
|
|
|
- },
|
|
|
- DstIP: net.IPNet{
|
|
|
- IP: peer.Address6.IP,
|
|
|
- Mask: net.CIDRMask(128, 128),
|
|
|
- },
|
|
|
- AllowedProtocol: policy.Proto,
|
|
|
- AllowedPorts: policy.Port,
|
|
|
- Allow: true,
|
|
|
- })
|
|
|
- }
|
|
|
- if policy.AllowedDirection == models.TrafficDirectionBi {
|
|
|
- if node.Address.IP != nil {
|
|
|
- rules = append(rules, models.FwRule{
|
|
|
- SrcIP: net.IPNet{
|
|
|
- IP: peer.Address.IP,
|
|
|
- Mask: net.CIDRMask(32, 32),
|
|
|
- },
|
|
|
- DstIP: net.IPNet{
|
|
|
- IP: node.Address.IP,
|
|
|
- Mask: net.CIDRMask(32, 32),
|
|
|
- },
|
|
|
- AllowedProtocol: policy.Proto,
|
|
|
- AllowedPorts: policy.Port,
|
|
|
- Allow: true,
|
|
|
- })
|
|
|
- }
|
|
|
-
|
|
|
- if node.Address6.IP != nil {
|
|
|
- rules = append(rules, models.FwRule{
|
|
|
- SrcIP: net.IPNet{
|
|
|
- IP: peer.Address6.IP,
|
|
|
- Mask: net.CIDRMask(128, 128),
|
|
|
- },
|
|
|
- DstIP: net.IPNet{
|
|
|
- IP: node.Address6.IP,
|
|
|
- Mask: net.CIDRMask(128, 128),
|
|
|
- },
|
|
|
- AllowedProtocol: policy.Proto,
|
|
|
- AllowedPorts: policy.Port,
|
|
|
- Allow: true,
|
|
|
- })
|
|
|
- }
|
|
|
- }
|
|
|
- if len(node.StaticNode.ExtraAllowedIPs) > 0 {
|
|
|
- for _, additionalAllowedIPNet := range node.StaticNode.ExtraAllowedIPs {
|
|
|
- _, ipNet, err := net.ParseCIDR(additionalAllowedIPNet)
|
|
|
- if err != nil {
|
|
|
- continue
|
|
|
- }
|
|
|
- if ipNet.IP.To4() != nil && peer.Address.IP != nil {
|
|
|
- rules = append(rules, models.FwRule{
|
|
|
- SrcIP: net.IPNet{
|
|
|
- IP: peer.Address.IP,
|
|
|
- Mask: net.CIDRMask(32, 32),
|
|
|
- },
|
|
|
- DstIP: *ipNet,
|
|
|
- Allow: true,
|
|
|
- })
|
|
|
- } else if peer.Address6.IP != nil {
|
|
|
- rules = append(rules, models.FwRule{
|
|
|
- SrcIP: net.IPNet{
|
|
|
- IP: peer.Address6.IP,
|
|
|
- Mask: net.CIDRMask(128, 128),
|
|
|
- },
|
|
|
- DstIP: *ipNet,
|
|
|
- Allow: true,
|
|
|
- })
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
- if len(peer.StaticNode.ExtraAllowedIPs) > 0 {
|
|
|
- for _, additionalAllowedIPNet := range peer.StaticNode.ExtraAllowedIPs {
|
|
|
- _, ipNet, err := net.ParseCIDR(additionalAllowedIPNet)
|
|
|
- if err != nil {
|
|
|
- continue
|
|
|
- }
|
|
|
- if ipNet.IP.To4() != nil && node.Address.IP != nil {
|
|
|
- rules = append(rules, models.FwRule{
|
|
|
- SrcIP: net.IPNet{
|
|
|
- IP: node.Address.IP,
|
|
|
- Mask: net.CIDRMask(32, 32),
|
|
|
- },
|
|
|
- DstIP: *ipNet,
|
|
|
- Allow: true,
|
|
|
- })
|
|
|
- } else if node.Address6.IP != nil {
|
|
|
- rules = append(rules, models.FwRule{
|
|
|
- SrcIP: net.IPNet{
|
|
|
- IP: node.Address6.IP,
|
|
|
- Mask: net.CIDRMask(128, 128),
|
|
|
- },
|
|
|
- DstIP: *ipNet,
|
|
|
- Allow: true,
|
|
|
- })
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- // add egress range rules
|
|
|
- for _, dstI := range policy.Dst {
|
|
|
- if dstI.ID == models.EgressID {
|
|
|
-
|
|
|
- e := schema.Egress{ID: dstI.Value}
|
|
|
- err := e.Get(db.WithContext(context.TODO()))
|
|
|
- if err != nil {
|
|
|
- continue
|
|
|
- }
|
|
|
- dstI.Value = e.Range
|
|
|
-
|
|
|
- ip, cidr, err := net.ParseCIDR(dstI.Value)
|
|
|
- if err == nil {
|
|
|
- if ip.To4() != nil {
|
|
|
- if node.Address.IP != nil {
|
|
|
- rules = append(rules, models.FwRule{
|
|
|
- SrcIP: net.IPNet{
|
|
|
- IP: node.Address.IP,
|
|
|
- Mask: net.CIDRMask(32, 32),
|
|
|
- },
|
|
|
- DstIP: *cidr,
|
|
|
- AllowedProtocol: policy.Proto,
|
|
|
- AllowedPorts: policy.Port,
|
|
|
- Allow: true,
|
|
|
- })
|
|
|
- }
|
|
|
- } else {
|
|
|
- if node.Address6.IP != nil {
|
|
|
- rules = append(rules, models.FwRule{
|
|
|
- SrcIP: net.IPNet{
|
|
|
- IP: node.Address6.IP,
|
|
|
- Mask: net.CIDRMask(128, 128),
|
|
|
- },
|
|
|
- DstIP: *cidr,
|
|
|
- AllowedProtocol: policy.Proto,
|
|
|
- AllowedPorts: policy.Port,
|
|
|
- Allow: true,
|
|
|
- })
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return
|
|
|
-}
|
|
|
-
|
|
|
-func getFwRulesForUserNodesOnGw(node models.Node, nodes []models.Node) (rules []models.FwRule) {
|
|
|
- defaultUserPolicy, _ := GetDefaultPolicy(models.NetworkID(node.Network), models.UserPolicy)
|
|
|
- userNodes := GetStaticUserNodesByNetwork(models.NetworkID(node.Network))
|
|
|
- for _, userNodeI := range userNodes {
|
|
|
- for _, peer := range nodes {
|
|
|
- if peer.IsUserNode {
|
|
|
- continue
|
|
|
- }
|
|
|
-
|
|
|
- if ok, allowedPolicies := IsUserAllowedToCommunicate(userNodeI.StaticNode.OwnerID, peer); ok {
|
|
|
- if peer.IsStatic {
|
|
|
- peer = peer.StaticNode.ConvertToStaticNode()
|
|
|
- }
|
|
|
- if !defaultUserPolicy.Enabled {
|
|
|
- for _, policy := range allowedPolicies {
|
|
|
- if userNodeI.StaticNode.Address != "" {
|
|
|
- rules = append(rules, models.FwRule{
|
|
|
- SrcIP: userNodeI.StaticNode.AddressIPNet4(),
|
|
|
- DstIP: net.IPNet{
|
|
|
- IP: peer.Address.IP,
|
|
|
- Mask: net.CIDRMask(32, 32),
|
|
|
- },
|
|
|
- AllowedProtocol: policy.Proto,
|
|
|
- AllowedPorts: policy.Port,
|
|
|
- Allow: true,
|
|
|
- })
|
|
|
- }
|
|
|
- if userNodeI.StaticNode.Address6 != "" {
|
|
|
- rules = append(rules, models.FwRule{
|
|
|
- SrcIP: userNodeI.StaticNode.AddressIPNet6(),
|
|
|
- DstIP: net.IPNet{
|
|
|
- IP: peer.Address6.IP,
|
|
|
- Mask: net.CIDRMask(128, 128),
|
|
|
- },
|
|
|
- AllowedProtocol: policy.Proto,
|
|
|
- AllowedPorts: policy.Port,
|
|
|
- Allow: true,
|
|
|
- })
|
|
|
- }
|
|
|
-
|
|
|
- // add egress ranges
|
|
|
- for _, dstI := range policy.Dst {
|
|
|
- if dstI.ID == models.EgressID {
|
|
|
-
|
|
|
- e := schema.Egress{ID: dstI.Value}
|
|
|
- err := e.Get(db.WithContext(context.TODO()))
|
|
|
- if err != nil {
|
|
|
- continue
|
|
|
- }
|
|
|
- dstI.Value = e.Range
|
|
|
-
|
|
|
- ip, cidr, err := net.ParseCIDR(dstI.Value)
|
|
|
- if err == nil {
|
|
|
- if ip.To4() != nil && userNodeI.StaticNode.Address != "" {
|
|
|
- rules = append(rules, models.FwRule{
|
|
|
- SrcIP: userNodeI.StaticNode.AddressIPNet4(),
|
|
|
- DstIP: *cidr,
|
|
|
- AllowedProtocol: policy.Proto,
|
|
|
- AllowedPorts: policy.Port,
|
|
|
- Allow: true,
|
|
|
- })
|
|
|
- } else if ip.To16() != nil && userNodeI.StaticNode.Address6 != "" {
|
|
|
- rules = append(rules, models.FwRule{
|
|
|
- SrcIP: userNodeI.StaticNode.AddressIPNet6(),
|
|
|
- DstIP: *cidr,
|
|
|
- AllowedProtocol: policy.Proto,
|
|
|
- AllowedPorts: policy.Port,
|
|
|
- Allow: true,
|
|
|
- })
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- return
|
|
|
-}
|
|
|
-
|
|
|
-func GetFwRulesOnIngressGateway(node models.Node) (rules []models.FwRule) {
|
|
|
- // fetch user access to static clients via policies
|
|
|
- defer func() {
|
|
|
- sort.Slice(rules, func(i, j int) bool {
|
|
|
- if !rules[i].SrcIP.IP.Equal(rules[j].SrcIP.IP) {
|
|
|
- return string(rules[i].SrcIP.IP.To16()) < string(rules[j].SrcIP.IP.To16())
|
|
|
- }
|
|
|
- return string(rules[i].DstIP.IP.To16()) < string(rules[j].DstIP.IP.To16())
|
|
|
- })
|
|
|
- }()
|
|
|
- defaultDevicePolicy, _ := GetDefaultPolicy(models.NetworkID(node.Network), models.DevicePolicy)
|
|
|
- nodes, _ := GetNetworkNodes(node.Network)
|
|
|
- nodes = append(nodes, GetStaticNodesByNetwork(models.NetworkID(node.Network), true)...)
|
|
|
- rules = getFwRulesForUserNodesOnGw(node, nodes)
|
|
|
- if defaultDevicePolicy.Enabled {
|
|
|
- return
|
|
|
- }
|
|
|
- for _, nodeI := range nodes {
|
|
|
- if !nodeI.IsStatic || nodeI.IsUserNode {
|
|
|
- continue
|
|
|
- }
|
|
|
- // if nodeI.StaticNode.IngressGatewayID != node.ID.String() {
|
|
|
- // continue
|
|
|
- // }
|
|
|
- for _, peer := range nodes {
|
|
|
- if peer.StaticNode.ClientID == nodeI.StaticNode.ClientID || peer.IsUserNode {
|
|
|
- continue
|
|
|
- }
|
|
|
- if nodeI.StaticNode.IngressGatewayID != node.ID.String() &&
|
|
|
- ((!peer.IsStatic && peer.ID.String() != node.ID.String()) ||
|
|
|
- (peer.IsStatic && peer.StaticNode.IngressGatewayID != node.ID.String())) {
|
|
|
- continue
|
|
|
- }
|
|
|
- if peer.IsStatic {
|
|
|
- peer = peer.StaticNode.ConvertToStaticNode()
|
|
|
- }
|
|
|
- var allowedPolicies1 []models.Acl
|
|
|
- var ok bool
|
|
|
- if ok, allowedPolicies1 = IsNodeAllowedToCommunicate(nodeI.StaticNode.ConvertToStaticNode(), peer, true); ok {
|
|
|
- rules = append(rules, getFwRulesForNodeAndPeerOnGw(nodeI.StaticNode.ConvertToStaticNode(), peer, allowedPolicies1)...)
|
|
|
- }
|
|
|
- if ok, allowedPolicies2 := IsNodeAllowedToCommunicate(peer, nodeI.StaticNode.ConvertToStaticNode(), true); ok {
|
|
|
- rules = append(rules,
|
|
|
- getFwRulesForNodeAndPeerOnGw(peer, nodeI.StaticNode.ConvertToStaticNode(),
|
|
|
- GetUniquePolicies(allowedPolicies1, allowedPolicies2))...)
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- return
|
|
|
-}
|
|
|
-
|
|
|
-func GetUniquePolicies(policies1, policies2 []models.Acl) []models.Acl {
|
|
|
- policies1Map := make(map[string]struct{})
|
|
|
- for _, policy1I := range policies1 {
|
|
|
- policies1Map[policy1I.ID] = struct{}{}
|
|
|
- }
|
|
|
- for i := len(policies2) - 1; i >= 0; i-- {
|
|
|
- if _, ok := policies1Map[policies2[i].ID]; ok {
|
|
|
- policies2 = append(policies2[:i], policies2[i+1:]...)
|
|
|
- }
|
|
|
- }
|
|
|
- return policies2
|
|
|
-}
|
|
|
-
|
|
|
func GetExtPeers(node, peer *models.Node) ([]wgtypes.PeerConfig, []models.IDandAddr, []models.EgressNetworkRoutes, error) {
|
|
|
var peers []wgtypes.PeerConfig
|
|
|
var idsAndAddr []models.IDandAddr
|