From c9b9a0b1d19506d6b985e8b58ba182e2601410b6 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 9 Jun 2022 13:48:55 +0200 Subject: [PATCH 01/13] Improve read rights on log files --- cmds/portmaster-start/logs.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmds/portmaster-start/logs.go b/cmds/portmaster-start/logs.go index d14f1250..09fa01c6 100644 --- a/cmds/portmaster-start/logs.go +++ b/cmds/portmaster-start/logs.go @@ -17,7 +17,7 @@ import ( ) func initializeLogFile(logFilePath string, identifier string, version string) *os.File { - logFile, err := os.OpenFile(logFilePath, os.O_RDWR|os.O_CREATE, 0o0444) + logFile, err := os.OpenFile(logFilePath, os.O_RDWR|os.O_CREATE, 0o0440) if err != nil { log.Printf("failed to create log file %s: %s\n", logFilePath, err) return nil From 0439894efc3097214178a31e70718f66b81c4c09 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 9 Jun 2022 13:49:34 +0200 Subject: [PATCH 02/13] Suppress error when stopping during filterlist update --- intel/filterlists/updater.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/intel/filterlists/updater.go b/intel/filterlists/updater.go index ff5ce3e9..7d15e85e 100644 --- a/intel/filterlists/updater.go +++ b/intel/filterlists/updater.go @@ -24,6 +24,11 @@ var updateInProgress = abool.New() func tryListUpdate(ctx context.Context) error { err := performUpdate(ctx) if err != nil { + // Check if we are shutting down. + if module.IsStopping() { + return nil + } + // Check if the module already has a failure status set. If not, set a // generic one with the returned error. failureStatus, _, _ := module.FailureStatus() From c442a7e51c1c605a6f016ebe9a7591ffb5840e73 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 9 Jun 2022 13:50:18 +0200 Subject: [PATCH 03/13] Deactivate IPv6 integrations when no IPv6 stack is detected --- firewall/interception/nfqueue_linux.go | 52 ++++++++++++++++---------- nameserver/module.go | 6 ++- netenv/main.go | 23 ++++++++++++ resolver/resolver-mdns.go | 51 +++++++++++++------------ 4 files changed, 89 insertions(+), 43 deletions(-) diff --git a/firewall/interception/nfqueue_linux.go b/firewall/interception/nfqueue_linux.go index f117cd67..9d731648 100644 --- a/firewall/interception/nfqueue_linux.go +++ b/firewall/interception/nfqueue_linux.go @@ -10,8 +10,8 @@ import ( "github.com/hashicorp/go-multierror" "github.com/safing/portbase/log" - "github.com/safing/portbase/notifications" "github.com/safing/portmaster/firewall/interception/nfq" + "github.com/safing/portmaster/netenv" "github.com/safing/portmaster/network/packet" ) @@ -141,13 +141,10 @@ func activateNfqueueFirewall() error { return err } - if err := activateIPTables(iptables.ProtocolIPv6, v6rules, v6once, v6chains); err != nil { - notifications.NotifyError( - "interception:ipv6-possibly-disabled", - "Is IPv6 enabled?", - "The Portmaster succeeded with IPv4 network integration, but failed with IPv6 integration. Please make sure IPv6 is enabled on your device.", - ) - return err + if netenv.IPv6Enabled() { + if err := activateIPTables(iptables.ProtocolIPv6, v6rules, v6once, v6chains); err != nil { + return err + } } return nil @@ -163,8 +160,10 @@ func DeactivateNfqueueFirewall() error { } // IPv6 - if err := deactivateIPTables(iptables.ProtocolIPv6, v6once, v6chains); err != nil { - result = multierror.Append(result, err) + if netenv.IPv6Enabled() { + if err := deactivateIPTables(iptables.ProtocolIPv6, v6once, v6chains); err != nil { + result = multierror.Append(result, err) + } } return result.ErrorOrNil() @@ -264,15 +263,22 @@ func StartNfqueueInterception(packets chan<- packet.Packet) (err error) { _ = Stop() return fmt.Errorf("nfqueue(IPv4, in): %w", err) } - out6Queue, err = nfq.New(17060, true) - if err != nil { - _ = Stop() - return fmt.Errorf("nfqueue(IPv6, out): %w", err) - } - in6Queue, err = nfq.New(17160, true) - if err != nil { - _ = Stop() - return fmt.Errorf("nfqueue(IPv6, in): %w", err) + + if netenv.IPv6Enabled() { + out6Queue, err = nfq.New(17060, true) + if err != nil { + _ = Stop() + return fmt.Errorf("nfqueue(IPv6, out): %w", err) + } + in6Queue, err = nfq.New(17160, true) + if err != nil { + _ = Stop() + return fmt.Errorf("nfqueue(IPv6, in): %w", err) + } + } else { + log.Warningf("interception: no IPv6 stack detected, disabling IPv6 network integration") + out6Queue = &disabledNfQueue{} + in6Queue = &disabledNfQueue{} } go handleInterception(packets) @@ -327,3 +333,11 @@ func handleInterception(packets chan<- packet.Packet) { } } } + +type disabledNfQueue struct{} + +func (dnfq *disabledNfQueue) PacketChannel() <-chan packet.Packet { + return nil +} + +func (dnfq *disabledNfQueue) Destroy() {} diff --git a/nameserver/module.go b/nameserver/module.go index f5165585..ed7eb740 100644 --- a/nameserver/module.go +++ b/nameserver/module.go @@ -259,7 +259,11 @@ func getListenAddresses(listenAddress string) (ip1, ip2 net.IP, port uint16, err // listen separately for IPv4 and IPv6. if ipString == "localhost" { ip1 = net.IPv4(127, 0, 0, 17) - ip2 = net.IPv6loopback + if netenv.IPv6Enabled() { + ip2 = net.IPv6loopback + } else { + log.Warningf("nameserver: no IPv6 stack detected, disabling IPv6 nameserver listener") + } } else { ip1 = net.ParseIP(ipString) if ip1 == nil { diff --git a/netenv/main.go b/netenv/main.go index 0d831e76..232696cd 100644 --- a/netenv/main.go +++ b/netenv/main.go @@ -1,7 +1,9 @@ package netenv import ( + "github.com/safing/portbase/log" "github.com/safing/portbase/modules" + "github.com/tevino/abool" ) // Event Names. @@ -20,6 +22,8 @@ func init() { } func prep() error { + checkForIPv6Stack() + if err := registerAPIEndpoints(); err != nil { return err } @@ -46,3 +50,22 @@ func start() error { return nil } + +var ipv6Enabled = abool.NewBool(true) + +// IPv6Enabled returns whether the device has an active IPv6 stack. +// This is only checked once on startup in order to maintain consistency. +func IPv6Enabled() bool { + return ipv6Enabled.IsSet() +} + +func checkForIPv6Stack() { + _, v6IPs, err := GetAssignedAddresses() + if err != nil { + log.Warningf("netenv: failed to get assigned addresses to check for ipv6 stack: %s", err) + return + } + + // Set IPv6 as enabled if any IPv6 addresses are found. + ipv6Enabled.SetTo(len(v6IPs) > 0) +} diff --git a/resolver/resolver-mdns.go b/resolver/resolver-mdns.go index ce0eab6d..29677350 100644 --- a/resolver/resolver-mdns.go +++ b/resolver/resolver-mdns.go @@ -12,6 +12,7 @@ import ( "github.com/miekg/dns" "github.com/safing/portbase/log" + "github.com/safing/portmaster/netenv" "github.com/safing/portmaster/network/netutils" ) @@ -91,19 +92,6 @@ func listenToMDNS(ctx context.Context) error { }() } - multicast6Conn, err = net.ListenMulticastUDP("udp6", nil, &net.UDPAddr{IP: net.IP([]byte{0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb}), Port: 5353}) - if err != nil { - // TODO: retry after some time - log.Warningf("intel(mdns): failed to create udp6 listen multicast socket: %s", err) - } else { - module.StartServiceWorker("mdns udp6 multicast listener", 0, func(ctx context.Context) error { - return listenForDNSPackets(ctx, multicast6Conn, messages) - }) - defer func() { - _ = multicast6Conn.Close() - }() - } - unicast4Conn, err = net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4zero, Port: 0}) if err != nil { // TODO: retry after some time @@ -117,17 +105,34 @@ func listenToMDNS(ctx context.Context) error { }() } - unicast6Conn, err = net.ListenUDP("udp6", &net.UDPAddr{IP: net.IPv6zero, Port: 0}) - if err != nil { - // TODO: retry after some time - log.Warningf("intel(mdns): failed to create udp6 listen socket: %s", err) + if netenv.IPv6Enabled() { + multicast6Conn, err = net.ListenMulticastUDP("udp6", nil, &net.UDPAddr{IP: net.IP([]byte{0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb}), Port: 5353}) + if err != nil { + // TODO: retry after some time + log.Warningf("intel(mdns): failed to create udp6 listen multicast socket: %s", err) + } else { + module.StartServiceWorker("mdns udp6 multicast listener", 0, func(ctx context.Context) error { + return listenForDNSPackets(ctx, multicast6Conn, messages) + }) + defer func() { + _ = multicast6Conn.Close() + }() + } + + unicast6Conn, err = net.ListenUDP("udp6", &net.UDPAddr{IP: net.IPv6zero, Port: 0}) + if err != nil { + // TODO: retry after some time + log.Warningf("intel(mdns): failed to create udp6 listen socket: %s", err) + } else { + module.StartServiceWorker("mdns udp6 unicast listener", 0, func(ctx context.Context) error { + return listenForDNSPackets(ctx, unicast6Conn, messages) + }) + defer func() { + _ = unicast6Conn.Close() + }() + } } else { - module.StartServiceWorker("mdns udp6 unicast listener", 0, func(ctx context.Context) error { - return listenForDNSPackets(ctx, unicast6Conn, messages) - }) - defer func() { - _ = unicast6Conn.Close() - }() + log.Warningf("resolver: no IPv6 stack detected, disabling IPv6 mDNS resolver") } // start message handler From b392a1e8fff5a6dae3a1f1619a1865fc7b24f0cd Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 9 Jun 2022 13:59:50 +0200 Subject: [PATCH 04/13] Add geoip.IsInitialized to expose if the databases have been loaded --- intel/geoip/lookup.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/intel/geoip/lookup.go b/intel/geoip/lookup.go index 7210bc79..c635b574 100644 --- a/intel/geoip/lookup.go +++ b/intel/geoip/lookup.go @@ -26,3 +26,8 @@ func GetLocation(ip net.IP) (*Location, error) { record.FillMissingInfo() return record, nil } + +// IsInitialized returns whether the geoip database has been initialized. +func IsInitialized(v6, wait bool) bool { + return worker.GetReader(v6, wait) != nil +} From 86d4f64d429ea37e43d899dc5b12f21a2553609a Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 9 Jun 2022 14:36:03 +0200 Subject: [PATCH 05/13] Improve device location system with more safeguards --- netenv/api.go | 2 +- netenv/location.go | 151 ++++++++++++++++++++++++--------------------- 2 files changed, 80 insertions(+), 73 deletions(-) diff --git a/netenv/api.go b/netenv/api.go index 237dee57..20a2f688 100644 --- a/netenv/api.go +++ b/netenv/api.go @@ -55,7 +55,7 @@ func registerAPIEndpoints() error { Read: api.PermitUser, BelongsTo: module, StructFunc: func(ar *api.Request) (i interface{}, err error) { - return getLocationFromTraceroute() + return getLocationFromTraceroute(&DeviceLocations{}) }, Name: "Get Approximate Internet Location via Traceroute", Description: "Returns an approximation of where the device is on the Internet using a the traceroute technique.", diff --git a/netenv/location.go b/netenv/location.go index 6245ab45..8c8c5d56 100644 --- a/netenv/location.go +++ b/netenv/location.go @@ -47,16 +47,16 @@ type DeviceLocations struct { } // Best returns the best (most accurate) device location. -func (dl *DeviceLocations) Best() *DeviceLocation { - if len(dl.All) > 0 { - return dl.All[0] +func (dls *DeviceLocations) Best() *DeviceLocation { + if len(dls.All) > 0 { + return dls.All[0] } return nil } // BestV4 returns the best (most accurate) IPv4 device location. -func (dl *DeviceLocations) BestV4() *DeviceLocation { - for _, loc := range dl.All { +func (dls *DeviceLocations) BestV4() *DeviceLocation { + for _, loc := range dls.All { if loc.IPVersion == packet.IPv4 { return loc } @@ -65,8 +65,8 @@ func (dl *DeviceLocations) BestV4() *DeviceLocation { } // BestV6 returns the best (most accurate) IPv6 device location. -func (dl *DeviceLocations) BestV6() *DeviceLocation { - for _, loc := range dl.All { +func (dls *DeviceLocations) BestV6() *DeviceLocation { + for _, loc := range dls.All { if loc.IPVersion == packet.IPv6 { return loc } @@ -74,11 +74,8 @@ func (dl *DeviceLocations) BestV6() *DeviceLocation { return nil } -func copyDeviceLocations() *DeviceLocations { - locationsLock.Lock() - defer locationsLock.Unlock() - - // Create a copy of the locations, but not the entries. +// Copy creates a copy of the locations, but not the individual entries. +func (dls *DeviceLocations) Copy() *DeviceLocations { cp := &DeviceLocations{ All: make([]*DeviceLocation, len(locations.All)), } @@ -87,6 +84,32 @@ func copyDeviceLocations() *DeviceLocations { return cp } +// AddLocation adds a location. +func (dls *DeviceLocations) AddLocation(dl *DeviceLocation) { + if dls == nil { + return + } + + // Add to locations, if better. + var exists bool + for i, existing := range dls.All { + if (dl.IP == nil && existing.IP == nil) || dl.IP.Equal(existing.IP) { + exists = true + if dl.IsMoreAccurateThan(existing) { + // Replace + dls.All[i] = dl + break + } + } + } + if !exists { + dls.All = append(dls.All, dl) + } + + // Sort locations. + sort.Sort(sortLocationsByAccuracy(dls.All)) +} + // DeviceLocation represents a single IP and metadata. It must not be changed // once created. type DeviceLocation struct { @@ -147,6 +170,12 @@ func (dl *DeviceLocation) String() string { return "" case dl.Location == nil: return dl.IP.String() + case dl.Source == SourceTimezone: + return fmt.Sprintf( + "TZ(%.0f/%.0f)", + dl.Location.Coordinates.Latitude, + dl.Location.Coordinates.Longitude, + ) default: return fmt.Sprintf("%s (AS%d in %s)", dl.IP, dl.Location.AutonomousSystemNumber, dl.Location.Country.ISOCode) } @@ -193,6 +222,14 @@ func (a sortLocationsByAccuracy) Less(i, j int) bool { return !a[j].IsMoreAccura // SetInternetLocation provides the location management system with a possible Internet location. func SetInternetLocation(ip net.IP, source DeviceLocationSource) (dl *DeviceLocation, ok bool) { + locationsLock.Lock() + defer locationsLock.Unlock() + + return locations.AddIP(ip, source) +} + +// AddIP adds a new location based on the given IP. +func (dls *DeviceLocations) AddIP(ip net.IP, source DeviceLocationSource) (dl *DeviceLocation, ok bool) { // Check if IP is global. if netutils.GetIPScope(ip) != netutils.Global { return nil, false @@ -222,38 +259,10 @@ func SetInternetLocation(ip net.IP, source DeviceLocationSource) (dl *DeviceLoca } loc.Location = geoLoc - addLocation(loc) + dls.AddLocation(loc) return loc, true } -func addLocation(dl *DeviceLocation) { - if dl == nil { - return - } - - locationsLock.Lock() - defer locationsLock.Unlock() - - // Add to locations, if better. - var exists bool - for i, existing := range locations.All { - if (dl.IP == nil && existing.IP == nil) || dl.IP.Equal(existing.IP) { - exists = true - if dl.IsMoreAccurateThan(existing) { - // Replace - locations.All[i] = dl - break - } - } - } - if !exists { - locations.All = append(locations.All, dl) - } - - // Sort locations. - sort.Sort(sortLocationsByAccuracy(locations.All)) -} - // GetApproximateInternetLocation returns the approximate Internet location. // Deprecated: Please use GetInternetLocation instead. func GetApproximateInternetLocation() (net.IP, error) { @@ -271,30 +280,21 @@ func GetInternetLocation() (deviceLocations *DeviceLocations, ok bool) { // Check if the network changed, if not, return cache. if !locationNetworkChangedFlag.IsSet() { - return copyDeviceLocations(), true + locationsLock.Lock() + defer locationsLock.Unlock() + return locations.Copy(), true } locationNetworkChangedFlag.Refresh() - // Reset locations. - func() { - locationsLock.Lock() - defer locationsLock.Unlock() - locations = &DeviceLocations{} - }() - - // Get all assigned addresses. - v4s, v6s, err := GetAssignedAddresses() - if err != nil { - log.Warningf("netenv: failed to get assigned addresses for device location: %s", err) - return nil, false - } + // Create new location list. + dls := &DeviceLocations{} // Check interfaces for global addresses. - v4ok, v6ok := getLocationFromInterfaces() + v4ok, v6ok := getLocationFromInterfaces(dls) // Try other methods for missing locations. - if len(v4s) > 0 && !v4ok { - _, err = getLocationFromTraceroute() + if !v4ok { + _, err := getLocationFromTraceroute(dls) if err != nil { log.Warningf("netenv: failed to get IPv4 device location from traceroute: %s", err) } else { @@ -303,35 +303,43 @@ func GetInternetLocation() (deviceLocations *DeviceLocations, ok bool) { // Get location from timezone as final fallback. if !v4ok { - getLocationFromTimezone(packet.IPv4) + getLocationFromTimezone(dls, packet.IPv4) } } - if len(v6s) > 0 && !v6ok { + if !v6ok && IPv6Enabled() { // TODO: Find more ways to get IPv6 device location // Get location from timezone as final fallback. - getLocationFromTimezone(packet.IPv6) + getLocationFromTimezone(dls, packet.IPv6) } + // As a last guard, make sure there is at least one location in the list. + if len(dls.All) == 0 { + getLocationFromTimezone(dls, packet.IPv4) + } + + // Set new locations. + locationsLock.Lock() + defer locationsLock.Unlock() + locations = dls + // Return gathered locations. - cp := copyDeviceLocations() - return cp, true + return locations.Copy(), true } -func getLocationFromInterfaces() (v4ok, v6ok bool) { +func getLocationFromInterfaces(dls *DeviceLocations) (v4ok, v6ok bool) { globalIPv4, globalIPv6, err := GetAssignedGlobalAddresses() if err != nil { log.Warningf("netenv: location: failed to get assigned global addresses: %s", err) return false, false } - for _, ip := range globalIPv4 { - if _, ok := SetInternetLocation(ip, SourceInterface); ok { + if _, ok := dls.AddIP(ip, SourceInterface); ok { v4ok = true } } for _, ip := range globalIPv6 { - if _, ok := SetInternetLocation(ip, SourceInterface); ok { + if _, ok := dls.AddIP(ip, SourceInterface); ok { v6ok = true } } @@ -349,7 +357,7 @@ func getLocationFromUPnP() (ok bool) { } */ -func getLocationFromTraceroute() (dl *DeviceLocation, err error) { +func getLocationFromTraceroute(dls *DeviceLocations) (dl *DeviceLocation, err error) { // Create connection. conn, err := net.ListenPacket("ip4:icmp", "") if err != nil { @@ -470,7 +478,7 @@ nextHop: // We have received a valid time exceeded error. // If message came from a global unicast, us it! if netutils.GetIPScope(remoteIP) == netutils.Global { - dl, ok := SetInternetLocation(remoteIP, SourceTraceroute) + dl, ok := dls.AddIP(remoteIP, SourceTraceroute) if !ok { return nil, errors.New("invalid IP address") } @@ -516,7 +524,7 @@ func recvICMP(currentHop int, icmpPacketsViaFirewall chan packet.Packet) ( } } -func getLocationFromTimezone(ipVersion packet.IPVersion) (ok bool) { //nolint:unparam // This is documentation. +func getLocationFromTimezone(dls *DeviceLocations, ipVersion packet.IPVersion) { // Create base struct. tzLoc := &DeviceLocation{ IPVersion: ipVersion, @@ -531,6 +539,5 @@ func getLocationFromTimezone(ipVersion packet.IPVersion) (ok bool) { //nolint:un tzLoc.Location.Coordinates.Latitude = 48 tzLoc.Location.Coordinates.Longitude = float64(offsetSeconds) / 43200 * 180 - addLocation(tzLoc) - return true + dls.AddLocation(tzLoc) } From 9c0b7ddf51085fa536af97cac3cbcb40cb17359a Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Jun 2022 09:56:21 +0200 Subject: [PATCH 06/13] Fix direction detection for unsupported protocols --- network/state/lookup.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/state/lookup.go b/network/state/lookup.go index 6e2525f1..46aac9a6 100644 --- a/network/state/lookup.go +++ b/network/state/lookup.go @@ -60,7 +60,7 @@ func Lookup(pktInfo *packet.Info, fast bool) (pid int, inbound bool, err error) return udp6Table.lookup(pktInfo, fast) default: - return socket.UndefinedProcessID, false, errors.New("unsupported protocol for finding process") + return socket.UndefinedProcessID, pktInfo.Inbound, errors.New("unsupported protocol for finding process") } } From bb782ba98fc23bcad1e2edfe5670d8920c9f2444 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Jun 2022 09:56:47 +0200 Subject: [PATCH 07/13] Add check to ensure matching PID constants --- process/special.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/process/special.go b/process/special.go index 4b6a89e4..7d2c3e93 100644 --- a/process/special.go +++ b/process/special.go @@ -8,6 +8,7 @@ import ( "golang.org/x/sync/singleflight" "github.com/safing/portbase/log" + "github.com/safing/portmaster/network/socket" "github.com/safing/portmaster/profile" ) @@ -28,6 +29,13 @@ const ( NetworkHostProcessID = -255 ) +func init() { + // Check required matching values. + if UndefinedProcessID != socket.UndefinedProcessID { + panic("UndefinedProcessID does not match socket.UndefinedProcessID") + } +} + var ( // unidentifiedProcess is used for non-attributed outgoing connections. unidentifiedProcess = &Process{ From 3a98b2cc054bdb100da322e0fdd1eda108f42f7a Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Jun 2022 09:57:55 +0200 Subject: [PATCH 08/13] Make intel index override-able by other indexes --- updates/helper/indexes.go | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/updates/helper/indexes.go b/updates/helper/indexes.go index db97b84d..e925be1a 100644 --- a/updates/helper/indexes.go +++ b/updates/helper/indexes.go @@ -33,6 +33,12 @@ func SetIndexes(registry *updater.ResourceRegistry, releaseChannel string, delet // Reset indexes before adding them (again). registry.ResetIndexes() + // Add the intel index first, in order to be able to override it with the + // other indexes when needed. + registry.AddIndex(updater.Index{ + Path: "all/intel/intel.json", + }) + // Always add the stable index as a base. registry.AddIndex(updater.Index{ Path: ReleaseChannelStable + ".json", @@ -85,13 +91,6 @@ func SetIndexes(registry *updater.ResourceRegistry, releaseChannel string, delet } } - // Add the intel index last, as it updates the fastest and should not be - // crippled by other faulty indexes. It can only specify versions for its - // scope anyway. - registry.AddIndex(updater.Index{ - Path: "all/intel/intel.json", - }) - // Set pre-release usage. registry.SetUsePreReleases(usePreReleases) From 0dce13d18fd22b29a140833816933b0ae4dff3ff Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Jun 2022 10:40:21 +0200 Subject: [PATCH 09/13] Fix detection of incoming localhost packets on Linux --- firewall/interception/nfq/packet.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/firewall/interception/nfq/packet.go b/firewall/interception/nfq/packet.go index 46ca3ea5..528f2e3c 100644 --- a/firewall/interception/nfq/packet.go +++ b/firewall/interception/nfq/packet.go @@ -141,6 +141,13 @@ func (pkt *packet) Drop() error { } func (pkt *packet) PermanentAccept() error { + // If the packet is localhost only, do not permanently accept the outgoing + // packet, as the packet mark will be copied to the connection mark, which + // will stick and it will bypass the incoming queue. + if !pkt.Info().Inbound && pkt.Info().Dst.IsLoopback() { + return pkt.Accept() + } + return pkt.mark(MarkAcceptAlways) } From 65974e989dd883c8b6fde8835d45b7914d279113 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Jun 2022 10:41:35 +0200 Subject: [PATCH 10/13] Stop using deprecated function --- firewall/dns.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firewall/dns.go b/firewall/dns.go index cb78e5c4..d8c4b582 100644 --- a/firewall/dns.go +++ b/firewall/dns.go @@ -263,7 +263,7 @@ func UpdateIPsAndCNAMEs(q *resolver.Query, rrCache *resolver.RRCache, conn *netw // Package IPs and CNAMEs into IPInfo structs. for _, ip := range ips { // Never save domain attributions for localhost IPs. - if netutils.ClassifyIP(ip) == netutils.HostLocal { + if netutils.GetIPScope(ip) == netutils.HostLocal { continue } From d40ad3125d7b00b5e3310cb71e7cba6bca910f08 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Jun 2022 14:33:09 +0200 Subject: [PATCH 11/13] Improve rules and filterlists config options --- profile/config.go | 45 ++++++++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/profile/config.go b/profile/config.go index 987594d4..f03fca74 100644 --- a/profile/config.go +++ b/profile/config.go @@ -258,6 +258,12 @@ Examples: "192.168.0.1 TCP/HTTP", "LAN UDP/50000-55000", "example.com */HTTPS", Important: DNS Requests are only matched against domain and filter list rules, all others require an IP address and are checked only with the following IP connection. `, `"`, "`") + // rulesVerdictNames defines the verdicts names to be used for filter rules. + rulesVerdictNames := map[string]string{ + "-": "Block", // Default. + "+": "Allow", + } + // Endpoint Filter List err = config.Register(&config.Option{ Name: "Outgoing Rules", @@ -268,10 +274,11 @@ Important: DNS Requests are only matched against domain and filter list rules, a OptType: config.OptTypeStringArray, DefaultValue: []string{}, Annotations: config.Annotations{ - config.StackableAnnotation: true, - config.DisplayHintAnnotation: endpoints.DisplayHintEndpointList, - config.DisplayOrderAnnotation: cfgOptionEndpointsOrder, - config.CategoryAnnotation: "Rules", + config.StackableAnnotation: true, + config.DisplayHintAnnotation: endpoints.DisplayHintEndpointList, + config.DisplayOrderAnnotation: cfgOptionEndpointsOrder, + config.CategoryAnnotation: "Rules", + endpoints.EndpointListVerdictNamesAnnotation: rulesVerdictNames, }, ValidationRegex: endpoints.ListEntryValidationRegex, ValidationFunc: endpoints.ValidateEndpointListConfigOption, @@ -283,6 +290,7 @@ Important: DNS Requests are only matched against domain and filter list rules, a cfgStringArrayOptions[CfgOptionEndpointsKey] = cfgOptionEndpoints // Service Endpoint Filter List + defaultIncomingRulesValue := []string{"+ Localhost"} err = config.Register(&config.Option{ Name: "Incoming Rules", Key: CfgOptionServiceEndpointsKey, @@ -290,13 +298,14 @@ Important: DNS Requests are only matched against domain and filter list rules, a Help: rulesHelp, Sensitive: true, OptType: config.OptTypeStringArray, - DefaultValue: []string{"+ Localhost"}, + DefaultValue: defaultIncomingRulesValue, ExpertiseLevel: config.ExpertiseLevelExpert, Annotations: config.Annotations{ - config.StackableAnnotation: true, - config.DisplayHintAnnotation: endpoints.DisplayHintEndpointList, - config.DisplayOrderAnnotation: cfgOptionServiceEndpointsOrder, - config.CategoryAnnotation: "Rules", + config.StackableAnnotation: true, + config.DisplayHintAnnotation: endpoints.DisplayHintEndpointList, + config.DisplayOrderAnnotation: cfgOptionServiceEndpointsOrder, + config.CategoryAnnotation: "Rules", + endpoints.EndpointListVerdictNamesAnnotation: rulesVerdictNames, config.QuickSettingsAnnotation: []config.QuickSetting{ { Name: "SSH", @@ -313,6 +322,16 @@ Important: DNS Requests are only matched against domain and filter list rules, a Action: config.QuickMergeTop, Value: []string{"+ * */3389"}, }, + { + Name: "Allow all from LAN", + Action: config.QuickMergeTop, + Value: []string{"+ LAN"}, + }, + { + Name: "Allow all from Internet", + Action: config.QuickMergeTop, + Value: []string{"+ Internet"}, + }, }, }, ValidationRegex: endpoints.ListEntryValidationRegex, @@ -321,7 +340,7 @@ Important: DNS Requests are only matched against domain and filter list rules, a if err != nil { return err } - cfgOptionServiceEndpoints = config.Concurrent.GetAsStringArray(CfgOptionServiceEndpointsKey, []string{}) + cfgOptionServiceEndpoints = config.Concurrent.GetAsStringArray(CfgOptionServiceEndpointsKey, defaultIncomingRulesValue) cfgStringArrayOptions[CfgOptionServiceEndpointsKey] = cfgOptionServiceEndpoints filterListsHelp := strings.ReplaceAll(`Filter lists contain domains and IP addresses that are known to be used adversarial. The data is collected from many public sources and put into the following categories. In order to active a category, add it's "ID" to the list. @@ -346,13 +365,13 @@ The lists are automatically updated every hour using incremental updates. `, `"`, "`") // Filter list IDs + defaultFilterListsValue := []string{"TRAC", "MAL", "BAD"} err = config.Register(&config.Option{ Name: "Filter Lists", Key: CfgOptionFilterListsKey, Description: "Block connections that match enabled filter lists.", - Help: filterListsHelp, OptType: config.OptTypeStringArray, - DefaultValue: []string{"TRAC", "MAL", "BAD"}, + DefaultValue: defaultFilterListsValue, Annotations: config.Annotations{ config.DisplayHintAnnotation: "filter list", config.DisplayOrderAnnotation: cfgOptionFilterListsOrder, @@ -363,7 +382,7 @@ The lists are automatically updated every hour using incremental updates. if err != nil { return err } - cfgOptionFilterLists = config.Concurrent.GetAsStringArray(CfgOptionFilterListsKey, []string{}) + cfgOptionFilterLists = config.Concurrent.GetAsStringArray(CfgOptionFilterListsKey, defaultFilterListsValue) cfgStringArrayOptions[CfgOptionFilterListsKey] = cfgOptionFilterLists // Include CNAMEs From 6339eb0c49e01ea627319f25e6725f9495291876 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Jun 2022 14:33:28 +0200 Subject: [PATCH 12/13] Remove filter lists help --- profile/config.go | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/profile/config.go b/profile/config.go index f03fca74..e371a175 100644 --- a/profile/config.go +++ b/profile/config.go @@ -343,27 +343,6 @@ Important: DNS Requests are only matched against domain and filter list rules, a cfgOptionServiceEndpoints = config.Concurrent.GetAsStringArray(CfgOptionServiceEndpointsKey, defaultIncomingRulesValue) cfgStringArrayOptions[CfgOptionServiceEndpointsKey] = cfgOptionServiceEndpoints - filterListsHelp := strings.ReplaceAll(`Filter lists contain domains and IP addresses that are known to be used adversarial. The data is collected from many public sources and put into the following categories. In order to active a category, add it's "ID" to the list. - -**Ads & Trackers** - ID: "TRAC" -Services that track and profile people online, including as ads, analytics and telemetry. - -**Malware** - ID: "MAL" -Services that are (ab)used for attacking devices through technical means. - -**Deception** - ID: "DECEP" -Services that trick humans into thinking the service is genuine, while it is not, including phishing, fake news and fraud. - -**Bad Stuff (Mixed)** - ID: "BAD" -Miscellaneous services that are believed to be harmful to security or privacy, but their exact use is unknown, not categorized, or lists have mixed categories. - -**NSFW** - ID: "NSFW" -Services that are generally not accepted in work environments, including pornography, violence and gambling. - -The lists are automatically updated every hour using incremental updates. -[See here](https://github.com/safing/intel-data) for more detail about these lists, their sources and how to help to improve them. -`, `"`, "`") - // Filter list IDs defaultFilterListsValue := []string{"TRAC", "MAL", "BAD"} err = config.Register(&config.Option{ From dac6b3c6fe31bb9e333c18abcaa3e5eac28163a9 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 22 Jun 2022 14:49:36 +0200 Subject: [PATCH 13/13] Fix linter error --- netenv/main.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/netenv/main.go b/netenv/main.go index 232696cd..3363754a 100644 --- a/netenv/main.go +++ b/netenv/main.go @@ -1,9 +1,10 @@ package netenv import ( + "github.com/tevino/abool" + "github.com/safing/portbase/log" "github.com/safing/portbase/modules" - "github.com/tevino/abool" ) // Event Names.