2021-07-30 17:46:38 +02:00
package server
2021-07-18 20:51:09 +02:00
import (
2024-07-03 11:33:02 +02:00
"context"
2021-07-18 20:51:09 +02:00
"os"
"path/filepath"
"strings"
"sync"
2022-11-07 17:52:23 +01:00
"time"
2021-07-18 20:51:09 +02:00
2024-09-27 16:10:50 +02:00
"github.com/rs/xid"
log "github.com/sirupsen/logrus"
2023-03-16 15:57:44 +01:00
2024-10-03 15:50:35 +02:00
nbgroup "github.com/netbirdio/netbird/management/server/group"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
"github.com/netbirdio/netbird/management/server/telemetry"
2022-03-26 12:08:54 +01:00
"github.com/netbirdio/netbird/util"
2021-07-18 20:51:09 +02:00
)
// storeFileName Store file name. Stored in the datadir
const storeFileName = "store.json"
2021-07-18 21:00:32 +02:00
// FileStore represents an account storage backed by a file persisted to disk
2021-07-18 20:51:09 +02:00
type FileStore struct {
2022-03-01 15:22:18 +01:00
Accounts map [ string ] * Account
2022-11-07 12:10:56 +01:00
SetupKeyID2AccountID map [ string ] string ` json:"-" `
PeerKeyID2AccountID map [ string ] string ` json:"-" `
2023-02-03 10:33:28 +01:00
PeerID2AccountID map [ string ] string ` json:"-" `
2022-11-07 12:10:56 +01:00
UserID2AccountID map [ string ] string ` json:"-" `
PrivateDomain2AccountID map [ string ] string ` json:"-" `
2023-03-16 15:57:44 +01:00
HashedPAT2TokenID map [ string ] string ` json:"-" `
TokenID2UserID map [ string ] string ` json:"-" `
2022-10-16 13:33:46 +02:00
InstallationID string
2021-07-18 20:51:09 +02:00
// mutex to synchronise Store read/write operations
mux sync . Mutex ` json:"-" `
storeFile string ` json:"-" `
2022-11-07 17:52:23 +01:00
2023-05-19 11:42:25 +02:00
metrics telemetry . AppMetrics ` json:"-" `
2021-07-18 20:51:09 +02:00
}
2022-11-08 10:46:12 +01:00
// NewFileStore restores a store from the file located in the datadir
2024-07-03 11:33:02 +02:00
func NewFileStore ( ctx context . Context , dataDir string , metrics telemetry . AppMetrics ) ( * FileStore , error ) {
fs , err := restore ( ctx , filepath . Join ( dataDir , storeFileName ) )
2023-05-19 11:42:25 +02:00
if err != nil {
return nil , err
}
fs . metrics = metrics
return fs , nil
2021-07-18 20:51:09 +02:00
}
2022-11-07 12:10:56 +01:00
// restore the state of the store from the file.
2021-07-18 20:51:09 +02:00
// Creates a new empty store file if doesn't exist
2024-07-03 11:33:02 +02:00
func restore ( ctx context . Context , file string ) ( * FileStore , error ) {
2021-07-18 20:51:09 +02:00
if _ , err := os . Stat ( file ) ; os . IsNotExist ( err ) {
// create a new FileStore if previously didn't exist (e.g. first run)
s := & FileStore {
2022-03-01 15:22:18 +01:00
Accounts : make ( map [ string ] * Account ) ,
mux : sync . Mutex { } ,
2022-11-07 12:10:56 +01:00
SetupKeyID2AccountID : make ( map [ string ] string ) ,
PeerKeyID2AccountID : make ( map [ string ] string ) ,
UserID2AccountID : make ( map [ string ] string ) ,
PrivateDomain2AccountID : make ( map [ string ] string ) ,
2023-02-03 10:33:28 +01:00
PeerID2AccountID : make ( map [ string ] string ) ,
2023-03-16 15:57:44 +01:00
HashedPAT2TokenID : make ( map [ string ] string ) ,
TokenID2UserID : make ( map [ string ] string ) ,
2022-03-01 15:22:18 +01:00
storeFile : file ,
2021-07-18 20:51:09 +02:00
}
2024-07-03 11:33:02 +02:00
err = s . persist ( ctx , file )
2021-07-18 20:51:09 +02:00
if err != nil {
return nil , err
}
return s , nil
}
read , err := util . ReadJson ( file , & FileStore { } )
if err != nil {
return nil , err
}
store := read . ( * FileStore )
store . storeFile = file
2022-11-07 12:10:56 +01:00
store . SetupKeyID2AccountID = make ( map [ string ] string )
store . PeerKeyID2AccountID = make ( map [ string ] string )
store . UserID2AccountID = make ( map [ string ] string )
store . PrivateDomain2AccountID = make ( map [ string ] string )
2023-02-03 10:33:28 +01:00
store . PeerID2AccountID = make ( map [ string ] string )
2023-03-16 15:57:44 +01:00
store . HashedPAT2TokenID = make ( map [ string ] string )
store . TokenID2UserID = make ( map [ string ] string )
2022-11-07 12:10:56 +01:00
for accountID , account := range store . Accounts {
2023-02-13 15:07:15 +01:00
if account . Settings == nil {
account . Settings = & Settings {
PeerLoginExpirationEnabled : false ,
PeerLoginExpiration : DefaultPeerLoginExpiration ,
2024-10-13 14:52:43 +02:00
PeerInactivityExpirationEnabled : false ,
PeerInactivityExpiration : DefaultPeerInactivityExpiration ,
2023-02-13 15:07:15 +01:00
}
2023-02-13 12:21:02 +01:00
}
2021-07-18 20:51:09 +02:00
for setupKeyId := range account . SetupKeys {
2022-11-07 12:10:56 +01:00
store . SetupKeyID2AccountID [ strings . ToUpper ( setupKeyId ) ] = accountID
2022-05-21 15:21:39 +02:00
}
2022-11-07 12:10:56 +01:00
2021-07-30 17:46:38 +02:00
for _ , peer := range account . Peers {
2022-11-07 12:10:56 +01:00
store . PeerKeyID2AccountID [ peer . Key ] = accountID
2023-02-03 10:33:28 +01:00
store . PeerID2AccountID [ peer . ID ] = accountID
2021-07-22 10:28:00 +02:00
}
2021-12-27 13:17:15 +01:00
for _ , user := range account . Users {
2022-11-07 12:10:56 +01:00
store . UserID2AccountID [ user . Id ] = accountID
2023-11-01 11:04:17 +01:00
if user . Issued == "" {
user . Issued = UserIssuedAPI
account . Users [ user . Id ] = user
}
2023-03-16 15:57:44 +01:00
for _ , pat := range user . PATs {
store . TokenID2UserID [ pat . ID ] = user . Id
2023-03-29 15:21:53 +02:00
store . HashedPAT2TokenID [ pat . HashedToken ] = pat . ID
2023-03-16 15:57:44 +01:00
}
2022-08-18 18:22:15 +02:00
}
2022-11-07 12:10:56 +01:00
2022-05-21 15:21:39 +02:00
if account . Domain != "" && account . DomainCategory == PrivateCategory &&
account . IsDomainPrimaryAccount {
2022-11-07 12:10:56 +01:00
store . PrivateDomain2AccountID [ account . Domain ] = accountID
2022-03-01 15:22:18 +01:00
}
2022-11-08 10:31:34 +01:00
2023-05-29 16:00:18 +02:00
// TODO: delete this block after migration
2023-04-01 12:02:08 +02:00
policies := make ( map [ string ] int , len ( account . Policies ) )
for i , policy := range account . Policies {
policies [ policy . ID ] = i
2023-05-29 16:00:18 +02:00
policy . UpgradeAndFix ( )
2023-04-01 12:02:08 +02:00
}
if account . Policies == nil {
2023-03-13 15:14:18 +01:00
account . Policies = make ( [ ] * Policy , 0 )
2023-04-01 12:02:08 +02:00
}
2023-03-13 15:14:18 +01:00
2022-11-08 10:31:34 +01:00
// for data migration. Can be removed once most base will be with labels
existingLabels := account . getPeerDNSLabels ( )
if len ( existingLabels ) != len ( account . Peers ) {
2024-07-03 11:33:02 +02:00
addPeerLabelsToAccount ( ctx , account , existingLabels )
2022-11-08 10:31:34 +01:00
}
2022-12-06 10:11:57 +01:00
2023-06-27 16:51:05 +02:00
// TODO: delete this block after migration
// Set API as issuer for groups which has not this field
for _ , group := range account . Groups {
if group . Issued == "" {
2024-03-27 18:48:48 +01:00
group . Issued = nbgroup . GroupIssuedAPI
2023-06-27 16:51:05 +02:00
}
}
2022-12-06 10:11:57 +01:00
allGroup , err := account . GetGroupAll ( )
if err != nil {
2024-07-03 11:33:02 +02:00
log . WithContext ( ctx ) . Errorf ( "unable to find the All group, this should happen only when migrate from a version that didn't support groups. Error: %v" , err )
2022-12-06 10:11:57 +01:00
// if the All group didn't exist we probably don't have routes to update
continue
}
for _ , route := range account . Routes {
if len ( route . Groups ) == 0 {
route . Groups = [ ] string { allGroup . ID }
}
}
2023-02-03 10:33:28 +01:00
// migration to Peer.ID from Peer.Key.
// Old peers that require migration have an empty Peer.ID in the store.json.
// Generate new ID with xid for these peers.
// Set the Peer.ID to the newly generated value.
// Replace all the mentions of Peer.Key as ID (groups and routes).
// Swap Peer.Key with Peer.ID in the Account.Peers map.
2023-11-28 13:45:26 +01:00
migrationPeers := make ( map [ string ] * nbpeer . Peer ) // key to Peer
2023-02-03 10:33:28 +01:00
for key , peer := range account . Peers {
2023-03-09 11:24:42 +01:00
// set LastLogin for the peers that were onboarded before the peer login expiration feature
if peer . LastLogin . IsZero ( ) {
2023-04-03 15:09:35 +02:00
peer . LastLogin = time . Now ( ) . UTC ( )
2023-03-09 11:24:42 +01:00
}
2023-02-03 10:33:28 +01:00
if peer . ID != "" {
continue
}
id := xid . New ( ) . String ( )
peer . ID = id
migrationPeers [ key ] = peer
}
if len ( migrationPeers ) > 0 {
// swap Peer.Key with Peer.ID in the Account.Peers map.
for key , peer := range migrationPeers {
delete ( account . Peers , key )
account . Peers [ peer . ID ] = peer
store . PeerID2AccountID [ peer . ID ] = accountID
}
// detect groups that have Peer.Key as a reference and replace it with ID.
for _ , group := range account . Groups {
for i , peer := range group . Peers {
if p , ok := migrationPeers [ peer ] ; ok {
group . Peers [ i ] = p . ID
}
}
}
2023-03-13 15:14:18 +01:00
2023-02-03 10:33:28 +01:00
// detect routes that have Peer.Key as a reference and replace it with ID.
for _ , route := range account . Routes {
if peer , ok := migrationPeers [ route . Peer ] ; ok {
route . Peer = peer . ID
}
}
}
2021-07-22 10:28:00 +02:00
}
2021-07-18 20:51:09 +02:00
2022-11-07 12:10:56 +01:00
// we need this persist to apply changes we made to account.Peers (we set them to Disconnected)
2024-07-03 11:33:02 +02:00
err = store . persist ( ctx , store . storeFile )
2021-08-23 21:43:05 +02:00
if err != nil {
return nil , err
}
2022-11-07 12:10:56 +01:00
return store , nil
2021-08-23 21:43:05 +02:00
}
2022-11-07 12:10:56 +01:00
// persist account data to a file
// It is recommended to call it with locking FileStore.mux
2024-07-03 11:33:02 +02:00
func ( s * FileStore ) persist ( ctx context . Context , file string ) error {
2023-05-19 11:42:25 +02:00
start := time . Now ( )
err := util . WriteJson ( file , s )
if err != nil {
return err
}
took := time . Since ( start )
if s . metrics != nil {
s . metrics . StoreMetrics ( ) . CountPersistenceDuration ( took )
}
2024-07-03 11:33:02 +02:00
log . WithContext ( ctx ) . Debugf ( "took %d ms to persist the FileStore" , took . Milliseconds ( ) )
2023-05-19 11:42:25 +02:00
return nil
2021-07-18 20:51:09 +02:00
}
2022-08-18 18:22:15 +02:00
// GetAllAccounts returns all accounts
2024-07-03 11:33:02 +02:00
func ( s * FileStore ) GetAllAccounts ( _ context . Context ) ( all [ ] * Account ) {
2022-10-16 13:33:46 +02:00
s . mux . Lock ( )
defer s . mux . Unlock ( )
2022-05-21 15:21:39 +02:00
for _ , a := range s . Accounts {
2022-10-16 13:33:46 +02:00
all = append ( all , a . Copy ( ) )
2022-05-21 15:21:39 +02:00
}
return all
}
2022-11-08 10:46:12 +01:00
// Close the FileStore persisting data to disk
2024-07-03 11:33:02 +02:00
func ( s * FileStore ) Close ( ctx context . Context ) error {
2022-10-16 13:33:46 +02:00
s . mux . Lock ( )
defer s . mux . Unlock ( )
2024-07-03 11:33:02 +02:00
log . WithContext ( ctx ) . Infof ( "closing FileStore" )
2022-10-16 13:33:46 +02:00
2024-07-03 11:33:02 +02:00
return s . persist ( ctx , s . storeFile )
2022-10-16 13:33:46 +02:00
}
2023-10-12 15:42:36 +02:00
2023-10-16 11:19:39 +02:00
// GetStoreEngine returns FileStoreEngine
func ( s * FileStore ) GetStoreEngine ( ) StoreEngine {
return FileStoreEngine
2023-10-12 15:42:36 +02:00
}