Related Issue Details: In some situations, SPN intel data was not fully applied. This lead to Portmaster making connections that don’t align with the intended intel preferences. https://github.com/safing/portmaster/issues/1999 https://github.com/safing/portmaster-shadow/issues/35
227 lines
5.5 KiB
Go
227 lines
5.5 KiB
Go
package navigator
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
|
|
"golang.org/x/exp/slices"
|
|
|
|
"github.com/safing/portmaster/base/log"
|
|
"github.com/safing/portmaster/service/intel/geoip"
|
|
"github.com/safing/portmaster/service/profile/endpoints"
|
|
"github.com/safing/portmaster/spn/hub"
|
|
)
|
|
|
|
// UpdateIntel supplies the map with new intel data. The data is not copied, so
|
|
// it must not be modified after being supplied. If the map is empty, the
|
|
// bootstrap hubs will be added to the map.
|
|
func (m *Map) UpdateIntel(update *hub.Intel, trustNodes []string) error {
|
|
// Check if intel data is already parsed.
|
|
if update.Parsed() == nil {
|
|
return errors.New("intel data is not parsed")
|
|
}
|
|
|
|
m.Lock()
|
|
defer m.Unlock()
|
|
|
|
// Update the map's reference to the intel data.
|
|
m.intel = update
|
|
|
|
// Update pins with new intel data.
|
|
for _, pin := range m.all {
|
|
// Add/Update location data from IP addresses.
|
|
pin.updateLocationData()
|
|
|
|
// Override Pin Data.
|
|
m.updateInfoOverrides(pin)
|
|
|
|
// Update Trust and Advisory Statuses.
|
|
m.updateIntelStatuses(pin, trustNodes)
|
|
|
|
// Push changes.
|
|
// TODO: Only set when pin changed.
|
|
pin.pushChanges.Set()
|
|
}
|
|
|
|
// Configure the map's regions.
|
|
m.updateRegions(m.intel.Regions)
|
|
|
|
// Push pin changes.
|
|
m.PushPinChanges()
|
|
|
|
log.Infof("spn/navigator: updated intel on map %s", m.Name)
|
|
|
|
// Add bootstrap hubs if map is empty.
|
|
if m.isEmpty() {
|
|
return m.addBootstrapHubs(m.intel.BootstrapHubs)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetIntel returns the map's intel data.
|
|
func (m *Map) GetIntel() *hub.Intel {
|
|
m.RLock()
|
|
defer m.RUnlock()
|
|
|
|
return m.intel
|
|
}
|
|
|
|
func (m *Map) updateIntelStatuses(pin *Pin, trustNodes []string) {
|
|
// Reset all related states (StateSummaryStatusesAppliedFromIntel).
|
|
pin.stateIntelApplied.UnSet()
|
|
pin.removeStates(StateTrusted | StateUsageDiscouraged | StateUsageAsHomeDiscouraged | StateUsageAsDestinationDiscouraged) // same as: StateSummaryStatusesAppliedFromIntel
|
|
|
|
// Check if Intel data is loaded.
|
|
if m.intel == nil {
|
|
return
|
|
}
|
|
|
|
// Indicate that intel statuses have been applied to the pin
|
|
defer pin.stateIntelApplied.Set()
|
|
|
|
// Check Hub Intel
|
|
hubIntel, ok := m.intel.Hubs[pin.Hub.ID]
|
|
if ok {
|
|
// Apply the verified owner, if any.
|
|
pin.VerifiedOwner = hubIntel.VerifiedOwner
|
|
|
|
// Check if Hub is discontinued.
|
|
if hubIntel.Discontinued {
|
|
// Reset state, set offline and return.
|
|
pin.State = StateNone
|
|
pin.addStates(StateOffline)
|
|
return
|
|
}
|
|
|
|
// Check if Hub is trusted.
|
|
if hubIntel.Trusted {
|
|
pin.addStates(StateTrusted)
|
|
}
|
|
}
|
|
|
|
// Check manual trust status.
|
|
switch {
|
|
case slices.Contains[[]string, string](trustNodes, pin.VerifiedOwner):
|
|
pin.addStates(StateTrusted)
|
|
case slices.Contains[[]string, string](trustNodes, pin.Hub.ID):
|
|
pin.addStates(StateTrusted)
|
|
}
|
|
|
|
// Check advisories.
|
|
// Check for UsageDiscouraged.
|
|
checkStatusList(
|
|
pin,
|
|
StateUsageDiscouraged,
|
|
m.intel.AdviseOnlyTrustedHubs,
|
|
m.intel.Parsed().HubAdvisory,
|
|
)
|
|
// Check for UsageAsHomeDiscouraged.
|
|
checkStatusList(
|
|
pin,
|
|
StateUsageAsHomeDiscouraged,
|
|
m.intel.AdviseOnlyTrustedHomeHubs,
|
|
m.intel.Parsed().HomeHubAdvisory,
|
|
)
|
|
// Check for UsageAsDestinationDiscouraged.
|
|
checkStatusList(
|
|
pin,
|
|
StateUsageAsDestinationDiscouraged,
|
|
m.intel.AdviseOnlyTrustedDestinationHubs,
|
|
m.intel.Parsed().DestinationHubAdvisory,
|
|
)
|
|
}
|
|
|
|
func checkStatusList(pin *Pin, state PinState, requireTrusted bool, endpointList endpoints.Endpoints) {
|
|
if requireTrusted && !pin.State.Has(StateTrusted) {
|
|
pin.addStates(state)
|
|
return
|
|
}
|
|
|
|
if pin.EntityV4 != nil {
|
|
result, _ := endpointList.Match(context.TODO(), pin.EntityV4)
|
|
if result == endpoints.Denied {
|
|
pin.addStates(state)
|
|
return
|
|
}
|
|
}
|
|
|
|
if pin.EntityV6 != nil {
|
|
result, _ := endpointList.Match(context.TODO(), pin.EntityV6)
|
|
if result == endpoints.Denied {
|
|
pin.addStates(state)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (m *Map) updateInfoOverrides(pin *Pin) {
|
|
// Check if Intel data is loaded and if there are any overrides.
|
|
if m.intel == nil {
|
|
return
|
|
}
|
|
|
|
// Get overrides for this pin.
|
|
hubIntel, ok := m.intel.Hubs[pin.Hub.ID]
|
|
if !ok || hubIntel.Override == nil {
|
|
return
|
|
}
|
|
overrides := hubIntel.Override
|
|
|
|
// Apply overrides
|
|
if overrides.CountryCode != "" {
|
|
if pin.LocationV4 != nil {
|
|
pin.LocationV4.Country = geoip.GetCountryInfo(overrides.CountryCode)
|
|
}
|
|
if pin.EntityV4 != nil {
|
|
pin.EntityV4.Country = overrides.CountryCode
|
|
}
|
|
if pin.LocationV6 != nil {
|
|
pin.LocationV6.Country = geoip.GetCountryInfo(overrides.CountryCode)
|
|
}
|
|
if pin.EntityV6 != nil {
|
|
pin.EntityV6.Country = overrides.CountryCode
|
|
}
|
|
}
|
|
if overrides.Coordinates != nil {
|
|
if pin.LocationV4 != nil {
|
|
pin.LocationV4.Coordinates = *overrides.Coordinates
|
|
}
|
|
if pin.EntityV4 != nil {
|
|
pin.EntityV4.Coordinates = overrides.Coordinates
|
|
}
|
|
if pin.LocationV6 != nil {
|
|
pin.LocationV6.Coordinates = *overrides.Coordinates
|
|
}
|
|
if pin.EntityV6 != nil {
|
|
pin.EntityV6.Coordinates = overrides.Coordinates
|
|
}
|
|
}
|
|
if overrides.ASN != 0 {
|
|
if pin.LocationV4 != nil {
|
|
pin.LocationV4.AutonomousSystemNumber = overrides.ASN
|
|
}
|
|
if pin.EntityV4 != nil {
|
|
pin.EntityV4.ASN = overrides.ASN
|
|
}
|
|
if pin.LocationV6 != nil {
|
|
pin.LocationV6.AutonomousSystemNumber = overrides.ASN
|
|
}
|
|
if pin.EntityV6 != nil {
|
|
pin.EntityV6.ASN = overrides.ASN
|
|
}
|
|
}
|
|
if overrides.ASOrg != "" {
|
|
if pin.LocationV4 != nil {
|
|
pin.LocationV4.AutonomousSystemOrganization = overrides.ASOrg
|
|
}
|
|
if pin.EntityV4 != nil {
|
|
pin.EntityV4.ASOrg = overrides.ASOrg
|
|
}
|
|
if pin.LocationV6 != nil {
|
|
pin.LocationV6.AutonomousSystemOrganization = overrides.ASOrg
|
|
}
|
|
if pin.EntityV6 != nil {
|
|
pin.EntityV6.ASOrg = overrides.ASOrg
|
|
}
|
|
}
|
|
}
|