Skip to content

Commit 149559a

Browse files
authored
[management] login filter to fix multiple peers connected with the same pub key (#3986)
1 parent e14c6de commit 149559a

File tree

9 files changed

+568
-34
lines changed

9 files changed

+568
-34
lines changed

management/server/account.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ type DefaultAccountManager struct {
104104
accountUpdateLocks sync.Map
105105
updateAccountPeersBufferInterval atomic.Int64
106106

107+
loginFilter *loginFilter
108+
107109
disableDefaultPolicy bool
108110
}
109111

@@ -211,6 +213,7 @@ func BuildManager(
211213
proxyController: proxyController,
212214
settingsManager: settingsManager,
213215
permissionsManager: permissionsManager,
216+
loginFilter: newLoginFilter(),
214217
disableDefaultPolicy: disableDefaultPolicy,
215218
}
216219

@@ -1612,6 +1615,10 @@ func domainIsUpToDate(domain string, domainCategory string, userAuth nbcontext.U
16121615
return domainCategory == types.PrivateCategory || userAuth.DomainCategory != types.PrivateCategory || domain != userAuth.Domain
16131616
}
16141617

1618+
func (am *DefaultAccountManager) AllowSync(wgPubKey string, metahash uint64) bool {
1619+
return am.loginFilter.allowLogin(wgPubKey, metahash)
1620+
}
1621+
16151622
func (am *DefaultAccountManager) SyncAndMarkPeer(ctx context.Context, accountID string, peerPubKey string, meta nbpeer.PeerSystemMeta, realIP net.IP) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, error) {
16161623
start := time.Now()
16171624
defer func() {
@@ -1628,6 +1635,9 @@ func (am *DefaultAccountManager) SyncAndMarkPeer(ctx context.Context, accountID
16281635
log.WithContext(ctx).Warnf("failed marking peer as connected %s %v", peerPubKey, err)
16291636
}
16301637

1638+
metahash := metaHash(meta, realIP.String())
1639+
am.loginFilter.addLogin(peerPubKey, metahash)
1640+
16311641
return peer, netMap, postureChecks, nil
16321642
}
16331643

@@ -1636,7 +1646,6 @@ func (am *DefaultAccountManager) OnPeerDisconnected(ctx context.Context, account
16361646
if err != nil {
16371647
log.WithContext(ctx).Warnf("failed marking peer as disconnected %s %v", peerPubKey, err)
16381648
}
1639-
16401649
return nil
16411650
}
16421651

management/server/account/manager.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,4 +123,5 @@ type Manager interface {
123123
UpdateToPrimaryAccount(ctx context.Context, accountId string) error
124124
GetOwnerInfo(ctx context.Context, accountId string) (*types.UserInfo, error)
125125
GetCurrentUserInfo(ctx context.Context, userAuth nbcontext.UserAuth) (*users.UserInfoWithPermissions, error)
126+
AllowSync(string, uint64) bool
126127
}

management/server/grpcserver.go

Lines changed: 77 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ package server
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67
"net"
78
"net/netip"
9+
"os"
810
"strings"
911
"sync"
1012
"time"
@@ -38,20 +40,28 @@ import (
3840
internalStatus "github.com/netbirdio/netbird/shared/management/status"
3941
)
4042

43+
const (
44+
envLogBlockedPeers = "NB_LOG_BLOCKED_PEERS"
45+
envBlockPeers = "NB_BLOCK_SAME_PEERS"
46+
)
47+
4148
// GRPCServer an instance of a Management gRPC API server
4249
type GRPCServer struct {
4350
accountManager account.Manager
4451
settingsManager settings.Manager
4552
wgKey wgtypes.Key
4653
proto.UnimplementedManagementServiceServer
47-
peersUpdateManager *PeersUpdateManager
48-
config *nbconfig.Config
49-
secretsManager SecretsManager
50-
appMetrics telemetry.AppMetrics
51-
ephemeralManager *EphemeralManager
52-
peerLocks sync.Map
53-
authManager auth.Manager
54-
integratedPeerValidator integrated_validator.IntegratedValidator
54+
peersUpdateManager *PeersUpdateManager
55+
config *nbconfig.Config
56+
secretsManager SecretsManager
57+
appMetrics telemetry.AppMetrics
58+
ephemeralManager *EphemeralManager
59+
peerLocks sync.Map
60+
authManager auth.Manager
61+
62+
logBlockedPeers bool
63+
blockPeersWithSameConfig bool
64+
integratedPeerValidator integrated_validator.IntegratedValidator
5565
}
5666

5767
// NewServer creates a new Management server
@@ -82,18 +92,23 @@ func NewServer(
8292
}
8393
}
8494

95+
logBlockedPeers := strings.ToLower(os.Getenv(envLogBlockedPeers)) == "true"
96+
blockPeersWithSameConfig := strings.ToLower(os.Getenv(envBlockPeers)) == "true"
97+
8598
return &GRPCServer{
8699
wgKey: key,
87100
// peerKey -> event channel
88-
peersUpdateManager: peersUpdateManager,
89-
accountManager: accountManager,
90-
settingsManager: settingsManager,
91-
config: config,
92-
secretsManager: secretsManager,
93-
authManager: authManager,
94-
appMetrics: appMetrics,
95-
ephemeralManager: ephemeralManager,
96-
integratedPeerValidator: integratedPeerValidator,
101+
peersUpdateManager: peersUpdateManager,
102+
accountManager: accountManager,
103+
settingsManager: settingsManager,
104+
config: config,
105+
secretsManager: secretsManager,
106+
authManager: authManager,
107+
appMetrics: appMetrics,
108+
ephemeralManager: ephemeralManager,
109+
logBlockedPeers: logBlockedPeers,
110+
blockPeersWithSameConfig: blockPeersWithSameConfig,
111+
integratedPeerValidator: integratedPeerValidator,
97112
}, nil
98113
}
99114

@@ -136,9 +151,6 @@ func getRealIP(ctx context.Context) net.IP {
136151
// notifies the connected peer of any updates (e.g. new peers under the same account)
137152
func (s *GRPCServer) Sync(req *proto.EncryptedMessage, srv proto.ManagementService_SyncServer) error {
138153
reqStart := time.Now()
139-
if s.appMetrics != nil {
140-
s.appMetrics.GRPCMetrics().CountSyncRequest()
141-
}
142154

143155
ctx := srv.Context()
144156

@@ -147,6 +159,25 @@ func (s *GRPCServer) Sync(req *proto.EncryptedMessage, srv proto.ManagementServi
147159
if err != nil {
148160
return err
149161
}
162+
realIP := getRealIP(ctx)
163+
sRealIP := realIP.String()
164+
peerMeta := extractPeerMeta(ctx, syncReq.GetMeta())
165+
metahashed := metaHash(peerMeta, sRealIP)
166+
if !s.accountManager.AllowSync(peerKey.String(), metahashed) {
167+
if s.appMetrics != nil {
168+
s.appMetrics.GRPCMetrics().CountSyncRequestBlocked()
169+
}
170+
if s.logBlockedPeers {
171+
log.WithContext(ctx).Warnf("peer %s with meta hash %d is blocked from syncing", peerKey.String(), metahashed)
172+
}
173+
if s.blockPeersWithSameConfig {
174+
return mapError(ctx, internalStatus.ErrPeerAlreadyLoggedIn)
175+
}
176+
}
177+
178+
if s.appMetrics != nil {
179+
s.appMetrics.GRPCMetrics().CountSyncRequest()
180+
}
150181

151182
// nolint:staticcheck
152183
ctx = context.WithValue(ctx, nbContext.PeerIDKey, peerKey.String())
@@ -172,14 +203,13 @@ func (s *GRPCServer) Sync(req *proto.EncryptedMessage, srv proto.ManagementServi
172203
// nolint:staticcheck
173204
ctx = context.WithValue(ctx, nbContext.AccountIDKey, accountID)
174205

175-
realIP := getRealIP(ctx)
176-
log.WithContext(ctx).Debugf("Sync request from peer [%s] [%s]", req.WgPubKey, realIP.String())
206+
log.WithContext(ctx).Debugf("Sync request from peer [%s] [%s]", req.WgPubKey, sRealIP)
177207

178208
if syncReq.GetMeta() == nil {
179209
log.WithContext(ctx).Tracef("peer system meta has to be provided on sync. Peer %s, remote addr %s", peerKey.String(), realIP)
180210
}
181211

182-
peer, netMap, postureChecks, err := s.accountManager.SyncAndMarkPeer(ctx, accountID, peerKey.String(), extractPeerMeta(ctx, syncReq.GetMeta()), realIP)
212+
peer, netMap, postureChecks, err := s.accountManager.SyncAndMarkPeer(ctx, accountID, peerKey.String(), peerMeta, realIP)
183213
if err != nil {
184214
log.WithContext(ctx).Debugf("error while syncing peer %s: %v", peerKey.String(), err)
185215
return mapError(ctx, err)
@@ -345,6 +375,9 @@ func mapError(ctx context.Context, err error) error {
345375
default:
346376
}
347377
}
378+
if errors.Is(err, internalStatus.ErrPeerAlreadyLoggedIn) {
379+
return status.Error(codes.PermissionDenied, internalStatus.ErrPeerAlreadyLoggedIn.Error())
380+
}
348381
log.WithContext(ctx).Errorf("got an unhandled error: %s", err)
349382
return status.Errorf(codes.Internal, "failed handling request")
350383
}
@@ -436,19 +469,34 @@ func (s *GRPCServer) parseRequest(ctx context.Context, req *proto.EncryptedMessa
436469
// In case of the successful registration login is also successful
437470
func (s *GRPCServer) Login(ctx context.Context, req *proto.EncryptedMessage) (*proto.EncryptedMessage, error) {
438471
reqStart := time.Now()
439-
440-
if s.appMetrics != nil {
441-
s.appMetrics.GRPCMetrics().CountLoginRequest()
442-
}
443472
realIP := getRealIP(ctx)
444-
log.WithContext(ctx).Debugf("Login request from peer [%s] [%s]", req.WgPubKey, realIP.String())
473+
sRealIP := realIP.String()
474+
log.WithContext(ctx).Debugf("Login request from peer [%s] [%s]", req.WgPubKey, sRealIP)
445475

446476
loginReq := &proto.LoginRequest{}
447477
peerKey, err := s.parseRequest(ctx, req, loginReq)
448478
if err != nil {
449479
return nil, err
450480
}
451481

482+
peerMeta := extractPeerMeta(ctx, loginReq.GetMeta())
483+
metahashed := metaHash(peerMeta, sRealIP)
484+
if !s.accountManager.AllowSync(peerKey.String(), metahashed) {
485+
if s.logBlockedPeers {
486+
log.WithContext(ctx).Warnf("peer %s with meta hash %d is blocked from login", peerKey.String(), metahashed)
487+
}
488+
if s.appMetrics != nil {
489+
s.appMetrics.GRPCMetrics().CountLoginRequestBlocked()
490+
}
491+
if s.blockPeersWithSameConfig {
492+
return nil, internalStatus.ErrPeerAlreadyLoggedIn
493+
}
494+
}
495+
496+
if s.appMetrics != nil {
497+
s.appMetrics.GRPCMetrics().CountLoginRequest()
498+
}
499+
452500
//nolint
453501
ctx = context.WithValue(ctx, nbContext.PeerIDKey, peerKey.String())
454502
accountID, err := s.accountManager.GetAccountIDForPeerKey(ctx, peerKey.String())
@@ -485,7 +533,7 @@ func (s *GRPCServer) Login(ctx context.Context, req *proto.EncryptedMessage) (*p
485533
peer, netMap, postureChecks, err := s.accountManager.LoginPeer(ctx, types.PeerLogin{
486534
WireGuardPubKey: peerKey.String(),
487535
SSHKey: string(sshKey),
488-
Meta: extractPeerMeta(ctx, loginReq.GetMeta()),
536+
Meta: peerMeta,
489537
UserID: userID,
490538
SetupKey: loginReq.GetSetupKey(),
491539
ConnectionIP: realIP,

0 commit comments

Comments
 (0)