Merge pull request #39 from safing/feature/unidentified-process-and-profile
Add support for unidentified processes and profiles
This commit is contained in:
@@ -1,7 +1,14 @@
|
||||
package profile
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
activeProfileCleanerTickDuration = 10 * time.Minute
|
||||
activeProfileCleanerThreshold = 1 * time.Hour
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -38,7 +45,34 @@ func markActiveProfileAsOutdated(scopedID string) {
|
||||
|
||||
profile, ok := activeProfiles[scopedID]
|
||||
if ok {
|
||||
profile.oudated.Set()
|
||||
profile.outdated.Set()
|
||||
delete(activeProfiles, scopedID)
|
||||
}
|
||||
}
|
||||
|
||||
func cleanActiveProfiles(ctx context.Context) error {
|
||||
for {
|
||||
select {
|
||||
case <-time.After(activeProfileCleanerTickDuration):
|
||||
|
||||
threshold := time.Now().Add(-activeProfileCleanerThreshold)
|
||||
|
||||
activeProfilesLock.Lock()
|
||||
for id, profile := range activeProfiles {
|
||||
// get last used
|
||||
profile.Lock()
|
||||
lastUsed := profile.lastUsed
|
||||
profile.Unlock()
|
||||
// remove if not used for a while
|
||||
if lastUsed.Before(threshold) {
|
||||
profile.outdated.Set()
|
||||
delete(activeProfiles, id)
|
||||
}
|
||||
}
|
||||
activeProfilesLock.Unlock()
|
||||
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,8 +70,8 @@ func updateGlobalConfigProfile(ctx context.Context, data interface{}) error {
|
||||
|
||||
// build global profile for reference
|
||||
profile := &Profile{
|
||||
ID: "config",
|
||||
Source: SourceGlobal,
|
||||
ID: "global-config",
|
||||
Source: SourceSpecial,
|
||||
Name: "Global Configuration",
|
||||
Config: make(map[string]interface{}),
|
||||
internalSave: true,
|
||||
|
||||
@@ -42,6 +42,8 @@ func start() error {
|
||||
return err
|
||||
}
|
||||
|
||||
module.StartServiceWorker("clean active profiles", 0, cleanActiveProfiles)
|
||||
|
||||
err = updateGlobalConfigProfile(module.Ctx, nil)
|
||||
if err != nil {
|
||||
log.Warningf("profile: error during loading global profile from configuration: %s", err)
|
||||
|
||||
@@ -128,7 +128,7 @@ func (lp *LayeredProfile) Update() (revisionCounter uint64) {
|
||||
|
||||
var changed bool
|
||||
for i, layer := range lp.layers {
|
||||
if layer.oudated.IsSet() {
|
||||
if layer.outdated.IsSet() {
|
||||
changed = true
|
||||
// update layer
|
||||
newLayer, err := GetProfile(layer.Source, layer.ID)
|
||||
@@ -175,6 +175,11 @@ func (lp *LayeredProfile) updateCaches() {
|
||||
// TODO: ignore community profiles
|
||||
}
|
||||
|
||||
// MarkUsed marks the localProfile as used.
|
||||
func (lp *LayeredProfile) MarkUsed() {
|
||||
lp.localProfile.MarkUsed()
|
||||
}
|
||||
|
||||
// SecurityLevel returns the highest security level of all layered profiles.
|
||||
func (lp *LayeredProfile) SecurityLevel() uint8 {
|
||||
return uint8(atomic.LoadUint32(lp.securityLevel))
|
||||
|
||||
@@ -19,15 +19,15 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
lastUsedUpdateThreshold = 1 * time.Hour
|
||||
lastUsedUpdateThreshold = 24 * time.Hour
|
||||
)
|
||||
|
||||
// Profile Sources
|
||||
const (
|
||||
SourceLocal string = "local"
|
||||
SourceLocal string = "local" // local, editable
|
||||
SourceSpecial string = "special" // specials (read-only)
|
||||
SourceCommunity string = "community"
|
||||
SourceEnterprise string = "enterprise"
|
||||
SourceGlobal string = "global"
|
||||
)
|
||||
|
||||
// Default Action IDs
|
||||
@@ -77,7 +77,8 @@ type Profile struct { //nolint:maligned // not worth the effort
|
||||
filterListIDs []string
|
||||
|
||||
// Lifecycle Management
|
||||
oudated *abool.AtomicBool
|
||||
outdated *abool.AtomicBool
|
||||
lastUsed time.Time
|
||||
|
||||
// Framework
|
||||
// If a Profile is declared as a Framework (i.e. an Interpreter and the likes), then the real process/actor must be found
|
||||
@@ -94,7 +95,7 @@ type Profile struct { //nolint:maligned // not worth the effort
|
||||
func (profile *Profile) prepConfig() (err error) {
|
||||
// prepare configuration
|
||||
profile.configPerspective, err = config.NewPerspective(profile.Config)
|
||||
profile.oudated = abool.New()
|
||||
profile.outdated = abool.New()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -156,10 +157,11 @@ func (profile *Profile) parseConfig() error {
|
||||
// New returns a new Profile.
|
||||
func New() *Profile {
|
||||
profile := &Profile{
|
||||
ID: uuid.NewV4().String(),
|
||||
Source: SourceLocal,
|
||||
Created: time.Now().Unix(),
|
||||
Config: make(map[string]interface{}),
|
||||
ID: uuid.NewV4().String(),
|
||||
Source: SourceLocal,
|
||||
Created: time.Now().Unix(),
|
||||
Config: make(map[string]interface{}),
|
||||
internalSave: true,
|
||||
}
|
||||
|
||||
// create placeholders
|
||||
@@ -190,13 +192,26 @@ func (profile *Profile) Save() error {
|
||||
return profileDB.Put(profile)
|
||||
}
|
||||
|
||||
// MarkUsed marks the profile as used, eventually.
|
||||
func (profile *Profile) MarkUsed() (updated bool) {
|
||||
// MarkUsed marks the profile as used and saves it when it has changed.
|
||||
func (profile *Profile) MarkUsed() {
|
||||
profile.Lock()
|
||||
// lastUsed
|
||||
profile.lastUsed = time.Now()
|
||||
|
||||
// ApproxLastUsed
|
||||
save := false
|
||||
if time.Now().Add(-lastUsedUpdateThreshold).Unix() > profile.ApproxLastUsed {
|
||||
profile.ApproxLastUsed = time.Now().Unix()
|
||||
return true
|
||||
save = true
|
||||
}
|
||||
profile.Unlock()
|
||||
|
||||
if save {
|
||||
err := profile.Save()
|
||||
if err != nil {
|
||||
log.Warningf("profiles: failed to save profile %s after marking as used: %s", profile.ScopedID(), err)
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// String returns a string representation of the Profile.
|
||||
@@ -224,8 +239,6 @@ func (profile *Profile) addEndpointyEntry(cfgKey, newEntry string) {
|
||||
endpointList = append(endpointList, newEntry)
|
||||
profile.Config[cfgKey] = endpointList
|
||||
|
||||
// save without full reload
|
||||
profile.internalSave = true
|
||||
profile.Unlock()
|
||||
err := profile.Save()
|
||||
if err != nil {
|
||||
@@ -233,10 +246,13 @@ func (profile *Profile) addEndpointyEntry(cfgKey, newEntry string) {
|
||||
}
|
||||
|
||||
// reload manually
|
||||
profile.Lock()
|
||||
profile.dataParsed = false
|
||||
err = profile.parseConfig()
|
||||
if err != nil {
|
||||
log.Warningf("profile: failed to parse profile config after adding endpoint: %s", err)
|
||||
}
|
||||
profile.Unlock()
|
||||
}
|
||||
|
||||
// GetProfile loads a profile from the database.
|
||||
@@ -249,6 +265,7 @@ func GetProfileByScopedID(scopedID string) (*Profile, error) {
|
||||
// check cache
|
||||
profile := getActiveProfile(scopedID)
|
||||
if profile != nil {
|
||||
profile.MarkUsed()
|
||||
return profile, nil
|
||||
}
|
||||
|
||||
@@ -266,7 +283,6 @@ func GetProfileByScopedID(scopedID string) (*Profile, error) {
|
||||
|
||||
// lock for prepping
|
||||
profile.Lock()
|
||||
defer profile.Unlock()
|
||||
|
||||
// prepare config
|
||||
err = profile.prepConfig()
|
||||
@@ -280,7 +296,13 @@ func GetProfileByScopedID(scopedID string) (*Profile, error) {
|
||||
log.Warningf("profiles: profile %s has (partly) invalid configuration: %s", profile.ID, err)
|
||||
}
|
||||
|
||||
// mark as internal
|
||||
profile.internalSave = true
|
||||
|
||||
profile.Unlock()
|
||||
|
||||
// mark active
|
||||
profile.MarkUsed()
|
||||
markProfileActive(profile)
|
||||
|
||||
return profile, nil
|
||||
|
||||
56
profile/special.go
Normal file
56
profile/special.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package profile
|
||||
|
||||
import (
|
||||
"github.com/safing/portbase/log"
|
||||
)
|
||||
|
||||
const (
|
||||
unidentifiedProfileID = "_unidentified"
|
||||
systemProfileID = "_system"
|
||||
)
|
||||
|
||||
// GetUnidentifiedProfile returns the special profile assigned to unidentified processes.
|
||||
func GetUnidentifiedProfile() *Profile {
|
||||
// get profile
|
||||
profile, err := GetProfile(SourceLocal, unidentifiedProfileID)
|
||||
if err == nil {
|
||||
return profile
|
||||
}
|
||||
|
||||
// create if not available (or error)
|
||||
profile = New()
|
||||
profile.Name = "Unidentified Processes"
|
||||
profile.Source = SourceLocal
|
||||
profile.ID = unidentifiedProfileID
|
||||
|
||||
// save to db
|
||||
err = profile.Save()
|
||||
if err != nil {
|
||||
log.Warningf("profiles: failed to save %s: %s", profile.ScopedID(), err)
|
||||
}
|
||||
|
||||
return profile
|
||||
}
|
||||
|
||||
// GetSystemProfile returns the special profile used for the Kernel.
|
||||
func GetSystemProfile() *Profile {
|
||||
// get profile
|
||||
profile, err := GetProfile(SourceLocal, systemProfileID)
|
||||
if err == nil {
|
||||
return profile
|
||||
}
|
||||
|
||||
// create if not available (or error)
|
||||
profile = New()
|
||||
profile.Name = "Operating System"
|
||||
profile.Source = SourceLocal
|
||||
profile.ID = systemProfileID
|
||||
|
||||
// save to db
|
||||
err = profile.Save()
|
||||
if err != nil {
|
||||
log.Warningf("profiles: failed to save %s: %s", profile.ScopedID(), err)
|
||||
}
|
||||
|
||||
return profile
|
||||
}
|
||||
Reference in New Issue
Block a user