Merge pull request #251 from safing/feature/minor-improvements-1

Improve internal profile handling
This commit is contained in:
Daniel 2021-02-11 15:59:22 +01:00 committed by GitHub
commit 087b8de216
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 223 additions and 68 deletions

View file

@ -228,6 +228,12 @@ func NewConnectionFromDNSRequest(ctx context.Context, fqdn string, cnames []stri
Started: timestamp, Started: timestamp,
Ended: timestamp, Ended: timestamp,
} }
// Inherit internal status of profile.
if localProfile := proc.Profile().LocalProfile(); localProfile != nil {
dnsConn.Internal = localProfile.Internal
}
return dnsConn return dnsConn
} }
@ -238,7 +244,7 @@ func NewConnectionFromExternalDNSRequest(ctx context.Context, fqdn string, cname
} }
timestamp := time.Now().Unix() timestamp := time.Now().Unix()
return &Connection{ dnsConn := &Connection{
Scope: fqdn, Scope: fqdn,
Entity: &intel.Entity{ Entity: &intel.Entity{
Domain: fqdn, Domain: fqdn,
@ -248,7 +254,14 @@ func NewConnectionFromExternalDNSRequest(ctx context.Context, fqdn string, cname
ProcessContext: getProcessContext(ctx, remoteHost), ProcessContext: getProcessContext(ctx, remoteHost),
Started: timestamp, Started: timestamp,
Ended: timestamp, Ended: timestamp,
}, nil }
// Inherit internal status of profile.
if localProfile := remoteHost.Profile().LocalProfile(); localProfile != nil {
dnsConn.Internal = localProfile.Internal
}
return dnsConn, nil
} }
// NewConnectionFromFirstPacket returns a new connection based on the given packet. // NewConnectionFromFirstPacket returns a new connection based on the given packet.
@ -335,7 +348,8 @@ func NewConnectionFromFirstPacket(pkt packet.Packet) *Connection {
} }
} }
return &Connection{ // Create new connection object.
newConn := &Connection{
ID: pkt.GetConnectionID(), ID: pkt.GetConnectionID(),
Scope: scope, Scope: scope,
IPVersion: pkt.Info().Version, IPVersion: pkt.Info().Version,
@ -352,6 +366,13 @@ func NewConnectionFromFirstPacket(pkt packet.Packet) *Connection {
Started: time.Now().Unix(), Started: time.Now().Unix(),
ProfileRevisionCounter: proc.Profile().RevisionCnt(), ProfileRevisionCounter: proc.Profile().RevisionCnt(),
} }
// Inherit internal status of profile.
if localProfile := proc.Profile().LocalProfile(); localProfile != nil {
newConn.Internal = localProfile.Internal
}
return newConn
} }
// GetConnection fetches a Connection from the database. // GetConnection fetches a Connection from the database.

View file

@ -1,17 +1,32 @@
package process package process
import ( import (
"os"
"github.com/safing/portbase/log"
"github.com/safing/portbase/modules" "github.com/safing/portbase/modules"
"github.com/safing/portmaster/updates"
) )
var ( var (
module *modules.Module module *modules.Module
updatesPath string
) )
func init() { func init() {
module = modules.Register("processes", prep, nil, nil, "profiles") module = modules.Register("processes", prep, start, nil, "profiles", "updates")
} }
func prep() error { func prep() error {
return registerConfiguration() return registerConfiguration()
} }
func start() error {
updatesPath = updates.RootPath()
if updatesPath != "" {
updatesPath += string(os.PathSeparator)
}
log.Warningf("process: using updates path %s", updatesPath)
return nil
}

View file

@ -3,6 +3,7 @@ package process
import ( import (
"context" "context"
"os" "os"
"strings"
"github.com/safing/portbase/log" "github.com/safing/portbase/log"
"github.com/safing/portmaster/profile" "github.com/safing/portmaster/profile"
@ -38,6 +39,21 @@ func (p *Process) GetProfile(ctx context.Context) (changed bool, err error) {
profileID = profile.SystemProfileID profileID = profile.SystemProfileID
case ownPID: case ownPID:
profileID = profile.PortmasterProfileID profileID = profile.PortmasterProfileID
default:
// Check if this is another Portmaster component.
if updatesPath != "" && strings.HasPrefix(p.Path, updatesPath) {
switch {
case strings.Contains(p.Path, "portmaster-app"):
profileID = profile.PortmasterAppProfileID
case strings.Contains(p.Path, "portmaster-notifier"):
profileID = profile.PortmasterNotifierProfileID
default:
// Unexpected binary from within the Portmaster updates directpry.
log.Warningf("process: unexpected binary in the updates directory: %s", p.Path)
// TODO: Assign a fully restricted profile in the future when we are
// sure that we won't kill any of our own things.
}
}
} }
// Get the (linked) local profile. // Get the (linked) local profile.
@ -63,7 +79,7 @@ func (p *Process) UpdateProfileMetadata() {
} }
// Update metadata of profile. // Update metadata of profile.
metadataUpdated := localProfile.UpdateMetadata(p.Name, p.Path) metadataUpdated := localProfile.UpdateMetadata(p.Path)
// Mark profile as used. // Mark profile as used.
profileChanged := localProfile.MarkUsed() profileChanged := localProfile.MarkUsed()

