mirror of
https://github.com/netbirdio/netbird.git
synced 2025-02-08 14:29:39 +01:00
[management] Generate network map for new network concept (#3044)
This commit is contained in:
parent
cbd333a3e0
commit
bcf32f215c
@ -6,6 +6,10 @@ import (
|
||||
"net/netip"
|
||||
"regexp"
|
||||
|
||||
nbDomain "github.com/netbirdio/netbird/management/domain"
|
||||
routerTypes "github.com/netbirdio/netbird/management/server/networks/routers/types"
|
||||
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
||||
"github.com/netbirdio/netbird/route"
|
||||
"github.com/rs/xid"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server/http/api"
|
||||
@ -88,9 +92,51 @@ func (n *NetworkResource) Copy() *NetworkResource {
|
||||
Description: n.Description,
|
||||
Type: n.Type,
|
||||
Address: n.Address,
|
||||
Domain: n.Domain,
|
||||
Prefix: n.Prefix,
|
||||
}
|
||||
}
|
||||
|
||||
func (n *NetworkResource) ToRoute(peer *nbpeer.Peer, router *routerTypes.NetworkRouter) *route.Route {
|
||||
r := &route.Route{
|
||||
ID: route.ID(n.ID),
|
||||
AccountID: n.AccountID,
|
||||
KeepRoute: true,
|
||||
NetID: route.NetID(n.Name),
|
||||
Description: n.Description,
|
||||
Peer: peer.Key,
|
||||
PeerGroups: nil,
|
||||
Masquerade: router.Masquerade,
|
||||
Metric: router.Metric,
|
||||
Enabled: true,
|
||||
Groups: nil,
|
||||
AccessControlGroups: nil,
|
||||
}
|
||||
|
||||
if n.Type == host || n.Type == subnet {
|
||||
r.Network = n.Prefix
|
||||
|
||||
r.NetworkType = route.IPv4Network
|
||||
if n.Prefix.Addr().Is6() {
|
||||
r.NetworkType = route.IPv6Network
|
||||
}
|
||||
}
|
||||
|
||||
if n.Type == domain {
|
||||
domainList, err := nbDomain.FromStringList([]string{n.Domain})
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
r.Domains = domainList
|
||||
r.NetworkType = route.DomainNetwork
|
||||
|
||||
// add default placeholder for domain network
|
||||
r.Network = netip.PrefixFrom(netip.AddrFrom4([4]byte{192, 0, 2, 0}), 32)
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// GetResourceType returns the type of the resource based on the address
|
||||
func GetResourceType(address string) (NetworkResourceType, string, netip.Prefix, error) {
|
||||
if prefix, err := netip.ParsePrefix(address); err == nil {
|
||||
|
@ -13,6 +13,9 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
resourceTypes "github.com/netbirdio/netbird/management/server/networks/resources/types"
|
||||
routerTypes "github.com/netbirdio/netbird/management/server/networks/routers/types"
|
||||
networkTypes "github.com/netbirdio/netbird/management/server/networks/types"
|
||||
"github.com/rs/xid"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
@ -758,8 +761,66 @@ func setupTestAccountManager(b *testing.B, peers int, groups int) (*DefaultAccou
|
||||
peerIndex := i*(peers/groups) + j
|
||||
group.Peers = append(group.Peers, fmt.Sprintf("peer-%d", peerIndex))
|
||||
}
|
||||
|
||||
// Create network, router and resource for this group
|
||||
network := &networkTypes.Network{
|
||||
ID: fmt.Sprintf("network-%d", i),
|
||||
AccountID: account.Id,
|
||||
Name: fmt.Sprintf("Network for Group %d", i),
|
||||
}
|
||||
account.Networks = append(account.Networks, network)
|
||||
|
||||
ips := account.GetTakenIPs()
|
||||
peerIP, err := types.AllocatePeerIP(account.Network.Net, ips)
|
||||
if err != nil {
|
||||
return nil, "", "", err
|
||||
}
|
||||
|
||||
peerKey, _ := wgtypes.GeneratePrivateKey()
|
||||
peer := &nbpeer.Peer{
|
||||
ID: fmt.Sprintf("peer-%d", len(account.Peers)+1),
|
||||
DNSLabel: fmt.Sprintf("peer-%d", len(account.Peers)+1),
|
||||
Key: peerKey.PublicKey().String(),
|
||||
IP: peerIP,
|
||||
Status: &nbpeer.PeerStatus{},
|
||||
UserID: regularUser,
|
||||
Meta: nbpeer.PeerSystemMeta{
|
||||
Hostname: fmt.Sprintf("peer-%d", len(account.Peers)+1),
|
||||
GoOS: "linux",
|
||||
Kernel: "Linux",
|
||||
Core: "21.04",
|
||||
Platform: "x86_64",
|
||||
OS: "Ubuntu",
|
||||
WtVersion: "development",
|
||||
UIVersion: "development",
|
||||
},
|
||||
}
|
||||
account.Peers[peer.ID] = peer
|
||||
|
||||
group.Peers = append(group.Peers, peer.ID)
|
||||
account.Groups[groupID] = group
|
||||
|
||||
router := &routerTypes.NetworkRouter{
|
||||
ID: fmt.Sprintf("network-router-%d", i),
|
||||
NetworkID: network.ID,
|
||||
AccountID: account.Id,
|
||||
Peer: peer.ID,
|
||||
PeerGroups: []string{},
|
||||
Masquerade: false,
|
||||
Metric: 9999,
|
||||
}
|
||||
account.NetworkRouters = append(account.NetworkRouters, router)
|
||||
|
||||
resource := &resourceTypes.NetworkResource{
|
||||
ID: fmt.Sprintf("network-resource-%d", i),
|
||||
NetworkID: network.ID,
|
||||
AccountID: account.Id,
|
||||
Name: fmt.Sprintf("Network resource for Group %d", i),
|
||||
Type: "host",
|
||||
Address: "192.0.2.0/32",
|
||||
}
|
||||
account.NetworkResources = append(account.NetworkResources, resource)
|
||||
|
||||
// Create a policy for this group
|
||||
policy := &types.Policy{
|
||||
ID: fmt.Sprintf("policy-%d", i),
|
||||
@ -767,11 +828,14 @@ func setupTestAccountManager(b *testing.B, peers int, groups int) (*DefaultAccou
|
||||
Enabled: true,
|
||||
Rules: []*types.PolicyRule{
|
||||
{
|
||||
ID: fmt.Sprintf("rule-%d", i),
|
||||
Name: fmt.Sprintf("Rule for Group %d", i),
|
||||
Enabled: true,
|
||||
Sources: []string{groupID},
|
||||
Destinations: []string{groupID},
|
||||
ID: fmt.Sprintf("rule-%d", i),
|
||||
Name: fmt.Sprintf("Rule for Group %d", i),
|
||||
Enabled: true,
|
||||
Sources: []string{groupID},
|
||||
Destinations: []string{groupID},
|
||||
DestinationResource: types.Resource{
|
||||
ID: resource.ID,
|
||||
},
|
||||
Bidirectional: true,
|
||||
Protocol: types.PolicyRuleProtocolALL,
|
||||
Action: types.PolicyTrafficActionAccept,
|
||||
|
@ -9,6 +9,9 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
resourceTypes "github.com/netbirdio/netbird/management/server/networks/resources/types"
|
||||
routerTypes "github.com/netbirdio/netbird/management/server/networks/routers/types"
|
||||
networkTypes "github.com/netbirdio/netbird/management/server/networks/types"
|
||||
"github.com/rs/xid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
@ -1876,6 +1879,7 @@ func TestAccount_getPeersRoutesFirewall(t *testing.T) {
|
||||
Action: "accept",
|
||||
Destination: "192.0.2.0/32",
|
||||
Protocol: "all",
|
||||
Domains: domain.List{"example.com"},
|
||||
IsDynamic: true,
|
||||
},
|
||||
{
|
||||
@ -1883,6 +1887,7 @@ func TestAccount_getPeersRoutesFirewall(t *testing.T) {
|
||||
Action: "accept",
|
||||
Destination: "192.0.2.0/32",
|
||||
Protocol: "all",
|
||||
Domains: domain.List{"example.com"},
|
||||
IsDynamic: true,
|
||||
},
|
||||
}
|
||||
@ -2160,3 +2165,444 @@ func TestRouteAccountPeersUpdate(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccount_GetPeerNetworkResourceFirewallRules(t *testing.T) {
|
||||
var (
|
||||
peerBIp = "100.65.80.39"
|
||||
peerCIp = "100.65.254.139"
|
||||
peerHIp = "100.65.29.55"
|
||||
peerJIp = "100.65.29.65"
|
||||
peerKIp = "100.65.29.66"
|
||||
)
|
||||
|
||||
account := &types.Account{
|
||||
Peers: map[string]*nbpeer.Peer{
|
||||
"peerA": {
|
||||
ID: "peerA",
|
||||
IP: net.ParseIP("100.65.14.88"),
|
||||
Key: "peerA",
|
||||
Status: &nbpeer.PeerStatus{},
|
||||
Meta: nbpeer.PeerSystemMeta{
|
||||
GoOS: "linux",
|
||||
},
|
||||
},
|
||||
"peerB": {
|
||||
ID: "peerB",
|
||||
IP: net.ParseIP(peerBIp),
|
||||
Status: &nbpeer.PeerStatus{},
|
||||
Meta: nbpeer.PeerSystemMeta{},
|
||||
},
|
||||
"peerC": {
|
||||
ID: "peerC",
|
||||
IP: net.ParseIP(peerCIp),
|
||||
Status: &nbpeer.PeerStatus{},
|
||||
},
|
||||
"peerD": {
|
||||
ID: "peerD",
|
||||
IP: net.ParseIP("100.65.62.5"),
|
||||
Key: "peerD",
|
||||
Status: &nbpeer.PeerStatus{},
|
||||
Meta: nbpeer.PeerSystemMeta{
|
||||
GoOS: "linux",
|
||||
},
|
||||
},
|
||||
"peerE": {
|
||||
ID: "peerE",
|
||||
IP: net.ParseIP("100.65.32.206"),
|
||||
Key: "peerE",
|
||||
Status: &nbpeer.PeerStatus{},
|
||||
Meta: nbpeer.PeerSystemMeta{
|
||||
GoOS: "linux",
|
||||
},
|
||||
},
|
||||
"peerF": {
|
||||
ID: "peerF",
|
||||
IP: net.ParseIP("100.65.250.202"),
|
||||
Status: &nbpeer.PeerStatus{},
|
||||
},
|
||||
"peerG": {
|
||||
ID: "peerG",
|
||||
IP: net.ParseIP("100.65.13.186"),
|
||||
Status: &nbpeer.PeerStatus{},
|
||||
},
|
||||
"peerH": {
|
||||
ID: "peerH",
|
||||
IP: net.ParseIP(peerHIp),
|
||||
Status: &nbpeer.PeerStatus{},
|
||||
},
|
||||
"peerJ": {
|
||||
ID: "peerJ",
|
||||
IP: net.ParseIP(peerJIp),
|
||||
Status: &nbpeer.PeerStatus{},
|
||||
},
|
||||
"peerK": {
|
||||
ID: "peerK",
|
||||
IP: net.ParseIP(peerKIp),
|
||||
Status: &nbpeer.PeerStatus{},
|
||||
},
|
||||
},
|
||||
Groups: map[string]*types.Group{
|
||||
"router1": {
|
||||
ID: "router1",
|
||||
Name: "router1",
|
||||
Peers: []string{
|
||||
"peerA",
|
||||
},
|
||||
},
|
||||
"router2": {
|
||||
ID: "router2",
|
||||
Name: "router2",
|
||||
Peers: []string{
|
||||
"peerD",
|
||||
},
|
||||
},
|
||||
"finance": {
|
||||
ID: "finance",
|
||||
Name: "Finance",
|
||||
Peers: []string{
|
||||
"peerF",
|
||||
"peerG",
|
||||
},
|
||||
},
|
||||
"dev": {
|
||||
ID: "dev",
|
||||
Name: "Dev",
|
||||
Peers: []string{
|
||||
"peerC",
|
||||
"peerH",
|
||||
"peerB",
|
||||
},
|
||||
Resources: []types.Resource{
|
||||
{ID: "resource2"},
|
||||
},
|
||||
},
|
||||
"qa": {
|
||||
ID: "qa",
|
||||
Name: "QA",
|
||||
Peers: []string{
|
||||
"peerJ",
|
||||
"peerK",
|
||||
},
|
||||
},
|
||||
"restrictQA": {
|
||||
ID: "restrictQA",
|
||||
Name: "restrictQA",
|
||||
Peers: []string{
|
||||
"peerJ",
|
||||
},
|
||||
Resources: []types.Resource{
|
||||
{ID: "resource4"},
|
||||
},
|
||||
},
|
||||
"unrestrictedQA": {
|
||||
ID: "unrestrictedQA",
|
||||
Name: "unrestrictedQA",
|
||||
Peers: []string{
|
||||
"peerK",
|
||||
},
|
||||
Resources: []types.Resource{
|
||||
{ID: "resource4"},
|
||||
},
|
||||
},
|
||||
"contractors": {
|
||||
ID: "contractors",
|
||||
Name: "Contractors",
|
||||
Peers: []string{},
|
||||
},
|
||||
},
|
||||
Networks: []*networkTypes.Network{
|
||||
{
|
||||
ID: "network1",
|
||||
Name: "Finance Network",
|
||||
},
|
||||
{
|
||||
ID: "network2",
|
||||
Name: "Devs Network",
|
||||
},
|
||||
{
|
||||
ID: "network3",
|
||||
Name: "Contractors Network",
|
||||
},
|
||||
{
|
||||
ID: "network4",
|
||||
Name: "QA Network",
|
||||
},
|
||||
},
|
||||
NetworkRouters: []*routerTypes.NetworkRouter{
|
||||
{
|
||||
ID: "router1",
|
||||
NetworkID: "network1",
|
||||
Peer: "peerE",
|
||||
PeerGroups: nil,
|
||||
Masquerade: false,
|
||||
Metric: 9999,
|
||||
},
|
||||
{
|
||||
ID: "router2",
|
||||
NetworkID: "network2",
|
||||
PeerGroups: []string{"router1", "router2"},
|
||||
Masquerade: false,
|
||||
Metric: 9999,
|
||||
},
|
||||
{
|
||||
ID: "router3",
|
||||
NetworkID: "network3",
|
||||
Peer: "peerE",
|
||||
PeerGroups: []string{},
|
||||
},
|
||||
{
|
||||
ID: "router4",
|
||||
NetworkID: "network4",
|
||||
PeerGroups: []string{"router1"},
|
||||
Masquerade: false,
|
||||
Metric: 9999,
|
||||
},
|
||||
},
|
||||
NetworkResources: []*resourceTypes.NetworkResource{
|
||||
{
|
||||
ID: "resource1",
|
||||
NetworkID: "network1",
|
||||
Name: "Resource 1",
|
||||
Type: "subnet",
|
||||
Prefix: netip.MustParsePrefix("10.10.10.0/24"),
|
||||
},
|
||||
{
|
||||
ID: "resource2",
|
||||
NetworkID: "network2",
|
||||
Name: "Resource 2",
|
||||
Type: "subnet",
|
||||
Prefix: netip.MustParsePrefix("192.168.0.0/16"),
|
||||
},
|
||||
{
|
||||
ID: "resource3",
|
||||
NetworkID: "network3",
|
||||
Name: "Resource 3",
|
||||
Type: "domain",
|
||||
Domain: "example.com",
|
||||
},
|
||||
{
|
||||
ID: "resource4",
|
||||
NetworkID: "network4",
|
||||
Name: "Resource 4",
|
||||
Type: "domain",
|
||||
Domain: "example.com",
|
||||
},
|
||||
},
|
||||
Policies: []*types.Policy{
|
||||
{
|
||||
ID: "policyResource1",
|
||||
Name: "Policy for resource 1",
|
||||
Enabled: true,
|
||||
Rules: []*types.PolicyRule{
|
||||
{
|
||||
ID: "ruleResource1",
|
||||
Name: "ruleResource1",
|
||||
Bidirectional: true,
|
||||
Enabled: true,
|
||||
Protocol: types.PolicyRuleProtocolTCP,
|
||||
Action: types.PolicyTrafficActionAccept,
|
||||
PortRanges: []types.RulePortRange{
|
||||
{
|
||||
Start: 80,
|
||||
End: 350,
|
||||
}, {
|
||||
Start: 80,
|
||||
End: 350,
|
||||
},
|
||||
},
|
||||
Sources: []string{
|
||||
"finance",
|
||||
},
|
||||
DestinationResource: types.Resource{ID: "resource1"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "policyResource2",
|
||||
Name: "Policy for resource 2",
|
||||
Enabled: true,
|
||||
Rules: []*types.PolicyRule{
|
||||
{
|
||||
ID: "ruleResource2",
|
||||
Name: "ruleResource2",
|
||||
Bidirectional: true,
|
||||
Enabled: true,
|
||||
Protocol: types.PolicyRuleProtocolALL,
|
||||
Action: types.PolicyTrafficActionAccept,
|
||||
Ports: []string{"80", "320"},
|
||||
Sources: []string{"dev"},
|
||||
Destinations: []string{"dev"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "policyResource3",
|
||||
Name: "policyResource3",
|
||||
Enabled: true,
|
||||
Rules: []*types.PolicyRule{
|
||||
{
|
||||
ID: "ruleResource3",
|
||||
Name: "ruleResource3",
|
||||
Bidirectional: true,
|
||||
Enabled: true,
|
||||
Protocol: types.PolicyRuleProtocolTCP,
|
||||
Action: types.PolicyTrafficActionAccept,
|
||||
Ports: []string{"80"},
|
||||
Sources: []string{"restrictQA"},
|
||||
Destinations: []string{"restrictQA"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "policyResource4",
|
||||
Name: "policyResource4",
|
||||
Enabled: true,
|
||||
Rules: []*types.PolicyRule{
|
||||
{
|
||||
ID: "ruleResource4",
|
||||
Name: "ruleResource4",
|
||||
Bidirectional: true,
|
||||
Enabled: true,
|
||||
Protocol: types.PolicyRuleProtocolALL,
|
||||
Action: types.PolicyTrafficActionAccept,
|
||||
Sources: []string{"unrestrictedQA"},
|
||||
Destinations: []string{"unrestrictedQA"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
validatedPeers := make(map[string]struct{})
|
||||
for p := range account.Peers {
|
||||
validatedPeers[p] = struct{}{}
|
||||
}
|
||||
|
||||
t.Run("validate applied policies for different network resources", func(t *testing.T) {
|
||||
getNetworkResourceByID := func(account *types.Account, id string) *resourceTypes.NetworkResource {
|
||||
for _, resource := range account.NetworkResources {
|
||||
if resource.ID == id {
|
||||
return resource
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
getNetworkRouterByID := func(account *types.Account, id string) *routerTypes.NetworkRouter {
|
||||
for _, router := range account.NetworkRouters {
|
||||
if router.ID == id {
|
||||
return router
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Test case: Resource1 is directly applied to the policy (policyResource1)
|
||||
peerE := account.GetPeer("peerE")
|
||||
router1 := getNetworkRouterByID(account, "router1")
|
||||
route1 := getNetworkResourceByID(account, "resource1").ToRoute(peerE, router1)
|
||||
policies := account.GetPoliciesForNetworkResourceRoute(route1)
|
||||
assert.Len(t, policies, 1, "resource1 should have exactly 1 policy applied directly")
|
||||
|
||||
// Test case: Resource2 is applied to an access control group (dev),
|
||||
// which is part of the destination in the policy (policyResource2)
|
||||
peerA := account.GetPeer("peerA")
|
||||
router2 := getNetworkRouterByID(account, "router2")
|
||||
route2 := getNetworkResourceByID(account, "resource2").ToRoute(peerA, router2)
|
||||
policies = account.GetPoliciesForNetworkResourceRoute(route2)
|
||||
assert.Len(t, policies, 1, "resource2 should have exactly 1 policy applied via access control group")
|
||||
|
||||
// Test case: Resource3 is not applied to any access control group or policy
|
||||
router3 := getNetworkRouterByID(account, "router3")
|
||||
route3 := getNetworkResourceByID(account, "resource3").ToRoute(peerE, router3)
|
||||
policies = account.GetPoliciesForNetworkResourceRoute(route3)
|
||||
assert.Len(t, policies, 0, "resource3 should have no policies applied")
|
||||
|
||||
// Test case: Resource4 is applied to the access control groups (restrictQA and unrestrictedQA),
|
||||
// which is part of the destination in the policies (policyResource3 and policyResource4)
|
||||
router4 := getNetworkRouterByID(account, "router4")
|
||||
route4 := getNetworkResourceByID(account, "resource4").ToRoute(peerA, router4)
|
||||
policies = account.GetPoliciesForNetworkResourceRoute(route4)
|
||||
assert.Len(t, policies, 2, "resource4 should have exactly 2 policy applied via access control groups")
|
||||
})
|
||||
|
||||
t.Run("validate routing peer firewall rules for network resources", func(t *testing.T) {
|
||||
firewallRules := account.GetPeerNetworkResourceFirewallRules(context.Background(), "peerA", validatedPeers)
|
||||
assert.Len(t, firewallRules, 4)
|
||||
|
||||
expectedFirewallRules := []*types.RouteFirewallRule{
|
||||
{
|
||||
SourceRanges: []string{
|
||||
fmt.Sprintf(types.AllowedIPsFormat, peerCIp),
|
||||
fmt.Sprintf(types.AllowedIPsFormat, peerHIp),
|
||||
fmt.Sprintf(types.AllowedIPsFormat, peerBIp),
|
||||
},
|
||||
Action: "accept",
|
||||
Destination: "192.168.0.0/16",
|
||||
Protocol: "all",
|
||||
Port: 80,
|
||||
},
|
||||
{
|
||||
SourceRanges: []string{
|
||||
fmt.Sprintf(types.AllowedIPsFormat, peerCIp),
|
||||
fmt.Sprintf(types.AllowedIPsFormat, peerHIp),
|
||||
fmt.Sprintf(types.AllowedIPsFormat, peerBIp),
|
||||
},
|
||||
Action: "accept",
|
||||
Destination: "192.168.0.0/16",
|
||||
Protocol: "all",
|
||||
Port: 320,
|
||||
},
|
||||
}
|
||||
|
||||
additionalFirewallRules := []*types.RouteFirewallRule{
|
||||
{
|
||||
SourceRanges: []string{
|
||||
fmt.Sprintf(types.AllowedIPsFormat, peerJIp),
|
||||
},
|
||||
Action: "accept",
|
||||
Destination: "192.0.2.0/32",
|
||||
Protocol: "tcp",
|
||||
Port: 80,
|
||||
Domains: domain.List{"example.com"},
|
||||
IsDynamic: true,
|
||||
},
|
||||
{
|
||||
SourceRanges: []string{
|
||||
fmt.Sprintf(types.AllowedIPsFormat, peerKIp),
|
||||
},
|
||||
Action: "accept",
|
||||
Destination: "192.0.2.0/32",
|
||||
Protocol: "all",
|
||||
Domains: domain.List{"example.com"},
|
||||
IsDynamic: true,
|
||||
},
|
||||
}
|
||||
assert.ElementsMatch(t, orderRuleSourceRanges(firewallRules), orderRuleSourceRanges(append(expectedFirewallRules, additionalFirewallRules...)))
|
||||
|
||||
// peerD is also the routing peer for resource2
|
||||
firewallRules = account.GetPeerNetworkResourceFirewallRules(context.Background(), "peerD", validatedPeers)
|
||||
assert.Len(t, firewallRules, 2)
|
||||
assert.ElementsMatch(t, orderRuleSourceRanges(firewallRules), orderRuleSourceRanges(expectedFirewallRules))
|
||||
|
||||
// peerE is a single routing peer for resource1 and resource3
|
||||
// PeerE should only receive rules for resource1 since resource3 has no applied policy
|
||||
firewallRules = account.GetPeerNetworkResourceFirewallRules(context.Background(), "peerE", validatedPeers)
|
||||
assert.Len(t, firewallRules, 1)
|
||||
|
||||
expectedFirewallRules = []*types.RouteFirewallRule{
|
||||
{
|
||||
SourceRanges: []string{"100.65.250.202/32", "100.65.13.186/32"},
|
||||
Action: "accept",
|
||||
Destination: "10.10.10.0/24",
|
||||
Protocol: "tcp",
|
||||
PortRange: types.RulePortRange{Start: 80, End: 350},
|
||||
},
|
||||
}
|
||||
assert.ElementsMatch(t, orderRuleSourceRanges(firewallRules), orderRuleSourceRanges(expectedFirewallRules))
|
||||
|
||||
// peerC is part of distribution groups for resource2 but should not receive the firewall rules
|
||||
firewallRules = account.GetPeerRoutesFirewallRules(context.Background(), "peerC", validatedPeers)
|
||||
assert.Len(t, firewallRules, 0)
|
||||
})
|
||||
}
|
||||
|
@ -256,6 +256,9 @@ func (a *Account) GetPeerNetworkMap(
|
||||
routesUpdate := a.GetRoutesToSync(ctx, peerID, peersToConnect)
|
||||
routesFirewallRules := a.GetPeerRoutesFirewallRules(ctx, peerID, validatedPeersMap)
|
||||
|
||||
networkResourcesRoutes := a.GetNetworkResourcesRoutesToSync(ctx, peerID, peersToConnect)
|
||||
networkResourcesFirewallRules := a.GetPeerNetworkResourceFirewallRules(ctx, peerID, validatedPeersMap)
|
||||
|
||||
dnsManagementStatus := a.getPeerDNSManagementStatus(peerID)
|
||||
dnsUpdate := nbdns.Config{
|
||||
ServiceEnable: dnsManagementStatus,
|
||||
@ -274,11 +277,11 @@ func (a *Account) GetPeerNetworkMap(
|
||||
nm := &NetworkMap{
|
||||
Peers: peersToConnect,
|
||||
Network: a.Network.Copy(),
|
||||
Routes: routesUpdate,
|
||||
Routes: slices.Concat(networkResourcesRoutes, routesUpdate),
|
||||
DNSConfig: dnsUpdate,
|
||||
OfflinePeers: expiredPeers,
|
||||
FirewallRules: firewallRules,
|
||||
RoutesFirewallRules: routesFirewallRules,
|
||||
RoutesFirewallRules: slices.Concat(networkResourcesFirewallRules, routesFirewallRules),
|
||||
}
|
||||
|
||||
if metrics != nil {
|
||||
@ -1158,6 +1161,7 @@ func getDefaultPermit(route *route.Route) []*RouteFirewallRule {
|
||||
Action: string(PolicyTrafficActionAccept),
|
||||
Destination: route.Network.String(),
|
||||
Protocol: string(PolicyRuleProtocolALL),
|
||||
Domains: route.Domains,
|
||||
IsDynamic: route.IsDynamic(),
|
||||
}
|
||||
|
||||
@ -1198,3 +1202,176 @@ func GetAllRoutePoliciesFromGroups(account *Account, accessControlGroups []strin
|
||||
|
||||
return routePolicies
|
||||
}
|
||||
|
||||
// getRoutingPeerNetworkResourcesRoutes returns the network resources routes associated with a routing peer ID for the account.
|
||||
func (a *Account) getRoutingPeerNetworkResourcesRoutes(ctx context.Context, peerID string) []*route.Route {
|
||||
var routes []*route.Route
|
||||
|
||||
peer := a.GetPeer(peerID)
|
||||
if peer == nil {
|
||||
log.WithContext(ctx).Errorf("peer %s that doesn't exist under account %s", peerID, a.Id)
|
||||
return routes
|
||||
}
|
||||
|
||||
// currently we support only linux routing peers
|
||||
if peer.Meta.GoOS != "linux" {
|
||||
return routes
|
||||
}
|
||||
|
||||
for _, router := range a.NetworkRouters {
|
||||
for _, groupID := range router.PeerGroups {
|
||||
group := a.GetGroup(groupID)
|
||||
if group == nil {
|
||||
log.WithContext(ctx).Errorf("router %s has peers group %s that doesn't exist under account %s", router.ID, groupID, a.Id)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, id := range group.Peers {
|
||||
if id != peerID {
|
||||
continue
|
||||
}
|
||||
|
||||
resources := a.getNetworkResources(router.NetworkID)
|
||||
routes = append(routes, a.getNetworkResourcesRoutes(resources, router, peer)...)
|
||||
}
|
||||
}
|
||||
|
||||
if router.Peer == peerID {
|
||||
resources := a.getNetworkResources(router.NetworkID)
|
||||
routes = append(routes, a.getNetworkResourcesRoutes(resources, router, peer)...)
|
||||
}
|
||||
}
|
||||
|
||||
return routes
|
||||
}
|
||||
|
||||
// GetPeerNetworkResourceFirewallRules gets the network resources firewall rules associated with a routing peer ID for the account.
|
||||
func (a *Account) GetPeerNetworkResourceFirewallRules(ctx context.Context, peerID string, validatedPeersMap map[string]struct{}) []*RouteFirewallRule {
|
||||
routesFirewallRules := make([]*RouteFirewallRule, 0)
|
||||
routes := a.getRoutingPeerNetworkResourcesRoutes(ctx, peerID)
|
||||
|
||||
for _, route := range routes {
|
||||
resourceAppliedPolicies := a.GetPoliciesForNetworkResourceRoute(route)
|
||||
distributionPeers := getPoliciesSourcePeers(resourceAppliedPolicies, a.Groups)
|
||||
|
||||
rules := a.getRouteFirewallRules(ctx, peerID, resourceAppliedPolicies, route, validatedPeersMap, distributionPeers)
|
||||
routesFirewallRules = append(routesFirewallRules, rules...)
|
||||
}
|
||||
|
||||
return routesFirewallRules
|
||||
}
|
||||
|
||||
// getNetworkResourceGroups retrieves all groups associated with the given network resource route.
|
||||
func (a *Account) getNetworkResourceGroups(route *route.Route) []*Group {
|
||||
var networkResourceGroups []*Group
|
||||
|
||||
for _, group := range a.Groups {
|
||||
for _, resource := range group.Resources {
|
||||
if resource.ID == string(route.ID) {
|
||||
networkResourceGroups = append(networkResourceGroups, group)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return networkResourceGroups
|
||||
}
|
||||
|
||||
// GetNetworkResourcesRoutesToSync returns network routes for syncing with a specific peer and its ACL peers.
|
||||
func (a *Account) GetNetworkResourcesRoutesToSync(ctx context.Context, peerID string, aclPeers []*nbpeer.Peer) []*route.Route {
|
||||
routes := a.getRoutingPeerNetworkResourcesRoutes(ctx, peerID)
|
||||
peerRoutesMembership := make(LookupMap)
|
||||
for _, r := range routes {
|
||||
peerRoutesMembership[string(r.GetHAUniqueID())] = struct{}{}
|
||||
}
|
||||
|
||||
for _, peer := range aclPeers {
|
||||
peerRoutes := a.getRoutingPeerNetworkResourcesRoutes(ctx, peer.ID)
|
||||
filteredRoutes := a.filterRoutesFromPeersOfSameHAGroup(peerRoutes, peerRoutesMembership)
|
||||
routes = append(routes, filteredRoutes...)
|
||||
}
|
||||
|
||||
return routes
|
||||
}
|
||||
|
||||
// getNetworkResources filters and returns a list of network resources associated with the given network ID.
|
||||
func (a *Account) getNetworkResources(networkID string) []*resourceTypes.NetworkResource {
|
||||
var resources []*resourceTypes.NetworkResource
|
||||
for _, resource := range a.NetworkResources {
|
||||
if resource.NetworkID == networkID {
|
||||
resources = append(resources, resource)
|
||||
}
|
||||
}
|
||||
return resources
|
||||
}
|
||||
|
||||
// GetPoliciesForNetworkResourceRoute retrieves the list of policies that apply to a specific network resource route.
|
||||
// A policy is deemed applicable if its destination groups include any of the given network resource groups
|
||||
// or if its destination resource explicitly matches the provided route.
|
||||
func (a *Account) GetPoliciesForNetworkResourceRoute(route *route.Route) []*Policy {
|
||||
var resourceAppliedPolicies []*Policy
|
||||
|
||||
networkResourceGroups := a.getNetworkResourceGroups(route)
|
||||
|
||||
for _, policy := range a.Policies {
|
||||
if !policy.Enabled {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, rule := range policy.Rules {
|
||||
if !rule.Enabled {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, group := range networkResourceGroups {
|
||||
if slices.Contains(rule.Destinations, group.ID) {
|
||||
resourceAppliedPolicies = append(resourceAppliedPolicies, policy)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if rule.DestinationResource.ID == string(route.ID) {
|
||||
resourceAppliedPolicies = append(resourceAppliedPolicies, policy)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return resourceAppliedPolicies
|
||||
}
|
||||
|
||||
// getNetworkResourcesRoutes convert the network resources list to routes list.
|
||||
func (a *Account) getNetworkResourcesRoutes(resources []*resourceTypes.NetworkResource, router *routerTypes.NetworkRouter, peer *nbpeer.Peer) []*route.Route {
|
||||
routes := make([]*route.Route, 0, len(resources))
|
||||
for _, resource := range resources {
|
||||
resourceRoute := resource.ToRoute(peer, router)
|
||||
resourceAppliedPolicies := a.GetPoliciesForNetworkResourceRoute(resourceRoute)
|
||||
|
||||
// distribute the resource routes only if there is policy applied to it
|
||||
if len(resourceAppliedPolicies) > 0 {
|
||||
routes = append(routes, resourceRoute)
|
||||
}
|
||||
}
|
||||
|
||||
return routes
|
||||
}
|
||||
|
||||
// getPoliciesSourcePeers collects all unique peers from the source groups defined in the given policies.
|
||||
func getPoliciesSourcePeers(policies []*Policy, groups map[string]*Group) map[string]struct{} {
|
||||
sourcePeers := make(map[string]struct{})
|
||||
|
||||
for _, policy := range policies {
|
||||
for _, rule := range policy.Rules {
|
||||
for _, sourceGroup := range rule.Sources {
|
||||
group := groups[sourceGroup]
|
||||
if group == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, peer := range group.Peers {
|
||||
sourcePeers[peer] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sourcePeers
|
||||
}
|
||||
|
@ -53,6 +53,7 @@ func generateRouteFirewallRules(ctx context.Context, route *nbroute.Route, rule
|
||||
Action: string(rule.Action),
|
||||
Destination: route.Network.String(),
|
||||
Protocol: string(rule.Protocol),
|
||||
Domains: route.Domains,
|
||||
IsDynamic: route.IsDynamic(),
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,9 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"github.com/netbirdio/netbird/management/domain"
|
||||
)
|
||||
|
||||
// RouteFirewallRule a firewall rule applicable for a routed network.
|
||||
type RouteFirewallRule struct {
|
||||
// SourceRanges IP ranges of the routing peers.
|
||||
@ -20,6 +24,9 @@ type RouteFirewallRule struct {
|
||||
// PortRange represents the range of ports for a firewall rule
|
||||
PortRange RulePortRange
|
||||
|
||||
// Domains list of network domains for the routed traffic
|
||||
Domains domain.List
|
||||
|
||||
// isDynamic indicates whether the rule is for DNS routing
|
||||
IsDynamic bool
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user