mirror of
https://github.com/netbirdio/netbird.git
synced 2025-02-15 01:40:34 +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"
|
"net/netip"
|
||||||
"regexp"
|
"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/rs/xid"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/management/server/http/api"
|
"github.com/netbirdio/netbird/management/server/http/api"
|
||||||
@ -88,9 +92,51 @@ func (n *NetworkResource) Copy() *NetworkResource {
|
|||||||
Description: n.Description,
|
Description: n.Description,
|
||||||
Type: n.Type,
|
Type: n.Type,
|
||||||
Address: n.Address,
|
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
|
// GetResourceType returns the type of the resource based on the address
|
||||||
func GetResourceType(address string) (NetworkResourceType, string, netip.Prefix, error) {
|
func GetResourceType(address string) (NetworkResourceType, string, netip.Prefix, error) {
|
||||||
if prefix, err := netip.ParsePrefix(address); err == nil {
|
if prefix, err := netip.ParsePrefix(address); err == nil {
|
||||||
|
@ -13,6 +13,9 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"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/rs/xid"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@ -758,8 +761,66 @@ func setupTestAccountManager(b *testing.B, peers int, groups int) (*DefaultAccou
|
|||||||
peerIndex := i*(peers/groups) + j
|
peerIndex := i*(peers/groups) + j
|
||||||
group.Peers = append(group.Peers, fmt.Sprintf("peer-%d", peerIndex))
|
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
|
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
|
// Create a policy for this group
|
||||||
policy := &types.Policy{
|
policy := &types.Policy{
|
||||||
ID: fmt.Sprintf("policy-%d", i),
|
ID: fmt.Sprintf("policy-%d", i),
|
||||||
@ -767,11 +828,14 @@ func setupTestAccountManager(b *testing.B, peers int, groups int) (*DefaultAccou
|
|||||||
Enabled: true,
|
Enabled: true,
|
||||||
Rules: []*types.PolicyRule{
|
Rules: []*types.PolicyRule{
|
||||||
{
|
{
|
||||||
ID: fmt.Sprintf("rule-%d", i),
|
ID: fmt.Sprintf("rule-%d", i),
|
||||||
Name: fmt.Sprintf("Rule for Group %d", i),
|
Name: fmt.Sprintf("Rule for Group %d", i),
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
Sources: []string{groupID},
|
Sources: []string{groupID},
|
||||||
Destinations: []string{groupID},
|
Destinations: []string{groupID},
|
||||||
|
DestinationResource: types.Resource{
|
||||||
|
ID: resource.ID,
|
||||||
|
},
|
||||||
Bidirectional: true,
|
Bidirectional: true,
|
||||||
Protocol: types.PolicyRuleProtocolALL,
|
Protocol: types.PolicyRuleProtocolALL,
|
||||||
Action: types.PolicyTrafficActionAccept,
|
Action: types.PolicyTrafficActionAccept,
|
||||||
|
@ -9,6 +9,9 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"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/rs/xid"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@ -1876,6 +1879,7 @@ func TestAccount_getPeersRoutesFirewall(t *testing.T) {
|
|||||||
Action: "accept",
|
Action: "accept",
|
||||||
Destination: "192.0.2.0/32",
|
Destination: "192.0.2.0/32",
|
||||||
Protocol: "all",
|
Protocol: "all",
|
||||||
|
Domains: domain.List{"example.com"},
|
||||||
IsDynamic: true,
|
IsDynamic: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -1883,6 +1887,7 @@ func TestAccount_getPeersRoutesFirewall(t *testing.T) {
|
|||||||
Action: "accept",
|
Action: "accept",
|
||||||
Destination: "192.0.2.0/32",
|
Destination: "192.0.2.0/32",
|
||||||
Protocol: "all",
|
Protocol: "all",
|
||||||
|
Domains: domain.List{"example.com"},
|
||||||
IsDynamic: true,
|
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)
|
routesUpdate := a.GetRoutesToSync(ctx, peerID, peersToConnect)
|
||||||
routesFirewallRules := a.GetPeerRoutesFirewallRules(ctx, peerID, validatedPeersMap)
|
routesFirewallRules := a.GetPeerRoutesFirewallRules(ctx, peerID, validatedPeersMap)
|
||||||
|
|
||||||
|
networkResourcesRoutes := a.GetNetworkResourcesRoutesToSync(ctx, peerID, peersToConnect)
|
||||||
|
networkResourcesFirewallRules := a.GetPeerNetworkResourceFirewallRules(ctx, peerID, validatedPeersMap)
|
||||||
|
|
||||||
dnsManagementStatus := a.getPeerDNSManagementStatus(peerID)
|
dnsManagementStatus := a.getPeerDNSManagementStatus(peerID)
|
||||||
dnsUpdate := nbdns.Config{
|
dnsUpdate := nbdns.Config{
|
||||||
ServiceEnable: dnsManagementStatus,
|
ServiceEnable: dnsManagementStatus,
|
||||||
@ -274,11 +277,11 @@ func (a *Account) GetPeerNetworkMap(
|
|||||||
nm := &NetworkMap{
|
nm := &NetworkMap{
|
||||||
Peers: peersToConnect,
|
Peers: peersToConnect,
|
||||||
Network: a.Network.Copy(),
|
Network: a.Network.Copy(),
|
||||||
Routes: routesUpdate,
|
Routes: slices.Concat(networkResourcesRoutes, routesUpdate),
|
||||||
DNSConfig: dnsUpdate,
|
DNSConfig: dnsUpdate,
|
||||||
OfflinePeers: expiredPeers,
|
OfflinePeers: expiredPeers,
|
||||||
FirewallRules: firewallRules,
|
FirewallRules: firewallRules,
|
||||||
RoutesFirewallRules: routesFirewallRules,
|
RoutesFirewallRules: slices.Concat(networkResourcesFirewallRules, routesFirewallRules),
|
||||||
}
|
}
|
||||||
|
|
||||||
if metrics != nil {
|
if metrics != nil {
|
||||||
@ -1158,6 +1161,7 @@ func getDefaultPermit(route *route.Route) []*RouteFirewallRule {
|
|||||||
Action: string(PolicyTrafficActionAccept),
|
Action: string(PolicyTrafficActionAccept),
|
||||||
Destination: route.Network.String(),
|
Destination: route.Network.String(),
|
||||||
Protocol: string(PolicyRuleProtocolALL),
|
Protocol: string(PolicyRuleProtocolALL),
|
||||||
|
Domains: route.Domains,
|
||||||
IsDynamic: route.IsDynamic(),
|
IsDynamic: route.IsDynamic(),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1198,3 +1202,176 @@ func GetAllRoutePoliciesFromGroups(account *Account, accessControlGroups []strin
|
|||||||
|
|
||||||
return routePolicies
|
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),
|
Action: string(rule.Action),
|
||||||
Destination: route.Network.String(),
|
Destination: route.Network.String(),
|
||||||
Protocol: string(rule.Protocol),
|
Protocol: string(rule.Protocol),
|
||||||
|
Domains: route.Domains,
|
||||||
IsDynamic: route.IsDynamic(),
|
IsDynamic: route.IsDynamic(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
package types
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/netbirdio/netbird/management/domain"
|
||||||
|
)
|
||||||
|
|
||||||
// RouteFirewallRule a firewall rule applicable for a routed network.
|
// RouteFirewallRule a firewall rule applicable for a routed network.
|
||||||
type RouteFirewallRule struct {
|
type RouteFirewallRule struct {
|
||||||
// SourceRanges IP ranges of the routing peers.
|
// 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 represents the range of ports for a firewall rule
|
||||||
PortRange RulePortRange
|
PortRange RulePortRange
|
||||||
|
|
||||||
|
// Domains list of network domains for the routed traffic
|
||||||
|
Domains domain.List
|
||||||
|
|
||||||
// isDynamic indicates whether the rule is for DNS routing
|
// isDynamic indicates whether the rule is for DNS routing
|
||||||
IsDynamic bool
|
IsDynamic bool
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user