Merge branch 'v2.0' into task/refactor-spn
This commit is contained in:
@@ -1,43 +1,51 @@
|
||||
package base
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/safing/portmaster/base/database"
|
||||
_ "github.com/safing/portmaster/base/database/dbmodule"
|
||||
_ "github.com/safing/portmaster/base/database/storage/bbolt"
|
||||
_ "github.com/safing/portmaster/base/database/storage/sqlite"
|
||||
"github.com/safing/portmaster/base/utils"
|
||||
)
|
||||
|
||||
// Default Values (changeable for testing).
|
||||
var (
|
||||
DefaultDatabaseStorageType = "bbolt"
|
||||
DefaultDatabaseStorageType = "sqlite"
|
||||
)
|
||||
|
||||
func registerDatabases() error {
|
||||
// If there is an existing bbolt core database, use it instead.
|
||||
coreStorageType := DefaultDatabaseStorageType
|
||||
if utils.PathExists(filepath.Join(module.instance.DataDir(), "databases", "core", "bbolt")) {
|
||||
coreStorageType = "bbolt"
|
||||
}
|
||||
|
||||
// Register core database.
|
||||
_, err := database.Register(&database.Database{
|
||||
Name: "core",
|
||||
Description: "Holds core data, such as settings and profiles",
|
||||
StorageType: DefaultDatabaseStorageType,
|
||||
StorageType: coreStorageType,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If there is an existing cache bbolt database, use it instead.
|
||||
cacheStorageType := DefaultDatabaseStorageType
|
||||
if utils.PathExists(filepath.Join(module.instance.DataDir(), "databases", "cache", "bbolt")) {
|
||||
cacheStorageType = "bbolt"
|
||||
}
|
||||
|
||||
// Register cache database.
|
||||
_, err = database.Register(&database.Database{
|
||||
Name: "cache",
|
||||
Description: "Cached data, such as Intelligence and DNS Records",
|
||||
StorageType: DefaultDatabaseStorageType,
|
||||
StorageType: cacheStorageType,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// _, err = database.Register(&database.Database{
|
||||
// Name: "history",
|
||||
// Description: "Historic event data",
|
||||
// StorageType: DefaultDatabaseStorageType,
|
||||
// })
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -107,6 +107,7 @@ func Handler(ctx context.Context, packets chan packet.Packet, bandwidthUpdate ch
|
||||
newPacket := &Packet{
|
||||
verdictRequest: conn.ID,
|
||||
payload: conn.Payload,
|
||||
payloadLayer: conn.PayloadLayer,
|
||||
verdictSet: abool.NewBool(false),
|
||||
}
|
||||
info := newPacket.Info()
|
||||
|
||||
@@ -397,28 +397,32 @@ func (e *Entity) getDomainLists(ctx context.Context) {
|
||||
}
|
||||
|
||||
func splitDomain(domain string) []string {
|
||||
domain = strings.Trim(domain, ".")
|
||||
suffix, _ := publicsuffix.PublicSuffix(domain)
|
||||
if suffix == domain {
|
||||
// Get suffix.
|
||||
d := strings.TrimSuffix(domain, ".")
|
||||
suffix, icann := publicsuffix.PublicSuffix(d)
|
||||
if suffix == d {
|
||||
return []string{domain}
|
||||
}
|
||||
|
||||
domainWithoutSuffix := domain[:len(domain)-len(suffix)]
|
||||
domainWithoutSuffix = strings.Trim(domainWithoutSuffix, ".")
|
||||
|
||||
splitted := strings.FieldsFunc(domainWithoutSuffix, func(r rune) bool {
|
||||
// Split all subdomain into labels.
|
||||
labels := strings.FieldsFunc(d[:len(d)-len(suffix)], func(r rune) bool {
|
||||
return r == '.'
|
||||
})
|
||||
|
||||
domains := make([]string, 0, len(splitted))
|
||||
for idx := range splitted {
|
||||
|
||||
d := strings.Join(splitted[idx:], ".") + "." + suffix
|
||||
if d[len(d)-1] != '.' {
|
||||
d += "."
|
||||
}
|
||||
domains = append(domains, d)
|
||||
// Build list of all domains up to the public suffix.
|
||||
domains := make([]string, 0, len(labels)+1)
|
||||
for idx := range labels {
|
||||
domains = append(
|
||||
domains,
|
||||
strings.Join(labels[idx:], ".")+"."+suffix+".",
|
||||
)
|
||||
}
|
||||
|
||||
// If the suffix is not a real TLD, but a public suffix, add it to the list.
|
||||
if !icann {
|
||||
domains = append(domains, suffix+".")
|
||||
}
|
||||
|
||||
return domains
|
||||
}
|
||||
|
||||
|
||||
33
service/intel/entity_test.go
Normal file
33
service/intel/entity_test.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package intel
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var splitDomainTestCases = [][]string{
|
||||
// Regular registered domains and subdomains.
|
||||
{"example.com."},
|
||||
{"www.example.com.", "example.com."},
|
||||
{"sub.domain.example.com.", "domain.example.com.", "example.com."},
|
||||
{"example.co.uk."},
|
||||
{"www.example.co.uk.", "example.co.uk."},
|
||||
|
||||
// TLD or public suffix: Return as is.
|
||||
{"com."},
|
||||
{"googleapis.com."},
|
||||
|
||||
// Public suffix domains: Return including public suffix.
|
||||
{"test.googleapis.com.", "googleapis.com."},
|
||||
{"sub.domain.googleapis.com.", "domain.googleapis.com.", "googleapis.com."},
|
||||
}
|
||||
|
||||
func TestSplitDomain(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for _, testCase := range splitDomainTestCases {
|
||||
splitted := splitDomain(testCase[0])
|
||||
assert.Equal(t, testCase, splitted, "result must match")
|
||||
}
|
||||
}
|
||||
@@ -144,7 +144,7 @@ func persistRecords(startJob func(func() error), records <-chan record.Record) {
|
||||
|
||||
timePerEntity := time.Since(start) / time.Duration(cnt)
|
||||
speed := float64(time.Second) / float64(timePerEntity)
|
||||
log.Debugf("processed %d entities in %s with %s / entity (%.2f entities/second)", cnt, time.Since(start), timePerEntity, speed)
|
||||
log.Debugf("intel/filterlists: processed %d entities in %s with %s / entity (%.2f entities/second)", cnt, time.Since(start), timePerEntity, speed)
|
||||
}
|
||||
|
||||
batch := database.NewInterface(&database.Options{Local: true, Internal: true})
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
|
||||
"github.com/safing/portmaster/base/database"
|
||||
"github.com/safing/portmaster/base/database/query"
|
||||
"github.com/safing/portmaster/base/database/storage"
|
||||
"github.com/safing/portmaster/base/log"
|
||||
"github.com/safing/portmaster/service/mgr"
|
||||
"github.com/safing/portmaster/service/updates"
|
||||
@@ -158,9 +159,25 @@ func performUpdate(ctx context.Context) error {
|
||||
|
||||
func removeAllObsoleteFilterEntries(wc *mgr.WorkerCtx) error {
|
||||
log.Debugf("intel/filterlists: cleanup task started, removing obsolete filter list entries ...")
|
||||
n, err := cache.Purge(wc.Ctx(), query.New(filterListKeyPrefix).Where(
|
||||
// TODO(ppacher): remember the timestamp we started the last update
|
||||
// and use that rather than "one hour ago"
|
||||
|
||||
// TODO: Remember the timestamp we started the last update and use that rather than "one hour ago".
|
||||
|
||||
// First try to purge with PurgeOlderThan.
|
||||
n, err := cache.PurgeOlderThan(wc.Ctx(), filterListKeyPrefix, time.Now().Add(-time.Hour))
|
||||
switch {
|
||||
case err == nil:
|
||||
// Success!
|
||||
log.Debugf("intel/filterlists: successfully removed %d obsolete entries", n)
|
||||
return nil
|
||||
case errors.Is(err, database.ErrNotImplemented) || errors.Is(err, storage.ErrNotImplemented):
|
||||
// Try next method.
|
||||
default:
|
||||
// Return error.
|
||||
return err
|
||||
}
|
||||
|
||||
// Try with regular purge.
|
||||
n, err = cache.Purge(wc.Ctx(), query.New(filterListKeyPrefix).Where(
|
||||
query.Where("UpdatedAt", query.LessThan, time.Now().Add(-time.Hour).Unix()),
|
||||
))
|
||||
if err != nil {
|
||||
|
||||
@@ -118,6 +118,8 @@ func (p *Process) IsPortmasterUi(ctx context.Context) bool {
|
||||
var previousPid int
|
||||
proc := p
|
||||
|
||||
hasPmWebviewEnvVar := false
|
||||
|
||||
for i := 0; i < checkLevels; i++ {
|
||||
if proc.Pid == UnidentifiedProcessID || proc.Pid == SystemProcessID {
|
||||
break
|
||||
@@ -125,7 +127,43 @@ func (p *Process) IsPortmasterUi(ctx context.Context) bool {
|
||||
|
||||
realPath, err := filepath.EvalSymlinks(proc.Path)
|
||||
if err == nil && realPath == module.portmasterUIPath {
|
||||
return true
|
||||
if runtime.GOOS != "windows" {
|
||||
return true
|
||||
}
|
||||
|
||||
// On Windows, avoid false positive detection of the Portmaster UI.
|
||||
// For example:
|
||||
// There may be cases where a system browser is launched from the Portmaster UI,
|
||||
// making it a child of the Portmaster UI process (e.g., user clicked a link in the UI).
|
||||
// In this case, the parent process tree may look like this:
|
||||
// Portmaster.exe
|
||||
// ├─ WebView (PM UI)
|
||||
// │ └─ WebView (PM UI child)
|
||||
// └─ System Web Browser ...
|
||||
//
|
||||
// To ensure that 'p' is the actual Portmaster UI process, we check for the presence
|
||||
// of the 'PORTMASTER_UI_WEBVIEW_PROCESS' environment variable in the process and its parents.
|
||||
// If the env var is set, we are a child (WebView window) of the Portmaster UI process.
|
||||
// Otherwise, the process was launched by the Portmaster UI, but should not be trusted as the Portmaster UI process.
|
||||
if i == 0 {
|
||||
return true // We are the main Portmaster UI process.
|
||||
}
|
||||
if hasPmWebviewEnvVar {
|
||||
return true // We are a WebView window of the Portmaster UI process.
|
||||
}
|
||||
// The process was launched by the Portmaster UI, but should not be trusted as the Portmaster UI process.
|
||||
log.Tracer(ctx).Warningf("process: %d %q is a child of the Portmaster UI, but does not have the PORTMASTER_UI_WEBVIEW_PROCESS environment variable set. Ignoring.", p.Pid, p.Path)
|
||||
return false
|
||||
}
|
||||
|
||||
// Check if the process has the environment variable set.
|
||||
//
|
||||
// It is OK to check for the existence of the environment variable in all
|
||||
// processes in the parent chain (on all loop iterations). This increases the
|
||||
// chance of correct detection, even if a child or grandchild WebView process
|
||||
// did not inherit the environment variable for some reason.
|
||||
if _, ok := proc.Env["PORTMASTER_UI_WEBVIEW_PROCESS"]; ok {
|
||||
hasPmWebviewEnvVar = true
|
||||
}
|
||||
|
||||
if i < checkLevels-1 { // no need to check parent if we are at the last level
|
||||
|
||||
@@ -237,14 +237,20 @@ func createSpecialProfile(profileID string, path string) *Profile {
|
||||
Source: SourceLocal,
|
||||
PresentationPath: path,
|
||||
Config: map[string]interface{}{
|
||||
// Block all connections by default for the Portmaster UI profile,
|
||||
// since the only required connections are to the Portmaster Core,
|
||||
// which are fast-tracked.
|
||||
//
|
||||
// This ensures that any unexpected connections —
|
||||
// possibly made by the internal WebView implementation —
|
||||
// are blocked.
|
||||
CfgOptionDefaultActionKey: DefaultActionBlockValue,
|
||||
CfgOptionBlockScopeInternetKey: false,
|
||||
CfgOptionBlockScopeLANKey: false,
|
||||
CfgOptionBlockScopeLocalKey: false,
|
||||
CfgOptionBlockP2PKey: false,
|
||||
CfgOptionBlockScopeInternetKey: false, // This is stronger than the rules, and thus must be false in order to access safing.io.
|
||||
CfgOptionBlockScopeLANKey: true,
|
||||
CfgOptionBlockScopeLocalKey: true,
|
||||
CfgOptionBlockP2PKey: true,
|
||||
CfgOptionBlockInboundKey: true,
|
||||
CfgOptionEndpointsKey: []string{
|
||||
"+ Localhost",
|
||||
"+ .safing.io",
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user