mirror of
https://github.com/netbirdio/netbird.git
synced 2026-08-11 18:21:29 +02:00
Compare commits
24 Commits
revert/com
...
l10n_main2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4adc9056ed | ||
|
|
239b1c7641 | ||
|
|
c9fe1e06d1 | ||
|
|
16e0b4ce70 | ||
|
|
cfd1aa352b | ||
|
|
4f717e63e5 | ||
|
|
413d59be8a | ||
|
|
3b9c3e26a9 | ||
|
|
4b87d2f3a7 | ||
|
|
7b2f91cf16 | ||
|
|
5f735e2389 | ||
|
|
20e37798b9 | ||
|
|
4902a07918 | ||
|
|
123a4d0e8e | ||
|
|
598ab165bd | ||
|
|
d50f02673c | ||
|
|
7debd2c286 | ||
|
|
5f15d3d64a | ||
|
|
41f20e63d1 | ||
|
|
c2eb2701c3 | ||
|
|
a06618d0ea | ||
|
|
ecfada98ac | ||
|
|
f805c149d9 | ||
|
|
99048e2bf2 |
@@ -124,7 +124,7 @@ func startManagement(t *testing.T, config *config.Config, testFile string) (*grp
|
||||
|
||||
updateManager := update_channel.NewPeersUpdateManager(metrics)
|
||||
requestBuffer := mgmt.NewAccountRequestBuffer(ctx, store)
|
||||
networkMapController := controller.NewController(ctx, store, metrics, updateManager, requestBuffer, mgmt.MockIntegratedValidator{}, settingsMockManager, "netbird.cloud", port_forwarding.NewControllerMock(), manager.NewEphemeralManager(store, peersmanager), config, nil)
|
||||
networkMapController := controller.NewController(ctx, store, metrics, updateManager, requestBuffer, mgmt.MockIntegratedValidator{}, settingsMockManager, "netbird.cloud", port_forwarding.NewControllerMock(), manager.NewEphemeralManager(store, peersmanager), config)
|
||||
|
||||
accountManager, err := mgmt.BuildManager(ctx, config, store, networkMapController, jobManager, nil, "", eventStore, nil, false, iv, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManagerMock, false, cacheStore)
|
||||
if err != nil {
|
||||
|
||||
@@ -21,8 +21,8 @@ import (
|
||||
"github.com/netbirdio/netbird/client/internal"
|
||||
"github.com/netbirdio/netbird/client/internal/peer"
|
||||
"github.com/netbirdio/netbird/client/internal/profilemanager"
|
||||
nbnet "github.com/netbirdio/netbird/client/net"
|
||||
"github.com/netbirdio/netbird/client/proto"
|
||||
nbnet "github.com/netbirdio/netbird/client/net"
|
||||
"github.com/netbirdio/netbird/client/server"
|
||||
"github.com/netbirdio/netbird/client/system"
|
||||
"github.com/netbirdio/netbird/shared/management/domain"
|
||||
|
||||
@@ -146,7 +146,7 @@ func startManagement(t *testing.T, signalAddr string) string {
|
||||
|
||||
updateManager := update_channel.NewPeersUpdateManager(metrics)
|
||||
requestBuffer := mgmt.NewAccountRequestBuffer(context.Background(), testStore)
|
||||
networkMapController := controller.NewController(context.Background(), testStore, metrics, updateManager, requestBuffer, mgmt.MockIntegratedValidator{}, settingsMockManager, "netbird.selfhosted", port_forwarding.NewControllerMock(), manager.NewEphemeralManager(testStore, peersManager), cfg, nil)
|
||||
networkMapController := controller.NewController(context.Background(), testStore, metrics, updateManager, requestBuffer, mgmt.MockIntegratedValidator{}, settingsMockManager, "netbird.selfhosted", port_forwarding.NewControllerMock(), manager.NewEphemeralManager(testStore, peersManager), cfg)
|
||||
accountManager, err := mgmt.BuildManager(context.Background(), cfg, testStore, networkMapController, jobManager, nil, "", eventStore, nil, false, iv, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager, false, cacheStore)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
||||
@@ -519,7 +519,7 @@ func startManagement(t *testing.T, dataDir, testFile string) (*grpc.Server, stri
|
||||
|
||||
updateManager := update_channel.NewPeersUpdateManager(metrics)
|
||||
requestBuffer := server.NewAccountRequestBuffer(context.Background(), store)
|
||||
networkMapController := controller.NewController(context.Background(), store, metrics, updateManager, requestBuffer, server.MockIntegratedValidator{}, settingsMockManager, "netbird.selfhosted", port_forwarding.NewControllerMock(), manager.NewEphemeralManager(store, peersManager), config, nil)
|
||||
networkMapController := controller.NewController(context.Background(), store, metrics, updateManager, requestBuffer, server.MockIntegratedValidator{}, settingsMockManager, "netbird.selfhosted", port_forwarding.NewControllerMock(), manager.NewEphemeralManager(store, peersManager), config)
|
||||
accountManager, err := server.BuildManager(context.Background(), config, store, networkMapController, jobManager, nil, "", eventStore, nil, false, ia, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManager, false, cacheStore)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
|
||||
@@ -232,3 +232,4 @@ func toNetIDs(routes []string) []route.NetID {
|
||||
}
|
||||
return netIDs
|
||||
}
|
||||
|
||||
|
||||
@@ -200,7 +200,7 @@ func startManagement(t *testing.T, signalAddr string, counter *int) (*grpc.Serve
|
||||
|
||||
requestBuffer := server.NewAccountRequestBuffer(context.Background(), store)
|
||||
peersUpdateManager := update_channel.NewPeersUpdateManager(metrics)
|
||||
networkMapController := controller.NewController(context.Background(), store, metrics, peersUpdateManager, requestBuffer, server.MockIntegratedValidator{}, settingsMockManager, "netbird.selfhosted", port_forwarding.NewControllerMock(), manager.NewEphemeralManager(store, peersManager), config, nil)
|
||||
networkMapController := controller.NewController(context.Background(), store, metrics, peersUpdateManager, requestBuffer, server.MockIntegratedValidator{}, settingsMockManager, "netbird.selfhosted", port_forwarding.NewControllerMock(), manager.NewEphemeralManager(store, peersManager), config)
|
||||
accountManager, err := server.BuildManager(context.Background(), config, store, networkMapController, jobManager, nil, "", eventStore, nil, false, ia, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManagerMock, false, cacheStore)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
|
||||
1814
client/ui/i18n/locales/de-DE/common.json
Normal file
1814
client/ui/i18n/locales/de-DE/common.json
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1814
client/ui/i18n/locales/fr-FR/common.json
Normal file
1814
client/ui/i18n/locales/fr-FR/common.json
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1814
client/ui/i18n/locales/he-IL/common.json
Normal file
1814
client/ui/i18n/locales/he-IL/common.json
Normal file
File diff suppressed because it is too large
Load Diff
1814
client/ui/i18n/locales/he/common.json
Normal file
1814
client/ui/i18n/locales/he/common.json
Normal file
File diff suppressed because it is too large
Load Diff
1814
client/ui/i18n/locales/hu-HU/common.json
Normal file
1814
client/ui/i18n/locales/hu-HU/common.json
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1814
client/ui/i18n/locales/it-IT/common.json
Normal file
1814
client/ui/i18n/locales/it-IT/common.json
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1814
client/ui/i18n/locales/ja-JP/common.json
Normal file
1814
client/ui/i18n/locales/ja-JP/common.json
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1814
client/ui/i18n/locales/ru-RU/common.json
Normal file
1814
client/ui/i18n/locales/ru-RU/common.json
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1814
client/ui/i18n/locales/uk-UA/common.json
Normal file
1814
client/ui/i18n/locales/uk-UA/common.json
Normal file
File diff suppressed because it is too large
Load Diff
1814
client/ui/i18n/locales/uk/common.json
Normal file
1814
client/ui/i18n/locales/uk/common.json
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
8
crowdin.yml
Normal file
8
crowdin.yml
Normal file
@@ -0,0 +1,8 @@
|
||||
files:
|
||||
- source: /client/ui/i18n/locales/en/common.json
|
||||
translation: /client/ui/i18n/locales/%locale%/common.json
|
||||
type: chrome
|
||||
languages_mapping:
|
||||
locale:
|
||||
es-ES: es
|
||||
pt-BR: pt
|
||||
2
go.mod
2
go.mod
@@ -81,7 +81,7 @@ require (
|
||||
github.com/miekg/dns v1.1.72
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2
|
||||
github.com/moby/moby/api v1.54.1
|
||||
github.com/netbirdio/management-integrations/integrations v0.0.0-20260803100840-78e79ba20f87
|
||||
github.com/netbirdio/management-integrations/integrations v0.0.0-20260416123949-2355d972be42
|
||||
github.com/netbirdio/signal-dispatcher/dispatcher v0.0.0-20250805121659-6b4ac470ca45
|
||||
github.com/oapi-codegen/runtime v1.1.2
|
||||
github.com/okta/okta-sdk-golang/v2 v2.18.0
|
||||
|
||||
4
go.sum
4
go.sum
@@ -482,8 +482,8 @@ github.com/netbirdio/easyjson v0.9.0 h1:6Nw2lghSVuy8RSkAYDhDv1thBVEmfVbKZnV7T7Z6
|
||||
github.com/netbirdio/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
|
||||
github.com/netbirdio/ice/v4 v4.0.0-20250908184934-6202be846b51 h1:Ov4qdafATOgGMB1wbSuh+0aAHcwz9hdvB6VZjh1mVMI=
|
||||
github.com/netbirdio/ice/v4 v4.0.0-20250908184934-6202be846b51/go.mod h1:ZSIbPdBn5hePO8CpF1PekH2SfpTxg1PDhEwtbqZS7R8=
|
||||
github.com/netbirdio/management-integrations/integrations v0.0.0-20260803100840-78e79ba20f87 h1:iJeUvSMC0BTpkw7u4JyWcY4/3dl7fEL9DR/TpKf2+1w=
|
||||
github.com/netbirdio/management-integrations/integrations v0.0.0-20260803100840-78e79ba20f87/go.mod h1:pmsCPx1S0nuZRxCextGpc9AV4hLgGSuTsc4NMuwGeCo=
|
||||
github.com/netbirdio/management-integrations/integrations v0.0.0-20260416123949-2355d972be42 h1:F3zS5fT9xzD1OFLfcdAE+3FfyiwjGukF1hvj0jErgs8=
|
||||
github.com/netbirdio/management-integrations/integrations v0.0.0-20260416123949-2355d972be42/go.mod h1:n47r67ZSPgwSmT/Z1o48JjZQW9YJ6m/6Bd/uAXkL3Pg=
|
||||
github.com/netbirdio/service v0.0.0-20240911161631-f62744f42502 h1:3tHlFmhTdX9axERMVN63dqyFqnvuD+EMJHzM7mNGON8=
|
||||
github.com/netbirdio/service v0.0.0-20240911161631-f62744f42502/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM=
|
||||
github.com/netbirdio/signal-dispatcher/dispatcher v0.0.0-20250805121659-6b4ac470ca45 h1:ujgviVYmx243Ksy7NdSwrdGPSRNE3pb8kEDSpH0QuAQ=
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
//go:build integration
|
||||
|
||||
package networkmap_pgsql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetAccountSettings(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
|
||||
execQuery(t, ctx,
|
||||
`insert into accounts (id, settings_peer_login_expiration_enabled, settings_peer_login_expiration, settings_peer_inactivity_expiration_enabled,
|
||||
settings_peer_inactivity_expiration, settings_dns_domain, settings_ipv6_enabled_groups, settings_routing_peer_dns_resolution_enabled,
|
||||
settings_lazy_connection_enabled, settings_auto_update_version, settings_auto_update_always, settings_metrics_push_enabled)
|
||||
values('account-3',null,null,null,null,null,null,null,null,null,null,null)`)
|
||||
|
||||
accountSettings, err := conn(t, ctx).GetAccountSettings(ctx, "account-1")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, accountSettings, nmdata.AccountSettingsInfo{
|
||||
PeerLoginExpirationEnabled: true,
|
||||
PeerLoginExpiration: 86400000000000 * time.Nanosecond,
|
||||
PeerInactivityExpirationEnabled: false,
|
||||
PeerInactivityExpiration: 86400000000000 * time.Nanosecond,
|
||||
DNSDomain: "",
|
||||
IPv6EnabledGroups: []string{"group-one-resource-id"},
|
||||
RoutingPeerDNSResolutionEnabled: false,
|
||||
LazyConnectionEnabled: false,
|
||||
AutoUpdateVersion: "disabled",
|
||||
AutoUpdateAlways: false,
|
||||
MetricsPushEnabled: false,
|
||||
})
|
||||
|
||||
accountSettings, err = conn(t, ctx).GetAccountSettings(ctx, "account-2")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, accountSettings, nmdata.AccountSettingsInfo{
|
||||
PeerLoginExpirationEnabled: true,
|
||||
PeerLoginExpiration: 86400000000000 * time.Nanosecond,
|
||||
PeerInactivityExpirationEnabled: false,
|
||||
PeerInactivityExpiration: 86400000000000 * time.Nanosecond,
|
||||
DNSDomain: "",
|
||||
IPv6EnabledGroups: []string{"group-two-resources-id"},
|
||||
RoutingPeerDNSResolutionEnabled: false,
|
||||
LazyConnectionEnabled: false,
|
||||
AutoUpdateVersion: "disabled",
|
||||
AutoUpdateAlways: false,
|
||||
MetricsPushEnabled: false,
|
||||
})
|
||||
|
||||
accountSettings, err = conn(t, ctx).GetAccountSettings(ctx, "account-3")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, accountSettings, nmdata.AccountSettingsInfo{})
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
insert into accounts (id, network_identifier, network_net, network_net_v6, network_dns, network_serial,dns_settings_disabled_management_groups,
|
||||
settings_peer_login_expiration_enabled, settings_peer_login_expiration, settings_peer_inactivity_expiration_enabled,
|
||||
settings_peer_inactivity_expiration, settings_dns_domain, settings_ipv6_enabled_groups, settings_routing_peer_dns_resolution_enabled,
|
||||
settings_lazy_connection_enabled, settings_auto_update_version, settings_auto_update_always, settings_metrics_push_enabled)
|
||||
VALUES('account-1','network-1','{"IP":"100.103.0.0","Mask":"//8AAA=="}','{"IP":"fdde:e995:fd38:a465::","Mask":"//////////8AAAAAAAAAAA=="}','',1,'["disabled-group-1","disabled-group-2"]',
|
||||
true, 86400000000000, false,
|
||||
86400000000000, null, '["group-one-resource-id"]', false,
|
||||
false, 'disabled', false, false);
|
||||
insert into accounts (id, network_identifier, network_net, network_net_v6, network_dns, network_serial,dns_settings_disabled_management_groups,
|
||||
settings_peer_login_expiration_enabled, settings_peer_login_expiration, settings_peer_inactivity_expiration_enabled,
|
||||
settings_peer_inactivity_expiration, settings_dns_domain, settings_ipv6_enabled_groups, settings_routing_peer_dns_resolution_enabled,
|
||||
settings_lazy_connection_enabled, settings_auto_update_version, settings_auto_update_always, settings_metrics_push_enabled)
|
||||
VALUES('account-2','network-2','{"IP":"110.0.0.0","Mask":"//8AAA=="}','{"IP":"fddf:e995:fd38:a465::","Mask":"//////////8AAAAAAAAAAA=="}','',2,null,
|
||||
true, 86400000000000, false,
|
||||
86400000000000, null, '["group-two-resources-id"]', false,
|
||||
false, 'disabled', false, false);
|
||||
insert into groups (id, account_id, name, resources, public_id) VALUES('group-one-resource-id','account-1','group-1-name', '[{"ID":"host-id-1","Type":"host"}]','group-one-resource-id-public');
|
||||
insert into groups (id, account_id, name, resources, public_id) VALUES('group-two-resources-id','account-1','group-2-name', '[{"ID":"subnet-id-1","Type":"subnet"}, {"ID":"host-id-2","Type":"host"}]','group-two-resources-id-public');
|
||||
insert into groups (id, account_id, name, resources, public_id) VALUES('group-no-resources-id','account-1','group-3-name', null,'group-no-resources-id-public');
|
||||
insert into group_peers (account_id, peer_id, group_id) VALUES('account-1','peer-id-1','group-one-resource-id');
|
||||
insert into group_peers (account_id, peer_id, group_id) VALUES('account-1','peer-id-2','group-two-resources-id');
|
||||
insert into group_peers (account_id, peer_id, group_id) VALUES('account-1','peer-id-3','group-two-resources-id');
|
||||
@@ -1,25 +0,0 @@
|
||||
//go:build integration
|
||||
|
||||
package networkmap_pgsql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetDnsSettings(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
|
||||
settings, err := conn(t, ctx).GetDnsSettings(ctx, "account-1")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, settings, nmdata.DNSSettings{
|
||||
DisabledManagementGroups: []string{"disabled-group-1", "disabled-group-2"},
|
||||
})
|
||||
|
||||
settings, err = conn(t, ctx).GetDnsSettings(ctx, "account-2")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, settings, nmdata.DNSSettings{})
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
//go:build integration
|
||||
|
||||
package networkmap_pgsql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap"
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetAppliedZoneCandidatesViaPgxConnection(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
|
||||
execQuery(t, ctx,
|
||||
`insert into zones (id, account_id, domain, enable_search_domain, distribution_groups)
|
||||
VALUES('zone-1','account-1','test-1.com',true,'["group-one-resource-id"]')`)
|
||||
execQuery(t, ctx,
|
||||
`insert into zones (id, account_id, domain, enable_search_domain, distribution_groups)
|
||||
VALUES('zone-2','account-1','test-2.com',false,'["group-two-resources-id"]')`)
|
||||
execQuery(t, ctx,
|
||||
`insert into records (id, account_id, zone_id, name, type, ttl, content)
|
||||
VALUES('record-1','account-1','zone-1','test.test-1.com','A',1800,'1.1.1.1')`)
|
||||
execQuery(t, ctx,
|
||||
`insert into records (id, account_id, zone_id, name, type, ttl, content)
|
||||
VALUES('record-2','account-1','zone-1','test2.test-1.com','A',1800,'1.1.1.2')`)
|
||||
execQuery(t, ctx,
|
||||
`insert into records (id, account_id, zone_id, name, type, ttl, content)
|
||||
VALUES('record-3','account-1','zone-1','test3.test-1.com','CNAME',1800,'test4.test-1.com')`)
|
||||
execQuery(t, ctx,
|
||||
`insert into records (id, account_id, zone_id, name, type, ttl, content)
|
||||
VALUES('record-4','account-1','zone-2','test2.test-2.com','CNAME',1800,'test3.test-2.com')`)
|
||||
|
||||
zoneCandidates, err := conn(t, ctx).GetAppliedZoneCandidates(ctx, "account-1")
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Contains(t, zoneCandidates, networkmap.AppliedZoneCandidate{
|
||||
DistributionGroups: []string{"group-one-resource-id"},
|
||||
Zone: nmdata.CustomZone{
|
||||
Domain: "test-1.com",
|
||||
SearchDomainDisabled: false,
|
||||
Records: []nmdata.SimpleRecord{
|
||||
{Name: "test.test-1.com", Type: int(dns.TypeA), Class: "IN", TTL: 1800, RData: "1.1.1.1"},
|
||||
{Name: "test2.test-1.com", Type: int(dns.TypeA), Class: "IN", TTL: 1800, RData: "1.1.1.2"},
|
||||
{Name: "test3.test-1.com", Type: int(dns.TypeCNAME), Class: "IN", TTL: 1800, RData: "test4.test-1.com."},
|
||||
},
|
||||
},
|
||||
})
|
||||
assert.Contains(t, zoneCandidates, networkmap.AppliedZoneCandidate{
|
||||
DistributionGroups: []string{"group-two-resources-id"},
|
||||
Zone: nmdata.CustomZone{
|
||||
Domain: "test-2.com",
|
||||
SearchDomainDisabled: true,
|
||||
Records: []nmdata.SimpleRecord{
|
||||
{Name: "test2.test-2.com", Type: int(dns.TypeCNAME), Class: "IN", TTL: 1800, RData: "test3.test-2.com."},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
//go:build integration
|
||||
|
||||
package networkmap_pgsql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"testing"
|
||||
|
||||
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetDomains(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
|
||||
execQuery(t, ctx,
|
||||
`insert into domains (id, account_id, domain, target_cluster)
|
||||
VALUES('domain-1','account-1','test-1.com','target-1.cluster.local')`)
|
||||
execQuery(t, ctx,
|
||||
`insert into domains (id, account_id, domain, target_cluster)
|
||||
VALUES('domain-2','account-1','test-2.com','target-2.cluster.local')`)
|
||||
execQuery(t, ctx,
|
||||
`insert into domains (id, account_id, domain, target_cluster)
|
||||
VALUES('domain-3','account-1',null,null)`)
|
||||
|
||||
domains, err := conn(t, ctx).GetDomains(ctx, "account-1")
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, domains, 2)
|
||||
|
||||
assert.Contains(t, domains, networkmapdb.Domain{
|
||||
Domain: sql.NullString{String: "test-1.com", Valid: true},
|
||||
TargetCluster: sql.NullString{String: "target-1.cluster.local", Valid: true},
|
||||
})
|
||||
assert.Contains(t, domains, networkmapdb.Domain{
|
||||
Domain: sql.NullString{String: "test-2.com", Valid: true},
|
||||
TargetCluster: sql.NullString{String: "target-2.cluster.local", Valid: true},
|
||||
})
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
//go:build integration
|
||||
|
||||
package networkmap_pgsql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGetGroups(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
|
||||
groups, resourceToGroupIdx, err := conn(t, ctx).GetGroups(ctx, "account-1")
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t,
|
||||
groups,
|
||||
nmdata.Group{ID: "group-one-resource-id", Name: "group-1-name", PublicID: "group-one-resource-id-public", Resources: []nmdata.Resource{{ID: "host-id-1", Type: "host"}}, Peers: []string{"peer-id-1"}},
|
||||
)
|
||||
assert.NotNil(t, resourceToGroupIdx["host-id-1"]["group-one-resource-id"])
|
||||
assert.Contains(t,
|
||||
groups,
|
||||
nmdata.Group{ID: "group-two-resources-id", Name: "group-2-name", PublicID: "group-two-resources-id-public",
|
||||
Resources: []nmdata.Resource{{ID: "subnet-id-1", Type: "subnet"}, {ID: "host-id-2", Type: "host"}},
|
||||
Peers: []string{"peer-id-2", "peer-id-3"}},
|
||||
)
|
||||
assert.NotNil(t, resourceToGroupIdx["host-id-2"]["group-two-resources-id"])
|
||||
assert.NotNil(t, resourceToGroupIdx["subnet-id-1"]["group-two-resources-id"])
|
||||
assert.Contains(t,
|
||||
groups,
|
||||
nmdata.Group{ID: "group-no-resources-id", Name: "group-3-name", PublicID: "group-no-resources-id-public"})
|
||||
}
|
||||
|
||||
// Verify handling of empty fields in groups table
|
||||
// Verify that group's PublicID gets populated on retrieval
|
||||
// TODO (dmitri) PublicID should not be populated with delta updates,
|
||||
// which require stable PublicIDs
|
||||
func TestGetGroupsWithoutExpectedFields(t *testing.T) {
|
||||
if engine == string(types.SqliteStoreEngine) {
|
||||
t.Skip()
|
||||
}
|
||||
ctx := context.TODO()
|
||||
|
||||
execQuery(t, ctx,
|
||||
"insert into accounts (id) VALUES('random-id')")
|
||||
|
||||
execQuery(t, ctx,
|
||||
"insert into groups (id, account_id) VALUES('g2-test-group-id-1','random-id')")
|
||||
|
||||
groups, _, err := conn(t, ctx).GetGroups(ctx, "random-id")
|
||||
assert.NoError(t, err)
|
||||
require.Len(t, groups, 1)
|
||||
assert.NotEmpty(t, groups[0].PublicID)
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
//go:build integration
|
||||
|
||||
package networkmap_pgsql
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
|
||||
networkmap_pgsql "github.com/netbirdio/netbird/management/internals/network_map_db/pgsql"
|
||||
networkmap_sqlite "github.com/netbirdio/netbird/management/internals/network_map_db/sqlite"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
)
|
||||
|
||||
//go:embed base_data.sql
|
||||
var baseData string
|
||||
|
||||
//go:embed pg_data.sql
|
||||
var pgData string
|
||||
|
||||
//go:embed sqlite_data.sql
|
||||
var sqliteData string
|
||||
|
||||
var (
|
||||
pgstore *networkmap_pgsql.PgStore
|
||||
sqlitestore *networkmap_sqlite.SqliteStore
|
||||
engine string
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
var cleanup func()
|
||||
kind, _ := os.LookupEnv("NETBIRD_STORE_ENGINE")
|
||||
switch kind {
|
||||
case string(types.PostgresStoreEngine):
|
||||
engine = string(types.PostgresStoreEngine)
|
||||
pgstore, cleanup = createPGTestStore(baseData, pgData)
|
||||
case "", string(types.SqliteStoreEngine):
|
||||
engine = string(types.SqliteStoreEngine)
|
||||
sqlitestore, cleanup = createSqliteTestStore(baseData, sqliteData)
|
||||
default:
|
||||
log.Fatalf("unsupported db '%s' in NETBIRD_STORE_ENGINE env var", kind)
|
||||
}
|
||||
|
||||
code := m.Run()
|
||||
|
||||
cleanup()
|
||||
os.Exit(code)
|
||||
}
|
||||
|
||||
func conn(t *testing.T, ctx context.Context) networkmapdb.NetworkMapDBStoreConn {
|
||||
t.Helper()
|
||||
switch engine {
|
||||
case string(types.PostgresStoreEngine):
|
||||
c, err := pgstore.Pool.Acquire(ctx)
|
||||
assert.NoError(t, err)
|
||||
return pgstore.UsingConnection(c.Conn())
|
||||
case string(types.SqliteStoreEngine):
|
||||
return sqlitestore.UsingConn()
|
||||
}
|
||||
log.Fatalf("unknown db engine kind %s", engine)
|
||||
return nil
|
||||
}
|
||||
|
||||
func execQuery(t *testing.T, ctx context.Context, q string) {
|
||||
t.Helper()
|
||||
switch engine {
|
||||
case string(types.PostgresStoreEngine):
|
||||
_, err := pgstore.Pool.Exec(ctx, q)
|
||||
assert.NoError(t, err)
|
||||
case string(types.SqliteStoreEngine):
|
||||
_, err := sqlitestore.Db.ExecContext(ctx, q)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
// use to parse time in time.RFC3339Nano format
|
||||
// returns the time in the local time zone, as that's what being returned from sql queries
|
||||
// pgx returns time in the "local" timezone
|
||||
// sql with sqlite driver returns time in UTC timezone
|
||||
func mustParseTime(t string) *time.Time {
|
||||
tt, err := time.Parse(time.RFC3339Nano, t)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if engine == string(types.SqliteStoreEngine) {
|
||||
utc := tt.UTC()
|
||||
return &utc
|
||||
}
|
||||
|
||||
local := tt.Local()
|
||||
return &local
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
//go:build integration
|
||||
|
||||
package networkmap_pgsql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/netip"
|
||||
"testing"
|
||||
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetNameServerGroups(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
|
||||
execQuery(t, ctx,
|
||||
`insert into name_server_groups (id, public_id, name, description, name_servers, groups, domains, enabled, search_domains_enabled, "primary", account_id)
|
||||
VALUES('nsgroup-1','nsgroup-1-public','nsgroup-1','nsgroup-1','[{"IP":"192.168.31.2","NSType":1,"Port":53}]','["group-one-resource-id"]','["test-1.com"]',TRUE,FALSE,TRUE,'account-1')`)
|
||||
execQuery(t, ctx,
|
||||
`insert into name_server_groups (id, public_id, name, description, name_servers, groups, domains, enabled, search_domains_enabled,"primary",account_id)
|
||||
VALUES('nsgroup-2','nsgroup-2-public','nsgroup-2','nsgroup-2','[{"IP":"192.168.32.3","NSType":1,"Port":53}]','["group-one-resource-id","group-no-resources-id"]','["test-1.com","test-2.com"]',TRUE,FALSE,TRUE,'account-1')`)
|
||||
execQuery(t, ctx,
|
||||
`insert into name_server_groups (id, public_id, name, description, name_servers, groups, domains, enabled, search_domains_enabled,"primary",account_id)
|
||||
VALUES('nsgroup-3','nsgroup-3-public',null,null,null,null,null,TRUE,FALSE,FALSE,'account-1')`)
|
||||
|
||||
nsgroups, err := conn(t, ctx).GetNameServerGroups(ctx, "account-1")
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Contains(t, nsgroups, nmdata.NameServerGroup{
|
||||
ID: "nsgroup-1",
|
||||
PublicID: "nsgroup-1-public",
|
||||
Name: "nsgroup-1",
|
||||
Description: "nsgroup-1",
|
||||
NameServers: []nmdata.NameServer{{IP: netip.MustParseAddr("192.168.31.2"), NSType: 1, Port: 53}},
|
||||
Groups: []string{"group-one-resource-id"},
|
||||
Domains: []string{"test-1.com"},
|
||||
Primary: true,
|
||||
SearchDomainsEnabled: false,
|
||||
Enabled: true,
|
||||
})
|
||||
assert.Contains(t, nsgroups, nmdata.NameServerGroup{
|
||||
ID: "nsgroup-2",
|
||||
PublicID: "nsgroup-2-public",
|
||||
Name: "nsgroup-2",
|
||||
Description: "nsgroup-2",
|
||||
NameServers: []nmdata.NameServer{{IP: netip.MustParseAddr("192.168.32.3"), NSType: 1, Port: 53}},
|
||||
Groups: []string{"group-one-resource-id", "group-no-resources-id"},
|
||||
Domains: []string{"test-1.com", "test-2.com"},
|
||||
Primary: true,
|
||||
SearchDomainsEnabled: false,
|
||||
Enabled: true,
|
||||
})
|
||||
assert.Contains(t, nsgroups, nmdata.NameServerGroup{
|
||||
ID: "nsgroup-3",
|
||||
PublicID: "nsgroup-3-public",
|
||||
Primary: false,
|
||||
SearchDomainsEnabled: false,
|
||||
Enabled: true,
|
||||
})
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
//go:build integration
|
||||
|
||||
package networkmap_pgsql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/netip"
|
||||
"testing"
|
||||
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetNetworkResources(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
|
||||
execQuery(t, ctx,
|
||||
`insert into network_resources (id, account_id, network_id, public_id, name, description, type, domain, prefix, enabled)
|
||||
VALUES('net-resource-1','account-1','network-1','net-resource-public-1','network-resource-1','network-resource-1','subnet','','"10.0.0.0/16"',TRUE)`)
|
||||
execQuery(t, ctx,
|
||||
`insert into network_resources (id, account_id, network_id, public_id, name, description, type, domain, prefix, enabled)
|
||||
VALUES('net-resource-2','account-1','network-2','net-resource-public-2','network-resource-2','network-resource-2','domain','test.com','',TRUE)`)
|
||||
execQuery(t, ctx,
|
||||
`insert into network_resources (id, account_id, network_id, public_id, name, description, type, domain, prefix, enabled)
|
||||
VALUES('net-resource-3','account-1','network-3','net-resource-public-3','network-resource-3','network-resource-3','host','','"10.0.0.1/32"',TRUE)`)
|
||||
|
||||
resources, err := conn(t, ctx).GetNetworkResources(ctx, "account-1")
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Contains(t, resources, nmdata.NetworkResource{
|
||||
ID: "net-resource-1",
|
||||
AccountID: "account-1",
|
||||
NetworkID: "network-1",
|
||||
PublicID: "net-resource-public-1",
|
||||
Name: "network-resource-1",
|
||||
Description: "network-resource-1",
|
||||
Type: "subnet",
|
||||
Domain: "",
|
||||
Prefix: netip.MustParsePrefix("10.0.0.0/16"),
|
||||
Enabled: true,
|
||||
})
|
||||
assert.Contains(t, resources, nmdata.NetworkResource{
|
||||
ID: "net-resource-2",
|
||||
AccountID: "account-1",
|
||||
NetworkID: "network-2",
|
||||
PublicID: "net-resource-public-2",
|
||||
Name: "network-resource-2",
|
||||
Description: "network-resource-2",
|
||||
Type: "domain",
|
||||
Domain: "test.com",
|
||||
Enabled: true,
|
||||
})
|
||||
assert.Contains(t, resources, nmdata.NetworkResource{
|
||||
ID: "net-resource-3",
|
||||
AccountID: "account-1",
|
||||
NetworkID: "network-3",
|
||||
PublicID: "net-resource-public-3",
|
||||
Name: "network-resource-3",
|
||||
Description: "network-resource-3",
|
||||
Type: "host",
|
||||
Domain: "",
|
||||
Prefix: netip.MustParsePrefix("10.0.0.1/32"),
|
||||
Enabled: true,
|
||||
})
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
//go:build integration
|
||||
|
||||
package networkmap_pgsql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetNetworkRouters(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
|
||||
execQuery(t, ctx,
|
||||
`insert into network_routers (id, account_id, public_id, peer, network_id, masquerade, metric, enabled, peer_groups)
|
||||
VALUES('test-nr-id-1','account-1','public-id-1','peer-id-1','network-id-1',TRUE,999,TRUE,'["group-one-resource-id"]')`)
|
||||
execQuery(t, ctx,
|
||||
`insert into network_routers (id, account_id, public_id, peer, network_id, masquerade, metric, enabled, peer_groups)
|
||||
VALUES('test-nr-id-2','account-1','public-id-2','','network-id-2',TRUE,333,TRUE,'["group-two-resources-id","group-no-resources-id"]')`)
|
||||
|
||||
routers, err := conn(t, ctx).GetNetworkRouters(ctx, "account-1")
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, routers)
|
||||
|
||||
assert.Equal(t, routers["network-id-1"],
|
||||
map[string]*nmdata.NetworkRouter{"peer-id-1": {PublicID: "public-id-1", Masquerade: true, Metric: 999, Enabled: true, PeerGroups: []string{"group-one-resource-id"}}})
|
||||
assert.Equal(t, routers["network-id-2"],
|
||||
map[string]*nmdata.NetworkRouter{
|
||||
"peer-id-2": {PublicID: "public-id-2", Masquerade: true, Metric: 333, Enabled: true, PeerGroups: []string{"group-two-resources-id", "group-no-resources-id"}},
|
||||
"peer-id-3": {PublicID: "public-id-2", Masquerade: true, Metric: 333, Enabled: true, PeerGroups: []string{"group-two-resources-id", "group-no-resources-id"}}})
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
//go:build integration
|
||||
|
||||
package networkmap_pgsql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetNetwork(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
|
||||
network, err := conn(t, ctx).GetNetwork(ctx, "account-1")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, network, nmdata.Network{
|
||||
Identifier: "network-1",
|
||||
Net: mustParseCIDR("100.103.0.0/16"),
|
||||
NetV6: mustParseCIDR("fdde:e995:fd38:a465::/64"),
|
||||
Serial: 1,
|
||||
})
|
||||
|
||||
network, err = conn(t, ctx).GetNetwork(ctx, "account-2")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, network, nmdata.Network{
|
||||
Identifier: "network-2",
|
||||
Net: mustParseCIDR("110.0.0.0/16"),
|
||||
NetV6: mustParseCIDR("fddf:e995:fd38:a465::/64"),
|
||||
Serial: 2,
|
||||
})
|
||||
}
|
||||
|
||||
func mustParseCIDR(s string) net.IPNet {
|
||||
var toret net.IPNet
|
||||
|
||||
_, net, err := net.ParseCIDR(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
jn, err := json.Marshal(net)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = json.Unmarshal(jn, &toret)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return toret
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
//go:build integration
|
||||
|
||||
package networkmap_pgsql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetNetworks(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
|
||||
execQuery(t, ctx,
|
||||
`insert into networks (id, account_id, public_id) VALUES('network-1','account-1','network-1-public')`)
|
||||
execQuery(t, ctx,
|
||||
`insert into networks (id, account_id, public_id) VALUES('network-2','account-1','network-2-public')`)
|
||||
|
||||
networksIdx, err := conn(t, ctx).GetNetworkXIDToPublicIdMap(ctx, "account-1")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, networksIdx, map[string]string{
|
||||
"network-1": "network-1-public",
|
||||
"network-2": "network-2-public",
|
||||
})
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
//go:build integration
|
||||
|
||||
package networkmap_pgsql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/netip"
|
||||
"testing"
|
||||
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetPeers(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
|
||||
peers, clusterToPeersIdx, err := conn(t, ctx).GetPeers(ctx, "account-1")
|
||||
assert.NoError(t, err)
|
||||
|
||||
// shouldn't be returned in the index, as it's not connected
|
||||
execQuery(t, ctx,
|
||||
`insert into peers (id,account_id,"key",ssh_key,proxy_meta_embedded,peer_status_connected)
|
||||
values('peer-4','account-1','key-4','ssh-key-4',true,false)`)
|
||||
// shouldn't be returned in the index as it doesn't have cluster set
|
||||
execQuery(t, ctx,
|
||||
`insert into peers (id,account_id,"key",ssh_key,proxy_meta_embedded,peer_status_connected)
|
||||
values('peer-5','account-1','key-5','ssh-key-5',false,true)`)
|
||||
|
||||
peer1 := nmdata.Peer{
|
||||
ID: "peer-id-1",
|
||||
Key: "key-1",
|
||||
SSHKey: "ssh-key-1",
|
||||
DNSLabel: "peer-1",
|
||||
ExtraDNSLabels: []string{"extra-peer-1"},
|
||||
UserID: "user-id-1",
|
||||
SSHEnabled: true,
|
||||
LoginExpirationEnabled: true,
|
||||
LastLogin: mustParseTime("2026-08-06T13:25:59.12999+00:00"),
|
||||
IP: netip.MustParseAddr("10.10.10.1"),
|
||||
IPv6: netip.MustParseAddr("fdf4:ba80:6aa5:89f1:44d7:8701:8699:4940"),
|
||||
RequiresApproval: false,
|
||||
Meta: nmdata.PeerSystemMeta{
|
||||
WtVersion: "0.76.0",
|
||||
GoOS: "linux",
|
||||
OSVersion: "26.4.1",
|
||||
KernelVersion: "6.8.0-134-generic",
|
||||
NetworkAddresses: []nmdata.NetworkAddress{
|
||||
{NetIP: netip.MustParsePrefix("fe80::8b4c:973f:a76b:3771/64")},
|
||||
{NetIP: netip.MustParsePrefix("192.168.16.1/20")},
|
||||
},
|
||||
Files: []nmdata.File{
|
||||
{Path: "/usr/bin/netbird", ProcessIsRunning: false},
|
||||
},
|
||||
Capabilities: []int32{1, 2},
|
||||
Flags: nmdata.Flags{
|
||||
ServerSSHAllowed: true,
|
||||
DisableIPv6: false,
|
||||
},
|
||||
SyncMessageVersion: 1,
|
||||
},
|
||||
ProxyMeta: nmdata.ProxyMeta{
|
||||
Embedded: true,
|
||||
},
|
||||
Location: nmdata.PeerLocation{
|
||||
CountryCode: "DE",
|
||||
CityName: "Berlin",
|
||||
ConnectionIP: net.ParseIP("46.201.148.187"),
|
||||
},
|
||||
}
|
||||
peer2 := nmdata.Peer{
|
||||
ID: "peer-id-2",
|
||||
Key: "key-2",
|
||||
SSHKey: "ssh-key-2",
|
||||
DNSLabel: "peer-2",
|
||||
ExtraDNSLabels: []string{"extra-peer-2"},
|
||||
UserID: "user-id-2",
|
||||
SSHEnabled: true,
|
||||
LoginExpirationEnabled: true,
|
||||
LastLogin: mustParseTime("2026-08-06T14:25:59.12999+00:00"),
|
||||
IP: netip.MustParseAddr("10.10.100.1"),
|
||||
IPv6: netip.MustParseAddr("fdf5:ba80:6aa5:89f1:44d7:8701:8699:4940"),
|
||||
RequiresApproval: false,
|
||||
Meta: nmdata.PeerSystemMeta{
|
||||
WtVersion: "0.76.1",
|
||||
GoOS: "linux",
|
||||
OSVersion: "26.4.2",
|
||||
KernelVersion: "6.8.0-135-generic",
|
||||
NetworkAddresses: []nmdata.NetworkAddress{
|
||||
{NetIP: netip.MustParsePrefix("fe81::8b4c:973f:a76b:3771/64")},
|
||||
{NetIP: netip.MustParsePrefix("192.168.17.1/20")},
|
||||
},
|
||||
Files: []nmdata.File{
|
||||
{Path: "/usr/bin/netbird", ProcessIsRunning: false},
|
||||
},
|
||||
Capabilities: []int32{1, 2},
|
||||
Flags: nmdata.Flags{
|
||||
ServerSSHAllowed: true,
|
||||
DisableIPv6: false,
|
||||
},
|
||||
SyncMessageVersion: 0,
|
||||
},
|
||||
ProxyMeta: nmdata.ProxyMeta{
|
||||
Embedded: true,
|
||||
},
|
||||
Location: nmdata.PeerLocation{
|
||||
CountryCode: "DE",
|
||||
CityName: "Berlin",
|
||||
ConnectionIP: net.ParseIP("46.201.149.187"),
|
||||
},
|
||||
}
|
||||
peer3 := nmdata.Peer{
|
||||
ID: "peer-id-3",
|
||||
Key: "key-3",
|
||||
SSHKey: "ssh-key-3",
|
||||
DNSLabel: "peer-3",
|
||||
ExtraDNSLabels: []string{"extra-peer-3"},
|
||||
UserID: "user-id-3",
|
||||
SSHEnabled: true,
|
||||
LoginExpirationEnabled: true,
|
||||
LastLogin: mustParseTime("2026-08-06T12:25:59.12999+00:00"),
|
||||
IP: netip.MustParseAddr("10.10.200.1"),
|
||||
IPv6: netip.MustParseAddr("fdf6:ba80:6aa5:89f1:44d7:8701:8699:4940"),
|
||||
RequiresApproval: false,
|
||||
Meta: nmdata.PeerSystemMeta{
|
||||
WtVersion: "0.76.2",
|
||||
GoOS: "linux",
|
||||
OSVersion: "26.4.3",
|
||||
KernelVersion: "6.8.0-136-generic",
|
||||
NetworkAddresses: []nmdata.NetworkAddress{
|
||||
{NetIP: netip.MustParsePrefix("fe82::8b4c:973f:a76b:3771/64")},
|
||||
{NetIP: netip.MustParsePrefix("192.168.18.1/20")},
|
||||
},
|
||||
Files: []nmdata.File{
|
||||
{Path: "/usr/bin/netbird", ProcessIsRunning: false},
|
||||
},
|
||||
Capabilities: []int32{1, 2},
|
||||
Flags: nmdata.Flags{
|
||||
ServerSSHAllowed: true,
|
||||
DisableIPv6: false,
|
||||
},
|
||||
SyncMessageVersion: 1,
|
||||
},
|
||||
ProxyMeta: nmdata.ProxyMeta{
|
||||
Embedded: true,
|
||||
},
|
||||
Location: nmdata.PeerLocation{
|
||||
CountryCode: "DE",
|
||||
CityName: "Berlin",
|
||||
ConnectionIP: net.ParseIP("46.201.150.187"),
|
||||
},
|
||||
}
|
||||
|
||||
assert.Contains(t, peers, peer1)
|
||||
assert.Contains(t, peers, peer2)
|
||||
assert.Contains(t, peers, peer3)
|
||||
|
||||
assert.Equal(t, clusterToPeersIdx, map[string][]*nmdata.Peer{
|
||||
"cluster-1.netbird.services": {&peer1},
|
||||
"cluster-2.netbird.services": {&peer2},
|
||||
"cluster-3.netbird.services": {&peer3},
|
||||
})
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
insert into peers (id, account_id, "key", ssh_key, dns_label, extra_dns_labels, user_id, ssh_enabled, login_expiration_enabled, last_login, ip, ipv6,
|
||||
peer_status_requires_approval, peer_status_connected, proxy_meta_embedded, proxy_meta_cluster,
|
||||
meta_wt_version, meta_go_os, meta_os_version, meta_kernel_version, meta_network_addresses, meta_files,
|
||||
meta_capabilities, meta_flags, meta_sync_message_version,
|
||||
location_country_code, location_city_name, location_connection_ip)
|
||||
values('peer-id-1','account-1','key-1','ssh-key-1','peer-1','["extra-peer-1"]','user-id-1',true,true,'2026-08-06 13:25:59.12999+00','"10.10.10.1"','"fdf4:ba80:6aa5:89f1:44d7:8701:8699:4940"',
|
||||
false,true,true,'cluster-1.netbird.services',
|
||||
'0.76.0','linux','26.4.1','6.8.0-134-generic','[{"NetIP":"fe80::8b4c:973f:a76b:3771/64","Mac":"00:15:5d:24:0c:ac"},{"NetIP":"192.168.16.1/20","Mac":"00:15:5d:24:0c:ac"}]','[{"Path":"/usr/bin/netbird","Exist":false,"ProcessIsRunning":false}]',
|
||||
'[1,2]','{"RosenpassEnabled":false,"RosenpassPermissive":false,"ServerSSHAllowed":true,"DisableClientRoutes":false,"DisableServerRoutes":false,"DisableDNS":false,"DisableFirewall":false,"BlockLANAccess":false,"BlockInbound":false,"DisableIPv6":false,"LazyConnectionEnabled":false}',1,
|
||||
'DE','Berlin','"46.201.148.187"');
|
||||
insert into peers (id,account_id,"key", ssh_key, dns_label, extra_dns_labels, user_id, ssh_enabled, login_expiration_enabled, last_login, ip, ipv6,
|
||||
peer_status_requires_approval, peer_status_connected, proxy_meta_embedded, proxy_meta_cluster,
|
||||
meta_wt_version, meta_go_os, meta_os_version, meta_kernel_version, meta_network_addresses, meta_files,
|
||||
meta_capabilities, meta_flags, meta_sync_message_version,
|
||||
location_country_code, location_city_name, location_connection_ip)
|
||||
values('peer-id-2','account-1','key-2','ssh-key-2','peer-2','["extra-peer-2"]','user-id-2',true,true,'2026-08-06 14:25:59.12999+00','"10.10.100.1"','"fdf5:ba80:6aa5:89f1:44d7:8701:8699:4940"',
|
||||
false,true,true,'cluster-2.netbird.services',
|
||||
'0.76.1','linux','26.4.2','6.8.0-135-generic','[{"NetIP":"fe81::8b4c:973f:a76b:3771/64","Mac":"00:15:5d:24:0c:ad"},{"NetIP":"192.168.17.1/20","Mac":"00:15:5d:24:0c:ad"}]','[{"Path":"/usr/bin/netbird","Exist":false,"ProcessIsRunning":false}]',
|
||||
'[1,2]','{"RosenpassEnabled":false,"RosenpassPermissive":false,"ServerSSHAllowed":true,"DisableClientRoutes":false,"DisableServerRoutes":false,"DisableDNS":false,"DisableFirewall":false,"BlockLANAccess":false,"BlockInbound":false,"DisableIPv6":false,"LazyConnectionEnabled":false}',0,
|
||||
'DE','Berlin','"46.201.149.187"');
|
||||
insert into peers (id,account_id,"key", ssh_key, dns_label, extra_dns_labels, user_id, ssh_enabled, login_expiration_enabled, last_login, ip, ipv6,
|
||||
peer_status_requires_approval, peer_status_connected, proxy_meta_embedded, proxy_meta_cluster,
|
||||
meta_wt_version, meta_go_os, meta_os_version, meta_kernel_version, meta_network_addresses, meta_files,
|
||||
meta_capabilities, meta_flags, meta_sync_message_version,
|
||||
location_country_code, location_city_name, location_connection_ip)
|
||||
values('peer-id-3','account-1','key-3','ssh-key-3','peer-3','["extra-peer-3"]','user-id-3',true,true,'2026-08-06 12:25:59.12999+00','"10.10.200.1"','"fdf6:ba80:6aa5:89f1:44d7:8701:8699:4940"',
|
||||
false,true,true,'cluster-3.netbird.services',
|
||||
'0.76.2','linux','26.4.3','6.8.0-136-generic','[{"NetIP":"fe82::8b4c:973f:a76b:3771/64","Mac":"00:15:5d:24:0c:ae"},{"NetIP":"192.168.18.1/20","Mac":"00:15:5d:24:0c:ae"}]','[{"Path":"/usr/bin/netbird","Exist":false,"ProcessIsRunning":false}]',
|
||||
'[1,2]','{"RosenpassEnabled":false,"RosenpassPermissive":false,"ServerSSHAllowed":true,"DisableClientRoutes":false,"DisableServerRoutes":false,"DisableDNS":false,"DisableFirewall":false,"BlockLANAccess":false,"BlockInbound":false,"DisableIPv6":false,"LazyConnectionEnabled":false}',1,
|
||||
'DE','Berlin','"46.201.150.187"');
|
||||
@@ -1,122 +0,0 @@
|
||||
//go:build integration
|
||||
|
||||
package networkmap_pgsql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/google/uuid"
|
||||
networkmap_pgsql "github.com/netbirdio/netbird/management/internals/network_map_db/pgsql"
|
||||
gormstore "github.com/netbirdio/netbird/management/server/store"
|
||||
"github.com/netbirdio/netbird/management/server/testutil"
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func createPGTestStore(baseData, pgData string) (*networkmap_pgsql.PgStore, func()) {
|
||||
_, tmpdsn, err := testutil.CreatePostgresTestContainer()
|
||||
if err != nil {
|
||||
log.Fatalf("error starting postres container %v", err)
|
||||
}
|
||||
|
||||
var db *gorm.DB
|
||||
for i := range 5 {
|
||||
db, err = gorm.Open(postgres.Open(tmpdsn), &gorm.Config{})
|
||||
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
|
||||
if i < 5 {
|
||||
waitTime := time.Duration(100*(i+1)) * time.Millisecond
|
||||
time.Sleep(waitTime)
|
||||
continue
|
||||
}
|
||||
|
||||
log.Fatalf("error connecting to postres db %v", err)
|
||||
}
|
||||
|
||||
var cleanup func()
|
||||
dsn, cleanup, err := createRandomDB(tmpdsn, db)
|
||||
sqlDB, _ := db.DB()
|
||||
if sqlDB != nil {
|
||||
sqlDB.Close()
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatalf("error creating postres db %v", err)
|
||||
}
|
||||
|
||||
_, err = gormstore.NewPostgresqlStoreForTests(context.TODO(), dsn, nil, false)
|
||||
if err != nil {
|
||||
log.Fatalf("error running migrations %v", err)
|
||||
}
|
||||
|
||||
ctx := context.TODO()
|
||||
pgstore, err := networkmap_pgsql.NewPostgresqlStore(ctx, dsn)
|
||||
if err != nil {
|
||||
log.Fatal("error creating postgres store %w", err)
|
||||
}
|
||||
|
||||
for _, query := range slices.Concat(strings.Split(baseData, ";"), strings.Split(pgData, ";")) {
|
||||
if _, err := pgstore.Pool.Exec(ctx, query); err != nil {
|
||||
log.Fatalf("error initializing db: %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
return pgstore, cleanup
|
||||
}
|
||||
|
||||
func createRandomDB(dsn string, db *gorm.DB) (string, func(), error) {
|
||||
dbName := fmt.Sprintf("test_db_%s", strings.ReplaceAll(uuid.New().String(), "-", "_"))
|
||||
|
||||
if err := db.Exec(fmt.Sprintf("CREATE DATABASE %s", dbName)).Error; err != nil {
|
||||
return "", nil, fmt.Errorf("failed to create database: %v", err)
|
||||
}
|
||||
|
||||
originalDSN := dsn
|
||||
|
||||
cleanup := func() {
|
||||
var dropDB *gorm.DB
|
||||
var err error
|
||||
|
||||
dropDB, err = gorm.Open(postgres.Open(originalDSN), &gorm.Config{
|
||||
SkipDefaultTransaction: true,
|
||||
PrepareStmt: false,
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("failed to connect for dropping database %s: %v", dbName, err)
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if sqlDB, _ := dropDB.DB(); sqlDB != nil {
|
||||
sqlDB.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
if sqlDB, _ := dropDB.DB(); sqlDB != nil {
|
||||
sqlDB.SetMaxOpenConns(1)
|
||||
sqlDB.SetMaxIdleConns(0)
|
||||
sqlDB.SetConnMaxLifetime(time.Second)
|
||||
}
|
||||
|
||||
err = dropDB.Exec(fmt.Sprintf("DROP DATABASE IF EXISTS %s WITH (FORCE)", dbName)).Error
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("failed to drop database %s: %v", dbName, err)
|
||||
}
|
||||
}
|
||||
|
||||
return replaceDBName(dsn, dbName), cleanup, nil
|
||||
}
|
||||
|
||||
func replaceDBName(dsn, newDBName string) string {
|
||||
re := regexp.MustCompile(`(?P<pre>[:/@])(?P<dbname>[^/?]+)(?P<post>\?|$)`)
|
||||
return re.ReplaceAllString(dsn, `${pre}`+newDBName+`${post}`)
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
//go:build integration
|
||||
|
||||
package networkmap_pgsql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetPolicies(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
|
||||
execQuery(t, ctx,
|
||||
`insert into policies (id, public_id, account_id, enabled, source_posture_checks)
|
||||
values('policy-1','policy-1-public','account-1',true,'["posture-checks-1","posture-checks-2"]')`)
|
||||
execQuery(t, ctx,
|
||||
`insert into policy_rules (id, policy_id, enabled, action, protocol, bidirectional, sources, destinations,
|
||||
source_resource, destination_resource, ports, port_ranges,
|
||||
authorized_groups, authorized_user)
|
||||
values('policy-1-rule-1','policy-1',true,'accept','tcp',true,'["group-one-resource-id","group-two-resources-id"]','["group-one-resource-id","group-two-resources-id"]',
|
||||
'{"ID":"host-id-1","Type":"host"}','{"ID":"domain-1","Type":"domain"}','["8080","8443"]', '[{"Start":8080,"End":8090}]',
|
||||
'{"group-one-resource-id":["user-1", "user-2"]}','user-3')`)
|
||||
execQuery(t, ctx,
|
||||
`insert into policies (id, public_id, account_id, enabled, source_posture_checks)
|
||||
values('policy-2','policy-2-public','account-1',true,'["posture-checks-3","posture-checks-4"]')`)
|
||||
execQuery(t, ctx,
|
||||
`insert into policy_rules (id, policy_id, enabled, action, protocol, bidirectional, sources, destinations,
|
||||
source_resource, destination_resource, ports, port_ranges,
|
||||
authorized_groups, authorized_user)
|
||||
values('policy-2-rule-1','policy-2',true,'accept','tcp',true,'["group-one-resource-id"]','["group-two-resources-id"]',
|
||||
'{"ID":"host-id-3","Type":"host"}','{"ID":"domain-3","Type":"domain"}','["8080","8443"]', '[{"Start":8080,"End":8090}]',
|
||||
'{"group-one-resource-id":["user-6", "user-7"]}','user-8')`)
|
||||
// policy with a rule with null fields
|
||||
execQuery(t, ctx,
|
||||
`insert into policies (id, public_id, account_id, enabled, source_posture_checks)
|
||||
values('policy-3','policy-3-public','account-1',true,null)`)
|
||||
execQuery(t, ctx,
|
||||
`insert into policy_rules (id, policy_id, enabled, action, protocol, bidirectional, sources, destinations,
|
||||
source_resource, destination_resource, ports, port_ranges,
|
||||
authorized_groups, authorized_user)
|
||||
values('policy-3-rule-1','policy-3',true,null,null,null,null,null,null,null,null,null,null,null)`)
|
||||
// policy with a disabled rule, destination resource and groups should not be in indexes
|
||||
execQuery(t, ctx,
|
||||
`insert into policies (id, public_id, account_id, enabled, source_posture_checks)
|
||||
values('policy-4','policy-4-public','account-1',true,null)`)
|
||||
execQuery(t, ctx,
|
||||
`insert into policy_rules (id, policy_id, enabled, action, protocol, bidirectional, sources, destinations,
|
||||
source_resource, destination_resource, ports, port_ranges,
|
||||
authorized_groups, authorized_user)
|
||||
values('policy-4-rule-1','policy-4',false,null,null,null,null,'["group-two-resources-id"]',
|
||||
null,'{"ID":"domain-3","Type":"domain"}',null,null,null,null)`)
|
||||
|
||||
policies, policyToDestinationResourceIdx, policyToDestinationGroupIdx, err := conn(t, ctx).GetPolicies(ctx, "account-1")
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Contains(t, policies, nmdata.Policy{
|
||||
ID: "policy-1",
|
||||
PublicID: "policy-1-public",
|
||||
Enabled: true,
|
||||
SourcePostureChecks: []string{"posture-checks-1", "posture-checks-2"},
|
||||
Rules: []*nmdata.PolicyRule{
|
||||
{
|
||||
ID: "policy-1",
|
||||
PolicyID: "policy-1",
|
||||
Enabled: true,
|
||||
Action: "accept",
|
||||
Protocol: "tcp",
|
||||
Bidirectional: true,
|
||||
Sources: []string{"group-one-resource-id", "group-two-resources-id"},
|
||||
Destinations: []string{"group-one-resource-id", "group-two-resources-id"},
|
||||
SourceResource: nmdata.Resource{ID: "host-id-1", Type: "host"},
|
||||
DestinationResource: nmdata.Resource{ID: "domain-1", Type: "domain"},
|
||||
Ports: []string{"8080", "8443"},
|
||||
PortRanges: []nmdata.RulePortRange{{Start: 8080, End: 8090}},
|
||||
AuthorizedGroups: map[string][]string{"group-one-resource-id": {"user-1", "user-2"}},
|
||||
AuthorizedUser: "user-3",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
assert.Contains(t, policies, nmdata.Policy{
|
||||
ID: "policy-2",
|
||||
PublicID: "policy-2-public",
|
||||
Enabled: true,
|
||||
SourcePostureChecks: []string{"posture-checks-3", "posture-checks-4"},
|
||||
Rules: []*nmdata.PolicyRule{
|
||||
{
|
||||
ID: "policy-2",
|
||||
PolicyID: "policy-2",
|
||||
Enabled: true,
|
||||
Action: "accept",
|
||||
Protocol: "tcp",
|
||||
Bidirectional: true,
|
||||
Sources: []string{"group-one-resource-id"},
|
||||
Destinations: []string{"group-two-resources-id"},
|
||||
SourceResource: nmdata.Resource{ID: "host-id-3", Type: "host"},
|
||||
DestinationResource: nmdata.Resource{ID: "domain-3", Type: "domain"},
|
||||
Ports: []string{"8080", "8443"},
|
||||
PortRanges: []nmdata.RulePortRange{{Start: 8080, End: 8090}},
|
||||
AuthorizedGroups: map[string][]string{"group-one-resource-id": {"user-6", "user-7"}},
|
||||
AuthorizedUser: "user-8",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
assert.Contains(t, policies, nmdata.Policy{
|
||||
ID: "policy-3",
|
||||
PublicID: "policy-3-public",
|
||||
Enabled: true,
|
||||
SourcePostureChecks: nil,
|
||||
Rules: []*nmdata.PolicyRule{
|
||||
{
|
||||
ID: "policy-3",
|
||||
PolicyID: "policy-3",
|
||||
Enabled: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
assert.Contains(t, policies, nmdata.Policy{
|
||||
ID: "policy-4",
|
||||
PublicID: "policy-4-public",
|
||||
Enabled: true,
|
||||
SourcePostureChecks: nil,
|
||||
Rules: []*nmdata.PolicyRule{
|
||||
{
|
||||
ID: "policy-4",
|
||||
PolicyID: "policy-4",
|
||||
Enabled: false,
|
||||
Destinations: []string{"group-two-resources-id"},
|
||||
DestinationResource: nmdata.Resource{ID: "domain-3", Type: "domain"},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
assert.Equal(t, policyToDestinationGroupIdx, map[string]map[string]any{
|
||||
"policy-1": {"group-one-resource-id": struct{}{}, "group-two-resources-id": struct{}{}},
|
||||
"policy-2": {"group-two-resources-id": struct{}{}},
|
||||
})
|
||||
assert.Equal(t, policyToDestinationResourceIdx, map[string]map[string]any{
|
||||
"policy-1": {"domain-1": struct{}{}},
|
||||
"policy-2": {"domain-3": struct{}{}},
|
||||
})
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
//go:build integration
|
||||
|
||||
package networkmap_pgsql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/netip"
|
||||
"testing"
|
||||
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetPostureChecks(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
|
||||
execQuery(t, ctx,
|
||||
`insert into posture_checks (id, account_id, public_id, checks)
|
||||
VALUES('posturecheck-1','account-1','posturecheck-1-public',
|
||||
'{"NBVersionCheck":{"MinVersion":"0.25.0"},
|
||||
"OSVersionCheck":{"Darwin":{"MinVersion":"12.0"}},
|
||||
"GeoLocationCheck":{"Locations":[{"CountryCode":"FI","CityName":""}],"Action":"allow"},
|
||||
"PeerNetworkRangeCheck":{"Action":"deny","Ranges":["192.168.0.1/24"]}}')`)
|
||||
|
||||
execQuery(t, ctx,
|
||||
`insert into posture_checks (id, account_id, public_id, checks)
|
||||
VALUES('posturecheck-2','account-1','posturecheck-2-public',
|
||||
'{"NBVersionCheck":{"MinVersion":"0.25.0"},
|
||||
"OSVersionCheck":{"Android":{"MinVersion":"0"}},
|
||||
"GeoLocationCheck":{"Locations":[{"CountryCode":"US","CityName":"Harker Heights"}],"Action":"allow"},
|
||||
"PeerNetworkRangeCheck":{"Action":"allow","Ranges":["0.0.0.0/0"]}}')`)
|
||||
execQuery(t, ctx,
|
||||
`insert into posture_checks (id, account_id, public_id, checks)
|
||||
VALUES('posturecheck-3','account-1','posturecheck-3-public', null)`)
|
||||
|
||||
postureChecks, idToPublicIDIdx, err := conn(t, ctx).GetPostureChecks(ctx, "account-1")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, idToPublicIDIdx, map[string]string{
|
||||
"posturecheck-1": "posturecheck-1-public",
|
||||
"posturecheck-2": "posturecheck-2-public",
|
||||
"posturecheck-3": "posturecheck-3-public",
|
||||
})
|
||||
assert.Contains(t, postureChecks, nmdata.PostureChecks{
|
||||
ID: "posturecheck-1",
|
||||
Checks: nmdata.ChecksDefinition{
|
||||
NBVersionCheck: &nmdata.NBVersionCheck{MinVersion: "0.25.0"},
|
||||
OSVersionCheck: &nmdata.OSVersionCheck{Darwin: &nmdata.MinVersionCheck{MinVersion: "12.0"}},
|
||||
GeoLocationCheck: &nmdata.GeoLocationCheck{Locations: []nmdata.GeoLocation{{CountryCode: "FI"}}, Action: "allow"},
|
||||
PeerNetworkRangeCheck: &nmdata.PeerNetworkRangeCheck{Action: "deny", Ranges: []netip.Prefix{netip.MustParsePrefix("192.168.0.1/24")}},
|
||||
}})
|
||||
assert.Contains(t, postureChecks, nmdata.PostureChecks{
|
||||
ID: "posturecheck-2",
|
||||
Checks: nmdata.ChecksDefinition{
|
||||
NBVersionCheck: &nmdata.NBVersionCheck{MinVersion: "0.25.0"},
|
||||
OSVersionCheck: &nmdata.OSVersionCheck{Android: &nmdata.MinVersionCheck{MinVersion: "0"}},
|
||||
GeoLocationCheck: &nmdata.GeoLocationCheck{Locations: []nmdata.GeoLocation{{CountryCode: "US", CityName: "Harker Heights"}}, Action: "allow"},
|
||||
PeerNetworkRangeCheck: &nmdata.PeerNetworkRangeCheck{Action: "allow", Ranges: []netip.Prefix{netip.MustParsePrefix("0.0.0.0/0")}},
|
||||
}})
|
||||
assert.Contains(t, postureChecks, nmdata.PostureChecks{
|
||||
ID: "posturecheck-3"})
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
//go:build integration
|
||||
|
||||
package networkmap_pgsql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/netip"
|
||||
"testing"
|
||||
|
||||
"github.com/netbirdio/netbird/shared/management/domain"
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetRoutes(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
|
||||
execQuery(t, ctx,
|
||||
`insert into routes (id, account_id, public_id, network, domains, keep_route, net_id, description,
|
||||
peer, peer_groups, network_type, masquerade, metric, enabled,
|
||||
groups, access_control_groups, skip_auto_apply)
|
||||
VALUES('route-1','account-1','route-1-public','"172.0.0.0/16"','["test-1.com"]',true,'route-1-net-id','route-1',
|
||||
'peer-id-1','["group-one-resource-id"]',1,true,9999,true,
|
||||
'["group-one-resource-id"]','["group-one-resource-id"]',false)`)
|
||||
execQuery(t, ctx,
|
||||
`insert into routes (id, account_id, public_id, network, domains, keep_route, net_id, description,
|
||||
peer, peer_groups, network_type, masquerade, metric, enabled,
|
||||
groups, access_control_groups, skip_auto_apply)
|
||||
VALUES('route-2','account-1','route-2-public','"172.10.0.0/16"','["test-1.com","test-2.com"]',true,'route-2-net-id','route-2',
|
||||
'peer-id-2','["group-two-resources-id"]',1,true,9999,true,
|
||||
'["group-two-resources-id"]','["group-two-resources-id"]',false)`)
|
||||
execQuery(t, ctx,
|
||||
`insert into routes (id, account_id, public_id, network, domains, keep_route, net_id, description,
|
||||
peer, peer_groups, network_type, masquerade, metric, enabled,
|
||||
groups, access_control_groups, skip_auto_apply)
|
||||
VALUES('route-3','account-1','route-3-public',null,null,null,null,'route-3',
|
||||
null,null,null,null,null,null,null,null,null)`)
|
||||
|
||||
routes, err := conn(t, ctx).GetRoutes(ctx, "account-1")
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, routes, nmdata.Route{
|
||||
ID: "route-1",
|
||||
AccountID: "account-1",
|
||||
PublicID: "route-1-public",
|
||||
Network: netip.MustParsePrefix("172.0.0.0/16"),
|
||||
Domains: domain.List{"test-1.com"},
|
||||
KeepRoute: true,
|
||||
NetID: "route-1-net-id",
|
||||
Description: "route-1",
|
||||
Peer: "peer-id-1",
|
||||
PeerID: "peer-id-1",
|
||||
PeerGroups: []string{"group-one-resource-id"},
|
||||
NetworkType: 1,
|
||||
Masquerade: true,
|
||||
Metric: 9999,
|
||||
Enabled: true,
|
||||
Groups: []string{"group-one-resource-id"},
|
||||
AccessControlGroups: []string{"group-one-resource-id"},
|
||||
SkipAutoApply: false,
|
||||
})
|
||||
assert.Contains(t, routes, nmdata.Route{
|
||||
ID: "route-2",
|
||||
AccountID: "account-1",
|
||||
PublicID: "route-2-public",
|
||||
Network: netip.MustParsePrefix("172.10.0.0/16"),
|
||||
Domains: domain.List{"test-1.com", "test-2.com"},
|
||||
KeepRoute: true,
|
||||
NetID: "route-2-net-id",
|
||||
Description: "route-2",
|
||||
Peer: "peer-id-2",
|
||||
PeerID: "peer-id-2",
|
||||
PeerGroups: []string{"group-two-resources-id"},
|
||||
NetworkType: 1,
|
||||
Masquerade: true,
|
||||
Metric: 9999,
|
||||
Enabled: true,
|
||||
Groups: []string{"group-two-resources-id"},
|
||||
AccessControlGroups: []string{"group-two-resources-id"},
|
||||
SkipAutoApply: false,
|
||||
})
|
||||
assert.Contains(t, routes, nmdata.Route{
|
||||
ID: "route-3",
|
||||
AccountID: "account-1",
|
||||
PublicID: "route-3-public",
|
||||
Description: "route-3",
|
||||
})
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
//go:build integration
|
||||
|
||||
package networkmap_pgsql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
|
||||
)
|
||||
|
||||
func TestGetPrivateServices(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
|
||||
execQuery(t, ctx,
|
||||
`insert into services (id, account_id, enabled, private, access_groups, proxy_cluster, domain)
|
||||
values('service-1','account-1',true,true,'["group-one-resource-id"]','test-1.com','test-2.com')`)
|
||||
execQuery(t, ctx,
|
||||
`insert into services (id, account_id, enabled, private, access_groups, proxy_cluster, domain)
|
||||
values('service-2','account-1',true,true,'["group-one-resource-id","group-two-resources-id"]','test-3.com','test-4.com')`)
|
||||
execQuery(t, ctx,
|
||||
`insert into services (id, account_id, enabled, private, access_groups, proxy_cluster, domain)
|
||||
values('service-3','account-1',null,null,null,null,null)`)
|
||||
|
||||
services, err := conn(t, ctx).GetPrivateServices(ctx, "account-1")
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, services, networkmapdb.Service{
|
||||
Enabled: sql.NullBool{Bool: true, Valid: true},
|
||||
Private: sql.NullBool{Bool: true, Valid: true},
|
||||
AccessGroups: []string{"group-one-resource-id"},
|
||||
ProxyCluster: sql.NullString{String: "test-1.com", Valid: true},
|
||||
Domain: sql.NullString{String: "test-2.com", Valid: true},
|
||||
})
|
||||
assert.Contains(t, services, networkmapdb.Service{
|
||||
Enabled: sql.NullBool{Bool: true, Valid: true},
|
||||
Private: sql.NullBool{Bool: true, Valid: true},
|
||||
AccessGroups: []string{"group-one-resource-id", "group-two-resources-id"},
|
||||
ProxyCluster: sql.NullString{String: "test-3.com", Valid: true},
|
||||
Domain: sql.NullString{String: "test-4.com", Valid: true},
|
||||
})
|
||||
assert.Contains(t, services, networkmapdb.Service{
|
||||
Enabled: sql.NullBool{Bool: false, Valid: false},
|
||||
Private: sql.NullBool{Bool: false, Valid: false},
|
||||
AccessGroups: []string{},
|
||||
ProxyCluster: sql.NullString{String: "", Valid: false},
|
||||
Domain: sql.NullString{String: "", Valid: false},
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetProxyTargetedDomainResourceIDs(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
|
||||
execQuery(t, ctx,
|
||||
`insert into services (id, account_id, enabled, terminated)
|
||||
values('service-4','account-1',true,false)`)
|
||||
execQuery(t, ctx,
|
||||
`insert into targets (target_id, account_id, service_id, enabled, target_type)
|
||||
values('target-1','account-1','service-4',true,'domain')`)
|
||||
// id shouldn't be returned as the taget_type is not "domain"
|
||||
execQuery(t, ctx,
|
||||
`insert into targets (target_id, account_id, service_id, enabled, target_type)
|
||||
values('target-2','account-1','service-4',true,'cluster')`)
|
||||
// id shouldn't be included as the target is disabled
|
||||
execQuery(t, ctx,
|
||||
`insert into targets (target_id, account_id, service_id, enabled, target_type)
|
||||
values('target-3','account-1','service-4',false,'domain')`)
|
||||
// id shouldn't be included as the service is disabled
|
||||
execQuery(t, ctx,
|
||||
`insert into services (id, account_id, enabled, terminated)
|
||||
values('service-5','account-1',false,false)`)
|
||||
execQuery(t, ctx,
|
||||
`insert into targets (target_id, account_id, service_id, enabled, target_type)
|
||||
values('target-4','account-1','service-5',false,'domain')`)
|
||||
// id shouldn't be included as the service is terminated (explicitly)
|
||||
execQuery(t, ctx,
|
||||
`insert into services (id, account_id, enabled, terminated)
|
||||
values('service-6','account-1',true,true)`)
|
||||
execQuery(t, ctx,
|
||||
`insert into targets (target_id, account_id, service_id, enabled, target_type)
|
||||
values('target-5','account-1','service-6',true,'domain')`)
|
||||
// id shouldn't be included as the service is terminated (implicitly)
|
||||
execQuery(t, ctx,
|
||||
`insert into services (id, account_id, enabled, terminated)
|
||||
values('service-7','account-1',true,null)`)
|
||||
execQuery(t, ctx,
|
||||
`insert into targets (target_id, account_id, service_id, enabled, target_type)
|
||||
values('target-6','account-1','service-7',true,'domain')`)
|
||||
execQuery(t, ctx,
|
||||
`insert into services (id, account_id, enabled, terminated)
|
||||
values('service-8','account-1',true,false)`)
|
||||
execQuery(t, ctx,
|
||||
`insert into targets (target_id, account_id, service_id, enabled, target_type)
|
||||
values('target-7','account-1','service-8',true,'domain')`)
|
||||
// id shouldn't be returned as the taget_id is null
|
||||
execQuery(t, ctx,
|
||||
`insert into targets (target_id, account_id, service_id, enabled, target_type)
|
||||
values(null,'account-1','service-4',true,'cluster')`)
|
||||
|
||||
servtargetedDomains, err := conn(t, ctx).GetProxyTargetedDomainResourceIDs(ctx, "account-1")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, servtargetedDomains, map[string]struct{}{
|
||||
"target-1": {},
|
||||
"target-6": {},
|
||||
"target-7": {},
|
||||
})
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
insert into peers (id, account_id, "key", ssh_key, dns_label, extra_dns_labels, user_id, ssh_enabled, login_expiration_enabled, last_login, ip, ipv6,
|
||||
peer_status_requires_approval, peer_status_connected, proxy_meta_embedded, proxy_meta_cluster,
|
||||
meta_wt_version, meta_go_os, meta_os_version, meta_kernel_version, meta_network_addresses, meta_files,
|
||||
meta_capabilities, meta_flags, meta_sync_message_version,
|
||||
location_country_code, location_city_name, location_connection_ip)
|
||||
values('peer-id-1','account-1','key-1','ssh-key-1','peer-1','["extra-peer-1"]','user-id-1',true,true,'2026-08-06 13:25:59.12999','"10.10.10.1"','"fdf4:ba80:6aa5:89f1:44d7:8701:8699:4940"',
|
||||
false,true,true,'cluster-1.netbird.services',
|
||||
'0.76.0','linux','26.4.1','6.8.0-134-generic','[{"NetIP":"fe80::8b4c:973f:a76b:3771/64","Mac":"00:15:5d:24:0c:ac"},{"NetIP":"192.168.16.1/20","Mac":"00:15:5d:24:0c:ac"}]','[{"Path":"/usr/bin/netbird","Exist":false,"ProcessIsRunning":false}]',
|
||||
'[1,2]','{"RosenpassEnabled":false,"RosenpassPermissive":false,"ServerSSHAllowed":true,"DisableClientRoutes":false,"DisableServerRoutes":false,"DisableDNS":false,"DisableFirewall":false,"BlockLANAccess":false,"BlockInbound":false,"DisableIPv6":false,"LazyConnectionEnabled":false}',1,
|
||||
'DE','Berlin','"46.201.148.187"');
|
||||
insert into peers (id,account_id,"key", ssh_key, dns_label, extra_dns_labels, user_id, ssh_enabled, login_expiration_enabled, last_login, ip, ipv6,
|
||||
peer_status_requires_approval, peer_status_connected, proxy_meta_embedded, proxy_meta_cluster,
|
||||
meta_wt_version, meta_go_os, meta_os_version, meta_kernel_version, meta_network_addresses, meta_files,
|
||||
meta_capabilities, meta_flags, meta_sync_message_version,
|
||||
location_country_code, location_city_name, location_connection_ip)
|
||||
values('peer-id-2','account-1','key-2','ssh-key-2','peer-2','["extra-peer-2"]','user-id-2',true,true,'2026-08-06 14:25:59.12999','"10.10.100.1"','"fdf5:ba80:6aa5:89f1:44d7:8701:8699:4940"',
|
||||
false,true,true,'cluster-2.netbird.services',
|
||||
'0.76.1','linux','26.4.2','6.8.0-135-generic','[{"NetIP":"fe81::8b4c:973f:a76b:3771/64","Mac":"00:15:5d:24:0c:ad"},{"NetIP":"192.168.17.1/20","Mac":"00:15:5d:24:0c:ad"}]','[{"Path":"/usr/bin/netbird","Exist":false,"ProcessIsRunning":false}]',
|
||||
'[1,2]','{"RosenpassEnabled":false,"RosenpassPermissive":false,"ServerSSHAllowed":true,"DisableClientRoutes":false,"DisableServerRoutes":false,"DisableDNS":false,"DisableFirewall":false,"BlockLANAccess":false,"BlockInbound":false,"DisableIPv6":false,"LazyConnectionEnabled":false}',0,
|
||||
'DE','Berlin','"46.201.149.187"');
|
||||
insert into peers (id,account_id,"key", ssh_key, dns_label, extra_dns_labels, user_id, ssh_enabled, login_expiration_enabled, last_login, ip, ipv6,
|
||||
peer_status_requires_approval, peer_status_connected, proxy_meta_embedded, proxy_meta_cluster,
|
||||
meta_wt_version, meta_go_os, meta_os_version, meta_kernel_version, meta_network_addresses, meta_files,
|
||||
meta_capabilities, meta_flags, meta_sync_message_version,
|
||||
location_country_code, location_city_name, location_connection_ip)
|
||||
values('peer-id-3','account-1','key-3','ssh-key-3','peer-3','["extra-peer-3"]','user-id-3',true,true,'2026-08-06 12:25:59.12999','"10.10.200.1"','"fdf6:ba80:6aa5:89f1:44d7:8701:8699:4940"',
|
||||
false,true,true,'cluster-3.netbird.services',
|
||||
'0.76.2','linux','26.4.3','6.8.0-136-generic','[{"NetIP":"fe82::8b4c:973f:a76b:3771/64","Mac":"00:15:5d:24:0c:ae"},{"NetIP":"192.168.18.1/20","Mac":"00:15:5d:24:0c:ae"}]','[{"Path":"/usr/bin/netbird","Exist":false,"ProcessIsRunning":false}]',
|
||||
'[1,2]','{"RosenpassEnabled":false,"RosenpassPermissive":false,"ServerSSHAllowed":true,"DisableClientRoutes":false,"DisableServerRoutes":false,"DisableDNS":false,"DisableFirewall":false,"BlockLANAccess":false,"BlockInbound":false,"DisableIPv6":false,"LazyConnectionEnabled":false}',1,
|
||||
'DE','Berlin','"46.201.150.187"');
|
||||
@@ -1,49 +0,0 @@
|
||||
//go:build integration
|
||||
|
||||
package networkmap_pgsql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
networkmap_sqlite "github.com/netbirdio/netbird/management/internals/network_map_db/sqlite"
|
||||
gormstore "github.com/netbirdio/netbird/management/server/store"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func createSqliteTestStore(baseData, sqliteData string) (*networkmap_sqlite.SqliteStore, func()) {
|
||||
storeSqliteFileName := ":memory:"
|
||||
storeStr := fmt.Sprintf("%s?cache=shared", storeSqliteFileName)
|
||||
if runtime.GOOS == "windows" {
|
||||
// Vo avoid `The process cannot access the file because it is being used by another process` on Windows
|
||||
storeStr = storeSqliteFileName
|
||||
}
|
||||
|
||||
db, err := gorm.Open(sqlite.Open(storeStr), &gorm.Config{})
|
||||
if err != nil {
|
||||
log.Fatalf("error initializing db: %s", err.Error())
|
||||
}
|
||||
_, err = gormstore.NewSqlStore(context.TODO(), db, types.SqliteStoreEngine, nil, false)
|
||||
if err != nil {
|
||||
log.Fatalf("error initializing db: %s", err.Error())
|
||||
}
|
||||
|
||||
sqldb, err := db.DB()
|
||||
if err != nil {
|
||||
log.Fatalf("error initializing db: %s", err.Error())
|
||||
|
||||
}
|
||||
for _, query := range slices.Concat(strings.Split(baseData, ";"), strings.Split(sqliteData, ";")) {
|
||||
if _, err := sqldb.Exec(query); err != nil {
|
||||
log.Fatalf("error initializing db: %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
return &networkmap_sqlite.SqliteStore{Db: sqldb}, func() {}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
//go:build integration
|
||||
|
||||
package networkmap_pgsql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetAllowedUsers(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
|
||||
execQuery(t, ctx,
|
||||
`insert into users (id, name, account_id, auto_groups, blocked, is_service_user)
|
||||
VALUES('user-1','user-1','account-1','["group-one-resource-id"]',false,false)`)
|
||||
execQuery(t, ctx,
|
||||
`insert into users (id, name, account_id, auto_groups, blocked, is_service_user)
|
||||
VALUES('user-2','user-2','account-1','["group-one-resource-id","group-two-resources-id"]',false,false)`)
|
||||
execQuery(t, ctx,
|
||||
`insert into users (id, name, account_id, auto_groups, blocked, is_service_user)
|
||||
VALUES('user-3','user-3','account-1','["group-two-resources-id"]',false,false)`)
|
||||
// shouldn't be included as it's blocked
|
||||
execQuery(t, ctx,
|
||||
`insert into users (id, name, account_id, auto_groups, blocked, is_service_user)
|
||||
VALUES('user-4','user-4','account-1','["group-two-resources-id"]',true,false)`)
|
||||
// shouldn't be included as it's a service_user
|
||||
execQuery(t, ctx,
|
||||
`insert into users (id, name, account_id, auto_groups, blocked, is_service_user)
|
||||
VALUES('user-5','user-5','account-1','["group-two-resources-id"]',false,true)`)
|
||||
execQuery(t, ctx,
|
||||
`insert into groups (id, name, account_id)
|
||||
VALUES('all-group-1','All','account-1')`)
|
||||
execQuery(t, ctx,
|
||||
`insert into groups (id, name, account_id)
|
||||
VALUES('all-group-2','All','account-1')`)
|
||||
execQuery(t, ctx,
|
||||
`insert into groups (id, name, account_id)
|
||||
VALUES('all-group-3','All','account-1')`)
|
||||
|
||||
userIdx, groupIdToUserIds, err := conn(t, ctx).GetAllowedUsers(ctx, "account-1")
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, userIdx, map[string]struct{}{
|
||||
"user-1": {},
|
||||
"user-2": {},
|
||||
"user-3": {},
|
||||
})
|
||||
assert.Equal(t, groupIdToUserIds, map[string][]string{
|
||||
"group-one-resource-id": {"user-1", "user-2"},
|
||||
"group-two-resources-id": {"user-2", "user-3"},
|
||||
"all-group-1": {"user-1", "user-2", "user-3"},
|
||||
"all-group-2": {"user-1", "user-2", "user-3"},
|
||||
"all-group-3": {"user-1", "user-2", "user-3"},
|
||||
})
|
||||
}
|
||||
@@ -18,7 +18,6 @@ import (
|
||||
"github.com/netbirdio/netbird/management/internals/controllers/network_map"
|
||||
"github.com/netbirdio/netbird/management/internals/controllers/network_map/controller/cache"
|
||||
"github.com/netbirdio/netbird/management/internals/modules/peers/ephemeral"
|
||||
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
|
||||
"github.com/netbirdio/netbird/management/internals/server/config"
|
||||
"github.com/netbirdio/netbird/management/internals/shared/grpc"
|
||||
"github.com/netbirdio/netbird/management/server/account"
|
||||
@@ -31,8 +30,6 @@ import (
|
||||
"github.com/netbirdio/netbird/management/server/telemetry"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
sharedgrpc "github.com/netbirdio/netbird/shared/management/grpc"
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap"
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
|
||||
"github.com/netbirdio/netbird/shared/management/proto"
|
||||
"github.com/netbirdio/netbird/shared/management/status"
|
||||
"github.com/netbirdio/netbird/util"
|
||||
@@ -64,8 +61,6 @@ type Controller struct {
|
||||
serverSupportedSyncMessageVersion sharedgrpc.SyncMessageVersion
|
||||
|
||||
perAccountServerSupportedSyncMessageVersions map[string]sharedgrpc.SyncMessageVersion
|
||||
|
||||
nmdataStore *networkmapdb.NetworkMapDBStoreImpl
|
||||
}
|
||||
|
||||
type bufferUpdate struct {
|
||||
@@ -83,7 +78,7 @@ type bufferAffectedUpdate struct {
|
||||
|
||||
var _ network_map.Controller = (*Controller)(nil)
|
||||
|
||||
func NewController(ctx context.Context, store store.Store, metrics telemetry.AppMetrics, peersUpdateManager network_map.PeersUpdateManager, requestBuffer account.RequestBuffer, integratedPeerValidator integrated_validator.IntegratedValidator, settingsManager settings.Manager, dnsDomain string, proxyController port_forwarding.Controller, ephemeralPeersManager ephemeral.Manager, config *config.Config, nmdataStore *networkmapdb.NetworkMapDBStoreImpl) *Controller {
|
||||
func NewController(ctx context.Context, store store.Store, metrics telemetry.AppMetrics, peersUpdateManager network_map.PeersUpdateManager, requestBuffer account.RequestBuffer, integratedPeerValidator integrated_validator.IntegratedValidator, settingsManager settings.Manager, dnsDomain string, proxyController port_forwarding.Controller, ephemeralPeersManager ephemeral.Manager, config *config.Config) *Controller {
|
||||
nMetrics, err := newMetrics(metrics.UpdateChannelMetrics())
|
||||
if err != nil {
|
||||
log.Fatal(fmt.Errorf("error creating metrics: %w", err))
|
||||
@@ -104,7 +99,6 @@ func NewController(ctx context.Context, store store.Store, metrics telemetry.App
|
||||
EphemeralPeersManager: ephemeralPeersManager,
|
||||
serverSupportedSyncMessageVersion: sharedgrpc.SyncMessageVersionFromConfig(config.HighestSupportedSyncMessageVersion),
|
||||
perAccountServerSupportedSyncMessageVersions: sharedgrpc.SyncMessageVersionsFromMap(config.PerAccountHighestSupportedSyncMessageVersion),
|
||||
nmdataStore: nmdataStore,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,11 +147,6 @@ func (c *Controller) CountStreams() int {
|
||||
|
||||
func (c *Controller) sendUpdateAccountPeers(ctx context.Context, accountID string, reason types.UpdateReason) error {
|
||||
log.WithContext(ctx).Tracef("updating peers for account %s from %s", accountID, util.GetCallerName())
|
||||
|
||||
if nmData := c.getNetworkMapData(ctx, accountID); nmData != nil {
|
||||
return c.sendUpdateAccountPeersFromData(ctx, accountID, reason, nmData)
|
||||
}
|
||||
|
||||
account, err := c.requestBuffer.GetAccountWithBackpressure(ctx, accountID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get account: %v", err)
|
||||
@@ -178,7 +167,7 @@ func (c *Controller) sendUpdateAccountPeers(ctx context.Context, accountID strin
|
||||
return nil
|
||||
}
|
||||
|
||||
approvedPeersMap, err := c.integratedPeerValidator.GetValidatedPeers(ctx, account.Id, types.TwinGroups(maps.Values(account.Groups)), types.TwinPeers(maps.Values(account.Peers)), account.Settings.Extra)
|
||||
approvedPeersMap, err := c.integratedPeerValidator.GetValidatedPeers(ctx, account.Id, maps.Values(account.Groups), maps.Values(account.Peers), account.Settings.Extra)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get validate peers: %v", err)
|
||||
}
|
||||
@@ -266,7 +255,7 @@ func (c *Controller) sendUpdateAccountPeers(ctx context.Context, accountID strin
|
||||
// proxyNetworkMap rides the envelope as a ProxyPatch sidecar;
|
||||
// the client merges it into Calculate()'s output the same
|
||||
// way the legacy server did via NetworkMap.Merge.
|
||||
update = grpc.ToComponentSyncResponse(ctx, nil, c.config.HttpConfig, c.config.DeviceAuthorizationFlow, types.TwinPeer(p), nil, nil, components, proxyNetworkMap, dnsDomain, postureChecks, types.TwinAccountSettings(account.Settings), extraSetting, maps.Keys(peerGroups), dnsFwdPort)
|
||||
update = grpc.ToComponentSyncResponse(ctx, nil, c.config.HttpConfig, c.config.DeviceAuthorizationFlow, p, nil, nil, components, proxyNetworkMap, dnsDomain, postureChecks, account.Settings, extraSetting, maps.Keys(peerGroups), dnsFwdPort)
|
||||
c.metrics.CountToComponentSyncResponseDuration(time.Since(start))
|
||||
|
||||
c.peersUpdateManager.SendUpdate(ctx, p.ID, &network_map.UpdateMessage{
|
||||
@@ -287,7 +276,7 @@ func (c *Controller) sendUpdateAccountPeers(ctx context.Context, accountID strin
|
||||
}
|
||||
|
||||
start = time.Now()
|
||||
update = grpc.ToSyncResponse(ctx, nil, c.config.HttpConfig, c.config.DeviceAuthorizationFlow, types.TwinPeer(p), nil, nil, nmap, dnsDomain, postureChecks, dnsCache, types.TwinAccountSettings(account.Settings), extraSetting, maps.Keys(peerGroups), dnsFwdPort)
|
||||
update = grpc.ToSyncResponse(ctx, nil, c.config.HttpConfig, c.config.DeviceAuthorizationFlow, p, nil, nil, nmap, dnsDomain, postureChecks, dnsCache, account.Settings, extraSetting, maps.Keys(peerGroups), dnsFwdPort)
|
||||
c.metrics.CountToSyncResponseDuration(time.Since(start))
|
||||
|
||||
c.peersUpdateManager.SendUpdate(ctx, p.ID, &network_map.UpdateMessage{
|
||||
@@ -305,261 +294,6 @@ func (c *Controller) sendUpdateAccountPeers(ctx context.Context, accountID strin
|
||||
return nil
|
||||
}
|
||||
|
||||
// sendUpdateAccountPeersFromData is the account-free variant of
|
||||
// sendUpdateAccountPeers: everything is computed from the network-map DB
|
||||
// store's twin data; only extra settings and validated peers are resolved at
|
||||
// runtime. Proxy network maps and policy injection, private-service zones,
|
||||
// group-to-user SSH mappings and forced routing-peer DNS resolution have no
|
||||
// DB-backed source yet and are omitted.
|
||||
func (c *Controller) sendUpdateAccountPeersFromData(ctx context.Context, accountID string, reason types.UpdateReason, nmData *networkmap.NetworkMapData) error {
|
||||
peersToUpdate := c.connectedPeersFromData(nmData, nil)
|
||||
if len(peersToUpdate) == 0 {
|
||||
return nil
|
||||
}
|
||||
return c.sendUpdatesFromData(ctx, accountID, nmData, peersToUpdate, &reason)
|
||||
}
|
||||
|
||||
// sendUpdateForAffectedPeersFromData is the account-free variant of
|
||||
// sendUpdateForAffectedPeers.
|
||||
func (c *Controller) sendUpdateForAffectedPeersFromData(ctx context.Context, accountID string, peerIDs []string, nmData *networkmap.NetworkMapData) error {
|
||||
if len(peerIDs) == 0 {
|
||||
log.WithContext(ctx).Tracef("sendUpdateForAffectedPeersFromData: no affected peers")
|
||||
return nil
|
||||
}
|
||||
|
||||
peersToUpdate := c.connectedPeersFromData(nmData, peerIDs)
|
||||
if len(peersToUpdate) == 0 {
|
||||
log.WithContext(ctx).Tracef("sendUpdateForAffectedPeersFromData: no peers to update (affected peers not found in data or no channels)")
|
||||
return nil
|
||||
}
|
||||
|
||||
log.WithContext(ctx).Tracef("sendUpdateForAffectedPeersFromData: sending network map to %d connected peers", len(peersToUpdate))
|
||||
|
||||
return c.sendUpdatesFromData(ctx, accountID, nmData, peersToUpdate, nil)
|
||||
}
|
||||
|
||||
// connectedPeersFromData returns the peers with an open update channel. An
|
||||
// empty affected list means all peers; a non-empty list restricts the result
|
||||
// to those peer IDs.
|
||||
func (c *Controller) connectedPeersFromData(nmData *networkmap.NetworkMapData, affected []string) []*nmdata.Peer {
|
||||
if len(affected) == 0 {
|
||||
result := make([]*nmdata.Peer, 0, len(nmData.Peers))
|
||||
for _, peer := range nmData.Peers {
|
||||
if c.peersUpdateManager.HasChannel(peer.ID) {
|
||||
result = append(result, peer)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
result := make([]*nmdata.Peer, 0, len(affected))
|
||||
for _, peerID := range affected {
|
||||
peer := nmData.Peers[peerID]
|
||||
if peer == nil {
|
||||
continue
|
||||
}
|
||||
if c.peersUpdateManager.HasChannel(peerID) {
|
||||
result = append(result, peer)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (c *Controller) sendUpdatesFromData(ctx context.Context, accountID string, nmData *networkmap.NetworkMapData, peersToUpdate []*nmdata.Peer, reason *types.UpdateReason) error {
|
||||
globalStart := time.Now()
|
||||
|
||||
extraSettings, err := c.settingsManager.GetExtraSettings(ctx, accountID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get flow enabled status: %v", err)
|
||||
}
|
||||
|
||||
nmData.PrecomputePostureValidation()
|
||||
|
||||
dnsCache := &cache.DNSConfigCache{}
|
||||
dnsDomain := c.getDNSDomainFromData(nmData.AccountSettings)
|
||||
peersCustomZone := networkmap.PeersCustomZone(ctx, accountID, dnsDomain, nmData.Peers, ipv6AllowedPeersFromData(nmData))
|
||||
|
||||
dnsFwdPort := computeForwarderPortFromData(nmData.Peers, network_map.DnsForwarderPortMinVersion)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
semaphore := make(chan struct{}, 10)
|
||||
|
||||
for _, peer := range peersToUpdate {
|
||||
if reason != nil && c.accountManagerMetrics != nil {
|
||||
c.accountManagerMetrics.CountNmapTriggered(string(reason.Resource), string(reason.Operation))
|
||||
}
|
||||
|
||||
wg.Add(1)
|
||||
semaphore <- struct{}{}
|
||||
go func(p *nmdata.Peer) {
|
||||
defer wg.Done()
|
||||
defer func() { <-semaphore }()
|
||||
|
||||
start := time.Now()
|
||||
|
||||
postureChecks := peerPostureChecksFromData(nmData, p.ID)
|
||||
|
||||
c.metrics.CountCalcPostureChecksDuration(time.Since(start))
|
||||
start = time.Now()
|
||||
|
||||
peerGroups := maps.Keys(nmData.GetPeerGroups(p.ID))
|
||||
var update *proto.SyncResponse
|
||||
|
||||
commonSyncMessageVersion := sharedgrpc.HighestCommonSyncMessageVersion(
|
||||
c.perAccountOrGlobalSupportedSyncMessageVersions(accountID),
|
||||
sharedgrpc.SyncMessageVersionFromConfig(&p.Meta.SyncMessageVersion))
|
||||
|
||||
log.WithContext(ctx).
|
||||
WithFields(log.Fields{
|
||||
"sync_message_version": commonSyncMessageVersion,
|
||||
"server_sync_message_version": c.perAccountOrGlobalSupportedSyncMessageVersions(accountID),
|
||||
"peer_sync_message_version": sharedgrpc.SyncMessageVersionFromConfig(&p.Meta.SyncMessageVersion),
|
||||
}).Debug("common highest sync message version")
|
||||
|
||||
if commonSyncMessageVersion == sharedgrpc.ComponentNetworkMap {
|
||||
components := nmData.GetPeerNetworkMapComponents(p.ID, peersCustomZone)
|
||||
|
||||
c.metrics.CountCalcPeerNetworkMapDuration(time.Since(start))
|
||||
|
||||
start = time.Now()
|
||||
update = grpc.ToComponentSyncResponse(ctx, nil, c.config.HttpConfig, c.config.DeviceAuthorizationFlow, p, nil, nil, components, nil, dnsDomain, postureChecks, nmData.AccountSettings, extraSettings, peerGroups, dnsFwdPort)
|
||||
c.metrics.CountToComponentSyncResponseDuration(time.Since(start))
|
||||
|
||||
c.peersUpdateManager.SendUpdate(ctx, p.ID, &network_map.UpdateMessage{
|
||||
Update: update,
|
||||
MessageType: network_map.MessageTypeNetworkMap,
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
nmap := networkMapFromData(ctx, nmData, p.ID, peersCustomZone)
|
||||
|
||||
c.metrics.CountCalcPeerNetworkMapDuration(time.Since(start))
|
||||
|
||||
start = time.Now()
|
||||
update = grpc.ToSyncResponse(ctx, nil, c.config.HttpConfig, c.config.DeviceAuthorizationFlow, p, nil, nil, nmap, dnsDomain, postureChecks, dnsCache, nmData.AccountSettings, extraSettings, peerGroups, dnsFwdPort)
|
||||
c.metrics.CountToSyncResponseDuration(time.Since(start))
|
||||
|
||||
c.peersUpdateManager.SendUpdate(ctx, p.ID, &network_map.UpdateMessage{
|
||||
Update: update,
|
||||
MessageType: network_map.MessageTypeNetworkMap,
|
||||
})
|
||||
}(peer)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
if c.accountManagerMetrics != nil {
|
||||
c.accountManagerMetrics.CountUpdateAccountPeersDuration(time.Since(globalStart))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Controller) getNetworkMapData(ctx context.Context, accountID string) *networkmap.NetworkMapData {
|
||||
if c.nmdataStore == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
nmData, err := c.nmdataStore.GetNetworkMapData(ctx, accountID)
|
||||
if err != nil {
|
||||
log.WithContext(ctx).Errorf("failed to get network map data for account %s, falling back to account-based computation: %v", accountID, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
return nmData
|
||||
}
|
||||
|
||||
func (c *Controller) getDNSDomainFromData(settings *nmdata.AccountSettingsInfo) string {
|
||||
if settings == nil || settings.DNSDomain == "" {
|
||||
return c.dnsDomain
|
||||
}
|
||||
return settings.DNSDomain
|
||||
}
|
||||
|
||||
func ipv6AllowedPeersFromData(nmData *networkmap.NetworkMapData) map[string]struct{} {
|
||||
result := make(map[string]struct{})
|
||||
if nmData.AccountSettings != nil {
|
||||
for _, groupID := range nmData.AccountSettings.IPv6EnabledGroups {
|
||||
group := nmData.Groups[groupID]
|
||||
if group == nil {
|
||||
continue
|
||||
}
|
||||
for _, peerID := range group.Peers {
|
||||
result[peerID] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
for id, p := range nmData.Peers {
|
||||
if p != nil && p.ProxyMeta.Embedded {
|
||||
result[id] = struct{}{}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func networkMapFromData(ctx context.Context, nmData *networkmap.NetworkMapData, peerID string, peersCustomZone nmdata.CustomZone) *types.NetworkMap {
|
||||
components := nmData.GetPeerNetworkMapComponents(peerID, peersCustomZone)
|
||||
if components.IsEmpty() {
|
||||
return &types.NetworkMap{Network: components.Network}
|
||||
}
|
||||
return types.CalculateNetworkMapFromComponents(ctx, components)
|
||||
}
|
||||
|
||||
// peerPostureChecksFromData mirrors getPeerPostureChecks on the twin store. The
|
||||
// sync response only encodes process-check file paths, so only ProcessCheck is
|
||||
// converted back to the server posture type.
|
||||
func peerPostureChecksFromData(nmData *networkmap.NetworkMapData, peerID string) []*posture.Checks {
|
||||
if len(nmData.PostureChecks) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
peerPostureChecks := make(map[string]*posture.Checks)
|
||||
for _, policy := range nmData.Policies {
|
||||
if policy == nil || !policy.Enabled || len(policy.SourcePostureChecks) == 0 {
|
||||
continue
|
||||
}
|
||||
if !isPeerInPolicySourceGroupsFromData(nmData, peerID, policy) {
|
||||
continue
|
||||
}
|
||||
for _, checkID := range policy.SourcePostureChecks {
|
||||
twin := nmData.PostureChecks[checkID]
|
||||
if twin == nil {
|
||||
continue
|
||||
}
|
||||
peerPostureChecks[checkID] = postureChecksFromTwin(twin)
|
||||
}
|
||||
}
|
||||
|
||||
return maps.Values(peerPostureChecks)
|
||||
}
|
||||
|
||||
func isPeerInPolicySourceGroupsFromData(nmData *networkmap.NetworkMapData, peerID string, policy *nmdata.Policy) bool {
|
||||
for _, rule := range policy.Rules {
|
||||
if rule == nil || !rule.Enabled {
|
||||
continue
|
||||
}
|
||||
for _, groupID := range rule.Sources {
|
||||
if group := nmData.Groups[groupID]; group != nil && slices.Contains(group.Peers, peerID) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func postureChecksFromTwin(twin *nmdata.PostureChecks) *posture.Checks {
|
||||
checks := &posture.Checks{ID: twin.ID}
|
||||
if twin.Checks.ProcessCheck != nil {
|
||||
processes := make([]posture.Process, 0, len(twin.Checks.ProcessCheck.Processes))
|
||||
for _, p := range twin.Checks.ProcessCheck.Processes {
|
||||
processes = append(processes, posture.Process{LinuxPath: p.LinuxPath, MacPath: p.MacPath, WindowsPath: p.WindowsPath})
|
||||
}
|
||||
checks.Checks.ProcessCheck = &posture.ProcessCheck{Processes: processes}
|
||||
}
|
||||
return checks
|
||||
}
|
||||
|
||||
func (c *Controller) perAccountOrGlobalSupportedSyncMessageVersions(accountId string) sharedgrpc.SyncMessageVersion {
|
||||
if perAccount, ok := c.perAccountServerSupportedSyncMessageVersions[accountId]; ok {
|
||||
return perAccount
|
||||
@@ -592,10 +326,6 @@ func (c *Controller) sendUpdateForAffectedPeers(ctx context.Context, accountID s
|
||||
return nil
|
||||
}
|
||||
|
||||
if nmData := c.getNetworkMapData(ctx, accountID); nmData != nil {
|
||||
return c.sendUpdateForAffectedPeersFromData(ctx, accountID, peerIDs, nmData)
|
||||
}
|
||||
|
||||
account, err := c.requestBuffer.GetAccountWithBackpressure(ctx, accountID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get account: %v", err)
|
||||
@@ -611,7 +341,7 @@ func (c *Controller) sendUpdateForAffectedPeers(ctx context.Context, accountID s
|
||||
|
||||
log.WithContext(ctx).Tracef("sendUpdateForAffectedPeers: sending network map to %d connected peers", len(peersToUpdate))
|
||||
|
||||
approvedPeersMap, err := c.integratedPeerValidator.GetValidatedPeers(ctx, account.Id, types.TwinGroups(maps.Values(account.Groups)), types.TwinPeers(maps.Values(account.Peers)), account.Settings.Extra)
|
||||
approvedPeersMap, err := c.integratedPeerValidator.GetValidatedPeers(ctx, account.Id, maps.Values(account.Groups), maps.Values(account.Peers), account.Settings.Extra)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get validate peers: %v", err)
|
||||
}
|
||||
@@ -698,7 +428,7 @@ func (c *Controller) sendUpdateForAffectedPeers(ctx context.Context, accountID s
|
||||
// proxyNetworkMap rides the envelope as a ProxyPatch sidecar;
|
||||
// the client merges it into Calculate()'s output the same
|
||||
// way the legacy server did via NetworkMap.Merge.
|
||||
update = grpc.ToComponentSyncResponse(ctx, nil, c.config.HttpConfig, c.config.DeviceAuthorizationFlow, types.TwinPeer(p), nil, nil, components, proxyNetworkMap, dnsDomain, postureChecks, types.TwinAccountSettings(account.Settings), extraSetting, maps.Keys(peerGroups), dnsFwdPort)
|
||||
update = grpc.ToComponentSyncResponse(ctx, nil, c.config.HttpConfig, c.config.DeviceAuthorizationFlow, p, nil, nil, components, proxyNetworkMap, dnsDomain, postureChecks, account.Settings, extraSetting, maps.Keys(peerGroups), dnsFwdPort)
|
||||
c.metrics.CountToComponentSyncResponseDuration(time.Since(start))
|
||||
|
||||
c.peersUpdateManager.SendUpdate(ctx, p.ID, &network_map.UpdateMessage{
|
||||
@@ -719,7 +449,7 @@ func (c *Controller) sendUpdateForAffectedPeers(ctx context.Context, accountID s
|
||||
}
|
||||
|
||||
start = time.Now()
|
||||
update = grpc.ToSyncResponse(ctx, nil, c.config.HttpConfig, c.config.DeviceAuthorizationFlow, types.TwinPeer(p), nil, nil, nmap, dnsDomain, postureChecks, dnsCache, types.TwinAccountSettings(account.Settings), extraSetting, maps.Keys(peerGroups), dnsFwdPort)
|
||||
update = grpc.ToSyncResponse(ctx, nil, c.config.HttpConfig, c.config.DeviceAuthorizationFlow, p, nil, nil, nmap, dnsDomain, postureChecks, dnsCache, account.Settings, extraSetting, maps.Keys(peerGroups), dnsFwdPort)
|
||||
c.metrics.CountToSyncResponseDuration(time.Since(start))
|
||||
|
||||
c.peersUpdateManager.SendUpdate(ctx, p.ID, &network_map.UpdateMessage{
|
||||
@@ -776,7 +506,7 @@ func (c *Controller) UpdateAccountPeer(ctx context.Context, accountId string, pe
|
||||
return fmt.Errorf("peer %s doesn't exists in account %s", peerId, accountId)
|
||||
}
|
||||
|
||||
approvedPeersMap, err := c.integratedPeerValidator.GetValidatedPeers(ctx, account.Id, types.TwinGroups(maps.Values(account.Groups)), types.TwinPeers(maps.Values(account.Peers)), account.Settings.Extra)
|
||||
approvedPeersMap, err := c.integratedPeerValidator.GetValidatedPeers(ctx, account.Id, maps.Values(account.Groups), maps.Values(account.Peers), account.Settings.Extra)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get validated peers: %v", err)
|
||||
}
|
||||
@@ -836,7 +566,7 @@ func (c *Controller) UpdateAccountPeer(ctx context.Context, accountId string, pe
|
||||
// proxyNetworkMap rides the envelope as a ProxyPatch sidecar;
|
||||
// the client merges it into Calculate()'s output the same
|
||||
// way the legacy server did via NetworkMap.Merge.
|
||||
update = grpc.ToComponentSyncResponse(ctx, nil, c.config.HttpConfig, c.config.DeviceAuthorizationFlow, types.TwinPeer(peer), nil, nil, components, proxyNetworkMap, dnsDomain, postureChecks, types.TwinAccountSettings(account.Settings), extraSettings, maps.Keys(peerGroups), dnsFwdPort)
|
||||
update = grpc.ToComponentSyncResponse(ctx, nil, c.config.HttpConfig, c.config.DeviceAuthorizationFlow, peer, nil, nil, components, proxyNetworkMap, dnsDomain, postureChecks, account.Settings, extraSettings, maps.Keys(peerGroups), dnsFwdPort)
|
||||
|
||||
c.peersUpdateManager.SendUpdate(ctx, peer.ID, &network_map.UpdateMessage{
|
||||
Update: update,
|
||||
@@ -853,7 +583,7 @@ func (c *Controller) UpdateAccountPeer(ctx context.Context, accountId string, pe
|
||||
nmap.Merge(proxyNetworkMap)
|
||||
}
|
||||
|
||||
update = grpc.ToSyncResponse(ctx, nil, c.config.HttpConfig, c.config.DeviceAuthorizationFlow, types.TwinPeer(peer), nil, nil, nmap, dnsDomain, postureChecks, dnsCache, types.TwinAccountSettings(account.Settings), extraSettings, maps.Keys(peerGroups), dnsFwdPort)
|
||||
update = grpc.ToSyncResponse(ctx, nil, c.config.HttpConfig, c.config.DeviceAuthorizationFlow, peer, nil, nil, nmap, dnsDomain, postureChecks, dnsCache, account.Settings, extraSettings, maps.Keys(peerGroups), dnsFwdPort)
|
||||
|
||||
c.peersUpdateManager.SendUpdate(ctx, peer.ID, &network_map.UpdateMessage{
|
||||
Update: update,
|
||||
@@ -913,11 +643,7 @@ func (c *Controller) GetValidatedPeerWithComponents(ctx context.Context, isRequi
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, 0, err
|
||||
}
|
||||
return peer, &types.NetworkMapComponents{Network: types.TwinNetwork(network)}, nil, nil, 0, nil
|
||||
}
|
||||
|
||||
if nmData := c.getNetworkMapData(ctx, accountID); nmData != nil {
|
||||
return c.getValidatedPeerWithComponentsFromData(ctx, accountID, peer, nmData)
|
||||
return peer, &types.NetworkMapComponents{Network: network.Copy()}, nil, nil, 0, nil
|
||||
}
|
||||
|
||||
account, err := c.requestBuffer.GetAccountWithBackpressure(ctx, accountID)
|
||||
@@ -927,7 +653,7 @@ func (c *Controller) GetValidatedPeerWithComponents(ctx context.Context, isRequi
|
||||
|
||||
c.injectAllProxyPolicies(ctx, account)
|
||||
|
||||
approvedPeersMap, err := c.integratedPeerValidator.GetValidatedPeers(ctx, account.Id, types.TwinGroups(maps.Values(account.Groups)), types.TwinPeers(maps.Values(account.Peers)), account.Settings.Extra)
|
||||
approvedPeersMap, err := c.integratedPeerValidator.GetValidatedPeers(ctx, account.Id, maps.Values(account.Groups), maps.Values(account.Peers), account.Settings.Extra)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, 0, err
|
||||
}
|
||||
@@ -964,21 +690,6 @@ func (c *Controller) GetValidatedPeerWithComponents(ctx context.Context, isRequi
|
||||
return peer, components, proxyNetworkMaps[peer.ID], postureChecks, dnsFwdPort, nil
|
||||
}
|
||||
|
||||
// getValidatedPeerWithComponentsFromData is the account-free variant of
|
||||
// GetValidatedPeerWithComponents. The proxy network map fragment is omitted
|
||||
// like on the other nmdata paths.
|
||||
func (c *Controller) getValidatedPeerWithComponentsFromData(ctx context.Context, accountID string, peer *nbpeer.Peer, nmData *networkmap.NetworkMapData) (*nbpeer.Peer, *types.NetworkMapComponents, *types.NetworkMap, []*posture.Checks, int64, error) {
|
||||
postureChecks := peerPostureChecksFromData(nmData, peer.ID)
|
||||
|
||||
dnsDomain := c.getDNSDomainFromData(nmData.AccountSettings)
|
||||
peersCustomZone := networkmap.PeersCustomZone(ctx, accountID, dnsDomain, nmData.Peers, ipv6AllowedPeersFromData(nmData))
|
||||
|
||||
components := nmData.GetPeerNetworkMapComponents(peer.ID, peersCustomZone)
|
||||
dnsFwdPort := computeForwarderPortFromData(nmData.Peers, network_map.DnsForwarderPortMinVersion)
|
||||
|
||||
return peer, components, nil, postureChecks, dnsFwdPort, nil
|
||||
}
|
||||
|
||||
// BufferUpdateAffectedPeers accumulates peer IDs and flushes them after the buffer interval.
|
||||
func (c *Controller) BufferUpdateAffectedPeers(ctx context.Context, accountID string, peerIDs []string, reason types.UpdateReason) error {
|
||||
if len(peerIDs) == 0 {
|
||||
@@ -1085,15 +796,11 @@ func (c *Controller) GetValidatedPeerWithMap(ctx context.Context, isRequiresAppr
|
||||
}
|
||||
|
||||
emptyMap := &types.NetworkMap{
|
||||
Network: types.TwinNetwork(network),
|
||||
Network: network.Copy(),
|
||||
}
|
||||
return emptyMap, nil, 0, nil
|
||||
}
|
||||
|
||||
if nmData := c.getNetworkMapData(ctx, accountID); nmData != nil {
|
||||
return c.getValidatedPeerWithMapFromData(ctx, accountID, peerID, nmData)
|
||||
}
|
||||
|
||||
account, err := c.requestBuffer.GetAccountWithBackpressure(ctx, accountID)
|
||||
if err != nil {
|
||||
return nil, nil, 0, err
|
||||
@@ -1101,7 +808,7 @@ func (c *Controller) GetValidatedPeerWithMap(ctx context.Context, isRequiresAppr
|
||||
|
||||
c.injectAllProxyPolicies(ctx, account)
|
||||
|
||||
approvedPeersMap, err := c.integratedPeerValidator.GetValidatedPeers(ctx, account.Id, types.TwinGroups(maps.Values(account.Groups)), types.TwinPeers(maps.Values(account.Peers)), account.Settings.Extra)
|
||||
approvedPeersMap, err := c.integratedPeerValidator.GetValidatedPeers(ctx, account.Id, maps.Values(account.Groups), maps.Values(account.Peers), account.Settings.Extra)
|
||||
if err != nil {
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
@@ -1141,21 +848,6 @@ func (c *Controller) GetValidatedPeerWithMap(ctx context.Context, isRequiresAppr
|
||||
return networkMap, postureChecks, dnsFwdPort, nil
|
||||
}
|
||||
|
||||
// getValidatedPeerWithMapFromData is the account-free variant of
|
||||
// GetValidatedPeerWithMap. The proxy network map fragment is omitted like on
|
||||
// the other nmdata paths.
|
||||
func (c *Controller) getValidatedPeerWithMapFromData(ctx context.Context, accountID string, peerID string, nmData *networkmap.NetworkMapData) (*types.NetworkMap, []*posture.Checks, int64, error) {
|
||||
postureChecks := peerPostureChecksFromData(nmData, peerID)
|
||||
|
||||
dnsDomain := c.getDNSDomainFromData(nmData.AccountSettings)
|
||||
peersCustomZone := networkmap.PeersCustomZone(ctx, accountID, dnsDomain, nmData.Peers, ipv6AllowedPeersFromData(nmData))
|
||||
|
||||
networkMap := networkMapFromData(ctx, nmData, peerID, peersCustomZone)
|
||||
dnsFwdPort := computeForwarderPortFromData(nmData.Peers, network_map.DnsForwarderPortMinVersion)
|
||||
|
||||
return networkMap, postureChecks, dnsFwdPort, nil
|
||||
}
|
||||
|
||||
// GetDNSDomain returns the configured dnsDomain
|
||||
func (c *Controller) GetDNSDomain(settings *types.Settings) string {
|
||||
if settings == nil {
|
||||
@@ -1218,36 +910,20 @@ func (c *Controller) StartWarmup(ctx context.Context) {
|
||||
// computeForwarderPort checks if all peers in the account have updated to a specific version or newer.
|
||||
// If all peers have the required version, it returns the new well-known port (22054), otherwise returns 0.
|
||||
func computeForwarderPort(peers []*nbpeer.Peer, requiredVersion string) int64 {
|
||||
versions := make([]string, 0, len(peers))
|
||||
for _, peer := range peers {
|
||||
versions = append(versions, peer.Meta.WtVersion)
|
||||
}
|
||||
return computeForwarderPortFromVersions(versions, requiredVersion)
|
||||
}
|
||||
|
||||
func computeForwarderPortFromData(peers map[string]*nmdata.Peer, requiredVersion string) int64 {
|
||||
versions := make([]string, 0, len(peers))
|
||||
for _, peer := range peers {
|
||||
versions = append(versions, peer.Meta.WtVersion)
|
||||
}
|
||||
return computeForwarderPortFromVersions(versions, requiredVersion)
|
||||
}
|
||||
|
||||
func computeForwarderPortFromVersions(wtVersions []string, requiredVersion string) int64 {
|
||||
if len(wtVersions) == 0 {
|
||||
if len(peers) == 0 {
|
||||
return int64(network_map.OldForwarderPort)
|
||||
}
|
||||
|
||||
reqVer := semver.Canonical(requiredVersion)
|
||||
|
||||
// Check if all peers have the required version or newer
|
||||
for _, wtVersion := range wtVersions {
|
||||
for _, peer := range peers {
|
||||
|
||||
// Development version is always supported
|
||||
if version.IsDevelopmentVersion(wtVersion) {
|
||||
if version.IsDevelopmentVersion(peer.Meta.WtVersion) {
|
||||
continue
|
||||
}
|
||||
peerVersion := semver.Canonical("v" + wtVersion)
|
||||
peerVersion := semver.Canonical("v" + peer.Meta.WtVersion)
|
||||
if peerVersion == "" {
|
||||
// If any peer doesn't have version info, return 0
|
||||
return int64(network_map.OldForwarderPort)
|
||||
@@ -1381,7 +1057,7 @@ func (c *Controller) GetNetworkMap(ctx context.Context, peerID string) (*types.N
|
||||
groups[groupID] = group.Peers
|
||||
}
|
||||
|
||||
validatedPeers, err := c.integratedPeerValidator.GetValidatedPeers(ctx, account.Id, types.TwinGroups(maps.Values(account.Groups)), types.TwinPeers(maps.Values(account.Peers)), account.Settings.Extra)
|
||||
validatedPeers, err := c.integratedPeerValidator.GetValidatedPeers(ctx, account.Id, maps.Values(account.Groups), maps.Values(account.Peers), account.Settings.Extra)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
// Package activity records that a principal used a reverse proxy service, so
|
||||
// that activity accounting counts people and devices which reach services
|
||||
// through the proxy but never touch the dashboard or the management API.
|
||||
package activity
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server/peer"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
)
|
||||
|
||||
// Manager records reverse proxy usage against the timestamps activity
|
||||
// accounting reads. Both methods are best effort from the caller's point of
|
||||
// view: a lost record is corrected by the next request, and no authorization
|
||||
// decision reads them back.
|
||||
type Manager interface {
|
||||
// RecordUserLogin records a completed SSO sign-in to a proxied service.
|
||||
// Service users have no interactive login and are ignored.
|
||||
RecordUserLogin(ctx context.Context, accountID string, user *types.User) error
|
||||
// RecordPeerSeen records that a peer reached a private service over the
|
||||
// mesh, which is what lets its owner count as active. Peers activity
|
||||
// accounting excludes, and peers already seen recently, are ignored.
|
||||
RecordPeerSeen(ctx context.Context, accountID string, peer *peer.Peer) error
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package manager
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/netbirdio/netbird/management/internals/modules/reverseproxy/activity"
|
||||
"github.com/netbirdio/netbird/management/server/peer"
|
||||
"github.com/netbirdio/netbird/management/server/store"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
)
|
||||
|
||||
// peerSeenInterval is how stale a peer's LastSeen must be before reaching a
|
||||
// private service refreshes it. Positive tunnel validations are cached on the
|
||||
// proxy for five minutes, so without a floor a busy peer would rewrite its row
|
||||
// behind every request; an hour still sits well inside the window activity
|
||||
// accounting asks about.
|
||||
const peerSeenInterval = time.Hour
|
||||
|
||||
type managerImpl struct {
|
||||
store store.Store
|
||||
}
|
||||
|
||||
// NewManager returns the activity manager backed by the management store.
|
||||
func NewManager(store store.Store) activity.Manager {
|
||||
return &managerImpl{store: store}
|
||||
}
|
||||
|
||||
// RecordUserLogin stamps the login the same way the dashboard and device login
|
||||
// paths do, so a person who only ever reaches proxied services still has a
|
||||
// login on record.
|
||||
func (m *managerImpl) RecordUserLogin(ctx context.Context, accountID string, user *types.User) error {
|
||||
if user == nil || user.IsServiceUser {
|
||||
return nil
|
||||
}
|
||||
|
||||
return m.store.SaveUserLastLogin(ctx, accountID, user.Id, time.Now().UTC())
|
||||
}
|
||||
|
||||
// RecordPeerSeen stamps LastSeen, the column a peer activates its owner
|
||||
// through. The peer the caller already holds answers the throttle without a
|
||||
// query, so a peer seen inside the interval costs nothing to skip; the same
|
||||
// cutoff goes to the store, which enforces it inside the UPDATE so concurrent
|
||||
// requests for one peer cannot each write off their own stale read.
|
||||
func (m *managerImpl) RecordPeerSeen(ctx context.Context, accountID string, peer *peer.Peer) error {
|
||||
if peer == nil || !countsTowardActivity(peer) {
|
||||
return nil
|
||||
}
|
||||
|
||||
staleBefore := time.Now().UTC().Add(-peerSeenInterval)
|
||||
if peer.Status != nil && peer.Status.LastSeen.After(staleBefore) {
|
||||
return nil
|
||||
}
|
||||
|
||||
_, err := m.store.RefreshPeerLastSeen(ctx, accountID, peer.ID, staleBefore)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// countsTowardActivity reports whether the peer represents a device a person
|
||||
// actually runs. Embedded proxy peers are infrastructure and browser (WASM)
|
||||
// clients are ephemeral sessions, so activity accounting ignores both and a
|
||||
// write for them could never count.
|
||||
func countsTowardActivity(peer *peer.Peer) bool {
|
||||
return !peer.ProxyMeta.Embedded && peer.Meta.KernelVersion != "wasm"
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
package manager
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server/peer"
|
||||
"github.com/netbirdio/netbird/management/server/store"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
)
|
||||
|
||||
// recordingStore captures the two writes the activity manager makes. The
|
||||
// embedded interface satisfies the rest and panics if anything else is called,
|
||||
// which keeps the manager honest about its surface.
|
||||
type recordingStore struct {
|
||||
store.Store
|
||||
logins []loginWrite
|
||||
seen []seenWrite
|
||||
}
|
||||
|
||||
type loginWrite struct {
|
||||
accountID string
|
||||
userID string
|
||||
at time.Time
|
||||
}
|
||||
|
||||
type seenWrite struct {
|
||||
accountID string
|
||||
peerID string
|
||||
staleBefore time.Time
|
||||
}
|
||||
|
||||
func (s *recordingStore) SaveUserLastLogin(_ context.Context, accountID, userID string, lastLogin time.Time) error {
|
||||
s.logins = append(s.logins, loginWrite{accountID: accountID, userID: userID, at: lastLogin})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *recordingStore) RefreshPeerLastSeen(_ context.Context, accountID, peerID string, staleBefore time.Time) (bool, error) {
|
||||
s.seen = append(s.seen, seenWrite{accountID: accountID, peerID: peerID, staleBefore: staleBefore})
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func TestRecordUserLogin(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
user *types.User
|
||||
expectWrite bool
|
||||
}{
|
||||
{
|
||||
name: "regular user is recorded",
|
||||
user: &types.User{Id: "user1", AccountID: "account1"},
|
||||
expectWrite: true,
|
||||
},
|
||||
{
|
||||
// Activity accounting never counts service users, so a row for one
|
||||
// would be noise.
|
||||
name: "service user is ignored",
|
||||
user: &types.User{Id: "svc1", AccountID: "account1", IsServiceUser: true},
|
||||
expectWrite: false,
|
||||
},
|
||||
{
|
||||
name: "missing user is ignored",
|
||||
user: nil,
|
||||
expectWrite: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
st := &recordingStore{}
|
||||
require.NoError(t, NewManager(st).RecordUserLogin(context.Background(), "account1", tt.user))
|
||||
|
||||
if !tt.expectWrite {
|
||||
assert.Empty(t, st.logins, "no login should have been recorded")
|
||||
return
|
||||
}
|
||||
|
||||
require.Len(t, st.logins, 1, "exactly one login should have been recorded")
|
||||
assert.Equal(t, "account1", st.logins[0].accountID, "login must be recorded against the service account")
|
||||
assert.Equal(t, tt.user.Id, st.logins[0].userID, "login must be recorded against the signing-in user")
|
||||
assert.Equal(t, time.UTC, st.logins[0].at.Location(), "timestamps are written in UTC")
|
||||
assert.WithinDuration(t, time.Now().UTC(), st.logins[0].at, time.Minute, "login should be stamped now")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRecordPeerSeen(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
peer *peer.Peer
|
||||
expectWrite bool
|
||||
}{
|
||||
{
|
||||
name: "peer seen long ago is recorded",
|
||||
peer: &peer.Peer{ID: "peer1", Status: &peer.PeerStatus{LastSeen: time.Now().Add(-3 * time.Hour)}},
|
||||
expectWrite: true,
|
||||
},
|
||||
{
|
||||
name: "peer never seen is recorded",
|
||||
peer: &peer.Peer{ID: "peer1", Status: &peer.PeerStatus{}},
|
||||
expectWrite: true,
|
||||
},
|
||||
{
|
||||
// The throttle. The caller already holds the peer, so skipping a
|
||||
// recently seen one costs nothing.
|
||||
name: "peer seen inside the interval is skipped",
|
||||
peer: &peer.Peer{ID: "peer1", Status: &peer.PeerStatus{LastSeen: time.Now().Add(-10 * time.Minute)}},
|
||||
expectWrite: false,
|
||||
},
|
||||
{
|
||||
name: "embedded proxy peer is skipped",
|
||||
peer: &peer.Peer{ID: "peer1", ProxyMeta: peer.ProxyMeta{Embedded: true}, Status: &peer.PeerStatus{LastSeen: time.Now().Add(-3 * time.Hour)}},
|
||||
expectWrite: false,
|
||||
},
|
||||
{
|
||||
name: "browser client is skipped",
|
||||
peer: &peer.Peer{ID: "peer1", Meta: peer.PeerSystemMeta{KernelVersion: "wasm"}, Status: &peer.PeerStatus{LastSeen: time.Now().Add(-3 * time.Hour)}},
|
||||
expectWrite: false,
|
||||
},
|
||||
{
|
||||
name: "missing peer is ignored",
|
||||
peer: nil,
|
||||
expectWrite: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
st := &recordingStore{}
|
||||
require.NoError(t, NewManager(st).RecordPeerSeen(context.Background(), "account1", tt.peer))
|
||||
|
||||
if !tt.expectWrite {
|
||||
assert.Empty(t, st.seen, "no activity should have been recorded")
|
||||
return
|
||||
}
|
||||
|
||||
require.Len(t, st.seen, 1, "exactly one activity write should have been recorded")
|
||||
assert.Equal(t, "account1", st.seen[0].accountID, "activity must be recorded against the service account")
|
||||
assert.Equal(t, tt.peer.ID, st.seen[0].peerID, "activity must be recorded against the calling peer")
|
||||
assert.Equal(t, time.UTC, st.seen[0].staleBefore.Location(), "cutoffs are passed in UTC")
|
||||
assert.WithinDuration(t, time.Now().UTC().Add(-peerSeenInterval), st.seen[0].staleBefore, time.Minute,
|
||||
"the store must enforce the same interval the local check applies")
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
package networkmapdb
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server/integrations/integrated_validator"
|
||||
"github.com/netbirdio/netbird/management/server/settings"
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap"
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
|
||||
)
|
||||
|
||||
type NetworkMapDBStore interface { //nolint:revive // established name across the codebase
|
||||
BeginTx(ctx context.Context) (NetworkMapDBStoreConn, error)
|
||||
}
|
||||
|
||||
type NetworkMapDBStoreConn interface { //nolint:revive // established name across the codebase
|
||||
GetGroups(ctx context.Context, accountId string) ([]nmdata.Group, map[string]map[string]any, error)
|
||||
GetDomains(ctx context.Context, accountId string) ([]Domain, error)
|
||||
GetPeers(ctx context.Context, accountId string) ([]nmdata.Peer, map[string][]*nmdata.Peer, error)
|
||||
GetPolicies(ctx context.Context, accountId string) ([]nmdata.Policy, map[string]map[string]any, map[string]map[string]any, error)
|
||||
GetRoutes(ctx context.Context, accountId string) ([]nmdata.Route, error)
|
||||
GetNameServerGroups(ctx context.Context, accountId string) ([]nmdata.NameServerGroup, error)
|
||||
GetNetworkResources(ctx context.Context, accountId string) ([]nmdata.NetworkResource, error)
|
||||
GetNetworkRouters(ctx context.Context, accountId string) (map[string]map[string]*nmdata.NetworkRouter, error)
|
||||
GetNetwork(ctx context.Context, accountId string) (nmdata.Network, error)
|
||||
GetAppliedZoneCandidates(ctx context.Context, accountId string) ([]networkmap.AppliedZoneCandidate, error)
|
||||
GetAccountSettings(ctx context.Context, accountId string) (nmdata.AccountSettingsInfo, error)
|
||||
GetPostureChecks(ctx context.Context, accountId string) ([]nmdata.PostureChecks, map[string]string, error)
|
||||
GetAllowedUsers(ctx context.Context, accountId string) (map[string]struct{}, map[string][]string, error)
|
||||
GetDnsSettings(ctx context.Context, accountId string) (nmdata.DNSSettings, error)
|
||||
GetNetworkXIDToPublicIdMap(ctx context.Context, accountId string) (map[string]string, error)
|
||||
GetPrivateServices(ctx context.Context, accountId string) ([]Service, error)
|
||||
GetProxyTargetedDomainResourceIDs(ctx context.Context, accountId string) (map[string]struct{}, error)
|
||||
|
||||
CommitTx(ctx context.Context) error
|
||||
RollbackTx(ctx context.Context) error
|
||||
}
|
||||
|
||||
type NetworkMapDBStoreImpl struct { //nolint:revive // established name across the codebase
|
||||
store NetworkMapDBStore
|
||||
integratedPeerValidator integrated_validator.IntegratedValidator
|
||||
extraSettingsManager settings.Manager
|
||||
}
|
||||
|
||||
func NewNetworkMapDBStoreImpl(store NetworkMapDBStore, integratedPeerValidator integrated_validator.IntegratedValidator, extraSettingsManager settings.Manager) *NetworkMapDBStoreImpl {
|
||||
return &NetworkMapDBStoreImpl{
|
||||
store: store,
|
||||
integratedPeerValidator: integratedPeerValidator,
|
||||
extraSettingsManager: extraSettingsManager,
|
||||
}
|
||||
}
|
||||
@@ -1,252 +0,0 @@
|
||||
package networkmapdb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/exp/maps"
|
||||
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap"
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
|
||||
)
|
||||
|
||||
func (s *NetworkMapDBStoreImpl) GetNetworkMapData(ctx context.Context, accountId string) (*networkmap.NetworkMapData, error) {
|
||||
tx, err := s.store.BeginTx(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
acctSettings, err := tx.GetAccountSettings(ctx, accountId)
|
||||
if err != nil {
|
||||
return rollbackAndReturnError(ctx, tx, fmt.Errorf("failed to get account settings: %w", err))
|
||||
}
|
||||
dnsZones, err := tx.GetAppliedZoneCandidates(ctx, accountId)
|
||||
if err != nil {
|
||||
return rollbackAndReturnError(ctx, tx, fmt.Errorf("failed to get applied zone candidates: %w", err))
|
||||
}
|
||||
groups, resourceToGroupIdx, err := tx.GetGroups(ctx, accountId)
|
||||
if err != nil {
|
||||
return rollbackAndReturnError(ctx, tx, fmt.Errorf("failed to get groups: %w", err))
|
||||
}
|
||||
nsGroups, err := tx.GetNameServerGroups(ctx, accountId)
|
||||
if err != nil {
|
||||
return rollbackAndReturnError(ctx, tx, fmt.Errorf("failed to get nameserver groups: %w", err))
|
||||
}
|
||||
networkResources, err := tx.GetNetworkResources(ctx, accountId)
|
||||
if err != nil {
|
||||
return rollbackAndReturnError(ctx, tx, fmt.Errorf("failed to get network resources: %w", err))
|
||||
}
|
||||
routers, err := tx.GetNetworkRouters(ctx, accountId)
|
||||
if err != nil {
|
||||
return rollbackAndReturnError(ctx, tx, fmt.Errorf("failed to get network routers: %w", err))
|
||||
}
|
||||
network, err := tx.GetNetwork(ctx, accountId)
|
||||
if err != nil {
|
||||
return rollbackAndReturnError(ctx, tx, fmt.Errorf("failed to get network: %w", err))
|
||||
}
|
||||
peers, proxyPeers, err := tx.GetPeers(ctx, accountId)
|
||||
if err != nil {
|
||||
return rollbackAndReturnError(ctx, tx, fmt.Errorf("failed to get peers: %w", err))
|
||||
}
|
||||
policies, policyToDestinationResourceIdx, policyToDestinationGroupIdx, err := tx.GetPolicies(ctx, accountId)
|
||||
if err != nil {
|
||||
return rollbackAndReturnError(ctx, tx, fmt.Errorf("failed to get policies: %w", err))
|
||||
}
|
||||
postureChecks, postureCheckXIDToPublicID, err := tx.GetPostureChecks(ctx, accountId)
|
||||
if err != nil {
|
||||
return rollbackAndReturnError(ctx, tx, fmt.Errorf("failed to get posture checks: %w", err))
|
||||
}
|
||||
routes, err := tx.GetRoutes(ctx, accountId)
|
||||
if err != nil {
|
||||
return rollbackAndReturnError(ctx, tx, fmt.Errorf("failed to get routes: %w", err))
|
||||
}
|
||||
networkXIDToPublicID, err := tx.GetNetworkXIDToPublicIdMap(ctx, accountId)
|
||||
if err != nil {
|
||||
return rollbackAndReturnError(ctx, tx, fmt.Errorf("failed to get network xid to public id map: %w", err))
|
||||
}
|
||||
allowedUserIds, groupsToUserIds, err := tx.GetAllowedUsers(ctx, accountId)
|
||||
if err != nil {
|
||||
return rollbackAndReturnError(ctx, tx, fmt.Errorf("failed to get allowed users: %w", err))
|
||||
}
|
||||
dnsSettings, err := tx.GetDnsSettings(ctx, accountId)
|
||||
if err != nil {
|
||||
return rollbackAndReturnError(ctx, tx, fmt.Errorf("failed to get dns settings: %w", err))
|
||||
}
|
||||
domains, err := tx.GetDomains(ctx, accountId)
|
||||
if err != nil {
|
||||
return rollbackAndReturnError(ctx, tx, err)
|
||||
}
|
||||
services, err := tx.GetPrivateServices(ctx, accountId)
|
||||
if err != nil {
|
||||
return rollbackAndReturnError(ctx, tx, err)
|
||||
}
|
||||
proxyTargetedDomainResourceIDs, err := tx.GetProxyTargetedDomainResourceIDs(ctx, accountId)
|
||||
if err != nil {
|
||||
return rollbackAndReturnError(ctx, tx, fmt.Errorf("failed to get proxy targeted domain resources: %w", err))
|
||||
}
|
||||
|
||||
resourcePolicies := make(map[string][]*nmdata.Policy)
|
||||
for _, resource := range networkResources {
|
||||
if !resource.Enabled {
|
||||
continue
|
||||
}
|
||||
networkResourceGroups := resourceToGroupIdx[resource.ID]
|
||||
for _, policy := range policies {
|
||||
if !policy.Enabled {
|
||||
continue
|
||||
}
|
||||
if _, ok := policyToDestinationResourceIdx[policy.ID][resource.ID]; ok {
|
||||
resourcePolicies[resource.ID] = append(resourcePolicies[resource.ID], &policy) // TODO (dmitri) maybe use public id?
|
||||
continue
|
||||
}
|
||||
if groupIds, ok := policyToDestinationGroupIdx[policy.ID]; ok {
|
||||
for networkResourceGroup := range networkResourceGroups {
|
||||
if _, ok := groupIds[networkResourceGroup]; ok {
|
||||
resourcePolicies[resource.ID] = append(resourcePolicies[resource.ID], &policy)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err = tx.CommitTx(ctx); err != nil {
|
||||
log.WithContext(ctx).Warnf("failed to commit network map read transaction: %v", err)
|
||||
}
|
||||
|
||||
toret := networkmap.NetworkMapData{
|
||||
AccountSettings: &acctSettings,
|
||||
DNSSettings: &dnsSettings,
|
||||
Network: &network,
|
||||
Peers: toMap(peers, func(p nmdata.Peer) string { return p.ID }),
|
||||
Groups: toMap(groups, func(g nmdata.Group) string { return g.ID }),
|
||||
Policies: toSliceOfPtrs(policies),
|
||||
ResourcePolicies: resourcePolicies,
|
||||
Routes: toSliceOfPtrs(routes),
|
||||
Routers: routers,
|
||||
NameServerGroups: toSliceOfPtrs(nsGroups),
|
||||
NetworkResources: toSliceOfPtrs(networkResources),
|
||||
PostureChecks: toMap(postureChecks, func(pc nmdata.PostureChecks) string { return pc.ID }),
|
||||
AllowedUserIDs: allowedUserIds,
|
||||
GroupIDToUserIDs: groupsToUserIds,
|
||||
NetworkXIDToPublicID: networkXIDToPublicID, // TODO (dmitri) maybe we can switch to public ids everywhere?
|
||||
AppliedZoneCandidates: dnsZones,
|
||||
PrivateServiceCandidates: buildPrivateServiceCandidates(services, domains, proxyPeers),
|
||||
PostureCheckXIDToPublicID: postureCheckXIDToPublicID,
|
||||
ProxyTargetedDomainResourceIDs: proxyTargetedDomainResourceIDs,
|
||||
}
|
||||
|
||||
extraSettings, err := s.extraSettingsManager.GetExtraSettings(ctx, accountId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
toret.ValidatedPeers, err = s.integratedPeerValidator.GetValidatedPeers(ctx, accountId, maps.Values(toret.Groups), maps.Values(toret.Peers), extraSettings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &toret, nil
|
||||
}
|
||||
|
||||
func rollbackAndReturnError(ctx context.Context, tx NetworkMapDBStoreConn, err error) (*networkmap.NetworkMapData, error) {
|
||||
if errr := tx.RollbackTx(ctx); errr != nil {
|
||||
log.WithContext(ctx).Warnf("failed to rollback network map read transaction: %v", errr)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func toMap[T any](all []T, id func(t T) string) map[string]*T {
|
||||
toret := make(map[string]*T, len(all))
|
||||
for _, t := range all {
|
||||
toret[id(t)] = &t
|
||||
}
|
||||
return toret
|
||||
}
|
||||
|
||||
func toSliceOfPtrs[T any](all []T) []*T {
|
||||
toret := make([]*T, 0, len(all))
|
||||
for _, t := range all {
|
||||
toret = append(toret, &t)
|
||||
}
|
||||
return toret
|
||||
}
|
||||
|
||||
func serviceDomainZone(svc Service, ds []Domain) string {
|
||||
if domainFromSuffix(svc.Domain.String, svc.ProxyCluster.String) {
|
||||
return svc.ProxyCluster.String
|
||||
}
|
||||
|
||||
var zoneName string
|
||||
for _, domain := range ds {
|
||||
if domain.TargetCluster.String != svc.ProxyCluster.String {
|
||||
continue
|
||||
}
|
||||
if domainFromSuffix(svc.Domain.String, domain.Domain.String) && len(domain.Domain.String) > len(zoneName) {
|
||||
zoneName = domain.Domain.String
|
||||
}
|
||||
}
|
||||
|
||||
return zoneName
|
||||
}
|
||||
|
||||
func domainFromSuffix(domain, suffix string) bool {
|
||||
if suffix == "" {
|
||||
return false
|
||||
}
|
||||
return domain == suffix || strings.HasSuffix(domain, "."+suffix)
|
||||
}
|
||||
|
||||
func buildPrivateServiceCandidates(svcs []Service, domains []Domain, proxyPeersByCluster map[string][]*nmdata.Peer) []networkmap.PrivateServiceCandidate {
|
||||
var out []networkmap.PrivateServiceCandidate
|
||||
|
||||
if len(proxyPeersByCluster) == 0 {
|
||||
return out
|
||||
}
|
||||
|
||||
for _, svc := range svcs {
|
||||
if !svc.Enabled.Bool || !svc.Private.Bool {
|
||||
continue
|
||||
}
|
||||
if len(svc.AccessGroups) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
domainZone := serviceDomainZone(svc, domains)
|
||||
if domainZone == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
var records []nmdata.SimpleRecord
|
||||
for _, proxyPeer := range proxyPeersByCluster[svc.ProxyCluster.String] {
|
||||
if !proxyPeer.IP.IsValid() {
|
||||
continue
|
||||
}
|
||||
records = append(records, nmdata.SimpleRecord{
|
||||
Name: dns.Fqdn(svc.Domain.String),
|
||||
Type: int(dns.TypeA),
|
||||
Class: "IN",
|
||||
TTL: 5,
|
||||
RData: proxyPeer.IP.String(),
|
||||
})
|
||||
}
|
||||
if len(records) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
out = append(out, networkmap.PrivateServiceCandidate{
|
||||
AccessGroups: svc.AccessGroups,
|
||||
Zone: nmdata.CustomZone{
|
||||
Domain: dns.Fqdn(domainZone),
|
||||
Records: records,
|
||||
NonAuthoritative: true,
|
||||
SearchDomainDisabled: true,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
package networkmap_pgsql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
|
||||
)
|
||||
|
||||
const (
|
||||
GetAccountSettingsQuery = `
|
||||
select settings_peer_login_expiration_enabled as peer_login_expiration_enabled,
|
||||
settings_peer_login_expiration as peer_login_expiration,
|
||||
settings_peer_inactivity_expiration_enabled as peer_inactivity_expiration_enabled,
|
||||
settings_peer_inactivity_expiration as peer_inactivity_expiration,
|
||||
settings_dns_domain as dns_domain,
|
||||
settings_ipv6_enabled_groups as ipv6_enabled_groups,
|
||||
settings_routing_peer_dns_resolution_enabled as routing_peer_dns_resolution_enabled,
|
||||
settings_lazy_connection_enabled as lazy_connection_enabled,
|
||||
settings_auto_update_version as auto_update_version,
|
||||
settings_auto_update_always as auto_update_always,
|
||||
settings_metrics_push_enabled as metrics_push_enabled
|
||||
from accounts
|
||||
where id=$1
|
||||
`
|
||||
)
|
||||
|
||||
func (pgc *PgStoreConn) GetAccountSettings(ctx context.Context, accountId string) (nmdata.AccountSettingsInfo, error) {
|
||||
rows, err := pgc.Conn.Query(ctx, GetAccountSettingsQuery, accountId)
|
||||
if err != nil {
|
||||
return nmdata.AccountSettingsInfo{}, err
|
||||
}
|
||||
|
||||
settings, err := pgx.CollectOneRow(rows, pgx.RowToStructByName[networkmapdb.Account])
|
||||
if err != nil {
|
||||
return nmdata.AccountSettingsInfo{}, err
|
||||
}
|
||||
|
||||
settingsInfo := nmdata.AccountSettingsInfo{
|
||||
PeerLoginExpirationEnabled: settings.PeerLoginExpirationEnabled.Bool,
|
||||
PeerLoginExpiration: time.Duration(settings.PeerLoginExpiration.Int64),
|
||||
PeerInactivityExpirationEnabled: settings.PeerInactivityExpirationEnabled.Bool,
|
||||
PeerInactivityExpiration: time.Duration(settings.PeerInactivityExpiration.Int64),
|
||||
DNSDomain: settings.DNSDomain.String,
|
||||
RoutingPeerDNSResolutionEnabled: settings.RoutingPeerDNSResolutionEnabled.Bool,
|
||||
LazyConnectionEnabled: settings.LazyConnectionEnabled.Bool,
|
||||
AutoUpdateVersion: settings.AutoUpdateVersion.String,
|
||||
AutoUpdateAlways: settings.AutoUpdateAlways.Bool,
|
||||
MetricsPushEnabled: settings.MetricsPushEnabled.Bool,
|
||||
}
|
||||
if settings.IPv6EnabledGroups != nil {
|
||||
if err := json.Unmarshal(settings.IPv6EnabledGroups, &settingsInfo.IPv6EnabledGroups); err != nil {
|
||||
return nmdata.AccountSettingsInfo{}, err
|
||||
}
|
||||
}
|
||||
|
||||
return settingsInfo, nil
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package networkmap_pgsql
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap"
|
||||
)
|
||||
|
||||
const (
|
||||
GetAccountZonesQuery = `
|
||||
select zones.id as id, domain, not enable_search_domain as search_domain_disabled, distribution_groups,
|
||||
r.name as record_name, r.type as record_type, 'IN' record_class, r.ttl as record_ttl, r.content as record_rdata
|
||||
from zones
|
||||
left join records as r on r.zone_id = zones.id
|
||||
where zones.account_id=$1
|
||||
`
|
||||
)
|
||||
|
||||
func (pgc *PgStoreConn) GetAppliedZoneCandidates(ctx context.Context, accountId string) ([]networkmap.AppliedZoneCandidate, error) {
|
||||
rows, err := pgc.Conn.Query(ctx, GetAccountZonesQuery, accountId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
zones, err := pgx.CollectRows(rows, pgx.RowToStructByName[networkmapdb.Zone])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return networkmapdb.ZonesToAppliedZoneCandidates(zones)
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
package networkmap_pgsql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
|
||||
)
|
||||
|
||||
const (
|
||||
GetDnsSettingsQuery = `
|
||||
select dns_settings_disabled_management_groups
|
||||
from accounts
|
||||
where id=$1
|
||||
`
|
||||
)
|
||||
|
||||
func (pgc *PgStoreConn) GetDnsSettings(ctx context.Context, accountId string) (nmdata.DNSSettings, error) {
|
||||
rows, err := pgc.Conn.Query(ctx, GetDnsSettingsQuery, accountId)
|
||||
if err != nil {
|
||||
return nmdata.DNSSettings{}, err
|
||||
}
|
||||
|
||||
return pgx.CollectOneRow(rows, rowToDnsSettings)
|
||||
}
|
||||
|
||||
func rowToDnsSettings(row pgx.CollectableRow) (nmdata.DNSSettings, error) {
|
||||
var value nmdata.DNSSettings
|
||||
var settings json.RawMessage
|
||||
|
||||
if err := row.Scan(&settings); err != nil {
|
||||
return value, err
|
||||
}
|
||||
|
||||
if settings == nil {
|
||||
return nmdata.DNSSettings{}, nil
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(settings, &value.DisabledManagementGroups); err != nil {
|
||||
return value, err
|
||||
}
|
||||
|
||||
return value, nil
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package networkmap_pgsql
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
|
||||
)
|
||||
|
||||
const (
|
||||
GetDomainsQuery = `
|
||||
select domain, target_cluster
|
||||
from domains
|
||||
where account_id=$1 and domain<>'' and target_cluster<>''
|
||||
`
|
||||
)
|
||||
|
||||
func (pgc *PgStoreConn) GetDomains(ctx context.Context, accountId string) ([]networkmapdb.Domain, error) {
|
||||
rows, err := pgc.Conn.Query(ctx, GetDomainsQuery, accountId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return pgx.CollectRows(rows, pgx.RowToStructByName[networkmapdb.Domain])
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
package networkmap_pgsql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
|
||||
)
|
||||
|
||||
const (
|
||||
GetGroupsQuery = `
|
||||
select id, name, public_id, resources,
|
||||
(
|
||||
select array_agg(group_peers.peer_id)
|
||||
from group_peers
|
||||
where group_peers.group_id = groups.id and group_peers.account_id=$1
|
||||
) as peers
|
||||
from groups where account_id=$1
|
||||
`
|
||||
)
|
||||
|
||||
// we also return a resource-to-group index.
|
||||
// an alternative is to add json indexes, query this directly. Not sure how expensive
|
||||
// json indexes are. TODO (dmitri) verify and maybe change the implementation here.
|
||||
func (pgc *PgStoreConn) GetGroups(ctx context.Context, accountId string) ([]nmdata.Group, map[string]map[string]any, error) {
|
||||
rows, err := pgc.Conn.Query(ctx, GetGroupsQuery, accountId)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
groups, err := pgx.CollectRows(rows, pgx.RowToStructByName[group])
|
||||
toret := make([]nmdata.Group, 0, len(groups))
|
||||
resourceToGroupIdx := make(map[string]map[string]any)
|
||||
|
||||
for _, g := range groups {
|
||||
dg := nmdata.Group{}
|
||||
err := networkmapdb.FromSqlTypesToSharedTypes(
|
||||
reflect.ValueOf(&g), reflect.ValueOf(&dg))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
toret = append(toret, dg)
|
||||
for _, resource := range dg.Resources {
|
||||
if _, ok := resourceToGroupIdx[resource.ID]; !ok {
|
||||
resourceToGroupIdx[resource.ID] = make(map[string]any)
|
||||
}
|
||||
resourceToGroupIdx[resource.ID][g.ID] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
return toret, resourceToGroupIdx, err
|
||||
}
|
||||
|
||||
type group struct {
|
||||
ID string
|
||||
Name sql.NullString
|
||||
PublicID sql.NullString
|
||||
Resources json.RawMessage
|
||||
Peers []string
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
package networkmap_pgsql
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
|
||||
)
|
||||
|
||||
const (
|
||||
GetNameserversQuery = `
|
||||
select id, public_id, name, description, name_servers, groups, "primary", domains, enabled, search_domains_enabled
|
||||
from name_server_groups
|
||||
where account_id=$1
|
||||
`
|
||||
)
|
||||
|
||||
func (pgc *PgStoreConn) GetNameServerGroups(ctx context.Context, accountId string) ([]nmdata.NameServerGroup, error) {
|
||||
rows, err := pgc.Conn.Query(ctx, GetNameserversQuery, accountId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nsgroups, err := pgx.CollectRows(rows, pgx.RowToStructByName[networkmapdb.NameserverGroup])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return networkmapdb.ConvertAllToSharedTypes[networkmapdb.NameserverGroup, nmdata.NameServerGroup](nsgroups)
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package networkmap_pgsql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
|
||||
)
|
||||
|
||||
const (
|
||||
GetNetworkQuery = `
|
||||
select network_identifier as identifier, network_net as net, network_net_v6 as net_v6, network_dns as dns, network_serial as serial
|
||||
from accounts
|
||||
where id=$1
|
||||
`
|
||||
)
|
||||
|
||||
func (pgc *PgStoreConn) GetNetwork(ctx context.Context, accountId string) (nmdata.Network, error) {
|
||||
rows, err := pgc.Conn.Query(ctx, GetNetworkQuery, accountId)
|
||||
if err != nil {
|
||||
return nmdata.Network{}, err
|
||||
}
|
||||
|
||||
n, err := pgx.CollectOneRow(rows, pgx.RowToStructByName[networkmapdb.AccountNetwork])
|
||||
if err != nil {
|
||||
return nmdata.Network{}, err
|
||||
}
|
||||
|
||||
toret := nmdata.Network{}
|
||||
err = networkmapdb.FromSqlTypesToSharedTypes(
|
||||
reflect.ValueOf(&n), reflect.ValueOf(&toret))
|
||||
if err != nil {
|
||||
return nmdata.Network{}, err
|
||||
}
|
||||
|
||||
return toret, nil
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
package networkmap_pgsql
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
|
||||
)
|
||||
|
||||
const (
|
||||
GetNetworkResourcesQuery = `
|
||||
select id, network_id, account_id, public_id, name, description, type, domain, prefix, enabled
|
||||
from network_resources
|
||||
where account_id=$1
|
||||
`
|
||||
)
|
||||
|
||||
func (pgc *PgStoreConn) GetNetworkResources(ctx context.Context, accountId string) ([]nmdata.NetworkResource, error) {
|
||||
rows, err := pgc.Conn.Query(ctx, GetNetworkResourcesQuery, accountId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
netresorces, err := pgx.CollectRows(rows, pgx.RowToStructByName[networkmapdb.Networkresource])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return networkmapdb.ConvertAllToSharedTypes[networkmapdb.Networkresource, nmdata.NetworkResource](netresorces)
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
package networkmap_pgsql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
|
||||
)
|
||||
|
||||
const (
|
||||
GetNetworkRouterQuery = `
|
||||
select public_id, peer, network_id, masquerade, metric, enabled, peer_groups,
|
||||
(
|
||||
select array_agg(group_peers.peer_id)
|
||||
from group_peers
|
||||
where group_peers.account_id=$1 and group_peers.group_id in (select json_array_elements_text(peer_groups::json))
|
||||
) as peers_via_groups
|
||||
from network_routers
|
||||
where account_id=$1
|
||||
`
|
||||
)
|
||||
|
||||
func (pgc *PgStoreConn) GetNetworkRouters(ctx context.Context, accountId string) (map[string]map[string]*nmdata.NetworkRouter, error) {
|
||||
rows, err := pgc.Conn.Query(ctx, GetNetworkRouterQuery, accountId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
routers, err := pgx.CollectRows(rows, pgx.RowToStructByName[networkrouter])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
toret := make(map[string]map[string]*nmdata.NetworkRouter)
|
||||
for _, router := range routers {
|
||||
if !router.Enabled.Bool {
|
||||
continue
|
||||
}
|
||||
|
||||
networkId := router.NetworkID.String
|
||||
if networkId == "" {
|
||||
return nil, fmt.Errorf("router with public_id %s doesn't have network_id set", router.PublicID.String)
|
||||
}
|
||||
|
||||
nmdatarouter := nmdata.NetworkRouter{}
|
||||
err := networkmapdb.FromSqlTypesToSharedTypes(reflect.ValueOf(&router), reflect.ValueOf(&nmdatarouter))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if toret[networkId] == nil {
|
||||
toret[networkId] = make(map[string]*nmdata.NetworkRouter)
|
||||
}
|
||||
if router.Peer.String != "" {
|
||||
toret[networkId][router.Peer.String] = &nmdatarouter
|
||||
continue
|
||||
}
|
||||
for _, peerId := range router.PeersViaGroups {
|
||||
toret[networkId][peerId] = &nmdatarouter
|
||||
}
|
||||
}
|
||||
|
||||
return toret, nil
|
||||
}
|
||||
|
||||
type networkrouter struct {
|
||||
PublicID sql.NullString
|
||||
NetworkID sql.NullString `nmap:"skip"`
|
||||
Peer sql.NullString `nmap:"skip"`
|
||||
PeerGroups json.RawMessage
|
||||
PeersViaGroups []string `nmap:"skip"`
|
||||
Masquerade sql.NullBool
|
||||
Metric sql.NullInt64
|
||||
Enabled sql.NullBool
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package networkmap_pgsql
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
|
||||
)
|
||||
|
||||
const (
|
||||
GetNetworksQuery = `
|
||||
select id, public_id
|
||||
from networks where account_id=$1
|
||||
`
|
||||
)
|
||||
|
||||
func (pgc *PgStoreConn) GetNetworkXIDToPublicIdMap(ctx context.Context, accountId string) (map[string]string, error) {
|
||||
rows, err := pgc.Conn.Query(ctx, GetNetworksQuery, accountId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
networks, err := pgx.CollectRows(rows, pgx.RowToStructByName[networkmapdb.Network])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
toret := make(map[string]string)
|
||||
for _, n := range networks {
|
||||
if n.PublicID.Valid {
|
||||
toret[n.ID] = n.PublicID.String
|
||||
}
|
||||
}
|
||||
|
||||
return toret, nil
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
package networkmap_pgsql
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
|
||||
)
|
||||
|
||||
const (
|
||||
GetPeersQuery = `
|
||||
select id, key, ssh_key, dns_label, extra_dns_labels, user_id, ssh_enabled, login_expiration_enabled, last_login, ip, ipv6,
|
||||
peer_status_requires_approval, peer_status_connected, proxy_meta_embedded, proxy_meta_cluster,
|
||||
meta_wt_version, meta_go_os, meta_os_version, meta_kernel_version, meta_network_addresses, meta_files, meta_capabilities, meta_flags, meta_sync_message_version,
|
||||
location_country_code, location_city_name, location_connection_ip
|
||||
from peers
|
||||
where account_id = $1
|
||||
`
|
||||
)
|
||||
|
||||
func (pgc *PgStoreConn) GetPeers(ctx context.Context, accountId string) ([]nmdata.Peer, map[string][]*nmdata.Peer, error) {
|
||||
rows, err := pgc.Conn.Query(ctx, GetPeersQuery, accountId)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
peers, err := pgx.CollectRows(rows, pgx.RowToStructByName[networkmapdb.Peer])
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return networkmapdb.ConvertToNmdataPeers(peers)
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
package networkmap_pgsql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
|
||||
)
|
||||
|
||||
const (
|
||||
pgMaxConnections = 30
|
||||
pgMinConnections = 1
|
||||
pgMaxConnLifetime = 60 * time.Minute
|
||||
pgHealthCheckPeriod = 1 * time.Minute
|
||||
)
|
||||
|
||||
var _ networkmapdb.NetworkMapDBStore = &PgStore{}
|
||||
|
||||
type PgStore struct {
|
||||
Pool *pgxpool.Pool
|
||||
}
|
||||
|
||||
type PgStoreConn struct {
|
||||
Conn pgInterface
|
||||
}
|
||||
|
||||
type pgInterface interface {
|
||||
Query(ctx context.Context, sql string, args ...any) (pgx.Rows, error)
|
||||
Exec(ctx context.Context, sql string, arguments ...any) (pgconn.CommandTag, error)
|
||||
}
|
||||
|
||||
var _ networkmapdb.NetworkMapDBStoreConn = &PgStoreConn{}
|
||||
|
||||
func NewPostgresqlStore(ctx context.Context, dsn string) (*PgStore, error) {
|
||||
pool, err := connectToPgDb(ctx, dsn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &PgStore{Pool: pool}, nil
|
||||
}
|
||||
|
||||
func (p *PgStore) UsingConnection(c *pgx.Conn) networkmapdb.NetworkMapDBStoreConn {
|
||||
return &PgStoreConn{Conn: c}
|
||||
}
|
||||
|
||||
func (p *PgStore) BeginTx(ctx context.Context) (networkmapdb.NetworkMapDBStoreConn, error) {
|
||||
tx, err := p.Pool.BeginTx(ctx, pgx.TxOptions{IsoLevel: pgx.RepeatableRead, AccessMode: pgx.ReadOnly})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &PgStoreConn{Conn: tx}, nil
|
||||
}
|
||||
|
||||
func (c *PgStoreConn) RollbackTx(ctx context.Context) error {
|
||||
tx, ok := c.Conn.(pgx.Tx)
|
||||
if !ok {
|
||||
return fmt.Errorf("expected an pgx.Tx got %s", reflect.TypeOf(c.Conn).Kind())
|
||||
}
|
||||
return tx.Rollback(ctx)
|
||||
}
|
||||
|
||||
func (c *PgStoreConn) CommitTx(ctx context.Context) error {
|
||||
tx, ok := c.Conn.(pgx.Tx)
|
||||
if !ok {
|
||||
return fmt.Errorf("expected an sql.Tx got %s", reflect.TypeOf(c.Conn).Kind())
|
||||
}
|
||||
return tx.Commit(ctx)
|
||||
}
|
||||
|
||||
func connectToPgDb(ctx context.Context, dsn string) (*pgxpool.Pool, error) {
|
||||
config, err := pgxpool.ParseConfig(dsn)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to parse database config: %w", err)
|
||||
}
|
||||
|
||||
config.MaxConns = pgMaxConnections
|
||||
config.MinConns = pgMinConnections
|
||||
config.MaxConnLifetime = pgMaxConnLifetime
|
||||
config.HealthCheckPeriod = pgHealthCheckPeriod
|
||||
|
||||
pool, err := pgxpool.NewWithConfig(ctx, config)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to create connection pool: %w", err)
|
||||
}
|
||||
|
||||
if err := pool.Ping(ctx); err != nil {
|
||||
pool.Close()
|
||||
return nil, fmt.Errorf("unable to ping database: %w", err)
|
||||
}
|
||||
|
||||
return pool, nil
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
package networkmap_pgsql
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
|
||||
)
|
||||
|
||||
const (
|
||||
GetPoliciesQuery = `
|
||||
select p.id, p.public_id, p.enabled, p.source_posture_checks, pr.enabled as rule_enabled, pr.action, pr.protocol, pr.bidirectional,
|
||||
pr.sources, pr.destinations, pr.source_resource, pr.destination_resource, pr.ports, pr.port_ranges,
|
||||
pr.authorized_groups, pr.authorized_user
|
||||
from policies as p
|
||||
left join policy_rules as pr on p.id = pr.policy_id
|
||||
where account_id=$1
|
||||
`
|
||||
)
|
||||
|
||||
func (pgc *PgStoreConn) GetPolicies(ctx context.Context, accountId string) ([]nmdata.Policy, map[string]map[string]any, map[string]map[string]any, error) {
|
||||
rows, err := pgc.Conn.Query(ctx, GetPoliciesQuery, accountId)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
policies, err := pgx.CollectRows(rows, pgx.RowToStructByName[networkmapdb.Policy])
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
return networkmapdb.ConvertToNmdataPolicy(policies)
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
package networkmap_pgsql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
|
||||
)
|
||||
|
||||
const (
|
||||
GetPostureChecksQuery = `
|
||||
select id, public_id, checks
|
||||
from posture_checks
|
||||
where account_id=$1
|
||||
`
|
||||
)
|
||||
|
||||
func (pgc *PgStoreConn) GetPostureChecks(ctx context.Context, accountId string) ([]nmdata.PostureChecks, map[string]string, error) {
|
||||
rows, err := pgc.Conn.Query(ctx, GetPostureChecksQuery, accountId)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
checks, err := pgx.CollectRows(rows, pgx.RowToStructByName[networkmapdb.PostureChecks])
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
toret := make([]nmdata.PostureChecks, 0, len(checks))
|
||||
idToPublicIDIdx := make(map[string]string)
|
||||
for _, c := range checks {
|
||||
checks := nmdata.PostureChecks{}
|
||||
err := networkmapdb.FromSqlTypesToSharedTypes(reflect.ValueOf(&c), reflect.ValueOf(&checks))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
toret = append(toret, checks)
|
||||
idToPublicIDIdx[checks.ID] = c.PublicID.String
|
||||
}
|
||||
|
||||
return toret, idToPublicIDIdx, nil
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package networkmap_pgsql
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
|
||||
)
|
||||
|
||||
const (
|
||||
GetRoutesQuery = `
|
||||
select id, account_id, public_id, network, domains, keep_route, net_id, description,
|
||||
peer, peer as peer_id, peer_groups, network_type, masquerade, metric, enabled,
|
||||
groups, access_control_groups, skip_auto_apply
|
||||
from routes
|
||||
where account_id=$1
|
||||
`
|
||||
)
|
||||
|
||||
func (pgc *PgStoreConn) GetRoutes(ctx context.Context, accountId string) ([]nmdata.Route, error) {
|
||||
rows, err := pgc.Conn.Query(ctx, GetRoutesQuery, accountId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
routes, err := pgx.CollectRows(rows, pgx.RowToStructByName[networkmapdb.Route])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return networkmapdb.ConvertAllToSharedTypes[networkmapdb.Route, nmdata.Route](routes)
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
package networkmap_pgsql
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
|
||||
)
|
||||
|
||||
const (
|
||||
GetServicesQuery = `
|
||||
select enabled, private, array (select json_array_elements_text(access_groups::json)) as access_groups, proxy_cluster, domain
|
||||
from services
|
||||
where account_id=$1
|
||||
`
|
||||
|
||||
GetProxyTargetedDomainResourcesQuery = `
|
||||
select t.target_id
|
||||
from targets as t
|
||||
join services as s on s.id = t.service_id
|
||||
where s.account_id=$1 and s.enabled and not coalesce(s.terminated, false)
|
||||
and t.enabled and t.target_type='domain' and t.target_id is not null
|
||||
`
|
||||
)
|
||||
|
||||
func (pgc *PgStoreConn) GetPrivateServices(ctx context.Context, accountId string) ([]networkmapdb.Service, error) {
|
||||
rows, err := pgc.Conn.Query(ctx, GetServicesQuery, accountId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return pgx.CollectRows(rows, pgx.RowToStructByName[networkmapdb.Service])
|
||||
}
|
||||
|
||||
func (pgc *PgStoreConn) GetProxyTargetedDomainResourceIDs(ctx context.Context, accountId string) (map[string]struct{}, error) {
|
||||
rows, err := pgc.Conn.Query(ctx, GetProxyTargetedDomainResourcesQuery, accountId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ids, err := pgx.CollectRows(rows, pgx.RowTo[string])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
toret := make(map[string]struct{}, len(ids))
|
||||
for _, id := range ids {
|
||||
toret[id] = struct{}{}
|
||||
}
|
||||
return toret, nil
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
package networkmap_pgsql
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
)
|
||||
|
||||
const (
|
||||
GetAllowedUserIdsQuery = `
|
||||
select id, array (select json_array_elements_text(auto_groups::json)) as auto_groups
|
||||
from users
|
||||
where account_id=$1 and not blocked and not is_service_user
|
||||
`
|
||||
|
||||
GetAllGroupIdQuery = `
|
||||
select array_agg(id) from groups
|
||||
where account_id=$1 and name='All'
|
||||
`
|
||||
)
|
||||
|
||||
func (pgc *PgStoreConn) GetAllowedUsers(ctx context.Context, accountId string) (map[string]struct{}, map[string][]string, error) {
|
||||
rows, err := pgc.Conn.Query(ctx, GetAllowedUserIdsQuery, accountId)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
users, err := pgx.CollectRows(rows, pgx.RowToStructByName[user])
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
rows, err = pgc.Conn.Query(ctx, GetAllGroupIdQuery, accountId)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
allGroupIds, err := pgx.CollectOneRow(rows, pgx.RowTo[[]string])
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
userIdIdx := make(map[string]struct{})
|
||||
groupIdToUserIds := make(map[string][]string)
|
||||
for _, user := range users {
|
||||
userIdIdx[user.ID] = struct{}{}
|
||||
for _, groupId := range user.AutoGroups {
|
||||
groupIdToUserIds[groupId] = append(groupIdToUserIds[groupId], user.ID)
|
||||
}
|
||||
for _, allgid := range allGroupIds {
|
||||
groupIdToUserIds[allgid] = append(groupIdToUserIds[allgid], user.ID)
|
||||
}
|
||||
}
|
||||
|
||||
return userIdIdx, groupIdToUserIds, nil
|
||||
}
|
||||
|
||||
type user struct {
|
||||
ID string
|
||||
AutoGroups []string
|
||||
}
|
||||
@@ -1,428 +0,0 @@
|
||||
package networkmapdb
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap"
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
|
||||
)
|
||||
|
||||
var ErrDnsUnsupportedRecordType = errors.New("unsupported record type")
|
||||
|
||||
// The order of fields in these structs is important.
|
||||
// Mapping of results of sqlite queries relies on the order
|
||||
// of the fields in these structs, when a query or a struct changes,
|
||||
// corresponding changes must be made to its counterpart.
|
||||
|
||||
type Account struct {
|
||||
PeerLoginExpirationEnabled sql.NullBool
|
||||
PeerLoginExpiration sql.NullInt64
|
||||
PeerInactivityExpirationEnabled sql.NullBool
|
||||
PeerInactivityExpiration sql.NullInt64
|
||||
DNSDomain sql.NullString
|
||||
IPv6EnabledGroups []byte `nmap:"json"`
|
||||
RoutingPeerDNSResolutionEnabled sql.NullBool
|
||||
LazyConnectionEnabled sql.NullBool
|
||||
AutoUpdateVersion sql.NullString
|
||||
AutoUpdateAlways sql.NullBool
|
||||
MetricsPushEnabled sql.NullBool
|
||||
}
|
||||
|
||||
type Domain struct {
|
||||
Domain sql.NullString
|
||||
TargetCluster sql.NullString
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
Enabled sql.NullBool
|
||||
Private sql.NullBool
|
||||
AccessGroups []string
|
||||
ProxyCluster sql.NullString
|
||||
Domain sql.NullString
|
||||
}
|
||||
|
||||
type Zone struct {
|
||||
Id string `nmap:"skip"`
|
||||
Domain sql.NullString
|
||||
SearchDomainDisabled sql.NullBool
|
||||
DistributionGroups []byte `nmap:"skip,json"`
|
||||
RecordName sql.NullString `nmap:"skip"`
|
||||
RecordType sql.NullString `nmap:"skip"`
|
||||
RecordClass sql.NullString `nmap:"skip"`
|
||||
RecordTTL sql.NullInt64 `nmap:"skip"`
|
||||
RecordRData sql.NullString `nmap:"skip"`
|
||||
}
|
||||
|
||||
type NameserverGroup struct {
|
||||
ID string
|
||||
PublicID sql.NullString
|
||||
Name sql.NullString
|
||||
Description sql.NullString
|
||||
NameServers []byte `nmap:"json"`
|
||||
Groups []byte `nmap:"json"`
|
||||
Primary sql.NullBool
|
||||
Domains []byte `nmap:"json"`
|
||||
Enabled sql.NullBool
|
||||
SearchDomainsEnabled sql.NullBool
|
||||
}
|
||||
|
||||
type Networkresource struct {
|
||||
ID string
|
||||
NetworkID sql.NullString
|
||||
AccountID sql.NullString
|
||||
PublicID sql.NullString
|
||||
Name sql.NullString
|
||||
Description sql.NullString
|
||||
Type sql.NullString
|
||||
Domain sql.NullString
|
||||
Prefix []byte `nmap:"json"`
|
||||
Enabled sql.NullBool
|
||||
}
|
||||
|
||||
type AccountNetwork struct {
|
||||
Identifier sql.NullString
|
||||
Net []byte `nmap:"json"`
|
||||
NetV6 []byte `nmap:"json"`
|
||||
Dns sql.NullString
|
||||
Serial sql.NullInt64
|
||||
}
|
||||
|
||||
type Network struct {
|
||||
ID string
|
||||
PublicID sql.NullString
|
||||
}
|
||||
|
||||
type Policy struct {
|
||||
ID string
|
||||
PublicID sql.NullString
|
||||
Enabled sql.NullBool
|
||||
SourcePostureChecks []byte `nmap:"json"`
|
||||
RuleEnabled sql.NullBool `nmap:"skip"`
|
||||
Action sql.NullString `nmap:"skip"`
|
||||
Protocol sql.NullString `nmap:"skip"`
|
||||
Bidirectional sql.NullBool `nmap:"skip"`
|
||||
Sources []byte `nmap:"skip,json"`
|
||||
Destinations []byte `nmap:"skip,json"`
|
||||
SourceResource []byte `nmap:"skip,json"`
|
||||
DestinationResource []byte `nmap:"skip,json"`
|
||||
Ports []byte `nmap:"skip,json"`
|
||||
PortRanges []byte `nmap:"skip,json"`
|
||||
AuthorizedGroups []byte `nmap:"skip,json"`
|
||||
AuthorizedUser sql.NullString `nmap:"skip"`
|
||||
}
|
||||
|
||||
// Depending on db interface LastLogin contains time in different formats:
|
||||
// for sqlite/sql.NullTime the time in UTC
|
||||
// for pgx the time is in the local timezone
|
||||
// TODO add support for creating struct fields from denormalized fields
|
||||
type Peer struct {
|
||||
ID string
|
||||
Key sql.NullString
|
||||
SSHKey sql.NullString
|
||||
DNSLabel sql.NullString
|
||||
ExtraDNSLabels []byte `nmap:"json"`
|
||||
UserID sql.NullString
|
||||
SSHEnabled sql.NullBool
|
||||
LoginExpirationEnabled sql.NullBool
|
||||
LastLogin sql.NullTime
|
||||
IP []byte `nmap:"json"`
|
||||
IPv6 []byte `nmap:"json"`
|
||||
PeerStatusRequiresApproval sql.NullBool `nmap:"map_to:RequiresApproval"`
|
||||
PeerStatusConnected sql.NullBool `nmap:"skip"`
|
||||
ProxyMetaEmbedded sql.NullBool `nmap:"skip"`
|
||||
ProxyMetaCluster sql.NullString `nmap:"skip"`
|
||||
MetaWtVersion sql.NullString `nmap:"skip"`
|
||||
MetaGoOS sql.NullString `nmap:"skip"`
|
||||
MetaOSVersion sql.NullString `nmap:"skip"`
|
||||
MetaKernelVersion sql.NullString `nmap:"skip"`
|
||||
MetaNetworkAddresses []byte `nmap:"skip,json"`
|
||||
MetaFiles []byte `nmap:"skip,json"`
|
||||
MetaCapabilities []byte `nmap:"skip,json"`
|
||||
MetaFlags []byte `nmap:"skip,json"`
|
||||
MetaSyncMessageVersion sql.NullInt64 `nmap:"skip"`
|
||||
LocationCountryCode sql.NullString `nmap:"skip"`
|
||||
LocationCityName sql.NullString `nmap:"skip"`
|
||||
LocationConnectionIp []byte `nmap:"skip,json"`
|
||||
}
|
||||
|
||||
type PostureChecks struct {
|
||||
ID string
|
||||
PublicID sql.NullString `nmap:"skip"`
|
||||
Checks []byte `nmap:"json"`
|
||||
}
|
||||
|
||||
type Route struct {
|
||||
ID string
|
||||
AccountID sql.NullString
|
||||
PublicID sql.NullString
|
||||
Network []byte `nmap:"json"`
|
||||
Domains []byte `nmap:"json"`
|
||||
KeepRoute sql.NullBool
|
||||
NetID sql.NullString
|
||||
Description sql.NullString
|
||||
Peer sql.NullString
|
||||
PeerID sql.NullString
|
||||
PeerGroups []byte `nmap:"json"`
|
||||
NetworkType sql.NullInt64
|
||||
Masquerade sql.NullBool
|
||||
Metric sql.NullInt64
|
||||
Enabled sql.NullBool
|
||||
Groups []byte `nmap:"json"`
|
||||
AccessControlGroups []byte `nmap:"json"`
|
||||
SkipAutoApply sql.NullBool
|
||||
}
|
||||
|
||||
func RecordTypeAndRdata(t, rdata string) (int, string, error) {
|
||||
switch t {
|
||||
case "A":
|
||||
return int(dns.TypeA), rdata, nil
|
||||
case "AAAA":
|
||||
return int(dns.TypeAAAA), rdata, nil
|
||||
case "CNAME":
|
||||
return int(dns.TypeCNAME), dns.Fqdn(rdata), nil
|
||||
default:
|
||||
return 0, "", fmt.Errorf("record type: %s %w", t, ErrDnsUnsupportedRecordType)
|
||||
}
|
||||
}
|
||||
|
||||
func ZonesToAppliedZoneCandidates(zones []Zone) ([]networkmap.AppliedZoneCandidate, error) {
|
||||
toret := make([]networkmap.AppliedZoneCandidate, 0, len(zones))
|
||||
currentZoneId := ""
|
||||
for _, z := range zones {
|
||||
if !z.RecordType.Valid {
|
||||
continue
|
||||
}
|
||||
|
||||
zone := nmdata.CustomZone{}
|
||||
err := FromSqlTypesToSharedTypes(
|
||||
reflect.ValueOf(&z), reflect.ValueOf(&zone))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var distributionGroups []string
|
||||
if err := json.Unmarshal(z.DistributionGroups, &distributionGroups); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if z.Id != currentZoneId {
|
||||
zone.Records = []nmdata.SimpleRecord{}
|
||||
toret = append(toret, AppliedZoneCandidateFromZone(zone, distributionGroups))
|
||||
currentZoneId = z.Id
|
||||
}
|
||||
|
||||
rtype, rdata, err := RecordTypeAndRdata(z.RecordType.String, z.RecordRData.String)
|
||||
if err != nil {
|
||||
if errors.Is(err, ErrDnsUnsupportedRecordType) {
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
lastZone := &toret[len(toret)-1]
|
||||
lastZone.Zone.Records = append(lastZone.Zone.Records, nmdata.SimpleRecord{
|
||||
Name: z.RecordName.String,
|
||||
Class: z.RecordClass.String,
|
||||
TTL: int(z.RecordTTL.Int64),
|
||||
RData: rdata,
|
||||
Type: rtype,
|
||||
})
|
||||
}
|
||||
return toret, nil
|
||||
}
|
||||
|
||||
func AppliedZoneCandidateFromZone(z nmdata.CustomZone, distributionGroups []string) networkmap.AppliedZoneCandidate {
|
||||
return networkmap.AppliedZoneCandidate{
|
||||
DistributionGroups: distributionGroups,
|
||||
Zone: z,
|
||||
}
|
||||
}
|
||||
|
||||
func ConvertToNmdataPeers(peers []Peer) ([]nmdata.Peer, map[string][]*nmdata.Peer, error) {
|
||||
toret := make([]nmdata.Peer, 0, len(peers))
|
||||
clusterToPeerIdx := make(map[string][]*nmdata.Peer)
|
||||
for _, p := range peers {
|
||||
dp := nmdata.Peer{}
|
||||
err := FromSqlTypesToSharedTypes(
|
||||
reflect.ValueOf(&p), reflect.ValueOf(&dp))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if p.ProxyMetaEmbedded.Valid {
|
||||
dp.ProxyMeta.Embedded = p.ProxyMetaEmbedded.Bool
|
||||
}
|
||||
// This is only used to build private service candidates, not connected peers are skipped
|
||||
if dp.ProxyMeta.Embedded && p.PeerStatusConnected.Bool {
|
||||
clusterToPeerIdx[p.ProxyMetaCluster.String] = append(clusterToPeerIdx[p.ProxyMetaCluster.String], &dp)
|
||||
}
|
||||
if p.MetaWtVersion.Valid {
|
||||
dp.Meta.WtVersion = p.MetaWtVersion.String
|
||||
}
|
||||
if p.MetaSyncMessageVersion.Valid {
|
||||
dp.Meta.SyncMessageVersion = int(p.MetaSyncMessageVersion.Int64)
|
||||
}
|
||||
if p.MetaGoOS.Valid {
|
||||
dp.Meta.GoOS = p.MetaGoOS.String
|
||||
}
|
||||
if p.MetaOSVersion.Valid {
|
||||
dp.Meta.OSVersion = p.MetaOSVersion.String
|
||||
}
|
||||
if p.MetaKernelVersion.Valid {
|
||||
dp.Meta.KernelVersion = p.MetaKernelVersion.String
|
||||
}
|
||||
if p.LocationCountryCode.Valid {
|
||||
dp.Location.CountryCode = p.LocationCountryCode.String
|
||||
}
|
||||
if p.LocationCityName.Valid {
|
||||
dp.Location.CityName = p.LocationCityName.String
|
||||
}
|
||||
if p.LocationConnectionIp != nil {
|
||||
err := json.Unmarshal(p.LocationConnectionIp, &dp.Location.ConnectionIP)
|
||||
if err != nil {
|
||||
return toret, nil, err
|
||||
}
|
||||
}
|
||||
if p.MetaFiles != nil {
|
||||
err := json.Unmarshal(p.MetaFiles, &dp.Meta.Files)
|
||||
if err != nil {
|
||||
return toret, nil, err
|
||||
}
|
||||
}
|
||||
if p.MetaCapabilities != nil {
|
||||
err := json.Unmarshal(p.MetaCapabilities, &dp.Meta.Capabilities)
|
||||
if err != nil {
|
||||
return toret, nil, err
|
||||
}
|
||||
}
|
||||
if p.MetaFlags != nil {
|
||||
err := json.Unmarshal(p.MetaFlags, &dp.Meta.Flags)
|
||||
if err != nil {
|
||||
return toret, nil, err
|
||||
}
|
||||
}
|
||||
if p.MetaNetworkAddresses != nil {
|
||||
err := json.Unmarshal(p.MetaNetworkAddresses, &dp.Meta.NetworkAddresses)
|
||||
if err != nil {
|
||||
return toret, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
toret = append(toret, dp)
|
||||
}
|
||||
|
||||
return toret, clusterToPeerIdx, nil
|
||||
}
|
||||
|
||||
func ConvertToNmdataPolicy(policies []Policy) ([]nmdata.Policy, map[string]map[string]any, map[string]map[string]any, error) {
|
||||
toret := make([]nmdata.Policy, 0, len(policies))
|
||||
policyToDestinationResourceIdx := make(map[string]map[string]any) // policy id to destination resource id
|
||||
policyToDestinationGroupIdx := make(map[string]map[string]any) // policy id to destination group id
|
||||
for _, p := range policies {
|
||||
policy := nmdata.Policy{}
|
||||
err := FromSqlTypesToSharedTypes(
|
||||
reflect.ValueOf(&p), reflect.ValueOf(&policy))
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
var policyRule *nmdata.PolicyRule
|
||||
pr := func() *nmdata.PolicyRule {
|
||||
if policyRule != nil {
|
||||
return policyRule
|
||||
}
|
||||
|
||||
policyRule = &nmdata.PolicyRule{}
|
||||
return policyRule
|
||||
}
|
||||
|
||||
if p.RuleEnabled.Valid {
|
||||
pr().Enabled = p.RuleEnabled.Bool
|
||||
}
|
||||
if p.Action.Valid {
|
||||
pr().Action = p.Action.String
|
||||
}
|
||||
if p.Protocol.Valid {
|
||||
pr().Protocol = p.Protocol.String
|
||||
}
|
||||
if p.Bidirectional.Valid {
|
||||
pr().Bidirectional = p.Bidirectional.Bool
|
||||
}
|
||||
if len(p.Sources) > 0 {
|
||||
err := json.Unmarshal([]byte(p.Sources), &pr().Sources)
|
||||
if err != nil {
|
||||
return toret, nil, nil, err
|
||||
}
|
||||
}
|
||||
if len(p.Destinations) > 0 {
|
||||
err := json.Unmarshal([]byte(p.Destinations), &pr().Destinations)
|
||||
if err != nil {
|
||||
return toret, nil, nil, err
|
||||
}
|
||||
|
||||
if p.RuleEnabled.Valid && p.RuleEnabled.Bool {
|
||||
for _, dst := range pr().Destinations {
|
||||
if _, ok := policyToDestinationGroupIdx[p.ID]; !ok {
|
||||
policyToDestinationGroupIdx[p.ID] = make(map[string]any)
|
||||
}
|
||||
policyToDestinationGroupIdx[p.ID][dst] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(p.SourceResource) > 0 {
|
||||
err := json.Unmarshal([]byte(p.SourceResource), &pr().SourceResource)
|
||||
if err != nil {
|
||||
return toret, nil, nil, err
|
||||
}
|
||||
}
|
||||
if len(p.DestinationResource) > 0 {
|
||||
err := json.Unmarshal([]byte(p.DestinationResource), &pr().DestinationResource)
|
||||
if err != nil {
|
||||
return toret, nil, nil, err
|
||||
}
|
||||
|
||||
if p.RuleEnabled.Valid && p.RuleEnabled.Bool {
|
||||
if _, ok := policyToDestinationResourceIdx[p.ID]; !ok {
|
||||
policyToDestinationResourceIdx[p.ID] = make(map[string]any)
|
||||
}
|
||||
policyToDestinationResourceIdx[p.ID][pr().DestinationResource.ID] = struct{}{}
|
||||
}
|
||||
}
|
||||
if len(p.Ports) > 0 {
|
||||
err := json.Unmarshal([]byte(p.Ports), &pr().Ports)
|
||||
if err != nil {
|
||||
return toret, nil, nil, err
|
||||
}
|
||||
}
|
||||
if len(p.PortRanges) > 0 {
|
||||
err := json.Unmarshal([]byte(p.PortRanges), &pr().PortRanges)
|
||||
if err != nil {
|
||||
return toret, nil, nil, err
|
||||
}
|
||||
}
|
||||
if len(p.AuthorizedGroups) > 0 {
|
||||
err := json.Unmarshal([]byte(p.AuthorizedGroups), &pr().AuthorizedGroups)
|
||||
if err != nil {
|
||||
return toret, nil, nil, err
|
||||
}
|
||||
}
|
||||
if p.AuthorizedUser.Valid {
|
||||
pr().AuthorizedUser = p.AuthorizedUser.String
|
||||
}
|
||||
|
||||
if policyRule != nil {
|
||||
policyRule.ID = p.ID
|
||||
policyRule.PolicyID = p.ID
|
||||
policy.Rules = []*nmdata.PolicyRule{policyRule}
|
||||
}
|
||||
|
||||
toret = append(toret, policy)
|
||||
}
|
||||
|
||||
return toret, policyToDestinationResourceIdx, policyToDestinationGroupIdx, nil
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package networkmapdb
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestRecordTypeAndRdata(t *testing.T) {
|
||||
var tests = []struct {
|
||||
recordType string
|
||||
expectedRecordType int
|
||||
rdata string
|
||||
expectedRdata string
|
||||
expectedErr error
|
||||
}{
|
||||
{recordType: "A", expectedRecordType: 1, rdata: "test.com", expectedRdata: "test.com", expectedErr: nil},
|
||||
{recordType: "AAAA", expectedRecordType: 28, rdata: "test.com", expectedRdata: "test.com", expectedErr: nil},
|
||||
{recordType: "CNAME", expectedRecordType: 5, rdata: "test.com", expectedRdata: "test.com.", expectedErr: nil},
|
||||
{recordType: "CNAME", expectedRecordType: 5, rdata: "test.com.", expectedRdata: "test.com.", expectedErr: nil},
|
||||
{recordType: "TypeMX", expectedErr: ErrDnsUnsupportedRecordType},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.recordType, func(t *testing.T) {
|
||||
recordType, rdata, err := RecordTypeAndRdata(tt.recordType, tt.rdata)
|
||||
|
||||
if tt.expectedErr != nil {
|
||||
assert.ErrorIs(t, err, ErrDnsUnsupportedRecordType)
|
||||
return
|
||||
}
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, recordType, tt.expectedRecordType)
|
||||
assert.Equal(t, rdata, tt.expectedRdata)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,253 +0,0 @@
|
||||
package networkmapdb
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNullStringSupport(t *testing.T) {
|
||||
src := withNullString{Name: sql.NullString{String: "string", Valid: true}}
|
||||
dst := withString{}
|
||||
assert.NoError(t, FromSqlTypesToSharedTypes(reflect.ValueOf(&src), reflect.ValueOf(&dst)))
|
||||
assert.Equal(t, withString{Name: "string"}, dst)
|
||||
|
||||
src = withNullString{Name: sql.NullString{Valid: false}}
|
||||
dst = withString{}
|
||||
assert.NoError(t, FromSqlTypesToSharedTypes(reflect.ValueOf(&src), reflect.ValueOf(&dst)))
|
||||
assert.Equal(t, withString{Name: ""}, dst)
|
||||
}
|
||||
|
||||
func TestNullBoolSupport(t *testing.T) {
|
||||
src := withNullBool{TrueOrFalse: sql.NullBool{Bool: true, Valid: true}}
|
||||
dst := withBool{}
|
||||
assert.NoError(t, FromSqlTypesToSharedTypes(reflect.ValueOf(&src), reflect.ValueOf(&dst)))
|
||||
assert.Equal(t, withBool{TrueOrFalse: true}, dst)
|
||||
|
||||
}
|
||||
|
||||
func TestRawJsonSupport(t *testing.T) {
|
||||
jb, _ := json.Marshal(embeddedS{Name: "blob-name", SomeField: 1})
|
||||
src := withRawJson{Blob: json.RawMessage(jb)}
|
||||
dst := fromJson{}
|
||||
assert.NoError(t, FromSqlTypesToSharedTypes(reflect.ValueOf(&src), reflect.ValueOf(&dst)))
|
||||
assert.Equal(t, fromJson{Blob: embeddedS{Name: "blob-name", SomeField: 1}}, dst)
|
||||
|
||||
src1 := withRawJson{}
|
||||
dst1 := fromJson{}
|
||||
assert.NoError(t, FromSqlTypesToSharedTypes(reflect.ValueOf(&src1), reflect.ValueOf(&dst1)))
|
||||
assert.Equal(t, fromJson{}, dst1)
|
||||
}
|
||||
|
||||
func TestShouldSkipTag(t *testing.T) {
|
||||
src5 := withSkipTag{Field: "shouldskip"}
|
||||
dst5 := emptySkipTagTarget{}
|
||||
assert.NoError(t, FromSqlTypesToSharedTypes(reflect.ValueOf(&src5), reflect.ValueOf(&dst5)))
|
||||
assert.Equal(t, emptySkipTagTarget{}, dst5)
|
||||
|
||||
}
|
||||
|
||||
func TestMapToTag(t *testing.T) {
|
||||
src6 := withMapToTag{Field: "fieldvalue"}
|
||||
dst6 := mapToTagTarget{}
|
||||
assert.NoError(t, FromSqlTypesToSharedTypes(reflect.ValueOf(&src6), reflect.ValueOf(&dst6)))
|
||||
assert.Equal(t, mapToTagTarget{AnotherField: "fieldvalue"}, dst6)
|
||||
}
|
||||
|
||||
func TestNullableInt64Support(t *testing.T) {
|
||||
src := withInt64{Field: sql.NullInt64{Int64: int64(1), Valid: true}}
|
||||
dst := int64Target{}
|
||||
assert.NoError(t, FromSqlTypesToSharedTypes(reflect.ValueOf(&src), reflect.ValueOf(&dst)))
|
||||
assert.Equal(t, int64Target{Field: 1}, dst)
|
||||
}
|
||||
|
||||
func TestNullableTimeSupport(t *testing.T) {
|
||||
now := time.Now()
|
||||
src := withNullableTime{Field: sql.NullTime{Time: now, Valid: true}}
|
||||
dst := nullableTimeTarget{}
|
||||
assert.NoError(t, FromSqlTypesToSharedTypes(reflect.ValueOf(&src), reflect.ValueOf(&dst)))
|
||||
assert.Equal(t, nullableTimeTarget{Field: now}, dst)
|
||||
}
|
||||
|
||||
func TestNullableTimePointerSupport(t *testing.T) {
|
||||
now := time.Now()
|
||||
src := withNullableTime{Field: sql.NullTime{Time: now, Valid: true}}
|
||||
dst := nullableTimePointerTarget{}
|
||||
assert.NoError(t, FromSqlTypesToSharedTypes(reflect.ValueOf(&src), reflect.ValueOf(&dst)))
|
||||
assert.Equal(t, nullableTimePointerTarget{Field: &now}, dst)
|
||||
}
|
||||
|
||||
func TestStringSLiceSupport(t *testing.T) {
|
||||
src := withStringSlice{Field: []string{"one"}}
|
||||
dst := withStringSlice{}
|
||||
assert.NoError(t, FromSqlTypesToSharedTypes(reflect.ValueOf(&src), reflect.ValueOf(&dst)))
|
||||
assert.Equal(t, withStringSlice{Field: []string{"one"}}, dst)
|
||||
}
|
||||
|
||||
func TestNullStringSLiceSupport(t *testing.T) {
|
||||
src := withStringSlice{}
|
||||
dst := withStringSlice{}
|
||||
assert.NoError(t, FromSqlTypesToSharedTypes(reflect.ValueOf(&src), reflect.ValueOf(&dst)))
|
||||
assert.Equal(t, withStringSlice{}, dst)
|
||||
}
|
||||
|
||||
func TestWithMultipleFields(t *testing.T) {
|
||||
now := time.Now()
|
||||
src := withMultipleFields{
|
||||
Field1: sql.NullString{String: "aaa", Valid: true},
|
||||
Field2: sql.NullBool{Bool: true, Valid: true},
|
||||
Field3: sql.NullTime{Time: now, Valid: true},
|
||||
Field4: sql.NullInt64{Int64: 1, Valid: true},
|
||||
Field5: "another",
|
||||
}
|
||||
dst := multipleFieldsTarget{}
|
||||
assert.NoError(t, FromSqlTypesToSharedTypes(reflect.ValueOf(&src), reflect.ValueOf(&dst)))
|
||||
assert.Equal(t, multipleFieldsTarget{
|
||||
Field1: "aaa",
|
||||
Field2: true,
|
||||
Field3: now,
|
||||
Field4: 1,
|
||||
Field5: "another",
|
||||
}, dst)
|
||||
}
|
||||
|
||||
func TestEmptyPublicIdsFilled(t *testing.T) {
|
||||
src := withEmptyPublicIds{}
|
||||
dst := emptyPublicIdTarget{}
|
||||
assert.NoError(t, FromSqlTypesToSharedTypes(reflect.ValueOf(&src), reflect.ValueOf(&dst)))
|
||||
assert.NotEmpty(t, dst.PublicID)
|
||||
assert.NotEmpty(t, dst.PublicId)
|
||||
}
|
||||
|
||||
// only []byte and []uint8 slices with "json" tag are being parsed
|
||||
func TestByteSliceSupport(t *testing.T) {
|
||||
src := withByteSlice{
|
||||
Field: []byte("[\"one\",\"two\",\"three\"]"),
|
||||
}
|
||||
dst := byteSliceTarget{}
|
||||
assert.NoError(t, FromSqlTypesToSharedTypes(reflect.ValueOf(&src), reflect.ValueOf(&dst)))
|
||||
assert.Equal(t, []string{"one", "two", "three"}, dst.Field)
|
||||
}
|
||||
|
||||
func TestUint8SliceSupport(t *testing.T) {
|
||||
src := withUint8Slice{
|
||||
Field: []uint8("[\"one\",\"two\",\"three\"]"),
|
||||
}
|
||||
dst := uint8SliceTarget{}
|
||||
assert.NoError(t, FromSqlTypesToSharedTypes(reflect.ValueOf(&src), reflect.ValueOf(&dst)))
|
||||
assert.Equal(t, []string{"one", "two", "three"}, dst.Field)
|
||||
}
|
||||
|
||||
type withNullString struct {
|
||||
Name sql.NullString
|
||||
}
|
||||
|
||||
type withString struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
type withMultipleFields struct {
|
||||
Field1 sql.NullString
|
||||
Field2 sql.NullBool
|
||||
Field3 sql.NullTime
|
||||
Field4 sql.NullInt64
|
||||
Field5 string
|
||||
}
|
||||
|
||||
type multipleFieldsTarget struct {
|
||||
Field1 string
|
||||
Field2 bool
|
||||
Field3 time.Time
|
||||
Field4 int64
|
||||
Field5 string
|
||||
}
|
||||
|
||||
type withNullBool struct {
|
||||
TrueOrFalse sql.NullBool
|
||||
}
|
||||
|
||||
type withBool struct {
|
||||
TrueOrFalse bool
|
||||
}
|
||||
|
||||
type withRawJson struct {
|
||||
Blob json.RawMessage
|
||||
}
|
||||
|
||||
type embeddedS struct {
|
||||
Name string
|
||||
SomeField int
|
||||
}
|
||||
type fromJson struct {
|
||||
Blob embeddedS
|
||||
}
|
||||
|
||||
type withSkipTag struct {
|
||||
Field string `nmap:"skip"`
|
||||
}
|
||||
|
||||
type emptySkipTagTarget struct {
|
||||
Field string
|
||||
}
|
||||
|
||||
type withMapToTag struct {
|
||||
Field string `nmap:"map_to:AnotherField"`
|
||||
}
|
||||
|
||||
type mapToTagTarget struct {
|
||||
AnotherField string
|
||||
}
|
||||
|
||||
type withInt64 struct {
|
||||
Field sql.NullInt64
|
||||
}
|
||||
|
||||
type int64Target struct {
|
||||
Field int
|
||||
}
|
||||
|
||||
type withNullableTime struct {
|
||||
Field sql.NullTime
|
||||
}
|
||||
|
||||
type nullableTimeTarget struct {
|
||||
Field time.Time
|
||||
}
|
||||
|
||||
type nullableTimePointerTarget struct {
|
||||
Field *time.Time
|
||||
}
|
||||
|
||||
type withStringSlice struct {
|
||||
Field []string
|
||||
}
|
||||
|
||||
type withEmptyPublicIds struct {
|
||||
PublicID sql.NullString
|
||||
PublicId sql.NullString
|
||||
}
|
||||
|
||||
type emptyPublicIdTarget struct {
|
||||
PublicID string
|
||||
PublicId string
|
||||
}
|
||||
|
||||
type withByteSlice struct {
|
||||
Field []byte `nmap:"json"`
|
||||
}
|
||||
|
||||
type byteSliceTarget struct {
|
||||
Field []string
|
||||
}
|
||||
|
||||
type withUint8Slice struct {
|
||||
Field []byte `nmap:"json"`
|
||||
}
|
||||
|
||||
type uint8SliceTarget struct {
|
||||
Field []string
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
package networkmap_sqlite
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
|
||||
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
|
||||
)
|
||||
|
||||
const (
|
||||
GetAccountSettingsQuery = `
|
||||
select settings_peer_login_expiration_enabled as peer_login_expiration_enabled,
|
||||
settings_peer_login_expiration as peer_login_expiration,
|
||||
settings_peer_inactivity_expiration_enabled as peer_inactivity_expiration_enabled,
|
||||
settings_peer_inactivity_expiration as peer_inactivity_expiration,
|
||||
settings_dns_domain as dns_domain,
|
||||
settings_ipv6_enabled_groups as ipv6_enabled_groups,
|
||||
settings_routing_peer_dns_resolution_enabled as routing_peer_dns_resolution_enabled,
|
||||
settings_lazy_connection_enabled as lazy_connection_enabled,
|
||||
settings_auto_update_version as auto_update_version,
|
||||
settings_auto_update_always as auto_update_always,
|
||||
settings_metrics_push_enabled as metrics_push_enabled
|
||||
from accounts
|
||||
where id=?
|
||||
`
|
||||
)
|
||||
|
||||
func (sc *SqliteStoreConn) GetAccountSettings(ctx context.Context, accountId string) (nmdata.AccountSettingsInfo, error) {
|
||||
rows, err := sc.Conn.QueryContext(ctx, GetAccountSettingsQuery, accountId)
|
||||
if err != nil {
|
||||
return nmdata.AccountSettingsInfo{}, err
|
||||
}
|
||||
|
||||
a, err := CollectOneRowForSqlite[networkmapdb.Account](rows)
|
||||
if err != nil {
|
||||
return nmdata.AccountSettingsInfo{}, err
|
||||
}
|
||||
|
||||
settingsInfo := nmdata.AccountSettingsInfo{}
|
||||
err = networkmapdb.FromSqlTypesToSharedTypes(reflect.ValueOf(&a), reflect.ValueOf(&settingsInfo))
|
||||
if err != nil {
|
||||
return nmdata.AccountSettingsInfo{}, err
|
||||
}
|
||||
|
||||
return settingsInfo, nil
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package networkmap_sqlite
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap"
|
||||
)
|
||||
|
||||
const (
|
||||
GetAccountZonesQuery = `
|
||||
select zones.id as id, domain, not enable_search_domain as search_domain_disabled, distribution_groups,
|
||||
r.name as record_name, r.type as record_type, 'IN' record_class, r.ttl as record_ttl, r.content as record_rdata
|
||||
from zones
|
||||
left join records as r on r.zone_id = zones.id
|
||||
where zones.account_id=?
|
||||
`
|
||||
)
|
||||
|
||||
func (sc *SqliteStoreConn) GetAppliedZoneCandidates(ctx context.Context, accountId string) ([]networkmap.AppliedZoneCandidate, error) {
|
||||
rows, err := sc.Conn.QueryContext(ctx, GetAccountZonesQuery, accountId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
zones, err := CollectRowsForSqlite[networkmapdb.Zone](rows)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return networkmapdb.ZonesToAppliedZoneCandidates(zones)
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package networkmap_sqlite
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
|
||||
)
|
||||
|
||||
const (
|
||||
GetDnsSettingsQuery = `
|
||||
select dns_settings_disabled_management_groups
|
||||
from accounts
|
||||
where id=?
|
||||
`
|
||||
)
|
||||
|
||||
func (sc *SqliteStoreConn) GetDnsSettings(ctx context.Context, accountId string) (nmdata.DNSSettings, error) {
|
||||
rows, err := sc.Conn.QueryContext(ctx, GetDnsSettingsQuery, accountId)
|
||||
if err != nil {
|
||||
return nmdata.DNSSettings{}, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var value nmdata.DNSSettings
|
||||
var settings []byte
|
||||
|
||||
rows.Next()
|
||||
if err := rows.Scan(&settings); err != nil {
|
||||
return value, err
|
||||
}
|
||||
|
||||
if settings == nil {
|
||||
return nmdata.DNSSettings{}, nil
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(settings, &value.DisabledManagementGroups); err != nil {
|
||||
return value, err
|
||||
}
|
||||
|
||||
return value, nil
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package networkmap_sqlite
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
|
||||
)
|
||||
|
||||
const (
|
||||
GetDomainsQuery = `
|
||||
select domain, target_cluster
|
||||
from domains
|
||||
where account_id=? and domain<>'' and target_cluster<>''
|
||||
`
|
||||
)
|
||||
|
||||
func (sc *SqliteStoreConn) GetDomains(ctx context.Context, accountId string) ([]networkmapdb.Domain, error) {
|
||||
rows, err := sc.Conn.QueryContext(ctx, GetDomainsQuery, accountId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return CollectRowsForSqlite[networkmapdb.Domain](rows)
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
package networkmap_sqlite
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"reflect"
|
||||
|
||||
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
|
||||
)
|
||||
|
||||
const (
|
||||
GetGroupsQuery = `
|
||||
select groups.id, groups.name, groups.public_id, groups.resources, gp.peer_id
|
||||
from groups
|
||||
left join group_peers gp on gp.group_id=groups.id and gp.account_id=?
|
||||
where groups.account_id=?
|
||||
`
|
||||
)
|
||||
|
||||
// we also return a resource-to-group index.
|
||||
// an alternative is to add json indexes, query this directly. Not sure how expensive
|
||||
// json indexes are. TODO (dmitri) verify and maybe change the implementation here.
|
||||
func (sc *SqliteStoreConn) GetGroups(ctx context.Context, accountId string) ([]nmdata.Group, map[string]map[string]any, error) {
|
||||
rows, err := sc.Conn.QueryContext(ctx, GetGroupsQuery, accountId, accountId)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
groups, err := CollectRowsForSqlite[group](rows)
|
||||
|
||||
toret := make([]nmdata.Group, 0, len(groups))
|
||||
resourceToGroupIdx := make(map[string]map[string]any)
|
||||
|
||||
for _, g := range groups {
|
||||
if len(toret) > 0 && toret[len(toret)-1].ID == g.ID && g.PeerID.Valid {
|
||||
toret[len(toret)-1].Peers = append(toret[len(toret)-1].Peers, g.PeerID.String)
|
||||
continue
|
||||
}
|
||||
|
||||
dg := nmdata.Group{}
|
||||
err := networkmapdb.FromSqlTypesToSharedTypes(
|
||||
reflect.ValueOf(&g), reflect.ValueOf(&dg))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if g.PeerID.Valid {
|
||||
dg.Peers = append(dg.Peers, g.PeerID.String)
|
||||
}
|
||||
toret = append(toret, dg)
|
||||
|
||||
for _, resource := range dg.Resources {
|
||||
if _, ok := resourceToGroupIdx[resource.ID]; !ok {
|
||||
resourceToGroupIdx[resource.ID] = make(map[string]any)
|
||||
}
|
||||
resourceToGroupIdx[resource.ID][g.ID] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
return toret, resourceToGroupIdx, err
|
||||
}
|
||||
|
||||
type group struct {
|
||||
ID string
|
||||
Name sql.NullString
|
||||
PublicID sql.NullString
|
||||
Resources []byte `nmap:"json"`
|
||||
PeerID sql.NullString `nmap:"skip"`
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package networkmap_sqlite
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
|
||||
)
|
||||
|
||||
const (
|
||||
GetNameserversQuery = `
|
||||
select id, public_id, name, description, name_servers, groups, "primary", domains, enabled, search_domains_enabled
|
||||
from name_server_groups
|
||||
where account_id=?
|
||||
`
|
||||
)
|
||||
|
||||
func (sc *SqliteStoreConn) GetNameServerGroups(ctx context.Context, accountId string) ([]nmdata.NameServerGroup, error) {
|
||||
rows, err := sc.Conn.QueryContext(ctx, GetNameserversQuery, accountId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nsgroups, err := CollectRowsForSqlite[networkmapdb.NameserverGroup](rows)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return networkmapdb.ConvertAllToSharedTypes[networkmapdb.NameserverGroup, nmdata.NameServerGroup](nsgroups)
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package networkmap_sqlite
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
|
||||
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
|
||||
)
|
||||
|
||||
const (
|
||||
GetNetworkQuery = `
|
||||
select network_identifier as identifier, network_net as net, network_net_v6 as net_v6, network_dns as dns, network_serial as serial
|
||||
from accounts
|
||||
where id=?
|
||||
`
|
||||
)
|
||||
|
||||
func (sc *SqliteStoreConn) GetNetwork(ctx context.Context, accountId string) (nmdata.Network, error) {
|
||||
rows, err := sc.Conn.QueryContext(ctx, GetNetworkQuery, accountId)
|
||||
if err != nil {
|
||||
return nmdata.Network{}, err
|
||||
}
|
||||
|
||||
n, err := CollectOneRowForSqlite[networkmapdb.AccountNetwork](rows)
|
||||
if err != nil {
|
||||
return nmdata.Network{}, err
|
||||
}
|
||||
|
||||
toret := nmdata.Network{}
|
||||
err = networkmapdb.FromSqlTypesToSharedTypes(
|
||||
reflect.ValueOf(&n), reflect.ValueOf(&toret))
|
||||
if err != nil {
|
||||
return nmdata.Network{}, err
|
||||
}
|
||||
|
||||
return toret, nil
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package networkmap_sqlite
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
|
||||
)
|
||||
|
||||
const (
|
||||
GetNetworkResourcesQuery = `
|
||||
select id, network_id, account_id, public_id, name, description, type, domain, prefix, enabled
|
||||
from network_resources
|
||||
where account_id=?
|
||||
`
|
||||
)
|
||||
|
||||
func (sc *SqliteStoreConn) GetNetworkResources(ctx context.Context, accountId string) ([]nmdata.NetworkResource, error) {
|
||||
rows, err := sc.Conn.QueryContext(ctx, GetNetworkResourcesQuery, accountId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
netresorces, err := CollectRowsForSqlite[networkmapdb.Networkresource](rows)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return networkmapdb.ConvertAllToSharedTypes[networkmapdb.Networkresource, nmdata.NetworkResource](netresorces)
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
package networkmap_sqlite
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
|
||||
)
|
||||
|
||||
const (
|
||||
GetNetworkRouterQuery = `
|
||||
select public_id, peer, network_id, masquerade, metric, enabled, peer_groups, group_peers.peer_id
|
||||
from network_routers, json_each(peer_groups)
|
||||
left join group_peers on group_peers.account_id=? and group_peers.group_id=json_each.value
|
||||
where network_routers.account_id=?
|
||||
`
|
||||
)
|
||||
|
||||
func (sc *SqliteStoreConn) GetNetworkRouters(ctx context.Context, accountId string) (map[string]map[string]*nmdata.NetworkRouter, error) {
|
||||
rows, err := sc.Conn.QueryContext(ctx, GetNetworkRouterQuery, accountId, accountId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
routers, err := CollectRowsForSqlite[networkrouter](rows)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
toret := make(map[string]map[string]*nmdata.NetworkRouter)
|
||||
for _, router := range routers {
|
||||
if !router.Enabled.Bool {
|
||||
continue
|
||||
}
|
||||
|
||||
networkId := router.NetworkID.String
|
||||
if networkId == "" {
|
||||
return nil, fmt.Errorf("router with public_id %s doesn't have network_id set", router.PublicID.String)
|
||||
}
|
||||
|
||||
nmdatarouter := nmdata.NetworkRouter{}
|
||||
err := networkmapdb.FromSqlTypesToSharedTypes(reflect.ValueOf(&router), reflect.ValueOf(&nmdatarouter))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if toret[networkId] == nil {
|
||||
toret[networkId] = make(map[string]*nmdata.NetworkRouter)
|
||||
}
|
||||
if router.Peer.String != "" {
|
||||
toret[networkId][router.Peer.String] = &nmdatarouter
|
||||
continue
|
||||
}
|
||||
if router.PeerViaGroups.String != "" {
|
||||
toret[networkId][router.PeerViaGroups.String] = &nmdatarouter
|
||||
}
|
||||
}
|
||||
|
||||
return toret, nil
|
||||
}
|
||||
|
||||
type networkrouter struct {
|
||||
PublicID sql.NullString
|
||||
Peer sql.NullString `nmap:"skip"`
|
||||
NetworkID sql.NullString `nmap:"skip"`
|
||||
Masquerade sql.NullBool
|
||||
Metric sql.NullInt64
|
||||
Enabled sql.NullBool
|
||||
PeerGroups []byte `nmap:"json"`
|
||||
PeerViaGroups sql.NullString `nmap:"skip"`
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package networkmap_sqlite
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
|
||||
)
|
||||
|
||||
const (
|
||||
GetNetworksQuery = `
|
||||
select id, public_id
|
||||
from networks where account_id=?
|
||||
`
|
||||
)
|
||||
|
||||
func (sc *SqliteStoreConn) GetNetworkXIDToPublicIdMap(ctx context.Context, accountId string) (map[string]string, error) {
|
||||
rows, err := sc.Conn.QueryContext(ctx, GetNetworksQuery, accountId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
networks, err := CollectRowsForSqlite[networkmapdb.Network](rows)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
toret := make(map[string]string)
|
||||
for _, n := range networks {
|
||||
if n.PublicID.Valid {
|
||||
toret[n.ID] = n.PublicID.String
|
||||
}
|
||||
}
|
||||
|
||||
return toret, nil
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package networkmap_sqlite
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
|
||||
)
|
||||
|
||||
const (
|
||||
GetPeersQuery = `
|
||||
select id, key, ssh_key, dns_label, extra_dns_labels, user_id, ssh_enabled, login_expiration_enabled, last_login, ip, ipv6,
|
||||
peer_status_requires_approval, peer_status_connected, proxy_meta_embedded, proxy_meta_cluster,
|
||||
meta_wt_version, meta_go_os, meta_os_version, meta_kernel_version, meta_network_addresses, meta_files, meta_capabilities, meta_flags, meta_sync_message_version,
|
||||
location_country_code, location_city_name, location_connection_ip
|
||||
from peers
|
||||
where account_id = ?
|
||||
`
|
||||
)
|
||||
|
||||
func (sc *SqliteStoreConn) GetPeers(ctx context.Context, accountId string) ([]nmdata.Peer, map[string][]*nmdata.Peer, error) {
|
||||
rows, err := sc.Conn.QueryContext(ctx, GetPeersQuery, accountId)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
peers, err := CollectRowsForSqlite[networkmapdb.Peer](rows)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return networkmapdb.ConvertToNmdataPeers(peers)
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package networkmap_sqlite
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
|
||||
)
|
||||
|
||||
const (
|
||||
GetPoliciesQuery = `
|
||||
select p.id, p.public_id, p.enabled, p.source_posture_checks, pr.enabled as rule_enabled, pr.action, pr.protocol, pr.bidirectional,
|
||||
pr.sources, pr.destinations, pr.source_resource, pr.destination_resource, pr.ports, pr.port_ranges,
|
||||
pr.authorized_groups, pr.authorized_user
|
||||
from policies as p
|
||||
left join policy_rules as pr on p.id = pr.policy_id
|
||||
where account_id=?
|
||||
`
|
||||
)
|
||||
|
||||
func (sc *SqliteStoreConn) GetPolicies(ctx context.Context, accountId string) ([]nmdata.Policy, map[string]map[string]any, map[string]map[string]any, error) {
|
||||
rows, err := sc.Conn.QueryContext(ctx, GetPoliciesQuery, accountId)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
policies, err := CollectRowsForSqlite[networkmapdb.Policy](rows)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
return networkmapdb.ConvertToNmdataPolicy(policies)
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package networkmap_sqlite
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
|
||||
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
|
||||
)
|
||||
|
||||
const (
|
||||
GetPostureChecksQuery = `
|
||||
select id, public_id, checks
|
||||
from posture_checks
|
||||
where account_id=?
|
||||
`
|
||||
)
|
||||
|
||||
func (sc *SqliteStoreConn) GetPostureChecks(ctx context.Context, accountId string) ([]nmdata.PostureChecks, map[string]string, error) {
|
||||
rows, err := sc.Conn.QueryContext(ctx, GetPostureChecksQuery, accountId)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
checks, err := CollectRowsForSqlite[networkmapdb.PostureChecks](rows)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
toret := make([]nmdata.PostureChecks, 0, len(checks))
|
||||
idToPublicIDIdx := make(map[string]string)
|
||||
for _, c := range checks {
|
||||
checks := nmdata.PostureChecks{}
|
||||
err := networkmapdb.FromSqlTypesToSharedTypes(reflect.ValueOf(&c), reflect.ValueOf(&checks))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
toret = append(toret, checks)
|
||||
idToPublicIDIdx[checks.ID] = c.PublicID.String
|
||||
}
|
||||
|
||||
return toret, idToPublicIDIdx, nil
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package networkmap_sqlite
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
|
||||
)
|
||||
|
||||
const (
|
||||
GetRoutesQuery = `
|
||||
select id, account_id, public_id, network, domains, keep_route, net_id, description,
|
||||
peer, peer as peer_id, peer_groups, network_type, masquerade, metric, enabled,
|
||||
groups, access_control_groups, skip_auto_apply
|
||||
from routes
|
||||
where account_id=?
|
||||
`
|
||||
)
|
||||
|
||||
func (sc *SqliteStoreConn) GetRoutes(ctx context.Context, accountId string) ([]nmdata.Route, error) {
|
||||
rows, err := sc.Conn.QueryContext(ctx, GetRoutesQuery, accountId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
routes, err := CollectRowsForSqlite[networkmapdb.Route](rows)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return networkmapdb.ConvertAllToSharedTypes[networkmapdb.Route, nmdata.Route](routes)
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
package networkmap_sqlite
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
|
||||
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
|
||||
)
|
||||
|
||||
const (
|
||||
GetServicesQuery = `
|
||||
select enabled, private, access_groups, proxy_cluster, domain
|
||||
from services
|
||||
where account_id=?
|
||||
`
|
||||
|
||||
GetProxyTargetedDomainResourcesQuery = `
|
||||
select t.target_id
|
||||
from targets as t
|
||||
join services as s on s.id = t.service_id
|
||||
where s.account_id=? and s.enabled and not coalesce(s.terminated, false)
|
||||
and t.enabled and t.target_type='domain' and t.target_id is not null
|
||||
`
|
||||
)
|
||||
|
||||
func (sc *SqliteStoreConn) GetPrivateServices(ctx context.Context, accountId string) ([]networkmapdb.Service, error) {
|
||||
rows, err := sc.Conn.QueryContext(ctx, GetServicesQuery, accountId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
services, err := CollectRowsForSqlite[service](rows)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
toret := make([]networkmapdb.Service, 0, len(services))
|
||||
for _, service := range services {
|
||||
acg := []string{}
|
||||
if service.AccessGroups != nil {
|
||||
if err := json.Unmarshal(service.AccessGroups, &acg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
s := networkmapdb.Service{
|
||||
Enabled: service.Enabled,
|
||||
Private: service.Private,
|
||||
AccessGroups: acg,
|
||||
ProxyCluster: service.ProxyCluster,
|
||||
Domain: service.Domain,
|
||||
}
|
||||
|
||||
toret = append(toret, s)
|
||||
}
|
||||
return toret, nil
|
||||
}
|
||||
|
||||
func (sc *SqliteStoreConn) GetProxyTargetedDomainResourceIDs(ctx context.Context, accountId string) (map[string]struct{}, error) {
|
||||
rows, err := sc.Conn.QueryContext(ctx, GetProxyTargetedDomainResourcesQuery, accountId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
toret := make(map[string]struct{})
|
||||
for rows.Next() {
|
||||
var id string
|
||||
err := rows.Scan(&id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
toret[id] = struct{}{}
|
||||
}
|
||||
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return toret, nil
|
||||
}
|
||||
|
||||
type service struct {
|
||||
Enabled sql.NullBool
|
||||
Private sql.NullBool
|
||||
AccessGroups []byte
|
||||
ProxyCluster sql.NullString
|
||||
Domain sql.NullString
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
package networkmap_sqlite
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"database/sql"
|
||||
|
||||
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
|
||||
)
|
||||
|
||||
var ErrNoRows = errors.New("no rows in result set")
|
||||
|
||||
type SqliteStore struct {
|
||||
Db *sql.DB
|
||||
}
|
||||
|
||||
type sqliteInterface interface {
|
||||
QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error)
|
||||
ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error)
|
||||
}
|
||||
|
||||
type SqliteStoreConn struct {
|
||||
Conn sqliteInterface
|
||||
}
|
||||
|
||||
func NewSqliteStore(ctx context.Context, storeFile, dataDir string) (*SqliteStore, error) {
|
||||
dbfile := storeFile
|
||||
if envFile, ok := os.LookupEnv("NB_STORE_ENGINE_SQLITE_FILE"); ok && envFile != "" {
|
||||
dbfile = envFile
|
||||
}
|
||||
|
||||
// Separate file path from any SQLite URI query parameters (e.g., "store.db?mode=rwc")
|
||||
filePath, query, hasQuery := strings.Cut(dbfile, "?")
|
||||
|
||||
connStr := filePath
|
||||
if filePath != ":memory:" && !filepath.IsAbs(filePath) {
|
||||
connStr = filepath.Join(dataDir, filePath)
|
||||
}
|
||||
|
||||
// Compose query parameters. User-provided ?_busy_timeout (or its mattn alias
|
||||
// ?_timeout) overrides our default; otherwise inject 30s so SQLite waits at
|
||||
// most that long on a lock instead of blocking the only Go-side connection.
|
||||
// mattn/go-sqlite3 applies PRAGMA from the DSN on every fresh connection, so
|
||||
// the value survives ConnMaxIdleTime/ConnMaxLifetime recycling. cache=shared
|
||||
// stays the default on non-Windows for the same reason as before.
|
||||
parsed, _ := url.ParseQuery(query)
|
||||
var defaults []string
|
||||
if parsed.Get("_busy_timeout") == "" && parsed.Get("_timeout") == "" {
|
||||
defaults = append(defaults, "_busy_timeout=30000")
|
||||
}
|
||||
if !hasQuery && runtime.GOOS != "windows" {
|
||||
// To avoid `The process cannot access the file because it is being used by another process` on Windows
|
||||
defaults = append(defaults, "cache=shared")
|
||||
}
|
||||
parts := defaults
|
||||
if hasQuery {
|
||||
parts = append(parts, query)
|
||||
}
|
||||
if len(parts) > 0 {
|
||||
connStr += "?" + strings.Join(parts, "&")
|
||||
}
|
||||
|
||||
db, err := sql.Open("sqlite3", connStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &SqliteStore{Db: db}, nil
|
||||
}
|
||||
|
||||
func (s *SqliteStore) BeginTx(ctx context.Context) (*SqliteStoreConn, error) {
|
||||
tx, err := s.Db.BeginTx(ctx, &sql.TxOptions{ReadOnly: true, Isolation: sql.LevelRepeatableRead})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &SqliteStoreConn{Conn: tx}, nil
|
||||
}
|
||||
|
||||
func (sc *SqliteStoreConn) RollbackTx(ctx context.Context) error {
|
||||
tx, ok := sc.Conn.(*sql.Tx)
|
||||
if !ok {
|
||||
return fmt.Errorf("expected an sql.Tx got %s", reflect.TypeOf(sc.Conn).Kind())
|
||||
}
|
||||
return tx.Rollback()
|
||||
}
|
||||
|
||||
func (sc *SqliteStoreConn) CommitTx(ctx context.Context) error {
|
||||
tx, ok := sc.Conn.(*sql.Tx)
|
||||
if !ok {
|
||||
return fmt.Errorf("expected an sql.Tx got %s", reflect.TypeOf(sc.Conn).Kind())
|
||||
}
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
func (s *SqliteStore) UsingConn() *SqliteStoreConn {
|
||||
return &SqliteStoreConn{Conn: s.Db}
|
||||
}
|
||||
|
||||
func CollectOneRowForSqlite[T any](rows *sql.Rows) (T, error) {
|
||||
defer rows.Close()
|
||||
var r T
|
||||
|
||||
if !rows.Next() {
|
||||
if err := rows.Err(); err != nil {
|
||||
return r, err
|
||||
}
|
||||
return r, ErrNoRows
|
||||
}
|
||||
err := rows.Scan(networkmapdb.StructFields(&r)...)
|
||||
if err != nil {
|
||||
return r, err
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func CollectRowsForSqlite[T any](rows *sql.Rows) ([]T, error) {
|
||||
defer rows.Close()
|
||||
toret := make([]T, 0)
|
||||
|
||||
for rows.Next() {
|
||||
var r T
|
||||
err := rows.Scan(networkmapdb.StructFields(&r)...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
toret = append(toret, r)
|
||||
}
|
||||
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return toret, nil
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
package networkmap_sqlite
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
const (
|
||||
GetAllowedUserIdsQuery = `
|
||||
select id, auto_groups
|
||||
from users
|
||||
where account_id=? and not blocked and not is_service_user
|
||||
`
|
||||
|
||||
GetAllGroupIdQuery = `
|
||||
select id from groups
|
||||
where account_id=? and name='All'
|
||||
`
|
||||
)
|
||||
|
||||
func (sc *SqliteStoreConn) GetAllowedUsers(ctx context.Context, accountId string) (map[string]struct{}, map[string][]string, error) {
|
||||
rows, err := sc.Conn.QueryContext(ctx, GetAllowedUserIdsQuery, accountId)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
users, err := CollectRowsForSqlite[user](rows)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
rows, err = sc.Conn.QueryContext(ctx, GetAllGroupIdQuery, accountId)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
allGroupIds, err := collectAllGroupIds(rows)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
userIdIdx := make(map[string]struct{})
|
||||
groupIdToUserIds := make(map[string][]string)
|
||||
for _, user := range users {
|
||||
autogroups := make([]string, 0)
|
||||
if err := json.Unmarshal(user.AutoGroups, &autogroups); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
userIdIdx[user.ID] = struct{}{}
|
||||
for _, groupId := range autogroups {
|
||||
groupIdToUserIds[groupId] = append(groupIdToUserIds[groupId], user.ID)
|
||||
}
|
||||
for _, allgid := range allGroupIds {
|
||||
groupIdToUserIds[allgid] = append(groupIdToUserIds[allgid], user.ID)
|
||||
}
|
||||
}
|
||||
|
||||
return userIdIdx, groupIdToUserIds, nil
|
||||
}
|
||||
|
||||
func collectAllGroupIds(rows *sql.Rows) ([]string, error) {
|
||||
defer rows.Close()
|
||||
var toret []string
|
||||
|
||||
for rows.Next() {
|
||||
var id string
|
||||
err := rows.Scan(&id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
toret = append(toret, id)
|
||||
}
|
||||
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return toret, nil
|
||||
}
|
||||
|
||||
type user struct {
|
||||
ID string
|
||||
AutoGroups []byte
|
||||
}
|
||||
@@ -1,157 +0,0 @@
|
||||
package networkmapdb
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/rs/xid"
|
||||
)
|
||||
|
||||
var ErrNoRows = errors.New("no rows in result set")
|
||||
|
||||
const (
|
||||
NMAP_STRUCT_TAG = "nmap"
|
||||
NMAP_SKIP = "skip"
|
||||
NMAP_MAP_TO = "map_to"
|
||||
NMAP_JSON = "json"
|
||||
)
|
||||
|
||||
type fieldTag struct {
|
||||
Key string
|
||||
Value string
|
||||
}
|
||||
|
||||
func tagFromString(t string) fieldTag {
|
||||
kv := strings.Split(t, ":")
|
||||
if len(kv) == 1 {
|
||||
return fieldTag{Key: strings.TrimSpace(kv[0])}
|
||||
}
|
||||
return fieldTag{Key: strings.TrimSpace(kv[0]), Value: strings.TrimSpace(kv[1])}
|
||||
}
|
||||
|
||||
func FromSqlTypesToSharedTypes(src reflect.Value, dst reflect.Value) error {
|
||||
typ := src.Elem().Type()
|
||||
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
f := typ.Field(i)
|
||||
|
||||
fieldTags := make(map[string]string)
|
||||
if v := f.Tag.Get(NMAP_STRUCT_TAG); v != "" {
|
||||
for _, t := range strings.Split(v, ",") {
|
||||
kv := tagFromString(t)
|
||||
fieldTags[kv.Key] = kv.Value
|
||||
}
|
||||
}
|
||||
if _, ok := fieldTags[NMAP_SKIP]; ok {
|
||||
continue
|
||||
}
|
||||
if f.PkgPath != "" { // skip unexported fields
|
||||
continue
|
||||
}
|
||||
dstFieldName := f.Name
|
||||
if override, ok := fieldTags[NMAP_MAP_TO]; ok {
|
||||
dstFieldName = override
|
||||
}
|
||||
|
||||
dstField := dst.Elem().FieldByName(dstFieldName)
|
||||
if !dstField.IsValid() {
|
||||
return errors.New("unsupported type in destination field: " + dstFieldName)
|
||||
}
|
||||
|
||||
srcField := src.Elem().Field(i)
|
||||
srcFieldType := srcField.Type().String()
|
||||
switch srcFieldType {
|
||||
case "string":
|
||||
s := srcField.Interface().(string)
|
||||
dstField.SetString(s)
|
||||
case "sql.NullString":
|
||||
s := srcField.Interface().(sql.NullString)
|
||||
if s.Valid {
|
||||
dstField.SetString(s.String)
|
||||
}
|
||||
if (dstFieldName == "PublicId" || dstFieldName == "PublicID") && s.String == "" {
|
||||
dstField.SetString(xid.New().String()) // TODO (dmitri) this needs to be removed to support delta updates
|
||||
}
|
||||
case "sql.NullTime":
|
||||
s := srcField.Interface().(sql.NullTime)
|
||||
if s.Valid {
|
||||
if dstField.Kind() == reflect.Ptr {
|
||||
t := reflect.ValueOf(&s.Time).Elem()
|
||||
dstField.Set(t.Addr())
|
||||
} else {
|
||||
dstField.Set(reflect.ValueOf(s.Time))
|
||||
}
|
||||
}
|
||||
case "sql.NullBool":
|
||||
s := srcField.Interface().(sql.NullBool)
|
||||
if s.Valid {
|
||||
dstField.SetBool(s.Bool)
|
||||
}
|
||||
case "sql.NullInt64":
|
||||
s := srcField.Interface().(sql.NullInt64)
|
||||
if s.Valid {
|
||||
dstField.SetInt(s.Int64)
|
||||
}
|
||||
case "json.RawMessage":
|
||||
s := srcField.Interface().(json.RawMessage)
|
||||
if len(s) == 0 {
|
||||
continue
|
||||
}
|
||||
if err := json.Unmarshal(s, dstField.Addr().Interface()); err != nil {
|
||||
return err
|
||||
}
|
||||
case "[]byte", "[]uint8":
|
||||
s := srcField.Interface().([]byte)
|
||||
if _, ok := fieldTags[NMAP_JSON]; !ok || len(s) == 0 {
|
||||
continue
|
||||
}
|
||||
if err := json.Unmarshal(s, dstField.Addr().Interface()); err != nil {
|
||||
return err
|
||||
}
|
||||
case "[]string":
|
||||
if srcField.IsNil() {
|
||||
continue
|
||||
}
|
||||
dstv := reflect.MakeSlice(dstField.Type(), srcField.Len(), srcField.Cap())
|
||||
reflect.Copy(dstv, srcField)
|
||||
dstField.Set(dstv)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func StructFields(s any) []any {
|
||||
src := reflect.ValueOf(s)
|
||||
toret := make([]any, 0)
|
||||
typ := src.Elem().Type()
|
||||
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
f := typ.Field(i)
|
||||
if f.PkgPath != "" { // skip unexported fields
|
||||
continue
|
||||
}
|
||||
|
||||
srcField := src.Elem().Field(i)
|
||||
toret = append(toret, srcField.Addr().Interface())
|
||||
}
|
||||
|
||||
return toret
|
||||
}
|
||||
|
||||
func ConvertAllToSharedTypes[T any, T1 any](allsrc []T) ([]T1, error) {
|
||||
toret := make([]T1, 0, len(allsrc))
|
||||
for _, src := range allsrc {
|
||||
var dst T1
|
||||
err := FromSqlTypesToSharedTypes(
|
||||
reflect.ValueOf(&src), reflect.ValueOf(&dst))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
toret = append(toret, dst)
|
||||
}
|
||||
return toret, nil
|
||||
}
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"crypto/tls"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"os"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
@@ -28,9 +27,9 @@ import (
|
||||
"github.com/netbirdio/netbird/management/internals/modules/agentnetwork"
|
||||
"github.com/netbirdio/netbird/management/internals/modules/reverseproxy/accesslogs"
|
||||
accesslogsmanager "github.com/netbirdio/netbird/management/internals/modules/reverseproxy/accesslogs/manager"
|
||||
proxyactivity "github.com/netbirdio/netbird/management/internals/modules/reverseproxy/activity"
|
||||
proxyactivitymanager "github.com/netbirdio/netbird/management/internals/modules/reverseproxy/activity/manager"
|
||||
rpservice "github.com/netbirdio/netbird/management/internals/modules/reverseproxy/service"
|
||||
networkmapdb "github.com/netbirdio/netbird/management/internals/network_map_db"
|
||||
networkmap_pgsql "github.com/netbirdio/netbird/management/internals/network_map_db/pgsql"
|
||||
nbgrpc "github.com/netbirdio/netbird/management/internals/shared/grpc"
|
||||
"github.com/netbirdio/netbird/management/server/activity"
|
||||
activitystore "github.com/netbirdio/netbird/management/server/activity/store"
|
||||
@@ -102,22 +101,6 @@ func (s *BaseServer) Store() store.Store {
|
||||
})
|
||||
}
|
||||
|
||||
func (s *BaseServer) NetworkMapStore() *networkmapdb.NetworkMapDBStoreImpl {
|
||||
return Create(s, func() *networkmapdb.NetworkMapDBStoreImpl {
|
||||
dsn := os.Getenv("NETBIRD_NMAP_STORE_DSN") // Todo: this needs to be hoocked up properly
|
||||
if dsn == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
store, err := networkmap_pgsql.NewPostgresqlStore(context.Background(), dsn)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to create network map store: %v", err)
|
||||
}
|
||||
|
||||
return networkmapdb.NewNetworkMapDBStoreImpl(store, s.IntegratedValidator(), s.SettingsManager())
|
||||
})
|
||||
}
|
||||
|
||||
func (s *BaseServer) EventStore() activity.Store {
|
||||
return Create(s, func() activity.Store {
|
||||
var err error
|
||||
@@ -250,6 +233,7 @@ func (s *BaseServer) ReverseProxyGRPCServer() *nbgrpc.ProxyServiceServer {
|
||||
proxyService := nbgrpc.NewProxyServiceServer(s.AccessLogsManager(), s.ProxyTokenStore(), s.PKCEVerifierStore(), s.proxyOIDCConfig(), s.PeersManager(), s.UsersManager(), s.IdpManager(), s.ProxyManager(), s.Store())
|
||||
s.AfterInit(func(s *BaseServer) {
|
||||
proxyService.SetServiceManager(s.ServiceManager())
|
||||
proxyService.SetActivityManager(s.ProxyActivityManager())
|
||||
proxyService.SetProxyController(s.ServiceProxyController())
|
||||
proxyService.SetAgentNetworkSynthesizer(newAgentNetworkSynthesizer(s.Store()))
|
||||
proxyService.SetAgentNetworkLimitsService(s.AgentNetworkManager())
|
||||
@@ -309,6 +293,13 @@ func (s *BaseServer) PKCEVerifierStore() *nbgrpc.PKCEVerifierStore {
|
||||
})
|
||||
}
|
||||
|
||||
// ProxyActivityManager records reverse proxy usage for activity accounting.
|
||||
func (s *BaseServer) ProxyActivityManager() proxyactivity.Manager {
|
||||
return Create(s, func() proxyactivity.Manager {
|
||||
return proxyactivitymanager.NewManager(s.Store())
|
||||
})
|
||||
}
|
||||
|
||||
func (s *BaseServer) AccessLogsManager() accesslogs.Manager {
|
||||
return Create(s, func() accesslogs.Manager {
|
||||
accessLogManager := accesslogsmanager.NewManager(s.Store(), s.PermissionsManager(), s.GeoLocationManager())
|
||||
|
||||
@@ -123,7 +123,7 @@ func (s *BaseServer) EphemeralManager() ephemeral.Manager {
|
||||
|
||||
func (s *BaseServer) NetworkMapController() network_map.Controller {
|
||||
return Create(s, func() network_map.Controller {
|
||||
return nmapcontroller.NewController(context.Background(), s.Store(), s.Metrics(), s.PeersUpdateManager(), s.AccountRequestBuffer(), s.IntegratedValidator(), s.SettingsManager(), s.DNSDomain(), s.ProxyController(), s.EphemeralManager(), s.Config, s.NetworkMapStore())
|
||||
return nmapcontroller.NewController(context.Background(), s.Store(), s.Metrics(), s.PeersUpdateManager(), s.AccountRequestBuffer(), s.IntegratedValidator(), s.SettingsManager(), s.DNSDomain(), s.ProxyController(), s.EphemeralManager(), s.Config)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -4,9 +4,10 @@ import (
|
||||
"encoding/base64"
|
||||
"strconv"
|
||||
|
||||
nbdns "github.com/netbirdio/netbird/dns"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
nbroute "github.com/netbirdio/netbird/route"
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap"
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
|
||||
"github.com/netbirdio/netbird/shared/management/proto"
|
||||
)
|
||||
|
||||
@@ -83,7 +84,6 @@ func EncodeNetworkMapEnvelope(in ComponentsEnvelopeInput) *proto.NetworkMapEnvel
|
||||
enc := newComponentEncoder(c)
|
||||
enc.indexAllPeers()
|
||||
routerIdxs := enc.indexRouterPeers(c.RouterPeers)
|
||||
enc.indexAllNetworkResources()
|
||||
|
||||
// Phase 2: gather every policy that any consumer references (peer-pair
|
||||
// policies + resource-only policies) so encodeResourcePoliciesMap can
|
||||
@@ -105,6 +105,7 @@ func EncodeNetworkMapEnvelope(in ComponentsEnvelopeInput) *proto.NetworkMapEnvel
|
||||
DnsSettings: enc.encodeDNSSettings(c.DNSSettings),
|
||||
DnsDomain: in.DNSDomain,
|
||||
CustomZoneDomain: c.CustomZoneDomain,
|
||||
AgentVersions: enc.agentVersions,
|
||||
Peers: enc.peers,
|
||||
RouterPeerIndexes: routerIdxs,
|
||||
Policies: policies,
|
||||
@@ -129,7 +130,7 @@ func EncodeNetworkMapEnvelope(in ComponentsEnvelopeInput) *proto.NetworkMapEnvel
|
||||
// networkSerial returns c.Network.CurrentSerial() with a nil guard. The
|
||||
// production path always populates c.Network, but the encoder is exported
|
||||
// and a hand-built components struct may omit it.
|
||||
func networkSerial(n *nmdata.Network) uint64 {
|
||||
func networkSerial(n *types.Network) uint64 {
|
||||
if n == nil {
|
||||
return 0
|
||||
}
|
||||
@@ -142,15 +143,16 @@ type componentEncoder struct {
|
||||
peerOrder map[string]uint32
|
||||
peers []*proto.PeerCompact
|
||||
|
||||
networkIdToPublicId map[string]string
|
||||
agentVersionOrder map[string]uint32
|
||||
agentVersions []string
|
||||
}
|
||||
|
||||
func newComponentEncoder(c *types.NetworkMapComponents) *componentEncoder {
|
||||
return &componentEncoder{
|
||||
components: c,
|
||||
peerOrder: make(map[string]uint32, len(c.Peers)),
|
||||
peers: make([]*proto.PeerCompact, 0, len(c.Peers)),
|
||||
networkIdToPublicId: make(map[string]string),
|
||||
components: c,
|
||||
peerOrder: make(map[string]uint32, len(c.Peers)),
|
||||
peers: make([]*proto.PeerCompact, 0, len(c.Peers)),
|
||||
agentVersionOrder: make(map[string]uint32),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,7 +165,7 @@ func (e *componentEncoder) indexAllPeers() {
|
||||
}
|
||||
}
|
||||
|
||||
func (e *componentEncoder) appendPeer(p *nmdata.Peer) uint32 {
|
||||
func (e *componentEncoder) appendPeer(p *types.ComponentPeer) uint32 {
|
||||
if idx, ok := e.peerOrder[p.ID]; ok {
|
||||
return idx
|
||||
}
|
||||
@@ -177,7 +179,7 @@ func (e *componentEncoder) appendPeer(p *nmdata.Peer) uint32 {
|
||||
// (c.RouterPeers may contain peers not in c.Peers when validation rules drop
|
||||
// them) and returns their wire indexes for the RouterPeerIndexes field. Must
|
||||
// run before any encoder that resolves peer ids via e.peerOrder.
|
||||
func (e *componentEncoder) indexRouterPeers(routers map[string]*nmdata.Peer) []uint32 {
|
||||
func (e *componentEncoder) indexRouterPeers(routers map[string]*types.ComponentPeer) []uint32 {
|
||||
if len(routers) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -191,15 +193,6 @@ func (e *componentEncoder) indexRouterPeers(routers map[string]*nmdata.Peer) []u
|
||||
return out
|
||||
}
|
||||
|
||||
func (e *componentEncoder) indexAllNetworkResources() {
|
||||
for _, r := range e.components.NetworkResources {
|
||||
if !r.Enabled {
|
||||
continue
|
||||
}
|
||||
e.networkIdToPublicId[r.ID] = r.PublicID
|
||||
}
|
||||
}
|
||||
|
||||
func (e *componentEncoder) encodeGroups() []*proto.GroupCompact {
|
||||
if len(e.components.Groups) == 0 {
|
||||
return nil
|
||||
@@ -213,22 +206,10 @@ func (e *componentEncoder) encodeGroups() []*proto.GroupCompact {
|
||||
peerIdxs = append(peerIdxs, idx)
|
||||
}
|
||||
}
|
||||
|
||||
groupCompactResources := func() []*proto.ResourceCompact {
|
||||
var toret []*proto.ResourceCompact
|
||||
for _, r := range g.Resources {
|
||||
if pr := e.resourceToProto(r); pr != nil {
|
||||
toret = append(toret, pr)
|
||||
}
|
||||
}
|
||||
return toret
|
||||
}
|
||||
|
||||
out = append(out, &proto.GroupCompact{
|
||||
Id: g.PublicID,
|
||||
PeerIndexes: peerIdxs,
|
||||
IsAll: g.IsGroupAll(),
|
||||
Resources: groupCompactResources(),
|
||||
})
|
||||
}
|
||||
return out
|
||||
@@ -238,7 +219,7 @@ func (e *componentEncoder) encodeGroups() []*proto.GroupCompact {
|
||||
// list and a map from policy pointer to the indexes of its emitted rules in
|
||||
// that list — used by encodeResourcePoliciesMap to translate
|
||||
// ResourcePoliciesMap[resourceID][]*Policy into wire-side indexes.
|
||||
func (e *componentEncoder) encodePolicies(policies []*nmdata.Policy) []*proto.PolicyCompact {
|
||||
func (e *componentEncoder) encodePolicies(policies []*types.Policy) []*proto.PolicyCompact {
|
||||
if len(policies) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -260,7 +241,7 @@ func (e *componentEncoder) encodePolicies(policies []*nmdata.Policy) []*proto.Po
|
||||
}
|
||||
|
||||
// encodePolicyRule maps a single PolicyRule under pol to a PolicyCompact entry.
|
||||
func (e *componentEncoder) encodePolicyRule(pol *nmdata.Policy, r *nmdata.PolicyRule) *proto.PolicyCompact {
|
||||
func (e *componentEncoder) encodePolicyRule(pol *types.Policy, r *types.PolicyRule) *proto.PolicyCompact {
|
||||
return &proto.PolicyCompact{
|
||||
Id: pol.PublicID,
|
||||
Action: networkmap.GetProtoAction(string(r.Action)),
|
||||
@@ -299,14 +280,14 @@ func (e *componentEncoder) groupPublicXids(src []string) []string {
|
||||
// only live in ResourcePoliciesMap; without this union step they'd be lost
|
||||
// from the wire and the client's resource-policy lookup would come back
|
||||
// empty.
|
||||
func unionPolicies(policies []*nmdata.Policy, resourcePolicies map[string][]*nmdata.Policy) []*nmdata.Policy {
|
||||
func unionPolicies(policies []*types.Policy, resourcePolicies map[string][]*types.Policy) []*types.Policy {
|
||||
// Fast path: non-router peers have no resource-only policies, so the
|
||||
// "union" is identical to `policies`. Skip the dedup map allocation.
|
||||
if len(resourcePolicies) == 0 {
|
||||
return policies
|
||||
}
|
||||
seen := make(map[string]struct{}, len(policies))
|
||||
out := make([]*nmdata.Policy, 0, len(policies))
|
||||
out := make([]*types.Policy, 0, len(policies))
|
||||
for _, p := range policies {
|
||||
if p == nil {
|
||||
continue
|
||||
@@ -364,31 +345,18 @@ func (e *componentEncoder) groupPublicXid(groupID string) (string, bool) {
|
||||
// peers array. For other resource types only the type string is shipped
|
||||
// today (Calculate's resource-typed rule path consults SourceResource only
|
||||
// for "peer" — other types fall through to group-based lookup).
|
||||
func (e *componentEncoder) resourceToProto(r nmdata.Resource) *proto.ResourceCompact {
|
||||
t, ok := proto.ResourceCompactType_value[string(r.Type)]
|
||||
if !ok || t == 0 || r.ID == "" {
|
||||
func (e *componentEncoder) resourceToProto(r types.Resource) *proto.ResourceCompact {
|
||||
if r.ID == "" && r.Type == "" {
|
||||
return nil
|
||||
}
|
||||
if t == int32(proto.ResourceCompactType_peer) {
|
||||
idx, ok := e.peerOrder[r.ID]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return &proto.ResourceCompact{
|
||||
Type: proto.ResourceCompactType_peer,
|
||||
ResourceId: &proto.ResourceCompact_PeerIndex{PeerIndex: idx},
|
||||
out := &proto.ResourceCompact{Type: string(r.Type)}
|
||||
if r.Type == types.ResourceTypePeer && r.ID != "" {
|
||||
if idx, ok := e.peerOrder[r.ID]; ok {
|
||||
out.PeerIndexSet = true
|
||||
out.PeerIndex = idx
|
||||
}
|
||||
}
|
||||
|
||||
publicID, ok := e.networkIdToPublicId[r.ID]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &proto.ResourceCompact{
|
||||
Type: proto.ResourceCompactType(t),
|
||||
ResourceId: &proto.ResourceCompact_Id{Id: publicID},
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// postureCheckSeqs translates a slice of posture-check xids to their
|
||||
@@ -421,7 +389,7 @@ func (e *componentEncoder) networkPublicId(xid string) (string, bool) {
|
||||
return id, true
|
||||
}
|
||||
|
||||
func (e *componentEncoder) encodeDNSSettings(s *nmdata.DNSSettings) *proto.DNSSettingsCompact {
|
||||
func (e *componentEncoder) encodeDNSSettings(s *types.DNSSettings) *proto.DNSSettingsCompact {
|
||||
if s == nil || len(s.DisabledManagementGroups) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -436,7 +404,7 @@ func (e *componentEncoder) encodeDNSSettings(s *nmdata.DNSSettings) *proto.DNSSe
|
||||
return out
|
||||
}
|
||||
|
||||
func (e *componentEncoder) encodeRoutes(routes []*nmdata.Route) []*proto.RouteRaw {
|
||||
func (e *componentEncoder) encodeRoutes(routes []*nbroute.Route) []*proto.RouteRaw {
|
||||
if len(routes) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -474,7 +442,7 @@ func (e *componentEncoder) encodeRoutes(routes []*nmdata.Route) []*proto.RouteRa
|
||||
return out
|
||||
}
|
||||
|
||||
func (e *componentEncoder) encodeNameServerGroups(nsgs []*nmdata.NameServerGroup) []*proto.NameServerGroupRaw {
|
||||
func (e *componentEncoder) encodeNameServerGroups(nsgs []*nbdns.NameServerGroup) []*proto.NameServerGroupRaw {
|
||||
if len(nsgs) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -497,7 +465,7 @@ func (e *componentEncoder) encodeNameServerGroups(nsgs []*nmdata.NameServerGroup
|
||||
return out
|
||||
}
|
||||
|
||||
func encodeNameServers(servers []nmdata.NameServer) []*proto.NameServer {
|
||||
func encodeNameServers(servers []nbdns.NameServer) []*proto.NameServer {
|
||||
if len(servers) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -512,7 +480,7 @@ func encodeNameServers(servers []nmdata.NameServer) []*proto.NameServer {
|
||||
return out
|
||||
}
|
||||
|
||||
func encodeSimpleRecords(records []nmdata.SimpleRecord) []*proto.SimpleRecord {
|
||||
func encodeSimpleRecords(records []nbdns.SimpleRecord) []*proto.SimpleRecord {
|
||||
if len(records) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -529,7 +497,7 @@ func encodeSimpleRecords(records []nmdata.SimpleRecord) []*proto.SimpleRecord {
|
||||
return out
|
||||
}
|
||||
|
||||
func encodeCustomZones(zones []nmdata.CustomZone) []*proto.CustomZone {
|
||||
func encodeCustomZones(zones []nbdns.CustomZone) []*proto.CustomZone {
|
||||
if len(zones) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -545,7 +513,7 @@ func encodeCustomZones(zones []nmdata.CustomZone) []*proto.CustomZone {
|
||||
return out
|
||||
}
|
||||
|
||||
func (e *componentEncoder) encodeNetworkResources(resources []*nmdata.NetworkResource) []*proto.NetworkResourceRaw {
|
||||
func (e *componentEncoder) encodeNetworkResources(resources []*types.ComponentResource) []*proto.NetworkResourceRaw {
|
||||
if len(resources) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -574,7 +542,7 @@ func (e *componentEncoder) encodeNetworkResources(resources []*nmdata.NetworkRes
|
||||
return out
|
||||
}
|
||||
|
||||
func (e *componentEncoder) encodeRoutersMap(routersMap map[string]map[string]*nmdata.NetworkRouter) map[string]*proto.NetworkRouterList {
|
||||
func (e *componentEncoder) encodeRoutersMap(routersMap map[string]map[string]*types.ComponentRouter) map[string]*proto.NetworkRouterList {
|
||||
if len(routersMap) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -610,7 +578,7 @@ func (e *componentEncoder) encodeRoutersMap(routersMap map[string]map[string]*nm
|
||||
return out
|
||||
}
|
||||
|
||||
func (e *componentEncoder) encodeResourcePoliciesMap(rpm map[string][]*nmdata.Policy) map[string]*proto.PolicyIds {
|
||||
func (e *componentEncoder) encodeResourcePoliciesMap(rpm map[string][]*types.Policy) map[string]*proto.PolicyIds {
|
||||
if len(rpm) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -631,9 +599,6 @@ func (e *componentEncoder) encodeResourcePoliciesMap(rpm map[string][]*nmdata.Po
|
||||
}
|
||||
ids := make([]string, 0, len(policies))
|
||||
for _, pol := range policies {
|
||||
if pol == nil {
|
||||
continue
|
||||
}
|
||||
ids = append(ids, pol.PublicID)
|
||||
}
|
||||
if len(ids) == 0 {
|
||||
@@ -700,7 +665,7 @@ func (e *componentEncoder) encodePostureFailedPeers(m map[string]map[string]stru
|
||||
// (which shouldn't happen in production but the encoder is exported)
|
||||
// degrades to login_expiration_enabled = false, which makes
|
||||
// LoginExpired() return false for every peer.
|
||||
func toAccountSettingsCompact(s *nmdata.AccountSettingsInfo) *proto.AccountSettingsCompact {
|
||||
func toAccountSettingsCompact(s *types.AccountSettingsInfo) *proto.AccountSettingsCompact {
|
||||
if s == nil {
|
||||
return &proto.AccountSettingsCompact{}
|
||||
}
|
||||
@@ -710,7 +675,7 @@ func toAccountSettingsCompact(s *nmdata.AccountSettingsInfo) *proto.AccountSetti
|
||||
}
|
||||
}
|
||||
|
||||
func toAccountNetwork(n *nmdata.Network) *proto.AccountNetwork {
|
||||
func toAccountNetwork(n *types.Network) *proto.AccountNetwork {
|
||||
if n == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -726,20 +691,20 @@ func toAccountNetwork(n *nmdata.Network) *proto.AccountNetwork {
|
||||
return out
|
||||
}
|
||||
|
||||
func toPeerCompact(p *nmdata.Peer) *proto.PeerCompact {
|
||||
func toPeerCompact(p *types.ComponentPeer) *proto.PeerCompact {
|
||||
pc := &proto.PeerCompact{
|
||||
WgPubKey: decodeWgKey(p.Key),
|
||||
SshPubKey: []byte(p.SSHKey),
|
||||
DnsLabel: p.DNSLabel,
|
||||
AgentVersion: p.Meta.WtVersion,
|
||||
AddedWithSsoLogin: p.UserID != "",
|
||||
AgentVersion: p.AgentVersion,
|
||||
AddedWithSsoLogin: p.AddedWithSSOLogin,
|
||||
LoginExpirationEnabled: p.LoginExpirationEnabled,
|
||||
SshEnabled: p.SSHEnabled,
|
||||
SupportsIpv6: p.SupportsIPv6(),
|
||||
SupportsSourcePrefixes: p.SupportsSourcePrefixes(),
|
||||
ServerSshAllowed: p.Meta.Flags.ServerSSHAllowed,
|
||||
SupportsIpv6: p.SupportsIPv6,
|
||||
SupportsSourcePrefixes: p.SupportsSourcePrefixes,
|
||||
ServerSshAllowed: p.ServerSSHAllowed,
|
||||
}
|
||||
if p.LastLogin != nil {
|
||||
if !p.LastLogin.IsZero() {
|
||||
pc.LastLoginUnixNano = p.LastLogin.UnixNano()
|
||||
}
|
||||
switch {
|
||||
@@ -788,7 +753,7 @@ func portsToUint32(ports []string) []uint32 {
|
||||
return out
|
||||
}
|
||||
|
||||
func portRangesToProto(ranges []nmdata.RulePortRange) []*proto.PortInfo_Range {
|
||||
func portRangesToProto(ranges []types.RulePortRange) []*proto.PortInfo_Range {
|
||||
if len(ranges) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
|
||||
nbdns "github.com/netbirdio/netbird/dns"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
|
||||
nbroute "github.com/netbirdio/netbird/route"
|
||||
"github.com/netbirdio/netbird/shared/management/proto"
|
||||
)
|
||||
|
||||
@@ -152,66 +152,66 @@ func envelopesEquivalent(a, b *proto.NetworkMapEnvelope) bool {
|
||||
}
|
||||
|
||||
func newTestComponents() *types.NetworkMapComponents {
|
||||
peerA := &nmdata.Peer{
|
||||
ID: "peer-a",
|
||||
Key: testWgKeyA,
|
||||
IP: netip.AddrFrom4([4]byte{100, 64, 0, 1}),
|
||||
DNSLabel: "peera",
|
||||
SSHKey: "ssh-a",
|
||||
Meta: nmdata.PeerSystemMeta{WtVersion: "0.40.0"},
|
||||
peerA := &types.ComponentPeer{
|
||||
ID: "peer-a",
|
||||
Key: testWgKeyA,
|
||||
IP: netip.AddrFrom4([4]byte{100, 64, 0, 1}),
|
||||
DNSLabel: "peera",
|
||||
SSHKey: "ssh-a",
|
||||
AgentVersion: "0.40.0",
|
||||
}
|
||||
peerB := &nmdata.Peer{
|
||||
ID: "peer-b",
|
||||
Key: testWgKeyB,
|
||||
IP: netip.AddrFrom4([4]byte{100, 64, 0, 2}),
|
||||
IPv6: netip.AddrFrom16([16]byte{0xfd, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2}),
|
||||
DNSLabel: "peerb",
|
||||
Meta: nmdata.PeerSystemMeta{WtVersion: "0.25.0"},
|
||||
peerB := &types.ComponentPeer{
|
||||
ID: "peer-b",
|
||||
Key: testWgKeyB,
|
||||
IP: netip.AddrFrom4([4]byte{100, 64, 0, 2}),
|
||||
IPv6: netip.AddrFrom16([16]byte{0xfd, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2}),
|
||||
DNSLabel: "peerb",
|
||||
AgentVersion: "0.25.0",
|
||||
}
|
||||
peerC := &nmdata.Peer{
|
||||
ID: "peer-c",
|
||||
Key: testWgKeyC,
|
||||
IP: netip.AddrFrom4([4]byte{100, 64, 0, 3}),
|
||||
DNSLabel: "peerc",
|
||||
Meta: nmdata.PeerSystemMeta{WtVersion: "0.40.0"},
|
||||
peerC := &types.ComponentPeer{
|
||||
ID: "peer-c",
|
||||
Key: testWgKeyC,
|
||||
IP: netip.AddrFrom4([4]byte{100, 64, 0, 3}),
|
||||
DNSLabel: "peerc",
|
||||
AgentVersion: "0.40.0",
|
||||
}
|
||||
|
||||
return &types.NetworkMapComponents{
|
||||
PeerID: "peer-a",
|
||||
Network: &nmdata.Network{
|
||||
Network: &types.Network{
|
||||
Identifier: "net-test",
|
||||
Net: net.IPNet{IP: net.IP{100, 64, 0, 0}, Mask: net.CIDRMask(10, 32)},
|
||||
Serial: 7,
|
||||
},
|
||||
AccountSettings: &nmdata.AccountSettingsInfo{
|
||||
AccountSettings: &types.AccountSettingsInfo{
|
||||
PeerLoginExpirationEnabled: true,
|
||||
PeerLoginExpiration: 2 * time.Hour,
|
||||
},
|
||||
Peers: map[string]*nmdata.Peer{
|
||||
Peers: map[string]*types.ComponentPeer{
|
||||
"peer-a": peerA,
|
||||
"peer-b": peerB,
|
||||
"peer-c": peerC,
|
||||
},
|
||||
Groups: map[string]*nmdata.Group{
|
||||
"group-src": {PublicID: "1", Name: "Src", Peers: []string{"peer-a"}},
|
||||
"group-dst": {PublicID: "2", Name: "Dst", Peers: []string{"peer-b", "peer-c"}},
|
||||
Groups: map[string]*types.ComponentGroup{
|
||||
"group-src": {ID: "group-src", PublicID: "1", Name: "Src", Peers: []string{"peer-a"}},
|
||||
"group-dst": {ID: "group-dst", PublicID: "2", Name: "Dst", Peers: []string{"peer-b", "peer-c"}},
|
||||
},
|
||||
Policies: []*nmdata.Policy{
|
||||
Policies: []*types.Policy{
|
||||
{
|
||||
ID: "pol-1",
|
||||
PublicID: "10",
|
||||
Enabled: true,
|
||||
Rules: []*nmdata.PolicyRule{{
|
||||
ID: "rule-1", Enabled: true, Action: string(types.PolicyTrafficActionAccept),
|
||||
Protocol: string(types.PolicyRuleProtocolTCP), Bidirectional: true,
|
||||
Rules: []*types.PolicyRule{{
|
||||
ID: "rule-1", Enabled: true, Action: types.PolicyTrafficActionAccept,
|
||||
Protocol: types.PolicyRuleProtocolTCP, Bidirectional: true,
|
||||
Ports: []string{"22", "80"},
|
||||
PortRanges: []nmdata.RulePortRange{{Start: 8000, End: 8100}},
|
||||
PortRanges: []types.RulePortRange{{Start: 8000, End: 8100}},
|
||||
Sources: []string{"group-src"},
|
||||
Destinations: []string{"group-dst"},
|
||||
}},
|
||||
},
|
||||
},
|
||||
RouterPeers: map[string]*nmdata.Peer{"peer-c": peerC},
|
||||
RouterPeers: map[string]*types.ComponentPeer{"peer-c": peerC},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,31 +304,6 @@ func TestEncodeNetworkMapEnvelope_GroupsByAccountPublicId(t *testing.T) {
|
||||
assert.Len(t, groupByID["2"].PeerIndexes, 2)
|
||||
}
|
||||
|
||||
func TestEncodePolicy(t *testing.T) {
|
||||
encoder := componentEncoder{peerOrder: map[string]uint32{"peerId": uint32(1234)}, networkIdToPublicId: map[string]string{"domain": "publicDomain", "host": "publicHost", "subnet": "publicSubnet"}}
|
||||
assert.Equal(t,
|
||||
encoder.resourceToProto(nmdata.Resource{Type: "peer", ID: "peerId"}),
|
||||
&proto.ResourceCompact{Type: proto.ResourceCompactType_peer, ResourceId: &proto.ResourceCompact_PeerIndex{PeerIndex: uint32(1234)}})
|
||||
// verify invalid peer id results in nil
|
||||
assert.Nil(t,
|
||||
encoder.resourceToProto(nmdata.Resource{Type: "peer", ID: "boom"}))
|
||||
assert.Equal(t,
|
||||
encoder.resourceToProto(nmdata.Resource{Type: "domain", ID: "domain"}),
|
||||
&proto.ResourceCompact{Type: proto.ResourceCompactType_domain, ResourceId: &proto.ResourceCompact_Id{Id: "publicDomain"}})
|
||||
assert.Equal(t,
|
||||
encoder.resourceToProto(nmdata.Resource{Type: "host", ID: "host"}),
|
||||
&proto.ResourceCompact{Type: proto.ResourceCompactType_host, ResourceId: &proto.ResourceCompact_Id{Id: "publicHost"}})
|
||||
assert.Equal(t,
|
||||
encoder.resourceToProto(nmdata.Resource{Type: "subnet", ID: "subnet"}),
|
||||
&proto.ResourceCompact{Type: proto.ResourceCompactType_subnet, ResourceId: &proto.ResourceCompact_Id{Id: "publicSubnet"}})
|
||||
// verify invalid resource type results in nil
|
||||
assert.Nil(t,
|
||||
encoder.resourceToProto(nmdata.Resource{Type: "boom", ID: "boom"}))
|
||||
// verify invalid networkresource id results in nil
|
||||
assert.Nil(t,
|
||||
encoder.resourceToProto(nmdata.Resource{Type: "host", ID: "boom"}))
|
||||
}
|
||||
|
||||
func TestEncodeNetworkMapEnvelope_PolicyExpansion(t *testing.T) {
|
||||
c := newTestComponents()
|
||||
|
||||
@@ -402,12 +377,12 @@ func TestEncodeNetworkMapEnvelope_MalformedWgKey(t *testing.T) {
|
||||
|
||||
func TestEncodeNetworkMapEnvelope_IPv6OnlyPeer(t *testing.T) {
|
||||
c := newTestComponents()
|
||||
v6Only := &nmdata.Peer{
|
||||
ID: "peer-v6",
|
||||
Key: testWgKeyA,
|
||||
IPv6: netip.AddrFrom16([16]byte{0xfd, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9}),
|
||||
DNSLabel: "peerv6",
|
||||
Meta: nmdata.PeerSystemMeta{WtVersion: "0.40.0"},
|
||||
v6Only := &types.ComponentPeer{
|
||||
ID: "peer-v6",
|
||||
Key: testWgKeyA,
|
||||
IPv6: netip.AddrFrom16([16]byte{0xfd, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9}),
|
||||
DNSLabel: "peerv6",
|
||||
AgentVersion: "0.40.0",
|
||||
}
|
||||
c.Peers["peer-v6"] = v6Only
|
||||
|
||||
@@ -426,11 +401,11 @@ func TestEncodeNetworkMapEnvelope_IPv6OnlyPeer(t *testing.T) {
|
||||
|
||||
func TestEncodeNetworkMapEnvelope_PeerWithoutIP(t *testing.T) {
|
||||
c := newTestComponents()
|
||||
c.Peers["peer-noip"] = &nmdata.Peer{
|
||||
ID: "peer-noip",
|
||||
Key: testWgKeyA,
|
||||
DNSLabel: "peernoip",
|
||||
Meta: nmdata.PeerSystemMeta{WtVersion: "0.40.0"},
|
||||
c.Peers["peer-noip"] = &types.ComponentPeer{
|
||||
ID: "peer-noip",
|
||||
Key: testWgKeyA,
|
||||
DNSLabel: "peernoip",
|
||||
AgentVersion: "0.40.0",
|
||||
}
|
||||
|
||||
full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}).GetFull()
|
||||
@@ -448,7 +423,7 @@ func TestEncodeNetworkMapEnvelope_PeerWithoutIP(t *testing.T) {
|
||||
|
||||
func TestEncodeNetworkMapEnvelope_EmptyInput(t *testing.T) {
|
||||
c := &types.NetworkMapComponents{
|
||||
Network: &nmdata.Network{Identifier: "x", Net: net.IPNet{IP: net.IP{100, 64, 0, 0}, Mask: net.CIDRMask(10, 32)}},
|
||||
Network: &types.Network{Identifier: "x", Net: net.IPNet{IP: net.IP{100, 64, 0, 0}, Mask: net.CIDRMask(10, 32)}},
|
||||
}
|
||||
|
||||
env := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c})
|
||||
@@ -465,9 +440,9 @@ func TestEncodeNetworkMapEnvelope_EmptyInput(t *testing.T) {
|
||||
func TestEncodeNetworkMapEnvelope_PeerLoginExpirationFields(t *testing.T) {
|
||||
c := newTestComponents()
|
||||
now := time.Date(2024, 1, 2, 3, 4, 5, 0, time.UTC)
|
||||
c.Peers["peer-a"].UserID = "user-1"
|
||||
c.Peers["peer-a"].AddedWithSSOLogin = true
|
||||
c.Peers["peer-a"].LoginExpirationEnabled = true
|
||||
c.Peers["peer-a"].LastLogin = &now
|
||||
c.Peers["peer-a"].LastLogin = now
|
||||
|
||||
full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}).GetFull()
|
||||
|
||||
@@ -497,7 +472,7 @@ func TestEncodeNetworkMapEnvelope_PeerLoginExpirationFields(t *testing.T) {
|
||||
|
||||
func TestEncodeNetworkMapEnvelope_RoutesRoundTrip(t *testing.T) {
|
||||
c := newTestComponents()
|
||||
c.Routes = []*nmdata.Route{
|
||||
c.Routes = []*nbroute.Route{
|
||||
{
|
||||
ID: "route-peer",
|
||||
PublicID: "100",
|
||||
@@ -544,7 +519,7 @@ func TestEncodeNetworkMapEnvelope_RoutesRoundTrip(t *testing.T) {
|
||||
|
||||
func TestEncodeNetworkMapEnvelope_RouteWithMissingPeerLeavesIndexUnset(t *testing.T) {
|
||||
c := newTestComponents()
|
||||
c.Routes = []*nmdata.Route{{
|
||||
c.Routes = []*nbroute.Route{{
|
||||
ID: "route-x",
|
||||
PublicID: "100",
|
||||
Peer: "peer-not-in-components",
|
||||
@@ -564,21 +539,21 @@ func TestEncodeNetworkMapEnvelope_ResourceOnlyPolicyShippedAndIndexed(t *testing
|
||||
// Policy that exists ONLY in ResourcePoliciesMap, not in c.Policies. This
|
||||
// is the I1 case — without unionPolicies the encoder would silently
|
||||
// drop it from the wire.
|
||||
resourceOnlyPolicy := &nmdata.Policy{
|
||||
resourceOnlyPolicy := &types.Policy{
|
||||
ID: "pol-resource", PublicID: "99", Enabled: true,
|
||||
Rules: []*nmdata.PolicyRule{{
|
||||
ID: "rule-r", Enabled: true, Action: string(types.PolicyTrafficActionAccept),
|
||||
Protocol: string(types.PolicyRuleProtocolTCP),
|
||||
Rules: []*types.PolicyRule{{
|
||||
ID: "rule-r", Enabled: true, Action: types.PolicyTrafficActionAccept,
|
||||
Protocol: types.PolicyRuleProtocolTCP,
|
||||
Sources: []string{"group-src"},
|
||||
Destinations: []string{"group-dst"},
|
||||
}},
|
||||
}
|
||||
c.ResourcePoliciesMap = map[string][]*nmdata.Policy{
|
||||
c.ResourcePoliciesMap = map[string][]*types.Policy{
|
||||
"resource-x": {c.Policies[0], resourceOnlyPolicy}, // shared + resource-only
|
||||
}
|
||||
// Resource must appear in components.NetworkResources with a seq id —
|
||||
// encoder uses that to translate the xid map key to uint32.
|
||||
c.NetworkResources = []*nmdata.NetworkResource{
|
||||
c.NetworkResources = []*types.ComponentResource{
|
||||
{ID: "resource-x", PublicID: "77", Name: "res-x", Enabled: true},
|
||||
}
|
||||
|
||||
@@ -604,10 +579,10 @@ func TestEncodeNetworkMapEnvelope_ResourceOnlyPolicyShippedAndIndexed(t *testing
|
||||
|
||||
func TestEncodeNetworkMapEnvelope_NameServerGroups(t *testing.T) {
|
||||
c := newTestComponents()
|
||||
c.NameServerGroups = []*nmdata.NameServerGroup{{
|
||||
c.NameServerGroups = []*nbdns.NameServerGroup{{
|
||||
ID: "nsg-1", PublicID: "50", Name: "Main", Description: "primary",
|
||||
NameServers: []nmdata.NameServer{{
|
||||
IP: netip.MustParseAddr("8.8.8.8"), NSType: int(nbdns.UDPNameServerType), Port: 53,
|
||||
NameServers: []nbdns.NameServer{{
|
||||
IP: netip.MustParseAddr("8.8.8.8"), NSType: nbdns.UDPNameServerType, Port: 53,
|
||||
}},
|
||||
Groups: []string{"group-src", "group-not-persisted"},
|
||||
Primary: true, Enabled: true,
|
||||
@@ -646,11 +621,11 @@ func TestEncodeNetworkMapEnvelope_PostureFailedPeers(t *testing.T) {
|
||||
func TestEncodeNetworkMapEnvelope_RoutersMap(t *testing.T) {
|
||||
c := newTestComponents()
|
||||
c.NetworkXIDToPublicID = map[string]string{"net-1": "5"}
|
||||
c.RoutersMap = map[string]map[string]*nmdata.NetworkRouter{
|
||||
c.RoutersMap = map[string]map[string]*types.ComponentRouter{
|
||||
"net-1": {
|
||||
"peer-c": {
|
||||
PublicID: "200",
|
||||
Masquerade: true, Metric: 10, Enabled: true,
|
||||
PublicID: "200",
|
||||
Peer: "peer-c", Masquerade: true, Metric: 10, Enabled: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -676,14 +651,14 @@ func TestEncodeNetworkMapEnvelope_RouterPeerNotInComponentsPeers(t *testing.T) {
|
||||
// peer_index reference must still resolve.
|
||||
c := newTestComponents()
|
||||
delete(c.Peers, "peer-c")
|
||||
routerPeer := &nmdata.Peer{
|
||||
routerPeer := &types.ComponentPeer{
|
||||
ID: "peer-c", Key: testWgKeyC, IP: netip.AddrFrom4([4]byte{100, 64, 0, 3}),
|
||||
DNSLabel: "peerc", Meta: nmdata.PeerSystemMeta{WtVersion: "0.40.0"},
|
||||
DNSLabel: "peerc", AgentVersion: "0.40.0",
|
||||
}
|
||||
c.RouterPeers = map[string]*nmdata.Peer{"peer-c": routerPeer}
|
||||
c.RouterPeers = map[string]*types.ComponentPeer{"peer-c": routerPeer}
|
||||
c.NetworkXIDToPublicID = map[string]string{"net-1": "5"}
|
||||
c.RoutersMap = map[string]map[string]*nmdata.NetworkRouter{
|
||||
"net-1": {"peer-c": {PublicID: "1", Enabled: true}},
|
||||
c.RoutersMap = map[string]map[string]*types.ComponentRouter{
|
||||
"net-1": {"peer-c": {PublicID: "1", Peer: "peer-c", Enabled: true}},
|
||||
}
|
||||
|
||||
full := EncodeNetworkMapEnvelope(ComponentsEnvelopeInput{Components: c}).GetFull()
|
||||
@@ -716,9 +691,9 @@ func TestToProxyPatch_EmptyInputReturnsNil(t *testing.T) {
|
||||
|
||||
func TestToProxyPatch_PopulatesAllFields(t *testing.T) {
|
||||
nm := &types.NetworkMap{
|
||||
Peers: []*nmdata.Peer{{
|
||||
Peers: []*types.ComponentPeer{{
|
||||
ID: "ext-peer", Key: testWgKeyA, IP: netip.AddrFrom4([4]byte{100, 64, 0, 9}),
|
||||
DNSLabel: "extpeer", Meta: nmdata.PeerSystemMeta{WtVersion: "0.40.0"},
|
||||
DNSLabel: "extpeer", AgentVersion: "0.40.0",
|
||||
}},
|
||||
FirewallRules: []*types.FirewallRule{{
|
||||
PeerIP: "100.64.0.9", Action: "accept", Direction: 0, Protocol: "tcp",
|
||||
@@ -790,7 +765,7 @@ func TestEncodeNetworkMapEnvelope_NilComponentsGracefulDegrade(t *testing.T) {
|
||||
|
||||
func TestEncodeNetworkMapEnvelope_AccountSettingsAlwaysEmitted(t *testing.T) {
|
||||
c := &types.NetworkMapComponents{
|
||||
Network: &nmdata.Network{Identifier: "x", Net: net.IPNet{IP: net.IP{100, 64, 0, 0}, Mask: net.CIDRMask(10, 32)}},
|
||||
Network: &types.Network{Identifier: "x", Net: net.IPNet{IP: net.IP{100, 64, 0, 0}, Mask: net.CIDRMask(10, 32)}},
|
||||
// AccountSettings deliberately nil
|
||||
}
|
||||
|
||||
@@ -804,8 +779,8 @@ func TestEncodeNetworkMapEnvelope_AccountSettingsAlwaysEmitted(t *testing.T) {
|
||||
func emptyNetworkMapComponents() *types.NetworkMapComponents {
|
||||
return types.EmptyNetworkMapComponents(
|
||||
&types.NetworkMapComponents{
|
||||
PeerID: "peer-id", Peers: map[string]*nmdata.Peer{"peer-id": {}},
|
||||
Network: &nmdata.Network{
|
||||
PeerID: "peer-id", Peers: map[string]*types.ComponentPeer{"peer-id": {}},
|
||||
Network: &types.Network{
|
||||
Identifier: "net-empty",
|
||||
Net: net.IPNet{IP: net.IP{100, 64, 0, 0}, Mask: net.CIDRMask(10, 32)},
|
||||
Serial: 9,
|
||||
|
||||
@@ -7,11 +7,11 @@ import (
|
||||
|
||||
"github.com/netbirdio/netbird/client/ssh/auth"
|
||||
nbconfig "github.com/netbirdio/netbird/management/internals/server/config"
|
||||
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
||||
"github.com/netbirdio/netbird/management/server/posture"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
sharedgrpc "github.com/netbirdio/netbird/shared/management/grpc"
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap"
|
||||
nmdata "github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
|
||||
"github.com/netbirdio/netbird/shared/management/proto"
|
||||
)
|
||||
|
||||
@@ -31,14 +31,14 @@ func ToComponentSyncResponse(
|
||||
config *nbconfig.Config,
|
||||
httpConfig *nbconfig.HttpServerConfig,
|
||||
deviceFlowConfig *nbconfig.DeviceAuthorizationFlow,
|
||||
peer *nmdata.Peer,
|
||||
peer *nbpeer.Peer,
|
||||
turnCredentials *Token,
|
||||
relayCredentials *Token,
|
||||
components *types.NetworkMapComponents,
|
||||
proxyPatch *types.NetworkMap,
|
||||
dnsName string,
|
||||
checks []*posture.Checks,
|
||||
settings *nmdata.AccountSettingsInfo,
|
||||
settings *types.Settings,
|
||||
extraSettings *types.ExtraSettings,
|
||||
peerGroups []string,
|
||||
dnsFwdPort int64,
|
||||
@@ -145,7 +145,7 @@ func toProxyPatch(nm *types.NetworkMap, dnsName string, includeIPv6, useSourcePr
|
||||
//
|
||||
// The full SSH AuthorizedUsers map is still produced by the client when it
|
||||
// runs Calculate() over the envelope.
|
||||
func computeSSHEnabledForPeer(c *types.NetworkMapComponents, peer *nmdata.Peer) bool {
|
||||
func computeSSHEnabledForPeer(c *types.NetworkMapComponents, peer *nbpeer.Peer) bool {
|
||||
if c == nil || peer == nil {
|
||||
return false
|
||||
}
|
||||
@@ -170,25 +170,25 @@ func computeSSHEnabledForPeer(c *types.NetworkMapComponents, peer *nmdata.Peer)
|
||||
// ruleEnablesSSHForPeer returns true when rule is active, targets peer, and
|
||||
// either explicitly authorises SSH or covers the legacy TCP/22 path while the
|
||||
// peer itself has SSH enabled locally.
|
||||
func ruleEnablesSSHForPeer(c *types.NetworkMapComponents, rule *nmdata.PolicyRule, peer *nmdata.Peer) bool {
|
||||
func ruleEnablesSSHForPeer(c *types.NetworkMapComponents, rule *types.PolicyRule, peer *nbpeer.Peer) bool {
|
||||
if rule == nil || !rule.Enabled {
|
||||
return false
|
||||
}
|
||||
if !peerInDestinations(c, rule, peer.ID) {
|
||||
return false
|
||||
}
|
||||
if rule.Protocol == string(types.PolicyRuleProtocolNetbirdSSH) {
|
||||
if rule.Protocol == types.PolicyRuleProtocolNetbirdSSH {
|
||||
return true
|
||||
}
|
||||
return peer.SSHEnabled && nmdata.PolicyRuleImpliesLegacySSH(rule)
|
||||
return peer.SSHEnabled && types.PolicyRuleImpliesLegacySSH(rule)
|
||||
}
|
||||
|
||||
// peerInDestinations reports whether peerID is in any of rule.Destinations'
|
||||
// groups (or matches DestinationResource if it's a peer-typed resource —
|
||||
// for non-peer types Calculate falls through to group lookup, so we mirror
|
||||
// that exactly to avoid silent divergence).
|
||||
func peerInDestinations(c *types.NetworkMapComponents, rule *nmdata.PolicyRule, peerID string) bool {
|
||||
if rule.DestinationResource.Type == string(types.ResourceTypePeer) && rule.DestinationResource.ID != "" {
|
||||
func peerInDestinations(c *types.NetworkMapComponents, rule *types.PolicyRule, peerID string) bool {
|
||||
if rule.DestinationResource.Type == types.ResourceTypePeer && rule.DestinationResource.ID != "" {
|
||||
return rule.DestinationResource.ID == peerID
|
||||
}
|
||||
for _, groupID := range rule.Destinations {
|
||||
|
||||
@@ -5,8 +5,8 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
|
||||
)
|
||||
|
||||
// TestComputeSSHEnabledForPeer covers both Calculate-mirroring branches:
|
||||
@@ -17,15 +17,16 @@ func TestComputeSSHEnabledForPeer(t *testing.T) {
|
||||
const targetPeerID = "target"
|
||||
const targetGroupID = "g_dst"
|
||||
|
||||
mkComponents := func(rule *nmdata.PolicyRule, sshEnabled bool) (*types.NetworkMapComponents, *nmdata.Peer) {
|
||||
peer := &nmdata.Peer{ID: targetPeerID, SSHEnabled: sshEnabled}
|
||||
mkComponents := func(rule *types.PolicyRule, sshEnabled bool) (*types.NetworkMapComponents, *nbpeer.Peer) {
|
||||
peer := &nbpeer.Peer{ID: targetPeerID, SSHEnabled: sshEnabled}
|
||||
group := &types.ComponentGroup{ID: targetGroupID, Name: "dst", Peers: []string{targetPeerID}}
|
||||
return &types.NetworkMapComponents{
|
||||
Peers: map[string]*nmdata.Peer{targetPeerID: peer},
|
||||
Groups: map[string]*nmdata.Group{targetGroupID: {Name: "dst", Peers: []string{targetPeerID}}},
|
||||
Policies: []*nmdata.Policy{{
|
||||
Peers: map[string]*types.ComponentPeer{targetPeerID: peer.ToComponent()},
|
||||
Groups: map[string]*types.ComponentGroup{targetGroupID: group},
|
||||
Policies: []*types.Policy{{
|
||||
ID: "p",
|
||||
Enabled: true,
|
||||
Rules: []*nmdata.PolicyRule{rule},
|
||||
Rules: []*types.PolicyRule{rule},
|
||||
}},
|
||||
}, peer
|
||||
}
|
||||
@@ -33,14 +34,14 @@ func TestComputeSSHEnabledForPeer(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
peerSSH bool
|
||||
rule nmdata.PolicyRule
|
||||
rule types.PolicyRule
|
||||
wantEnabled bool
|
||||
}{
|
||||
{
|
||||
name: "explicit-netbird-ssh-activates-regardless-of-peer-ssh",
|
||||
peerSSH: false,
|
||||
rule: nmdata.PolicyRule{
|
||||
Enabled: true, Protocol: string(types.PolicyRuleProtocolNetbirdSSH),
|
||||
rule: types.PolicyRule{
|
||||
Enabled: true, Protocol: types.PolicyRuleProtocolNetbirdSSH,
|
||||
Destinations: []string{targetGroupID},
|
||||
},
|
||||
wantEnabled: true,
|
||||
@@ -48,8 +49,8 @@ func TestComputeSSHEnabledForPeer(t *testing.T) {
|
||||
{
|
||||
name: "implicit-tcp-22-with-peer-ssh",
|
||||
peerSSH: true,
|
||||
rule: nmdata.PolicyRule{
|
||||
Enabled: true, Protocol: string(types.PolicyRuleProtocolTCP), Ports: []string{"22"},
|
||||
rule: types.PolicyRule{
|
||||
Enabled: true, Protocol: types.PolicyRuleProtocolTCP, Ports: []string{"22"},
|
||||
Destinations: []string{targetGroupID},
|
||||
},
|
||||
wantEnabled: true,
|
||||
@@ -57,8 +58,8 @@ func TestComputeSSHEnabledForPeer(t *testing.T) {
|
||||
{
|
||||
name: "implicit-tcp-22-without-peer-ssh-disabled",
|
||||
peerSSH: false,
|
||||
rule: nmdata.PolicyRule{
|
||||
Enabled: true, Protocol: string(types.PolicyRuleProtocolTCP), Ports: []string{"22"},
|
||||
rule: types.PolicyRule{
|
||||
Enabled: true, Protocol: types.PolicyRuleProtocolTCP, Ports: []string{"22"},
|
||||
Destinations: []string{targetGroupID},
|
||||
},
|
||||
wantEnabled: false,
|
||||
@@ -66,8 +67,8 @@ func TestComputeSSHEnabledForPeer(t *testing.T) {
|
||||
{
|
||||
name: "implicit-tcp-22022-with-peer-ssh",
|
||||
peerSSH: true,
|
||||
rule: nmdata.PolicyRule{
|
||||
Enabled: true, Protocol: string(types.PolicyRuleProtocolTCP), Ports: []string{"22022"},
|
||||
rule: types.PolicyRule{
|
||||
Enabled: true, Protocol: types.PolicyRuleProtocolTCP, Ports: []string{"22022"},
|
||||
Destinations: []string{targetGroupID},
|
||||
},
|
||||
wantEnabled: true,
|
||||
@@ -75,8 +76,8 @@ func TestComputeSSHEnabledForPeer(t *testing.T) {
|
||||
{
|
||||
name: "implicit-all-protocol-with-peer-ssh",
|
||||
peerSSH: true,
|
||||
rule: nmdata.PolicyRule{
|
||||
Enabled: true, Protocol: string(types.PolicyRuleProtocolALL),
|
||||
rule: types.PolicyRule{
|
||||
Enabled: true, Protocol: types.PolicyRuleProtocolALL,
|
||||
Destinations: []string{targetGroupID},
|
||||
},
|
||||
wantEnabled: true,
|
||||
@@ -84,10 +85,10 @@ func TestComputeSSHEnabledForPeer(t *testing.T) {
|
||||
{
|
||||
name: "implicit-port-range-covers-22",
|
||||
peerSSH: true,
|
||||
rule: nmdata.PolicyRule{
|
||||
rule: types.PolicyRule{
|
||||
Enabled: true,
|
||||
Protocol: string(types.PolicyRuleProtocolTCP),
|
||||
PortRanges: []nmdata.RulePortRange{{Start: 20, End: 30}},
|
||||
Protocol: types.PolicyRuleProtocolTCP,
|
||||
PortRanges: []types.RulePortRange{{Start: 20, End: 30}},
|
||||
Destinations: []string{targetGroupID},
|
||||
},
|
||||
wantEnabled: true,
|
||||
@@ -95,8 +96,8 @@ func TestComputeSSHEnabledForPeer(t *testing.T) {
|
||||
{
|
||||
name: "tcp-80-no-ssh",
|
||||
peerSSH: true,
|
||||
rule: nmdata.PolicyRule{
|
||||
Enabled: true, Protocol: string(types.PolicyRuleProtocolTCP), Ports: []string{"80"},
|
||||
rule: types.PolicyRule{
|
||||
Enabled: true, Protocol: types.PolicyRuleProtocolTCP, Ports: []string{"80"},
|
||||
Destinations: []string{targetGroupID},
|
||||
},
|
||||
wantEnabled: false,
|
||||
@@ -104,8 +105,8 @@ func TestComputeSSHEnabledForPeer(t *testing.T) {
|
||||
{
|
||||
name: "disabled-rule-skipped",
|
||||
peerSSH: true,
|
||||
rule: nmdata.PolicyRule{
|
||||
Enabled: false, Protocol: string(types.PolicyRuleProtocolNetbirdSSH),
|
||||
rule: types.PolicyRule{
|
||||
Enabled: false, Protocol: types.PolicyRuleProtocolNetbirdSSH,
|
||||
Destinations: []string{targetGroupID},
|
||||
},
|
||||
wantEnabled: false,
|
||||
@@ -113,8 +114,8 @@ func TestComputeSSHEnabledForPeer(t *testing.T) {
|
||||
{
|
||||
name: "peer-not-in-destinations",
|
||||
peerSSH: true,
|
||||
rule: nmdata.PolicyRule{
|
||||
Enabled: true, Protocol: string(types.PolicyRuleProtocolNetbirdSSH),
|
||||
rule: types.PolicyRule{
|
||||
Enabled: true, Protocol: types.PolicyRuleProtocolNetbirdSSH,
|
||||
Destinations: []string{"g_other"}, // target not in this group
|
||||
},
|
||||
wantEnabled: false,
|
||||
@@ -122,21 +123,21 @@ func TestComputeSSHEnabledForPeer(t *testing.T) {
|
||||
{
|
||||
name: "peer-typed-destination-resource-matches",
|
||||
peerSSH: false,
|
||||
rule: nmdata.PolicyRule{
|
||||
rule: types.PolicyRule{
|
||||
Enabled: true,
|
||||
Protocol: string(types.PolicyRuleProtocolNetbirdSSH),
|
||||
DestinationResource: nmdata.Resource{ID: targetPeerID, Type: string(types.ResourceTypePeer)},
|
||||
Protocol: types.PolicyRuleProtocolNetbirdSSH,
|
||||
DestinationResource: types.Resource{ID: targetPeerID, Type: types.ResourceTypePeer},
|
||||
},
|
||||
wantEnabled: true,
|
||||
},
|
||||
{
|
||||
name: "non-peer-destination-resource-falls-through-to-groups",
|
||||
peerSSH: false,
|
||||
rule: nmdata.PolicyRule{
|
||||
rule: types.PolicyRule{
|
||||
Enabled: true,
|
||||
Protocol: string(types.PolicyRuleProtocolNetbirdSSH),
|
||||
DestinationResource: nmdata.Resource{ID: targetPeerID, Type: "host"}, // wrong type
|
||||
Destinations: []string{targetGroupID}, // saved by group fallback
|
||||
Protocol: types.PolicyRuleProtocolNetbirdSSH,
|
||||
DestinationResource: types.Resource{ID: targetPeerID, Type: "host"}, // wrong type
|
||||
Destinations: []string{targetGroupID}, // saved by group fallback
|
||||
},
|
||||
wantEnabled: true,
|
||||
},
|
||||
@@ -155,16 +156,16 @@ func TestComputeSSHEnabledForPeer(t *testing.T) {
|
||||
// belt-and-suspenders presence guard mirroring Calculate's
|
||||
// getAllPeersFromGroups invariant.
|
||||
func TestComputeSSHEnabledForPeer_TargetMissingFromComponents(t *testing.T) {
|
||||
peer := &nmdata.Peer{ID: "missing", SSHEnabled: true}
|
||||
peer := &nbpeer.Peer{ID: "missing", SSHEnabled: true}
|
||||
c := &types.NetworkMapComponents{
|
||||
Peers: map[string]*nmdata.Peer{}, // target peer NOT present
|
||||
Groups: map[string]*nmdata.Group{
|
||||
"g": {Peers: []string{"missing"}},
|
||||
Peers: map[string]*types.ComponentPeer{}, // target peer NOT present
|
||||
Groups: map[string]*types.ComponentGroup{
|
||||
"g": {ID: "g", Peers: []string{"missing"}},
|
||||
},
|
||||
Policies: []*nmdata.Policy{{
|
||||
Policies: []*types.Policy{{
|
||||
ID: "p", Enabled: true,
|
||||
Rules: []*nmdata.PolicyRule{{
|
||||
Enabled: true, Protocol: string(types.PolicyRuleProtocolNetbirdSSH),
|
||||
Rules: []*types.PolicyRule{{
|
||||
Enabled: true, Protocol: types.PolicyRuleProtocolNetbirdSSH,
|
||||
Destinations: []string{"g"},
|
||||
}},
|
||||
}},
|
||||
@@ -178,6 +179,6 @@ func TestComputeSSHEnabledForPeer_TargetMissingFromComponents(t *testing.T) {
|
||||
// exported indirectly via ToComponentSyncResponse and may receive nil
|
||||
// components on graceful-degrade paths.
|
||||
func TestComputeSSHEnabledForPeer_NilInputs(t *testing.T) {
|
||||
assert.False(t, computeSSHEnabledForPeer(nil, &nmdata.Peer{ID: "x"}))
|
||||
assert.False(t, computeSSHEnabledForPeer(nil, &nbpeer.Peer{ID: "x"}))
|
||||
assert.False(t, computeSSHEnabledForPeer(&types.NetworkMapComponents{}, nil))
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user