View file

@ -76,7 +76,7 @@ func updateGlobalConfigProfile(ctx context.Context, task *modules.Task) error {
} }
// build global profile for reference // build global profile for reference
profile := New(SourceSpecial, "global-config", "") profile := New(SourceSpecial, "global-config", "", nil)
profile.Name = "Global Configuration" profile.Name = "Global Configuration"
profile.Internal = true profile.Internal = true

View file

@ -2,30 +2,16 @@ package profile
import ( import (
"errors" "errors"
"os"
"strings" "strings"
"github.com/safing/portbase/database" "github.com/safing/portbase/database"
"github.com/safing/portbase/dataroot"
"github.com/safing/portbase/database/query" "github.com/safing/portbase/database/query"
"github.com/safing/portbase/database/record" "github.com/safing/portbase/database/record"
"github.com/safing/portbase/log" "github.com/safing/portbase/log"
"golang.org/x/sync/singleflight" "golang.org/x/sync/singleflight"
) )
const (
// UnidentifiedProfileID is the profile ID used for unidentified processes.
UnidentifiedProfileID = "_unidentified"
// SystemProfileID is the profile ID used for the system/kernel.
SystemProfileID = "_system"
// SystemProfileID is the profile ID used for the Portmaster itself.
PortmasterProfileID = "_portmaster"
)
var getProfileSingleInflight singleflight.Group var getProfileSingleInflight singleflight.Group
// GetProfile fetches a profile. This function ensures that the loaded profile // GetProfile fetches a profile. This function ensures that the loaded profile
@ -69,15 +55,8 @@ func GetProfile(source profileSource, id, linkedPath string) ( //nolint:gocognit
// If we cannot find a profile, check if the request is for a special // If we cannot find a profile, check if the request is for a special
// profile we can create. // profile we can create.
if errors.Is(err, database.ErrNotFound) { if errors.Is(err, database.ErrNotFound) {
switch id { profile = getSpecialProfile(id, linkedPath)
case UnidentifiedProfileID: if profile != nil {
profile = New(SourceLocal, UnidentifiedProfileID, linkedPath)
err = nil
case SystemProfileID:
profile = New(SourceLocal, SystemProfileID, linkedPath)
err = nil
case PortmasterProfileID:
profile = New(SourceLocal, PortmasterProfileID, linkedPath)
err = nil err = nil
} }
} }
@ -173,11 +152,11 @@ func findProfile(linkedPath string) (profile *Profile, err error) {
} }
// If there was no profile in the database, create a new one, and return it. // If there was no profile in the database, create a new one, and return it.
profile = New(SourceLocal, "", linkedPath) profile = New(SourceLocal, "", linkedPath, nil)
// Check if the profile should be marked as internal. // Check if the profile should be marked as internal.
// This is the case whenever the binary resides within the data root dir. // This is the case whenever the binary resides within the data root dir.
if strings.HasPrefix(linkedPath, dataroot.Root().Dir+string(os.PathSeparator)) { if updatesPath != "" && strings.HasPrefix(linkedPath, updatesPath) {
profile.Internal = true profile.Internal = true
} }

View file

