Compare commits

...

19 Commits

Author SHA1 Message Date
crn4
c7ababbf45 Merge branch 'main' into test/multiple-peer-logging 2025-06-18 19:04:47 +02:00
crn4
e993b633bd Merge branch 'main' into test/multiple-peer-logging 2025-06-18 18:35:11 +02:00
crn4
6aaec1002d minor fix 2025-06-18 18:22:58 +02:00
crn4
ebf3d26c91 another log and different approach for meta calc 2025-06-18 18:16:45 +02:00
crn4
9dc9402deb Merge branch 'main' into test/multiple-peer-logging 2025-06-17 19:21:05 +02:00
crn4
41a9e45c68 add metahash to sync log 2025-06-17 18:56:59 +02:00
crn4
641891e931 minor fix - logs for incrementing number of attempts 2025-06-17 18:05:52 +02:00
crn4
c43ddddcdb added delimeter to hash 2025-06-17 17:56:06 +02:00
crn4
0a9d09267a minor fix - removed errors 2025-06-17 17:53:44 +02:00
crn4
05733b00c1 test logging to asses number of peers with the same pub key 2025-06-17 17:50:09 +02:00
crn4
0a5f751343 removed unused test 2025-06-16 18:10:59 +02:00
crn4
b2a7a4c6d4 unrecoverable error for login 2025-06-16 17:48:13 +02:00
crn4
cfdaa82fea login filter check on login 2025-06-16 15:21:00 +02:00
crn4
c332ff0a47 fixed error wrap 2025-06-16 13:31:01 +02:00
crn4
6cd77cc17c minor fix 2025-06-16 13:16:52 +02:00
crn4
19835dc6d5 change grpc code to permission denied 2025-06-16 13:10:38 +02:00
crn4
3cd21cc7e5 Merge branch 'main' into fix/login-filter 2025-06-16 09:41:48 +02:00
crn4
4619d39e17 updated approach to filtering - through sync 2025-06-16 09:40:25 +02:00
crn4
5b09804a17 added login filter to filter different peers with the same pub key 2025-06-12 15:56:32 +01:00
7 changed files with 136 additions and 4 deletions

View File

