Finalize profile merging, add profile metadata state handling, re-attribute connections after profile deletion

This commit is contained in:
Daniel
2023-09-27 14:23:02 +02:00
parent 32342ec91a
commit bed5c72a6b
14 changed files with 622 additions and 96 deletions

View File

@@ -2,13 +2,18 @@ package firewall
import (
"context"
"fmt"
"strings"
"github.com/safing/portbase/config"
"github.com/safing/portbase/log"
"github.com/safing/portbase/modules"
"github.com/safing/portbase/modules/subsystems"
_ "github.com/safing/portmaster/core"
"github.com/safing/portmaster/network"
"github.com/safing/portmaster/profile"
"github.com/safing/spn/access"
"github.com/safing/spn/captain"
)
var module *modules.Module
@@ -25,12 +30,6 @@ func init() {
)
}
const (
configChangeEvent = "config change"
profileConfigChangeEvent = "profile config change"
onSPNConnectEvent = "spn connect"
)
func prep() error {
network.SetDefaultFirewallHandler(verdictHandler)
@@ -38,8 +37,8 @@ func prep() error {
// this will be triggered on spn enable/disable
err := module.RegisterEventHook(
"config",
configChangeEvent,
"reset connection verdicts",
config.ChangeEvent,
"reset connection verdicts after global config change",
func(ctx context.Context, _ interface{}) error {
resetAllConnectionVerdicts()
return nil
@@ -52,10 +51,20 @@ func prep() error {
// Reset connections every time profile changes
err = module.RegisterEventHook(
"profiles",
profileConfigChangeEvent,
"reset connection verdicts",
func(ctx context.Context, _ interface{}) error {
resetAllConnectionVerdicts()
profile.ConfigChangeEvent,
"reset connection verdicts after profile config change",
func(ctx context.Context, eventData interface{}) error {
// Expected event data: scoped profile ID.
profileID, ok := eventData.(string)
if !ok {
return fmt.Errorf("event data is not a string: %v", eventData)
}
profileSource, profileID, ok := strings.Cut(profileID, "/")
if !ok {
return fmt.Errorf("event data does not seem to be a scoped profile ID: %v", eventData)
}
resetProfileConnectionVerdict(profileSource, profileID)
return nil
},
)
@@ -67,8 +76,8 @@ func prep() error {
// connect and disconnecting is triggered on config change event but connecting takеs more time
err = module.RegisterEventHook(
"captain",
onSPNConnectEvent,
"reset connection verdicts",
captain.SPNConnectedEvent,
"reset connection verdicts on SPN connect",
func(ctx context.Context, _ interface{}) error {
resetAllConnectionVerdicts()
return nil
@@ -83,7 +92,7 @@ func prep() error {
err = module.RegisterEventHook(
"access",
access.AccountUpdateEvent,
"update connection feature flags",
"update connection feature flags after account update",
func(ctx context.Context, _ interface{}) error {
resetAllConnectionVerdicts()
return nil
@@ -93,6 +102,24 @@ func prep() error {
log.Errorf("filter: failed to register event hook: %s", err)
}
err = module.RegisterEventHook(
"network",
network.ConnectionReattributedEvent,
"reset verdict of re-attributed connection",
func(ctx context.Context, eventData interface{}) error {
// Expected event data: connection ID.
connID, ok := eventData.(string)
if !ok {
return fmt.Errorf("event data is not a string: %v", eventData)
}
resetSingleConnectionVerdict(connID)
return nil
},
)
if err != nil {
log.Errorf("filter: failed to register event hook: %s", err)
}
if err := registerConfig(); err != nil {
return err
}

View File

@@ -43,13 +43,55 @@ var (
ownPID = os.Getpid()
)
func resetAllConnectionVerdicts() {
// Resetting will force all the connection to be evaluated by the firewall again
// this will set new verdicts if configuration was update or spn has been disabled or enabled.
log.Info("interception: re-evaluating all connections")
func resetSingleConnectionVerdict(connID string) {
// Create tracing context.
ctx, tracer := log.AddTracer(context.Background())
defer tracer.Submit()
conn, ok := network.GetConnection(connID)
if !ok {
conn, ok = network.GetDNSConnection(connID)
if !ok {
tracer.Debugf("filter: could not find re-attributed connection %s for re-evaluation", connID)
return
}
}
resetConnectionVerdict(ctx, conn)
}
func resetProfileConnectionVerdict(profileSource, profileID string) {
// Create tracing context.
ctx, tracer := log.AddTracer(context.Background())
defer tracer.Submit()
// Resetting will force all the connection to be evaluated by the firewall again
// this will set new verdicts if configuration was update or spn has been disabled or enabled.
tracer.Infof("filter: re-evaluating connections of %s/%s", profileSource, profileID)
// Re-evaluate all connections.
var changedVerdicts int
for _, conn := range network.GetAllConnections() {
// Check if connection is complete and attributed to the deleted profile.
if conn.DataIsComplete() &&
conn.ProcessContext.Profile == profileID &&
conn.ProcessContext.Source == profileSource {
if resetConnectionVerdict(ctx, conn) {
changedVerdicts++
}
}
}
tracer.Infof("filter: changed verdict on %d connections", changedVerdicts)
}
func resetAllConnectionVerdicts() {
// Create tracing context.
ctx, tracer := log.AddTracer(context.Background())
defer tracer.Submit()
// Resetting will force all the connection to be evaluated by the firewall again
// this will set new verdicts if configuration was update or spn has been disabled or enabled.
tracer.Info("filter: re-evaluating all connections")
// Re-evaluate all connections.
var changedVerdicts int
@@ -59,54 +101,60 @@ func resetAllConnectionVerdicts() {
continue
}
func() {
conn.Lock()
defer conn.Unlock()
// Update feature flags.
if err := conn.UpdateFeatures(); err != nil && !errors.Is(err, access.ErrNotLoggedIn) {
tracer.Warningf("network: failed to update connection feature flags: %s", err)
}
// Skip internal connections:
// - Pre-authenticated connections from Portmaster
// - Redirected DNS requests
// - SPN Uplink to Home Hub
if conn.Internal {
tracer.Tracef("filter: skipping internal connection %s", conn)
return
}
tracer.Debugf("filter: re-evaluating verdict of %s", conn)
previousVerdict := conn.Verdict.Firewall
// Apply privacy filter and check tunneling.
FilterConnection(ctx, conn, nil, true, true)
// Stop existing SPN tunnel if not needed anymore.
if conn.Verdict.Active != network.VerdictRerouteToTunnel && conn.TunnelContext != nil {
err := conn.TunnelContext.StopTunnel()
if err != nil {
tracer.Debugf("filter: failed to stopped unneeded tunnel: %s", err)
}
}
// Save if verdict changed.
if conn.Verdict.Firewall != previousVerdict {
err := interception.UpdateVerdictOfConnection(conn)
if err != nil {
log.Debugf("filter: failed to update connection verdict: %s", err)
}
conn.Save()
tracer.Infof("filter: verdict of connection %s changed from %s to %s", conn, previousVerdict.Verb(), conn.VerdictVerb())
changedVerdicts++
} else {
tracer.Tracef("filter: verdict to connection %s unchanged at %s", conn, conn.VerdictVerb())
}
}()
if resetConnectionVerdict(ctx, conn) {
changedVerdicts++
}
}
tracer.Infof("filter: changed verdict on %d connections", changedVerdicts)
tracer.Submit()
}
func resetConnectionVerdict(ctx context.Context, conn *network.Connection) (verdictChanged bool) {
tracer := log.Tracer(ctx)
conn.Lock()
defer conn.Unlock()
// Update feature flags.
if err := conn.UpdateFeatures(); err != nil && !errors.Is(err, access.ErrNotLoggedIn) {
tracer.Warningf("filter: failed to update connection feature flags: %s", err)
}
// Skip internal connections:
// - Pre-authenticated connections from Portmaster
// - Redirected DNS requests
// - SPN Uplink to Home Hub
if conn.Internal {
// tracer.Tracef("filter: skipping internal connection %s", conn)
return false
}
tracer.Debugf("filter: re-evaluating verdict of %s", conn)
previousVerdict := conn.Verdict.Firewall
// Apply privacy filter and check tunneling.
FilterConnection(ctx, conn, nil, true, true)
// Stop existing SPN tunnel if not needed anymore.
if conn.Verdict.Active != network.VerdictRerouteToTunnel && conn.TunnelContext != nil {
err := conn.TunnelContext.StopTunnel()
if err != nil {
tracer.Debugf("filter: failed to stopped unneeded tunnel: %s", err)
}
}
// Save if verdict changed.
if conn.Verdict.Firewall != previousVerdict {
err := interception.UpdateVerdictOfConnection(conn)
if err != nil {
log.Debugf("filter: failed to update connection verdict: %s", err)
}
conn.Save()
tracer.Infof("filter: verdict of connection %s changed from %s to %s", conn, previousVerdict.Verb(), conn.VerdictVerb())
return true
}
tracer.Tracef("filter: verdict to connection %s unchanged at %s", conn, conn.VerdictVerb())
return false
}
// SetNameserverIPMatcher sets a function that is used to match the internal