@ -1,17 +1,19 @@
package profile package profile
import ( import (
"github.com/safing/portbase/log" "os"
"github.com/safing/portbase/log"
"github.com/safing/portbase/modules" "github.com/safing/portbase/modules"
"github.com/safing/portmaster/updates"
// module dependencies // module dependencies
_ "github.com/safing/portmaster/core/base" _ "github.com/safing/portmaster/core/base"
_ "github.com/safing/portmaster/updates" // dependency of semi-dependency filterlists
) )
var ( var (
module *modules.Module module *modules.Module
updatesPath string
) )
func init() { func init() {
@ -33,6 +35,12 @@ func prep() error {
} }
func start() error { func start() error {
updatesPath = updates.RootPath()
if updatesPath != "" {
updatesPath += string(os.PathSeparator)
}
log.Warningf("profile: using updates path %s", updatesPath)
err := registerValidationDBHook() err := registerValidationDBHook()
if err != nil { if err != nil {
return err return err

View file

@ -193,13 +193,25 @@ func (profile *Profile) parseConfig() error {
} }
// New returns a new Profile. // New returns a new Profile.
func New(source profileSource, id string, linkedPath string) *Profile { // Optionally, you may supply custom configuration in the flat (key=value) form.
func New(
source profileSource,
id string,
linkedPath string,
customConfig map[string]interface{},
) *Profile {
if customConfig != nil {
customConfig = config.Expand(customConfig)
} else {
customConfig = make(map[string]interface{})
}
profile := &Profile{ profile := &Profile{
ID: id, ID: id,
Source: source, Source: source,
LinkedPath: linkedPath, LinkedPath: linkedPath,
Created: time.Now().Unix(), Created: time.Now().Unix(),
Config: make(map[string]interface{}), Config: customConfig,
} }
// Generate random ID if none is given. // Generate random ID if none is given.
@ -210,9 +222,13 @@ func New(source profileSource, id string, linkedPath string) *Profile {
// Make key from ID and source. // Make key from ID and source.
profile.makeKey() profile.makeKey()
// Prepare profile to create placeholders. // Prepare and parse initial profile config.
_ = profile.prepConfig() if err := profile.prepConfig(); err != nil {
_ = profile.parseConfig() log.Errorf("profile: failed to prep new profile: %s", err)
}
if err := profile.parseConfig(); err != nil {
log.Errorf("profile: failed to parse new profile: %s", err)
}
return profile return profile
} }
@ -372,7 +388,7 @@ func EnsureProfile(r record.Record) (*Profile, error) {
// the profile was changed. If there is data that needs to be fetched from the // the profile was changed. If there is data that needs to be fetched from the
// operating system, it will start an async worker to fetch that data and save // operating system, it will start an async worker to fetch that data and save
// the profile afterwards. // the profile afterwards.
func (profile *Profile) UpdateMetadata(processName, binaryPath string) (changed bool) { func (profile *Profile) UpdateMetadata(binaryPath string) (changed bool) {
// Check if this is a local profile, else warn and return. // Check if this is a local profile, else warn and return.
if profile.Source != SourceLocal { if profile.Source != SourceLocal {
log.Warningf("tried to update metadata for non-local profile %s", profile.ScopedID()) log.Warningf("tried to update metadata for non-local profile %s", profile.ScopedID())
@ -382,22 +398,9 @@ func (profile *Profile) UpdateMetadata(processName, binaryPath string) (changed
profile.Lock() profile.Lock()
defer profile.Unlock() defer profile.Unlock()
// Check if this is a special profile. // Update special profile and return if it was one.
if binaryPath == "" { if ok, changed := updateSpecialProfileMetadata(profile, binaryPath); ok {
// This is a special profile, just assign the processName, if needed, and return changed
// return.
if profile.Name != processName {
profile.Name = processName
return true
}
return false
}
// Update LinkedPath if if differs from the process path.
// This will (at the moment) only be the case for the Portmaster profile.
if profile.LinkedPath != binaryPath {
profile.LinkedPath = binaryPath
changed = true
} }
var needsUpdateFromSystem bool var needsUpdateFromSystem bool

109
profile/special.go Normal file
View file

@ -0,0 +1,109 @@
package profile
const (
// UnidentifiedProfileID is the profile ID used for unidentified processes.
UnidentifiedProfileID = "_unidentified"
// UnidentifiedProfileName is the name used for unidentified processes.
UnidentifiedProfileName = "Unidentified Processes"
// SystemProfileID is the profile ID used for the system/kernel.
SystemProfileID = "_system"
// SystemProfileName is the name used for the system/kernel.
SystemProfileName = "Operating System"
// PortmasterProfileID is the profile ID used for the Portmaster Core itself.
PortmasterProfileID = "_portmaster"
// PortmasterProfileName is the name used for the Portmaster Core itself.
PortmasterProfileName = "Portmaster Core Service"
// PortmasterAppProfileID is the profile ID used for the Portmaster App.
PortmasterAppProfileID = "_portmaster-app"
// PortmasterAppProfileName is the name used for the Portmaster App.
PortmasterAppProfileName = "Portmaster User Interface"
// PortmasterNotifierProfileID is the profile ID used for the Portmaster Notifier.
PortmasterNotifierProfileID = "_portmaster-notifier"
// PortmasterNotifierProfileName is the name used for the Portmaster Notifier.
PortmasterNotifierProfileName = "Portmaster Notifier"
)
func updateSpecialProfileMetadata(profile *Profile, binaryPath string) (ok, changed bool) {
// Get new profile name and check if profile is applicable to special handling.
var newProfileName string
switch profile.ID {
case UnidentifiedProfileID:
newProfileName = UnidentifiedProfileName
case SystemProfileID:
newProfileName = SystemProfileName
case PortmasterProfileID:
newProfileName = PortmasterProfileName
case PortmasterAppProfileID:
newProfileName = PortmasterAppProfileName
case PortmasterNotifierProfileID:
newProfileName = PortmasterNotifierProfileName
default:
return false, false
}
// Update profile name if needed.
if profile.Name != newProfileName {
profile.Name = newProfileName
changed = true
}
// Update LinkedPath to new value.
if profile.LinkedPath != binaryPath {
profile.LinkedPath = binaryPath
changed = true
}
return true, changed
}
func getSpecialProfile(profileID, linkedPath string) *Profile {
switch profileID {
case UnidentifiedProfileID:
return New(SourceLocal, UnidentifiedProfileID, linkedPath, nil)
case SystemProfileID:
return New(SourceLocal, SystemProfileID, linkedPath, nil)
case PortmasterProfileID:
profile := New(SourceLocal, PortmasterProfileID, linkedPath, nil)
profile.Internal = true
return profile
case PortmasterAppProfileID:
profile := New(
SourceLocal,
PortmasterAppProfileID,
linkedPath,
map[string]interface{}{
CfgOptionDefaultActionKey: "block",
CfgOptionEndpointsKey: []string{
"+ Localhost",
},
},
)
profile.Internal = true
return profile
case PortmasterNotifierProfileID:
profile := New(
SourceLocal,
PortmasterNotifierProfileID,
linkedPath,
map[string]interface{}{
CfgOptionDefaultActionKey: "block",
CfgOptionEndpointsKey: []string{
"+ Localhost",
},
},
)
profile.Internal = true
return profile
default:
return nil
}
}

View file

@ -34,7 +34,7 @@ func setupRuntimeProvider() (err error) {
return nil return nil
} }
// setSelectedSecurityLevel updates the selected security level // setSelectedSecurityLevel updates the selected security level.
func setSelectedSecurityLevel(r record.Record) (record.Record, error) { func setSelectedSecurityLevel(r record.Record) (record.Record, error) {
var upd *SelectedSecurityLevelRecord var upd *SelectedSecurityLevelRecord
if r.IsWrapped() { if r.IsWrapped() {

View file

@ -33,7 +33,7 @@ type SystemStatusRecord struct {
// SelectedSecurityLevelRecord is used as a dummy record.Record // SelectedSecurityLevelRecord is used as a dummy record.Record
// to provide a simply runtime-configuration for the user. // to provide a simply runtime-configuration for the user.
// It is write-only and exposed at runtime:system/security-level // It is write-only and exposed at "runtime:system/security-level".
type SelectedSecurityLevelRecord struct { type SelectedSecurityLevelRecord struct {
record.Base record.Base
sync.Mutex sync.Mutex

View file

@ -15,7 +15,7 @@ type (
// It's meant to be used as a value for config.DisplayHintAnnotation. // It's meant to be used as a value for config.DisplayHintAnnotation.
const DisplayHintSecurityLevel string = "security level" const DisplayHintSecurityLevel string = "security level"
// Security levels // Security levels.
const ( const (
SecurityLevelOff uint8 = 0 SecurityLevelOff uint8 = 0
SecurityLevelNormal uint8 = 1 SecurityLevelNormal uint8 = 1
@ -31,24 +31,28 @@ const (
// SecurityLevelValues defines all possible security levels. // SecurityLevelValues defines all possible security levels.
var SecurityLevelValues = []config.PossibleValue{ var SecurityLevelValues = []config.PossibleValue{
{ {
Name: "Normal", Name: "Trusted / Home Network",
Value: SecurityLevelsAll, Value: SecurityLevelsAll,
Description: "Setting is always enabled.",
}, },
{ {
Name: "High", Name: "Untrusted / Public Network",
Value: SecurityLevelsHighAndExtreme, Value: SecurityLevelsHighAndExtreme,
Description: "Setting is enabled in untrusted and dangerous networks.",
}, },
{ {
Name: "Extreme", Name: "Danger / Hacked Network",
Value: SecurityLevelExtreme, Value: SecurityLevelExtreme,
Description: "Setting is enabled only in dangerous networks.",
}, },
} }
// AllSecurityLevelValues is like SecurityLevelValues but also includes Off. // AllSecurityLevelValues is like SecurityLevelValues but also includes Off.
var AllSecurityLevelValues = append([]config.PossibleValue{ var AllSecurityLevelValues = append([]config.PossibleValue{
{ {
Name: "Off", Name: "Off",
Value: SecurityLevelOff, Value: SecurityLevelOff,
Description: "Setting is always disabled.",
}, },
}, },
SecurityLevelValues..., SecurityLevelValues...,