Merge pull request #39 from safing/feature/unidentified-process-and-profile

Add support for unidentified processes and profiles
This commit is contained in:
Patrick Pacher
2020-04-21 10:15:11 +02:00
committed by GitHub
25 changed files with 336 additions and 140 deletions

View File

@@ -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
}
}
}

View File

@@ -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,

View File

@@ -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)

View File

@@ -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))

View File

@@ -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
View 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
}