[client,management] add netflow support to client and update management (#3414)

adds NetFlow functionality to track and log network traffic information between peers, with features including:

- Flow logging for TCP, UDP, and ICMP traffic
- Integration with connection tracking system
- Resource ID tracking in NetFlow events
- DNS and exit node collection configuration
- Flow API and Redis cache in management
- Memory-based flow storage implementation
- Kernel conntrack counters and userspace counters
- TCP state machine improvements for more accurate tracking
- Migration from net.IP to netip.Addr in the userspace firewall
This commit is contained in:
Maycon Santos
2025-03-20 17:05:48 +01:00
committed by GitHub
parent f51e0b59bd
commit c02e236196
151 changed files with 7117 additions and 2233 deletions
+42 -121
View File
@@ -6,7 +6,6 @@ import (
"fmt"
"math/rand"
"net"
"net/netip"
"reflect"
"regexp"
"slices"
@@ -14,16 +13,16 @@ import (
"sync"
"time"
"github.com/eko/gocache/v3/cache"
cacheStore "github.com/eko/gocache/v3/store"
gocache "github.com/patrickmn/go-cache"
cacheStore "github.com/eko/gocache/lib/v4/store"
"github.com/rs/xid"
log "github.com/sirupsen/logrus"
"github.com/vmihailenco/msgpack/v5"
"golang.org/x/exp/maps"
nbdns "github.com/netbirdio/netbird/dns"
"github.com/netbirdio/netbird/management/domain"
"github.com/netbirdio/netbird/management/server/account"
"github.com/netbirdio/netbird/management/server/activity"
nbcache "github.com/netbirdio/netbird/management/server/cache"
nbcontext "github.com/netbirdio/netbird/management/server/context"
"github.com/netbirdio/netbird/management/server/geolocation"
"github.com/netbirdio/netbird/management/server/idp"
@@ -31,6 +30,7 @@ import (
"github.com/netbirdio/netbird/management/server/integrations/port_forwarding"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
"github.com/netbirdio/netbird/management/server/posture"
"github.com/netbirdio/netbird/management/server/settings"
"github.com/netbirdio/netbird/management/server/status"
"github.com/netbirdio/netbird/management/server/store"
"github.com/netbirdio/netbird/management/server/telemetry"
@@ -40,8 +40,6 @@ import (
)
const (
CacheExpirationMax = 7 * 24 * 3600 * time.Second // 7 days
CacheExpirationMin = 3 * 24 * 3600 * time.Second // 3 days
peerSchedulerRetryInterval = 3 * time.Second
emptyUserID = "empty user ID in claims"
errorGettingDomainAccIDFmt = "error getting account ID by private domain: %v"
@@ -49,104 +47,11 @@ const (
type userLoggedInOnce bool
type ExternalCacheManager cache.CacheInterface[*idp.UserData]
func cacheEntryExpiration() time.Duration {
r := rand.Intn(int(CacheExpirationMax.Milliseconds()-CacheExpirationMin.Milliseconds())) + int(CacheExpirationMin.Milliseconds())
r := rand.Intn(int(nbcache.DefaultIDPCacheExpirationMax.Milliseconds()-nbcache.DefaultIDPCacheExpirationMin.Milliseconds())) + int(nbcache.DefaultIDPCacheExpirationMin.Milliseconds())
return time.Duration(r) * time.Millisecond
}
type AccountManager interface {
GetOrCreateAccountByUser(ctx context.Context, userId, domain string) (*types.Account, error)
GetAccount(ctx context.Context, accountID string) (*types.Account, error)
CreateSetupKey(ctx context.Context, accountID string, keyName string, keyType types.SetupKeyType, expiresIn time.Duration,
autoGroups []string, usageLimit int, userID string, ephemeral bool, allowExtraDNSLabels bool) (*types.SetupKey, error)
SaveSetupKey(ctx context.Context, accountID string, key *types.SetupKey, userID string) (*types.SetupKey, error)
CreateUser(ctx context.Context, accountID, initiatorUserID string, key *types.UserInfo) (*types.UserInfo, error)
DeleteUser(ctx context.Context, accountID, initiatorUserID string, targetUserID string) error
DeleteRegularUsers(ctx context.Context, accountID, initiatorUserID string, targetUserIDs []string, userInfos map[string]*types.UserInfo) error
InviteUser(ctx context.Context, accountID string, initiatorUserID string, targetUserID string) error
ListSetupKeys(ctx context.Context, accountID, userID string) ([]*types.SetupKey, error)
SaveUser(ctx context.Context, accountID, initiatorUserID string, update *types.User) (*types.UserInfo, error)
SaveOrAddUser(ctx context.Context, accountID, initiatorUserID string, update *types.User, addIfNotExists bool) (*types.UserInfo, error)
SaveOrAddUsers(ctx context.Context, accountID, initiatorUserID string, updates []*types.User, addIfNotExists bool) ([]*types.UserInfo, error)
GetSetupKey(ctx context.Context, accountID, userID, keyID string) (*types.SetupKey, error)
GetAccountByID(ctx context.Context, accountID string, userID string) (*types.Account, error)
AccountExists(ctx context.Context, accountID string) (bool, error)
GetAccountIDByUserID(ctx context.Context, userID, domain string) (string, error)
GetAccountIDFromUserAuth(ctx context.Context, userAuth nbcontext.UserAuth) (string, string, error)
DeleteAccount(ctx context.Context, accountID, userID string) error
GetUserByID(ctx context.Context, id string) (*types.User, error)
GetUserFromUserAuth(ctx context.Context, userAuth nbcontext.UserAuth) (*types.User, error)
ListUsers(ctx context.Context, accountID string) ([]*types.User, error)
GetPeers(ctx context.Context, accountID, userID, nameFilter, ipFilter string) ([]*nbpeer.Peer, error)
MarkPeerConnected(ctx context.Context, peerKey string, connected bool, realIP net.IP, accountID string) error
DeletePeer(ctx context.Context, accountID, peerID, userID string) error
UpdatePeer(ctx context.Context, accountID, userID string, peer *nbpeer.Peer) (*nbpeer.Peer, error)
GetNetworkMap(ctx context.Context, peerID string) (*types.NetworkMap, error)
GetPeerNetwork(ctx context.Context, peerID string) (*types.Network, error)
AddPeer(ctx context.Context, setupKey, userID string, peer *nbpeer.Peer) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, error)
CreatePAT(ctx context.Context, accountID string, initiatorUserID string, targetUserID string, tokenName string, expiresIn int) (*types.PersonalAccessTokenGenerated, error)
DeletePAT(ctx context.Context, accountID string, initiatorUserID string, targetUserID string, tokenID string) error
GetPAT(ctx context.Context, accountID string, initiatorUserID string, targetUserID string, tokenID string) (*types.PersonalAccessToken, error)
GetAllPATs(ctx context.Context, accountID string, initiatorUserID string, targetUserID string) ([]*types.PersonalAccessToken, error)
GetUsersFromAccount(ctx context.Context, accountID, userID string) (map[string]*types.UserInfo, error)
GetGroup(ctx context.Context, accountId, groupID, userID string) (*types.Group, error)
GetAllGroups(ctx context.Context, accountID, userID string) ([]*types.Group, error)
GetGroupByName(ctx context.Context, groupName, accountID string) (*types.Group, error)
SaveGroup(ctx context.Context, accountID, userID string, group *types.Group) error
SaveGroups(ctx context.Context, accountID, userID string, newGroups []*types.Group) error
DeleteGroup(ctx context.Context, accountId, userId, groupID string) error
DeleteGroups(ctx context.Context, accountId, userId string, groupIDs []string) error
GroupAddPeer(ctx context.Context, accountId, groupID, peerID string) error
GroupDeletePeer(ctx context.Context, accountId, groupID, peerID string) error
GetPeerGroups(ctx context.Context, accountID, peerID string) ([]*types.Group, error)
GetPolicy(ctx context.Context, accountID, policyID, userID string) (*types.Policy, error)
SavePolicy(ctx context.Context, accountID, userID string, policy *types.Policy) (*types.Policy, error)
DeletePolicy(ctx context.Context, accountID, policyID, userID string) error
ListPolicies(ctx context.Context, accountID, userID string) ([]*types.Policy, error)
GetRoute(ctx context.Context, accountID string, routeID route.ID, userID string) (*route.Route, error)
CreateRoute(ctx context.Context, accountID string, prefix netip.Prefix, networkType route.NetworkType, domains domain.List, peerID string, peerGroupIDs []string, description string, netID route.NetID, masquerade bool, metric int, groups, accessControlGroupIDs []string, enabled bool, userID string, keepRoute bool) (*route.Route, error)
SaveRoute(ctx context.Context, accountID, userID string, route *route.Route) error
DeleteRoute(ctx context.Context, accountID string, routeID route.ID, userID string) error
ListRoutes(ctx context.Context, accountID, userID string) ([]*route.Route, error)
GetNameServerGroup(ctx context.Context, accountID, userID, nsGroupID string) (*nbdns.NameServerGroup, error)
CreateNameServerGroup(ctx context.Context, accountID string, name, description string, nameServerList []nbdns.NameServer, groups []string, primary bool, domains []string, enabled bool, userID string, searchDomainsEnabled bool) (*nbdns.NameServerGroup, error)
SaveNameServerGroup(ctx context.Context, accountID, userID string, nsGroupToSave *nbdns.NameServerGroup) error
DeleteNameServerGroup(ctx context.Context, accountID, nsGroupID, userID string) error
ListNameServerGroups(ctx context.Context, accountID string, userID string) ([]*nbdns.NameServerGroup, error)
GetDNSDomain() string
StoreEvent(ctx context.Context, initiatorID, targetID, accountID string, activityID activity.ActivityDescriber, meta map[string]any)
GetEvents(ctx context.Context, accountID, userID string) ([]*activity.Event, error)
GetDNSSettings(ctx context.Context, accountID string, userID string) (*types.DNSSettings, error)
SaveDNSSettings(ctx context.Context, accountID string, userID string, dnsSettingsToSave *types.DNSSettings) error
GetPeer(ctx context.Context, accountID, peerID, userID string) (*nbpeer.Peer, error)
UpdateAccountSettings(ctx context.Context, accountID, userID string, newSettings *types.Settings) (*types.Account, error)
LoginPeer(ctx context.Context, login PeerLogin) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, error) // used by peer gRPC API
SyncPeer(ctx context.Context, sync PeerSync, accountID string) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, error) // used by peer gRPC API
GetAllConnectedPeers() (map[string]struct{}, error)
HasConnectedChannel(peerID string) bool
GetExternalCacheManager() ExternalCacheManager
GetPostureChecks(ctx context.Context, accountID, postureChecksID, userID string) (*posture.Checks, error)
SavePostureChecks(ctx context.Context, accountID, userID string, postureChecks *posture.Checks) (*posture.Checks, error)
DeletePostureChecks(ctx context.Context, accountID, postureChecksID, userID string) error
ListPostureChecks(ctx context.Context, accountID, userID string) ([]*posture.Checks, error)
GetIdpManager() idp.Manager
UpdateIntegratedValidatorGroups(ctx context.Context, accountID string, userID string, groups []string) error
GroupValidation(ctx context.Context, accountId string, groups []string) (bool, error)
GetValidatedPeers(ctx context.Context, accountID string) (map[string]struct{}, error)
SyncAndMarkPeer(ctx context.Context, accountID string, peerPubKey string, meta nbpeer.PeerSystemMeta, realIP net.IP) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, error)
OnPeerDisconnected(ctx context.Context, accountID string, peerPubKey string) error
SyncPeerMeta(ctx context.Context, peerPubKey string, meta nbpeer.PeerSystemMeta) error
FindExistingPostureCheck(accountID string, checks *posture.ChecksDefinition) (*posture.Checks, error)
GetAccountIDForPeerKey(ctx context.Context, peerKey string) (string, error)
GetAccountSettings(ctx context.Context, accountID string, userID string) (*types.Settings, error)
DeleteSetupKey(ctx context.Context, accountID, userID, keyID string) error
UpdateAccountPeers(ctx context.Context, accountID string)
BuildUserInfosForAccount(ctx context.Context, accountID, initiatorUserID string, accountUsers []*types.User) (map[string]*types.UserInfo, error)
SyncUserJWTGroups(ctx context.Context, userAuth nbcontext.UserAuth) error
}
type DefaultAccountManager struct {
Store store.Store
// cacheMux and cacheLoading helps to make sure that only a single cache reload runs at a time per accountID
@@ -155,8 +60,8 @@ type DefaultAccountManager struct {
cacheLoading map[string]chan struct{}
peersUpdateManager *PeersUpdateManager
idpManager idp.Manager
cacheManager cache.CacheInterface[[]*idp.UserData]
externalCacheManager ExternalCacheManager
cacheManager *nbcache.AccountUserDataCache
externalCacheManager nbcache.UserDataCache
ctx context.Context
eventStore activity.Store
geo geolocation.Geolocation
@@ -164,6 +69,7 @@ type DefaultAccountManager struct {
requestBuffer *AccountRequestBuffer
proxyController port_forwarding.Controller
settingsManager settings.Manager
// singleAccountMode indicates whether the instance has a single account.
// If true, then every new user will end up under the same account.
@@ -249,6 +155,7 @@ func BuildManager(
integratedPeerValidator integrated_validator.IntegratedValidator,
metrics telemetry.AppMetrics,
proxyController port_forwarding.Controller,
settingsManager settings.Manager,
) (*DefaultAccountManager, error) {
start := time.Now()
defer func() {
@@ -272,6 +179,7 @@ func BuildManager(
metrics: metrics,
requestBuffer: NewAccountRequestBuffer(ctx, store),
proxyController: proxyController,
settingsManager: settingsManager,
}
accountsCounter, err := store.GetAccountsCounter(ctx)
if err != nil {
@@ -290,14 +198,12 @@ func BuildManager(
log.WithContext(ctx).Infof("single account mode disabled, accounts number %d", accountsCounter)
}
goCacheClient := gocache.New(CacheExpirationMax, 30*time.Minute)
goCacheStore := cacheStore.NewGoCache(goCacheClient)
am.cacheManager = cache.NewLoadable[[]*idp.UserData](am.loadAccount, cache.New[[]*idp.UserData](goCacheStore))
// TODO: what is max expiration time? Should be quite long
am.externalCacheManager = cache.New[*idp.UserData](
cacheStore.NewGoCache(goCacheClient),
)
cacheStore, err := nbcache.NewStore(nbcache.DefaultIDPCacheExpirationMax, nbcache.DefaultIDPCacheCleanupInterval)
if err != nil {
return nil, fmt.Errorf("getting cache store: %s", err)
}
am.externalCacheManager = nbcache.NewUserDataCache(cacheStore)
am.cacheManager = nbcache.NewAccountUserDataCache(am.loadAccount, cacheStore)
if !isNil(am.idpManager) {
go func() {
@@ -317,7 +223,7 @@ func BuildManager(
return am, nil
}
func (am *DefaultAccountManager) GetExternalCacheManager() ExternalCacheManager {
func (am *DefaultAccountManager) GetExternalCacheManager() account.ExternalCacheManager {
return am.externalCacheManager
}
@@ -406,7 +312,12 @@ func (am *DefaultAccountManager) UpdateAccountSettings(ctx context.Context, acco
return nil, err
}
if updateAccountPeers {
extraSettingsChanged, err := am.settingsManager.UpdateExtraSettings(ctx, accountID, userID, newSettings.Extra)
if err != nil {
return nil, err
}
if updateAccountPeers || extraSettingsChanged {
go am.UpdateAccountPeers(ctx, accountID)
}
@@ -574,7 +485,7 @@ func (am *DefaultAccountManager) warmupIDPCache(ctx context.Context) error {
rcvdUsers := 0
for accountID, users := range userData {
rcvdUsers += len(users)
err = am.cacheManager.Set(am.ctx, accountID, users, cacheStore.WithExpiration(cacheEntryExpiration()))
err = am.cacheManager.Set(am.ctx, accountID, users, cacheEntryExpiration())
if err != nil {
return err
}
@@ -718,18 +629,18 @@ func (am *DefaultAccountManager) addAccountIDToIDPAppMeta(ctx context.Context, u
return nil
}
func (am *DefaultAccountManager) loadAccount(ctx context.Context, accountID interface{}) ([]*idp.UserData, error) {
func (am *DefaultAccountManager) loadAccount(ctx context.Context, accountID any) (any, []cacheStore.Option, error) {
log.WithContext(ctx).Debugf("account %s not found in cache, reloading", accountID)
accountIDString := fmt.Sprintf("%v", accountID)
account, err := am.Store.GetAccount(ctx, accountIDString)
if err != nil {
return nil, err
return nil, nil, err
}
userData, err := am.idpManager.GetAccount(ctx, accountIDString)
if err != nil {
return nil, err
return nil, nil, err
}
log.WithContext(ctx).Debugf("%d entries received from IdP management", len(userData))
@@ -750,7 +661,13 @@ func (am *DefaultAccountManager) loadAccount(ctx context.Context, accountID inte
}
matchedUserData = append(matchedUserData, datum)
}
return matchedUserData, nil
data, err := msgpack.Marshal(matchedUserData)
if err != nil {
return nil, nil, err
}
return data, []cacheStore.Option{cacheStore.WithExpiration(cacheEntryExpiration())}, nil
}
func (am *DefaultAccountManager) lookupUserInCacheByEmail(ctx context.Context, email string, accountID string) (*idp.UserData, error) {
@@ -936,7 +853,7 @@ func (am *DefaultAccountManager) removeUserFromCache(ctx context.Context, accoun
}
}
return am.cacheManager.Set(am.ctx, accountID, data, cacheStore.WithExpiration(cacheEntryExpiration()))
return am.cacheManager.Set(am.ctx, accountID, data, cacheEntryExpiration())
}
// updateAccountDomainAttributesIfNotUpToDate updates the account domain attributes if they are not up to date and then, saves the account changes
@@ -1476,7 +1393,7 @@ func (am *DefaultAccountManager) SyncAndMarkPeer(ctx context.Context, accountID
peerUnlock := am.Store.AcquireWriteLockByUID(ctx, peerPubKey)
defer peerUnlock()
peer, netMap, postureChecks, err := am.SyncPeer(ctx, PeerSync{WireGuardPubKey: peerPubKey, Meta: meta}, accountID)
peer, netMap, postureChecks, err := am.SyncPeer(ctx, types.PeerSync{WireGuardPubKey: peerPubKey, Meta: meta}, accountID)
if err != nil {
return nil, nil, nil, fmt.Errorf("error syncing peer: %w", err)
}
@@ -1516,7 +1433,7 @@ func (am *DefaultAccountManager) SyncPeerMeta(ctx context.Context, peerPubKey st
unlockPeer := am.Store.AcquireWriteLockByUID(ctx, peerPubKey)
defer unlockPeer()
_, _, _, err = am.SyncPeer(ctx, PeerSync{WireGuardPubKey: peerPubKey, Meta: meta, UpdateAccountPeers: true}, accountID)
_, _, _, err = am.SyncPeer(ctx, types.PeerSync{WireGuardPubKey: peerPubKey, Meta: meta, UpdateAccountPeers: true}, accountID)
if err != nil {
return mapError(ctx, err)
}
@@ -1685,3 +1602,7 @@ func separateGroups(autoGroups []string, allGroups []*types.Group) ([]string, ma
return newAutoGroups, jwtAutoGroups
}
func (am *DefaultAccountManager) GetStore() store.Store {
return am.Store
}
-19
View File
@@ -1,19 +0,0 @@
package account
type ExtraSettings struct {
// PeerApprovalEnabled enables or disables the need for peers bo be approved by an administrator
PeerApprovalEnabled bool
// IntegratedValidatorGroups list of group IDs to be used with integrated approval configurations
IntegratedValidatorGroups []string `gorm:"serializer:json"`
}
// Copy copies the ExtraSettings struct
func (e *ExtraSettings) Copy() *ExtraSettings {
var cpGroup []string
return &ExtraSettings{
PeerApprovalEnabled: e.PeerApprovalEnabled,
IntegratedValidatorGroups: append(cpGroup, e.IntegratedValidatorGroups...),
}
}
+114
View File
@@ -0,0 +1,114 @@
package account
import (
"context"
"net"
"net/netip"
"time"
nbdns "github.com/netbirdio/netbird/dns"
"github.com/netbirdio/netbird/management/domain"
"github.com/netbirdio/netbird/management/server/activity"
nbcache "github.com/netbirdio/netbird/management/server/cache"
nbcontext "github.com/netbirdio/netbird/management/server/context"
"github.com/netbirdio/netbird/management/server/idp"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
"github.com/netbirdio/netbird/management/server/posture"
"github.com/netbirdio/netbird/management/server/store"
"github.com/netbirdio/netbird/management/server/types"
"github.com/netbirdio/netbird/route"
)
type ExternalCacheManager nbcache.UserDataCache
type Manager interface {
GetOrCreateAccountByUser(ctx context.Context, userId, domain string) (*types.Account, error)
GetAccount(ctx context.Context, accountID string) (*types.Account, error)
CreateSetupKey(ctx context.Context, accountID string, keyName string, keyType types.SetupKeyType, expiresIn time.Duration,
autoGroups []string, usageLimit int, userID string, ephemeral bool, allowExtraDNSLabels bool) (*types.SetupKey, error)
SaveSetupKey(ctx context.Context, accountID string, key *types.SetupKey, userID string) (*types.SetupKey, error)
CreateUser(ctx context.Context, accountID, initiatorUserID string, key *types.UserInfo) (*types.UserInfo, error)
DeleteUser(ctx context.Context, accountID, initiatorUserID string, targetUserID string) error
DeleteRegularUsers(ctx context.Context, accountID, initiatorUserID string, targetUserIDs []string, userInfos map[string]*types.UserInfo) error
InviteUser(ctx context.Context, accountID string, initiatorUserID string, targetUserID string) error
ListSetupKeys(ctx context.Context, accountID, userID string) ([]*types.SetupKey, error)
SaveUser(ctx context.Context, accountID, initiatorUserID string, update *types.User) (*types.UserInfo, error)
SaveOrAddUser(ctx context.Context, accountID, initiatorUserID string, update *types.User, addIfNotExists bool) (*types.UserInfo, error)
SaveOrAddUsers(ctx context.Context, accountID, initiatorUserID string, updates []*types.User, addIfNotExists bool) ([]*types.UserInfo, error)
GetSetupKey(ctx context.Context, accountID, userID, keyID string) (*types.SetupKey, error)
GetAccountByID(ctx context.Context, accountID string, userID string) (*types.Account, error)
AccountExists(ctx context.Context, accountID string) (bool, error)
GetAccountIDByUserID(ctx context.Context, userID, domain string) (string, error)
GetAccountIDFromUserAuth(ctx context.Context, userAuth nbcontext.UserAuth) (string, string, error)
DeleteAccount(ctx context.Context, accountID, userID string) error
GetUserByID(ctx context.Context, id string) (*types.User, error)
GetUserFromUserAuth(ctx context.Context, userAuth nbcontext.UserAuth) (*types.User, error)
ListUsers(ctx context.Context, accountID string) ([]*types.User, error)
GetPeers(ctx context.Context, accountID, userID, nameFilter, ipFilter string) ([]*nbpeer.Peer, error)
MarkPeerConnected(ctx context.Context, peerKey string, connected bool, realIP net.IP, accountID string) error
DeletePeer(ctx context.Context, accountID, peerID, userID string) error
UpdatePeer(ctx context.Context, accountID, userID string, peer *nbpeer.Peer) (*nbpeer.Peer, error)
GetNetworkMap(ctx context.Context, peerID string) (*types.NetworkMap, error)
GetPeerNetwork(ctx context.Context, peerID string) (*types.Network, error)
AddPeer(ctx context.Context, setupKey, userID string, peer *nbpeer.Peer) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, error)
CreatePAT(ctx context.Context, accountID string, initiatorUserID string, targetUserID string, tokenName string, expiresIn int) (*types.PersonalAccessTokenGenerated, error)
DeletePAT(ctx context.Context, accountID string, initiatorUserID string, targetUserID string, tokenID string) error
GetPAT(ctx context.Context, accountID string, initiatorUserID string, targetUserID string, tokenID string) (*types.PersonalAccessToken, error)
GetAllPATs(ctx context.Context, accountID string, initiatorUserID string, targetUserID string) ([]*types.PersonalAccessToken, error)
GetUsersFromAccount(ctx context.Context, accountID, userID string) (map[string]*types.UserInfo, error)
GetGroup(ctx context.Context, accountId, groupID, userID string) (*types.Group, error)
GetAllGroups(ctx context.Context, accountID, userID string) ([]*types.Group, error)
GetGroupByName(ctx context.Context, groupName, accountID string) (*types.Group, error)
SaveGroup(ctx context.Context, accountID, userID string, group *types.Group) error
SaveGroups(ctx context.Context, accountID, userID string, newGroups []*types.Group) error
DeleteGroup(ctx context.Context, accountId, userId, groupID string) error
DeleteGroups(ctx context.Context, accountId, userId string, groupIDs []string) error
GroupAddPeer(ctx context.Context, accountId, groupID, peerID string) error
GroupDeletePeer(ctx context.Context, accountId, groupID, peerID string) error
GetPeerGroups(ctx context.Context, accountID, peerID string) ([]*types.Group, error)
GetPolicy(ctx context.Context, accountID, policyID, userID string) (*types.Policy, error)
SavePolicy(ctx context.Context, accountID, userID string, policy *types.Policy) (*types.Policy, error)
DeletePolicy(ctx context.Context, accountID, policyID, userID string) error
ListPolicies(ctx context.Context, accountID, userID string) ([]*types.Policy, error)
GetRoute(ctx context.Context, accountID string, routeID route.ID, userID string) (*route.Route, error)
CreateRoute(ctx context.Context, accountID string, prefix netip.Prefix, networkType route.NetworkType, domains domain.List, peerID string, peerGroupIDs []string, description string, netID route.NetID, masquerade bool, metric int, groups, accessControlGroupIDs []string, enabled bool, userID string, keepRoute bool) (*route.Route, error)
SaveRoute(ctx context.Context, accountID, userID string, route *route.Route) error
DeleteRoute(ctx context.Context, accountID string, routeID route.ID, userID string) error
ListRoutes(ctx context.Context, accountID, userID string) ([]*route.Route, error)
GetNameServerGroup(ctx context.Context, accountID, userID, nsGroupID string) (*nbdns.NameServerGroup, error)
CreateNameServerGroup(ctx context.Context, accountID string, name, description string, nameServerList []nbdns.NameServer, groups []string, primary bool, domains []string, enabled bool, userID string, searchDomainsEnabled bool) (*nbdns.NameServerGroup, error)
SaveNameServerGroup(ctx context.Context, accountID, userID string, nsGroupToSave *nbdns.NameServerGroup) error
DeleteNameServerGroup(ctx context.Context, accountID, nsGroupID, userID string) error
ListNameServerGroups(ctx context.Context, accountID string, userID string) ([]*nbdns.NameServerGroup, error)
GetDNSDomain() string
StoreEvent(ctx context.Context, initiatorID, targetID, accountID string, activityID activity.ActivityDescriber, meta map[string]any)
GetEvents(ctx context.Context, accountID, userID string) ([]*activity.Event, error)
GetDNSSettings(ctx context.Context, accountID string, userID string) (*types.DNSSettings, error)
SaveDNSSettings(ctx context.Context, accountID string, userID string, dnsSettingsToSave *types.DNSSettings) error
GetPeer(ctx context.Context, accountID, peerID, userID string) (*nbpeer.Peer, error)
UpdateAccountSettings(ctx context.Context, accountID, userID string, newSettings *types.Settings) (*types.Account, error)
LoginPeer(ctx context.Context, login types.PeerLogin) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, error) // used by peer gRPC API
SyncPeer(ctx context.Context, sync types.PeerSync, accountID string) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, error) // used by peer gRPC API
GetAllConnectedPeers() (map[string]struct{}, error)
HasConnectedChannel(peerID string) bool
GetExternalCacheManager() ExternalCacheManager
GetPostureChecks(ctx context.Context, accountID, postureChecksID, userID string) (*posture.Checks, error)
SavePostureChecks(ctx context.Context, accountID, userID string, postureChecks *posture.Checks) (*posture.Checks, error)
DeletePostureChecks(ctx context.Context, accountID, postureChecksID, userID string) error
ListPostureChecks(ctx context.Context, accountID, userID string) ([]*posture.Checks, error)
GetIdpManager() idp.Manager
UpdateIntegratedValidatorGroups(ctx context.Context, accountID string, userID string, groups []string) error
GroupValidation(ctx context.Context, accountId string, groups []string) (bool, error)
GetValidatedPeers(ctx context.Context, accountID string) (map[string]struct{}, error)
SyncAndMarkPeer(ctx context.Context, accountID string, peerPubKey string, meta nbpeer.PeerSystemMeta, realIP net.IP) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, error)
OnPeerDisconnected(ctx context.Context, accountID string, peerPubKey string) error
SyncPeerMeta(ctx context.Context, peerPubKey string, meta nbpeer.PeerSystemMeta) error
FindExistingPostureCheck(accountID string, checks *posture.ChecksDefinition) (*posture.Checks, error)
GetAccountIDForPeerKey(ctx context.Context, peerKey string) (string, error)
GetAccountSettings(ctx context.Context, accountID string, userID string) (*types.Settings, error)
DeleteSetupKey(ctx context.Context, accountID, userID, keyID string) error
UpdateAccountPeers(ctx context.Context, accountID string)
BuildUserInfosForAccount(ctx context.Context, accountID, initiatorUserID string, accountUsers []*types.User) (map[string]*types.UserInfo, error)
SyncUserJWTGroups(ctx context.Context, userAuth nbcontext.UserAuth) error
GetStore() store.Store
}
+24 -5
View File
@@ -13,7 +13,11 @@ import (
"testing"
"time"
"github.com/golang/mock/gomock"
nbAccount "github.com/netbirdio/netbird/management/server/account"
"github.com/netbirdio/netbird/management/server/integrations/port_forwarding"
"github.com/netbirdio/netbird/management/server/settings"
"github.com/netbirdio/netbird/management/server/util"
resourceTypes "github.com/netbirdio/netbird/management/server/networks/resources/types"
@@ -36,7 +40,7 @@ import (
"github.com/netbirdio/netbird/route"
)
func verifyCanAddPeerToAccount(t *testing.T, manager AccountManager, account *types.Account, userID string) {
func verifyCanAddPeerToAccount(t *testing.T, manager nbAccount.Manager, account *types.Account, userID string) {
t.Helper()
peer := &nbpeer.Peer{
Key: "BhRPtynAAYRDy08+q4HTMsos8fs4plTP4NOSh7C1ry8=",
@@ -1403,7 +1407,7 @@ func TestAccountManager_DeletePeer(t *testing.T) {
assert.Equal(t, peer.IP.String(), fmt.Sprint(ev.Meta["ip"]))
}
func getEvent(t *testing.T, accountID string, manager AccountManager, eventType activity.Activity) *activity.Event {
func getEvent(t *testing.T, accountID string, manager nbAccount.Manager, eventType activity.Activity) *activity.Event {
t.Helper()
for {
select {
@@ -2793,6 +2797,8 @@ type TB interface {
Cleanup(func())
Helper()
TempDir() string
Errorf(format string, args ...interface{})
Fatalf(format string, args ...interface{})
}
func createManager(t TB) (*DefaultAccountManager, error) {
@@ -2809,7 +2815,20 @@ func createManager(t TB) (*DefaultAccountManager, error) {
return nil, err
}
manager, err := BuildManager(context.Background(), store, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock())
ctrl := gomock.NewController(t)
t.Cleanup(ctrl.Finish)
settingsMockManager := settings.NewMockManager(ctrl)
settingsMockManager.EXPECT().
GetExtraSettings(gomock.Any(), gomock.Any()).
Return(&types.ExtraSettings{}, nil).
AnyTimes()
settingsMockManager.EXPECT().
UpdateExtraSettings(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
Return(false, nil).
AnyTimes()
manager, err := BuildManager(context.Background(), store, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager)
if err != nil {
return nil, err
}
@@ -3024,7 +3043,7 @@ func BenchmarkLoginPeer_ExistingPeer(b *testing.B) {
b.ResetTimer()
start := time.Now()
for i := 0; i < b.N; i++ {
_, _, _, err := manager.LoginPeer(context.Background(), PeerLogin{
_, _, _, err := manager.LoginPeer(context.Background(), types.PeerLogin{
WireGuardPubKey: account.Peers["peer-1"].Key,
SSHKey: "someKey",
Meta: nbpeer.PeerSystemMeta{Hostname: strconv.Itoa(i)},
@@ -3099,7 +3118,7 @@ func BenchmarkLoginPeer_NewPeer(b *testing.B) {
b.ResetTimer()
start := time.Now()
for i := 0; i < b.N; i++ {
_, _, _, err := manager.LoginPeer(context.Background(), PeerLogin{
_, _, _, err := manager.LoginPeer(context.Background(), types.PeerLogin{
WireGuardPubKey: "some-new-key" + strconv.Itoa(i),
SSHKey: "someKey",
Meta: nbpeer.PeerSystemMeta{Hostname: strconv.Itoa(i)},
+5 -5
View File
@@ -172,8 +172,8 @@ const (
)
var activityMap = map[Activity]Code{
PeerAddedByUser: {"Peer added", "user.peer.add"},
PeerAddedWithSetupKey: {"Peer added", "setupkey.peer.add"},
PeerAddedByUser: {"Peer added", "peer.user.add"},
PeerAddedWithSetupKey: {"Peer added", "peer.setupkey.add"},
UserJoined: {"User joined", "user.join"},
UserInvited: {"User invited", "user.invite"},
AccountCreated: {"Account created", "account.create"},
@@ -232,9 +232,9 @@ var activityMap = map[Activity]Code{
PeerApproved: {"Peer approved", "peer.approve"},
PeerApprovalRevoked: {"Peer approval revoked", "peer.approval.revoke"},
TransferredOwnerRole: {"Transferred owner role", "transferred.owner.role"},
PostureCheckCreated: {"Posture check created", "posture.check.created"},
PostureCheckUpdated: {"Posture check updated", "posture.check.updated"},
PostureCheckDeleted: {"Posture check deleted", "posture.check.deleted"},
PostureCheckCreated: {"Posture check created", "posture.check.create"},
PostureCheckUpdated: {"Posture check updated", "posture.check.update"},
PostureCheckDeleted: {"Posture check deleted", "posture.check.delete"},
PeerInactivityExpirationEnabled: {"Peer inactivity expiration enabled", "peer.inactivity.expiration.enable"},
PeerInactivityExpirationDisabled: {"Peer inactivity expiration disabled", "peer.inactivity.expiration.disable"},
+113
View File
@@ -0,0 +1,113 @@
package cache
import (
"context"
"fmt"
"time"
"github.com/eko/gocache/lib/v4/cache"
"github.com/eko/gocache/lib/v4/marshaler"
"github.com/eko/gocache/lib/v4/store"
"github.com/eko/gocache/store/redis/v4"
"github.com/vmihailenco/msgpack/v5"
"github.com/netbirdio/netbird/management/server/idp"
)
const (
DefaultIDPCacheExpirationMax = 7 * 24 * time.Hour // 7 days
DefaultIDPCacheExpirationMin = 3 * 24 * time.Hour // 3 days
DefaultIDPCacheCleanupInterval = 30 * time.Minute
)
// UserDataCache is an interface that wraps the basic Get, Set and Delete methods for idp.UserData objects.
type UserDataCache interface {
Get(ctx context.Context, key string) (*idp.UserData, error)
Set(ctx context.Context, key string, value *idp.UserData, expiration time.Duration) error
Delete(ctx context.Context, key string) error
}
// UserDataCacheImpl is a struct that implements the UserDataCache interface.
type UserDataCacheImpl struct {
cache Marshaler
}
func (u *UserDataCacheImpl) Get(ctx context.Context, key string) (*idp.UserData, error) {
v, err := u.cache.Get(ctx, key, new(idp.UserData))
if err != nil {
return nil, err
}
data := v.(*idp.UserData)
return data, nil
}
func (u *UserDataCacheImpl) Set(ctx context.Context, key string, value *idp.UserData, expiration time.Duration) error {
return u.cache.Set(ctx, key, value, store.WithExpiration(expiration))
}
func (u *UserDataCacheImpl) Delete(ctx context.Context, key string) error {
return u.cache.Delete(ctx, key)
}
// NewUserDataCache creates a new UserDataCacheImpl object.
func NewUserDataCache(store store.StoreInterface) *UserDataCacheImpl {
simpleCache := cache.New[any](store)
if store.GetType() == redis.RedisType {
m := marshaler.New(simpleCache)
return &UserDataCacheImpl{cache: m}
}
return &UserDataCacheImpl{cache: &marshalerWraper{simpleCache}}
}
// AccountUserDataCache wraps the basic Get, Set and Delete methods for []*idp.UserData objects.
type AccountUserDataCache struct {
cache Marshaler
}
func (a *AccountUserDataCache) Get(ctx context.Context, key string) ([]*idp.UserData, error) {
var m []*idp.UserData
v, err := a.cache.Get(ctx, key, &m)
if err != nil {
return nil, err
}
switch v := v.(type) {
case []*idp.UserData:
return v, nil
case *[]*idp.UserData:
return *v, nil
case []byte:
return unmarshalUserData(v)
}
return nil, fmt.Errorf("unexpected type: %T", v)
}
func unmarshalUserData(data []byte) ([]*idp.UserData, error) {
returnObj := &[]*idp.UserData{}
err := msgpack.Unmarshal(data, returnObj)
if err != nil {
return nil, err
}
return *returnObj, nil
}
func (a *AccountUserDataCache) Set(ctx context.Context, key string, value []*idp.UserData, expiration time.Duration) error {
return a.cache.Set(ctx, key, value, store.WithExpiration(expiration))
}
func (a *AccountUserDataCache) Delete(ctx context.Context, key string) error {
return a.cache.Delete(ctx, key)
}
// NewAccountUserDataCache creates a new AccountUserDataCache object.
func NewAccountUserDataCache(loadableFunc cache.LoadFunction[any], store store.StoreInterface) *AccountUserDataCache {
simpleCache := cache.New[any](store)
loadable := cache.NewLoadable[any](loadableFunc, simpleCache)
if store.GetType() == redis.RedisType {
m := marshaler.New(loadable)
return &AccountUserDataCache{cache: m}
}
return &AccountUserDataCache{cache: &marshalerWraper{loadable}}
}
+135
View File
@@ -0,0 +1,135 @@
package cache_test
import (
"context"
"os"
"testing"
"time"
"github.com/eko/gocache/lib/v4/store"
"github.com/redis/go-redis/v9"
"github.com/testcontainers/testcontainers-go"
testcontainersredis "github.com/testcontainers/testcontainers-go/modules/redis"
"github.com/vmihailenco/msgpack/v5"
"github.com/netbirdio/netbird/management/server/cache"
"github.com/netbirdio/netbird/management/server/idp"
)
func TestNewIDPCacheManagers(t *testing.T) {
tt := []struct {
name string
redis bool
}{
{"memory", false},
{"redis", true},
}
for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
if tc.redis {
ctx := context.Background()
redisContainer, err := testcontainersredis.RunContainer(ctx, testcontainers.WithImage("redis:7"))
if err != nil {
t.Fatalf("couldn't start redis container: %s", err)
}
defer func() {
if err := redisContainer.Terminate(ctx); err != nil {
t.Logf("failed to terminate container: %s", err)
}
}()
redisURL, err := redisContainer.ConnectionString(ctx)
if err != nil {
t.Fatalf("couldn't get connection string: %s", err)
}
t.Setenv(cache.RedisStoreEnvVar, redisURL)
}
cacheStore, err := cache.NewStore(cache.DefaultIDPCacheExpirationMax, cache.DefaultIDPCacheCleanupInterval)
if err != nil {
t.Fatalf("couldn't create cache store: %s", err)
}
simple := cache.NewUserDataCache(cacheStore)
loadable := cache.NewAccountUserDataCache(loader, cacheStore)
ctx := context.Background()
value := &idp.UserData{ID: "v", Name: "vv"}
err = simple.Set(ctx, "key1", value, time.Minute)
if err != nil {
t.Errorf("couldn't set testing data: %s", err)
}
result, err := simple.Get(ctx, "key1")
if err != nil {
t.Errorf("couldn't get testing data: %s", err)
}
if value.ID != result.ID || value.Name != result.Name {
t.Errorf("value returned doesn't match testing data, got %v, expected %v", result, "value1")
}
values := []*idp.UserData{
{ID: "v2", Name: "v2v2"},
{ID: "v3", Name: "v3v3"},
{ID: "v4", Name: "v4v4"},
}
err = loadable.Set(ctx, "key2", values, time.Minute)
if err != nil {
t.Errorf("couldn't set testing data: %s", err)
}
result2, err := loadable.Get(ctx, "key2")
if err != nil {
t.Errorf("couldn't get testing data: %s", err)
}
if values[0].ID != result2[0].ID || values[0].Name != result2[0].Name {
t.Errorf("value returned doesn't match testing data, got %v, expected %v", result2[0], values[0])
}
if values[1].ID != result2[1].ID || values[1].Name != result2[1].Name {
t.Errorf("value returned doesn't match testing data, got %v, expected %v", result2[1], values[1])
}
// checking with direct store client
if tc.redis {
// wait for redis to sync
options, err := redis.ParseURL(os.Getenv(cache.RedisStoreEnvVar))
if err != nil {
t.Fatalf("parsing redis cache url: %s", err)
}
redisClient := redis.NewClient(options)
_, err = redisClient.Get(ctx, "loadKey").Result()
if err == nil {
t.Errorf("shouldn't find testing data from redis")
}
}
// testing loadable capability
result2, err = loadable.Get(ctx, "loadKey")
if err != nil {
t.Errorf("couldn't get testing data: %s", err)
}
if loadData[0].ID != result2[0].ID || loadData[0].Name != result2[0].Name {
t.Errorf("value returned doesn't match testing data, got %v, expected %v", result2[0], loadData[0])
}
if loadData[1].ID != result2[1].ID || loadData[1].Name != result2[1].Name {
t.Errorf("value returned doesn't match testing data, got %v, expected %v", result2[1], loadData[1])
}
})
}
}
var loadData = []*idp.UserData{
{ID: "a", Name: "aa"},
{ID: "b", Name: "bb"},
{ID: "c", Name: "cc"},
}
func loader(ctx context.Context, key any) (any, []store.Option, error) {
bytes, err := msgpack.Marshal(loadData)
if err != nil {
return nil, nil, err
}
return bytes, nil, nil
}
+35
View File
@@ -0,0 +1,35 @@
package cache
import (
"context"
"github.com/eko/gocache/lib/v4/store"
)
type Marshaler interface {
Get(ctx context.Context, key any, returnObj any) (any, error)
Set(ctx context.Context, key, object any, options ...store.Option) error
Delete(ctx context.Context, key any) error
}
type cacher[T any] interface {
Get(ctx context.Context, key any) (T, error)
Set(ctx context.Context, key any, object T, options ...store.Option) error
Delete(ctx context.Context, key any) error
}
type marshalerWraper struct {
cache cacher[any]
}
func (m marshalerWraper) Get(ctx context.Context, key any, _ any) (any, error) {
return m.cache.Get(ctx, key)
}
func (m marshalerWraper) Set(ctx context.Context, key, object any, options ...store.Option) error {
return m.cache.Set(ctx, key, object, options...)
}
func (m marshalerWraper) Delete(ctx context.Context, key any) error {
return m.cache.Delete(ctx, key)
}
+50
View File
@@ -0,0 +1,50 @@
package cache
import (
"context"
"fmt"
"os"
"time"
"github.com/eko/gocache/lib/v4/store"
gocache_store "github.com/eko/gocache/store/go_cache/v4"
redis_store "github.com/eko/gocache/store/redis/v4"
gocache "github.com/patrickmn/go-cache"
"github.com/redis/go-redis/v9"
)
// RedisStoreEnvVar is the environment variable that determines if a redis store should be used.
// The value should follow redis URL format. https://github.com/redis/redis-specifications/blob/master/uri/redis.txt
const RedisStoreEnvVar = "NB_IDP_CACHE_REDIS_ADDRESS"
// NewStore creates a new cache store with the given max timeout and cleanup interval. It checks for the environment Variable RedisStoreEnvVar
// to determine if a redis store should be used. If the environment variable is set, it will attempt to connect to the redis store.
func NewStore(maxTimeout, cleanupInterval time.Duration) (store.StoreInterface, error) {
redisAddr := os.Getenv(RedisStoreEnvVar)
if redisAddr != "" {
return getRedisStore(redisAddr)
}
goc := gocache.New(maxTimeout, cleanupInterval)
return gocache_store.NewGoCache(goc), nil
}
func getRedisStore(redisEnvAddr string) (store.StoreInterface, error) {
options, err := redis.ParseURL(redisEnvAddr)
if err != nil {
return nil, fmt.Errorf("parsing redis cache url: %s", err)
}
options.MaxIdleConns = 6
options.MinIdleConns = 3
options.MaxActiveConns = 100
redisClient := redis.NewClient(options)
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
_, err = redisClient.Ping(ctx).Result()
if err != nil {
return nil, err
}
return redis_store.NewRedis(redisClient), nil
}
+105
View File
@@ -0,0 +1,105 @@
package cache_test
import (
"context"
"testing"
"time"
"github.com/eko/gocache/lib/v4/store"
"github.com/redis/go-redis/v9"
"github.com/testcontainers/testcontainers-go"
testcontainersredis "github.com/testcontainers/testcontainers-go/modules/redis"
"github.com/netbirdio/netbird/management/server/cache"
)
func TestMemoryStore(t *testing.T) {
memStore, err := cache.NewStore(100*time.Millisecond, 300*time.Millisecond)
if err != nil {
t.Fatalf("couldn't create memory store: %s", err)
}
ctx := context.Background()
key, value := "testing", "tested"
err = memStore.Set(ctx, key, value)
if err != nil {
t.Errorf("couldn't set testing data: %s", err)
}
result, err := memStore.Get(ctx, key)
if err != nil {
t.Errorf("couldn't get testing data: %s", err)
}
if value != result.(string) {
t.Errorf("value returned doesn't match testing data, got %s, expected %s", result, value)
}
// test expiration
time.Sleep(300 * time.Millisecond)
_, err = memStore.Get(ctx, key)
if err == nil {
t.Error("value should not be found")
}
}
func TestRedisStoreConnectionFailure(t *testing.T) {
t.Setenv(cache.RedisStoreEnvVar, "redis://127.0.0.1:6379")
_, err := cache.NewStore(10*time.Millisecond, 30*time.Millisecond)
if err == nil {
t.Fatal("getting redis cache store should return error")
}
}
func TestRedisStoreConnectionSuccess(t *testing.T) {
ctx := context.Background()
redisContainer, err := testcontainersredis.RunContainer(ctx, testcontainers.WithImage("redis:7"))
if err != nil {
t.Fatalf("couldn't start redis container: %s", err)
}
defer func() {
if err := redisContainer.Terminate(ctx); err != nil {
t.Logf("failed to terminate container: %s", err)
}
}()
redisURL, err := redisContainer.ConnectionString(ctx)
if err != nil {
t.Fatalf("couldn't get connection string: %s", err)
}
t.Setenv(cache.RedisStoreEnvVar, redisURL)
redisStore, err := cache.NewStore(100*time.Millisecond, 300*time.Millisecond)
if err != nil {
t.Fatalf("couldn't create redis store: %s", err)
}
key, value := "testing", "tested"
err = redisStore.Set(ctx, key, value, store.WithExpiration(100*time.Millisecond))
if err != nil {
t.Errorf("couldn't set testing data: %s", err)
}
result, err := redisStore.Get(ctx, key)
if err != nil {
t.Errorf("couldn't get testing data: %s", err)
}
if value != result.(string) {
t.Errorf("value returned doesn't match testing data, got %s, expected %s", result, value)
}
options, err := redis.ParseURL(redisURL)
if err != nil {
t.Errorf("parsing redis cache url: %s", err)
}
redisClient := redis.NewClient(options)
r, e := redisClient.Get(ctx, key).Result()
if e != nil {
t.Errorf("couldn't get testing data from redis: %s", e)
}
if value != r {
t.Errorf("value returned from redis doesn't match testing data, got %s, expected %s", r, value)
}
// test expiration
time.Sleep(300 * time.Millisecond)
_, err = redisStore.Get(ctx, key)
if err == nil {
t.Error("value should not be found")
}
}
+8 -1
View File
@@ -8,10 +8,12 @@ import (
"testing"
"time"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
nbdns "github.com/netbirdio/netbird/dns"
"github.com/netbirdio/netbird/management/server/integrations/port_forwarding"
"github.com/netbirdio/netbird/management/server/settings"
"github.com/netbirdio/netbird/management/server/store"
"github.com/netbirdio/netbird/management/server/telemetry"
"github.com/netbirdio/netbird/management/server/types"
@@ -209,7 +211,12 @@ func createDNSManager(t *testing.T) (*DefaultAccountManager, error) {
metrics, err := telemetry.NewDefaultAppMetrics(context.Background())
require.NoError(t, err)
return BuildManager(context.Background(), store, NewPeersUpdateManager(nil), nil, "", "netbird.test", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock())
ctrl := gomock.NewController(t)
t.Cleanup(ctrl.Finish)
settingsMockManager := settings.NewMockManager(ctrl)
return BuildManager(context.Background(), store, NewPeersUpdateManager(nil), nil, "", "netbird.test", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager)
}
func createDNSStore(t *testing.T) (store.Store, error) {
+3 -2
View File
@@ -7,6 +7,7 @@ import (
log "github.com/sirupsen/logrus"
nbAccount "github.com/netbirdio/netbird/management/server/account"
"github.com/netbirdio/netbird/management/server/activity"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
"github.com/netbirdio/netbird/management/server/store"
@@ -34,7 +35,7 @@ type ephemeralPeer struct {
// automatically. Inactivity means the peer disconnected from the Management server.
type EphemeralManager struct {
store store.Store
accountManager AccountManager
accountManager nbAccount.Manager
headPeer *ephemeralPeer
tailPeer *ephemeralPeer
@@ -43,7 +44,7 @@ type EphemeralManager struct {
}
// NewEphemeralManager instantiate new EphemeralManager
func NewEphemeralManager(store store.Store, accountManager AccountManager) *EphemeralManager {
func NewEphemeralManager(store store.Store, accountManager nbAccount.Manager) *EphemeralManager {
return &EphemeralManager{
store: store,
accountManager: accountManager,
+6 -1
View File
@@ -6,6 +6,7 @@ import (
"testing"
"time"
nbAccount "github.com/netbirdio/netbird/management/server/account"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
"github.com/netbirdio/netbird/management/server/store"
"github.com/netbirdio/netbird/management/server/types"
@@ -27,7 +28,7 @@ func (s *MockStore) GetAllEphemeralPeers(_ context.Context, _ store.LockingStren
}
type MocAccountManager struct {
AccountManager
nbAccount.Manager
store *MockStore
}
@@ -36,6 +37,10 @@ func (a MocAccountManager) DeletePeer(_ context.Context, accountID, peerID, user
return nil //nolint:nil
}
func (a MocAccountManager) GetStore() store.Store {
return a.store
}
func TestNewManager(t *testing.T) {
startTime := time.Now()
timeNow = func() time.Time {
+3 -3
View File
@@ -4,7 +4,7 @@ import (
"context"
"fmt"
s "github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/management/server/account"
"github.com/netbirdio/netbird/management/server/activity"
"github.com/netbirdio/netbird/management/server/http/api"
"github.com/netbirdio/netbird/management/server/permissions"
@@ -24,13 +24,13 @@ type Manager interface {
type managerImpl struct {
store store.Store
permissionsManager permissions.Manager
accountManager s.AccountManager
accountManager account.Manager
}
type mockManager struct {
}
func NewManager(store store.Store, permissionsManager permissions.Manager, accountManager s.AccountManager) Manager {
func NewManager(store store.Store, permissionsManager permissions.Manager, accountManager account.Manager) Manager {
return &managerImpl{
store: store,
permissionsManager: permissionsManager,
+29 -17
View File
@@ -18,8 +18,11 @@ import (
"google.golang.org/grpc/peer"
"google.golang.org/grpc/status"
integrationsConfig "github.com/netbirdio/management-integrations/integrations/config"
"github.com/netbirdio/netbird/encryption"
"github.com/netbirdio/netbird/management/proto"
"github.com/netbirdio/netbird/management/server/account"
"github.com/netbirdio/netbird/management/server/activity"
"github.com/netbirdio/netbird/management/server/auth"
nbContext "github.com/netbirdio/netbird/management/server/context"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
@@ -32,7 +35,7 @@ import (
// GRPCServer an instance of a Management gRPC API server
type GRPCServer struct {
accountManager AccountManager
accountManager account.Manager
settingsManager settings.Manager
wgKey wgtypes.Key
proto.UnimplementedManagementServiceServer
@@ -49,7 +52,7 @@ type GRPCServer struct {
func NewServer(
ctx context.Context,
config *Config,
accountManager AccountManager,
accountManager account.Manager,
settingsManager settings.Manager,
peersUpdateManager *PeersUpdateManager,
secretsManager SecretsManager,
@@ -184,7 +187,7 @@ func (s *GRPCServer) Sync(req *proto.EncryptedMessage, srv proto.ManagementServi
s.ephemeralManager.OnPeerConnected(ctx, peer)
s.secretsManager.SetupRefresh(ctx, peer.ID)
s.secretsManager.SetupRefresh(ctx, accountID, peer.ID)
if s.appMetrics != nil {
s.appMetrics.GRPCMetrics().CountSyncRequestDuration(time.Since(reqStart))
@@ -457,7 +460,7 @@ func (s *GRPCServer) Login(ctx context.Context, req *proto.EncryptedMessage) (*p
sshKey = loginReq.GetPeerKeys().GetSshPubKey()
}
peer, netMap, postureChecks, err := s.accountManager.LoginPeer(ctx, PeerLogin{
peer, netMap, postureChecks, err := s.accountManager.LoginPeer(ctx, types.PeerLogin{
WireGuardPubKey: peerKey.String(),
SSHKey: string(sshKey),
Meta: extractPeerMeta(ctx, loginReq.GetMeta()),
@@ -486,7 +489,7 @@ func (s *GRPCServer) Login(ctx context.Context, req *proto.EncryptedMessage) (*p
// if peer has reached this point then it has logged in
loginResp := &proto.LoginResponse{
NetbirdConfig: toNetbirdConfig(s.config, nil, relayToken),
NetbirdConfig: toNetbirdConfig(s.config, nil, relayToken, nil),
PeerConfig: toPeerConfig(peer, netMap.Network, s.accountManager.GetDNSDomain(), false),
Checks: toProtocolChecks(ctx, postureChecks),
}
@@ -544,7 +547,7 @@ func ToResponseProto(configProto Protocol) proto.HostConfig_Protocol {
}
}
func toNetbirdConfig(config *Config, turnCredentials *Token, relayToken *Token) *proto.NetbirdConfig {
func toNetbirdConfig(config *Config, turnCredentials *Token, relayToken *Token, extraSettings *types.ExtraSettings) *proto.NetbirdConfig {
if config == nil {
return nil
}
@@ -592,15 +595,24 @@ func toNetbirdConfig(config *Config, turnCredentials *Token, relayToken *Token)
}
}
return &proto.NetbirdConfig{
Stuns: stuns,
Turns: turns,
Signal: &proto.HostConfig{
var signalCfg *proto.HostConfig
if config.Signal != nil {
signalCfg = &proto.HostConfig{
Uri: config.Signal.URI,
Protocol: ToResponseProto(config.Signal.Proto),
},
Relay: relayCfg,
}
}
nbConfig := &proto.NetbirdConfig{
Stuns: stuns,
Turns: turns,
Signal: signalCfg,
Relay: relayCfg,
}
integrationsConfig.ExtendNetBirdConfig(nbConfig, extraSettings)
return nbConfig
}
func toPeerConfig(peer *nbpeer.Peer, network *types.Network, dnsName string, dnsResolutionOnRoutingPeerEnabled bool) *proto.PeerConfig {
@@ -614,10 +626,10 @@ func toPeerConfig(peer *nbpeer.Peer, network *types.Network, dnsName string, dns
}
}
func toSyncResponse(ctx context.Context, config *Config, peer *nbpeer.Peer, turnCredentials *Token, relayCredentials *Token, networkMap *types.NetworkMap, dnsName string, checks []*posture.Checks, dnsCache *DNSConfigCache, dnsResolutionOnRoutingPeerEnbled bool) *proto.SyncResponse {
func toSyncResponse(ctx context.Context, config *Config, peer *nbpeer.Peer, turnCredentials *Token, relayCredentials *Token, networkMap *types.NetworkMap, dnsName string, checks []*posture.Checks, dnsCache *DNSConfigCache, dnsResolutionOnRoutingPeerEnabled bool, extraSettings *types.ExtraSettings) *proto.SyncResponse {
response := &proto.SyncResponse{
NetbirdConfig: toNetbirdConfig(config, turnCredentials, relayCredentials),
PeerConfig: toPeerConfig(peer, networkMap.Network, dnsName, dnsResolutionOnRoutingPeerEnbled),
NetbirdConfig: toNetbirdConfig(config, turnCredentials, relayCredentials, extraSettings),
PeerConfig: toPeerConfig(peer, networkMap.Network, dnsName, dnsResolutionOnRoutingPeerEnabled),
NetworkMap: &proto.NetworkMap{
Serial: networkMap.Network.CurrentSerial(),
Routes: toProtocolRoutes(networkMap.Routes),
@@ -693,12 +705,12 @@ func (s *GRPCServer) sendInitialSync(ctx context.Context, peerKey wgtypes.Key, p
}
}
settings, err := s.settingsManager.GetSettings(ctx, peer.AccountID, peer.UserID)
settings, err := s.settingsManager.GetSettings(ctx, peer.AccountID, activity.SystemInitiator)
if err != nil {
return status.Errorf(codes.Internal, "error handling request")
}
plainResp := toSyncResponse(ctx, s.config, peer, turnToken, relayToken, networkMap, s.accountManager.GetDNSDomain(), postureChecks, nil, settings.RoutingPeerDNSResolutionEnabled)
plainResp := toSyncResponse(ctx, s.config, peer, turnToken, relayToken, networkMap, s.accountManager.GetDNSDomain(), postureChecks, nil, settings.RoutingPeerDNSResolutionEnabled, settings.Extra)
encryptedResp, err := encryption.EncryptMessage(peerKey, s.wgKey, plainResp)
if err != nil {
+166 -3
View File
@@ -106,6 +106,18 @@ components:
description: (Cloud only) Enables or disables peer approval globally. If enabled, all peers added will be in pending state until approved by an admin.
type: boolean
example: true
network_traffic_logs_enabled:
description: Enables or disables network traffic logs. If enabled, all network traffic logs from peers will be stored.
type: boolean
example: true
network_traffic_packet_counter_enabled:
description: Enables or disables network traffic packet counter. If enabled, network packets and their size will be counted and reported. (This can have an slight impact on performance)
type: boolean
example: true
required:
- peer_approval_enabled
- network_traffic_logs_enabled
- network_traffic_packet_counter_enabled
AccountRequest:
type: object
properties:
@@ -1817,6 +1829,137 @@ components:
- ingress_start
- ingress_end
- protocol
NetworkTrafficLocation:
type: object
properties:
city_name:
type: string
description: "Name of the city (if known)."
country_code:
type: string
description: "ISO country code (if known)."
required:
- city_name
- country_code
NetworkTrafficEndpoint:
type: object
properties:
id:
type: string
description: "ID of this endpoint (e.g., peer ID or resource ID)."
type:
type: string
description: "Type of the endpoint object (e.g., UNKNOWN, PEER, HOST_RESOURCE)."
name:
type: string
description: "Name is the name of the endpoint object (e.g., a peer name)."
geo_location:
$ref: '#/components/schemas/NetworkTrafficLocation'
os:
type: string
nullable: true
description: "Operating system of the peer, if applicable."
address:
type: string
description: "IP address (and possibly port) in string form."
example: "100.64.0.10:51820"
dns_label:
type: string
nullable: true
description: "DNS label/name if available."
required:
- id
- type
- name
- geo_location
- os
- address
- dns_label
NetworkTrafficEvent:
type: object
properties:
id:
type: string
description: "ID of the event. Unique."
flow_id:
type: string
description: "FlowID is the ID of the connection flow. Not unique because it can be the same for multiple events (e.g., start and end of the connection)."
reporter_id:
type: string
description: "ID of the reporter of the event (e.g., the peer that reported the event)."
timestamp:
type: string
format: date-time
description: "Timestamp of the event."
source:
$ref: '#/components/schemas/NetworkTrafficEndpoint'
user_id:
type: string
nullable: true
description: "UserID is the ID of the user that initiated the event (can be empty as not every event is user-initiated)."
user_email:
type: string
nullable: true
description: "Email of the user who initiated the event (if any)."
user_name:
type: string
nullable: true
description: "Name of the user who initiated the event (if any)."
destination:
$ref: '#/components/schemas/NetworkTrafficEndpoint'
protocol:
type: integer
description: "Protocol is the protocol of the traffic (e.g. 1 = ICMP, 6 = TCP, 17 = UDP, etc.)."
type:
type: string
description: "Type of the event (e.g. TYPE_UNKNOWN, TYPE_START, TYPE_END, TYPE_DROP)."
direction:
type: string
description: "Direction of the traffic (e.g. DIRECTION_UNKNOWN, INGRESS, EGRESS)."
rx_bytes:
type: integer
description: "Number of bytes received."
rx_packets:
type: integer
description: "Number of packets received."
tx_bytes:
type: integer
description: "Number of bytes transmitted."
tx_packets:
type: integer
description: "Number of packets transmitted."
policy_id:
type: string
description: "ID of the policy that allowed this event."
policy_name:
type: string
description: "Name of the policy that allowed this event."
icmp_type:
type: integer
description: "ICMP type (if applicable)."
icmp_code:
type: integer
description: "ICMP code (if applicable)."
required:
- id
- flow_id
- reporter_id
- timestamp
- source
- user_id
- user_email
- destination
- protocol
- type
- direction
- rx_bytes
- rx_packets
- tx_bytes
- tx_packets
- policy_id
- policy_name
- icmp_type
- icmp_code
responses:
not_found:
description: Resource not found
@@ -3972,10 +4115,10 @@ paths:
"$ref": "#/components/responses/forbidden"
'500':
"$ref": "#/components/responses/internal_error"
/api/events:
/api/events/audit:
get:
summary: List all Events
description: Returns a list of all events
summary: List all Audit Events
description: Returns a list of all audit events
tags: [ Events ]
security:
- BearerAuth: [ ]
@@ -3997,6 +4140,26 @@ paths:
"$ref": "#/components/responses/forbidden"
'500':
"$ref": "#/components/responses/internal_error"
/api/events/network-traffic:
get:
summary: List all Network Traffic Events
description: Returns a list of all network traffic events
tags: [ Events ]
x-cloud-only: true
x-experimental: true
responses:
"200":
description: List of network traffic events
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/NetworkTrafficEvent"
"401":
$ref: "#/components/responses/requires_authentication"
"500":
$ref: "#/components/responses/internal_error"
/api/posture-checks:
get:
summary: List all Posture Checks
+98 -1
View File
@@ -230,8 +230,14 @@ type Account struct {
// AccountExtraSettings defines model for AccountExtraSettings.
type AccountExtraSettings struct {
// NetworkTrafficLogsEnabled Enables or disables network traffic logs. If enabled, all network traffic logs from peers will be stored.
NetworkTrafficLogsEnabled bool `json:"network_traffic_logs_enabled"`
// NetworkTrafficPacketCounterEnabled Enables or disables network traffic packet counter. If enabled, network packets and their size will be counted and reported. (This can have an slight impact on performance)
NetworkTrafficPacketCounterEnabled bool `json:"network_traffic_packet_counter_enabled"`
// PeerApprovalEnabled (Cloud only) Enables or disables peer approval globally. If enabled, all peers added will be in pending state until approved by an admin.
PeerApprovalEnabled *bool `json:"peer_approval_enabled,omitempty"`
PeerApprovalEnabled bool `json:"peer_approval_enabled"`
}
// AccountRequest defines model for AccountRequest.
@@ -817,6 +823,97 @@ type NetworkRouterRequest struct {
PeerGroups *[]string `json:"peer_groups,omitempty"`
}
// NetworkTrafficEndpoint defines model for NetworkTrafficEndpoint.
type NetworkTrafficEndpoint struct {
// Address IP address (and possibly port) in string form.
Address string `json:"address"`
// DnsLabel DNS label/name if available.
DnsLabel *string `json:"dns_label"`
GeoLocation NetworkTrafficLocation `json:"geo_location"`
// Id ID of this endpoint (e.g., peer ID or resource ID).
Id string `json:"id"`
// Name Name is the name of the endpoint object (e.g., a peer name).
Name string `json:"name"`
// Os Operating system of the peer, if applicable.
Os *string `json:"os"`
// Type Type of the endpoint object (e.g., UNKNOWN, PEER, HOST_RESOURCE).
Type string `json:"type"`
}
// NetworkTrafficEvent defines model for NetworkTrafficEvent.
type NetworkTrafficEvent struct {
Destination NetworkTrafficEndpoint `json:"destination"`
// Direction Direction of the traffic (e.g. DIRECTION_UNKNOWN, INGRESS, EGRESS).
Direction string `json:"direction"`
// FlowId FlowID is the ID of the connection flow. Not unique because it can be the same for multiple events (e.g., start and end of the connection).
FlowId string `json:"flow_id"`
// IcmpCode ICMP code (if applicable).
IcmpCode int `json:"icmp_code"`
// IcmpType ICMP type (if applicable).
IcmpType int `json:"icmp_type"`
// Id ID of the event. Unique.
Id string `json:"id"`
// PolicyId ID of the policy that allowed this event.
PolicyId string `json:"policy_id"`
// PolicyName Name of the policy that allowed this event.
PolicyName string `json:"policy_name"`
// Protocol Protocol is the protocol of the traffic (e.g. 1 = ICMP, 6 = TCP, 17 = UDP, etc.).
Protocol int `json:"protocol"`
// ReporterId ID of the reporter of the event (e.g., the peer that reported the event).
ReporterId string `json:"reporter_id"`
// RxBytes Number of bytes received.
RxBytes int `json:"rx_bytes"`
// RxPackets Number of packets received.
RxPackets int `json:"rx_packets"`
Source NetworkTrafficEndpoint `json:"source"`
// Timestamp Timestamp of the event.
Timestamp time.Time `json:"timestamp"`
// TxBytes Number of bytes transmitted.
TxBytes int `json:"tx_bytes"`
// TxPackets Number of packets transmitted.
TxPackets int `json:"tx_packets"`
// Type Type of the event (e.g. TYPE_UNKNOWN, TYPE_START, TYPE_END, TYPE_DROP).
Type string `json:"type"`
// UserEmail Email of the user who initiated the event (if any).
UserEmail *string `json:"user_email"`
// UserId UserID is the ID of the user that initiated the event (can be empty as not every event is user-initiated).
UserId *string `json:"user_id"`
// UserName Name of the user who initiated the event (if any).
UserName *string `json:"user_name"`
}
// NetworkTrafficLocation defines model for NetworkTrafficLocation.
type NetworkTrafficLocation struct {
// CityName Name of the city (if known).
CityName string `json:"city_name"`
// CountryCode ISO country code (if known).
CountryCode string `json:"country_code"`
}
// OSVersionCheck Posture check for the version of operating system
type OSVersionCheck struct {
// Android Posture check for the version of operating system
+7 -4
View File
@@ -10,10 +10,12 @@ import (
"github.com/netbirdio/management-integrations/integrations"
"github.com/netbirdio/netbird/management/server/account"
"github.com/netbirdio/netbird/management/server/settings"
"github.com/netbirdio/netbird/management/server/integrations/port_forwarding"
"github.com/netbirdio/netbird/management/server/permissions"
s "github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/management/server/auth"
"github.com/netbirdio/netbird/management/server/geolocation"
nbgroups "github.com/netbirdio/netbird/management/server/groups"
@@ -41,7 +43,7 @@ const apiPrefix = "/api"
// NewAPIHandler creates the Management service HTTP API handler registering all the available endpoints.
func NewAPIHandler(
ctx context.Context,
accountManager s.AccountManager,
accountManager account.Manager,
networksManager nbnetworks.Manager,
resourceManager resources.Manager,
routerManager routers.Manager,
@@ -53,6 +55,7 @@ func NewAPIHandler(
proxyController port_forwarding.Controller,
permissionsManager permissions.Manager,
peersManager nbpeers.Manager,
settingsManager settings.Manager,
) (http.Handler, error) {
authMiddleware := middleware.NewAuthMiddleware(
@@ -73,11 +76,11 @@ func NewAPIHandler(
router.Use(metricsMiddleware.Handler, corsMiddleware.Handler, authMiddleware.Handler, acMiddleware.Handler)
if _, err := integrations.RegisterHandlers(ctx, prefix, router, accountManager, integratedValidator, appMetrics.GetMeter(), permissionsManager, peersManager, proxyController); err != nil {
if _, err := integrations.RegisterHandlers(ctx, prefix, router, accountManager, integratedValidator, appMetrics.GetMeter(), permissionsManager, peersManager, proxyController, settingsManager); err != nil {
return nil, fmt.Errorf("register integrations endpoints: %w", err)
}
accounts.AddEndpoints(accountManager, router)
accounts.AddEndpoints(accountManager, settingsManager, router)
peers.AddEndpoints(accountManager, router)
users.AddEndpoints(accountManager, router)
setup_keys.AddEndpoints(accountManager, router)
@@ -7,31 +7,33 @@ import (
"github.com/gorilla/mux"
"github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/management/server/account"
nbcontext "github.com/netbirdio/netbird/management/server/context"
"github.com/netbirdio/netbird/management/server/http/api"
"github.com/netbirdio/netbird/management/server/http/util"
"github.com/netbirdio/netbird/management/server/settings"
"github.com/netbirdio/netbird/management/server/status"
"github.com/netbirdio/netbird/management/server/types"
)
// handler is a handler that handles the server.Account HTTP endpoints
type handler struct {
accountManager server.AccountManager
accountManager account.Manager
settingsManager settings.Manager
}
func AddEndpoints(accountManager server.AccountManager, router *mux.Router) {
accountsHandler := newHandler(accountManager)
func AddEndpoints(accountManager account.Manager, settingsManager settings.Manager, router *mux.Router) {
accountsHandler := newHandler(accountManager, settingsManager)
router.HandleFunc("/accounts/{accountId}", accountsHandler.updateAccount).Methods("PUT", "OPTIONS")
router.HandleFunc("/accounts/{accountId}", accountsHandler.deleteAccount).Methods("DELETE", "OPTIONS")
router.HandleFunc("/accounts", accountsHandler.getAllAccounts).Methods("GET", "OPTIONS")
}
// newHandler creates a new handler HTTP handler
func newHandler(accountManager server.AccountManager) *handler {
func newHandler(accountManager account.Manager, settingsManager settings.Manager) *handler {
return &handler{
accountManager: accountManager,
accountManager: accountManager,
settingsManager: settingsManager,
}
}
@@ -45,7 +47,7 @@ func (h *handler) getAllAccounts(w http.ResponseWriter, r *http.Request) {
accountID, userID := userAuth.AccountId, userAuth.UserId
settings, err := h.accountManager.GetAccountSettings(r.Context(), accountID, userID)
settings, err := h.settingsManager.GetSettings(r.Context(), accountID, userID)
if err != nil {
util.WriteError(r.Context(), err, w)
return
@@ -89,7 +91,11 @@ func (h *handler) updateAccount(w http.ResponseWriter, r *http.Request) {
}
if req.Settings.Extra != nil {
settings.Extra = &account.ExtraSettings{PeerApprovalEnabled: *req.Settings.Extra.PeerApprovalEnabled}
settings.Extra = &types.ExtraSettings{
PeerApprovalEnabled: req.Settings.Extra.PeerApprovalEnabled,
FlowEnabled: req.Settings.Extra.NetworkTrafficLogsEnabled,
FlowPacketCounterEnabled: req.Settings.Extra.NetworkTrafficPacketCounterEnabled,
}
}
if req.Settings.JwtGroupsEnabled != nil {
@@ -163,7 +169,11 @@ func toAccountResponse(accountID string, settings *types.Settings) *api.Account
}
if settings.Extra != nil {
apiSettings.Extra = &api.AccountExtraSettings{PeerApprovalEnabled: &settings.Extra.PeerApprovalEnabled}
apiSettings.Extra = &api.AccountExtraSettings{
PeerApprovalEnabled: settings.Extra.PeerApprovalEnabled,
NetworkTrafficLogsEnabled: settings.Extra.FlowEnabled,
NetworkTrafficPacketCounterEnabled: settings.Extra.FlowPacketCounterEnabled,
}
}
return &api.Account{
@@ -10,17 +10,27 @@ import (
"testing"
"time"
"github.com/golang/mock/gomock"
"github.com/gorilla/mux"
"github.com/stretchr/testify/assert"
nbcontext "github.com/netbirdio/netbird/management/server/context"
"github.com/netbirdio/netbird/management/server/http/api"
"github.com/netbirdio/netbird/management/server/mock_server"
"github.com/netbirdio/netbird/management/server/settings"
"github.com/netbirdio/netbird/management/server/status"
"github.com/netbirdio/netbird/management/server/types"
)
func initAccountsTestData(account *types.Account) *handler {
func initAccountsTestData(t *testing.T, account *types.Account) *handler {
ctrl := gomock.NewController(t)
t.Cleanup(ctrl.Finish)
settingsMockManager := settings.NewMockManager(ctrl)
settingsMockManager.EXPECT().
GetSettings(gomock.Any(), account.Id, "test_user").
Return(account.Settings, nil).
AnyTimes()
return &handler{
accountManager: &mock_server.MockAccountManager{
GetAccountSettingsFunc: func(ctx context.Context, accountID string, userID string) (*types.Settings, error) {
@@ -41,6 +51,7 @@ func initAccountsTestData(account *types.Account) *handler {
return accCopy, nil
},
},
settingsManager: settingsMockManager,
}
}
@@ -51,7 +62,7 @@ func TestAccounts_AccountsHandler(t *testing.T) {
sr := func(v string) *string { return &v }
br := func(v bool) *bool { return &v }
handler := initAccountsTestData(&types.Account{
handler := initAccountsTestData(t, &types.Account{
Id: accountID,
Domain: "hotmail.com",
Network: types.NewNetwork(),
@@ -7,7 +7,7 @@ import (
"github.com/gorilla/mux"
log "github.com/sirupsen/logrus"
"github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/management/server/account"
nbcontext "github.com/netbirdio/netbird/management/server/context"
"github.com/netbirdio/netbird/management/server/http/api"
"github.com/netbirdio/netbird/management/server/http/util"
@@ -16,22 +16,22 @@ import (
// dnsSettingsHandler is a handler that returns the DNS settings of the account
type dnsSettingsHandler struct {
accountManager server.AccountManager
accountManager account.Manager
}
func AddEndpoints(accountManager server.AccountManager, router *mux.Router) {
func AddEndpoints(accountManager account.Manager, router *mux.Router) {
addDNSSettingEndpoint(accountManager, router)
addDNSNameserversEndpoint(accountManager, router)
}
func addDNSSettingEndpoint(accountManager server.AccountManager, router *mux.Router) {
func addDNSSettingEndpoint(accountManager account.Manager, router *mux.Router) {
dnsSettingsHandler := newDNSSettingsHandler(accountManager)
router.HandleFunc("/dns/settings", dnsSettingsHandler.getDNSSettings).Methods("GET", "OPTIONS")
router.HandleFunc("/dns/settings", dnsSettingsHandler.updateDNSSettings).Methods("PUT", "OPTIONS")
}
// newDNSSettingsHandler returns a new instance of dnsSettingsHandler handler
func newDNSSettingsHandler(accountManager server.AccountManager) *dnsSettingsHandler {
func newDNSSettingsHandler(accountManager account.Manager) *dnsSettingsHandler {
return &dnsSettingsHandler{accountManager: accountManager}
}
@@ -9,7 +9,7 @@ import (
log "github.com/sirupsen/logrus"
nbdns "github.com/netbirdio/netbird/dns"
"github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/management/server/account"
nbcontext "github.com/netbirdio/netbird/management/server/context"
"github.com/netbirdio/netbird/management/server/http/api"
"github.com/netbirdio/netbird/management/server/http/util"
@@ -18,10 +18,10 @@ import (
// nameserversHandler is the nameserver group handler of the account
type nameserversHandler struct {
accountManager server.AccountManager
accountManager account.Manager
}
func addDNSNameserversEndpoint(accountManager server.AccountManager, router *mux.Router) {
func addDNSNameserversEndpoint(accountManager account.Manager, router *mux.Router) {
nameserversHandler := newNameserversHandler(accountManager)
router.HandleFunc("/dns/nameservers", nameserversHandler.getAllNameservers).Methods("GET", "OPTIONS")
router.HandleFunc("/dns/nameservers", nameserversHandler.createNameserverGroup).Methods("POST", "OPTIONS")
@@ -31,7 +31,7 @@ func addDNSNameserversEndpoint(accountManager server.AccountManager, router *mux
}
// newNameserversHandler returns a new instance of nameserversHandler handler
func newNameserversHandler(accountManager server.AccountManager) *nameserversHandler {
func newNameserversHandler(accountManager account.Manager) *nameserversHandler {
return &nameserversHandler{accountManager: accountManager}
}
@@ -8,7 +8,7 @@ import (
"github.com/gorilla/mux"
log "github.com/sirupsen/logrus"
"github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/management/server/account"
"github.com/netbirdio/netbird/management/server/activity"
nbcontext "github.com/netbirdio/netbird/management/server/context"
"github.com/netbirdio/netbird/management/server/http/api"
@@ -17,16 +17,17 @@ import (
// handler HTTP handler
type handler struct {
accountManager server.AccountManager
accountManager account.Manager
}
func AddEndpoints(accountManager server.AccountManager, router *mux.Router) {
func AddEndpoints(accountManager account.Manager, router *mux.Router) {
eventsHandler := newHandler(accountManager)
router.HandleFunc("/events", eventsHandler.getAllEvents).Methods("GET", "OPTIONS")
router.HandleFunc("/events/audit", eventsHandler.getAllEvents).Methods("GET", "OPTIONS")
}
// newHandler creates a new events handler
func newHandler(accountManager server.AccountManager) *handler {
func newHandler(accountManager account.Manager) *handler {
return &handler{accountManager: accountManager}
}
@@ -7,10 +7,10 @@ import (
"github.com/gorilla/mux"
log "github.com/sirupsen/logrus"
"github.com/netbirdio/netbird/management/server/account"
nbcontext "github.com/netbirdio/netbird/management/server/context"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
"github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/management/server/http/api"
"github.com/netbirdio/netbird/management/server/http/util"
"github.com/netbirdio/netbird/management/server/status"
@@ -19,10 +19,10 @@ import (
// handler is a handler that returns groups of the account
type handler struct {
accountManager server.AccountManager
accountManager account.Manager
}
func AddEndpoints(accountManager server.AccountManager, router *mux.Router) {
func AddEndpoints(accountManager account.Manager, router *mux.Router) {
groupsHandler := newHandler(accountManager)
router.HandleFunc("/groups", groupsHandler.getAllGroups).Methods("GET", "OPTIONS")
router.HandleFunc("/groups", groupsHandler.createGroup).Methods("POST", "OPTIONS")
@@ -32,7 +32,7 @@ func AddEndpoints(accountManager server.AccountManager, router *mux.Router) {
}
// newHandler creates a new groups handler
func newHandler(accountManager server.AccountManager) *handler {
func newHandler(accountManager account.Manager) *handler {
return &handler{
accountManager: accountManager,
}
@@ -9,7 +9,7 @@ import (
"github.com/gorilla/mux"
log "github.com/sirupsen/logrus"
s "github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/management/server/account"
nbcontext "github.com/netbirdio/netbird/management/server/context"
"github.com/netbirdio/netbird/management/server/groups"
"github.com/netbirdio/netbird/management/server/http/api"
@@ -28,12 +28,12 @@ type handler struct {
networksManager networks.Manager
resourceManager resources.Manager
routerManager routers.Manager
accountManager s.AccountManager
accountManager account.Manager
groupsManager groups.Manager
}
func AddEndpoints(networksManager networks.Manager, resourceManager resources.Manager, routerManager routers.Manager, groupsManager groups.Manager, accountManager s.AccountManager, router *mux.Router) {
func AddEndpoints(networksManager networks.Manager, resourceManager resources.Manager, routerManager routers.Manager, groupsManager groups.Manager, accountManager account.Manager, router *mux.Router) {
addRouterEndpoints(routerManager, router)
addResourceEndpoints(resourceManager, groupsManager, router)
@@ -45,7 +45,7 @@ func AddEndpoints(networksManager networks.Manager, resourceManager resources.Ma
router.HandleFunc("/networks/{networkId}", networksHandler.deleteNetwork).Methods("DELETE", "OPTIONS")
}
func newHandler(networksManager networks.Manager, resourceManager resources.Manager, routerManager routers.Manager, groupsManager groups.Manager, accountManager s.AccountManager) *handler {
func newHandler(networksManager networks.Manager, resourceManager resources.Manager, routerManager routers.Manager, groupsManager groups.Manager, accountManager account.Manager) *handler {
return &handler{
networksManager: networksManager,
resourceManager: resourceManager,
@@ -9,7 +9,7 @@ import (
"github.com/gorilla/mux"
log "github.com/sirupsen/logrus"
"github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/management/server/account"
nbcontext "github.com/netbirdio/netbird/management/server/context"
"github.com/netbirdio/netbird/management/server/groups"
"github.com/netbirdio/netbird/management/server/http/api"
@@ -21,10 +21,10 @@ import (
// Handler is a handler that returns peers of the account
type Handler struct {
accountManager server.AccountManager
accountManager account.Manager
}
func AddEndpoints(accountManager server.AccountManager, router *mux.Router) {
func AddEndpoints(accountManager account.Manager, router *mux.Router) {
peersHandler := NewHandler(accountManager)
router.HandleFunc("/peers", peersHandler.GetAllPeers).Methods("GET", "OPTIONS")
router.HandleFunc("/peers/{peerId}", peersHandler.HandlePeer).
@@ -33,7 +33,7 @@ func AddEndpoints(accountManager server.AccountManager, router *mux.Router) {
}
// NewHandler creates a new peers Handler
func NewHandler(accountManager server.AccountManager) *Handler {
func NewHandler(accountManager account.Manager) *Handler {
return &Handler{
accountManager: accountManager,
}
@@ -6,7 +6,7 @@ import (
"github.com/gorilla/mux"
"github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/management/server/account"
nbcontext "github.com/netbirdio/netbird/management/server/context"
"github.com/netbirdio/netbird/management/server/geolocation"
"github.com/netbirdio/netbird/management/server/http/api"
@@ -20,18 +20,18 @@ var (
// geolocationsHandler is a handler that returns locations.
type geolocationsHandler struct {
accountManager server.AccountManager
accountManager account.Manager
geolocationManager geolocation.Geolocation
}
func addLocationsEndpoint(accountManager server.AccountManager, locationManager geolocation.Geolocation, router *mux.Router) {
func addLocationsEndpoint(accountManager account.Manager, locationManager geolocation.Geolocation, router *mux.Router) {
locationHandler := newGeolocationsHandlerHandler(accountManager, locationManager)
router.HandleFunc("/locations/countries", locationHandler.getAllCountries).Methods("GET", "OPTIONS")
router.HandleFunc("/locations/countries/{country}/cities", locationHandler.getCitiesByCountry).Methods("GET", "OPTIONS")
}
// newGeolocationsHandlerHandler creates a new Geolocations handler
func newGeolocationsHandlerHandler(accountManager server.AccountManager, geolocationManager geolocation.Geolocation) *geolocationsHandler {
func newGeolocationsHandlerHandler(accountManager account.Manager, geolocationManager geolocation.Geolocation) *geolocationsHandler {
return &geolocationsHandler{
accountManager: accountManager,
geolocationManager: geolocationManager,
@@ -7,7 +7,7 @@ import (
"github.com/gorilla/mux"
"github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/management/server/account"
nbcontext "github.com/netbirdio/netbird/management/server/context"
"github.com/netbirdio/netbird/management/server/geolocation"
"github.com/netbirdio/netbird/management/server/http/api"
@@ -18,10 +18,10 @@ import (
// handler is a handler that returns policy of the account
type handler struct {
accountManager server.AccountManager
accountManager account.Manager
}
func AddEndpoints(accountManager server.AccountManager, locationManager geolocation.Geolocation, router *mux.Router) {
func AddEndpoints(accountManager account.Manager, locationManager geolocation.Geolocation, router *mux.Router) {
policiesHandler := newHandler(accountManager)
router.HandleFunc("/policies", policiesHandler.getAllPolicies).Methods("GET", "OPTIONS")
router.HandleFunc("/policies", policiesHandler.createPolicy).Methods("POST", "OPTIONS")
@@ -32,7 +32,7 @@ func AddEndpoints(accountManager server.AccountManager, locationManager geolocat
}
// newHandler creates a new policies handler
func newHandler(accountManager server.AccountManager) *handler {
func newHandler(accountManager account.Manager) *handler {
return &handler{
accountManager: accountManager,
}
@@ -6,7 +6,7 @@ import (
"github.com/gorilla/mux"
"github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/management/server/account"
nbcontext "github.com/netbirdio/netbird/management/server/context"
"github.com/netbirdio/netbird/management/server/geolocation"
"github.com/netbirdio/netbird/management/server/http/api"
@@ -17,11 +17,11 @@ import (
// postureChecksHandler is a handler that returns posture checks of the account.
type postureChecksHandler struct {
accountManager server.AccountManager
accountManager account.Manager
geolocationManager geolocation.Geolocation
}
func addPostureCheckEndpoint(accountManager server.AccountManager, locationManager geolocation.Geolocation, router *mux.Router) {
func addPostureCheckEndpoint(accountManager account.Manager, locationManager geolocation.Geolocation, router *mux.Router) {
postureCheckHandler := newPostureChecksHandler(accountManager, locationManager)
router.HandleFunc("/posture-checks", postureCheckHandler.getAllPostureChecks).Methods("GET", "OPTIONS")
router.HandleFunc("/posture-checks", postureCheckHandler.createPostureCheck).Methods("POST", "OPTIONS")
@@ -32,7 +32,7 @@ func addPostureCheckEndpoint(accountManager server.AccountManager, locationManag
}
// newPostureChecksHandler creates a new PostureChecks handler
func newPostureChecksHandler(accountManager server.AccountManager, geolocationManager geolocation.Geolocation) *postureChecksHandler {
func newPostureChecksHandler(accountManager account.Manager, geolocationManager geolocation.Geolocation) *postureChecksHandler {
return &postureChecksHandler{
accountManager: accountManager,
geolocationManager: geolocationManager,
@@ -9,7 +9,7 @@ import (
"github.com/gorilla/mux"
"github.com/netbirdio/netbird/management/domain"
"github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/management/server/account"
nbcontext "github.com/netbirdio/netbird/management/server/context"
"github.com/netbirdio/netbird/management/server/http/api"
"github.com/netbirdio/netbird/management/server/http/util"
@@ -21,10 +21,10 @@ const failedToConvertRoute = "failed to convert route to response: %v"
// handler is the routes handler of the account
type handler struct {
accountManager server.AccountManager
accountManager account.Manager
}
func AddEndpoints(accountManager server.AccountManager, router *mux.Router) {
func AddEndpoints(accountManager account.Manager, router *mux.Router) {
routesHandler := newHandler(accountManager)
router.HandleFunc("/routes", routesHandler.getAllRoutes).Methods("GET", "OPTIONS")
router.HandleFunc("/routes", routesHandler.createRoute).Methods("POST", "OPTIONS")
@@ -34,7 +34,7 @@ func AddEndpoints(accountManager server.AccountManager, router *mux.Router) {
}
// newHandler returns a new instance of routes handler
func newHandler(accountManager server.AccountManager) *handler {
func newHandler(accountManager account.Manager) *handler {
return &handler{
accountManager: accountManager,
}
@@ -3,13 +3,12 @@ package setup_keys
import (
"context"
"encoding/json"
"net/http"
"time"
"github.com/gorilla/mux"
"github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/management/server/account"
nbcontext "github.com/netbirdio/netbird/management/server/context"
"github.com/netbirdio/netbird/management/server/http/api"
"github.com/netbirdio/netbird/management/server/http/util"
@@ -19,10 +18,10 @@ import (
// handler is a handler that returns a list of setup keys of the account
type handler struct {
accountManager server.AccountManager
accountManager account.Manager
}
func AddEndpoints(accountManager server.AccountManager, router *mux.Router) {
func AddEndpoints(accountManager account.Manager, router *mux.Router) {
keysHandler := newHandler(accountManager)
router.HandleFunc("/setup-keys", keysHandler.getAllSetupKeys).Methods("GET", "OPTIONS")
router.HandleFunc("/setup-keys", keysHandler.createSetupKey).Methods("POST", "OPTIONS")
@@ -32,7 +31,7 @@ func AddEndpoints(accountManager server.AccountManager, router *mux.Router) {
}
// newHandler creates a new setup key handler
func newHandler(accountManager server.AccountManager) *handler {
func newHandler(accountManager account.Manager) *handler {
return &handler{
accountManager: accountManager,
}
@@ -6,7 +6,7 @@ import (
"github.com/gorilla/mux"
"github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/management/server/account"
nbcontext "github.com/netbirdio/netbird/management/server/context"
"github.com/netbirdio/netbird/management/server/http/api"
"github.com/netbirdio/netbird/management/server/http/util"
@@ -16,10 +16,10 @@ import (
// patHandler is the nameserver group handler of the account
type patHandler struct {
accountManager server.AccountManager
accountManager account.Manager
}
func addUsersTokensEndpoint(accountManager server.AccountManager, router *mux.Router) {
func addUsersTokensEndpoint(accountManager account.Manager, router *mux.Router) {
tokenHandler := newPATsHandler(accountManager)
router.HandleFunc("/users/{userId}/tokens", tokenHandler.getAllTokens).Methods("GET", "OPTIONS")
router.HandleFunc("/users/{userId}/tokens", tokenHandler.createToken).Methods("POST", "OPTIONS")
@@ -28,7 +28,7 @@ func addUsersTokensEndpoint(accountManager server.AccountManager, router *mux.Ro
}
// newPATsHandler creates a new patHandler HTTP handler
func newPATsHandler(accountManager server.AccountManager) *patHandler {
func newPATsHandler(accountManager account.Manager) *patHandler {
return &patHandler{
accountManager: accountManager,
}
@@ -8,21 +8,21 @@ import (
"github.com/gorilla/mux"
log "github.com/sirupsen/logrus"
"github.com/netbirdio/netbird/management/server/account"
"github.com/netbirdio/netbird/management/server/http/api"
"github.com/netbirdio/netbird/management/server/http/util"
"github.com/netbirdio/netbird/management/server/status"
"github.com/netbirdio/netbird/management/server/types"
"github.com/netbirdio/netbird/management/server"
nbcontext "github.com/netbirdio/netbird/management/server/context"
)
// handler is a handler that returns users of the account
type handler struct {
accountManager server.AccountManager
accountManager account.Manager
}
func AddEndpoints(accountManager server.AccountManager, router *mux.Router) {
func AddEndpoints(accountManager account.Manager, router *mux.Router) {
userHandler := newHandler(accountManager)
router.HandleFunc("/users", userHandler.getAllUsers).Methods("GET", "OPTIONS")
router.HandleFunc("/users/{userId}", userHandler.updateUser).Methods("PUT", "OPTIONS")
@@ -33,7 +33,7 @@ func AddEndpoints(accountManager server.AccountManager, router *mux.Router) {
}
// newHandler creates a new UsersHandler HTTP handler
func newHandler(accountManager server.AccountManager) *handler {
func newHandler(accountManager account.Manager) *handler {
return &handler{
accountManager: accountManager,
}
@@ -15,7 +15,13 @@ import (
"time"
"github.com/golang-jwt/jwt"
"github.com/netbirdio/management-integrations/integrations"
"github.com/netbirdio/netbird/management/server/account"
"github.com/netbirdio/netbird/management/server/settings"
"github.com/netbirdio/netbird/management/server/users"
"github.com/stretchr/testify/assert"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
@@ -88,7 +94,7 @@ type PerformanceMetrics struct {
MaxMsPerOpCICD float64
}
func BuildApiBlackBoxWithDBState(t TB, sqlFile string, expectedPeerUpdate *server.UpdateMessage, validateUpdate bool) (http.Handler, server.AccountManager, chan struct{}) {
func BuildApiBlackBoxWithDBState(t TB, sqlFile string, expectedPeerUpdate *server.UpdateMessage, validateUpdate bool) (http.Handler, account.Manager, chan struct{}) {
store, cleanup, err := store.NewTestStoreFromSQL(context.Background(), sqlFile, t.TempDir())
if err != nil {
t.Fatalf("Failed to create test store: %v", err)
@@ -117,7 +123,9 @@ func BuildApiBlackBoxWithDBState(t TB, sqlFile string, expectedPeerUpdate *serve
geoMock := &geolocation.Mock{}
validatorMock := server.MocIntegratedValidator{}
proxyController := integrations.NewController(store)
am, err := server.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "", &activity.InMemoryEventStore{}, geoMock, false, validatorMock, metrics, proxyController)
userManager := users.NewManager(store)
settingsManager := settings.NewManager(store, userManager, integrations.NewManager(&activity.InMemoryEventStore{}))
am, err := server.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "", &activity.InMemoryEventStore{}, geoMock, false, validatorMock, metrics, proxyController, settingsManager)
if err != nil {
t.Fatalf("Failed to create manager: %v", err)
}
@@ -138,7 +146,7 @@ func BuildApiBlackBoxWithDBState(t TB, sqlFile string, expectedPeerUpdate *serve
permissionsManagerMock := permissions.NewManagerMock()
peersManager := peers.NewManager(store, permissionsManagerMock)
apiHandler, err := nbhttp.NewAPIHandler(context.Background(), am, networksManagerMock, resourcesManagerMock, routersManagerMock, groupsManagerMock, geoMock, authManagerMock, metrics, validatorMock, proxyController, permissionsManagerMock, peersManager)
apiHandler, err := nbhttp.NewAPIHandler(context.Background(), am, networksManagerMock, resourcesManagerMock, routersManagerMock, groupsManagerMock, geoMock, authManagerMock, metrics, validatorMock, proxyController, permissionsManagerMock, peersManager, settingsManager)
if err != nil {
t.Fatalf("Failed to create API handler: %v", err)
}
+18
View File
@@ -2,6 +2,7 @@ package idp
import (
"context"
"encoding/json"
"fmt"
"net/http"
"strings"
@@ -73,6 +74,23 @@ type UserData struct {
AppMetadata AppMetadata `json:"app_metadata"`
}
func (u *UserData) MarshalBinary() (data []byte, err error) {
return json.Marshal(u)
}
func (u *UserData) UnmarshalBinary(data []byte) (err error) {
return json.Unmarshal(data, &u)
}
func (u *UserData) Marshal() (data string, err error) {
d, err := json.Marshal(u)
return string(d), err
}
func (u *UserData) Unmarshal(data []byte) (err error) {
return json.Unmarshal(data, &u)
}
// AppMetadata user app metadata to associate with a profile
type AppMetadata struct {
// WTAccountID is a NetBird (previously Wiretrustee) account id to update in the IDP
+8 -9
View File
@@ -6,7 +6,6 @@ import (
log "github.com/sirupsen/logrus"
"github.com/netbirdio/netbird/management/server/account"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
"github.com/netbirdio/netbird/management/server/store"
"github.com/netbirdio/netbird/management/server/types"
@@ -43,12 +42,12 @@ func (am *DefaultAccountManager) UpdateIntegratedValidatorGroups(ctx context.Con
return err
}
var extra *account.ExtraSettings
var extra *types.ExtraSettings
if a.Settings.Extra != nil {
extra = a.Settings.Extra
} else {
extra = &account.ExtraSettings{}
extra = &types.ExtraSettings{}
a.Settings.Extra = extra
}
extra.IntegratedValidatorGroups = groups
@@ -104,21 +103,21 @@ func (am *DefaultAccountManager) GetValidatedPeers(ctx context.Context, accountI
}
type MocIntegratedValidator struct {
ValidatePeerFunc func(_ context.Context, update *nbpeer.Peer, peer *nbpeer.Peer, userID string, accountID string, dnsDomain string, peersGroup []string, extraSettings *account.ExtraSettings) (*nbpeer.Peer, bool, error)
ValidatePeerFunc func(_ context.Context, update *nbpeer.Peer, peer *nbpeer.Peer, userID string, accountID string, dnsDomain string, peersGroup []string, extraSettings *types.ExtraSettings) (*nbpeer.Peer, bool, error)
}
func (a MocIntegratedValidator) ValidateExtraSettings(_ context.Context, newExtraSettings *account.ExtraSettings, oldExtraSettings *account.ExtraSettings, peers map[string]*nbpeer.Peer, userID string, accountID string) error {
func (a MocIntegratedValidator) ValidateExtraSettings(_ context.Context, newExtraSettings *types.ExtraSettings, oldExtraSettings *types.ExtraSettings, peers map[string]*nbpeer.Peer, userID string, accountID string) error {
return nil
}
func (a MocIntegratedValidator) ValidatePeer(_ context.Context, update *nbpeer.Peer, peer *nbpeer.Peer, userID string, accountID string, dnsDomain string, peersGroup []string, extraSettings *account.ExtraSettings) (*nbpeer.Peer, bool, error) {
func (a MocIntegratedValidator) ValidatePeer(_ context.Context, update *nbpeer.Peer, peer *nbpeer.Peer, userID string, accountID string, dnsDomain string, peersGroup []string, extraSettings *types.ExtraSettings) (*nbpeer.Peer, bool, error) {
if a.ValidatePeerFunc != nil {
return a.ValidatePeerFunc(context.Background(), update, peer, userID, accountID, dnsDomain, peersGroup, extraSettings)
}
return update, false, nil
}
func (a MocIntegratedValidator) GetValidatedPeers(accountID string, groups []*types.Group, peers []*nbpeer.Peer, extraSettings *account.ExtraSettings) (map[string]struct{}, error) {
func (a MocIntegratedValidator) GetValidatedPeers(accountID string, groups []*types.Group, peers []*nbpeer.Peer, extraSettings *types.ExtraSettings) (map[string]struct{}, error) {
validatedPeers := make(map[string]struct{})
for _, peer := range peers {
validatedPeers[peer.ID] = struct{}{}
@@ -126,11 +125,11 @@ func (a MocIntegratedValidator) GetValidatedPeers(accountID string, groups []*ty
return validatedPeers, nil
}
func (MocIntegratedValidator) PreparePeer(_ context.Context, accountID string, peer *nbpeer.Peer, peersGroup []string, extraSettings *account.ExtraSettings) *nbpeer.Peer {
func (MocIntegratedValidator) PreparePeer(_ context.Context, accountID string, peer *nbpeer.Peer, peersGroup []string, extraSettings *types.ExtraSettings) *nbpeer.Peer {
return peer
}
func (MocIntegratedValidator) IsNotValidPeer(_ context.Context, accountID string, peer *nbpeer.Peer, peersGroup []string, extraSettings *account.ExtraSettings) (bool, bool, error) {
func (MocIntegratedValidator) IsNotValidPeer(_ context.Context, accountID string, peer *nbpeer.Peer, peersGroup []string, extraSettings *types.ExtraSettings) (bool, bool, error) {
return false, false, nil
}
@@ -0,0 +1,12 @@
package extra_settings
import (
"context"
"github.com/netbirdio/netbird/management/server/types"
)
type Manager interface {
GetExtraSettings(ctx context.Context, accountID string) (*types.ExtraSettings, error)
UpdateExtraSettings(ctx context.Context, accountID, userID string, extraSettings *types.ExtraSettings) (bool, error)
}
@@ -3,18 +3,17 @@ package integrated_validator
import (
"context"
"github.com/netbirdio/netbird/management/server/account"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
"github.com/netbirdio/netbird/management/server/types"
)
// IntegratedValidator interface exists to avoid the circle dependencies
type IntegratedValidator interface {
ValidateExtraSettings(ctx context.Context, newExtraSettings *account.ExtraSettings, oldExtraSettings *account.ExtraSettings, peers map[string]*nbpeer.Peer, userID string, accountID string) error
ValidatePeer(ctx context.Context, update *nbpeer.Peer, peer *nbpeer.Peer, userID string, accountID string, dnsDomain string, peersGroup []string, extraSettings *account.ExtraSettings) (*nbpeer.Peer, bool, error)
PreparePeer(ctx context.Context, accountID string, peer *nbpeer.Peer, peersGroup []string, extraSettings *account.ExtraSettings) *nbpeer.Peer
IsNotValidPeer(ctx context.Context, accountID string, peer *nbpeer.Peer, peersGroup []string, extraSettings *account.ExtraSettings) (bool, bool, error)
GetValidatedPeers(accountID string, groups []*types.Group, peers []*nbpeer.Peer, extraSettings *account.ExtraSettings) (map[string]struct{}, error)
ValidateExtraSettings(ctx context.Context, newExtraSettings *types.ExtraSettings, oldExtraSettings *types.ExtraSettings, peers map[string]*nbpeer.Peer, userID string, accountID string) error
ValidatePeer(ctx context.Context, update *nbpeer.Peer, peer *nbpeer.Peer, userID string, accountID string, dnsDomain string, peersGroup []string, extraSettings *types.ExtraSettings) (*nbpeer.Peer, bool, error)
PreparePeer(ctx context.Context, accountID string, peer *nbpeer.Peer, peersGroup []string, extraSettings *types.ExtraSettings) *nbpeer.Peer
IsNotValidPeer(ctx context.Context, accountID string, peer *nbpeer.Peer, peersGroup []string, extraSettings *types.ExtraSettings) (bool, bool, error)
GetValidatedPeers(accountID string, groups []*types.Group, peers []*nbpeer.Peer, extraSettings *types.ExtraSettings) (map[string]struct{}, error)
PeerDeleted(ctx context.Context, accountID, peerID string) error
SetPeerInvalidationListener(fn func(accountID string))
Stop(ctx context.Context)
+15 -5
View File
@@ -12,6 +12,7 @@ import (
"testing"
"time"
"github.com/golang/mock/gomock"
log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/require"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
@@ -430,18 +431,27 @@ func startManagementForTest(t *testing.T, testFile string, config *Config) (*grp
metrics, err := telemetry.NewDefaultAppMetrics(context.Background())
require.NoError(t, err)
ctrl := gomock.NewController(t)
t.Cleanup(ctrl.Finish)
settingsMockManager := settings.NewMockManager(ctrl)
settingsMockManager.
EXPECT().
GetSettings(gomock.Any(), gomock.Any(), gomock.Any()).
AnyTimes().
Return(&types.Settings{}, nil)
accountManager, err := BuildManager(ctx, store, peersUpdateManager, nil, "", "netbird.selfhosted",
eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock())
eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager)
if err != nil {
cleanup()
return nil, nil, "", cleanup, err
}
secretsManager := NewTimeBasedAuthSecretsManager(peersUpdateManager, config.TURNConfig, config.Relay)
secretsManager := NewTimeBasedAuthSecretsManager(peersUpdateManager, config.TURNConfig, config.Relay, settingsMockManager)
ephemeralMgr := NewEphemeralManager(store, accountManager)
mgmtServer, err := NewServer(context.Background(), config, accountManager, settings.NewManager(store), peersUpdateManager, secretsManager, nil, ephemeralMgr, nil)
mgmtServer, err := NewServer(context.Background(), config, accountManager, settingsMockManager, peersUpdateManager, secretsManager, nil, ephemeralMgr, nil)
if err != nil {
return nil, nil, "", cleanup, err
}
@@ -740,7 +750,7 @@ func Test_LoginPerformance(t *testing.T) {
NetbirdVersion: "",
}
peerLogin := PeerLogin{
peerLogin := types.PeerLogin{
WireGuardPubKey: key.String(),
SSHKey: "random",
Meta: extractPeerMeta(context.Background(), meta),
@@ -765,7 +775,7 @@ func Test_LoginPerformance(t *testing.T) {
messageCalls = append(messageCalls, login)
mu.Unlock()
go func(peerLogin PeerLogin, counterStart *int32) {
go func(peerLogin types.PeerLogin, counterStart *int32) {
defer wgPeer.Done()
_, _, _, err = am.LoginPeer(context.Background(), peerLogin)
if err != nil {
+19 -2
View File
@@ -10,6 +10,7 @@ import (
"testing"
"time"
"github.com/golang/mock/gomock"
pb "github.com/golang/protobuf/proto" //nolint
log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
@@ -26,6 +27,7 @@ import (
"github.com/netbirdio/netbird/management/server/settings"
"github.com/netbirdio/netbird/management/server/store"
"github.com/netbirdio/netbird/management/server/telemetry"
"github.com/netbirdio/netbird/management/server/types"
"github.com/netbirdio/netbird/util"
)
@@ -178,6 +180,20 @@ func startServer(
t.Fatalf("failed creating metrics: %v", err)
}
ctrl := gomock.NewController(t)
t.Cleanup(ctrl.Finish)
settingsMockManager := settings.NewMockManager(ctrl)
settingsMockManager.
EXPECT().
GetExtraSettings(gomock.Any(), gomock.Any()).
Return(&types.ExtraSettings{}, nil).
AnyTimes()
settingsMockManager.
EXPECT().
GetSettings(gomock.Any(), gomock.Any(), gomock.Any()).
Return(&types.Settings{}, nil).
AnyTimes()
accountManager, err := server.BuildManager(
context.Background(),
str,
@@ -191,17 +207,18 @@ func startServer(
server.MocIntegratedValidator{},
metrics,
port_forwarding.NewControllerMock(),
settingsMockManager,
)
if err != nil {
t.Fatalf("failed creating an account manager: %v", err)
}
secretsManager := server.NewTimeBasedAuthSecretsManager(peersUpdateManager, config.TURNConfig, config.Relay)
secretsManager := server.NewTimeBasedAuthSecretsManager(peersUpdateManager, config.TURNConfig, config.Relay, settingsMockManager)
mgmtServer, err := server.NewServer(
context.Background(),
config,
accountManager,
settings.NewManager(str),
settingsMockManager,
peersUpdateManager,
secretsManager,
nil,
+17 -8
View File
@@ -11,17 +11,18 @@ import (
nbdns "github.com/netbirdio/netbird/dns"
"github.com/netbirdio/netbird/management/domain"
"github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/management/server/account"
"github.com/netbirdio/netbird/management/server/activity"
nbcontext "github.com/netbirdio/netbird/management/server/context"
"github.com/netbirdio/netbird/management/server/idp"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
"github.com/netbirdio/netbird/management/server/posture"
"github.com/netbirdio/netbird/management/server/store"
"github.com/netbirdio/netbird/management/server/types"
"github.com/netbirdio/netbird/route"
)
var _ server.AccountManager = (*MockAccountManager)(nil)
var _ account.Manager = (*MockAccountManager)(nil)
type MockAccountManager struct {
GetOrCreateAccountByUserFunc func(ctx context.Context, userId, domain string) (*types.Account, error)
@@ -89,12 +90,12 @@ type MockAccountManager struct {
SaveDNSSettingsFunc func(ctx context.Context, accountID, userID string, dnsSettingsToSave *types.DNSSettings) error
GetPeerFunc func(ctx context.Context, accountID, peerID, userID string) (*nbpeer.Peer, error)
UpdateAccountSettingsFunc func(ctx context.Context, accountID, userID string, newSettings *types.Settings) (*types.Account, error)
LoginPeerFunc func(ctx context.Context, login server.PeerLogin) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, error)
SyncPeerFunc func(ctx context.Context, sync server.PeerSync, accountID string) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, error)
LoginPeerFunc func(ctx context.Context, login types.PeerLogin) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, error)
SyncPeerFunc func(ctx context.Context, sync types.PeerSync, accountID string) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, error)
InviteUserFunc func(ctx context.Context, accountID string, initiatorUserID string, targetUserEmail string) error
GetAllConnectedPeersFunc func() (map[string]struct{}, error)
HasConnectedChannelFunc func(peerID string) bool
GetExternalCacheManagerFunc func() server.ExternalCacheManager
GetExternalCacheManagerFunc func() account.ExternalCacheManager
GetPostureChecksFunc func(ctx context.Context, accountID, postureChecksID, userID string) (*posture.Checks, error)
SavePostureChecksFunc func(ctx context.Context, accountID, userID string, postureChecks *posture.Checks) (*posture.Checks, error)
DeletePostureChecksFunc func(ctx context.Context, accountID, postureChecksID, userID string) error
@@ -110,6 +111,7 @@ type MockAccountManager struct {
GetAccountSettingsFunc func(ctx context.Context, accountID string, userID string) (*types.Settings, error)
DeleteSetupKeyFunc func(ctx context.Context, accountID, userID, keyID string) error
BuildUserInfosForAccountFunc func(ctx context.Context, accountID, initiatorUserID string, accountUsers []*types.User) (map[string]*types.UserInfo, error)
GetStoreFunc func() store.Store
}
func (am *MockAccountManager) UpdateAccountPeers(ctx context.Context, accountID string) {
@@ -661,7 +663,7 @@ func (am *MockAccountManager) UpdateAccountSettings(ctx context.Context, account
}
// LoginPeer mocks LoginPeer of the AccountManager interface
func (am *MockAccountManager) LoginPeer(ctx context.Context, login server.PeerLogin) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, error) {
func (am *MockAccountManager) LoginPeer(ctx context.Context, login types.PeerLogin) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, error) {
if am.LoginPeerFunc != nil {
return am.LoginPeerFunc(ctx, login)
}
@@ -669,7 +671,7 @@ func (am *MockAccountManager) LoginPeer(ctx context.Context, login server.PeerLo
}
// SyncPeer mocks SyncPeer of the AccountManager interface
func (am *MockAccountManager) SyncPeer(ctx context.Context, sync server.PeerSync, accountID string) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, error) {
func (am *MockAccountManager) SyncPeer(ctx context.Context, sync types.PeerSync, accountID string) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, error) {
if am.SyncPeerFunc != nil {
return am.SyncPeerFunc(ctx, sync, accountID)
}
@@ -700,7 +702,7 @@ func (am *MockAccountManager) StoreEvent(ctx context.Context, initiatorID, targe
}
// GetExternalCacheManager mocks GetExternalCacheManager of the AccountManager interface
func (am *MockAccountManager) GetExternalCacheManager() server.ExternalCacheManager {
func (am *MockAccountManager) GetExternalCacheManager() account.ExternalCacheManager {
if am.GetExternalCacheManagerFunc() != nil {
return am.GetExternalCacheManagerFunc()
}
@@ -838,3 +840,10 @@ func (am *MockAccountManager) BuildUserInfosForAccount(ctx context.Context, acco
func (am *MockAccountManager) SyncUserJWTGroups(ctx context.Context, userAuth nbcontext.UserAuth) error {
return status.Errorf(codes.Unimplemented, "method SyncUserJWTGroups is not implemented")
}
func (am *MockAccountManager) GetStore() store.Store {
if am.GetStoreFunc != nil {
return am.GetStoreFunc()
}
return nil
}
+7 -1
View File
@@ -6,6 +6,7 @@ import (
"testing"
"time"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -13,6 +14,7 @@ import (
"github.com/netbirdio/netbird/management/server/activity"
"github.com/netbirdio/netbird/management/server/integrations/port_forwarding"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
"github.com/netbirdio/netbird/management/server/settings"
"github.com/netbirdio/netbird/management/server/store"
"github.com/netbirdio/netbird/management/server/telemetry"
"github.com/netbirdio/netbird/management/server/types"
@@ -772,7 +774,11 @@ func createNSManager(t *testing.T) (*DefaultAccountManager, error) {
metrics, err := telemetry.NewDefaultAppMetrics(context.Background())
require.NoError(t, err)
return BuildManager(context.Background(), store, NewPeersUpdateManager(nil), nil, "", "netbird.selfhosted", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock())
ctrl := gomock.NewController(t)
t.Cleanup(ctrl.Finish)
settingsMockManager := settings.NewMockManager(ctrl)
return BuildManager(context.Background(), store, NewPeersUpdateManager(nil), nil, "", "netbird.selfhosted", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager)
}
func createNSStore(t *testing.T) (store.Store, error) {
+3 -3
View File
@@ -6,7 +6,7 @@ import (
"github.com/rs/xid"
s "github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/management/server/account"
"github.com/netbirdio/netbird/management/server/activity"
"github.com/netbirdio/netbird/management/server/networks/resources"
"github.com/netbirdio/netbird/management/server/networks/routers"
@@ -26,7 +26,7 @@ type Manager interface {
type managerImpl struct {
store store.Store
accountManager s.AccountManager
accountManager account.Manager
permissionsManager permissions.Manager
resourcesManager resources.Manager
routersManager routers.Manager
@@ -35,7 +35,7 @@ type managerImpl struct {
type mockManager struct {
}
func NewManager(store store.Store, permissionsManager permissions.Manager, resourceManager resources.Manager, routersManager routers.Manager, accountManager s.AccountManager) Manager {
func NewManager(store store.Store, permissionsManager permissions.Manager, resourceManager resources.Manager, routersManager routers.Manager, accountManager account.Manager) Manager {
return &managerImpl{
store: store,
permissionsManager: permissionsManager,
@@ -5,7 +5,7 @@ import (
"errors"
"fmt"
s "github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/management/server/account"
"github.com/netbirdio/netbird/management/server/activity"
"github.com/netbirdio/netbird/management/server/groups"
"github.com/netbirdio/netbird/management/server/networks/resources/types"
@@ -31,13 +31,13 @@ type managerImpl struct {
store store.Store
permissionsManager permissions.Manager
groupsManager groups.Manager
accountManager s.AccountManager
accountManager account.Manager
}
type mockManager struct {
}
func NewManager(store store.Store, permissionsManager permissions.Manager, groupsManager groups.Manager, accountManager s.AccountManager) Manager {
func NewManager(store store.Store, permissionsManager permissions.Manager, groupsManager groups.Manager, accountManager account.Manager) Manager {
return &managerImpl{
store: store,
permissionsManager: permissionsManager,
@@ -20,9 +20,9 @@ import (
type NetworkResourceType string
const (
host NetworkResourceType = "host"
subnet NetworkResourceType = "subnet"
domain NetworkResourceType = "domain"
Host NetworkResourceType = "host"
Subnet NetworkResourceType = "subnet"
Domain NetworkResourceType = "domain"
)
func (p NetworkResourceType) String() string {
@@ -66,7 +66,7 @@ func NewNetworkResource(accountID, networkID, name, description, address string,
func (n *NetworkResource) ToAPIResponse(groups []api.GroupMinimum) *api.NetworkResource {
addr := n.Prefix.String()
if n.Type == domain {
if n.Type == Domain {
addr = n.Domain
}
@@ -125,7 +125,7 @@ func (n *NetworkResource) ToRoute(peer *nbpeer.Peer, router *routerTypes.Network
AccessControlGroups: nil,
}
if n.Type == host || n.Type == subnet {
if n.Type == Host || n.Type == Subnet {
r.Network = n.Prefix
r.NetworkType = route.IPv4Network
@@ -134,7 +134,7 @@ func (n *NetworkResource) ToRoute(peer *nbpeer.Peer, router *routerTypes.Network
}
}
if n.Type == domain {
if n.Type == Domain {
domainList, err := nbDomain.FromStringList([]string{n.Domain})
if err != nil {
return nil
@@ -157,18 +157,18 @@ func (n *NetworkResource) EventMeta(network *networkTypes.Network) map[string]an
func GetResourceType(address string) (NetworkResourceType, string, netip.Prefix, error) {
if prefix, err := netip.ParsePrefix(address); err == nil {
if prefix.Bits() == 32 || prefix.Bits() == 128 {
return host, "", prefix, nil
return Host, "", prefix, nil
}
return subnet, "", prefix, nil
return Subnet, "", prefix, nil
}
if ip, err := netip.ParseAddr(address); err == nil {
return host, "", netip.PrefixFrom(ip, ip.BitLen()), nil
return Host, "", netip.PrefixFrom(ip, ip.BitLen()), nil
}
domainRegex := regexp.MustCompile(`^(\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$`)
if domainRegex.MatchString(address) {
return domain, address, netip.Prefix{}, nil
return Domain, address, netip.Prefix{}, nil
}
return "", "", netip.Prefix{}, errors.New("not a valid host, subnet, or domain")
@@ -14,15 +14,15 @@ func TestGetResourceType(t *testing.T) {
expectedPrefix netip.Prefix
}{
// Valid host IPs
{"1.1.1.1", host, false, "", netip.MustParsePrefix("1.1.1.1/32")},
{"1.1.1.1/32", host, false, "", netip.MustParsePrefix("1.1.1.1/32")},
{"1.1.1.1", Host, false, "", netip.MustParsePrefix("1.1.1.1/32")},
{"1.1.1.1/32", Host, false, "", netip.MustParsePrefix("1.1.1.1/32")},
// Valid subnets
{"192.168.1.0/24", subnet, false, "", netip.MustParsePrefix("192.168.1.0/24")},
{"10.0.0.0/16", subnet, false, "", netip.MustParsePrefix("10.0.0.0/16")},
{"192.168.1.0/24", Subnet, false, "", netip.MustParsePrefix("192.168.1.0/24")},
{"10.0.0.0/16", Subnet, false, "", netip.MustParsePrefix("10.0.0.0/16")},
// Valid domains
{"example.com", domain, false, "example.com", netip.Prefix{}},
{"*.example.com", domain, false, "*.example.com", netip.Prefix{}},
{"sub.example.com", domain, false, "sub.example.com", netip.Prefix{}},
{"example.com", Domain, false, "example.com", netip.Prefix{}},
{"*.example.com", Domain, false, "*.example.com", netip.Prefix{}},
{"sub.example.com", Domain, false, "sub.example.com", netip.Prefix{}},
// Invalid inputs
{"invalid", "", true, "", netip.Prefix{}},
{"1.1.1.1/abc", "", true, "", netip.Prefix{}},
@@ -32,7 +32,7 @@ func TestGetResourceType(t *testing.T) {
for _, tt := range tests {
t.Run(tt.input, func(t *testing.T) {
result, domain, prefix, err := GetResourceType(tt.input)
if result != tt.expectedType {
t.Errorf("Expected type %v, got %v", tt.expectedType, result)
}
@@ -7,7 +7,7 @@ import (
"github.com/rs/xid"
s "github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/management/server/account"
"github.com/netbirdio/netbird/management/server/activity"
"github.com/netbirdio/netbird/management/server/networks/routers/types"
networkTypes "github.com/netbirdio/netbird/management/server/networks/types"
@@ -29,13 +29,13 @@ type Manager interface {
type managerImpl struct {
store store.Store
permissionsManager permissions.Manager
accountManager s.AccountManager
accountManager account.Manager
}
type mockManager struct {
}
func NewManager(store store.Store, permissionsManager permissions.Manager, accountManager s.AccountManager) Manager {
func NewManager(store store.Store, permissionsManager permissions.Manager, accountManager account.Manager) Manager {
return &managerImpl{
store: store,
permissionsManager: permissionsManager,
+18 -36
View File
@@ -29,36 +29,6 @@ import (
"github.com/netbirdio/netbird/management/server/status"
)
// PeerSync used as a data object between the gRPC API and AccountManager on Sync request.
type PeerSync struct {
// WireGuardPubKey is a peers WireGuard public key
WireGuardPubKey string
// Meta is the system information passed by peer, must be always present
Meta nbpeer.PeerSystemMeta
// UpdateAccountPeers indicate updating account peers,
// which occurs when the peer's metadata is updated
UpdateAccountPeers bool
}
// PeerLogin used as a data object between the gRPC API and AccountManager on Login request.
type PeerLogin struct {
// WireGuardPubKey is a peers WireGuard public key
WireGuardPubKey string
// SSHKey is a peer's ssh key. Can be empty (e.g., old version do not provide it, or this feature is disabled)
SSHKey string
// Meta is the system information passed by peer, must be always present.
Meta nbpeer.PeerSystemMeta
// UserID indicates that JWT was used to log in, and it was valid. Can be empty when SetupKey is used or auth is not required.
UserID string
// SetupKey references to a server.SetupKey to log in. Can be empty when UserID is used or auth is not required.
SetupKey string
// ConnectionIP is the real IP of the peer
ConnectionIP net.IP
// ExtraDNSLabels is a list of extra DNS labels that the peer wants to use
ExtraDNSLabels []string
}
// GetPeers returns a list of peers under the given account filtering out peers that do not belong to a user if
// the current user is not an admin.
func (am *DefaultAccountManager) GetPeers(ctx context.Context, accountID, userID, nameFilter, ipFilter string) ([]*nbpeer.Peer, error) {
@@ -709,7 +679,7 @@ func getFreeIP(ctx context.Context, transaction store.Store, accountID string) (
}
// SyncPeer checks whether peer is eligible for receiving NetworkMap (authenticated) and returns its NetworkMap if eligible
func (am *DefaultAccountManager) SyncPeer(ctx context.Context, sync PeerSync, accountID string) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, error) {
func (am *DefaultAccountManager) SyncPeer(ctx context.Context, sync types.PeerSync, accountID string) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, error) {
start := time.Now()
defer func() {
log.WithContext(ctx).Debugf("SyncPeer: took %v", time.Since(start))
@@ -784,7 +754,7 @@ func (am *DefaultAccountManager) SyncPeer(ctx context.Context, sync PeerSync, ac
return am.getValidatedPeerWithMap(ctx, peerNotValid, accountID, peer)
}
func (am *DefaultAccountManager) handlePeerLoginNotFound(ctx context.Context, login PeerLogin, err error) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, error) {
func (am *DefaultAccountManager) handlePeerLoginNotFound(ctx context.Context, login types.PeerLogin, err error) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, error) {
if errStatus, ok := status.FromError(err); ok && errStatus.Type() == status.NotFound {
// we couldn't find this peer by its public key which can mean that peer hasn't been registered yet.
// Try registering it.
@@ -804,7 +774,7 @@ func (am *DefaultAccountManager) handlePeerLoginNotFound(ctx context.Context, lo
// LoginPeer logs in or registers a peer.
// If peer doesn't exist the function checks whether a setup key or a user is present and registers a new peer if so.
func (am *DefaultAccountManager) LoginPeer(ctx context.Context, login PeerLogin) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, error) {
func (am *DefaultAccountManager) LoginPeer(ctx context.Context, login types.PeerLogin) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, error) {
accountID, err := am.Store.GetAccountIDByPeerPubKey(ctx, login.WireGuardPubKey)
if err != nil {
return am.handlePeerLoginNotFound(ctx, login, err)
@@ -994,7 +964,7 @@ func processPeerPostureChecks(ctx context.Context, transaction store.Store, poli
// The NetBird client doesn't have a way to check if the peer needs login besides sending a login request
// with no JWT token and usually no setup-key. As the client can send up to two login request to check if it is expired
// and before starting the engine, we do the checks without an account lock to avoid piling up requests.
func (am *DefaultAccountManager) checkIFPeerNeedsLoginWithoutLock(ctx context.Context, accountID string, login PeerLogin) error {
func (am *DefaultAccountManager) checkIFPeerNeedsLoginWithoutLock(ctx context.Context, accountID string, login types.PeerLogin) error {
peer, err := am.Store.GetPeerByPeerPubKey(ctx, store.LockingStrengthShare, login.WireGuardPubKey)
if err != nil {
return err
@@ -1237,7 +1207,13 @@ func (am *DefaultAccountManager) UpdateAccountPeers(ctx context.Context, account
remotePeerNetworkMap.Merge(proxyNetworkMap)
}
update := toSyncResponse(ctx, nil, p, nil, nil, remotePeerNetworkMap, am.GetDNSDomain(), postureChecks, dnsCache, account.Settings.RoutingPeerDNSResolutionEnabled)
extraSetting, err := am.settingsManager.GetExtraSettings(ctx, accountID)
if err != nil {
log.WithContext(ctx).Errorf("failed to get flow enabled status: %v", err)
return
}
update := toSyncResponse(ctx, &Config{}, p, nil, nil, remotePeerNetworkMap, am.GetDNSDomain(), postureChecks, dnsCache, account.Settings.RoutingPeerDNSResolutionEnabled, extraSetting)
am.peersUpdateManager.SendUpdate(ctx, p.ID, &UpdateMessage{Update: update, NetworkMap: remotePeerNetworkMap})
}(peer)
}
@@ -1300,7 +1276,13 @@ func (am *DefaultAccountManager) UpdateAccountPeer(ctx context.Context, accountI
remotePeerNetworkMap.Merge(proxyNetworkMap)
}
update := toSyncResponse(ctx, nil, peer, nil, nil, remotePeerNetworkMap, am.GetDNSDomain(), postureChecks, dnsCache, account.Settings.RoutingPeerDNSResolutionEnabled)
extraSettings, err := am.settingsManager.GetExtraSettings(ctx, peer.AccountID)
if err != nil {
log.WithContext(ctx).Errorf("failed to get extra settings: %v", err)
return
}
update := toSyncResponse(ctx, &Config{}, peer, nil, nil, remotePeerNetworkMap, am.GetDNSDomain(), postureChecks, dnsCache, account.Settings.RoutingPeerDNSResolutionEnabled, extraSettings)
am.peersUpdateManager.SendUpdate(ctx, peer.ID, &UpdateMessage{Update: update, NetworkMap: remotePeerNetworkMap})
}
+21 -7
View File
@@ -13,13 +13,15 @@ import (
"testing"
"time"
nbAccount "github.com/netbirdio/netbird/management/server/account"
"github.com/golang/mock/gomock"
"github.com/rs/xid"
log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"github.com/netbirdio/netbird/management/server/settings"
"github.com/netbirdio/netbird/management/server/integrations/port_forwarding"
"github.com/netbirdio/netbird/management/server/util"
@@ -1107,7 +1109,7 @@ func TestToSyncResponse(t *testing.T) {
}
dnsCache := &DNSConfigCache{}
response := toSyncResponse(context.Background(), config, peer, turnRelayToken, turnRelayToken, networkMap, dnsName, checks, dnsCache, true)
response := toSyncResponse(context.Background(), config, peer, turnRelayToken, turnRelayToken, networkMap, dnsName, checks, dnsCache, true, nil)
assert.NotNil(t, response)
// assert peer config
@@ -1211,7 +1213,11 @@ func Test_RegisterPeerByUser(t *testing.T) {
metrics, err := telemetry.NewDefaultAppMetrics(context.Background())
assert.NoError(t, err)
am, err := BuildManager(context.Background(), s, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock())
ctrl := gomock.NewController(t)
t.Cleanup(ctrl.Finish)
settingsMockManager := settings.NewMockManager(ctrl)
am, err := BuildManager(context.Background(), s, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager)
assert.NoError(t, err)
existingAccountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b"
@@ -1275,7 +1281,11 @@ func Test_RegisterPeerBySetupKey(t *testing.T) {
metrics, err := telemetry.NewDefaultAppMetrics(context.Background())
assert.NoError(t, err)
am, err := BuildManager(context.Background(), s, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock())
ctrl := gomock.NewController(t)
t.Cleanup(ctrl.Finish)
settingsMockManager := settings.NewMockManager(ctrl)
am, err := BuildManager(context.Background(), s, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager)
assert.NoError(t, err)
existingAccountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b"
@@ -1342,7 +1352,11 @@ func Test_RegisterPeerRollbackOnFailure(t *testing.T) {
metrics, err := telemetry.NewDefaultAppMetrics(context.Background())
assert.NoError(t, err)
am, err := BuildManager(context.Background(), s, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock())
ctrl := gomock.NewController(t)
t.Cleanup(ctrl.Finish)
settingsMockManager := settings.NewMockManager(ctrl)
am, err := BuildManager(context.Background(), s, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager)
assert.NoError(t, err)
existingAccountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b"
@@ -1531,7 +1545,7 @@ func TestPeerAccountPeersUpdate(t *testing.T) {
})
t.Run("validator requires update", func(t *testing.T) {
requireUpdateFunc := func(_ context.Context, update *nbpeer.Peer, peer *nbpeer.Peer, userID string, accountID string, dnsDomain string, peersGroup []string, extraSettings *nbAccount.ExtraSettings) (*nbpeer.Peer, bool, error) {
requireUpdateFunc := func(_ context.Context, update *nbpeer.Peer, peer *nbpeer.Peer, userID string, accountID string, dnsDomain string, peersGroup []string, extraSettings *types.ExtraSettings) (*nbpeer.Peer, bool, error) {
return update, true, nil
}
@@ -1553,7 +1567,7 @@ func TestPeerAccountPeersUpdate(t *testing.T) {
})
t.Run("validator requires no update", func(t *testing.T) {
requireNoUpdateFunc := func(_ context.Context, update *nbpeer.Peer, peer *nbpeer.Peer, userID string, accountID string, dnsDomain string, peersGroup []string, extraSettings *nbAccount.ExtraSettings) (*nbpeer.Peer, bool, error) {
requireNoUpdateFunc := func(_ context.Context, update *nbpeer.Peer, peer *nbpeer.Peer, userID string, accountID string, dnsDomain string, peersGroup []string, extraSettings *types.ExtraSettings) (*nbpeer.Peer, bool, error) {
return update, false, nil
}
+7
View File
@@ -1,5 +1,7 @@
package peers
//go:generate go run github.com/golang/mock/mockgen -package peers -destination=manager_mock.go -source=./manager.go -build_flags=-mod=mod
import (
"context"
"fmt"
@@ -12,6 +14,7 @@ import (
type Manager interface {
GetPeer(ctx context.Context, accountID, userID, peerID string) (*peer.Peer, error)
GetPeerAccountID(ctx context.Context, peerID string) (string, error)
GetAllPeers(ctx context.Context, accountID, userID string) ([]*peer.Peer, error)
}
@@ -52,3 +55,7 @@ func (m *managerImpl) GetAllPeers(ctx context.Context, accountID, userID string)
return m.store.GetAccountPeers(ctx, store.LockingStrengthShare, accountID, "", "")
}
func (m *managerImpl) GetPeerAccountID(ctx context.Context, peerID string) (string, error) {
return m.store.GetAccountIDByPeerID(ctx, store.LockingStrengthShare, peerID)
}
+81
View File
@@ -0,0 +1,81 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: ./manager.go
// Package peers is a generated GoMock package.
package peers
import (
context "context"
reflect "reflect"
gomock "github.com/golang/mock/gomock"
peer "github.com/netbirdio/netbird/management/server/peer"
)
// MockManager is a mock of Manager interface.
type MockManager struct {
ctrl *gomock.Controller
recorder *MockManagerMockRecorder
}
// MockManagerMockRecorder is the mock recorder for MockManager.
type MockManagerMockRecorder struct {
mock *MockManager
}
// NewMockManager creates a new mock instance.
func NewMockManager(ctrl *gomock.Controller) *MockManager {
mock := &MockManager{ctrl: ctrl}
mock.recorder = &MockManagerMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockManager) EXPECT() *MockManagerMockRecorder {
return m.recorder
}
// GetAllPeers mocks base method.
func (m *MockManager) GetAllPeers(ctx context.Context, accountID, userID string) ([]*peer.Peer, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetAllPeers", ctx, accountID, userID)
ret0, _ := ret[0].([]*peer.Peer)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetAllPeers indicates an expected call of GetAllPeers.
func (mr *MockManagerMockRecorder) GetAllPeers(ctx, accountID, userID interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllPeers", reflect.TypeOf((*MockManager)(nil).GetAllPeers), ctx, accountID, userID)
}
// GetPeer mocks base method.
func (m *MockManager) GetPeer(ctx context.Context, accountID, userID, peerID string) (*peer.Peer, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetPeer", ctx, accountID, userID, peerID)
ret0, _ := ret[0].(*peer.Peer)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetPeer indicates an expected call of GetPeer.
func (mr *MockManagerMockRecorder) GetPeer(ctx, accountID, userID, peerID interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPeer", reflect.TypeOf((*MockManager)(nil).GetPeer), ctx, accountID, userID, peerID)
}
// GetPeerAccountID mocks base method.
func (m *MockManager) GetPeerAccountID(ctx context.Context, peerID string) (string, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetPeerAccountID", ctx, peerID)
ret0, _ := ret[0].(string)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetPeerAccountID indicates an expected call of GetPeerAccountID.
func (mr *MockManagerMockRecorder) GetPeerAccountID(ctx, peerID interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPeerAccountID", reflect.TypeOf((*MockManager)(nil).GetPeerAccountID), ctx, peerID)
}
+2 -1
View File
@@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"github.com/netbirdio/netbird/management/server/activity"
"github.com/netbirdio/netbird/management/server/settings"
"github.com/netbirdio/netbird/management/server/types"
"github.com/netbirdio/netbird/management/server/users"
@@ -71,7 +72,7 @@ func (m *managerImpl) ValidateUserPermissions(ctx context.Context, accountID, us
}
func (m *managerImpl) validateRegularUserPermissions(ctx context.Context, accountID, userID string, module Module, operation Operation) (bool, error) {
settings, err := m.settingsManager.GetSettings(ctx, accountID, userID)
settings, err := m.settingsManager.GetSettings(ctx, accountID, activity.SystemInitiator)
if err != nil {
return false, fmt.Errorf("failed to get settings: %w", err)
}
+1
View File
@@ -256,6 +256,7 @@ func toProtocolFirewallRules(rules []*types.FirewallRule) []*proto.FirewallRule
rule := rules[i]
result[i] = &proto.FirewallRule{
PolicyID: []byte(rule.PolicyID),
PeerIP: rule.PeerIP,
Direction: getProtoDirection(rule.Direction),
Action: getProtoAction(rule.Action),
+27
View File
@@ -182,6 +182,7 @@ func TestAccount_getPeersByPolicy(t *testing.T) {
Action: "accept",
Protocol: "all",
Port: "",
PolicyID: "RuleDefault",
},
{
PeerIP: "0.0.0.0",
@@ -189,6 +190,7 @@ func TestAccount_getPeersByPolicy(t *testing.T) {
Action: "accept",
Protocol: "all",
Port: "",
PolicyID: "RuleDefault",
},
{
PeerIP: "100.65.14.88",
@@ -196,6 +198,7 @@ func TestAccount_getPeersByPolicy(t *testing.T) {
Action: "accept",
Protocol: "all",
Port: "",
PolicyID: "RuleSwarm",
},
{
PeerIP: "100.65.14.88",
@@ -203,6 +206,7 @@ func TestAccount_getPeersByPolicy(t *testing.T) {
Action: "accept",
Protocol: "all",
Port: "",
PolicyID: "RuleSwarm",
},
{
PeerIP: "100.65.62.5",
@@ -210,6 +214,7 @@ func TestAccount_getPeersByPolicy(t *testing.T) {
Action: "accept",
Protocol: "all",
Port: "",
PolicyID: "RuleSwarm",
},
{
PeerIP: "100.65.62.5",
@@ -217,6 +222,7 @@ func TestAccount_getPeersByPolicy(t *testing.T) {
Action: "accept",
Protocol: "all",
Port: "",
PolicyID: "RuleSwarm",
},
{
@@ -225,6 +231,7 @@ func TestAccount_getPeersByPolicy(t *testing.T) {
Action: "accept",
Protocol: "all",
Port: "",
PolicyID: "RuleSwarm",
},
{
PeerIP: "100.65.32.206",
@@ -232,6 +239,7 @@ func TestAccount_getPeersByPolicy(t *testing.T) {
Action: "accept",
Protocol: "all",
Port: "",
PolicyID: "RuleSwarm",
},
{
@@ -240,6 +248,7 @@ func TestAccount_getPeersByPolicy(t *testing.T) {
Action: "accept",
Protocol: "all",
Port: "",
PolicyID: "RuleSwarm",
},
{
PeerIP: "100.65.250.202",
@@ -247,6 +256,7 @@ func TestAccount_getPeersByPolicy(t *testing.T) {
Action: "accept",
Protocol: "all",
Port: "",
PolicyID: "RuleSwarm",
},
{
@@ -255,6 +265,7 @@ func TestAccount_getPeersByPolicy(t *testing.T) {
Action: "accept",
Protocol: "all",
Port: "",
PolicyID: "RuleSwarm",
},
{
PeerIP: "100.65.13.186",
@@ -262,6 +273,7 @@ func TestAccount_getPeersByPolicy(t *testing.T) {
Action: "accept",
Protocol: "all",
Port: "",
PolicyID: "RuleSwarm",
},
{
@@ -270,6 +282,7 @@ func TestAccount_getPeersByPolicy(t *testing.T) {
Action: "accept",
Protocol: "all",
Port: "",
PolicyID: "RuleSwarm",
},
{
PeerIP: "100.65.29.55",
@@ -277,6 +290,7 @@ func TestAccount_getPeersByPolicy(t *testing.T) {
Action: "accept",
Protocol: "all",
Port: "",
PolicyID: "RuleSwarm",
},
}
assert.Len(t, firewallRules, len(epectedFirewallRules))
@@ -404,6 +418,7 @@ func TestAccount_getPeersByPolicyDirect(t *testing.T) {
Action: "accept",
Protocol: "all",
Port: "",
PolicyID: "RuleSwarm",
},
{
PeerIP: "100.65.254.139",
@@ -411,6 +426,7 @@ func TestAccount_getPeersByPolicyDirect(t *testing.T) {
Action: "accept",
Protocol: "all",
Port: "",
PolicyID: "RuleSwarm",
},
}
assert.Len(t, firewallRules, len(epectedFirewallRules))
@@ -432,6 +448,7 @@ func TestAccount_getPeersByPolicyDirect(t *testing.T) {
Action: "accept",
Protocol: "all",
Port: "",
PolicyID: "RuleSwarm",
},
{
PeerIP: "100.65.80.39",
@@ -439,6 +456,7 @@ func TestAccount_getPeersByPolicyDirect(t *testing.T) {
Action: "accept",
Protocol: "all",
Port: "",
PolicyID: "RuleSwarm",
},
}
assert.Len(t, firewallRules, len(epectedFirewallRules))
@@ -462,6 +480,7 @@ func TestAccount_getPeersByPolicyDirect(t *testing.T) {
Action: "accept",
Protocol: "all",
Port: "",
PolicyID: "RuleSwarm",
},
}
assert.Len(t, firewallRules, len(epectedFirewallRules))
@@ -483,6 +502,7 @@ func TestAccount_getPeersByPolicyDirect(t *testing.T) {
Action: "accept",
Protocol: "all",
Port: "",
PolicyID: "RuleSwarm",
},
}
assert.Len(t, firewallRules, len(epectedFirewallRules))
@@ -690,6 +710,7 @@ func TestAccount_getPeersByPolicyPostureChecks(t *testing.T) {
Action: "accept",
Protocol: "tcp",
Port: "80",
PolicyID: "RuleSwarm",
},
}
assert.ElementsMatch(t, firewallRules, expectedFirewallRules)
@@ -773,6 +794,7 @@ func TestAccount_getPeersByPolicyPostureChecks(t *testing.T) {
Action: "accept",
Protocol: "tcp",
Port: "80",
PolicyID: "RuleSwarm",
},
{
PeerIP: "100.65.32.206",
@@ -780,6 +802,7 @@ func TestAccount_getPeersByPolicyPostureChecks(t *testing.T) {
Action: "accept",
Protocol: "tcp",
Port: "80",
PolicyID: "RuleSwarm",
},
{
PeerIP: "100.65.13.186",
@@ -787,6 +810,7 @@ func TestAccount_getPeersByPolicyPostureChecks(t *testing.T) {
Action: "accept",
Protocol: "tcp",
Port: "80",
PolicyID: "RuleSwarm",
},
{
PeerIP: "100.65.29.55",
@@ -794,6 +818,7 @@ func TestAccount_getPeersByPolicyPostureChecks(t *testing.T) {
Action: "accept",
Protocol: "tcp",
Port: "80",
PolicyID: "RuleSwarm",
},
{
PeerIP: "100.65.254.139",
@@ -801,6 +826,7 @@ func TestAccount_getPeersByPolicyPostureChecks(t *testing.T) {
Action: "accept",
Protocol: "tcp",
Port: "80",
PolicyID: "RuleSwarm",
},
{
PeerIP: "100.65.62.5",
@@ -808,6 +834,7 @@ func TestAccount_getPeersByPolicyPostureChecks(t *testing.T) {
Action: "accept",
Protocol: "tcp",
Port: "80",
PolicyID: "RuleSwarm",
},
}
assert.Len(t, firewallRules, len(expectedFirewallRules))
+1
View File
@@ -388,6 +388,7 @@ func toProtocolRoutesFirewallRules(rules []*types.RouteFirewallRule) []*proto.Ro
Protocol: getProtoProtocol(rule.Protocol),
PortInfo: getProtoPortInfo(rule),
IsDynamic: rule.IsDynamic,
PolicyID: []byte(rule.PolicyID),
}
}
+25 -1
View File
@@ -9,6 +9,7 @@ import (
"testing"
"time"
"github.com/golang/mock/gomock"
"github.com/rs/xid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -20,6 +21,7 @@ import (
routerTypes "github.com/netbirdio/netbird/management/server/networks/routers/types"
networkTypes "github.com/netbirdio/netbird/management/server/networks/types"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
"github.com/netbirdio/netbird/management/server/settings"
"github.com/netbirdio/netbird/management/server/store"
"github.com/netbirdio/netbird/management/server/telemetry"
"github.com/netbirdio/netbird/management/server/types"
@@ -1257,7 +1259,29 @@ func createRouterManager(t *testing.T) (*DefaultAccountManager, error) {
metrics, err := telemetry.NewDefaultAppMetrics(context.Background())
require.NoError(t, err)
return BuildManager(context.Background(), store, NewPeersUpdateManager(nil), nil, "", "netbird.selfhosted", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock())
ctrl := gomock.NewController(t)
t.Cleanup(ctrl.Finish)
settingsMockManager := settings.NewMockManager(ctrl)
settingsMockManager.
EXPECT().
GetSettings(
gomock.Any(),
gomock.Any(),
gomock.Any(),
).
Return(nil, nil).
AnyTimes()
settingsMockManager.
EXPECT().
GetExtraSettings(
gomock.Any(),
gomock.Any(),
).
AnyTimes().
Return(&types.ExtraSettings{}, nil)
return BuildManager(context.Background(), store, NewPeersUpdateManager(nil), nil, "", "netbird.selfhosted", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager)
}
func createRouterStore(t *testing.T) (store.Store, error) {
+73 -12
View File
@@ -1,37 +1,98 @@
package settings
//go:generate go run github.com/golang/mock/mockgen -package settings -destination=manager_mock.go -source=./manager.go -build_flags=-mod=mod
import (
"context"
"fmt"
"github.com/netbirdio/netbird/management/server/activity"
"github.com/netbirdio/netbird/management/server/integrations/extra_settings"
"github.com/netbirdio/netbird/management/server/status"
"github.com/netbirdio/netbird/management/server/store"
"github.com/netbirdio/netbird/management/server/types"
"github.com/netbirdio/netbird/management/server/users"
)
type Manager interface {
GetExtraSettingsManager() extra_settings.Manager
GetSettings(ctx context.Context, accountID string, userID string) (*types.Settings, error)
GetExtraSettings(ctx context.Context, accountID string) (*types.ExtraSettings, error)
UpdateExtraSettings(ctx context.Context, accountID, userID string, extraSettings *types.ExtraSettings) (bool, error)
}
type managerImpl struct {
store store.Store
store store.Store
extraSettingsManager extra_settings.Manager
userManager users.Manager
}
type managerMock struct {
}
func NewManager(store store.Store) Manager {
func NewManager(store store.Store, userManager users.Manager, extraSettingsManager extra_settings.Manager) Manager {
return &managerImpl{
store: store,
store: store,
extraSettingsManager: extraSettingsManager,
userManager: userManager,
}
}
func (m *managerImpl) GetSettings(ctx context.Context, accountID string, userID string) (*types.Settings, error) {
return m.store.GetAccountSettings(ctx, store.LockingStrengthShare, accountID)
func (m *managerImpl) GetExtraSettingsManager() extra_settings.Manager {
return m.extraSettingsManager
}
func NewManagerMock() Manager {
return &managerMock{}
func (m *managerImpl) GetSettings(ctx context.Context, accountID, userID string) (*types.Settings, error) {
if userID != activity.SystemInitiator {
user, err := m.userManager.GetUser(ctx, userID)
if err != nil {
return nil, fmt.Errorf("get user: %w", err)
}
if user.AccountID != accountID || (!user.HasAdminPower() && !user.IsServiceUser) {
return nil, status.Errorf(status.PermissionDenied, "the user has no permission to access account data")
}
}
extraSettings, err := m.extraSettingsManager.GetExtraSettings(ctx, accountID)
if err != nil {
return nil, fmt.Errorf("get extra settings: %w", err)
}
settings, err := m.store.GetAccountSettings(ctx, store.LockingStrengthShare, accountID)
if err != nil {
return nil, fmt.Errorf("get account settings: %w", err)
}
// Once we migrate the peer approval to settings manager this merging is obsolete
if settings.Extra != nil {
settings.Extra.FlowEnabled = extraSettings.FlowEnabled
settings.Extra.FlowPacketCounterEnabled = extraSettings.FlowPacketCounterEnabled
settings.Extra.FlowENCollectionEnabled = extraSettings.FlowENCollectionEnabled
settings.Extra.FlowDnsCollectionEnabled = extraSettings.FlowDnsCollectionEnabled
}
return settings, nil
}
func (m *managerMock) GetSettings(ctx context.Context, accountID string, userID string) (*types.Settings, error) {
return &types.Settings{}, nil
func (m *managerImpl) GetExtraSettings(ctx context.Context, accountID string) (*types.ExtraSettings, error) {
extraSettings, err := m.extraSettingsManager.GetExtraSettings(ctx, accountID)
if err != nil {
return nil, fmt.Errorf("get extra settings: %w", err)
}
settings, err := m.store.GetAccountSettings(ctx, store.LockingStrengthShare, accountID)
if err != nil {
return nil, fmt.Errorf("get account settings: %w", err)
}
// Once we migrate the peer approval to settings manager this merging is obsolete
if settings.Extra == nil {
settings.Extra = &types.ExtraSettings{}
}
settings.Extra.FlowEnabled = extraSettings.FlowEnabled
return settings.Extra, nil
}
func (m *managerImpl) UpdateExtraSettings(ctx context.Context, accountID, userID string, extraSettings *types.ExtraSettings) (bool, error) {
return m.extraSettingsManager.UpdateExtraSettings(ctx, accountID, userID, extraSettings)
}
@@ -0,0 +1,96 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: ./manager.go
// Package settings is a generated GoMock package.
package settings
import (
context "context"
reflect "reflect"
gomock "github.com/golang/mock/gomock"
extra_settings "github.com/netbirdio/netbird/management/server/integrations/extra_settings"
types "github.com/netbirdio/netbird/management/server/types"
)
// MockManager is a mock of Manager interface.
type MockManager struct {
ctrl *gomock.Controller
recorder *MockManagerMockRecorder
}
// MockManagerMockRecorder is the mock recorder for MockManager.
type MockManagerMockRecorder struct {
mock *MockManager
}
// NewMockManager creates a new mock instance.
func NewMockManager(ctrl *gomock.Controller) *MockManager {
mock := &MockManager{ctrl: ctrl}
mock.recorder = &MockManagerMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockManager) EXPECT() *MockManagerMockRecorder {
return m.recorder
}
// GetExtraSettings mocks base method.
func (m *MockManager) GetExtraSettings(ctx context.Context, accountID string) (*types.ExtraSettings, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetExtraSettings", ctx, accountID)
ret0, _ := ret[0].(*types.ExtraSettings)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetExtraSettings indicates an expected call of GetExtraSettings.
func (mr *MockManagerMockRecorder) GetExtraSettings(ctx, accountID interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetExtraSettings", reflect.TypeOf((*MockManager)(nil).GetExtraSettings), ctx, accountID)
}
// GetExtraSettingsManager mocks base method.
func (m *MockManager) GetExtraSettingsManager() extra_settings.Manager {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetExtraSettingsManager")
ret0, _ := ret[0].(extra_settings.Manager)
return ret0
}
// GetExtraSettingsManager indicates an expected call of GetExtraSettingsManager.
func (mr *MockManagerMockRecorder) GetExtraSettingsManager() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetExtraSettingsManager", reflect.TypeOf((*MockManager)(nil).GetExtraSettingsManager))
}
// GetSettings mocks base method.
func (m *MockManager) GetSettings(ctx context.Context, accountID, userID string) (*types.Settings, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetSettings", ctx, accountID, userID)
ret0, _ := ret[0].(*types.Settings)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetSettings indicates an expected call of GetSettings.
func (mr *MockManagerMockRecorder) GetSettings(ctx, accountID, userID interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSettings", reflect.TypeOf((*MockManager)(nil).GetSettings), ctx, accountID, userID)
}
// UpdateExtraSettings mocks base method.
func (m *MockManager) UpdateExtraSettings(ctx context.Context, accountID, userID string, extraSettings *types.ExtraSettings) (bool, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "UpdateExtraSettings", ctx, accountID, userID, extraSettings)
ret0, _ := ret[0].(bool)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// UpdateExtraSettings indicates an expected call of UpdateExtraSettings.
func (mr *MockManagerMockRecorder) UpdateExtraSettings(ctx, accountID, userID, extraSettings interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateExtraSettings", reflect.TypeOf((*MockManager)(nil).UpdateExtraSettings), ctx, accountID, userID, extraSettings)
}
+6
View File
@@ -40,6 +40,8 @@ const (
// Type is a type of the Error
type Type int32
var ErrExtraSettingsNotFound = fmt.Errorf("extra settings not found")
// Error is an internal error
type Error struct {
ErrorType Type
@@ -206,3 +208,7 @@ func NewOwnerDeletePermissionError() error {
func NewPATNotFoundError(patID string) error {
return Errorf(NotFound, "PAT: %s not found", patID)
}
func NewExtraSettingsNotFoundError() error {
return ErrExtraSettingsNotFound
}
+15 -3
View File
@@ -26,7 +26,6 @@ import (
"github.com/netbirdio/netbird/management/server/util"
nbdns "github.com/netbirdio/netbird/dns"
"github.com/netbirdio/netbird/management/server/account"
resourceTypes "github.com/netbirdio/netbird/management/server/networks/resources/types"
routerTypes "github.com/netbirdio/netbird/management/server/networks/routers/types"
networkTypes "github.com/netbirdio/netbird/management/server/networks/types"
@@ -95,7 +94,7 @@ func NewSqlStore(ctx context.Context, db *gorm.DB, storeEngine Engine, metrics t
err = db.AutoMigrate(
&types.SetupKey{}, &nbpeer.Peer{}, &types.User{}, &types.PersonalAccessToken{}, &types.Group{},
&types.Account{}, &types.Policy{}, &types.PolicyRule{}, &route.Route{}, &nbdns.NameServerGroup{},
&installation{}, &account.ExtraSettings{}, &posture.Checks{}, &nbpeer.NetworkAddress{},
&installation{}, &types.ExtraSettings{}, &posture.Checks{}, &nbpeer.NetworkAddress{},
&networkTypes.Network{}, &routerTypes.NetworkRouter{}, &resourceTypes.NetworkResource{},
)
if err != nil {
@@ -1317,7 +1316,6 @@ func (s *SqlStore) GetPeerByID(ctx context.Context, lockStrength LockingStrength
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, status.NewPeerNotFoundError(peerID)
}
log.WithContext(ctx).Errorf("failed to get peer from store: %s", result.Error)
return nil, status.Errorf(status.Internal, "failed to get peer from store")
}
@@ -2178,3 +2176,17 @@ func (s *SqlStore) DeletePAT(ctx context.Context, lockStrength LockingStrength,
return nil
}
func (s *SqlStore) GetPeerByIP(ctx context.Context, lockStrength LockingStrength, accountID string, ip net.IP) (*nbpeer.Peer, error) {
jsonValue := fmt.Sprintf(`"%s"`, ip.String())
var peer nbpeer.Peer
result := s.db.Clauses(clause.Locking{Strength: string(lockStrength)}).
First(&peer, "account_id = ? AND ip = ?", accountID, jsonValue)
if result.Error != nil {
// no logging here
return nil, status.Errorf(status.Internal, "failed to get peer from store")
}
return &peer, nil
}
+5 -6
View File
@@ -23,10 +23,9 @@ import (
"gorm.io/gorm"
"github.com/netbirdio/netbird/dns"
"github.com/netbirdio/netbird/management/server/telemetry"
"github.com/netbirdio/netbird/management/server/testutil"
"github.com/netbirdio/netbird/management/server/types"
"github.com/netbirdio/netbird/management/server/telemetry"
"github.com/netbirdio/netbird/util"
"github.com/netbirdio/netbird/management/server/migration"
@@ -185,6 +184,7 @@ type Store interface {
GetNetworkResourceByName(ctx context.Context, lockStrength LockingStrength, accountID, resourceName string) (*resourceTypes.NetworkResource, error)
SaveNetworkResource(ctx context.Context, lockStrength LockingStrength, resource *resourceTypes.NetworkResource) error
DeleteNetworkResource(ctx context.Context, lockStrength LockingStrength, accountID, resourceID string) error
GetPeerByIP(ctx context.Context, lockStrength LockingStrength, accountID string, ip net.IP) (*nbpeer.Peer, error)
}
type Engine string
@@ -342,7 +342,7 @@ func NewTestStoreFromSQL(ctx context.Context, filename string, dataDir string) (
}
if filename != "" {
err = loadSQL(db, filename)
err = LoadSQL(db, filename)
if err != nil {
return nil, nil, fmt.Errorf("failed to load SQL file: %v", err)
}
@@ -353,12 +353,11 @@ func NewTestStoreFromSQL(ctx context.Context, filename string, dataDir string) (
return nil, nil, fmt.Errorf("failed to create test store: %v", err)
}
err = addAllGroupToAccount(ctx, store)
err = addAllGroupToAccount(ctx, store)
if err != nil {
return nil, nil, fmt.Errorf("failed to add all group to account: %v", err)
}
maxRetries := 2
for i := 0; i < maxRetries; i++ {
sqlStore, cleanUp, err := getSqlStoreEngine(ctx, store, kind)
@@ -516,7 +515,7 @@ func replaceDBName(dsn, newDBName string) string {
return re.ReplaceAllString(dsn, `${pre}`+newDBName+`${post}`)
}
func loadSQL(db *gorm.DB, filepath string) error {
func LoadSQL(db *gorm.DB, filepath string) error {
sqlContent, err := os.ReadFile(filepath)
if err != nil {
return err
+43 -25
View File
@@ -12,8 +12,11 @@ import (
log "github.com/sirupsen/logrus"
"github.com/netbirdio/netbird/management/proto"
"github.com/netbirdio/netbird/management/server/settings"
auth "github.com/netbirdio/netbird/relay/auth/hmac"
authv2 "github.com/netbirdio/netbird/relay/auth/hmac/v2"
integrationsConfig "github.com/netbirdio/management-integrations/integrations/config"
)
const defaultDuration = 12 * time.Hour
@@ -22,31 +25,33 @@ const defaultDuration = 12 * time.Hour
type SecretsManager interface {
GenerateTurnToken() (*Token, error)
GenerateRelayToken() (*Token, error)
SetupRefresh(ctx context.Context, peerKey string)
SetupRefresh(ctx context.Context, accountID, peerKey string)
CancelRefresh(peerKey string)
}
// TimeBasedAuthSecretsManager generates credentials with TTL and using pre-shared secret known to TURN server
type TimeBasedAuthSecretsManager struct {
mux sync.Mutex
turnCfg *TURNConfig
relayCfg *Relay
turnHmacToken *auth.TimedHMAC
relayHmacToken *authv2.Generator
updateManager *PeersUpdateManager
turnCancelMap map[string]chan struct{}
relayCancelMap map[string]chan struct{}
mux sync.Mutex
turnCfg *TURNConfig
relayCfg *Relay
turnHmacToken *auth.TimedHMAC
relayHmacToken *authv2.Generator
updateManager *PeersUpdateManager
settingsManager settings.Manager
turnCancelMap map[string]chan struct{}
relayCancelMap map[string]chan struct{}
}
type Token auth.Token
func NewTimeBasedAuthSecretsManager(updateManager *PeersUpdateManager, turnCfg *TURNConfig, relayCfg *Relay) *TimeBasedAuthSecretsManager {
func NewTimeBasedAuthSecretsManager(updateManager *PeersUpdateManager, turnCfg *TURNConfig, relayCfg *Relay, settingsManager settings.Manager) *TimeBasedAuthSecretsManager {
mgr := &TimeBasedAuthSecretsManager{
updateManager: updateManager,
turnCfg: turnCfg,
relayCfg: relayCfg,
turnCancelMap: make(map[string]chan struct{}),
relayCancelMap: make(map[string]chan struct{}),
updateManager: updateManager,
turnCfg: turnCfg,
relayCfg: relayCfg,
turnCancelMap: make(map[string]chan struct{}),
relayCancelMap: make(map[string]chan struct{}),
settingsManager: settingsManager,
}
if turnCfg != nil {
@@ -126,7 +131,7 @@ func (m *TimeBasedAuthSecretsManager) CancelRefresh(peerID string) {
}
// SetupRefresh starts peer credentials refresh
func (m *TimeBasedAuthSecretsManager) SetupRefresh(ctx context.Context, peerID string) {
func (m *TimeBasedAuthSecretsManager) SetupRefresh(ctx context.Context, accountID, peerID string) {
m.mux.Lock()
defer m.mux.Unlock()
@@ -136,19 +141,19 @@ func (m *TimeBasedAuthSecretsManager) SetupRefresh(ctx context.Context, peerID s
if m.turnCfg != nil && m.turnCfg.TimeBasedCredentials {
turnCancel := make(chan struct{}, 1)
m.turnCancelMap[peerID] = turnCancel
go m.refreshTURNTokens(ctx, peerID, turnCancel)
go m.refreshTURNTokens(ctx, accountID, peerID, turnCancel)
log.WithContext(ctx).Debugf("starting TURN refresh for %s", peerID)
}
if m.relayCfg != nil {
relayCancel := make(chan struct{}, 1)
m.relayCancelMap[peerID] = relayCancel
go m.refreshRelayTokens(ctx, peerID, relayCancel)
go m.refreshRelayTokens(ctx, accountID, peerID, relayCancel)
log.WithContext(ctx).Debugf("starting relay refresh for %s", peerID)
}
}
func (m *TimeBasedAuthSecretsManager) refreshTURNTokens(ctx context.Context, peerID string, cancel chan struct{}) {
func (m *TimeBasedAuthSecretsManager) refreshTURNTokens(ctx context.Context, accountID, peerID string, cancel chan struct{}) {
ticker := time.NewTicker(m.turnCfg.CredentialsTTL.Duration / 4 * 3)
defer ticker.Stop()
@@ -158,12 +163,12 @@ func (m *TimeBasedAuthSecretsManager) refreshTURNTokens(ctx context.Context, pee
log.WithContext(ctx).Debugf("stopping TURN refresh for %s", peerID)
return
case <-ticker.C:
m.pushNewTURNAndRelayTokens(ctx, peerID)
m.pushNewTURNAndRelayTokens(ctx, accountID, peerID)
}
}
}
func (m *TimeBasedAuthSecretsManager) refreshRelayTokens(ctx context.Context, peerID string, cancel chan struct{}) {
func (m *TimeBasedAuthSecretsManager) refreshRelayTokens(ctx context.Context, accountID, peerID string, cancel chan struct{}) {
ticker := time.NewTicker(m.relayCfg.CredentialsTTL.Duration / 4 * 3)
defer ticker.Stop()
@@ -173,15 +178,15 @@ func (m *TimeBasedAuthSecretsManager) refreshRelayTokens(ctx context.Context, pe
log.WithContext(ctx).Debugf("stopping relay refresh for %s", peerID)
return
case <-ticker.C:
m.pushNewRelayTokens(ctx, peerID)
m.pushNewRelayTokens(ctx, accountID, peerID)
}
}
}
func (m *TimeBasedAuthSecretsManager) pushNewTURNAndRelayTokens(ctx context.Context, peerID string) {
func (m *TimeBasedAuthSecretsManager) pushNewTURNAndRelayTokens(ctx context.Context, accountID, peerID string) {
turnToken, err := m.turnHmacToken.GenerateToken(sha1.New)
if err != nil {
log.Errorf("failed to generate token for peer '%s': %s", peerID, err)
log.WithContext(ctx).Errorf("failed to generate token for peer '%s': %s", peerID, err)
return
}
@@ -216,11 +221,13 @@ func (m *TimeBasedAuthSecretsManager) pushNewTURNAndRelayTokens(ctx context.Cont
}
}
m.extendNetbirdConfig(ctx, accountID, update)
log.WithContext(ctx).Debugf("sending new TURN credentials to peer %s", peerID)
m.updateManager.SendUpdate(ctx, peerID, &UpdateMessage{Update: update})
}
func (m *TimeBasedAuthSecretsManager) pushNewRelayTokens(ctx context.Context, peerID string) {
func (m *TimeBasedAuthSecretsManager) pushNewRelayTokens(ctx context.Context, accountID, peerID string) {
relayToken, err := m.relayHmacToken.GenerateToken()
if err != nil {
log.Errorf("failed to generate relay token for peer '%s': %s", peerID, err)
@@ -238,6 +245,17 @@ func (m *TimeBasedAuthSecretsManager) pushNewRelayTokens(ctx context.Context, pe
},
}
m.extendNetbirdConfig(ctx, accountID, update)
log.WithContext(ctx).Debugf("sending new relay credentials to peer %s", peerID)
m.updateManager.SendUpdate(ctx, peerID, &UpdateMessage{Update: update})
}
func (m *TimeBasedAuthSecretsManager) extendNetbirdConfig(ctx context.Context, accountID string, update *proto.SyncResponse) {
extraSettings, err := m.settingsManager.GetExtraSettings(ctx, accountID)
if err != nil {
log.WithContext(ctx).Errorf("failed to get extra settings: %v", err)
}
integrationsConfig.ExtendNetBirdConfig(update.NetbirdConfig, extraSettings)
}
+23 -5
View File
@@ -10,9 +10,12 @@ import (
"testing"
"time"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/require"
"github.com/netbirdio/netbird/management/proto"
"github.com/netbirdio/netbird/management/server/settings"
"github.com/netbirdio/netbird/management/server/types"
"github.com/netbirdio/netbird/util"
)
@@ -34,12 +37,16 @@ func TestTimeBasedAuthSecretsManager_GenerateCredentials(t *testing.T) {
Secret: secret,
}
ctrl := gomock.NewController(t)
t.Cleanup(ctrl.Finish)
settingsMockManager := settings.NewMockManager(ctrl)
tested := NewTimeBasedAuthSecretsManager(peersManager, &TURNConfig{
CredentialsTTL: ttl,
Secret: secret,
Turns: []*Host{TurnTestHost},
TimeBasedCredentials: true,
}, rc)
}, rc, settingsMockManager)
turnCredentials, err := tested.GenerateTurnToken()
require.NoError(t, err)
@@ -79,17 +86,23 @@ func TestTimeBasedAuthSecretsManager_SetupRefresh(t *testing.T) {
CredentialsTTL: ttl,
Secret: secret,
}
ctrl := gomock.NewController(t)
t.Cleanup(ctrl.Finish)
settingsMockManager := settings.NewMockManager(ctrl)
settingsMockManager.EXPECT().GetExtraSettings(gomock.Any(), "someAccountID").Return(&types.ExtraSettings{}, nil).AnyTimes()
tested := NewTimeBasedAuthSecretsManager(peersManager, &TURNConfig{
CredentialsTTL: ttl,
Secret: secret,
Turns: []*Host{TurnTestHost},
TimeBasedCredentials: true,
}, rc)
}, rc, settingsMockManager)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
tested.SetupRefresh(ctx, peer)
tested.SetupRefresh(ctx, "someAccountID", peer)
if _, ok := tested.turnCancelMap[peer]; !ok {
t.Errorf("expecting peer to be present in the turn cancel map, got not present")
@@ -176,14 +189,19 @@ func TestTimeBasedAuthSecretsManager_CancelRefresh(t *testing.T) {
CredentialsTTL: ttl,
Secret: secret,
}
ctrl := gomock.NewController(t)
t.Cleanup(ctrl.Finish)
settingsMockManager := settings.NewMockManager(ctrl)
tested := NewTimeBasedAuthSecretsManager(peersManager, &TURNConfig{
CredentialsTTL: ttl,
Secret: secret,
Turns: []*Host{TurnTestHost},
TimeBasedCredentials: true,
}, rc)
}, rc, settingsMockManager)
tested.SetupRefresh(context.Background(), peer)
tested.SetupRefresh(context.Background(), "someAccountID", peer)
if _, ok := tested.turnCancelMap[peer]; !ok {
t.Errorf("expecting peer to be present in turn cancel map, got not present")
}
+1
View File
@@ -1012,6 +1012,7 @@ func (a *Account) connResourcesGenerator(ctx context.Context) (func(*PolicyRule,
}
fr := FirewallRule{
PolicyID: rule.ID,
PeerIP: peer.IP.String(),
Direction: direction,
Action: string(rule.Action),
+4
View File
@@ -20,6 +20,9 @@ const (
// FirewallRule is a rule of the firewall.
type FirewallRule struct {
// PolicyID is the ID of the policy this rule is derived from
PolicyID string
// PeerIP of the peer
PeerIP string
@@ -58,6 +61,7 @@ func generateRouteFirewallRules(ctx context.Context, route *nbroute.Route, rule
}
baseRule := RouteFirewallRule{
PolicyID: rule.PolicyID,
SourceRanges: sourceRanges,
Action: string(rule.Action),
Destination: route.Network.String(),
+37
View File
@@ -0,0 +1,37 @@
package types
import (
"net"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
)
// PeerSync used as a data object between the gRPC API and Manager on Sync request.
type PeerSync struct {
// WireGuardPubKey is a peers WireGuard public key
WireGuardPubKey string
// Meta is the system information passed by peer, must be always present
Meta nbpeer.PeerSystemMeta
// UpdateAccountPeers indicate updating account peers,
// which occurs when the peer's metadata is updated
UpdateAccountPeers bool
}
// PeerLogin used as a data object between the gRPC API and Manager on Login request.
type PeerLogin struct {
// WireGuardPubKey is a peers WireGuard public key
WireGuardPubKey string
// SSHKey is a peer's ssh key. Can be empty (e.g., old version do not provide it, or this feature is disabled)
SSHKey string
// Meta is the system information passed by peer, must be always present.
Meta nbpeer.PeerSystemMeta
// UserID indicates that JWT was used to log in, and it was valid. Can be empty when SetupKey is used or auth is not required.
UserID string
// SetupKey references to a server.SetupKey to log in. Can be empty when UserID is used or auth is not required.
SetupKey string
// ConnectionIP is the real IP of the peer
ConnectionIP net.IP
// ExtraDNSLabels is a list of extra DNS labels that the peer wants to use
ExtraDNSLabels []string
}
@@ -6,6 +6,9 @@ import (
// RouteFirewallRule a firewall rule applicable for a routed network.
type RouteFirewallRule struct {
// PolicyID is the ID of the policy this rule is derived from
PolicyID string
// SourceRanges IP ranges of the routing peers.
SourceRanges []string
+24 -3
View File
@@ -2,8 +2,6 @@ package types
import (
"time"
"github.com/netbirdio/netbird/management/server/account"
)
// Settings represents Account settings structure that can be modified via API and Dashboard
@@ -42,7 +40,7 @@ type Settings struct {
RoutingPeerDNSResolutionEnabled bool
// Extra is a dictionary of Account settings
Extra *account.ExtraSettings `gorm:"embedded;embeddedPrefix:extra_"`
Extra *ExtraSettings `gorm:"embedded;embeddedPrefix:extra_"`
}
// Copy copies the Settings struct
@@ -66,3 +64,26 @@ func (s *Settings) Copy() *Settings {
}
return settings
}
type ExtraSettings struct {
// PeerApprovalEnabled enables or disables the need for peers bo be approved by an administrator
PeerApprovalEnabled bool
// IntegratedValidatorGroups list of group IDs to be used with integrated approval configurations
IntegratedValidatorGroups []string `gorm:"serializer:json"`
FlowEnabled bool `gorm:"-"`
FlowPacketCounterEnabled bool `gorm:"-"`
FlowENCollectionEnabled bool `gorm:"-"`
FlowDnsCollectionEnabled bool `gorm:"-"`
}
// Copy copies the ExtraSettings struct
func (e *ExtraSettings) Copy() *ExtraSettings {
var cpGroup []string
return &ExtraSettings{
PeerApprovalEnabled: e.PeerApprovalEnabled,
IntegratedValidatorGroups: append(cpGroup, e.IntegratedValidatorGroups...),
}
}
+13 -15
View File
@@ -7,19 +7,18 @@ import (
"testing"
"time"
"github.com/eko/gocache/v3/cache"
cacheStore "github.com/eko/gocache/v3/store"
"github.com/google/go-cmp/cmp"
"golang.org/x/exp/maps"
nbcache "github.com/netbirdio/netbird/management/server/cache"
nbcontext "github.com/netbirdio/netbird/management/server/context"
"github.com/netbirdio/netbird/management/server/util"
"golang.org/x/exp/maps"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
"github.com/netbirdio/netbird/management/server/store"
"github.com/netbirdio/netbird/management/server/types"
gocache "github.com/patrickmn/go-cache"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
@@ -517,9 +516,10 @@ func TestUser_InviteNewUser(t *testing.T) {
cacheLoading: map[string]chan struct{}{},
}
goCacheClient := gocache.New(CacheExpirationMax, 30*time.Minute)
goCacheStore := cacheStore.NewGoCache(goCacheClient)
am.cacheManager = cache.NewLoadable[[]*idp.UserData](am.loadAccount, cache.New[[]*idp.UserData](goCacheStore))
cs, err := nbcache.NewStore(nbcache.DefaultIDPCacheExpirationMax, nbcache.DefaultIDPCacheCleanupInterval)
require.NoError(t, err)
am.cacheManager = nbcache.NewAccountUserDataCache(am.loadAccount, cs)
mockData := []*idp.UserData{
{
@@ -1092,21 +1092,19 @@ func TestDefaultAccountManager_ExternalCache(t *testing.T) {
eventStore: &activity.InMemoryEventStore{},
idpManager: &idp.GoogleWorkspaceManager{}, // empty manager
cacheLoading: map[string]chan struct{}{},
cacheManager: cache.New[[]*idp.UserData](
cacheStore.NewGoCache(gocache.New(CacheExpirationMax, 30*time.Minute)),
),
externalCacheManager: cache.New[*idp.UserData](
cacheStore.NewGoCache(gocache.New(CacheExpirationMax, 30*time.Minute)),
),
}
cacheStore, err := nbcache.NewStore(nbcache.DefaultIDPCacheExpirationMax, nbcache.DefaultIDPCacheCleanupInterval)
assert.NoError(t, err)
am.externalCacheManager = nbcache.NewUserDataCache(cacheStore)
am.cacheManager = nbcache.NewAccountUserDataCache(am.loadAccount, cacheStore)
// pretend that we receive mockUserID from IDP
err = am.cacheManager.Set(am.ctx, mockAccountID, []*idp.UserData{{Name: mockUserID, ID: mockUserID}})
err = am.cacheManager.Set(am.ctx, mockAccountID, []*idp.UserData{{Name: mockUserID, ID: mockUserID}}, time.Minute)
assert.NoError(t, err)
cacheManager := am.GetExternalCacheManager()
cacheKey := externalUser.IntegrationReference.CacheKey(mockAccountID, externalUser.Id)
err = cacheManager.Set(context.Background(), cacheKey, &idp.UserData{ID: externalUser.Id, Name: "Test User", Email: "user@example.com"})
err = cacheManager.Set(context.Background(), cacheKey, &idp.UserData{ID: externalUser.Id, Name: "Test User", Email: "user@example.com"}, time.Minute)
assert.NoError(t, err)
infos, err := am.GetUsersFromAccount(context.Background(), mockAccountID, mockUserID)