Send remote agents updates when peer re-authenticates (#737)

When peer login expires, all remote peers are updated to exclude the peer from connecting.
Once a peer re-authenticates, the remote peers are not updated.
This peer fixes the behavior.
This commit is contained in:
Misha Bragin 2023-03-10 17:39:29 +01:00 committed by GitHub
parent 9ce8056b17
commit 221934447e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 55 additions and 94 deletions

View File

@ -786,7 +786,7 @@ func (am *DefaultAccountManager) peerLoginExpirationJob(accountID string) func()
if len(peerIDs) != 0 {
// this will trigger peer disconnect from the management service
am.peersUpdateManager.CloseChannels(peerIDs)
err := am.updateAccountPeers(account)
err = am.updateAccountPeers(account)
if err != nil {
log.Errorf("failed updating account peers while expiring peers for account %s", accountID)
return account.GetNextPeerExpiration()

View File

@ -222,49 +222,6 @@ func (am *DefaultAccountManager) GetPeers(accountID, userID string) ([]*Peer, er
return peers, nil
}
func (am *DefaultAccountManager) markPeerLoginExpired(peer *Peer, account *Account, expired bool) (*Peer, error) {
peer.MarkLoginExpired(expired)
account.UpdatePeer(peer)
err := am.Store.SavePeerStatus(account.Id, peer.ID, *peer.Status)
if err != nil {
return nil, err
}
return peer, nil
}
// MarkPeerLoginExpired when peer login has expired
func (am *DefaultAccountManager) MarkPeerLoginExpired(peerPubKey string, loginExpired bool) error {
account, err := am.Store.GetAccountByPeerPubKey(peerPubKey)
if err != nil {
return err
}
unlock := am.Store.AcquireAccountLock(account.Id)
defer unlock()
// ensure that we consider modification happened meanwhile (because we were outside the account lock when we fetched the account)
account, err = am.Store.GetAccount(account.Id)
if err != nil {
return err
}
peer, err := account.FindPeerByPubKey(peerPubKey)
if err != nil {
return err
}
peer.MarkLoginExpired(loginExpired)
account.UpdatePeer(peer)
err = am.Store.SavePeerStatus(account.Id, peer.ID, *peer.Status)
if err != nil {
return err
}
return nil
}
// MarkPeerConnected marks peer as connected (true) or disconnected (false)
func (am *DefaultAccountManager) MarkPeerConnected(peerPubKey string, connected bool) error {
@ -463,10 +420,6 @@ func (am *DefaultAccountManager) GetPeerByIP(accountID string, peerIP string) (*
return nil, status.Errorf(status.NotFound, "peer with IP %s not found", peerIP)
}
func (am *DefaultAccountManager) getNetworkMap(peer *Peer, account *Account) *NetworkMap {
return account.GetPeerNetworkMap(peer.ID, am.dnsDomain)
}
// GetNetworkMap returns Network map for a given peer (omits original peer from the Peers result)
func (am *DefaultAccountManager) GetNetworkMap(peerID string) (*NetworkMap, error) {
@ -479,8 +432,7 @@ func (am *DefaultAccountManager) GetNetworkMap(peerID string) (*NetworkMap, erro
if peer == nil {
return nil, status.Errorf(status.NotFound, "peer with ID %s not found", peerID)
}
return am.getNetworkMap(peer, account), nil
return account.GetPeerNetworkMap(peer.ID, am.dnsDomain), nil
}
// GetPeerNetwork returns the Network for a given peer
@ -633,33 +585,6 @@ func (am *DefaultAccountManager) AddPeer(setupKey, userID string, peer *Peer) (*
return newPeer, networkMap, nil
}
func (am *DefaultAccountManager) checkPeerLoginExpiration(loginUserID string, peer *Peer, account *Account) error {
if peer.AddedWithSSOLogin() {
expired, expiresIn := peer.LoginExpired(account.Settings.PeerLoginExpiration)
expired = account.Settings.PeerLoginExpirationEnabled && expired
if expired || peer.Status.LoginExpired {
log.Debugf("peer %s login expired", peer.ID)
if loginUserID == "" {
// absence of a user ID indicates that JWT wasn't provided.
_, err := am.markPeerLoginExpired(peer, account, true)
if err != nil {
return err
}
return status.Errorf(status.PermissionDenied,
"peer login has expired %v ago. Please log in once more", expiresIn)
}
// user ID is there meaning that JWT validation passed successfully in the API layer.
if peer.UserID != loginUserID {
log.Warnf("user mismatch when loggin in peer %s: peer user %s, login user %s ", peer.ID, peer.UserID, loginUserID)
return status.Errorf(status.Unauthenticated, "can't login")
}
_ = am.updatePeerLastLogin(peer, account)
}
}
return nil
}
// SyncPeer checks whether peer is eligible for receiving NetworkMap (authenticated) and returns its NetworkMap if eligible
func (am *DefaultAccountManager) SyncPeer(sync PeerSync) (*Peer, *NetworkMap, error) {
account, err := am.Store.GetAccountByPeerPubKey(sync.WireGuardPubKey)
@ -685,13 +610,10 @@ func (am *DefaultAccountManager) SyncPeer(sync PeerSync) (*Peer, *NetworkMap, er
return nil, nil, status.Errorf(status.Unauthenticated, "peer is not registered")
}
err = am.checkPeerLoginExpiration("", peer, account)
if err != nil {
return nil, nil, err
if peerLoginExpired(peer, account) {
return nil, nil, status.Errorf(status.PermissionDenied, "peer login has expired, please log in once more")
}
return peer, am.getNetworkMap(peer, account), nil
return peer, account.GetPeerNetworkMap(peer.ID, am.dnsDomain), nil
}
// LoginPeer logs in or registers a peer.
@ -728,9 +650,16 @@ func (am *DefaultAccountManager) LoginPeer(login PeerLogin) (*Peer, *NetworkMap,
return nil, nil, status.Errorf(status.Unauthenticated, "peer is not registered")
}
err = am.checkPeerLoginExpiration(login.UserID, peer, account)
if err != nil {
return nil, nil, err
updateRemotePeers := false
if peerLoginExpired(peer, account) {
err = checkAuth(login.UserID, peer)
if err != nil {
return nil, nil, 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.
updatePeerLastLogin(peer, account)
updateRemotePeers = true
}
peer = updatePeerMeta(peer, login.Meta, account)
@ -744,18 +673,50 @@ func (am *DefaultAccountManager) LoginPeer(login PeerLogin) (*Peer, *NetworkMap,
if err != nil {
return nil, nil, err
}
networkMap := account.GetPeerNetworkMap(peer.ID, am.dnsDomain)
return peer, networkMap, nil
if updateRemotePeers {
err = am.updateAccountPeers(account)
if err != nil {
return nil, nil, err
}
}
return peer, account.GetPeerNetworkMap(peer.ID, am.dnsDomain), nil
}
func (am *DefaultAccountManager) updatePeerLastLogin(peer *Peer, account *Account) *Peer {
peer.LastLogin = time.Now()
newStatus := peer.Status.Copy()
newStatus.LoginExpired = false
peer.Status = newStatus
func checkAuth(loginUserID string, peer *Peer) error {
if loginUserID == "" {
// absence of a user ID indicates that JWT wasn't provided.
return status.Errorf(status.PermissionDenied, "peer login has expired, please log in once more")
}
if peer.UserID != loginUserID {
log.Warnf("user mismatch when loggin in peer %s: peer user %s, login user %s ", peer.ID, peer.UserID, loginUserID)
return status.Errorf(status.Unauthenticated, "can't login")
}
return nil
}
func peerLoginExpired(peer *Peer, account *Account) bool {
expired, expiresIn := peer.LoginExpired(account.Settings.PeerLoginExpiration)
expired = account.Settings.PeerLoginExpirationEnabled && expired
if expired || peer.Status.LoginExpired {
log.Debugf("peer's %s login expired %v ago", peer.ID, expiresIn)
return true
}
return false
}
func updatePeerLastLogin(peer *Peer, account *Account) {
peer.UpdateLastLogin()
account.UpdatePeer(peer)
return peer
}
// UpdateLastLogin and set login expired false
func (p *Peer) UpdateLastLogin() *Peer {
p.LastLogin = time.Now()
newStatus := p.Status.Copy()
newStatus.LoginExpired = false
p.Status = newStatus
return p
}
func (am *DefaultAccountManager) checkAndUpdatePeerSSHKey(peer *Peer, account *Account, newSSHKey string) (*Peer, error) {