mirror of
https://github.com/netbirdio/netbird.git
synced 2025-08-14 09:18:51 +02:00
Add system activity tracking and event store (#636)
This PR adds system activity tracking. The management service records events like add/remove peer, group, rule, route, etc. The activity events are stored in the SQLite event store and can be queried by the HTTP API.
This commit is contained in:
@ -2,7 +2,9 @@ package server
|
||||
|
||||
import (
|
||||
"github.com/google/uuid"
|
||||
"github.com/netbirdio/netbird/management/server/activity"
|
||||
"github.com/netbirdio/netbird/management/server/status"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"hash/fnv"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -107,12 +109,20 @@ func (key *SetupKey) Copy() *SetupKey {
|
||||
}
|
||||
}
|
||||
|
||||
// EventMeta returns activity event meta related to the setup key
|
||||
func (key *SetupKey) EventMeta() map[string]any {
|
||||
return map[string]any{"name": key.Name, "type": key.Type, "key": key.HiddenCopy(1).Key}
|
||||
}
|
||||
|
||||
// HiddenCopy returns a copy of the key with a Key value hidden with "*" and a 5 character prefix.
|
||||
// E.g., "831F6*******************************"
|
||||
func (key *SetupKey) HiddenCopy() *SetupKey {
|
||||
func (key *SetupKey) HiddenCopy(length int) *SetupKey {
|
||||
k := key.Copy()
|
||||
prefix := k.Key[0:5]
|
||||
k.Key = prefix + strings.Repeat("*", utf8.RuneCountInString(key.Key)-len(prefix))
|
||||
if length > utf8.RuneCountInString(key.Key) {
|
||||
length = utf8.RuneCountInString(key.Key) - len(prefix)
|
||||
}
|
||||
k.Key = prefix + strings.Repeat("*", length)
|
||||
return k
|
||||
}
|
||||
|
||||
@ -189,7 +199,7 @@ func Hash(s string) uint32 {
|
||||
// CreateSetupKey generates a new setup key with a given name, type, list of groups IDs to auto-assign to peers registered with this key,
|
||||
// and adds it to the specified account. A list of autoGroups IDs can be empty.
|
||||
func (am *DefaultAccountManager) CreateSetupKey(accountID string, keyName string, keyType SetupKeyType,
|
||||
expiresIn time.Duration, autoGroups []string, usageLimit int) (*SetupKey, error) {
|
||||
expiresIn time.Duration, autoGroups []string, usageLimit int, userID string) (*SetupKey, error) {
|
||||
unlock := am.Store.AcquireAccountLock(accountID)
|
||||
defer unlock()
|
||||
|
||||
@ -211,12 +221,44 @@ func (am *DefaultAccountManager) CreateSetupKey(accountID string, keyName string
|
||||
|
||||
setupKey := GenerateSetupKey(keyName, keyType, keyDuration, autoGroups, usageLimit)
|
||||
account.SetupKeys[setupKey.Key] = setupKey
|
||||
|
||||
err = am.Store.SaveAccount(account)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(status.Internal, "failed adding account key")
|
||||
}
|
||||
|
||||
_, err = am.eventStore.Save(&activity.Event{
|
||||
Timestamp: time.Now(),
|
||||
Activity: activity.SetupKeyCreated,
|
||||
InitiatorID: userID,
|
||||
TargetID: setupKey.Id,
|
||||
AccountID: accountID,
|
||||
Meta: setupKey.EventMeta(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, g := range setupKey.AutoGroups {
|
||||
group := account.GetGroup(g)
|
||||
if group != nil {
|
||||
_, err := am.eventStore.Save(&activity.Event{
|
||||
Timestamp: time.Now(),
|
||||
Activity: activity.GroupAddedToSetupKey,
|
||||
InitiatorID: userID,
|
||||
TargetID: setupKey.Id,
|
||||
AccountID: accountID,
|
||||
Meta: map[string]any{"group": group.Name, "group_id": group.ID, "setupkey": setupKey.Name},
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("failed saving setup key activity event %s: %v",
|
||||
activity.GroupAddedToSetupKey.StringCode(), err)
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
log.Errorf("group %s not found while saving setup key activity event of account %s", g, account.Id)
|
||||
}
|
||||
}
|
||||
|
||||
return setupKey, nil
|
||||
}
|
||||
|
||||
@ -224,7 +266,7 @@ func (am *DefaultAccountManager) CreateSetupKey(accountID string, keyName string
|
||||
// Due to the unique nature of a SetupKey certain properties must not be overwritten
|
||||
// (e.g. the key itself, creation date, ID, etc).
|
||||
// These properties are overwritten: Name, AutoGroups, Revoked. The rest is copied from the existing key.
|
||||
func (am *DefaultAccountManager) SaveSetupKey(accountID string, keyToSave *SetupKey) (*SetupKey, error) {
|
||||
func (am *DefaultAccountManager) SaveSetupKey(accountID string, keyToSave *SetupKey, userID string) (*SetupKey, error) {
|
||||
unlock := am.Store.AcquireAccountLock(accountID)
|
||||
defer unlock()
|
||||
|
||||
@ -261,6 +303,67 @@ func (am *DefaultAccountManager) SaveSetupKey(accountID string, keyToSave *Setup
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !oldKey.Revoked && newKey.Revoked {
|
||||
_, err = am.eventStore.Save(&activity.Event{
|
||||
Timestamp: time.Now(),
|
||||
Activity: activity.SetupKeyRevoked,
|
||||
InitiatorID: userID,
|
||||
TargetID: newKey.Id,
|
||||
AccountID: accountID,
|
||||
Meta: newKey.EventMeta(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
defer func() {
|
||||
addedGroups := difference(newKey.AutoGroups, oldKey.AutoGroups)
|
||||
removedGroups := difference(oldKey.AutoGroups, newKey.AutoGroups)
|
||||
for _, g := range removedGroups {
|
||||
group := account.GetGroup(g)
|
||||
if group != nil {
|
||||
_, err := am.eventStore.Save(&activity.Event{
|
||||
Timestamp: time.Now(),
|
||||
Activity: activity.GroupRemovedFromSetupKey,
|
||||
InitiatorID: userID,
|
||||
TargetID: oldKey.Id,
|
||||
AccountID: accountID,
|
||||
Meta: map[string]any{"group": group.Name, "group_id": group.ID, "setupkey": newKey.Name},
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("failed saving setup key activity event %s: %v",
|
||||
activity.GroupRemovedFromSetupKey.StringCode(), err)
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
log.Errorf("group %s not found while saving setup key activity event of account %s", g, account.Id)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for _, g := range addedGroups {
|
||||
group := account.GetGroup(g)
|
||||
if group != nil {
|
||||
_, err := am.eventStore.Save(&activity.Event{
|
||||
Timestamp: time.Now(),
|
||||
Activity: activity.GroupAddedToSetupKey,
|
||||
InitiatorID: userID,
|
||||
TargetID: oldKey.Id,
|
||||
AccountID: accountID,
|
||||
Meta: map[string]any{"group": group.Name, "group_id": group.ID, "setupkey": newKey.Name},
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("failed saving setup key activity event %s: %v",
|
||||
activity.GroupAddedToSetupKey.StringCode(), err)
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
log.Errorf("group %s not found while saving setup key activity event of account %s", g, account.Id)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return newKey, am.updateAccountPeers(account)
|
||||
}
|
||||
|
||||
@ -282,7 +385,7 @@ func (am *DefaultAccountManager) ListSetupKeys(accountID, userID string) ([]*Set
|
||||
for _, key := range account.SetupKeys {
|
||||
var k *SetupKey
|
||||
if !user.IsAdmin() {
|
||||
k = key.HiddenCopy()
|
||||
k = key.HiddenCopy(999)
|
||||
} else {
|
||||
k = key.Copy()
|
||||
}
|
||||
@ -324,7 +427,7 @@ func (am *DefaultAccountManager) GetSetupKey(accountID, userID, keyID string) (*
|
||||
}
|
||||
|
||||
if !user.IsAdmin() {
|
||||
foundKey = foundKey.HiddenCopy()
|
||||
foundKey = foundKey.HiddenCopy(999)
|
||||
}
|
||||
|
||||
return foundKey, nil
|
||||
|
Reference in New Issue
Block a user