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
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"
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 ) {
account , err := am . Store . GetAccount ( ctx , accountID )
2022-11-05 10:24:50 +01:00
if err != nil {
return nil , err
}
user , err := account . FindUser ( userID )
if err != nil {
return nil , err
}
2024-03-27 18:48:48 +01:00
approvedPeersMap , err := am . GetValidatedPeers ( account )
if err != nil {
return nil , err
}
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-08-07 10:22:12 +02:00
regularUser := ! user . HasAdminPower ( ) && ! user . IsServiceUser
if regularUser && account . Settings . RegularUsersViewBlocked {
2024-03-27 16:11:45 +01:00
return peers , nil
}
2022-11-05 10:24:50 +01:00
for _ , peer := range account . Peers {
2024-08-07 10:22:12 +02:00
if regularUser && 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
}
2022-11-21 17:45:14 +01:00
p := peer . Copy ( )
peers = append ( peers , p )
2023-02-03 10:33:28 +01:00
peersMap [ peer . ID ] = p
2022-11-21 17:45:14 +01:00
}
2024-08-07 10:22:12 +02:00
if ! regularUser {
return peers , nil
}
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
}
}
2023-11-28 13:45:26 +01:00
peers = make ( [ ] * nbpeer . Peer , 0 , len ( peersMap ) )
2022-11-21 17:45:14 +01:00
for _ , peer := range peersMap {
peers = append ( peers , peer )
2022-11-05 10:24:50 +01:00
}
return peers , nil
}
2022-05-21 15:21:39 +02:00
// MarkPeerConnected marks peer as connected (true) or disconnected (false)
2024-07-03 11:33:02 +02:00
func ( am * DefaultAccountManager ) MarkPeerConnected ( ctx context . Context , peerPubKey string , connected bool , realIP net . IP , account * Account ) error {
2022-11-07 12:10:56 +01:00
peer , err := account . FindPeerByPubKey ( peerPubKey )
2021-08-24 11:50:19 +02:00
if err != nil {
2024-11-12 14:19:22 +01:00
return fmt . Errorf ( "failed to find peer by pub key: %w" , err )
2021-08-24 11:50:19 +02:00
}
2024-10-13 14:52:43 +02:00
expired , err := am . updatePeerStatusAndLocation ( ctx , peer , connected , realIP , account )
if err != nil {
2024-11-12 14:19:22 +01:00
return fmt . Errorf ( "failed to update peer status and location: %w" , err )
2024-10-13 14:52:43 +02:00
}
2024-11-12 14:19:22 +01:00
log . WithContext ( ctx ) . Debugf ( "mark peer %s connected: %t" , peer . ID , connected )
2024-10-13 14:52:43 +02:00
if peer . AddedWithSSOLogin ( ) {
if peer . LoginExpirationEnabled && account . Settings . PeerLoginExpirationEnabled {
am . checkAndSchedulePeerLoginExpiration ( ctx , account )
}
if peer . InactivityExpirationEnabled && account . Settings . PeerInactivityExpirationEnabled {
am . checkAndSchedulePeerInactivityExpiration ( ctx , account )
}
}
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-08 16:38:32 +01:00
am . updateAccountPeers ( ctx , account . Id )
2024-10-13 14:52:43 +02:00
}
return nil
}
func ( am * DefaultAccountManager ) updatePeerStatusAndLocation ( ctx context . Context , peer * nbpeer . Peer , connected bool , realIP net . IP , account * Account ) ( 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
if am . geo != nil && realIP != nil {
location , err := am . geo . Lookup ( realIP )
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
err = am . Store . SavePeerLocation ( account . Id , peer )
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
}
}
}
2022-11-07 12:10:56 +01:00
account . UpdatePeer ( peer )
2024-10-13 14:52:43 +02:00
err := am . Store . SavePeerStatus ( account . Id , peer . ID , * newStatus )
2021-08-24 11:50:19 +02:00
if err != nil {
2024-11-12 14:19:22 +01:00
return false , fmt . Errorf ( "failed to save peer status: %w" , 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-07-03 11:33:02 +02:00
account , err := am . Store . GetAccount ( ctx , accountID )
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
}
2023-02-03 10:33:28 +01:00
peer := account . GetPeer ( update . ID )
if peer == nil {
return nil , status . Errorf ( status . NotFound , "peer %s not found" , update . ID )
2022-06-23 17:04:53 +02:00
}
2024-11-07 09:50:13 +01:00
var requiresPeerUpdates bool
update , requiresPeerUpdates , err = am . integratedPeerValidator . ValidatePeer ( ctx , update , peer , userID , accountID , am . GetDNSDomain ( ) , account . GetPeerGroupsList ( peer . ID ) , account . Settings . Extra )
2023-11-28 11:44:08 +01:00
if err != nil {
return nil , err
}
2023-01-25 16:29:59 +01:00
if peer . SSHEnabled != update . SSHEnabled {
peer . SSHEnabled = update . SSHEnabled
event := activity . PeerSSHEnabled
if ! update . SSHEnabled {
event = activity . PeerSSHDisabled
}
2024-07-03 11:33:02 +02:00
am . StoreEvent ( ctx , userID , peer . IP . String ( ) , accountID , event , peer . EventMeta ( am . GetDNSDomain ( ) ) )
2023-01-25 16:29:59 +01:00
}
2022-06-23 17:04:53 +02:00
2024-10-23 12:05:02 +02:00
peerLabelUpdated := peer . Name != update . Name
if peerLabelUpdated {
2023-01-20 10:07:37 +01:00
peer . Name = update . Name
2022-11-07 15:38:21 +01:00
2023-01-20 10:07:37 +01:00
existingLabels := account . getPeerDNSLabels ( )
newLabel , err := getPeerHostLabel ( peer . Name , existingLabels )
if err != nil {
return nil , err
}
2022-11-07 15:38:21 +01:00
2023-01-20 10:07:37 +01:00
peer . DNSLabel = newLabel
2023-01-25 16:29:59 +01:00
2024-07-03 11:33:02 +02:00
am . StoreEvent ( ctx , userID , peer . ID , accountID , activity . PeerRenamed , peer . EventMeta ( am . GetDNSDomain ( ) ) )
2023-01-20 10:07:37 +01:00
}
2022-11-07 15:38:21 +01:00
2023-02-14 10:14:00 +01:00
if peer . LoginExpirationEnabled != update . LoginExpirationEnabled {
2023-02-16 13:03:53 +01:00
if ! peer . AddedWithSSOLogin ( ) {
return nil , status . Errorf ( status . PreconditionFailed , "this peer hasn't been added with the SSO login, therefore the login expiration can't be updated" )
}
2023-02-14 10:14:00 +01:00
peer . LoginExpirationEnabled = update . LoginExpirationEnabled
event := activity . PeerLoginExpirationEnabled
if ! update . LoginExpirationEnabled {
event = activity . PeerLoginExpirationDisabled
}
2024-07-03 11:33:02 +02:00
am . StoreEvent ( ctx , userID , peer . IP . String ( ) , accountID , event , peer . EventMeta ( am . GetDNSDomain ( ) ) )
2023-02-27 16:44:26 +01:00
if peer . AddedWithSSOLogin ( ) && peer . LoginExpirationEnabled && account . Settings . PeerLoginExpirationEnabled {
2024-07-03 11:33:02 +02:00
am . checkAndSchedulePeerLoginExpiration ( ctx , account )
2023-02-27 16:44:26 +01:00
}
2023-02-14 10:14:00 +01:00
}
2024-10-13 14:52:43 +02:00
if peer . InactivityExpirationEnabled != update . InactivityExpirationEnabled {
if ! peer . AddedWithSSOLogin ( ) {
return nil , status . Errorf ( status . PreconditionFailed , "this peer hasn't been added with the SSO login, therefore the login expiration can't be updated" )
}
peer . InactivityExpirationEnabled = update . InactivityExpirationEnabled
event := activity . PeerInactivityExpirationEnabled
if ! update . InactivityExpirationEnabled {
event = activity . PeerInactivityExpirationDisabled
}
am . StoreEvent ( ctx , userID , peer . IP . String ( ) , accountID , event , peer . EventMeta ( am . GetDNSDomain ( ) ) )
if peer . AddedWithSSOLogin ( ) && peer . InactivityExpirationEnabled && account . Settings . PeerInactivityExpirationEnabled {
am . checkAndSchedulePeerInactivityExpiration ( ctx , account )
}
}
2022-11-07 12:10:56 +01:00
account . UpdatePeer ( peer )
2021-08-23 21:43:05 +02:00
2024-07-03 11:33:02 +02:00
err = am . Store . SaveAccount ( ctx , account )
2021-08-23 21:43:05 +02:00
if err != nil {
return nil , err
}
2024-11-07 09:50:13 +01:00
if peerLabelUpdated || requiresPeerUpdates {
2024-11-08 16:38:32 +01:00
am . updateAccountPeers ( ctx , accountID )
2024-10-23 12:05:02 +02:00
}
2021-08-23 21:43:05 +02:00
2022-11-07 12:10:56 +01:00
return peer , nil
2021-08-23 21:43:05 +02:00
}
2023-10-01 19:51:39 +02:00
// deletePeers will delete all specified peers and send updates to the remote peers. Don't call without acquiring account lock
2024-07-03 11:33:02 +02:00
func ( am * DefaultAccountManager ) deletePeers ( ctx context . Context , account * Account , peerIDs [ ] string , userID string ) error {
2021-09-07 18:36:46 +02:00
2023-10-03 16:46:58 +02:00
// the first loop is needed to ensure all peers present under the account before modifying, otherwise
// we might have some inconsistencies
2023-11-28 13:45:26 +01:00
peers := make ( [ ] * nbpeer . Peer , 0 , len ( peerIDs ) )
2023-10-01 19:51:39 +02:00
for _ , peerID := range peerIDs {
2022-01-14 14:34:27 +01:00
2023-10-01 19:51:39 +02:00
peer := account . GetPeer ( peerID )
if peer == nil {
2023-10-03 16:46:58 +02:00
return status . Errorf ( status . NotFound , "peer %s not found" , peerID )
2023-10-01 19:51:39 +02:00
}
2023-10-03 16:46:58 +02:00
peers = append ( peers , peer )
}
2021-09-07 18:36:46 +02:00
2023-10-03 16:46:58 +02:00
// the 2nd loop performs the actual modification
for _ , peer := range peers {
2024-03-27 18:48:48 +01:00
2024-07-03 11:33:02 +02:00
err := am . integratedPeerValidator . PeerDeleted ( ctx , account . Id , peer . ID )
2024-03-27 18:48:48 +01:00
if err != nil {
return err
}
2023-10-03 16:46:58 +02:00
account . DeletePeer ( peer . ID )
2024-07-03 11:33:02 +02:00
am . peersUpdateManager . SendUpdate ( ctx , peer . ID ,
2023-10-01 19:51:39 +02:00
& UpdateMessage {
Update : & proto . SyncResponse {
// fill those field for backward compatibility
RemotePeers : [ ] * proto . RemotePeerConfig { } ,
RemotePeersIsEmpty : true ,
// new field
NetworkMap : & proto . NetworkMap {
Serial : account . Network . CurrentSerial ( ) ,
RemotePeers : [ ] * proto . RemotePeerConfig { } ,
RemotePeersIsEmpty : true ,
FirewallRules : [ ] * proto . FirewallRule { } ,
FirewallRulesIsEmpty : true ,
} ,
} ,
2024-10-23 12:05:02 +02:00
NetworkMap : & NetworkMap { } ,
2023-10-01 19:51:39 +02:00
} )
2024-07-03 11:33:02 +02:00
am . peersUpdateManager . CloseChannel ( ctx , peer . ID )
am . StoreEvent ( ctx , userID , peer . ID , account . Id , activity . PeerRemovedByUser , peer . EventMeta ( am . GetDNSDomain ( ) ) )
2023-10-01 19:51:39 +02:00
}
2023-10-03 16:46:58 +02:00
return 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-07-03 11:33:02 +02:00
account , err := am . Store . GetAccount ( ctx , accountID )
2023-10-01 19:51:39 +02:00
if err != nil {
return err
2021-09-07 18:36:46 +02:00
}
2024-11-08 23:17:01 +01:00
updateAccountPeers , err := am . isPeerInActiveGroup ( ctx , account , peerID )
if err != nil {
return err
}
2024-10-23 12:05:02 +02:00
2024-07-03 11:33:02 +02:00
err = am . deletePeers ( ctx , account , [ ] string { peerID } , userID )
2023-10-03 16:46:58 +02:00
if err != nil {
return err
}
2024-07-03 11:33:02 +02:00
err = am . Store . SaveAccount ( ctx , account )
2023-10-03 16:46:58 +02:00
if err != nil {
return err
}
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-05-31 16:41:12 +02:00
accountID , err = am . Store . GetAccountIDByUserID ( 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-10-23 12:05:02 +02:00
var groupsToAdd [ ] string
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
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-09-16 15:47:03 +02:00
freeIP , err := am . getFreeIP ( ctx , transaction , accountID )
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 )
}
}
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-09-16 15:47:03 +02:00
account , err := am . requestBuffer . GetAccountWithBackpressure ( ctx , accountID )
if err != nil {
2024-11-12 14:19:22 +01:00
return nil , nil , nil , status . NewGetAccountError ( err )
2023-12-04 13:00:13 +01:00
}
2023-12-04 12:49:36 +01:00
2024-11-01 22:09:08 +01:00
allGroup , err := account . GetGroupAll ( )
if err != nil {
return nil , nil , nil , fmt . Errorf ( "error getting all group ID: %w" , err )
}
groupsToAdd = append ( groupsToAdd , allGroup . ID )
2024-11-08 23:17:01 +01:00
newGroupsAffectsPeers , err := areGroupChangesAffectPeers ( ctx , am . Store , accountID , groupsToAdd )
if err != nil {
return nil , nil , nil , err
}
if newGroupsAffectsPeers {
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-03-27 18:48:48 +01:00
approvedPeersMap , err := am . GetValidatedPeers ( account )
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-06-22 16:41:16 +02:00
2024-09-16 15:47:03 +02:00
postureChecks := am . getPeerPostureChecks ( account , newPeer )
2024-08-07 10:52:31 +02:00
customZone := account . GetPeersCustomZone ( ctx , am . dnsDomain )
networkMap := account . GetPeerNetworkMap ( ctx , newPeer . ID , customZone , approvedPeersMap , am . metrics . AccountManagerMetrics ( ) )
2024-06-22 16:41:16 +02:00
return newPeer , networkMap , postureChecks , nil
2021-08-23 21:43:05 +02:00
}
2022-05-23 13:03:57 +02:00
2024-09-16 15:47:03 +02:00
func ( am * DefaultAccountManager ) getFreeIP ( ctx context . Context , store Store , accountID string ) ( net . IP , error ) {
takenIps , err := store . GetTakenIPs ( ctx , LockingStrengthShare , accountID )
if err != nil {
return nil , fmt . Errorf ( "failed to get taken IPs: %w" , err )
}
network , err := store . GetAccountNetwork ( ctx , LockingStrengthUpdate , accountID )
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-07-03 11:33:02 +02:00
func ( am * DefaultAccountManager ) SyncPeer ( ctx context . Context , sync PeerSync , account * Account ) ( * nbpeer . Peer , * NetworkMap , [ ] * posture . Checks , error ) {
2023-03-03 18:35:38 +01:00
peer , err := account . FindPeerByPubKey ( sync . WireGuardPubKey )
if err != nil {
2024-06-22 16:41:16 +02:00
return nil , nil , nil , status . NewPeerNotRegisteredError ( )
2023-03-03 18:35:38 +01:00
}
2024-08-19 12:50:11 +02:00
if peer . UserID != "" {
user , err := account . FindUser ( peer . UserID )
if err != nil {
2024-11-12 14:19:22 +01:00
return nil , nil , nil , fmt . Errorf ( "failed to get user: %w" , err )
2024-08-19 12:50:11 +02:00
}
err = checkIfPeerOwnerIsBlocked ( peer , user )
if err != nil {
return nil , nil , nil , err
}
2023-05-11 18:09:36 +02:00
}
2024-07-03 11:33:02 +02:00
if peerLoginExpired ( ctx , peer , account . Settings ) {
2024-07-29 13:30:27 +02:00
return nil , nil , nil , status . NewPeerLoginExpiredError ( )
2023-02-13 12:21:02 +01:00
}
2024-03-27 18:48:48 +01:00
2024-08-19 12:50:11 +02:00
updated := peer . UpdateMetaIfNew ( sync . Meta )
2024-06-13 13:24:24 +02:00
if updated {
2024-07-26 07:49:05 +02:00
err = am . Store . SavePeer ( ctx , account . Id , peer )
2024-06-13 13:24:24 +02:00
if err != nil {
2024-11-12 14:19:22 +01:00
return nil , nil , nil , fmt . Errorf ( "failed to save peer: %w" , err )
2024-06-13 13:24:24 +02:00
}
if sync . UpdateAccountPeers {
2024-11-08 16:38:32 +01:00
am . updateAccountPeers ( ctx , account . Id )
2024-06-13 13:24:24 +02:00
}
}
2024-07-03 11:33:02 +02:00
peerNotValid , isStatusChanged , err := am . integratedPeerValidator . IsNotValidPeer ( ctx , account . Id , peer , account . GetPeerGroupsList ( peer . ID ) , account . Settings . Extra )
2024-05-24 13:29:25 +02:00
if err != nil {
2024-11-12 14:19:22 +01:00
return nil , nil , nil , fmt . Errorf ( "failed to validate peer: %w" , err )
2024-05-24 13:29:25 +02:00
}
2024-06-22 16:41:16 +02:00
var postureChecks [ ] * posture . Checks
2024-04-11 14:08:03 +02:00
if peerNotValid {
2024-03-27 18:48:48 +01:00
emptyMap := & NetworkMap {
Network : account . Network . Copy ( ) ,
}
2024-06-22 16:41:16 +02:00
return peer , emptyMap , postureChecks , nil
2024-03-27 18:48:48 +01:00
}
if isStatusChanged {
2024-11-08 16:38:32 +01:00
am . updateAccountPeers ( ctx , account . Id )
2024-03-27 18:48:48 +01:00
}
2024-04-11 14:08:03 +02:00
validPeersMap , err := am . GetValidatedPeers ( account )
2024-03-27 18:48:48 +01:00
if err != nil {
2024-11-12 14:19:22 +01:00
return nil , nil , nil , fmt . Errorf ( "failed to get validated peers: %w" , err )
2024-03-27 18:48:48 +01:00
}
2024-06-22 16:41:16 +02:00
postureChecks = am . getPeerPostureChecks ( account , peer )
2024-08-07 10:52:31 +02:00
customZone := account . GetPeersCustomZone ( ctx , am . dnsDomain )
return peer , account . GetPeerNetworkMap ( ctx , peer . ID , customZone , validPeersMap , am . metrics . AccountManagerMetrics ( ) ) , postureChecks , nil
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-09-16 15:47:03 +02:00
peer , err := am . Store . GetPeerByPeerPubKey ( ctx , LockingStrengthUpdate , login . WireGuardPubKey )
2023-03-03 18:35:38 +01:00
if err != nil {
2024-06-22 16:41:16 +02:00
return nil , nil , nil , err
2023-03-03 18:35:38 +01:00
}
2024-09-16 15:47:03 +02:00
settings , err := am . Store . GetAccountSettings ( ctx , LockingStrengthShare , accountID )
2023-05-11 18:09:36 +02:00
if err != nil {
2024-06-22 16:41:16 +02:00
return nil , nil , nil , err
2023-05-11 18:09:36 +02:00
}
2023-05-18 19:31:35 +02:00
// this flag prevents unnecessary calls to the persistent store.
2024-07-29 13:30:27 +02:00
shouldStorePeer := false
2023-03-10 17:39:29 +01:00
updateRemotePeers := false
2024-08-19 12:50:11 +02:00
if login . UserID != "" {
2024-10-07 18:06:26 +02:00
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 nil , nil , nil , status . Errorf ( status . Unauthenticated , "invalid user" )
}
2024-08-19 12:50:11 +02:00
changed , err := am . handleUserPeer ( ctx , peer , settings )
2023-03-10 17:39:29 +01:00
if err != nil {
2024-06-22 16:41:16 +02:00
return nil , nil , nil , err
2023-03-10 17:39:29 +01:00
}
2024-08-19 12:50:11 +02:00
if changed {
shouldStorePeer = true
updateRemotePeers = true
}
2023-03-03 18:35:38 +01:00
}
2024-11-07 22:32:14 +01:00
groups , err := am . Store . GetAccountGroups ( ctx , LockingStrengthShare , accountID )
2024-05-24 13:29:25 +02:00
if err != nil {
2024-06-22 16:41:16 +02:00
return nil , nil , nil , err
2024-05-24 13:29:25 +02:00
}
2024-07-29 13:30:27 +02:00
2024-08-19 12:50:11 +02:00
var grps [ ] string
for _ , group := range groups {
for _ , id := range group . Peers {
if id == peer . ID {
grps = append ( grps , group . ID )
break
}
}
}
isRequiresApproval , isStatusChanged , err := am . integratedPeerValidator . IsNotValidPeer ( ctx , accountID , peer , grps , settings . Extra )
if err != nil {
return nil , nil , nil , err
}
updated := peer . UpdateMetaIfNew ( login . Meta )
2023-05-18 19:31:35 +02:00
if updated {
2024-07-29 13:30:27 +02:00
shouldStorePeer = true
2023-05-18 19:31:35 +02:00
}
2023-03-03 18:35:38 +01:00
2024-07-29 13:30:27 +02:00
if peer . SSHKey != login . SSHKey {
peer . SSHKey = login . SSHKey
shouldStorePeer = true
2023-03-03 18:35:38 +01:00
}
2024-07-29 13:30:27 +02:00
if shouldStorePeer {
err = am . Store . SavePeer ( ctx , accountID , peer )
2023-05-18 19:31:35 +02:00
if err != nil {
2024-06-22 16:41:16 +02:00
return nil , nil , nil , err
2023-05-18 19:31:35 +02:00
}
2023-03-03 18:35:38 +01:00
}
2024-07-29 13:30:27 +02:00
2024-08-19 12:50:11 +02:00
unlockPeer ( )
unlockPeer = nil
2024-08-21 11:44:52 +02:00
account , err := am . requestBuffer . GetAccountWithBackpressure ( ctx , accountID )
2024-08-19 12:50:11 +02:00
if err != nil {
return nil , nil , nil , err
}
2023-05-18 19:31:35 +02:00
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-07-29 13:30:27 +02:00
return am . getValidatedPeerWithMap ( ctx , isRequiresApproval , account , peer )
}
// 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
}
func ( am * DefaultAccountManager ) getValidatedPeerWithMap ( ctx context . Context , isRequiresApproval bool , account * Account , peer * nbpeer . Peer ) ( * nbpeer . Peer , * NetworkMap , [ ] * posture . Checks , error ) {
2024-06-22 16:41:16 +02:00
var postureChecks [ ] * posture . Checks
2024-03-27 18:48:48 +01:00
if isRequiresApproval {
emptyMap := & NetworkMap {
Network : account . Network . Copy ( ) ,
}
2024-07-29 13:30:27 +02:00
return peer , emptyMap , nil , nil
2024-03-27 18:48:48 +01:00
}
approvedPeersMap , err := am . GetValidatedPeers ( account )
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-06-22 16:41:16 +02:00
postureChecks = am . getPeerPostureChecks ( account , peer )
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-08-19 12:50:11 +02:00
func ( am * DefaultAccountManager ) handleExpiredPeer ( ctx context . Context , user * User , peer * nbpeer . Peer ) error {
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 ( )
err = am . Store . SavePeer ( ctx , 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-09-16 15:47:03 +02:00
err = am . Store . 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-07-31 14:53:32 +02:00
unlock := am . Store . AcquireWriteLockByUID ( ctx , accountID )
2023-02-07 20:11:08 +01:00
defer unlock ( )
2024-07-03 11:33:02 +02:00
account , err := am . Store . GetAccount ( ctx , accountID )
2023-02-07 20:11:08 +01:00
if err != nil {
return nil , err
}
user , err := account . FindUser ( userID )
if err != nil {
return nil , err
}
2024-03-27 16:11:45 +01:00
if ! user . HasAdminPower ( ) && ! user . IsServiceUser && account . Settings . RegularUsersViewBlocked {
return nil , status . Errorf ( status . Internal , "user %s has no access to his own peer %s under account %s" , userID , peerID , accountID )
}
2023-02-07 20:11:08 +01:00
peer := account . GetPeer ( peerID )
if peer == nil {
return nil , status . Errorf ( status . NotFound , "peer with %s not found under account %s" , peerID , accountID )
}
// if admin or user owns this peer, return peer
2024-01-25 09:50:27 +01:00
if user . HasAdminPower ( ) || user . IsServiceUser || 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.
userPeers , err := account . FindUserPeers ( userID )
if err != nil {
return nil , err
}
2024-03-27 18:48:48 +01:00
approvedPeersMap , err := am . GetValidatedPeers ( account )
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 {
log . WithContext ( ctx ) . Errorf ( "failed to send out updates to peers: %v" , err )
return
}
2022-11-07 12:10:56 +01:00
peers := account . GetPeers ( )
2022-06-24 21:30:51 +02:00
2024-03-27 18:48:48 +01:00
approvedPeersMap , err := am . GetValidatedPeers ( account )
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 } ( )
postureChecks := am . getPeerPostureChecks ( account , p )
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
func ConvertSliceToMap ( existingLabels [ ] string ) map [ string ] struct { } {
labelMap := make ( map [ string ] struct { } , len ( existingLabels ) )
for _ , label := range existingLabels {
labelMap [ label ] = struct { } { }
}
return labelMap
}
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-08 23:17:01 +01:00
func ( am * DefaultAccountManager ) isPeerInActiveGroup ( ctx context . Context , account * Account , peerID string ) ( bool , error ) {
2024-10-23 12:05:02 +02:00
peerGroupIDs := make ( [ ] string , 0 )
for _ , group := range account . Groups {
if slices . Contains ( group . Peers , peerID ) {
peerGroupIDs = append ( peerGroupIDs , group . ID )
}
}
2024-11-08 23:17:01 +01:00
return areGroupChangesAffectPeers ( ctx , am . Store , account . Id , peerGroupIDs )
2024-10-23 12:05:02 +02:00
}