mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-23 13:59:09 +02:00
Merge remote-tracking branch 'origin/main' into dmitri-event-aggregation
Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io>
This commit is contained in:
+1
-1
@@ -130,7 +130,7 @@ func debugConfigDump(cmd *cobra.Command, _ []string) error {
|
|||||||
|
|
||||||
client := proto.NewDaemonServiceClient(conn)
|
client := proto.NewDaemonServiceClient(conn)
|
||||||
resp, err := client.GetConfig(cmd.Context(), &proto.GetConfigRequest{
|
resp, err := client.GetConfig(cmd.Context(), &proto.GetConfigRequest{
|
||||||
ProfileName: activeProf.Name,
|
ProfileName: string(activeProf.ID),
|
||||||
Username: currUser.Username,
|
Username: currUser.Username,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -1205,7 +1205,7 @@ func (s *Server) SyncMeta(ctx context.Context, req *proto.EncryptedMessage) (*pr
|
|||||||
return nil, msg
|
return nil, msg
|
||||||
}
|
}
|
||||||
|
|
||||||
err = s.accountManager.SyncPeerMeta(ctx, peerKey.String(), extractPeerMeta(ctx, syncMetaReq.GetMeta()))
|
err = s.accountManager.SyncPeerMeta(ctx, peerKey.String(), extractPeerMeta(ctx, syncMetaReq.GetMeta()), realIP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, mapError(ctx, err)
|
return nil, mapError(ctx, err)
|
||||||
}
|
}
|
||||||
@@ -1254,7 +1254,10 @@ func (s *Server) Logout(ctx context.Context, req *proto.EncryptedMessage) (*prot
|
|||||||
func toProtocolChecks(ctx context.Context, postureChecks []*posture.Checks) []*proto.Checks {
|
func toProtocolChecks(ctx context.Context, postureChecks []*posture.Checks) []*proto.Checks {
|
||||||
protoChecks := make([]*proto.Checks, 0, len(postureChecks))
|
protoChecks := make([]*proto.Checks, 0, len(postureChecks))
|
||||||
for _, postureCheck := range postureChecks {
|
for _, postureCheck := range postureChecks {
|
||||||
protoChecks = append(protoChecks, toProtocolCheck(postureCheck))
|
check := toProtocolCheck(postureCheck)
|
||||||
|
if check != nil {
|
||||||
|
protoChecks = append(protoChecks, check)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return protoChecks
|
return protoChecks
|
||||||
@@ -1278,5 +1281,9 @@ func toProtocolCheck(postureCheck *posture.Checks) *proto.Checks {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(protoCheck.Files) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
return protoCheck
|
return protoCheck
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1889,12 +1889,12 @@ func domainIsUpToDate(domain string, domainCategory string, userAuth auth.UserAu
|
|||||||
// concurrent stream that started earlier loses the optimistic-lock race
|
// concurrent stream that started earlier loses the optimistic-lock race
|
||||||
// in MarkPeerConnected and bails without writing.
|
// in MarkPeerConnected and bails without writing.
|
||||||
func (am *DefaultAccountManager) SyncAndMarkPeer(ctx context.Context, accountID string, peerPubKey string, meta nbpeer.PeerSystemMeta, realIP net.IP, syncTime time.Time) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, int64, error) {
|
func (am *DefaultAccountManager) SyncAndMarkPeer(ctx context.Context, accountID string, peerPubKey string, meta nbpeer.PeerSystemMeta, realIP net.IP, syncTime time.Time) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, int64, error) {
|
||||||
peer, netMap, postureChecks, dnsfwdPort, err := am.SyncPeer(ctx, types.PeerSync{WireGuardPubKey: peerPubKey, Meta: meta}, accountID)
|
peer, netMap, postureChecks, dnsfwdPort, err := am.SyncPeer(ctx, types.PeerSync{WireGuardPubKey: peerPubKey, Meta: meta, RealIP: realIP}, accountID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, 0, fmt.Errorf("error syncing peer: %w", err)
|
return nil, nil, nil, 0, fmt.Errorf("error syncing peer: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := am.MarkPeerConnected(ctx, peerPubKey, realIP, accountID, syncTime.UnixNano(), netMap); err != nil {
|
if err := am.MarkPeerConnected(ctx, peerPubKey, accountID, syncTime.UnixNano(), netMap); err != nil {
|
||||||
log.WithContext(ctx).Warnf("failed marking peer as connected %s %v", peerPubKey, err)
|
log.WithContext(ctx).Warnf("failed marking peer as connected %s %v", peerPubKey, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1914,13 +1914,13 @@ func (am *DefaultAccountManager) OnPeerDisconnected(ctx context.Context, account
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (am *DefaultAccountManager) SyncPeerMeta(ctx context.Context, peerPubKey string, meta nbpeer.PeerSystemMeta) error {
|
func (am *DefaultAccountManager) SyncPeerMeta(ctx context.Context, peerPubKey string, meta nbpeer.PeerSystemMeta, realIP net.IP) error {
|
||||||
accountID, err := am.Store.GetAccountIDByPeerPubKey(ctx, peerPubKey)
|
accountID, err := am.Store.GetAccountIDByPeerPubKey(ctx, peerPubKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, _, _, err = am.SyncPeer(ctx, types.PeerSync{WireGuardPubKey: peerPubKey, Meta: meta, UpdateAccountPeers: true}, accountID)
|
_, _, _, _, err = am.SyncPeer(ctx, types.PeerSync{WireGuardPubKey: peerPubKey, Meta: meta, RealIP: realIP, UpdateAccountPeers: true}, accountID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ type Manager interface {
|
|||||||
GetUserFromUserAuth(ctx context.Context, userAuth auth.UserAuth) (*types.User, error)
|
GetUserFromUserAuth(ctx context.Context, userAuth auth.UserAuth) (*types.User, error)
|
||||||
ListUsers(ctx context.Context, accountID string) ([]*types.User, error)
|
ListUsers(ctx context.Context, accountID string) ([]*types.User, error)
|
||||||
GetPeers(ctx context.Context, accountID, userID, nameFilter, ipFilter string) ([]*nbpeer.Peer, error)
|
GetPeers(ctx context.Context, accountID, userID, nameFilter, ipFilter string) ([]*nbpeer.Peer, error)
|
||||||
MarkPeerConnected(ctx context.Context, peerKey string, realIP net.IP, accountID string, sessionStartedAt int64, nmap *types.NetworkMap) error
|
MarkPeerConnected(ctx context.Context, peerKey string, accountID string, sessionStartedAt int64, nmap *types.NetworkMap) error
|
||||||
MarkPeerDisconnected(ctx context.Context, peerKey string, accountID string, sessionStartedAt int64) error
|
MarkPeerDisconnected(ctx context.Context, peerKey string, accountID string, sessionStartedAt int64) error
|
||||||
DeletePeer(ctx context.Context, accountID, peerID, userID string) error
|
DeletePeer(ctx context.Context, accountID, peerID, userID string) error
|
||||||
UpdatePeer(ctx context.Context, accountID, userID string, p *nbpeer.Peer) (*nbpeer.Peer, error)
|
UpdatePeer(ctx context.Context, accountID, userID string, p *nbpeer.Peer) (*nbpeer.Peer, error)
|
||||||
@@ -123,7 +123,7 @@ type Manager interface {
|
|||||||
GetValidatedPeers(ctx context.Context, accountID string) (map[string]struct{}, map[string]string, error)
|
GetValidatedPeers(ctx context.Context, accountID string) (map[string]struct{}, map[string]string, error)
|
||||||
SyncAndMarkPeer(ctx context.Context, accountID string, peerPubKey string, meta nbpeer.PeerSystemMeta, realIP net.IP, syncTime time.Time) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, int64, error)
|
SyncAndMarkPeer(ctx context.Context, accountID string, peerPubKey string, meta nbpeer.PeerSystemMeta, realIP net.IP, syncTime time.Time) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, int64, error)
|
||||||
OnPeerDisconnected(ctx context.Context, accountID string, peerPubKey string, streamStartTime time.Time) error
|
OnPeerDisconnected(ctx context.Context, accountID string, peerPubKey string, streamStartTime time.Time) error
|
||||||
SyncPeerMeta(ctx context.Context, peerPubKey string, meta nbpeer.PeerSystemMeta) error
|
SyncPeerMeta(ctx context.Context, peerPubKey string, meta nbpeer.PeerSystemMeta, realIP net.IP) error
|
||||||
FindExistingPostureCheck(accountID string, checks *posture.ChecksDefinition) (*posture.Checks, error)
|
FindExistingPostureCheck(accountID string, checks *posture.ChecksDefinition) (*posture.Checks, error)
|
||||||
GetAccountIDForPeerKey(ctx context.Context, peerKey string) (string, error)
|
GetAccountIDForPeerKey(ctx context.Context, peerKey string) (string, error)
|
||||||
GetAccountSettings(ctx context.Context, accountID string, userID string) (*types.Settings, error)
|
GetAccountSettings(ctx context.Context, accountID string, userID string) (*types.Settings, error)
|
||||||
|
|||||||
@@ -1323,17 +1323,17 @@ func (mr *MockManagerMockRecorder) ExtendPeerSession(ctx, peerPubKey, userID int
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MarkPeerConnected mocks base method.
|
// MarkPeerConnected mocks base method.
|
||||||
func (m *MockManager) MarkPeerConnected(ctx context.Context, peerKey string, realIP net.IP, accountID string, sessionStartedAt int64, nmap *types.NetworkMap) error {
|
func (m *MockManager) MarkPeerConnected(ctx context.Context, peerKey string, accountID string, sessionStartedAt int64, nmap *types.NetworkMap) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "MarkPeerConnected", ctx, peerKey, realIP, accountID, sessionStartedAt, nmap)
|
ret := m.ctrl.Call(m, "MarkPeerConnected", ctx, peerKey, accountID, sessionStartedAt, nmap)
|
||||||
ret0, _ := ret[0].(error)
|
ret0, _ := ret[0].(error)
|
||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarkPeerConnected indicates an expected call of MarkPeerConnected.
|
// MarkPeerConnected indicates an expected call of MarkPeerConnected.
|
||||||
func (mr *MockManagerMockRecorder) MarkPeerConnected(ctx, peerKey, realIP, accountID, sessionStartedAt, nmap interface{}) *gomock.Call {
|
func (mr *MockManagerMockRecorder) MarkPeerConnected(ctx, peerKey, accountID, sessionStartedAt, nmap interface{}) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MarkPeerConnected", reflect.TypeOf((*MockManager)(nil).MarkPeerConnected), ctx, peerKey, realIP, accountID, sessionStartedAt, nmap)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MarkPeerConnected", reflect.TypeOf((*MockManager)(nil).MarkPeerConnected), ctx, peerKey, accountID, sessionStartedAt, nmap)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarkPeerDisconnected mocks base method.
|
// MarkPeerDisconnected mocks base method.
|
||||||
@@ -1586,17 +1586,17 @@ func (mr *MockManagerMockRecorder) SyncPeer(ctx, sync, accountID interface{}) *g
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SyncPeerMeta mocks base method.
|
// SyncPeerMeta mocks base method.
|
||||||
func (m *MockManager) SyncPeerMeta(ctx context.Context, peerPubKey string, meta peer.PeerSystemMeta) error {
|
func (m *MockManager) SyncPeerMeta(ctx context.Context, peerPubKey string, meta peer.PeerSystemMeta, realIP net.IP) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "SyncPeerMeta", ctx, peerPubKey, meta)
|
ret := m.ctrl.Call(m, "SyncPeerMeta", ctx, peerPubKey, meta, realIP)
|
||||||
ret0, _ := ret[0].(error)
|
ret0, _ := ret[0].(error)
|
||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
// SyncPeerMeta indicates an expected call of SyncPeerMeta.
|
// SyncPeerMeta indicates an expected call of SyncPeerMeta.
|
||||||
func (mr *MockManagerMockRecorder) SyncPeerMeta(ctx, peerPubKey, meta interface{}) *gomock.Call {
|
func (mr *MockManagerMockRecorder) SyncPeerMeta(ctx, peerPubKey, meta, realIP interface{}) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SyncPeerMeta", reflect.TypeOf((*MockManager)(nil).SyncPeerMeta), ctx, peerPubKey, meta)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SyncPeerMeta", reflect.TypeOf((*MockManager)(nil).SyncPeerMeta), ctx, peerPubKey, meta, realIP)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SyncUserJWTGroups mocks base method.
|
// SyncUserJWTGroups mocks base method.
|
||||||
|
|||||||
@@ -1836,7 +1836,7 @@ func TestDefaultAccountManager_UpdatePeer_PeerLoginExpiration(t *testing.T) {
|
|||||||
accountID, err := manager.GetAccountIDByUserID(context.Background(), auth.UserAuth{UserId: userID})
|
accountID, err := manager.GetAccountIDByUserID(context.Background(), auth.UserAuth{UserId: userID})
|
||||||
require.NoError(t, err, "unable to get the account")
|
require.NoError(t, err, "unable to get the account")
|
||||||
|
|
||||||
err = manager.MarkPeerConnected(context.Background(), key.PublicKey().String(), nil, accountID, time.Now().UTC().UnixNano(), nil)
|
err = manager.MarkPeerConnected(context.Background(), key.PublicKey().String(), accountID, time.Now().UTC().UnixNano(), nil)
|
||||||
require.NoError(t, err, "unable to mark peer connected")
|
require.NoError(t, err, "unable to mark peer connected")
|
||||||
|
|
||||||
_, err = manager.UpdateAccountSettings(context.Background(), accountID, userID, &types.Settings{
|
_, err = manager.UpdateAccountSettings(context.Background(), accountID, userID, &types.Settings{
|
||||||
@@ -1907,7 +1907,7 @@ func TestDefaultAccountManager_MarkPeerConnected_PeerLoginExpiration(t *testing.
|
|||||||
require.NoError(t, err, "unable to get the account")
|
require.NoError(t, err, "unable to get the account")
|
||||||
|
|
||||||
// when we mark peer as connected, the peer login expiration routine should trigger
|
// when we mark peer as connected, the peer login expiration routine should trigger
|
||||||
err = manager.MarkPeerConnected(context.Background(), key.PublicKey().String(), nil, accountID, time.Now().UTC().UnixNano(), nil)
|
err = manager.MarkPeerConnected(context.Background(), key.PublicKey().String(), accountID, time.Now().UTC().UnixNano(), nil)
|
||||||
require.NoError(t, err, "unable to mark peer connected")
|
require.NoError(t, err, "unable to mark peer connected")
|
||||||
|
|
||||||
failed := waitTimeout(wg, time.Second)
|
failed := waitTimeout(wg, time.Second)
|
||||||
@@ -1935,7 +1935,7 @@ func TestDefaultAccountManager_OnPeerDisconnected_LastSeenCheck(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("disconnect peer when session token matches", func(t *testing.T) {
|
t.Run("disconnect peer when session token matches", func(t *testing.T) {
|
||||||
streamStartTime := time.Now().UTC()
|
streamStartTime := time.Now().UTC()
|
||||||
err = manager.MarkPeerConnected(context.Background(), peerPubKey, nil, accountID, streamStartTime.UnixNano(), nil)
|
err = manager.MarkPeerConnected(context.Background(), peerPubKey, accountID, streamStartTime.UnixNano(), nil)
|
||||||
require.NoError(t, err, "unable to mark peer connected")
|
require.NoError(t, err, "unable to mark peer connected")
|
||||||
|
|
||||||
peer, err := manager.Store.GetPeerByPeerPubKey(context.Background(), store.LockingStrengthNone, peerPubKey)
|
peer, err := manager.Store.GetPeerByPeerPubKey(context.Background(), store.LockingStrengthNone, peerPubKey)
|
||||||
@@ -1956,7 +1956,7 @@ func TestDefaultAccountManager_OnPeerDisconnected_LastSeenCheck(t *testing.T) {
|
|||||||
t.Run("skip disconnect when stored session is newer (zombie stream protection)", func(t *testing.T) {
|
t.Run("skip disconnect when stored session is newer (zombie stream protection)", func(t *testing.T) {
|
||||||
// Newer stream wins on connect (sets SessionStartedAt = now ns).
|
// Newer stream wins on connect (sets SessionStartedAt = now ns).
|
||||||
streamStartTime := time.Now().UTC()
|
streamStartTime := time.Now().UTC()
|
||||||
err = manager.MarkPeerConnected(context.Background(), peerPubKey, nil, accountID, streamStartTime.UnixNano(), nil)
|
err = manager.MarkPeerConnected(context.Background(), peerPubKey, accountID, streamStartTime.UnixNano(), nil)
|
||||||
require.NoError(t, err, "unable to mark peer connected")
|
require.NoError(t, err, "unable to mark peer connected")
|
||||||
|
|
||||||
peer, err := manager.Store.GetPeerByPeerPubKey(context.Background(), store.LockingStrengthNone, peerPubKey)
|
peer, err := manager.Store.GetPeerByPeerPubKey(context.Background(), store.LockingStrengthNone, peerPubKey)
|
||||||
@@ -1980,7 +1980,7 @@ func TestDefaultAccountManager_OnPeerDisconnected_LastSeenCheck(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("skip stale connect when stored session is newer (blocked goroutine protection)", func(t *testing.T) {
|
t.Run("skip stale connect when stored session is newer (blocked goroutine protection)", func(t *testing.T) {
|
||||||
node2SyncTime := time.Now().UTC()
|
node2SyncTime := time.Now().UTC()
|
||||||
err = manager.MarkPeerConnected(context.Background(), peerPubKey, nil, accountID, node2SyncTime.UnixNano(), nil)
|
err = manager.MarkPeerConnected(context.Background(), peerPubKey, accountID, node2SyncTime.UnixNano(), nil)
|
||||||
require.NoError(t, err, "node 2 should connect peer")
|
require.NoError(t, err, "node 2 should connect peer")
|
||||||
|
|
||||||
peer, err := manager.Store.GetPeerByPeerPubKey(context.Background(), store.LockingStrengthNone, peerPubKey)
|
peer, err := manager.Store.GetPeerByPeerPubKey(context.Background(), store.LockingStrengthNone, peerPubKey)
|
||||||
@@ -1990,7 +1990,7 @@ func TestDefaultAccountManager_OnPeerDisconnected_LastSeenCheck(t *testing.T) {
|
|||||||
"SessionStartedAt should equal node2SyncTime token")
|
"SessionStartedAt should equal node2SyncTime token")
|
||||||
|
|
||||||
node1StaleSyncTime := node2SyncTime.Add(-1 * time.Minute)
|
node1StaleSyncTime := node2SyncTime.Add(-1 * time.Minute)
|
||||||
err = manager.MarkPeerConnected(context.Background(), peerPubKey, nil, accountID, node1StaleSyncTime.UnixNano(), nil)
|
err = manager.MarkPeerConnected(context.Background(), peerPubKey, accountID, node1StaleSyncTime.UnixNano(), nil)
|
||||||
require.NoError(t, err, "stale connect should not return error")
|
require.NoError(t, err, "stale connect should not return error")
|
||||||
|
|
||||||
peer, err = manager.Store.GetPeerByPeerPubKey(context.Background(), store.LockingStrengthNone, peerPubKey)
|
peer, err = manager.Store.GetPeerByPeerPubKey(context.Background(), store.LockingStrengthNone, peerPubKey)
|
||||||
@@ -2052,7 +2052,7 @@ func TestDefaultAccountManager_MarkPeerConnected_ConcurrentRace(t *testing.T) {
|
|||||||
defer done.Done()
|
defer done.Done()
|
||||||
ready.Done()
|
ready.Done()
|
||||||
start.Wait()
|
start.Wait()
|
||||||
errs <- manager.MarkPeerConnected(context.Background(), peerPubKey, nil, accountID, token, nil)
|
errs <- manager.MarkPeerConnected(context.Background(), peerPubKey, accountID, token, nil)
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2093,7 +2093,7 @@ func TestDefaultAccountManager_UpdateAccountSettings_PeerLoginExpiration(t *test
|
|||||||
account, err := manager.Store.GetAccount(context.Background(), accountID)
|
account, err := manager.Store.GetAccount(context.Background(), accountID)
|
||||||
require.NoError(t, err, "unable to get the account")
|
require.NoError(t, err, "unable to get the account")
|
||||||
|
|
||||||
err = manager.MarkPeerConnected(context.Background(), key.PublicKey().String(), nil, accountID, time.Now().UTC().UnixNano(), nil)
|
err = manager.MarkPeerConnected(context.Background(), key.PublicKey().String(), accountID, time.Now().UTC().UnixNano(), nil)
|
||||||
require.NoError(t, err, "unable to mark peer connected")
|
require.NoError(t, err, "unable to mark peer connected")
|
||||||
|
|
||||||
wg := &sync.WaitGroup{}
|
wg := &sync.WaitGroup{}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ type MockAccountManager struct {
|
|||||||
GetUserFromUserAuthFunc func(ctx context.Context, userAuth auth.UserAuth) (*types.User, error)
|
GetUserFromUserAuthFunc func(ctx context.Context, userAuth auth.UserAuth) (*types.User, error)
|
||||||
ListUsersFunc func(ctx context.Context, accountID string) ([]*types.User, error)
|
ListUsersFunc func(ctx context.Context, accountID string) ([]*types.User, error)
|
||||||
GetPeersFunc func(ctx context.Context, accountID, userID, nameFilter, ipFilter string) ([]*nbpeer.Peer, error)
|
GetPeersFunc func(ctx context.Context, accountID, userID, nameFilter, ipFilter string) ([]*nbpeer.Peer, error)
|
||||||
MarkPeerConnectedFunc func(ctx context.Context, peerKey string, realIP net.IP, accountID string, sessionStartedAt int64, nmap *types.NetworkMap) error
|
MarkPeerConnectedFunc func(ctx context.Context, peerKey string, accountID string, sessionStartedAt int64, nmap *types.NetworkMap) error
|
||||||
MarkPeerDisconnectedFunc func(ctx context.Context, peerKey string, accountID string, sessionStartedAt int64) error
|
MarkPeerDisconnectedFunc func(ctx context.Context, peerKey string, accountID string, sessionStartedAt int64) error
|
||||||
SyncAndMarkPeerFunc func(ctx context.Context, accountID string, peerPubKey string, meta nbpeer.PeerSystemMeta, realIP net.IP, syncTime time.Time) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, int64, error)
|
SyncAndMarkPeerFunc func(ctx context.Context, accountID string, peerPubKey string, meta nbpeer.PeerSystemMeta, realIP net.IP, syncTime time.Time) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, int64, error)
|
||||||
DeletePeerFunc func(ctx context.Context, accountID, peerKey, userID string) error
|
DeletePeerFunc func(ctx context.Context, accountID, peerKey, userID string) error
|
||||||
@@ -114,7 +114,7 @@ type MockAccountManager struct {
|
|||||||
GetIdpManagerFunc func() idp.Manager
|
GetIdpManagerFunc func() idp.Manager
|
||||||
UpdateIntegratedValidatorFunc func(ctx context.Context, accountID, userID, validator string, groups []string) error
|
UpdateIntegratedValidatorFunc func(ctx context.Context, accountID, userID, validator string, groups []string) error
|
||||||
GroupValidationFunc func(ctx context.Context, accountId string, groups []string) (bool, error)
|
GroupValidationFunc func(ctx context.Context, accountId string, groups []string) (bool, error)
|
||||||
SyncPeerMetaFunc func(ctx context.Context, peerPubKey string, meta nbpeer.PeerSystemMeta) error
|
SyncPeerMetaFunc func(ctx context.Context, peerPubKey string, meta nbpeer.PeerSystemMeta, realIP net.IP) error
|
||||||
FindExistingPostureCheckFunc func(accountID string, checks *posture.ChecksDefinition) (*posture.Checks, error)
|
FindExistingPostureCheckFunc func(accountID string, checks *posture.ChecksDefinition) (*posture.Checks, error)
|
||||||
GetAccountIDForPeerKeyFunc func(ctx context.Context, peerKey string) (string, error)
|
GetAccountIDForPeerKeyFunc func(ctx context.Context, peerKey string) (string, error)
|
||||||
GetAccountByIDFunc func(ctx context.Context, accountID string, userID string) (*types.Account, error)
|
GetAccountByIDFunc func(ctx context.Context, accountID string, userID string) (*types.Account, error)
|
||||||
@@ -345,9 +345,9 @@ func (am *MockAccountManager) GetAccountIDByUserID(ctx context.Context, userAuth
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MarkPeerConnected mock implementation of MarkPeerConnected from server.AccountManager interface
|
// MarkPeerConnected mock implementation of MarkPeerConnected from server.AccountManager interface
|
||||||
func (am *MockAccountManager) MarkPeerConnected(ctx context.Context, peerKey string, realIP net.IP, accountID string, sessionStartedAt int64, nmap *types.NetworkMap) error {
|
func (am *MockAccountManager) MarkPeerConnected(ctx context.Context, peerKey string, accountID string, sessionStartedAt int64, nmap *types.NetworkMap) error {
|
||||||
if am.MarkPeerConnectedFunc != nil {
|
if am.MarkPeerConnectedFunc != nil {
|
||||||
return am.MarkPeerConnectedFunc(ctx, peerKey, realIP, accountID, sessionStartedAt, nmap)
|
return am.MarkPeerConnectedFunc(ctx, peerKey, accountID, sessionStartedAt, nmap)
|
||||||
}
|
}
|
||||||
return status.Errorf(codes.Unimplemented, "method MarkPeerConnected is not implemented")
|
return status.Errorf(codes.Unimplemented, "method MarkPeerConnected is not implemented")
|
||||||
}
|
}
|
||||||
@@ -975,9 +975,9 @@ func (am *MockAccountManager) GroupValidation(ctx context.Context, accountId str
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SyncPeerMeta mocks SyncPeerMeta of the AccountManager interface
|
// SyncPeerMeta mocks SyncPeerMeta of the AccountManager interface
|
||||||
func (am *MockAccountManager) SyncPeerMeta(ctx context.Context, peerPubKey string, meta nbpeer.PeerSystemMeta) error {
|
func (am *MockAccountManager) SyncPeerMeta(ctx context.Context, peerPubKey string, meta nbpeer.PeerSystemMeta, realIP net.IP) error {
|
||||||
if am.SyncPeerMetaFunc != nil {
|
if am.SyncPeerMetaFunc != nil {
|
||||||
return am.SyncPeerMetaFunc(ctx, peerPubKey, meta)
|
return am.SyncPeerMetaFunc(ctx, peerPubKey, meta, realIP)
|
||||||
}
|
}
|
||||||
return status.Errorf(codes.Unimplemented, "method SyncPeerMeta is not implemented")
|
return status.Errorf(codes.Unimplemented, "method SyncPeerMeta is not implemented")
|
||||||
}
|
}
|
||||||
|
|||||||
+27
-24
@@ -74,7 +74,7 @@ func (am *DefaultAccountManager) GetPeers(ctx context.Context, accountID, userID
|
|||||||
//
|
//
|
||||||
// Disconnects use MarkPeerDisconnected and require the session to match
|
// Disconnects use MarkPeerDisconnected and require the session to match
|
||||||
// exactly; see PeerStatus.SessionStartedAt for the protocol.
|
// exactly; see PeerStatus.SessionStartedAt for the protocol.
|
||||||
func (am *DefaultAccountManager) MarkPeerConnected(ctx context.Context, peerPubKey string, realIP net.IP, accountID string, sessionStartedAt int64, nmap *types.NetworkMap) error {
|
func (am *DefaultAccountManager) MarkPeerConnected(ctx context.Context, peerPubKey string, accountID string, sessionStartedAt int64, nmap *types.NetworkMap) error {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
defer func() {
|
defer func() {
|
||||||
am.metrics.AccountManagerMetrics().RecordPeerStatusUpdateDuration(telemetry.PeerStatusConnect, time.Since(start))
|
am.metrics.AccountManagerMetrics().RecordPeerStatusUpdateDuration(telemetry.PeerStatusConnect, time.Since(start))
|
||||||
@@ -102,10 +102,6 @@ func (am *DefaultAccountManager) MarkPeerConnected(ctx context.Context, peerPubK
|
|||||||
}
|
}
|
||||||
am.metrics.AccountManagerMetrics().CountPeerStatusUpdate(telemetry.PeerStatusConnect, telemetry.PeerStatusApplied)
|
am.metrics.AccountManagerMetrics().CountPeerStatusUpdate(telemetry.PeerStatusConnect, telemetry.PeerStatusApplied)
|
||||||
|
|
||||||
if am.geo != nil && realIP != nil {
|
|
||||||
am.updatePeerLocationIfChanged(ctx, accountID, peer, realIP)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = am.schedulePeerExpirations(ctx, accountID, peer); err != nil {
|
if err = am.schedulePeerExpirations(ctx, accountID, peer); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -195,24 +191,28 @@ func (am *DefaultAccountManager) MarkPeerDisconnected(ctx context.Context, peerP
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// updatePeerLocationIfChanged refreshes the geolocation on a separate
|
// resolvePeerLocation looks up the geo location for realIP, returning nil when
|
||||||
// row update, only when the connection IP actually changed. Geo lookups
|
// there is nothing to apply: geo disabled, no real IP, the IP is unchanged from
|
||||||
// are expensive so we skip same-IP reconnects.
|
// what the peer already has, or the lookup failed. Geo lookups are skipped on
|
||||||
func (am *DefaultAccountManager) updatePeerLocationIfChanged(ctx context.Context, accountID string, peer *nbpeer.Peer, realIP net.IP) {
|
// same-IP reconnects since they are comparatively expensive. The returned value
|
||||||
|
// is applied by Peer.UpdateMetaIfNew so the change is persisted by its peer save.
|
||||||
|
func (am *DefaultAccountManager) resolvePeerLocation(ctx context.Context, peer *nbpeer.Peer, realIP net.IP) *nbpeer.Location {
|
||||||
|
if am.geo == nil || realIP == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
if peer.Location.ConnectionIP != nil && peer.Location.ConnectionIP.Equal(realIP) {
|
if peer.Location.ConnectionIP != nil && peer.Location.ConnectionIP.Equal(realIP) {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
location, err := am.geo.Lookup(realIP)
|
location, err := am.geo.Lookup(realIP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithContext(ctx).Warnf("failed to get location for peer %s realip: [%s]: %v", peer.ID, realIP.String(), err)
|
log.WithContext(ctx).Warnf("failed to get location for peer %s realip: [%s]: %v", peer.ID, realIP.String(), err)
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
peer.Location.ConnectionIP = realIP
|
return &nbpeer.Location{
|
||||||
peer.Location.CountryCode = location.Country.ISOCode
|
ConnectionIP: realIP,
|
||||||
peer.Location.CityName = location.City.Names.En
|
CountryCode: location.Country.ISOCode,
|
||||||
peer.Location.GeoNameID = location.City.GeonameID
|
CityName: location.City.Names.En,
|
||||||
if err := am.Store.SavePeerLocation(ctx, accountID, peer); err != nil {
|
GeoNameID: location.City.GeonameID,
|
||||||
log.WithContext(ctx).Warnf("could not store location for peer %s: %s", peer.ID, err)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -980,7 +980,8 @@ func getPeerIPDNSLabel(ip netip.Addr, peerHostName string) (string, error) {
|
|||||||
// SyncPeer checks whether peer is eligible for receiving NetworkMap (authenticated) and returns its NetworkMap if eligible
|
// SyncPeer checks whether peer is eligible for receiving NetworkMap (authenticated) and returns its NetworkMap if eligible
|
||||||
func (am *DefaultAccountManager) SyncPeer(ctx context.Context, sync types.PeerSync, accountID string) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, int64, error) {
|
func (am *DefaultAccountManager) SyncPeer(ctx context.Context, sync types.PeerSync, accountID string) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, int64, error) {
|
||||||
var peer *nbpeer.Peer
|
var peer *nbpeer.Peer
|
||||||
var updated, versionChanged, ipv6CapabilityChanged bool
|
var ipv6CapabilityChanged bool
|
||||||
|
var metaDiff nbpeer.MetaDiff
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
settings, err := am.Store.GetAccountSettings(ctx, store.LockingStrengthNone, accountID)
|
settings, err := am.Store.GetAccountSettings(ctx, store.LockingStrengthNone, accountID)
|
||||||
@@ -1010,9 +1011,10 @@ func (am *DefaultAccountManager) SyncPeer(ctx context.Context, sync types.PeerSy
|
|||||||
}
|
}
|
||||||
|
|
||||||
oldHasIPv6Cap := peer.HasCapability(nbpeer.PeerCapabilityIPv6Overlay)
|
oldHasIPv6Cap := peer.HasCapability(nbpeer.PeerCapabilityIPv6Overlay)
|
||||||
updated, versionChanged = peer.UpdateMetaIfNew(ctx, sync.Meta)
|
newLocation := am.resolvePeerLocation(ctx, peer, sync.RealIP)
|
||||||
|
metaDiff = peer.UpdateMetaIfNew(ctx, sync.Meta, newLocation)
|
||||||
ipv6CapabilityChanged = oldHasIPv6Cap != peer.HasCapability(nbpeer.PeerCapabilityIPv6Overlay)
|
ipv6CapabilityChanged = oldHasIPv6Cap != peer.HasCapability(nbpeer.PeerCapabilityIPv6Overlay)
|
||||||
if updated {
|
if metaDiff.Updated() {
|
||||||
am.metrics.AccountManagerMetrics().CountPeerMetUpdate()
|
am.metrics.AccountManagerMetrics().CountPeerMetUpdate()
|
||||||
log.WithContext(ctx).Tracef("peer %s metadata updated", peer.ID)
|
log.WithContext(ctx).Tracef("peer %s metadata updated", peer.ID)
|
||||||
if err = transaction.SavePeer(ctx, accountID, peer); err != nil {
|
if err = transaction.SavePeer(ctx, accountID, peer); err != nil {
|
||||||
@@ -1040,9 +1042,10 @@ func (am *DefaultAccountManager) SyncPeer(ctx context.Context, sync types.PeerSy
|
|||||||
return nil, nil, nil, 0, err
|
return nil, nil, nil, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if isStatusChanged || sync.UpdateAccountPeers || ipv6CapabilityChanged || (updated && (len(resPostureChecks) > 0 || versionChanged)) {
|
metaDiffAffectsPosture := posture.AffectsPosture(&metaDiff, resPostureChecks)
|
||||||
|
if isStatusChanged || sync.UpdateAccountPeers || ipv6CapabilityChanged || metaDiffAffectsPosture || metaDiff.VersionChanged || metaDiff.Hostname {
|
||||||
changedPeerIDs := []string{peer.ID}
|
changedPeerIDs := []string{peer.ID}
|
||||||
affectedPeerIDs := am.syncPeerAffectedPeers(ctx, accountID, peer.ID, nmap, peerNotValid, updated, len(resPostureChecks) > 0)
|
affectedPeerIDs := am.syncPeerAffectedPeers(ctx, accountID, peer.ID, nmap, peerNotValid, metaDiffAffectsPosture)
|
||||||
if err = am.networkMapController.OnPeersUpdated(ctx, accountID, changedPeerIDs, affectedPeerIDs); err != nil {
|
if err = am.networkMapController.OnPeersUpdated(ctx, accountID, changedPeerIDs, affectedPeerIDs); err != nil {
|
||||||
return nil, nil, nil, 0, fmt.Errorf("notify network map controller of peer update: %w", err)
|
return nil, nil, nil, 0, fmt.Errorf("notify network map controller of peer update: %w", err)
|
||||||
}
|
}
|
||||||
@@ -1059,8 +1062,8 @@ func (am *DefaultAccountManager) SyncPeer(ctx context.Context, sync types.PeerSy
|
|||||||
// metadata change that flips a posture result removes this peer from others'
|
// metadata change that flips a posture result removes this peer from others'
|
||||||
// maps asymmetrically; that case (and an invalid peer, whose map is empty) falls
|
// maps asymmetrically; that case (and an invalid peer, whose map is empty) falls
|
||||||
// back to the resolver.
|
// back to the resolver.
|
||||||
func (am *DefaultAccountManager) syncPeerAffectedPeers(ctx context.Context, accountID, peerID string, nmap *types.NetworkMap, peerNotValid, metaUpdated, hasPostureChecks bool) []string {
|
func (am *DefaultAccountManager) syncPeerAffectedPeers(ctx context.Context, accountID, peerID string, nmap *types.NetworkMap, peerNotValid, metaChangeAffectedPosture bool) []string {
|
||||||
if peerNotValid || (metaUpdated && hasPostureChecks) {
|
if peerNotValid || metaChangeAffectedPosture {
|
||||||
return am.resolveAffectedPeersForPeerChanges(ctx, am.Store, accountID, []string{peerID})
|
return am.resolveAffectedPeersForPeerChanges(ctx, am.Store, accountID, []string{peerID})
|
||||||
}
|
}
|
||||||
return affectedPeerIDsFromNetworkMap(nmap, peerID)
|
return affectedPeerIDsFromNetworkMap(nmap, peerID)
|
||||||
|
|||||||
+104
-20
@@ -256,14 +256,18 @@ func (p *Peer) Copy() *Peer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateMetaIfNew updates peer's system metadata if new information is provided
|
// UpdateMetaIfNew updates peer's system metadata and connection geo location if
|
||||||
// returns true if meta was updated, false otherwise
|
// new information is provided. newLocation is the geo location resolved from the
|
||||||
func (p *Peer) UpdateMetaIfNew(ctx context.Context, meta PeerSystemMeta) (updated, versionChanged bool) {
|
// peer's current connection IP, or nil when there is nothing to apply (geo
|
||||||
|
// disabled, no real IP, or the IP is unchanged); the caller owns the expensive
|
||||||
|
// lookup and the same-IP guard. It returns a MetaDiff describing what changed;
|
||||||
|
// diff.Updated() reports whether the peer needs to be persisted.
|
||||||
|
func (p *Peer) UpdateMetaIfNew(ctx context.Context, meta PeerSystemMeta, newLocation *Location) MetaDiff {
|
||||||
if meta.isEmpty() {
|
if meta.isEmpty() {
|
||||||
return updated, versionChanged
|
return MetaDiff{}
|
||||||
}
|
}
|
||||||
|
|
||||||
versionChanged = p.Meta.WtVersion != meta.WtVersion
|
versionChanged := p.Meta.WtVersion != meta.WtVersion
|
||||||
|
|
||||||
// Avoid overwriting UIVersion if the update was triggered sole by the CLI client
|
// Avoid overwriting UIVersion if the update was triggered sole by the CLI client
|
||||||
if meta.UIVersion == "" {
|
if meta.UIVersion == "" {
|
||||||
@@ -272,97 +276,177 @@ func (p *Peer) UpdateMetaIfNew(ctx context.Context, meta PeerSystemMeta) (update
|
|||||||
|
|
||||||
oldVersion := p.Meta.WtVersion
|
oldVersion := p.Meta.WtVersion
|
||||||
|
|
||||||
diff := metaDiff(p.Meta, meta)
|
diff := diffMeta(p.Meta, meta)
|
||||||
if len(diff) != 0 {
|
if diff.Any() {
|
||||||
p.Meta = meta
|
p.Meta = meta
|
||||||
updated = true
|
}
|
||||||
|
diff.VersionChanged = versionChanged
|
||||||
|
|
||||||
|
locationInfo := ""
|
||||||
|
if newLocation != nil {
|
||||||
|
p.Location = *newLocation
|
||||||
|
diff.LocationChanged = true
|
||||||
|
locationInfo = fmt.Sprintf("location changed to %s, ", newLocation.ConnectionIP)
|
||||||
}
|
}
|
||||||
|
|
||||||
versionInfo := ""
|
versionInfo := ""
|
||||||
if versionChanged {
|
if diff.VersionChanged {
|
||||||
versionInfo = fmt.Sprintf("version changed: %s -> %s, ", oldVersion, meta.WtVersion)
|
versionInfo = fmt.Sprintf("version changed: %s -> %s, ", oldVersion, meta.WtVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(diff) > 0 || versionChanged {
|
if diff.Any() || diff.VersionChanged || diff.LocationChanged {
|
||||||
log.WithContext(ctx).
|
log.WithContext(ctx).
|
||||||
Debugf("peer meta updated, %s%d field(s) changed: %s", versionInfo, len(diff), strings.Join(diff, ", "))
|
Debugf("peer meta updated, %s%s%d field(s) changed: %s", versionInfo, locationInfo, len(diff.Changed), strings.Join(diff.Changed, ", "))
|
||||||
}
|
}
|
||||||
|
|
||||||
return updated, versionChanged
|
return diff
|
||||||
|
}
|
||||||
|
|
||||||
|
// MetaDiff records which PeerSystemMeta fields differ between two metas. Each bool
|
||||||
|
// maps to a single struct field, except Environment, which is split into Cloud and
|
||||||
|
// Platform. Changed holds the human-readable `field: <old> -> <new>` entries so the
|
||||||
|
// existing log line and isEqual can be derived from the same comparison.
|
||||||
|
//
|
||||||
|
// VersionChanged and LocationChanged sit outside the per-meta-field set:
|
||||||
|
// VersionChanged tracks the WireGuard client version specifically (compared before
|
||||||
|
// the UIVersion fixup, to signal client upgrades) and LocationChanged tracks the
|
||||||
|
// peer's connection geo location, which lives on Peer rather than PeerSystemMeta.
|
||||||
|
// Neither contributes an entry to Changed, so the field-coverage accounting stays
|
||||||
|
// driven purely by the PeerSystemMeta comparison.
|
||||||
|
type MetaDiff struct {
|
||||||
|
Hostname bool
|
||||||
|
GoOS bool
|
||||||
|
Kernel bool
|
||||||
|
KernelVersion bool
|
||||||
|
Core bool
|
||||||
|
Platform bool
|
||||||
|
OS bool
|
||||||
|
OSVersion bool
|
||||||
|
WtVersion bool
|
||||||
|
UIVersion bool
|
||||||
|
SystemSerialNumber bool
|
||||||
|
SystemProductName bool
|
||||||
|
SystemManufacturer bool
|
||||||
|
EnvironmentCloud bool
|
||||||
|
EnvironmentPlatform bool
|
||||||
|
Flags bool
|
||||||
|
Capabilities bool
|
||||||
|
NetworkAddresses bool
|
||||||
|
Files bool
|
||||||
|
|
||||||
|
VersionChanged bool
|
||||||
|
LocationChanged bool
|
||||||
|
|
||||||
|
Changed []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Any reports whether any PeerSystemMeta field changed.
|
||||||
|
func (d MetaDiff) Any() bool {
|
||||||
|
return len(d.Changed) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Updated reports whether the peer needs to be persisted: any meta field changed
|
||||||
|
// or the geo location changed. The version flag alone does not imply a write,
|
||||||
|
// since a version change is also reflected in the WtVersion meta field.
|
||||||
|
func (d MetaDiff) Updated() bool {
|
||||||
|
return d.Any() || d.LocationChanged || d.VersionChanged
|
||||||
}
|
}
|
||||||
|
|
||||||
// metaDiff returns a human-readable list of the fields that differ between the
|
|
||||||
// old and new meta, each formatted as `field: <old> -> <new>`. It is the single
|
|
||||||
// source of truth for meta comparison: isEqual reports equality as an empty
|
|
||||||
// diff, so the log line can never disagree with the change decision. Slices are
|
|
||||||
// cloned before sorting, so callers' meta is not mutated.
|
|
||||||
func metaDiff(oldMeta, newMeta PeerSystemMeta) []string {
|
func metaDiff(oldMeta, newMeta PeerSystemMeta) []string {
|
||||||
var diff []string
|
return diffMeta(oldMeta, newMeta).Changed
|
||||||
|
}
|
||||||
|
|
||||||
|
// diffMeta compares two metas field by field, returning both a per-field flag set
|
||||||
|
// (for callers that need to know exactly what changed, e.g. matching against
|
||||||
|
// posture checks) and the human-readable Changed list. It is the single source of
|
||||||
|
// truth for meta comparison: isEqual reports equality as an empty diff, so the log
|
||||||
|
// line, the change decision, and the flags can never disagree.
|
||||||
|
func diffMeta(oldMeta, newMeta PeerSystemMeta) MetaDiff {
|
||||||
|
var d MetaDiff
|
||||||
add := func(field string, oldVal, newVal any) {
|
add := func(field string, oldVal, newVal any) {
|
||||||
diff = append(diff, fmt.Sprintf("%s: %v -> %v", field, oldVal, newVal))
|
d.Changed = append(d.Changed, fmt.Sprintf("%s: %v -> %v", field, oldVal, newVal))
|
||||||
}
|
}
|
||||||
|
|
||||||
if oldMeta.Hostname != newMeta.Hostname {
|
if oldMeta.Hostname != newMeta.Hostname {
|
||||||
|
d.Hostname = true
|
||||||
add("hostname", oldMeta.Hostname, newMeta.Hostname)
|
add("hostname", oldMeta.Hostname, newMeta.Hostname)
|
||||||
}
|
}
|
||||||
if oldMeta.GoOS != newMeta.GoOS {
|
if oldMeta.GoOS != newMeta.GoOS {
|
||||||
|
d.GoOS = true
|
||||||
add("goos", oldMeta.GoOS, newMeta.GoOS)
|
add("goos", oldMeta.GoOS, newMeta.GoOS)
|
||||||
}
|
}
|
||||||
if oldMeta.Kernel != newMeta.Kernel {
|
if oldMeta.Kernel != newMeta.Kernel {
|
||||||
|
d.Kernel = true
|
||||||
add("kernel", oldMeta.Kernel, newMeta.Kernel)
|
add("kernel", oldMeta.Kernel, newMeta.Kernel)
|
||||||
}
|
}
|
||||||
if oldMeta.KernelVersion != newMeta.KernelVersion {
|
if oldMeta.KernelVersion != newMeta.KernelVersion {
|
||||||
|
d.KernelVersion = true
|
||||||
add("kernel_version", oldMeta.KernelVersion, newMeta.KernelVersion)
|
add("kernel_version", oldMeta.KernelVersion, newMeta.KernelVersion)
|
||||||
}
|
}
|
||||||
if oldMeta.Core != newMeta.Core {
|
if oldMeta.Core != newMeta.Core {
|
||||||
|
d.Core = true
|
||||||
add("core", oldMeta.Core, newMeta.Core)
|
add("core", oldMeta.Core, newMeta.Core)
|
||||||
}
|
}
|
||||||
if oldMeta.Platform != newMeta.Platform {
|
if oldMeta.Platform != newMeta.Platform {
|
||||||
|
d.Platform = true
|
||||||
add("platform", oldMeta.Platform, newMeta.Platform)
|
add("platform", oldMeta.Platform, newMeta.Platform)
|
||||||
}
|
}
|
||||||
if oldMeta.OS != newMeta.OS {
|
if oldMeta.OS != newMeta.OS {
|
||||||
|
d.OS = true
|
||||||
add("os", oldMeta.OS, newMeta.OS)
|
add("os", oldMeta.OS, newMeta.OS)
|
||||||
}
|
}
|
||||||
if oldMeta.OSVersion != newMeta.OSVersion {
|
if oldMeta.OSVersion != newMeta.OSVersion {
|
||||||
|
d.OSVersion = true
|
||||||
add("os_version", oldMeta.OSVersion, newMeta.OSVersion)
|
add("os_version", oldMeta.OSVersion, newMeta.OSVersion)
|
||||||
}
|
}
|
||||||
if oldMeta.WtVersion != newMeta.WtVersion {
|
if oldMeta.WtVersion != newMeta.WtVersion {
|
||||||
|
d.WtVersion = true
|
||||||
add("wt_version", oldMeta.WtVersion, newMeta.WtVersion)
|
add("wt_version", oldMeta.WtVersion, newMeta.WtVersion)
|
||||||
}
|
}
|
||||||
if oldMeta.UIVersion != newMeta.UIVersion {
|
if oldMeta.UIVersion != newMeta.UIVersion {
|
||||||
|
d.UIVersion = true
|
||||||
add("ui_version", oldMeta.UIVersion, newMeta.UIVersion)
|
add("ui_version", oldMeta.UIVersion, newMeta.UIVersion)
|
||||||
}
|
}
|
||||||
if oldMeta.SystemSerialNumber != newMeta.SystemSerialNumber {
|
if oldMeta.SystemSerialNumber != newMeta.SystemSerialNumber {
|
||||||
|
d.SystemSerialNumber = true
|
||||||
add("system_serial_number", oldMeta.SystemSerialNumber, newMeta.SystemSerialNumber)
|
add("system_serial_number", oldMeta.SystemSerialNumber, newMeta.SystemSerialNumber)
|
||||||
}
|
}
|
||||||
if oldMeta.SystemProductName != newMeta.SystemProductName {
|
if oldMeta.SystemProductName != newMeta.SystemProductName {
|
||||||
|
d.SystemProductName = true
|
||||||
add("system_product_name", oldMeta.SystemProductName, newMeta.SystemProductName)
|
add("system_product_name", oldMeta.SystemProductName, newMeta.SystemProductName)
|
||||||
}
|
}
|
||||||
if oldMeta.SystemManufacturer != newMeta.SystemManufacturer {
|
if oldMeta.SystemManufacturer != newMeta.SystemManufacturer {
|
||||||
|
d.SystemManufacturer = true
|
||||||
add("system_manufacturer", oldMeta.SystemManufacturer, newMeta.SystemManufacturer)
|
add("system_manufacturer", oldMeta.SystemManufacturer, newMeta.SystemManufacturer)
|
||||||
}
|
}
|
||||||
if oldMeta.Environment.Cloud != newMeta.Environment.Cloud {
|
if oldMeta.Environment.Cloud != newMeta.Environment.Cloud {
|
||||||
|
d.EnvironmentCloud = true
|
||||||
add("environment_cloud", oldMeta.Environment.Cloud, newMeta.Environment.Cloud)
|
add("environment_cloud", oldMeta.Environment.Cloud, newMeta.Environment.Cloud)
|
||||||
}
|
}
|
||||||
if oldMeta.Environment.Platform != newMeta.Environment.Platform {
|
if oldMeta.Environment.Platform != newMeta.Environment.Platform {
|
||||||
|
d.EnvironmentPlatform = true
|
||||||
add("environment_platform", oldMeta.Environment.Platform, newMeta.Environment.Platform)
|
add("environment_platform", oldMeta.Environment.Platform, newMeta.Environment.Platform)
|
||||||
}
|
}
|
||||||
if !oldMeta.Flags.isEqual(newMeta.Flags) {
|
if !oldMeta.Flags.isEqual(newMeta.Flags) {
|
||||||
|
d.Flags = true
|
||||||
add("flags", fmt.Sprintf("%+v", oldMeta.Flags), fmt.Sprintf("%+v", newMeta.Flags))
|
add("flags", fmt.Sprintf("%+v", oldMeta.Flags), fmt.Sprintf("%+v", newMeta.Flags))
|
||||||
}
|
}
|
||||||
if !capabilitiesEqual(oldMeta.Capabilities, newMeta.Capabilities) {
|
if !capabilitiesEqual(oldMeta.Capabilities, newMeta.Capabilities) {
|
||||||
|
d.Capabilities = true
|
||||||
add("capabilities", oldMeta.Capabilities, newMeta.Capabilities)
|
add("capabilities", oldMeta.Capabilities, newMeta.Capabilities)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !sameMultiset(oldMeta.NetworkAddresses, newMeta.NetworkAddresses) {
|
if !sameMultiset(oldMeta.NetworkAddresses, newMeta.NetworkAddresses) {
|
||||||
|
d.NetworkAddresses = true
|
||||||
add("network_addresses", fmt.Sprintf("%v", oldMeta.NetworkAddresses), fmt.Sprintf("%v", newMeta.NetworkAddresses))
|
add("network_addresses", fmt.Sprintf("%v", oldMeta.NetworkAddresses), fmt.Sprintf("%v", newMeta.NetworkAddresses))
|
||||||
}
|
}
|
||||||
|
|
||||||
if !sameMultiset(oldMeta.Files, newMeta.Files) {
|
if !sameMultiset(oldMeta.Files, newMeta.Files) {
|
||||||
|
d.Files = true
|
||||||
add("files", fmt.Sprintf("%v", oldMeta.Files), fmt.Sprintf("%v", newMeta.Files))
|
add("files", fmt.Sprintf("%v", oldMeta.Files), fmt.Sprintf("%v", newMeta.Files))
|
||||||
}
|
}
|
||||||
|
|
||||||
return diff
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
// sameMultiset reports whether two slices contain the same elements with the
|
// sameMultiset reports whether two slices contain the same elements with the
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
"github.com/hashicorp/go-version"
|
"github.com/hashicorp/go-version"
|
||||||
|
|
||||||
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
||||||
"github.com/netbirdio/netbird/shared/management/http/api"
|
"github.com/netbirdio/netbird/shared/management/http/api"
|
||||||
"github.com/netbirdio/netbird/shared/management/status"
|
"github.com/netbirdio/netbird/shared/management/status"
|
||||||
@@ -51,6 +52,34 @@ type Checks struct {
|
|||||||
Checks ChecksDefinition `gorm:"serializer:json"`
|
Checks ChecksDefinition `gorm:"serializer:json"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AffectsPosture reports whether the peer metadata changes described by diff can
|
||||||
|
// alter the outcome of any of the given posture checks. It maps each check kind to
|
||||||
|
// the metadata fields it inspects, so an unrelated change (e.g. a hostname update)
|
||||||
|
// does not force a posture re-evaluation.
|
||||||
|
func AffectsPosture(diff *nbpeer.MetaDiff, checks []*Checks) bool {
|
||||||
|
if diff == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, c := range checks {
|
||||||
|
if c.Checks.ProcessCheck != nil && diff.Files {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if c.Checks.OSVersionCheck != nil && (diff.OSVersion || diff.OS || diff.KernelVersion) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if c.Checks.NBVersionCheck != nil && diff.WtVersion {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if c.Checks.GeoLocationCheck != nil && diff.LocationChanged {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if c.Checks.PeerNetworkRangeCheck != nil && diff.NetworkAddresses {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// ChecksDefinition contains definition of actual check
|
// ChecksDefinition contains definition of actual check
|
||||||
type ChecksDefinition struct {
|
type ChecksDefinition struct {
|
||||||
NBVersionCheck *NBVersionCheck `json:",omitempty"`
|
NBVersionCheck *NBVersionCheck `json:",omitempty"`
|
||||||
|
|||||||
@@ -581,28 +581,6 @@ func (s *SqlStore) MarkPeerDisconnectedIfSameSession(ctx context.Context, accoun
|
|||||||
return result.RowsAffected > 0, nil
|
return result.RowsAffected > 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SqlStore) SavePeerLocation(ctx context.Context, accountID string, peerWithLocation *nbpeer.Peer) error {
|
|
||||||
// To maintain data integrity, we create a copy of the peer's location to prevent unintended updates to other fields.
|
|
||||||
var peerCopy nbpeer.Peer
|
|
||||||
// Since the location field has been migrated to JSON serialization,
|
|
||||||
// updating the struct ensures the correct data format is inserted into the database.
|
|
||||||
peerCopy.Location = peerWithLocation.Location
|
|
||||||
|
|
||||||
result := s.db.Model(&nbpeer.Peer{}).
|
|
||||||
Where(accountAndIDQueryCondition, accountID, peerWithLocation.ID).
|
|
||||||
Updates(peerCopy)
|
|
||||||
|
|
||||||
if result.Error != nil {
|
|
||||||
return status.Errorf(status.Internal, "failed to save peer locations to store: %v", result.Error)
|
|
||||||
}
|
|
||||||
|
|
||||||
if result.RowsAffected == 0 {
|
|
||||||
return status.Errorf(status.NotFound, peerNotFoundFMT, peerWithLocation.ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApproveAccountPeers marks all peers that currently require approval in the given account as approved.
|
// ApproveAccountPeers marks all peers that currently require approval in the given account as approved.
|
||||||
func (s *SqlStore) ApproveAccountPeers(ctx context.Context, accountID string) (int, error) {
|
func (s *SqlStore) ApproveAccountPeers(ctx context.Context, accountID string) (int, error) {
|
||||||
result := s.db.Model(&nbpeer.Peer{}).
|
result := s.db.Model(&nbpeer.Peer{}).
|
||||||
|
|||||||
@@ -618,56 +618,6 @@ func TestSqlStore_SavePeerStatus(t *testing.T) {
|
|||||||
assert.WithinDurationf(t, newStatus.LastSeen, actual.LastSeen.UTC(), time.Millisecond, "LastSeen should be equal")
|
assert.WithinDurationf(t, newStatus.LastSeen, actual.LastSeen.UTC(), time.Millisecond, "LastSeen should be equal")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSqlStore_SavePeerLocation(t *testing.T) {
|
|
||||||
store, cleanUp, err := NewTestStoreFromSQL(context.Background(), "../testdata/store.sql", t.TempDir())
|
|
||||||
t.Cleanup(cleanUp)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
account, err := store.GetAccount(context.Background(), "bf1c8084-ba50-4ce7-9439-34653001fc3b")
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
peer := &nbpeer.Peer{
|
|
||||||
AccountID: account.Id,
|
|
||||||
ID: "testpeer",
|
|
||||||
Location: nbpeer.Location{
|
|
||||||
ConnectionIP: net.ParseIP("0.0.0.0"),
|
|
||||||
CountryCode: "YY",
|
|
||||||
CityName: "City",
|
|
||||||
GeoNameID: 1,
|
|
||||||
},
|
|
||||||
CreatedAt: time.Now().UTC(),
|
|
||||||
Meta: nbpeer.PeerSystemMeta{},
|
|
||||||
}
|
|
||||||
// error is expected as peer is not in store yet
|
|
||||||
err = store.SavePeerLocation(context.Background(), account.Id, peer)
|
|
||||||
assert.Error(t, err)
|
|
||||||
|
|
||||||
account.Peers[peer.ID] = peer
|
|
||||||
err = store.SaveAccount(context.Background(), account)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
peer.Location.ConnectionIP = net.ParseIP("35.1.1.1")
|
|
||||||
peer.Location.CountryCode = "DE"
|
|
||||||
peer.Location.CityName = "Berlin"
|
|
||||||
peer.Location.GeoNameID = 2950159
|
|
||||||
|
|
||||||
err = store.SavePeerLocation(context.Background(), account.Id, account.Peers[peer.ID])
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
account, err = store.GetAccount(context.Background(), account.Id)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
actual := account.Peers[peer.ID].Location
|
|
||||||
assert.Equal(t, peer.Location, actual)
|
|
||||||
|
|
||||||
peer.ID = "non-existing-peer"
|
|
||||||
err = store.SavePeerLocation(context.Background(), account.Id, peer)
|
|
||||||
assert.Error(t, err)
|
|
||||||
parsedErr, ok := status.FromError(err)
|
|
||||||
require.True(t, ok)
|
|
||||||
require.Equal(t, status.NotFound, parsedErr.Type(), "should return not found error")
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_TestGetAccountByPrivateDomain(t *testing.T) {
|
func Test_TestGetAccountByPrivateDomain(t *testing.T) {
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
t.Skip("The SQLite store is not properly supported by Windows yet")
|
t.Skip("The SQLite store is not properly supported by Windows yet")
|
||||||
|
|||||||
@@ -185,7 +185,6 @@ type Store interface {
|
|||||||
// recorded by the database. Returns true when the update happened,
|
// recorded by the database. Returns true when the update happened,
|
||||||
// false when a newer session has taken over.
|
// false when a newer session has taken over.
|
||||||
MarkPeerDisconnectedIfSameSession(ctx context.Context, accountID, peerID string, sessionStartedAt int64) (bool, error)
|
MarkPeerDisconnectedIfSameSession(ctx context.Context, accountID, peerID string, sessionStartedAt int64) (bool, error)
|
||||||
SavePeerLocation(ctx context.Context, accountID string, peer *nbpeer.Peer) error
|
|
||||||
ApproveAccountPeers(ctx context.Context, accountID string) (int, error)
|
ApproveAccountPeers(ctx context.Context, accountID string) (int, error)
|
||||||
DeletePeer(ctx context.Context, accountID string, peerID string) error
|
DeletePeer(ctx context.Context, accountID string, peerID string) error
|
||||||
|
|
||||||
|
|||||||
@@ -2968,20 +2968,6 @@ func (mr *MockStoreMockRecorder) SavePeer(ctx, accountID, peer interface{}) *gom
|
|||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SavePeer", reflect.TypeOf((*MockStore)(nil).SavePeer), ctx, accountID, peer)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SavePeer", reflect.TypeOf((*MockStore)(nil).SavePeer), ctx, accountID, peer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SavePeerLocation mocks base method.
|
|
||||||
func (m *MockStore) SavePeerLocation(ctx context.Context, accountID string, peer *peer.Peer) error {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "SavePeerLocation", ctx, accountID, peer)
|
|
||||||
ret0, _ := ret[0].(error)
|
|
||||||
return ret0
|
|
||||||
}
|
|
||||||
|
|
||||||
// SavePeerLocation indicates an expected call of SavePeerLocation.
|
|
||||||
func (mr *MockStoreMockRecorder) SavePeerLocation(ctx, accountID, peer interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SavePeerLocation", reflect.TypeOf((*MockStore)(nil).SavePeerLocation), ctx, accountID, peer)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SavePeerStatus mocks base method.
|
// SavePeerStatus mocks base method.
|
||||||
func (m *MockStore) SavePeerStatus(ctx context.Context, accountID, peerID string, status peer.PeerStatus) error {
|
func (m *MockStore) SavePeerStatus(ctx context.Context, accountID, peerID string, status peer.PeerStatus) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
|
|||||||
@@ -12,6 +12,9 @@ type PeerSync struct {
|
|||||||
WireGuardPubKey string
|
WireGuardPubKey string
|
||||||
// Meta is the system information passed by peer, must be always present
|
// Meta is the system information passed by peer, must be always present
|
||||||
Meta nbpeer.PeerSystemMeta
|
Meta nbpeer.PeerSystemMeta
|
||||||
|
// RealIP is the peer's connection IP, used to refresh its geo location.
|
||||||
|
// May be nil when the request has no associated connection IP.
|
||||||
|
RealIP net.IP
|
||||||
// UpdateAccountPeers indicate updating account peers,
|
// UpdateAccountPeers indicate updating account peers,
|
||||||
// which occurs when the peer's metadata is updated
|
// which occurs when the peer's metadata is updated
|
||||||
UpdateAccountPeers bool
|
UpdateAccountPeers bool
|
||||||
|
|||||||
Reference in New Issue
Block a user