@@ -102,6 +102,8 @@ type DefaultAccountManager struct {
accountUpdateLocks sync.Map
updateAccountPeersBufferInterval atomic.Int64
loginFilter *loginFilter
}
// getJWTGroupsChanges calculates the changes needed to sync a user's JWT groups.
@@ -195,6 +197,7 @@ func BuildManager(
proxyController: proxyController,
settingsManager: settingsManager,
permissionsManager: permissionsManager,
loginFilter: newLoginFilter(),
}
am.startWarmup(ctx)
@@ -1536,6 +1539,10 @@ func domainIsUpToDate(domain string, domainCategory string, userAuth nbcontext.U
return domainCategory == types.PrivateCategory || userAuth.DomainCategory != types.PrivateCategory || domain != userAuth.Domain
}
func (am *DefaultAccountManager) AllowSync(wgPubKey, metahash string) bool {
return true
}
func (am *DefaultAccountManager) SyncAndMarkPeer(ctx context.Context, accountID string, peerPubKey string, meta nbpeer.PeerSystemMeta, realIP net.IP) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, error) {
start := time.Now()
defer func() {
@@ -1557,6 +1564,9 @@ func (am *DefaultAccountManager) SyncAndMarkPeer(ctx context.Context, accountID
log.WithContext(ctx).Warnf("failed marking peer as connected %s %v", peerPubKey, err)
}
metahash := metaHash(meta, realIP.String())
am.loginFilter.addLogin(peerPubKey, metahash)
return peer, netMap, postureChecks, nil
}
@@ -1570,7 +1580,6 @@ func (am *DefaultAccountManager) OnPeerDisconnected(ctx context.Context, account
if err != nil {
log.WithContext(ctx).Warnf("failed marking peer as disconnected %s %v", peerPubKey, err)
}
return nil
}

View File

@@ -117,4 +117,5 @@ type Manager interface {
UpdateToPrimaryAccount(ctx context.Context, accountId string) (*types.Account, error)
GetOwnerInfo(ctx context.Context, accountId string) (*types.UserInfo, error)
GetCurrentUserInfo(ctx context.Context, userAuth nbcontext.UserAuth) (*users.UserInfoWithPermissions, error)
AllowSync(string, string) bool
}

View File

@@ -166,7 +166,8 @@ func (s *GRPCServer) Sync(req *proto.EncryptedMessage, srv proto.ManagementServi
ctx = context.WithValue(ctx, nbContext.AccountIDKey, accountID)
realIP := getRealIP(ctx)
log.WithContext(ctx).Debugf("Sync request from peer [%s] [%s]", req.WgPubKey, realIP.String())
sip := realIP.String()
log.WithContext(ctx).Debugf("Sync request from peer [%s] [%s] [%s]", req.WgPubKey, sip, metaHash(extractPeerMeta(ctx, syncReq.GetMeta()), sip))
if syncReq.GetMeta() == nil {
log.WithContext(ctx).Tracef("peer system meta has to be provided on sync. Peer %s, remote addr %s", peerKey.String(), realIP)

View File

@@ -0,0 +1,113 @@
package server
import (
"strings"
"sync"
"time"
log "github.com/sirupsen/logrus"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
)
const (
loginFilterSize = 100_000 // Size of the login filter map, making it large enough for a future
filterTimeout = 5 * time.Minute // Duration to secure the previous login information in the filter
loggingLimit = 100
loggingLimitOnePeer = 30
loggingTresholdOnePeer = 5 * time.Minute
)
type loginFilter struct {
mu sync.RWMutex
logged map[string]metahash
}
type metahash struct {
hashes map[string]struct{}
counter int
start time.Time
}
func newLoginFilter() *loginFilter {
return &loginFilter{
logged: make(map[string]metahash, loginFilterSize),
}
}
func (l *loginFilter) addLogin(wgPubKey, metaHash string) {
l.mu.Lock()
defer l.mu.Unlock()
mh, ok := l.logged[wgPubKey]
if !ok {
mh = metahash{
hashes: make(map[string]struct{}, loggingLimit),
start: time.Now(),
}
}
mh.hashes[metaHash] = struct{}{}
mh.counter++
if mh.counter >= loggingLimit && mh.counter%loggingLimit == 0 && len(mh.hashes) > 1 {
log.WithFields(log.Fields{
"wgPubKey": wgPubKey,
"number of different hashes": len(mh.hashes),
"elapsed time for number of attempts": time.Since(mh.start),
"number of syncs": mh.counter,
}).Info(mh.prepareHashes())
} else if mh.counter%loggingLimitOnePeer == 0 && time.Since(mh.start) > loggingTresholdOnePeer && len(mh.hashes) == 1 {
log.WithFields(log.Fields{
"wgPubKey": wgPubKey,
"elapsed time for number of attempts": time.Since(mh.start),
"number of syncs": mh.counter,
}).Info(mh.prepareHashes())
mh.start = time.Now()
}
l.logged[wgPubKey] = mh
}
func (m *metahash) prepareHashes() string {
var sb strings.Builder
for hash := range m.hashes {
sb.WriteString(hash)
sb.WriteString(", ")
}
return sb.String()
}
func metaHash(meta nbpeer.PeerSystemMeta, pubip string) string {
mac := getMacAddress(meta.NetworkAddresses)
estimatedSize := len(meta.WtVersion) + len(meta.OSVersion) + len(meta.KernelVersion) + len(meta.Hostname) + len(meta.SystemSerialNumber) +
len(pubip) + len(mac) + 6
var b strings.Builder
b.Grow(estimatedSize)
b.WriteString(meta.WtVersion)
b.WriteByte('|')
b.WriteString(meta.OSVersion)
b.WriteByte('|')
b.WriteString(meta.KernelVersion)
b.WriteByte('|')
b.WriteString(meta.Hostname)
b.WriteByte('|')
b.WriteString(meta.SystemSerialNumber)
b.WriteByte('|')
b.WriteString(pubip)
b.WriteByte('|')
b.WriteString(mac)
return b.String()
}
func getMacAddress(nas []nbpeer.NetworkAddress) string {
if len(nas) == 0 {
return ""
}
macs := make([]string, 0, len(nas))
for _, na := range nas {
macs = append(macs, na.Mac)
}
return strings.Join(macs, "/")
}

View File

@@ -119,6 +119,8 @@ type MockAccountManager struct {
GetAccountMetaFunc func(ctx context.Context, accountID, userID string) (*types.AccountMeta, error)
GetOrCreateAccountByPrivateDomainFunc func(ctx context.Context, initiatorId, domain string) (*types.Account, bool, error)
AllowSyncFunc func(string, string) bool
}
func (am *MockAccountManager) UpdateAccountPeers(ctx context.Context, accountID string) {
@@ -890,3 +892,7 @@ func (am *MockAccountManager) GetCurrentUserInfo(ctx context.Context, userAuth n
}
return nil, status.Errorf(codes.Unimplemented, "method GetCurrentUserInfo is not implemented")
}
func (am *MockAccountManager) AllowSync(_, _ string) bool {
return true
}

View File

@@ -1579,7 +1579,6 @@ func Test_LoginPeer(t *testing.T) {
testCases := []struct {
name string
setupKey string
wireGuardPubKey string
expectExtraDNSLabelsMismatch bool
extraDNSLabels []string
expectLoginError bool

View File

@@ -42,7 +42,10 @@ const (
// Type is a type of the Error
type Type int32
var ErrExtraSettingsNotFound = fmt.Errorf("extra settings not found")
var (
ErrExtraSettingsNotFound = fmt.Errorf("extra settings not found")
ErrPeerAlreadyLoggedIn = errors.New("peer with the same public key is already logged in")
)
// Error is an internal error
type Error struct {