From 6cc0e470eeed9cc349c3ee088b400a1270e98450 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 10 Feb 2021 17:06:34 +0100 Subject: [PATCH 1/4] Update Security Level names and descriptions for docs --- status/security_level.go | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/status/security_level.go b/status/security_level.go index ea8badb7..199237d3 100644 --- a/status/security_level.go +++ b/status/security_level.go @@ -31,24 +31,28 @@ const ( // SecurityLevelValues defines all possible security levels. var SecurityLevelValues = []config.PossibleValue{ { - Name: "Normal", - Value: SecurityLevelsAll, + Name: "Trusted / Home Network", + Value: SecurityLevelsAll, + Description: "Setting is always enabled.", }, { - Name: "High", - Value: SecurityLevelsHighAndExtreme, + Name: "Untrusted / Public Network", + Value: SecurityLevelsHighAndExtreme, + Description: "Setting is enabled in untrusted and dangerous networks.", }, { - Name: "Extreme", - Value: SecurityLevelExtreme, + Name: "Danger / Hacked Network", + Value: SecurityLevelExtreme, + Description: "Setting is enabled only in dangerous networks.", }, } // AllSecurityLevelValues is like SecurityLevelValues but also includes Off. var AllSecurityLevelValues = append([]config.PossibleValue{ { - Name: "Off", - Value: SecurityLevelOff, + Name: "Off", + Value: SecurityLevelOff, + Description: "Setting is always disabled.", }, }, SecurityLevelValues..., From 971edcfa419231c5df40699b96b36184a0592776 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 11 Feb 2021 13:06:32 +0100 Subject: [PATCH 2/4] Use special profiles for Portmaster components and mark them as internal --- network/connection.go | 27 ++++++++++++++++--- process/module.go | 17 ++++++++++-- process/profile.go | 11 ++++++++ profile/get.go | 27 +++---------------- profile/module.go | 11 +++++++- profile/profile.go | 2 +- profile/special.go | 61 +++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 125 insertions(+), 31 deletions(-) create mode 100644 profile/special.go diff --git a/network/connection.go b/network/connection.go index 99078c87..f8b130e9 100644 --- a/network/connection.go +++ b/network/connection.go @@ -228,6 +228,12 @@ func NewConnectionFromDNSRequest(ctx context.Context, fqdn string, cnames []stri Started: timestamp, Ended: timestamp, } + + // Inherit internal status of profile. + if localProfile := proc.Profile().LocalProfile(); localProfile != nil { + dnsConn.Internal = localProfile.Internal + } + return dnsConn } @@ -238,7 +244,7 @@ func NewConnectionFromExternalDNSRequest(ctx context.Context, fqdn string, cname } timestamp := time.Now().Unix() - return &Connection{ + dnsConn := &Connection{ Scope: fqdn, Entity: &intel.Entity{ Domain: fqdn, @@ -248,7 +254,14 @@ func NewConnectionFromExternalDNSRequest(ctx context.Context, fqdn string, cname ProcessContext: getProcessContext(ctx, remoteHost), Started: 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. @@ -335,7 +348,8 @@ func NewConnectionFromFirstPacket(pkt packet.Packet) *Connection { } } - return &Connection{ + // Create new connection object. + newConn := &Connection{ ID: pkt.GetConnectionID(), Scope: scope, IPVersion: pkt.Info().Version, @@ -352,6 +366,13 @@ func NewConnectionFromFirstPacket(pkt packet.Packet) *Connection { Started: time.Now().Unix(), 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. diff --git a/process/module.go b/process/module.go index bfed029e..5b0d2a09 100644 --- a/process/module.go +++ b/process/module.go @@ -1,17 +1,30 @@ package process import ( + "os" + "github.com/safing/portbase/modules" + "github.com/safing/portmaster/updates" ) var ( - module *modules.Module + module *modules.Module + updatesPath string ) func init() { - module = modules.Register("processes", prep, nil, nil, "profiles") + module = modules.Register("processes", prep, start, nil, "profiles") } func prep() error { return registerConfiguration() } + +func start() error { + updatesPath = updates.RootPath() + string(os.PathSeparator) + if updatesPath != "" { + updatesPath += string(os.PathSeparator) + } + + return nil +} diff --git a/process/profile.go b/process/profile.go index 3294a281..9c0e3a4a 100644 --- a/process/profile.go +++ b/process/profile.go @@ -3,6 +3,7 @@ package process import ( "context" "os" + "strings" "github.com/safing/portbase/log" "github.com/safing/portmaster/profile" @@ -38,6 +39,16 @@ func (p *Process) GetProfile(ctx context.Context) (changed bool, err error) { profileID = profile.SystemProfileID case ownPID: 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 + } + } } // Get the (linked) local profile. diff --git a/profile/get.go b/profile/get.go index 7e1e88be..8a99cd8c 100644 --- a/profile/get.go +++ b/profile/get.go @@ -2,30 +2,16 @@ package profile import ( "errors" - "os" "strings" "github.com/safing/portbase/database" - "github.com/safing/portbase/dataroot" - "github.com/safing/portbase/database/query" "github.com/safing/portbase/database/record" "github.com/safing/portbase/log" "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 // 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 // profile we can create. if errors.Is(err, database.ErrNotFound) { - switch id { - case UnidentifiedProfileID: - profile = New(SourceLocal, UnidentifiedProfileID, linkedPath) - err = nil - case SystemProfileID: - profile = New(SourceLocal, SystemProfileID, linkedPath) - err = nil - case PortmasterProfileID: - profile = New(SourceLocal, PortmasterProfileID, linkedPath) + profile = getSpecialProfile(id, linkedPath) + if profile != nil { err = nil } } @@ -177,7 +156,7 @@ func findProfile(linkedPath string) (profile *Profile, err error) { // Check if the profile should be marked as internal. // 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 } diff --git a/profile/module.go b/profile/module.go index c42db691..71f18306 100644 --- a/profile/module.go +++ b/profile/module.go @@ -1,17 +1,21 @@ package profile import ( + "os" + "github.com/safing/portbase/log" "github.com/safing/portbase/modules" // module dependencies _ "github.com/safing/portmaster/core/base" + "github.com/safing/portmaster/updates" _ "github.com/safing/portmaster/updates" // dependency of semi-dependency filterlists ) var ( - module *modules.Module + module *modules.Module + updatesPath string ) func init() { @@ -33,6 +37,11 @@ func prep() error { } func start() error { + updatesPath = updates.RootPath() + string(os.PathSeparator) + if updatesPath != "" { + updatesPath += string(os.PathSeparator) + } + err := registerValidationDBHook() if err != nil { return err diff --git a/profile/profile.go b/profile/profile.go index 46668249..3eb428aa 100644 --- a/profile/profile.go +++ b/profile/profile.go @@ -394,7 +394,7 @@ func (profile *Profile) UpdateMetadata(processName, binaryPath string) (changed } // Update LinkedPath if if differs from the process path. - // This will (at the moment) only be the case for the Portmaster profile. + // This will be the case for profiles that are assigned in a special way. if profile.LinkedPath != binaryPath { profile.LinkedPath = binaryPath changed = true diff --git a/profile/special.go b/profile/special.go new file mode 100644 index 00000000..da7027de --- /dev/null +++ b/profile/special.go @@ -0,0 +1,61 @@ +package profile + +const ( + // UnidentifiedProfileID is the profile ID used for unidentified processes. + UnidentifiedProfileID = "_unidentified" + + // SystemProfileID is the profile ID used for the system/kernel. + SystemProfileID = "_system" + + // PortmasterProfileID is the profile ID used for the Portmaster Core itself. + PortmasterProfileID = "_portmaster" + + // PortmasterAppProfileID is the profile ID used for the Portmaster App. + PortmasterAppProfileID = "_portmaster-app" + + // PortmasterNotifierProfileID is the profile ID used for the Portmaster Notifier. + PortmasterNotifierProfileID = "_portmaster-notifier" +) + +func getSpecialProfile(profileID, linkedPath string) *Profile { + switch profileID { + case UnidentifiedProfileID: + return New(SourceLocal, UnidentifiedProfileID, linkedPath) + + case SystemProfileID: + return New(SourceLocal, SystemProfileID, linkedPath) + + case PortmasterProfileID: + profile := New(SourceLocal, PortmasterProfileID, linkedPath) + profile.Name = "Portmaster Core Service" + profile.Internal = true + return profile + + case PortmasterAppProfileID: + profile := New(SourceLocal, PortmasterAppProfileID, linkedPath) + profile.Name = "Portmaster User Interface" + profile.Internal = true + profile.Config = map[string]interface{}{ + CfgOptionDefaultActionKey: "block", + CfgOptionEndpointsKey: []string{ + "+ Localhost", + }, + } + return profile + + case PortmasterNotifierProfileID: + profile := New(SourceLocal, PortmasterNotifierProfileID, linkedPath) + profile.Name = "Portmaster Notifier" + profile.Internal = true + profile.Config = map[string]interface{}{ + CfgOptionDefaultActionKey: "block", + CfgOptionEndpointsKey: []string{ + "+ Localhost", + }, + } + return profile + + default: + return nil + } +} From 7d6d67992929b26db48a4be08ca45a101727a0ef Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 11 Feb 2021 13:07:02 +0100 Subject: [PATCH 3/4] Fix some linter errors --- status/provider.go | 2 +- status/records.go | 2 +- status/security_level.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/status/provider.go b/status/provider.go index 863b11be..e2230f65 100644 --- a/status/provider.go +++ b/status/provider.go @@ -34,7 +34,7 @@ func setupRuntimeProvider() (err error) { return nil } -// setSelectedSecurityLevel updates the selected security level +// setSelectedSecurityLevel updates the selected security level. func setSelectedSecurityLevel(r record.Record) (record.Record, error) { var upd *SelectedSecurityLevelRecord if r.IsWrapped() { diff --git a/status/records.go b/status/records.go index 73801c62..7f2c39b4 100644 --- a/status/records.go +++ b/status/records.go @@ -33,7 +33,7 @@ type SystemStatusRecord struct { // SelectedSecurityLevelRecord is used as a dummy record.Record // 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 { record.Base sync.Mutex diff --git a/status/security_level.go b/status/security_level.go index 199237d3..03a0f4e0 100644 --- a/status/security_level.go +++ b/status/security_level.go @@ -15,7 +15,7 @@ type ( // It's meant to be used as a value for config.DisplayHintAnnotation. const DisplayHintSecurityLevel string = "security level" -// Security levels +// Security levels. const ( SecurityLevelOff uint8 = 0 SecurityLevelNormal uint8 = 1 From 9bf89c32c02ff29832a74fae3a30e16133d13b8a Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 11 Feb 2021 14:53:33 +0100 Subject: [PATCH 4/4] Fix new profiles with custom config and implement review suggestions --- process/module.go | 6 ++- process/profile.go | 7 +++- profile/config-update.go | 2 +- profile/get.go | 2 +- profile/module.go | 7 ++-- profile/profile.go | 47 +++++++++++---------- profile/special.go | 88 +++++++++++++++++++++++++++++++--------- 7 files changed, 108 insertions(+), 51 deletions(-) diff --git a/process/module.go b/process/module.go index 5b0d2a09..4134d755 100644 --- a/process/module.go +++ b/process/module.go @@ -3,6 +3,7 @@ package process import ( "os" + "github.com/safing/portbase/log" "github.com/safing/portbase/modules" "github.com/safing/portmaster/updates" ) @@ -13,7 +14,7 @@ var ( ) func init() { - module = modules.Register("processes", prep, start, nil, "profiles") + module = modules.Register("processes", prep, start, nil, "profiles", "updates") } func prep() error { @@ -21,10 +22,11 @@ func prep() error { } func start() error { - updatesPath = updates.RootPath() + string(os.PathSeparator) + updatesPath = updates.RootPath() if updatesPath != "" { updatesPath += string(os.PathSeparator) } + log.Warningf("process: using updates path %s", updatesPath) return nil } diff --git a/process/profile.go b/process/profile.go index 9c0e3a4a..792c11f8 100644 --- a/process/profile.go +++ b/process/profile.go @@ -47,6 +47,11 @@ func (p *Process) GetProfile(ctx context.Context) (changed bool, err error) { 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. } } } @@ -74,7 +79,7 @@ func (p *Process) UpdateProfileMetadata() { } // Update metadata of profile. - metadataUpdated := localProfile.UpdateMetadata(p.Name, p.Path) + metadataUpdated := localProfile.UpdateMetadata(p.Path) // Mark profile as used. profileChanged := localProfile.MarkUsed() diff --git a/profile/config-update.go b/profile/config-update.go index 22b76c60..c403112a 100644 --- a/profile/config-update.go +++ b/profile/config-update.go @@ -76,7 +76,7 @@ func updateGlobalConfigProfile(ctx context.Context, task *modules.Task) error { } // build global profile for reference - profile := New(SourceSpecial, "global-config", "") + profile := New(SourceSpecial, "global-config", "", nil) profile.Name = "Global Configuration" profile.Internal = true diff --git a/profile/get.go b/profile/get.go index 8a99cd8c..b1f84ad8 100644 --- a/profile/get.go +++ b/profile/get.go @@ -152,7 +152,7 @@ func findProfile(linkedPath string) (profile *Profile, err error) { } // 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. // This is the case whenever the binary resides within the data root dir. diff --git a/profile/module.go b/profile/module.go index 71f18306..7a62aa1a 100644 --- a/profile/module.go +++ b/profile/module.go @@ -4,13 +4,11 @@ import ( "os" "github.com/safing/portbase/log" - "github.com/safing/portbase/modules" + "github.com/safing/portmaster/updates" // module dependencies _ "github.com/safing/portmaster/core/base" - "github.com/safing/portmaster/updates" - _ "github.com/safing/portmaster/updates" // dependency of semi-dependency filterlists ) var ( @@ -37,10 +35,11 @@ func prep() error { } func start() error { - updatesPath = updates.RootPath() + string(os.PathSeparator) + updatesPath = updates.RootPath() if updatesPath != "" { updatesPath += string(os.PathSeparator) } + log.Warningf("profile: using updates path %s", updatesPath) err := registerValidationDBHook() if err != nil { diff --git a/profile/profile.go b/profile/profile.go index 3eb428aa..aaf69304 100644 --- a/profile/profile.go +++ b/profile/profile.go @@ -193,13 +193,25 @@ func (profile *Profile) parseConfig() error { } // 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{ ID: id, Source: source, LinkedPath: linkedPath, Created: time.Now().Unix(), - Config: make(map[string]interface{}), + Config: customConfig, } // 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. profile.makeKey() - // Prepare profile to create placeholders. - _ = profile.prepConfig() - _ = profile.parseConfig() + // Prepare and parse initial profile config. + if err := profile.prepConfig(); err != nil { + 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 } @@ -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 // operating system, it will start an async worker to fetch that data and save // 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. if profile.Source != SourceLocal { 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() defer profile.Unlock() - // Check if this is a special profile. - if binaryPath == "" { - // This is a special profile, just assign the processName, if needed, and - // return. - if profile.Name != processName { - profile.Name = processName - return true - } - return false - } - - // Update LinkedPath if if differs from the process path. - // This will be the case for profiles that are assigned in a special way. - if profile.LinkedPath != binaryPath { - profile.LinkedPath = binaryPath - changed = true + // Update special profile and return if it was one. + if ok, changed := updateSpecialProfileMetadata(profile, binaryPath); ok { + return changed } var needsUpdateFromSystem bool diff --git a/profile/special.go b/profile/special.go index da7027de..754cdc67 100644 --- a/profile/special.go +++ b/profile/special.go @@ -3,56 +3,104 @@ 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) + return New(SourceLocal, UnidentifiedProfileID, linkedPath, nil) case SystemProfileID: - return New(SourceLocal, SystemProfileID, linkedPath) + return New(SourceLocal, SystemProfileID, linkedPath, nil) case PortmasterProfileID: - profile := New(SourceLocal, PortmasterProfileID, linkedPath) - profile.Name = "Portmaster Core Service" + profile := New(SourceLocal, PortmasterProfileID, linkedPath, nil) profile.Internal = true return profile case PortmasterAppProfileID: - profile := New(SourceLocal, PortmasterAppProfileID, linkedPath) - profile.Name = "Portmaster User Interface" - profile.Internal = true - profile.Config = map[string]interface{}{ - CfgOptionDefaultActionKey: "block", - CfgOptionEndpointsKey: []string{ - "+ Localhost", + 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) - profile.Name = "Portmaster Notifier" - profile.Internal = true - profile.Config = map[string]interface{}{ - CfgOptionDefaultActionKey: "block", - CfgOptionEndpointsKey: []string{ - "+ Localhost", + profile := New( + SourceLocal, + PortmasterNotifierProfileID, + linkedPath, + map[string]interface{}{ + CfgOptionDefaultActionKey: "block", + CfgOptionEndpointsKey: []string{ + "+ Localhost", + }, }, - } + ) + profile.Internal = true return profile default: