mirror of
https://github.com/safing/portmaster
synced 2025-04-22 03:49:09 +00:00
* Move portbase into monorepo * Add new simple module mgr * [WIP] Switch to new simple module mgr * Add StateMgr and more worker variants * [WIP] Switch more modules * [WIP] Switch more modules * [WIP] swtich more modules * [WIP] switch all SPN modules * [WIP] switch all service modules * [WIP] Convert all workers to the new module system * [WIP] add new task system to module manager * [WIP] Add second take for scheduling workers * [WIP] Add FIXME for bugs in new scheduler * [WIP] Add minor improvements to scheduler * [WIP] Add new worker scheduler * [WIP] Fix more bug related to new module system * [WIP] Fix start handing of the new module system * [WIP] Improve startup process * [WIP] Fix minor issues * [WIP] Fix missing subsystem in settings * [WIP] Initialize managers in constructor * [WIP] Move module event initialization to constrictors * [WIP] Fix setting for enabling and disabling the SPN module * [WIP] Move API registeration into module construction * [WIP] Update states mgr for all modules * [WIP] Add CmdLine operation support * Add state helper methods to module group and instance * Add notification and module status handling to status package * Fix starting issues * Remove pilot widget and update security lock to new status data * Remove debug logs * Improve http server shutdown * Add workaround for cleanly shutting down firewall+netquery * Improve logging * Add syncing states with notifications for new module system * Improve starting, stopping, shutdown; resolve FIXMEs/TODOs * [WIP] Fix most unit tests * Review new module system and fix minor issues * Push shutdown and restart events again via API * Set sleep mode via interface * Update example/template module * [WIP] Fix spn/cabin unit test * Remove deprecated UI elements * Make log output more similar for the logging transition phase * Switch spn hub and observer cmds to new module system * Fix log sources * Make worker mgr less error prone * Fix tests and minor issues * Fix observation hub * Improve shutdown and restart handling * Split up big connection.go source file * Move varint and dsd packages to structures repo * Improve expansion test * Fix linter warnings * Fix interception module on windows * Fix linter errors --------- Co-authored-by: Vladimir Stoilov <vladimir@safing.io>
330 lines
9.5 KiB
Go
330 lines
9.5 KiB
Go
package navigator
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/safing/portmaster/base/log"
|
|
"github.com/safing/portmaster/service/intel"
|
|
"github.com/safing/portmaster/service/profile"
|
|
"github.com/safing/portmaster/service/profile/endpoints"
|
|
"github.com/safing/portmaster/spn/hub"
|
|
)
|
|
|
|
// HubType is the usage type of a Hub in routing.
|
|
type HubType uint8
|
|
|
|
// Hub Types.
|
|
const (
|
|
HomeHub HubType = iota
|
|
TransitHub
|
|
DestinationHub
|
|
)
|
|
|
|
// DeriveTunnelOptions derives and returns the tunnel options from the connection and profile.
|
|
// This function lives in firewall/tunnel.go and is set here to avoid import loops.
|
|
var DeriveTunnelOptions func(lp *profile.LayeredProfile, destination *intel.Entity, connEncrypted bool) *Options
|
|
|
|
// Options holds configuration options for operations with the Map.
|
|
type Options struct { //nolint:maligned
|
|
// Home holds the options for Home Hubs.
|
|
Home *HomeHubOptions
|
|
|
|
// Transit holds the options for Transit Hubs.
|
|
Transit *TransitHubOptions
|
|
|
|
// Destination holds the options for Destination Hubs.
|
|
Destination *DestinationHubOptions
|
|
|
|
// RoutingProfile defines the algorithm to use to find a route.
|
|
RoutingProfile string
|
|
}
|
|
|
|
// HomeHubOptions holds configuration options for Home Hub operations with the Map.
|
|
type HomeHubOptions HubOptions
|
|
|
|
// TransitHubOptions holds configuration options for Transit Hub operations with the Map.
|
|
type TransitHubOptions HubOptions
|
|
|
|
// DestinationHubOptions holds configuration options for Destination Hub operations with the Map.
|
|
type DestinationHubOptions HubOptions
|
|
|
|
// HubOptions holds configuration options for a specific hub type for operations with the Map.
|
|
type HubOptions struct {
|
|
// Regard holds required States. Only Hubs where all of these are present
|
|
// will taken into account for the operation. If NoDefaults is not set, a
|
|
// basic set of desirable states is added automatically.
|
|
Regard PinState
|
|
|
|
// Disregard holds disqualifying States. Only Hubs where none of these are
|
|
// present will be taken into account for the operation. If NoDefaults is not
|
|
// set, a basic set of undesirable states is added automatically.
|
|
Disregard PinState
|
|
|
|
// NoDefaults declares whether default and recommended Regard and Disregard states should not be used.
|
|
NoDefaults bool
|
|
|
|
// HubPolicies is a collection of endpoint lists that Hubs must pass in order
|
|
// to be taken into account for the operation.
|
|
HubPolicies []endpoints.Endpoints
|
|
|
|
// RequireVerifiedOwners specifies which verified owners are allowed to be used.
|
|
// If the list is empty, all owners are allowed.
|
|
RequireVerifiedOwners []string
|
|
|
|
// CheckHubPolicyWith provides an entity that must match the Hubs entry or exit
|
|
// policy (depending on type) in order to be taken into account for the operation.
|
|
CheckHubPolicyWith *intel.Entity
|
|
}
|
|
|
|
// Copy returns a shallow copy of the Options.
|
|
func (o *Options) Copy() *Options {
|
|
copied := &Options{
|
|
RoutingProfile: o.RoutingProfile,
|
|
}
|
|
if o.Home != nil {
|
|
c := HomeHubOptions(HubOptions(*o.Home).Copy())
|
|
copied.Home = &c
|
|
}
|
|
if o.Transit != nil {
|
|
c := TransitHubOptions(HubOptions(*o.Transit).Copy())
|
|
copied.Transit = &c
|
|
}
|
|
if o.Destination != nil {
|
|
c := DestinationHubOptions(HubOptions(*o.Destination).Copy())
|
|
copied.Destination = &c
|
|
}
|
|
return copied
|
|
}
|
|
|
|
// Copy returns a shallow copy of the Options.
|
|
func (o HubOptions) Copy() HubOptions {
|
|
return HubOptions{
|
|
Regard: o.Regard,
|
|
Disregard: o.Disregard,
|
|
NoDefaults: o.NoDefaults,
|
|
HubPolicies: o.HubPolicies,
|
|
RequireVerifiedOwners: o.RequireVerifiedOwners,
|
|
CheckHubPolicyWith: o.CheckHubPolicyWith,
|
|
}
|
|
}
|
|
|
|
// PinMatcher is a stateful matching function generated by Options.
|
|
type PinMatcher func(pin *Pin) bool
|
|
|
|
// DefaultOptions returns the default options for this Map.
|
|
func (m *Map) DefaultOptions() *Options {
|
|
m.Lock()
|
|
defer m.Unlock()
|
|
|
|
return m.defaultOptions()
|
|
}
|
|
|
|
func (m *Map) defaultOptions() *Options {
|
|
opts := &Options{
|
|
RoutingProfile: DefaultRoutingProfileID,
|
|
}
|
|
|
|
return opts
|
|
}
|
|
|
|
// HubPoliciesAreSet returns whether any of the given hub policies are set and non-empty.
|
|
func HubPoliciesAreSet(policies []endpoints.Endpoints) bool {
|
|
for _, policy := range policies {
|
|
if policy.IsSet() {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
var emptyHubOptions = &HubOptions{}
|
|
|
|
// Matcher generates a PinMatcher based on the Options.
|
|
func (o *HomeHubOptions) Matcher(hubIntel *hub.Intel) PinMatcher {
|
|
if o == nil {
|
|
return emptyHubOptions.Matcher(HomeHub, hubIntel)
|
|
}
|
|
|
|
// Convert and call base func.
|
|
ho := HubOptions(*o)
|
|
return ho.Matcher(HomeHub, hubIntel)
|
|
}
|
|
|
|
// Matcher generates a PinMatcher based on the Options.
|
|
func (o *TransitHubOptions) Matcher(hubIntel *hub.Intel) PinMatcher {
|
|
if o == nil {
|
|
return emptyHubOptions.Matcher(TransitHub, hubIntel)
|
|
}
|
|
|
|
// Convert and call base func.
|
|
ho := HubOptions(*o)
|
|
return ho.Matcher(TransitHub, hubIntel)
|
|
}
|
|
|
|
// Matcher generates a PinMatcher based on the Options.
|
|
func (o *DestinationHubOptions) Matcher(hubIntel *hub.Intel) PinMatcher {
|
|
if o == nil {
|
|
return emptyHubOptions.Matcher(DestinationHub, hubIntel)
|
|
}
|
|
|
|
// Convert and call base func.
|
|
ho := HubOptions(*o)
|
|
return ho.Matcher(DestinationHub, hubIntel)
|
|
}
|
|
|
|
// Matcher generates a PinMatcher based on the Options.
|
|
// Always use the Matcher on option structs if you can.
|
|
func (o *Options) Matcher(hubType HubType, hubIntel *hub.Intel) PinMatcher {
|
|
switch hubType {
|
|
case HomeHub:
|
|
return o.Home.Matcher(hubIntel)
|
|
case TransitHub:
|
|
return o.Transit.Matcher(hubIntel)
|
|
case DestinationHub:
|
|
return o.Destination.Matcher(hubIntel)
|
|
default:
|
|
return nil // This will panic, but should never be used.
|
|
}
|
|
}
|
|
|
|
// Matcher generates a PinMatcher based on the Options.
|
|
func (o *HubOptions) Matcher(hubType HubType, hubIntel *hub.Intel) PinMatcher {
|
|
// Fallback to empty hub options.
|
|
if o == nil {
|
|
o = emptyHubOptions
|
|
}
|
|
|
|
// Compile states to regard and disregard.
|
|
regard := o.Regard
|
|
disregard := o.Disregard
|
|
|
|
// Add default states.
|
|
if !o.NoDefaults {
|
|
// Add default States.
|
|
regard = regard.Add(StateSummaryRegard)
|
|
disregard = disregard.Add(StateSummaryDisregard)
|
|
|
|
// Add type based Advisories.
|
|
switch hubType {
|
|
case HomeHub:
|
|
// Home Hubs don't need to be reachable and don't need keys ready to be used.
|
|
regard = regard.Remove(StateReachable)
|
|
regard = regard.Remove(StateActive)
|
|
// Follow advisory.
|
|
disregard = disregard.Add(StateUsageAsHomeDiscouraged)
|
|
// Home Hub may be the current Home Hub.
|
|
disregard = disregard.Remove(StateIsHomeHub)
|
|
case TransitHub:
|
|
// Transit Hubs get no additional states.
|
|
case DestinationHub:
|
|
// Follow advisory.
|
|
disregard = disregard.Add(StateUsageAsDestinationDiscouraged)
|
|
// Do not use if Hub reports network issues.
|
|
disregard = disregard.Add(StateConnectivityIssues)
|
|
}
|
|
}
|
|
|
|
// Add intel policies.
|
|
hubPolicies := o.HubPolicies
|
|
if hubIntel != nil && hubIntel.Parsed() != nil {
|
|
switch hubType {
|
|
case HomeHub:
|
|
hubPolicies = append(hubPolicies, hubIntel.Parsed().HubAdvisory, hubIntel.Parsed().HomeHubAdvisory)
|
|
case TransitHub:
|
|
hubPolicies = append(hubPolicies, hubIntel.Parsed().HubAdvisory)
|
|
case DestinationHub:
|
|
hubPolicies = append(hubPolicies, hubIntel.Parsed().HubAdvisory, hubIntel.Parsed().DestinationHubAdvisory)
|
|
}
|
|
}
|
|
|
|
// Add entry/exit policiy checks.
|
|
checkHubPolicyWith := o.CheckHubPolicyWith
|
|
|
|
return func(pin *Pin) bool {
|
|
// Check required Pin States.
|
|
if !pin.State.Has(regard) || pin.State.HasAnyOf(disregard) {
|
|
return false
|
|
}
|
|
|
|
// Check verified owners.
|
|
if len(o.RequireVerifiedOwners) > 0 {
|
|
// Check if Pin has a verified owner at all.
|
|
if pin.VerifiedOwner == "" {
|
|
return false
|
|
}
|
|
|
|
// Check if verified owner is in the list.
|
|
inList := false
|
|
for _, allowed := range o.RequireVerifiedOwners {
|
|
if pin.VerifiedOwner == allowed {
|
|
inList = true
|
|
break
|
|
}
|
|
}
|
|
|
|
// Pin does not have a verified owner from the allowed list.
|
|
if !inList {
|
|
return false
|
|
}
|
|
}
|
|
|
|
// Check policies.
|
|
policyCheck:
|
|
for _, policy := range hubPolicies {
|
|
// Check if policy is set.
|
|
if !policy.IsSet() {
|
|
continue
|
|
}
|
|
|
|
// Check if policy matches.
|
|
result, reason := policy.MatchMulti(context.TODO(), pin.EntityV4, pin.EntityV6)
|
|
switch result {
|
|
case endpoints.NoMatch:
|
|
// Continue with check.
|
|
case endpoints.MatchError:
|
|
log.Warningf("spn/navigator: failed to match policy: %s", reason)
|
|
// Continue with check for now.
|
|
// TODO: Rethink how to do this. If eg. the geoip database has a
|
|
// problem, then no Hub will match. For now, just continue to the
|
|
// next rule set. Not optimal, but fail safe.
|
|
case endpoints.Denied:
|
|
// Explicitly denied, abort immediately.
|
|
return false
|
|
case endpoints.Permitted:
|
|
// Explicitly allowed, abort check and continue.
|
|
break policyCheck
|
|
}
|
|
}
|
|
|
|
// Check entry/exit policies.
|
|
if checkHubPolicyWith != nil {
|
|
switch hubType {
|
|
case HomeHub:
|
|
if endpointListMatch(pin.Hub.Info.EntryPolicy(), checkHubPolicyWith) == endpoints.Denied {
|
|
// Hub does not allow entry from the given entity.
|
|
return false
|
|
}
|
|
case TransitHub:
|
|
// Transit Hubs do not have a hub policy.
|
|
case DestinationHub:
|
|
if endpointListMatch(pin.Hub.Info.ExitPolicy(), checkHubPolicyWith) == endpoints.Denied {
|
|
// Hub does not allow exit to the given entity.
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
return true // All checks have passed.
|
|
}
|
|
}
|
|
|
|
func endpointListMatch(list endpoints.Endpoints, entity *intel.Entity) endpoints.EPResult {
|
|
// Check if endpoint list and entity are available.
|
|
if !list.IsSet() || entity == nil {
|
|
return endpoints.NoMatch
|
|
}
|
|
|
|
// Match and return result only.
|
|
result, _ := list.Match(context.TODO(), entity)
|
|
return result
|
|
}
|