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

View file

@ -1,17 +1,32 @@
package process
import (
"os"
"github.com/safing/portbase/log"
"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", "updates")
}
func prep() error {
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 (
"context"
"os"
"strings"
"github.com/safing/portbase/log"
"github.com/safing/portmaster/profile"
@ -38,6 +39,21 @@ 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
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.
@ -63,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()

View file

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

View file

@ -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
}
}
@ -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.
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.
if strings.HasPrefix(linkedPath, dataroot.Root().Dir+string(os.PathSeparator)) {
if updatesPath != "" && strings.HasPrefix(linkedPath, updatesPath) {
profile.Internal = true
}

View file

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

View file

@ -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 (at the moment) only be the case for the Portmaster profile.
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

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
}
// 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() {

View file

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

View file

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