2021-08-23 21:43:05 +02:00
package server
import (
2024-07-03 11:33:02 +02:00
"context"
2024-10-28 17:52:23 +01:00
"crypto/sha256"
b64 "encoding/base64"
2023-01-02 15:11:32 +01:00
"fmt"
2024-02-20 09:59:56 +01:00
"net"
2024-10-23 12:05:02 +02:00
"slices"
2021-08-23 21:43:05 +02:00
"strings"
2024-08-07 10:52:31 +02:00
"sync"
2021-08-23 21:43:05 +02:00
"time"
2022-05-21 15:21:39 +02:00
2024-11-18 13:06:25 +01:00
"github.com/netbirdio/netbird/management/server/geolocation"
2024-11-14 17:33:57 +01:00
nbgroup "github.com/netbirdio/netbird/management/server/group"
2023-04-03 15:09:35 +02:00
"github.com/rs/xid"
2024-03-27 18:48:48 +01:00
log "github.com/sirupsen/logrus"
2024-11-14 17:33:57 +01:00
"golang.org/x/exp/maps"
2023-04-03 15:09:35 +02:00
2024-09-16 15:47:03 +02:00
"github.com/netbirdio/netbird/management/server/idp"
2024-07-26 07:49:05 +02:00
"github.com/netbirdio/netbird/management/server/posture"
2024-03-27 18:48:48 +01:00
"github.com/netbirdio/netbird/management/proto"
2023-03-13 15:14:18 +01:00
"github.com/netbirdio/netbird/management/server/activity"
2023-11-28 13:45:26 +01:00
nbpeer "github.com/netbirdio/netbird/management/server/peer"
2023-03-13 15:14:18 +01:00
"github.com/netbirdio/netbird/management/server/status"
2021-08-23 21:43:05 +02:00
)
2023-03-03 18:35:38 +01:00
// 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
2024-06-13 13:24:24 +02:00
// 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
2023-03-03 18:35:38 +01:00
}
// 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.
2023-11-28 13:45:26 +01:00
Meta nbpeer . PeerSystemMeta
2023-03-03 18:35:38 +01:00
// 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
2024-03-27 18:48:48 +01:00
// ConnectionIP is the real IP of the peer
ConnectionIP net . IP
2023-03-03 18:35:38 +01:00
}
2022-11-05 10:24:50 +01:00
// 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.
2024-07-03 11:33:02 +02:00
func ( am * DefaultAccountManager ) GetPeers ( ctx context . Context , accountID , userID string ) ( [ ] * nbpeer . Peer , error ) {
2024-11-14 17:33:57 +01:00
user , err := am . Store . GetUserByUserID ( ctx , LockingStrengthShare , userID )
2022-11-05 10:24:50 +01:00
if err != nil {
return nil , err
}
2024-11-14 17:33:57 +01:00
if user . AccountID != accountID {
return nil , status . NewUserNotPartOfAccountError ( )
}
settings , err := am . Store . GetAccountSettings ( ctx , LockingStrengthShare , accountID )
2022-11-05 10:24:50 +01:00
if err != nil {
return nil , err
}
2024-11-14 17:33:57 +01:00
if user . IsRegularUser ( ) && settings . RegularUsersViewBlocked {
return [ ] * nbpeer . Peer { } , nil
}
accountPeers , err := am . Store . GetAccountPeers ( ctx , LockingStrengthShare , accountID )
2024-03-27 18:48:48 +01:00
if err != nil {
return nil , err
}
2024-11-14 17:33:57 +01:00
2023-11-28 13:45:26 +01:00
peers := make ( [ ] * nbpeer . Peer , 0 )
peersMap := make ( map [ string ] * nbpeer . Peer )
2024-03-27 16:11:45 +01:00
2024-11-14 17:33:57 +01:00
for _ , peer := range accountPeers {
if user . IsRegularUser ( ) && user . Id != peer . UserID {
2022-11-05 10:24:50 +01:00
// only display peers that belong to the current user if the current user is not an admin
continue
}
2024-11-14 17:33:57 +01:00
peers = append ( peers , peer )
peersMap [ peer . ID ] = peer
2022-11-21 17:45:14 +01:00
}
2024-11-14 17:33:57 +01:00
if user . IsAdminOrServiceUser ( ) {
2024-08-07 10:22:12 +02:00
return peers , nil
}
2024-11-14 17:33:57 +01:00
account , err := am . requestBuffer . GetAccountWithBackpressure ( ctx , accountID )
if err != nil {
return nil , err
}
approvedPeersMap , err := am . GetValidatedPeers ( ctx , accountID )
if err != nil {
return nil , err
}
2022-11-21 17:45:14 +01:00
// fetch all the peers that have access to the user's peers
for _ , peer := range peers {
2024-07-03 11:33:02 +02:00
aclPeers , _ := account . getPeerConnectionResources ( ctx , peer . ID , approvedPeersMap )
2022-11-21 17:45:14 +01:00
for _ , p := range aclPeers {
2023-02-03 10:33:28 +01:00
peersMap [ p . ID ] = p
2022-11-21 17:45:14 +01:00
}
}
2024-11-14 17:33:57 +01:00
return maps . Values ( peersMap ) , nil
2022-11-05 10:24:50 +01:00
}
2022-05-21 15:21:39 +02:00
// MarkPeerConnected marks peer as connected (true) or disconnected (false)
2024-11-14 17:33:57 +01:00
func ( am * DefaultAccountManager ) MarkPeerConnected ( ctx context . Context , peerPubKey string , connected bool , realIP net . IP , accountID string ) error {
2024-11-18 13:06:25 +01:00
var peer * nbpeer . Peer
var settings * Settings
var expired bool
var err error
2021-08-24 11:50:19 +02:00
2024-11-18 13:06:25 +01:00
err = am . Store . ExecuteInTransaction ( ctx , func ( transaction Store ) error {
peer , err = transaction . GetPeerByPeerPubKey ( ctx , LockingStrengthUpdate , peerPubKey )
if err != nil {
return err
}
settings , err = transaction . GetAccountSettings ( ctx , LockingStrengthShare , accountID )
if err != nil {
return err
}
2024-10-13 14:52:43 +02:00
2024-11-18 13:06:25 +01:00
expired , err = updatePeerStatusAndLocation ( ctx , am . geo , transaction , peer , connected , realIP , accountID )
return err
} )
2024-11-14 17:33:57 +01:00
if err != nil {
return err
}
2024-11-12 14:19:22 +01:00
2024-10-13 14:52:43 +02:00
if peer . AddedWithSSOLogin ( ) {
2024-11-14 17:33:57 +01:00
if peer . LoginExpirationEnabled && settings . PeerLoginExpirationEnabled {
am . checkAndSchedulePeerLoginExpiration ( ctx , accountID )
2024-10-13 14:52:43 +02:00
}
2024-11-14 17:33:57 +01:00
if peer . InactivityExpirationEnabled && settings . PeerInactivityExpirationEnabled {
am . checkAndSchedulePeerInactivityExpiration ( ctx , accountID )
2024-10-13 14:52:43 +02:00
}
}
if expired {
// we need to update other peers because when peer login expires all other peers are notified to disconnect from
// the expired one. Here we notify them that connection is now allowed again.
2024-11-14 17:33:57 +01:00
am . updateAccountPeers ( ctx , accountID )
2024-10-13 14:52:43 +02:00
}
return nil
}
2024-11-18 13:06:25 +01:00
func updatePeerStatusAndLocation ( ctx context . Context , geo * geolocation . Geolocation , transaction Store , peer * nbpeer . Peer , connected bool , realIP net . IP , accountID string ) ( bool , error ) {
2023-02-27 16:44:26 +01:00
oldStatus := peer . Status . Copy ( )
newStatus := oldStatus
2023-04-03 15:09:35 +02:00
newStatus . LastSeen = time . Now ( ) . UTC ( )
2022-11-08 10:46:12 +01:00
newStatus . Connected = connected
2023-02-15 11:27:22 +01:00
// whenever peer got connected that means that it logged in successfully
if newStatus . Connected {
newStatus . LoginExpired = false
}
2022-11-08 10:46:12 +01:00
peer . Status = newStatus
2024-02-20 09:59:56 +01:00
2024-11-18 13:06:25 +01:00
if geo != nil && realIP != nil {
location , err := geo . Lookup ( realIP )
2024-02-20 09:59:56 +01:00
if err != nil {
2024-07-03 11:33:02 +02:00
log . WithContext ( ctx ) . Warnf ( "failed to get location for peer %s realip: [%s]: %v" , peer . ID , realIP . String ( ) , err )
2024-02-20 09:59:56 +01:00
} else {
peer . Location . ConnectionIP = realIP
peer . Location . CountryCode = location . Country . ISOCode
peer . Location . CityName = location . City . Names . En
peer . Location . GeoNameID = location . City . GeonameID
2024-11-18 13:06:25 +01:00
err = transaction . SavePeerLocation ( ctx , LockingStrengthUpdate , accountID , peer )
2024-02-20 09:59:56 +01:00
if err != nil {
2024-07-03 11:33:02 +02:00
log . WithContext ( ctx ) . Warnf ( "could not store location for peer %s: %s" , peer . ID , err )
2024-02-20 09:59:56 +01:00
}
}
}
2024-11-18 13:06:25 +01:00
err := transaction . SavePeerStatus ( ctx , LockingStrengthUpdate , accountID , peer . ID , * newStatus )
2021-08-24 11:50:19 +02:00
if err != nil {
2024-11-14 17:33:57 +01:00
return false , err
2021-08-24 11:50:19 +02:00
}
2023-02-27 16:44:26 +01:00
2024-10-13 14:52:43 +02:00
return oldStatus . LoginExpired , nil
2021-08-24 11:50:19 +02:00
}
2024-10-13 14:52:43 +02:00
// UpdatePeer updates peer. Only Peer.Name, Peer.SSHEnabled, Peer.LoginExpirationEnabled and Peer.InactivityExpirationEnabled can be updated.
2024-07-03 11:33:02 +02:00
func ( am * DefaultAccountManager ) UpdatePeer ( ctx context . Context , accountID , userID string , update * nbpeer . Peer ) ( * nbpeer . Peer , error ) {
2024-07-31 14:53:32 +02:00
unlock := am . Store . AcquireWriteLockByUID ( ctx , accountID )
2022-11-07 17:52:23 +01:00
defer unlock ( )
2022-06-23 17:04:53 +02:00
2024-11-14 17:33:57 +01:00
user , err := am . Store . GetUserByUserID ( ctx , LockingStrengthShare , userID )
2022-06-23 17:04:53 +02:00
if err != nil {
2022-11-11 20:36:45 +01:00
return nil , err
2022-06-23 17:04:53 +02:00
}
2024-11-14 17:33:57 +01:00
if user . AccountID != accountID {
return nil , status . NewUserNotPartOfAccountError ( )
}
2024-11-18 13:06:25 +01:00
var peer * nbpeer . Peer
var settings * Settings
var peerGroupList [ ] string
var requiresPeerUpdates bool
2024-11-18 14:43:09 +01:00
var peerLabelChanged bool
var sshChanged bool
var loginExpirationChanged bool
var inactivityExpirationChanged bool
2024-11-14 17:33:57 +01:00
2024-11-18 13:06:25 +01:00
err = am . Store . ExecuteInTransaction ( ctx , func ( transaction Store ) error {
peer , err = transaction . GetPeerByID ( ctx , LockingStrengthUpdate , accountID , update . ID )
if err != nil {
return err
}
2024-11-14 17:33:57 +01:00
2024-11-18 13:06:25 +01:00
settings , err = transaction . GetAccountSettings ( ctx , LockingStrengthShare , accountID )
if err != nil {
return err
}
2022-06-23 17:04:53 +02:00
2024-11-18 14:43:09 +01:00
peerGroupList , err = getPeerGroupIDs ( ctx , transaction , accountID , update . ID )
2024-11-18 13:06:25 +01:00
if err != nil {
return err
}
update , requiresPeerUpdates , err = am . integratedPeerValidator . ValidatePeer ( ctx , update , peer , userID , accountID , am . GetDNSDomain ( ) , peerGroupList , settings . Extra )
if err != nil {
return err
}
if peer . Name != update . Name {
existingLabels , err := getPeerDNSLabels ( ctx , transaction , accountID )
if err != nil {
return err
}
2024-11-18 14:43:09 +01:00
newLabel , err := getPeerHostLabel ( update . Name , existingLabels )
2024-11-18 13:06:25 +01:00
if err != nil {
return err
}
2024-11-18 14:43:09 +01:00
peer . Name = update . Name
2024-11-18 13:06:25 +01:00
peer . DNSLabel = newLabel
2024-11-18 14:43:09 +01:00
peerLabelChanged = true
2024-11-18 13:06:25 +01:00
}
2024-11-18 14:43:09 +01:00
if peer . SSHEnabled != update . SSHEnabled {
peer . SSHEnabled = update . SSHEnabled
sshChanged = true
}
2022-11-07 15:38:21 +01:00
2024-11-18 14:43:09 +01:00
if peer . LoginExpirationEnabled != update . LoginExpirationEnabled {
if ! peer . AddedWithSSOLogin ( ) {
return status . Errorf ( status . PreconditionFailed , "this peer hasn't been added with the SSO login, therefore the login expiration can't be updated" )
}
peer . LoginExpirationEnabled = update . LoginExpirationEnabled
loginExpirationChanged = true
2023-02-16 13:03:53 +01:00
}
2023-02-14 10:14:00 +01:00
2024-11-18 14:43:09 +01:00
if peer . InactivityExpirationEnabled != update . InactivityExpirationEnabled {
if ! peer . AddedWithSSOLogin ( ) {
return status . Errorf ( status . PreconditionFailed , "this peer hasn't been added with the SSO login, therefore the inactivity expiration can't be updated" )
}
peer . InactivityExpirationEnabled = update . InactivityExpirationEnabled
inactivityExpirationChanged = true
2024-10-13 14:52:43 +02:00
}
2024-11-18 14:43:09 +01:00
return transaction . SavePeer ( ctx , LockingStrengthUpdate , accountID , peer )
} )
if err != nil {
return nil , err
2024-10-13 14:52:43 +02:00
}
2024-11-14 17:33:57 +01:00
if sshChanged {
event := activity . PeerSSHEnabled
if ! peer . SSHEnabled {
event = activity . PeerSSHDisabled
}
am . StoreEvent ( ctx , userID , peer . IP . String ( ) , accountID , event , peer . EventMeta ( am . GetDNSDomain ( ) ) )
2024-10-23 12:05:02 +02:00
}
2021-08-23 21:43:05 +02:00
2024-11-14 17:33:57 +01:00
if peerLabelChanged {
am . StoreEvent ( ctx , userID , peer . ID , accountID , activity . PeerRenamed , peer . EventMeta ( am . GetDNSDomain ( ) ) )
}
2021-09-07 18:36:46 +02:00
2024-11-14 17:33:57 +01:00
if loginExpirationChanged {
event := activity . PeerLoginExpirationEnabled
if ! peer . LoginExpirationEnabled {
event = activity . PeerLoginExpirationDisabled
}
am . StoreEvent ( ctx , userID , peer . IP . String ( ) , accountID , event , peer . EventMeta ( am . GetDNSDomain ( ) ) )
2022-01-14 14:34:27 +01:00
2024-11-14 17:33:57 +01:00
if peer . AddedWithSSOLogin ( ) && peer . LoginExpirationEnabled && settings . PeerLoginExpirationEnabled {
am . checkAndSchedulePeerLoginExpiration ( ctx , accountID )
2023-10-01 19:51:39 +02:00
}
2023-10-03 16:46:58 +02:00
}
2021-09-07 18:36:46 +02:00
2024-11-14 17:33:57 +01:00
if inactivityExpirationChanged {
event := activity . PeerInactivityExpirationEnabled
if ! peer . InactivityExpirationEnabled {
event = activity . PeerInactivityExpirationDisabled
}
am . StoreEvent ( ctx , userID , peer . IP . String ( ) , accountID , event , peer . EventMeta ( am . GetDNSDomain ( ) ) )
2024-03-27 18:48:48 +01:00
2024-11-14 17:33:57 +01:00
if peer . AddedWithSSOLogin ( ) && peer . InactivityExpirationEnabled && settings . PeerInactivityExpirationEnabled {
am . checkAndSchedulePeerInactivityExpiration ( ctx , accountID )
2024-03-27 18:48:48 +01:00
}
2024-11-14 17:33:57 +01:00
}
2024-03-27 18:48:48 +01:00
2024-11-14 17:33:57 +01:00
if peerLabelChanged || requiresPeerUpdates {
am . updateAccountPeers ( ctx , accountID )
2023-10-01 19:51:39 +02:00
}
2024-11-14 17:33:57 +01:00
return peer , nil
2023-10-01 19:51:39 +02:00
}
// DeletePeer removes peer from the account by its IP
2024-07-03 11:33:02 +02:00
func ( am * DefaultAccountManager ) DeletePeer ( ctx context . Context , accountID , peerID , userID string ) error {
2024-07-31 14:53:32 +02:00
unlock := am . Store . AcquireWriteLockByUID ( ctx , accountID )
2023-10-01 19:51:39 +02:00
defer unlock ( )
2024-11-18 13:06:25 +01:00
peerAccountID , err := am . Store . GetAccountIDByPeerID ( ctx , LockingStrengthShare , peerID )
2023-10-01 19:51:39 +02:00
if err != nil {
return err
2021-09-07 18:36:46 +02:00
}
2024-11-18 13:06:25 +01:00
if peerAccountID != accountID {
return status . NewPeerNotPartOfAccountError ( )
}
2024-11-14 17:33:57 +01:00
var peer * nbpeer . Peer
2024-11-18 13:06:25 +01:00
var updateAccountPeers bool
var eventsToStore [ ] func ( )
2024-10-23 12:05:02 +02:00
2024-11-14 17:33:57 +01:00
err = am . Store . ExecuteInTransaction ( ctx , func ( transaction Store ) error {
peer , err = transaction . GetPeerByID ( ctx , LockingStrengthUpdate , accountID , peerID )
if err != nil {
return err
}
2023-10-03 16:46:58 +02:00
2024-11-18 13:06:25 +01:00
updateAccountPeers , err = isPeerInActiveGroup ( ctx , transaction , accountID , peerID )
2024-11-14 17:33:57 +01:00
if err != nil {
return err
}
2024-11-18 13:06:25 +01:00
if err = transaction . IncrementNetworkSerial ( ctx , LockingStrengthUpdate , accountID ) ; err != nil {
return err
}
eventsToStore , err = deletePeers ( ctx , am , transaction , accountID , userID , [ ] * nbpeer . Peer { peer } )
return err
2024-11-14 17:33:57 +01:00
} )
2024-11-18 13:06:25 +01:00
for _ , storeEvent := range eventsToStore {
storeEvent ( )
2023-10-03 16:46:58 +02:00
}
2024-10-23 12:05:02 +02:00
if updateAccountPeers {
2024-11-08 16:38:32 +01:00
am . updateAccountPeers ( ctx , accountID )
2024-10-23 12:05:02 +02:00
}
2023-10-04 15:08:50 +02:00
return nil
2021-08-23 21:43:05 +02:00
}
2022-01-16 17:10:36 +01:00
// GetNetworkMap returns Network map for a given peer (omits original peer from the Peers result)
2024-07-03 11:33:02 +02:00
func ( am * DefaultAccountManager ) GetNetworkMap ( ctx context . Context , peerID string ) ( * NetworkMap , error ) {
account , err := am . Store . GetAccountByPeerID ( ctx , peerID )
2021-08-23 21:43:05 +02:00
if err != nil {
2022-11-11 20:36:45 +01:00
return nil , err
2021-08-23 21:43:05 +02:00
}
2023-02-03 10:33:28 +01:00
peer := account . GetPeer ( peerID )
if peer == nil {
return nil , status . Errorf ( status . NotFound , "peer with ID %s not found" , peerID )
}
2024-03-27 18:48:48 +01:00
groups := make ( map [ string ] [ ] string )
for groupID , group := range account . Groups {
groups [ groupID ] = group . Peers
}
validatedPeers , err := am . integratedPeerValidator . GetValidatedPeers ( account . Id , account . Groups , account . Peers , account . Settings . Extra )
if err != nil {
return nil , err
}
2024-08-07 10:52:31 +02:00
customZone := account . GetPeersCustomZone ( ctx , am . dnsDomain )
return account . GetPeerNetworkMap ( ctx , peer . ID , customZone , validatedPeers , nil ) , nil
2021-08-23 21:43:05 +02:00
}
2022-06-24 21:30:51 +02:00
// GetPeerNetwork returns the Network for a given peer
2024-07-03 11:33:02 +02:00
func ( am * DefaultAccountManager ) GetPeerNetwork ( ctx context . Context , peerID string ) ( * Network , error ) {
account , err := am . Store . GetAccountByPeerID ( ctx , peerID )
2022-06-24 21:30:51 +02:00
if err != nil {
2022-11-11 20:36:45 +01:00
return nil , err
2022-06-24 21:30:51 +02:00
}
return account . Network . Copy ( ) , err
}
2021-08-23 21:43:05 +02:00
// AddPeer adds a new peer to the Store.
2023-03-03 18:35:38 +01:00
// Each Account has a list of pre-authorized SetupKey and if no Account has a given key err with a code status.PermissionDenied
// will be returned, meaning the setup key is invalid or not found.
2022-05-05 20:02:15 +02:00
// If a User ID is provided, it means that we passed the authentication using JWT, then we look for account by User ID and register the peer
2023-03-03 18:35:38 +01:00
// to it. We also add the User ID to the peer metadata to identify registrant. If no userID provided, then fail with status.PermissionDenied
2021-08-23 21:43:05 +02:00
// Each new Peer will be assigned a new next net.IP from the Account.Network and Account.Network.LastIP will be updated (IP's are not reused).
2021-08-24 11:50:19 +02:00
// The peer property is just a placeholder for the Peer properties to pass further
2024-07-03 11:33:02 +02:00
func ( am * DefaultAccountManager ) AddPeer ( ctx context . Context , setupKey , userID string , peer * nbpeer . Peer ) ( * nbpeer . Peer , * NetworkMap , [ ] * posture . Checks , error ) {
2023-03-03 18:35:38 +01:00
if setupKey == "" && userID == "" {
// no auth method provided => reject access
2024-06-22 16:41:16 +02:00
return nil , nil , nil , status . Errorf ( status . Unauthenticated , "no peer auth method provided, please use a setup key or interactive SSO login" )
2023-03-03 18:35:38 +01:00
}
2021-08-23 21:43:05 +02:00
upperKey := strings . ToUpper ( setupKey )
2024-10-28 17:52:23 +01:00
hashedKey := sha256 . Sum256 ( [ ] byte ( upperKey ) )
encodedHashedKey := b64 . StdEncoding . EncodeToString ( hashedKey [ : ] )
2024-05-31 16:41:12 +02:00
var accountID string
2021-08-23 21:43:05 +02:00
var err error
2022-11-08 16:14:36 +01:00
addedByUser := false
if len ( userID ) > 0 {
addedByUser = true
2024-11-14 17:33:57 +01:00
accountID , err = am . Store . GetAccountIDByUserID ( ctx , LockingStrengthShare , userID )
2022-05-05 20:02:15 +02:00
} else {
2024-10-28 17:52:23 +01:00
accountID , err = am . Store . GetAccountIDBySetupKey ( ctx , encodedHashedKey )
2022-11-08 16:14:36 +01:00
}
if err != nil {
2024-06-22 16:41:16 +02:00
return nil , nil , nil , status . Errorf ( status . NotFound , "failed adding new peer: account not found" )
2021-08-23 21:43:05 +02:00
}
2024-07-31 14:53:32 +02:00
unlock := am . Store . AcquireWriteLockByUID ( ctx , accountID )
2024-05-31 16:41:12 +02:00
defer func ( ) {
if unlock != nil {
unlock ( )
}
} ( )
2022-11-07 17:52:23 +01:00
2023-06-02 18:04:24 +02:00
// This is a handling for the case when the same machine (with the same WireGuard pub key) tries to register twice.
2024-07-31 14:53:32 +02:00
// Such case is possible when AddPeer function takes long time to finish after AcquireWriteLockByUID (e.g., database is slow)
2023-06-02 18:04:24 +02:00
// and the peer disconnects with a timeout and tries to register again.
// We just check if this machine has been registered before and reject the second registration.
// The connecting peer should be able to recover with a retry.
2024-09-16 15:47:03 +02:00
_ , err = am . Store . GetPeerByPeerPubKey ( ctx , LockingStrengthShare , peer . Key )
2023-06-02 17:32:55 +02:00
if err == nil {
2024-06-22 16:41:16 +02:00
return nil , nil , nil , status . Errorf ( status . PreconditionFailed , "peer has been already registered" )
2023-06-02 17:32:55 +02:00
}
2023-01-02 15:11:32 +01:00
opEvent := & activity . Event {
2023-04-03 15:09:35 +02:00
Timestamp : time . Now ( ) . UTC ( ) ,
2024-09-16 15:47:03 +02:00
AccountID : accountID ,
2023-01-02 15:11:32 +01:00
}
2024-09-16 15:47:03 +02:00
var newPeer * nbpeer . Peer
2024-11-18 13:06:25 +01:00
var updateAccountPeers bool
2022-11-08 16:14:36 +01:00
2024-09-16 15:47:03 +02:00
err = am . Store . ExecuteInTransaction ( ctx , func ( transaction Store ) error {
var setupKeyID string
var setupKeyName string
var ephemeral bool
2024-11-14 17:33:57 +01:00
var groupsToAdd [ ] string
2024-09-16 15:47:03 +02:00
if addedByUser {
user , err := transaction . GetUserByUserID ( ctx , LockingStrengthUpdate , userID )
if err != nil {
return fmt . Errorf ( "failed to get user groups: %w" , err )
}
groupsToAdd = user . AutoGroups
opEvent . InitiatorID = userID
opEvent . Activity = activity . PeerAddedByUser
} else {
// Validate the setup key
2024-10-28 17:52:23 +01:00
sk , err := transaction . GetSetupKeyBySecret ( ctx , LockingStrengthUpdate , encodedHashedKey )
2024-09-16 15:47:03 +02:00
if err != nil {
return fmt . Errorf ( "failed to get setup key: %w" , err )
}
2021-08-23 21:43:05 +02:00
2024-09-16 15:47:03 +02:00
if ! sk . IsValid ( ) {
return status . Errorf ( status . PreconditionFailed , "couldn't add peer: setup key is invalid" )
}
2022-11-08 16:14:36 +01:00
2024-09-16 15:47:03 +02:00
opEvent . InitiatorID = sk . Id
opEvent . Activity = activity . PeerAddedWithSetupKey
groupsToAdd = sk . AutoGroups
ephemeral = sk . Ephemeral
setupKeyID = sk . Id
setupKeyName = sk . Name
}
2022-11-07 15:38:21 +01:00
2024-09-16 15:47:03 +02:00
if strings . ToLower ( peer . Meta . Hostname ) == "iphone" || strings . ToLower ( peer . Meta . Hostname ) == "ipad" && userID != "" {
if am . idpManager != nil {
userdata , err := am . idpManager . GetUserDataByID ( ctx , userID , idp . AppMetadata { WTAccountID : accountID } )
if err == nil && userdata != nil {
peer . Meta . Hostname = fmt . Sprintf ( "%s-%s" , peer . Meta . Hostname , strings . Split ( userdata . Email , "@" ) [ 0 ] )
}
}
}
2021-08-23 21:43:05 +02:00
2024-09-16 15:47:03 +02:00
freeLabel , err := am . getFreeDNSLabel ( ctx , transaction , accountID , peer . Meta . Hostname )
2024-07-29 13:30:27 +02:00
if err != nil {
2024-09-16 15:47:03 +02:00
return fmt . Errorf ( "failed to get free DNS label: %w" , err )
2024-07-29 13:30:27 +02:00
}
2024-11-18 13:06:25 +01:00
freeIP , err := getFreeIP ( ctx , transaction , accountID )
2024-09-16 15:47:03 +02:00
if err != nil {
return fmt . Errorf ( "failed to get free IP: %w" , err )
}
registrationTime := time . Now ( ) . UTC ( )
newPeer = & nbpeer . Peer {
2024-10-13 14:52:43 +02:00
ID : xid . New ( ) . String ( ) ,
AccountID : accountID ,
Key : peer . Key ,
IP : freeIP ,
Meta : peer . Meta ,
Name : peer . Meta . Hostname ,
DNSLabel : freeLabel ,
UserID : userID ,
Status : & nbpeer . PeerStatus { Connected : false , LastSeen : registrationTime } ,
SSHEnabled : false ,
SSHKey : peer . SSHKey ,
LastLogin : registrationTime ,
CreatedAt : registrationTime ,
LoginExpirationEnabled : addedByUser ,
Ephemeral : ephemeral ,
Location : peer . Location ,
InactivityExpirationEnabled : addedByUser ,
2024-09-16 15:47:03 +02:00
}
opEvent . TargetID = newPeer . ID
opEvent . Meta = newPeer . EventMeta ( am . GetDNSDomain ( ) )
if ! addedByUser {
opEvent . Meta [ "setup_key_name" ] = setupKeyName
}
if am . geo != nil && newPeer . Location . ConnectionIP != nil {
location , err := am . geo . Lookup ( newPeer . Location . ConnectionIP )
if err != nil {
log . WithContext ( ctx ) . Warnf ( "failed to get location for new peer realip: [%s]: %v" , newPeer . Location . ConnectionIP . String ( ) , err )
} else {
newPeer . Location . CountryCode = location . Country . ISOCode
newPeer . Location . CityName = location . City . Names . En
newPeer . Location . GeoNameID = location . City . GeonameID
}
}
2022-05-21 15:21:39 +02:00
2024-09-16 15:47:03 +02:00
settings , err := transaction . GetAccountSettings ( ctx , LockingStrengthShare , accountID )
2022-11-08 16:14:36 +01:00
if err != nil {
2024-09-16 15:47:03 +02:00
return fmt . Errorf ( "failed to get account settings: %w" , err )
2022-11-08 16:14:36 +01:00
}
2024-09-16 15:47:03 +02:00
newPeer = am . integratedPeerValidator . PreparePeer ( ctx , accountID , newPeer , groupsToAdd , settings . Extra )
err = transaction . AddPeerToAllGroup ( ctx , accountID , newPeer . ID )
2022-11-08 16:14:36 +01:00
if err != nil {
2024-09-16 15:47:03 +02:00
return fmt . Errorf ( "failed adding peer to All group: %w" , err )
2022-11-08 16:14:36 +01:00
}
2024-09-16 15:47:03 +02:00
if len ( groupsToAdd ) > 0 {
for _ , g := range groupsToAdd {
err = transaction . AddPeerToGroup ( ctx , accountID , newPeer . ID , g )
if err != nil {
return err
}
2022-09-13 13:39:46 +02:00
}
}
2024-09-16 15:47:03 +02:00
err = transaction . AddPeerToAccount ( ctx , newPeer )
if err != nil {
return fmt . Errorf ( "failed to add peer to account: %w" , err )
}
2024-03-27 18:48:48 +01:00
2024-11-08 16:38:32 +01:00
err = transaction . IncrementNetworkSerial ( ctx , LockingStrengthUpdate , accountID )
2024-01-06 12:57:05 +01:00
if err != nil {
2024-09-16 15:47:03 +02:00
return fmt . Errorf ( "failed to increment network serial: %w" , err )
2024-01-06 12:57:05 +01:00
}
2024-09-16 15:47:03 +02:00
if addedByUser {
err := transaction . SaveUserLastLogin ( ctx , accountID , userID , newPeer . LastLogin )
if err != nil {
return fmt . Errorf ( "failed to update user last login: %w" , err )
}
} else {
err = transaction . IncrementSetupKeyUsage ( ctx , setupKeyID )
if err != nil {
return fmt . Errorf ( "failed to increment setup key usage: %w" , err )
}
}
2024-11-18 13:06:25 +01:00
updateAccountPeers , err = isPeerInActiveGroup ( ctx , transaction , accountID , newPeer . ID )
if err != nil {
return err
}
2024-09-16 15:47:03 +02:00
log . WithContext ( ctx ) . Debugf ( "Peer %s added to account %s" , newPeer . ID , accountID )
return nil
} )
2021-08-23 21:43:05 +02:00
if err != nil {
2024-09-16 15:47:03 +02:00
return nil , nil , nil , fmt . Errorf ( "failed to add peer to database: %w" , err )
}
if newPeer == nil {
return nil , nil , nil , fmt . Errorf ( "new peer is nil" )
2021-08-23 21:43:05 +02:00
}
2024-09-16 15:47:03 +02:00
am . StoreEvent ( ctx , opEvent . InitiatorID , opEvent . TargetID , opEvent . AccountID , opEvent . Activity , opEvent . Meta )
2024-05-31 16:41:12 +02:00
unlock ( )
unlock = nil
2024-11-14 17:33:57 +01:00
if updateAccountPeers {
2024-11-08 16:38:32 +01:00
am . updateAccountPeers ( ctx , accountID )
2024-10-23 12:05:02 +02:00
}
2023-03-03 18:35:38 +01:00
2024-11-14 17:33:57 +01:00
return am . getValidatedPeerWithMap ( ctx , false , accountID , newPeer )
2021-08-23 21:43:05 +02:00
}
2022-05-23 13:03:57 +02:00
2024-11-18 13:06:25 +01:00
func getFreeIP ( ctx context . Context , transaction Store , accountID string ) ( net . IP , error ) {
takenIps , err := transaction . GetTakenIPs ( ctx , LockingStrengthShare , accountID )
2024-09-16 15:47:03 +02:00
if err != nil {
return nil , fmt . Errorf ( "failed to get taken IPs: %w" , err )
}
2024-11-18 13:06:25 +01:00
network , err := transaction . GetAccountNetwork ( ctx , LockingStrengthUpdate , accountID )
2024-09-16 15:47:03 +02:00
if err != nil {
return nil , fmt . Errorf ( "failed getting network: %w" , err )
}
nextIp , err := AllocatePeerIP ( network . Net , takenIps )
if err != nil {
return nil , fmt . Errorf ( "failed to allocate new peer ip: %w" , err )
}
return nextIp , nil
}
2023-03-03 18:35:38 +01:00
// SyncPeer checks whether peer is eligible for receiving NetworkMap (authenticated) and returns its NetworkMap if eligible
2024-11-14 17:33:57 +01:00
func ( am * DefaultAccountManager ) SyncPeer ( ctx context . Context , sync PeerSync , accountID string ) ( * nbpeer . Peer , * NetworkMap , [ ] * posture . Checks , error ) {
2024-11-18 13:06:25 +01:00
var peer * nbpeer . Peer
var peerNotValid bool
var isStatusChanged bool
var updated bool
var err error
2023-03-03 18:35:38 +01:00
2024-11-18 13:06:25 +01:00
err = am . Store . ExecuteInTransaction ( ctx , func ( transaction Store ) error {
peer , err = transaction . GetPeerByPeerPubKey ( ctx , LockingStrengthUpdate , sync . WireGuardPubKey )
2024-08-19 12:50:11 +02:00
if err != nil {
2024-11-18 13:06:25 +01:00
return status . NewPeerNotRegisteredError ( )
2024-08-19 12:50:11 +02:00
}
2024-11-18 13:06:25 +01:00
if peer . UserID != "" {
user , err := transaction . GetUserByUserID ( ctx , LockingStrengthShare , peer . UserID )
if err != nil {
return err
}
if err = checkIfPeerOwnerIsBlocked ( peer , user ) ; err != nil {
return err
}
}
settings , err := transaction . GetAccountSettings ( ctx , LockingStrengthShare , accountID )
2024-08-19 12:50:11 +02:00
if err != nil {
2024-11-18 13:06:25 +01:00
return err
2024-08-19 12:50:11 +02:00
}
2023-05-11 18:09:36 +02:00
2024-11-18 13:06:25 +01:00
if peerLoginExpired ( ctx , peer , settings ) {
return status . NewPeerLoginExpiredError ( )
}
2024-03-27 18:48:48 +01:00
2024-11-18 13:06:25 +01:00
peerGroupIDs , err := getPeerGroupIDs ( ctx , transaction , accountID , peer . ID )
if err != nil {
return err
}
2024-06-13 13:24:24 +02:00
2024-11-18 13:06:25 +01:00
peerNotValid , isStatusChanged , err = am . integratedPeerValidator . IsNotValidPeer ( ctx , accountID , peer , peerGroupIDs , settings . Extra )
if err != nil {
return err
}
2024-03-27 18:48:48 +01:00
2024-11-18 13:06:25 +01:00
updated = peer . UpdateMetaIfNew ( sync . Meta )
if updated {
err = transaction . SavePeer ( ctx , LockingStrengthUpdate , accountID , peer )
if err != nil {
return err
}
}
return nil
} )
2024-11-14 17:33:57 +01:00
if err != nil {
return nil , nil , nil , err
2024-03-27 18:48:48 +01:00
}
2024-11-14 17:33:57 +01:00
if isStatusChanged || ( updated && sync . UpdateAccountPeers ) {
am . updateAccountPeers ( ctx , accountID )
2024-11-11 10:38:34 +01:00
}
2024-06-22 16:41:16 +02:00
2024-11-14 17:33:57 +01:00
return am . getValidatedPeerWithMap ( ctx , peerNotValid , accountID , peer )
2023-03-03 18:35:38 +01:00
}
// 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.
2024-07-03 11:33:02 +02:00
func ( am * DefaultAccountManager ) LoginPeer ( ctx context . Context , login PeerLogin ) ( * nbpeer . Peer , * NetworkMap , [ ] * posture . Checks , error ) {
accountID , err := am . Store . GetAccountIDByPeerPubKey ( ctx , login . WireGuardPubKey )
2023-03-03 18:35:38 +01:00
if err != nil {
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.
2024-03-27 18:48:48 +01:00
newPeer := & nbpeer . Peer {
2024-07-29 13:30:27 +02:00
Key : login . WireGuardPubKey ,
Meta : login . Meta ,
SSHKey : login . SSHKey ,
Location : nbpeer . Location { ConnectionIP : login . ConnectionIP } ,
2024-03-27 18:48:48 +01:00
}
2024-07-03 11:33:02 +02:00
return am . AddPeer ( ctx , login . SetupKey , login . UserID , newPeer )
2023-03-03 18:35:38 +01:00
}
2024-06-22 16:41:16 +02:00
2024-07-03 11:33:02 +02:00
log . WithContext ( ctx ) . Errorf ( "failed while logging in peer %s: %v" , login . WireGuardPubKey , err )
2024-06-22 16:41:16 +02:00
return nil , nil , nil , status . Errorf ( status . Internal , "failed while logging in peer" )
2023-03-03 18:35:38 +01:00
}
2024-07-29 13:30:27 +02:00
// when the client sends a login request with a JWT which is used to get the user ID,
// it means that the client has already checked if it needs login and had been through the SSO flow
// so, we can skip this check and directly proceed with the login
if login . UserID == "" {
2024-08-19 12:50:11 +02:00
log . Info ( "Peer needs login" )
2024-07-29 13:30:27 +02:00
err = am . checkIFPeerNeedsLoginWithoutLock ( ctx , accountID , login )
if err != nil {
2024-06-22 16:41:16 +02:00
return nil , nil , nil , err
2024-05-31 16:41:12 +02:00
}
}
2024-08-19 12:50:11 +02:00
unlockAccount := am . Store . AcquireReadLockByUID ( ctx , accountID )
defer unlockAccount ( )
unlockPeer := am . Store . AcquireWriteLockByUID ( ctx , login . WireGuardPubKey )
2024-05-31 16:41:12 +02:00
defer func ( ) {
2024-08-19 12:50:11 +02:00
if unlockPeer != nil {
unlockPeer ( )
2024-05-31 16:41:12 +02:00
}
} ( )
2023-03-03 18:35:38 +01:00
2024-11-18 13:06:25 +01:00
var peer * nbpeer . Peer
var updateRemotePeers bool
var isRequiresApproval bool
var isStatusChanged bool
2023-03-03 18:35:38 +01:00
2024-11-18 13:06:25 +01:00
err = am . Store . ExecuteInTransaction ( ctx , func ( transaction Store ) error {
peer , err = transaction . GetPeerByPeerPubKey ( ctx , LockingStrengthUpdate , login . WireGuardPubKey )
if err != nil {
return err
}
settings , err := transaction . GetAccountSettings ( ctx , LockingStrengthShare , accountID )
if err != nil {
return err
}
// this flag prevents unnecessary calls to the persistent store.
shouldStorePeer := false
if login . UserID != "" {
if peer . UserID != login . UserID {
log . Warnf ( "user mismatch when logging in peer %s: peer user %s, login user %s " , peer . ID , peer . UserID , login . UserID )
return status . Errorf ( status . Unauthenticated , "invalid user" )
}
2023-05-11 18:09:36 +02:00
2024-11-18 13:06:25 +01:00
changed , err := am . handleUserPeer ( ctx , transaction , peer , settings )
if err != nil {
return err
}
2024-08-19 12:50:11 +02:00
2024-11-18 13:06:25 +01:00
if changed {
shouldStorePeer = true
updateRemotePeers = true
}
2024-10-07 18:06:26 +02:00
}
2024-11-18 14:43:09 +01:00
peerGroupIDs , err := getPeerGroupIDs ( ctx , transaction , accountID , peer . ID )
2023-03-10 17:39:29 +01:00
if err != nil {
2024-11-18 13:06:25 +01:00
return err
2023-03-10 17:39:29 +01:00
}
2024-11-18 13:06:25 +01:00
isRequiresApproval , isStatusChanged , err = am . integratedPeerValidator . IsNotValidPeer ( ctx , accountID , peer , peerGroupIDs , settings . Extra )
if err != nil {
return err
}
updated := peer . UpdateMetaIfNew ( login . Meta )
if updated {
2024-08-19 12:50:11 +02:00
shouldStorePeer = true
}
2023-03-03 18:35:38 +01:00
2024-11-18 13:06:25 +01:00
if peer . SSHKey != login . SSHKey {
peer . SSHKey = login . SSHKey
shouldStorePeer = true
}
2024-07-29 13:30:27 +02:00
2024-11-18 13:06:25 +01:00
if shouldStorePeer {
if err = transaction . SavePeer ( ctx , LockingStrengthUpdate , accountID , peer ) ; err != nil {
return err
2024-08-19 12:50:11 +02:00
}
}
2024-11-18 13:06:25 +01:00
return nil
} )
2024-08-19 12:50:11 +02:00
if err != nil {
return nil , nil , nil , err
}
unlockPeer ( )
unlockPeer = nil
2024-03-27 18:48:48 +01:00
if updateRemotePeers || isStatusChanged {
2024-11-08 16:38:32 +01:00
am . updateAccountPeers ( ctx , accountID )
2023-03-10 17:39:29 +01:00
}
2024-03-27 18:48:48 +01:00
2024-11-14 17:33:57 +01:00
return am . getValidatedPeerWithMap ( ctx , isRequiresApproval , accountID , peer )
2024-07-29 13:30:27 +02:00
}
// checkIFPeerNeedsLoginWithoutLock checks if the peer needs login without acquiring the account lock. The check validate if the peer was not added via SSO
// and if the peer login is expired.
// 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 {
2024-09-16 15:47:03 +02:00
peer , err := am . Store . GetPeerByPeerPubKey ( ctx , LockingStrengthShare , login . WireGuardPubKey )
2024-07-29 13:30:27 +02:00
if err != nil {
return err
}
// if the peer was not added with SSO login we can exit early because peers activated with setup-key
// doesn't expire, and we avoid extra databases calls.
if ! peer . AddedWithSSOLogin ( ) {
return nil
}
2024-09-16 15:47:03 +02:00
settings , err := am . Store . GetAccountSettings ( ctx , LockingStrengthShare , accountID )
2024-07-29 13:30:27 +02:00
if err != nil {
return err
}
if peerLoginExpired ( ctx , peer , settings ) {
return status . NewPeerLoginExpiredError ( )
}
return nil
}
2024-11-14 17:33:57 +01:00
func ( am * DefaultAccountManager ) getValidatedPeerWithMap ( ctx context . Context , isRequiresApproval bool , accountID string , peer * nbpeer . Peer ) ( * nbpeer . Peer , * NetworkMap , [ ] * posture . Checks , error ) {
2024-03-27 18:48:48 +01:00
if isRequiresApproval {
2024-11-14 17:33:57 +01:00
network , err := am . Store . GetAccountNetwork ( ctx , LockingStrengthShare , accountID )
if err != nil {
return nil , nil , nil , err
}
2024-03-27 18:48:48 +01:00
emptyMap := & NetworkMap {
2024-11-14 17:33:57 +01:00
Network : network . Copy ( ) ,
2024-03-27 18:48:48 +01:00
}
2024-07-29 13:30:27 +02:00
return peer , emptyMap , nil , nil
2024-03-27 18:48:48 +01:00
}
2024-11-14 17:33:57 +01:00
account , err := am . requestBuffer . GetAccountWithBackpressure ( ctx , accountID )
2024-03-27 18:48:48 +01:00
if err != nil {
2024-06-22 16:41:16 +02:00
return nil , nil , nil , err
2024-03-27 18:48:48 +01:00
}
2024-11-11 10:38:34 +01:00
2024-11-14 17:33:57 +01:00
approvedPeersMap , err := am . GetValidatedPeers ( ctx , account . Id )
if err != nil {
return nil , nil , nil , err
}
postureChecks , err := am . getPeerPostureChecks ( ctx , account . Id , peer . ID )
2024-11-11 10:38:34 +01:00
if err != nil {
return nil , nil , nil , err
}
2024-03-27 18:48:48 +01:00
2024-08-07 10:52:31 +02:00
customZone := account . GetPeersCustomZone ( ctx , am . dnsDomain )
return peer , account . GetPeerNetworkMap ( ctx , peer . ID , customZone , approvedPeersMap , am . metrics . AccountManagerMetrics ( ) ) , postureChecks , nil
2023-03-03 18:35:38 +01:00
}
2024-11-18 13:06:25 +01:00
func ( am * DefaultAccountManager ) handleExpiredPeer ( ctx context . Context , transaction Store , user * User , peer * nbpeer . Peer ) error {
2024-08-19 12:50:11 +02:00
err := checkAuth ( ctx , user . Id , peer )
2024-07-29 13:30:27 +02:00
if err != nil {
return err
}
// If peer was expired before and if it reached this point, it is re-authenticated.
// UserID is present, meaning that JWT validation passed successfully in the API layer.
2024-08-19 12:50:11 +02:00
peer = peer . UpdateLastLogin ( )
2024-11-18 13:06:25 +01:00
err = transaction . SavePeer ( ctx , LockingStrengthUpdate , peer . AccountID , peer )
2024-07-29 13:30:27 +02:00
if err != nil {
2024-08-19 12:50:11 +02:00
return err
2024-07-29 13:30:27 +02:00
}
2024-11-18 13:06:25 +01:00
err = transaction . SaveUserLastLogin ( ctx , user . AccountID , user . Id , peer . LastLogin )
2024-07-29 13:30:27 +02:00
if err != nil {
return err
}
2024-08-19 12:50:11 +02:00
am . StoreEvent ( ctx , user . Id , peer . ID , user . AccountID , activity . UserLoggedInPeer , peer . EventMeta ( am . GetDNSDomain ( ) ) )
2024-07-29 13:30:27 +02:00
return nil
}
2024-08-19 12:50:11 +02:00
func checkIfPeerOwnerIsBlocked ( peer * nbpeer . Peer , user * User ) error {
2023-05-11 18:09:36 +02:00
if peer . AddedWithSSOLogin ( ) {
if user . IsBlocked ( ) {
return status . Errorf ( status . PermissionDenied , "user is blocked" )
}
}
return nil
}
2024-07-03 11:33:02 +02:00
func checkAuth ( ctx context . Context , loginUserID string , peer * nbpeer . Peer ) error {
2023-03-10 17:39:29 +01:00
if loginUserID == "" {
// absence of a user ID indicates that JWT wasn't provided.
2024-07-29 13:30:27 +02:00
return status . NewPeerLoginExpiredError ( )
2023-03-10 17:39:29 +01:00
}
if peer . UserID != loginUserID {
2024-07-03 11:33:02 +02:00
log . WithContext ( ctx ) . Warnf ( "user mismatch when logging in peer %s: peer user %s, login user %s " , peer . ID , peer . UserID , loginUserID )
2024-07-29 13:30:27 +02:00
return status . Errorf ( status . Unauthenticated , "can't login with this credentials" )
2023-03-10 17:39:29 +01:00
}
return nil
}
2024-07-03 11:33:02 +02:00
func peerLoginExpired ( ctx context . Context , peer * nbpeer . Peer , settings * Settings ) bool {
2024-05-31 16:41:12 +02:00
expired , expiresIn := peer . LoginExpired ( settings . PeerLoginExpiration )
expired = settings . PeerLoginExpirationEnabled && expired
2023-03-10 17:39:29 +01:00
if expired || peer . Status . LoginExpired {
2024-07-03 11:33:02 +02:00
log . WithContext ( ctx ) . Debugf ( "peer's %s login expired %v ago" , peer . ID , expiresIn )
2023-03-10 17:39:29 +01:00
return true
}
return false
}
2023-02-07 20:11:08 +01:00
// GetPeer for a given accountID, peerID and userID error if not found.
2024-07-03 11:33:02 +02:00
func ( am * DefaultAccountManager ) GetPeer ( ctx context . Context , accountID , peerID , userID string ) ( * nbpeer . Peer , error ) {
2024-11-14 17:33:57 +01:00
user , err := am . Store . GetUserByUserID ( ctx , LockingStrengthShare , userID )
2023-02-07 20:11:08 +01:00
if err != nil {
return nil , err
}
2024-11-14 17:33:57 +01:00
if user . AccountID != accountID {
return nil , status . NewUserNotPartOfAccountError ( )
}
settings , err := am . Store . GetAccountSettings ( ctx , LockingStrengthShare , accountID )
2023-02-07 20:11:08 +01:00
if err != nil {
return nil , err
}
2024-11-14 17:33:57 +01:00
if user . IsRegularUser ( ) && settings . RegularUsersViewBlocked {
2024-03-27 16:11:45 +01:00
return nil , status . Errorf ( status . Internal , "user %s has no access to his own peer %s under account %s" , userID , peerID , accountID )
}
2024-11-14 17:33:57 +01:00
peer , err := am . Store . GetPeerByID ( ctx , LockingStrengthShare , accountID , peerID )
if err != nil {
return nil , err
2023-02-07 20:11:08 +01:00
}
// if admin or user owns this peer, return peer
2024-11-14 17:33:57 +01:00
if user . IsAdminOrServiceUser ( ) || peer . UserID == userID {
2023-02-07 20:11:08 +01:00
return peer , nil
}
// it is also possible that user doesn't own the peer but some of his peers have access to it,
// this is a valid case, show the peer as well.
2024-11-14 17:33:57 +01:00
userPeers , err := am . Store . GetUserPeers ( ctx , LockingStrengthShare , accountID , userID )
if err != nil {
return nil , err
}
approvedPeersMap , err := am . GetValidatedPeers ( ctx , accountID )
2023-02-07 20:11:08 +01:00
if err != nil {
return nil , err
}
2024-11-14 17:33:57 +01:00
account , err := am . requestBuffer . GetAccountWithBackpressure ( ctx , accountID )
2024-03-27 18:48:48 +01:00
if err != nil {
return nil , err
}
2023-02-07 20:11:08 +01:00
for _ , p := range userPeers {
2024-07-03 11:33:02 +02:00
aclPeers , _ := account . getPeerConnectionResources ( ctx , p . ID , approvedPeersMap )
2023-02-07 20:11:08 +01:00
for _ , aclPeer := range aclPeers {
if aclPeer . ID == peerID {
return peer , nil
}
}
}
return nil , status . Errorf ( status . Internal , "user %s has no access to peer %s under account %s" , userID , peerID , accountID )
}
2022-06-23 17:04:53 +02:00
// updateAccountPeers updates all peers that belong to an account.
// Should be called when changes have to be synced to peers.
2024-11-08 16:38:32 +01:00
func ( am * DefaultAccountManager ) updateAccountPeers ( ctx context . Context , accountID string ) {
2024-08-07 10:52:31 +02:00
start := time . Now ( )
defer func ( ) {
if am . metrics != nil {
am . metrics . AccountManagerMetrics ( ) . CountUpdateAccountPeersDuration ( time . Since ( start ) )
}
} ( )
2024-11-08 16:38:32 +01:00
account , err := am . requestBuffer . GetAccountWithBackpressure ( ctx , accountID )
if err != nil {
2024-11-14 17:33:57 +01:00
log . WithContext ( ctx ) . Errorf ( "failed to send out updates to peers. failed to get account: %v" , err )
2024-11-08 16:38:32 +01:00
return
}
2024-11-14 17:33:57 +01:00
2022-11-07 12:10:56 +01:00
peers := account . GetPeers ( )
2022-06-24 21:30:51 +02:00
2024-11-14 17:33:57 +01:00
approvedPeersMap , err := am . GetValidatedPeers ( ctx , account . Id )
2024-03-27 18:48:48 +01:00
if err != nil {
2024-08-07 10:52:31 +02:00
log . WithContext ( ctx ) . Errorf ( "failed to send out updates to peers, failed to validate peer: %v" , err )
2024-03-27 18:48:48 +01:00
return
}
2024-08-07 10:52:31 +02:00
var wg sync . WaitGroup
semaphore := make ( chan struct { } , 10 )
dnsCache := & DNSConfigCache { }
customZone := account . GetPeersCustomZone ( ctx , am . dnsDomain )
2022-08-18 18:22:15 +02:00
for _ , peer := range peers {
2024-04-29 18:31:52 +02:00
if ! am . peersUpdateManager . HasChannel ( peer . ID ) {
2024-07-03 11:33:02 +02:00
log . WithContext ( ctx ) . Tracef ( "peer %s doesn't have a channel, skipping network map update" , peer . ID )
2024-04-29 18:31:52 +02:00
continue
}
2024-06-22 16:41:16 +02:00
2024-08-07 10:52:31 +02:00
wg . Add ( 1 )
semaphore <- struct { } { }
go func ( p * nbpeer . Peer ) {
defer wg . Done ( )
defer func ( ) { <- semaphore } ( )
2024-11-11 10:38:34 +01:00
postureChecks , err := am . getPeerPostureChecks ( ctx , account . Id , p . ID )
if err != nil {
2024-11-14 17:33:57 +01:00
log . WithContext ( ctx ) . Debugf ( "failed to get posture checks for peer %s: %v" , peer . ID , err )
2024-11-11 10:38:34 +01:00
return
}
2024-08-07 10:52:31 +02:00
remotePeerNetworkMap := account . GetPeerNetworkMap ( ctx , p . ID , customZone , approvedPeersMap , am . metrics . AccountManagerMetrics ( ) )
2024-09-08 12:06:14 +02:00
update := toSyncResponse ( ctx , nil , p , nil , nil , remotePeerNetworkMap , am . GetDNSDomain ( ) , postureChecks , dnsCache )
2024-10-23 12:05:02 +02:00
am . peersUpdateManager . SendUpdate ( ctx , p . ID , & UpdateMessage { Update : update , NetworkMap : remotePeerNetworkMap } )
2024-08-07 10:52:31 +02:00
} ( peer )
2022-06-04 22:02:22 +02:00
}
2024-08-07 10:52:31 +02:00
wg . Wait ( )
2022-06-04 22:02:22 +02:00
}
2024-09-16 15:47:03 +02:00
2024-11-14 17:33:57 +01:00
// getNextPeerExpiration returns the minimum duration in which the next peer of the account will expire if it was found.
// If there is no peer that expires this function returns false and a duration of 0.
// This function only considers peers that haven't been expired yet and that are connected.
func ( am * DefaultAccountManager ) getNextPeerExpiration ( ctx context . Context , accountID string ) ( time . Duration , bool ) {
peersWithExpiry , err := am . Store . GetAccountPeersWithExpiration ( ctx , LockingStrengthShare , accountID )
if err != nil {
log . WithContext ( ctx ) . Errorf ( "failed to get peers with expiration: %v" , err )
return 0 , false
2024-09-16 15:47:03 +02:00
}
2024-11-14 17:33:57 +01:00
if len ( peersWithExpiry ) == 0 {
return 0 , false
}
settings , err := am . Store . GetAccountSettings ( ctx , LockingStrengthShare , accountID )
if err != nil {
log . WithContext ( ctx ) . Errorf ( "failed to get account settings: %v" , err )
return 0 , false
}
var nextExpiry * time . Duration
for _ , peer := range peersWithExpiry {
// consider only connected peers because others will require login on connecting to the management server
if peer . Status . LoginExpired || ! peer . Status . Connected {
continue
}
_ , duration := peer . LoginExpired ( settings . PeerLoginExpiration )
if nextExpiry == nil || duration < * nextExpiry {
// if expiration is below 1s return 1s duration
// this avoids issues with ticker that can't be set to < 0
if duration < time . Second {
return time . Second , true
}
nextExpiry = & duration
}
}
if nextExpiry == nil {
return 0 , false
}
return * nextExpiry , true
}
// GetNextInactivePeerExpiration returns the minimum duration in which the next peer of the account will expire if it was found.
// If there is no peer that expires this function returns false and a duration of 0.
// This function only considers peers that haven't been expired yet and that are not connected.
func ( am * DefaultAccountManager ) getNextInactivePeerExpiration ( ctx context . Context , accountID string ) ( time . Duration , bool ) {
peersWithInactivity , err := am . Store . GetAccountPeersWithInactivity ( ctx , LockingStrengthShare , accountID )
if err != nil {
log . WithContext ( ctx ) . Errorf ( "failed to get peers with inactivity: %v" , err )
return 0 , false
}
if len ( peersWithInactivity ) == 0 {
return 0 , false
}
settings , err := am . Store . GetAccountSettings ( ctx , LockingStrengthShare , accountID )
if err != nil {
log . WithContext ( ctx ) . Errorf ( "failed to get account settings: %v" , err )
return 0 , false
}
var nextExpiry * time . Duration
for _ , peer := range peersWithInactivity {
if peer . Status . LoginExpired || peer . Status . Connected {
continue
}
_ , duration := peer . SessionExpired ( settings . PeerInactivityExpiration )
if nextExpiry == nil || duration < * nextExpiry {
// if expiration is below 1s return 1s duration
// this avoids issues with ticker that can't be set to < 0
if duration < time . Second {
return time . Second , true
}
nextExpiry = & duration
}
}
if nextExpiry == nil {
return 0 , false
}
return * nextExpiry , true
}
// getExpiredPeers returns peers that have been expired.
func ( am * DefaultAccountManager ) getExpiredPeers ( ctx context . Context , accountID string ) ( [ ] * nbpeer . Peer , error ) {
peersWithExpiry , err := am . Store . GetAccountPeersWithExpiration ( ctx , LockingStrengthShare , accountID )
if err != nil {
return nil , err
}
settings , err := am . Store . GetAccountSettings ( ctx , LockingStrengthShare , accountID )
if err != nil {
return nil , err
}
var peers [ ] * nbpeer . Peer
for _ , peer := range peersWithExpiry {
expired , _ := peer . LoginExpired ( settings . PeerLoginExpiration )
if expired {
peers = append ( peers , peer )
}
}
return peers , nil
}
// getInactivePeers returns peers that have been expired by inactivity
func ( am * DefaultAccountManager ) getInactivePeers ( ctx context . Context , accountID string ) ( [ ] * nbpeer . Peer , error ) {
peersWithInactivity , err := am . Store . GetAccountPeersWithInactivity ( ctx , LockingStrengthShare , accountID )
if err != nil {
return nil , err
}
settings , err := am . Store . GetAccountSettings ( ctx , LockingStrengthShare , accountID )
if err != nil {
return nil , err
}
var peers [ ] * nbpeer . Peer
for _ , inactivePeer := range peersWithInactivity {
inactive , _ := inactivePeer . SessionExpired ( settings . PeerInactivityExpiration )
if inactive {
peers = append ( peers , inactivePeer )
}
}
return peers , nil
}
// GetPeerGroups returns groups that the peer is part of.
func ( am * DefaultAccountManager ) GetPeerGroups ( ctx context . Context , accountID , peerID string ) ( [ ] * nbgroup . Group , error ) {
2024-11-18 13:06:25 +01:00
return getPeerGroups ( ctx , am . Store , accountID , peerID )
}
// getPeerGroups returns the IDs of the groups that the peer is part of.
func getPeerGroups ( ctx context . Context , transaction Store , accountID , peerID string ) ( [ ] * nbgroup . Group , error ) {
groups , err := transaction . GetAccountGroups ( ctx , LockingStrengthShare , accountID )
2024-11-14 17:33:57 +01:00
if err != nil {
return nil , err
}
peerGroups := make ( [ ] * nbgroup . Group , 0 )
for _ , group := range groups {
if slices . Contains ( group . Peers , peerID ) {
peerGroups = append ( peerGroups , group )
}
}
return peerGroups , nil
}
// getPeerGroupIDs returns the IDs of the groups that the peer is part of.
2024-11-18 13:06:25 +01:00
func getPeerGroupIDs ( ctx context . Context , transaction Store , accountID string , peerID string ) ( [ ] string , error ) {
groups , err := getPeerGroups ( ctx , transaction , accountID , peerID )
2024-11-14 17:33:57 +01:00
if err != nil {
return nil , err
}
groupIDs := make ( [ ] string , 0 , len ( groups ) )
for _ , group := range groups {
groupIDs = append ( groupIDs , group . ID )
}
return groupIDs , err
}
2024-11-18 13:06:25 +01:00
func getPeerDNSLabels ( ctx context . Context , transaction Store , accountID string ) ( lookupMap , error ) {
2024-11-18 13:09:30 +01:00
dnsLabels , err := transaction . GetPeerLabelsInAccount ( ctx , LockingStrengthShare , accountID )
2024-11-14 17:33:57 +01:00
if err != nil {
return nil , err
}
existingLabels := make ( lookupMap )
for _ , label := range dnsLabels {
existingLabels [ label ] = struct { } { }
}
return existingLabels , nil
2024-09-16 15:47:03 +02:00
}
2024-10-23 12:05:02 +02:00
// IsPeerInActiveGroup checks if the given peer is part of a group that is used
// in an active DNS, route, or ACL configuration.
2024-11-18 13:06:25 +01:00
func isPeerInActiveGroup ( ctx context . Context , transaction Store , accountID , peerID string ) ( bool , error ) {
peerGroupIDs , err := getPeerGroupIDs ( ctx , transaction , accountID , peerID )
2024-11-14 17:33:57 +01:00
if err != nil {
return false , err
}
2024-11-18 13:06:25 +01:00
return areGroupChangesAffectPeers ( ctx , transaction , accountID , peerGroupIDs ) // TODO: use transaction
2024-11-14 17:33:57 +01:00
}
// deletePeers deletes all specified peers and sends updates to the remote peers.
// Returns a slice of functions to save events after successful peer deletion.
func deletePeers ( ctx context . Context , am * DefaultAccountManager , transaction Store , accountID , userID string , peers [ ] * nbpeer . Peer ) ( [ ] func ( ) , error ) {
var peerDeletedEvents [ ] func ( )
for _ , peer := range peers {
if err := am . integratedPeerValidator . PeerDeleted ( ctx , accountID , peer . ID ) ; err != nil {
return nil , err
}
network , err := transaction . GetAccountNetwork ( ctx , LockingStrengthShare , accountID )
if err != nil {
return nil , err
2024-10-23 12:05:02 +02:00
}
2024-11-14 17:33:57 +01:00
if err = transaction . DeletePeer ( ctx , LockingStrengthUpdate , accountID , peer . ID ) ; err != nil {
return nil , err
}
am . peersUpdateManager . SendUpdate ( ctx , peer . ID , & UpdateMessage {
Update : & proto . SyncResponse {
RemotePeers : [ ] * proto . RemotePeerConfig { } ,
RemotePeersIsEmpty : true ,
NetworkMap : & proto . NetworkMap {
Serial : network . CurrentSerial ( ) ,
RemotePeers : [ ] * proto . RemotePeerConfig { } ,
RemotePeersIsEmpty : true ,
FirewallRules : [ ] * proto . FirewallRule { } ,
FirewallRulesIsEmpty : true ,
} ,
} ,
NetworkMap : & NetworkMap { } ,
} )
am . peersUpdateManager . CloseChannel ( ctx , peer . ID )
peerDeletedEvents = append ( peerDeletedEvents , func ( ) {
am . StoreEvent ( ctx , userID , peer . ID , accountID , activity . PeerRemovedByUser , peer . EventMeta ( am . GetDNSDomain ( ) ) )
} )
}
return peerDeletedEvents , nil
}
func ConvertSliceToMap ( existingLabels [ ] string ) map [ string ] struct { } {
labelMap := make ( map [ string ] struct { } , len ( existingLabels ) )
for _ , label := range existingLabels {
labelMap [ label ] = struct { } { }
2024-10-23 12:05:02 +02:00
}
2024-11-14 17:33:57 +01:00
return labelMap
2024-10-23 12:05:02 +02:00
}