From e262ad1db944854db44bfe308c623606ee915cbc Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 21 Apr 2020 15:02:17 +0200 Subject: [PATCH 01/28] Update config and add ordering --- Gopkg.lock | 33 ++++++++++++++ firewall/config.go | 12 +++-- profile/config.go | 108 ++++++++++++++++++++++++++++++--------------- profile/profile.go | 2 +- resolver/config.go | 94 +++++++++++++++++++++++++++------------ 5 files changed, 181 insertions(+), 68 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index 035c6de9..7d1613bb 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -41,6 +41,14 @@ revision = "78b5fff24e6df8886ef8eca9411f683a884349a5" version = "v0.4.1" +[[projects]] + digest = "1:0deddd908b6b4b768cfc272c16ee61e7088a60f7fe2f06c547bd3d8e1f8b8e77" + name = "github.com/davecgh/go-spew" + packages = ["spew"] + pruneopts = "" + revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73" + version = "v1.1.1" + [[projects]] digest = "1:b6581f9180e0f2d5549280d71819ab951db9d511478c87daca95669589d505c0" name = "github.com/go-ole/go-ole" @@ -120,6 +128,14 @@ revision = "2905694a1b00c5574f1418a7dbf8a22a7d247559" version = "v1.3.1" +[[projects]] + digest = "1:256484dbbcd271f9ecebc6795b2df8cad4c458dd0f5fd82a8c2fa0c29f233411" + name = "github.com/pmezard/go-difflib" + packages = ["difflib"] + pruneopts = "" + revision = "792786c7400a136282c1664665ae0a8db921c6c2" + version = "v1.0.0" + [[projects]] digest = "1:7f569d906bdd20d906b606415b7d794f798f91a62fcfb6a4daa6d50690fb7a3f" name = "github.com/satori/go.uuid" @@ -166,6 +182,14 @@ revision = "298182f68c66c05229eb03ac171abe6e309ee79a" version = "v1.0.3" +[[projects]] + digest = "1:cc4eb6813da8d08694e557fcafae8fcc24f47f61a0717f952da130ca9a486dfc" + name = "github.com/stretchr/testify" + packages = ["assert"] + pruneopts = "" + revision = "3ebf1ddaeb260c4b1ae502a01c7844fa8c1fa0e9" + version = "v1.5.1" + [[projects]] branch = "master" digest = "1:86e6712cfd4070a2120c03fcec41cfcbbc51813504a74e28d74479edfaf669ee" @@ -259,6 +283,14 @@ revision = "342b2e1fbaa52c93f31447ad2c6abc048c63e475" version = "v0.3.2" +[[projects]] + digest = "1:2efc9662a6a1ff28c65c84fc2f9030f13d3afecdb2ecad445f3b0c80e75fc281" + name = "gopkg.in/yaml.v2" + packages = ["."] + pruneopts = "" + revision = "53403b58ad1b561927d19068c655246f2db79d48" + version = "v2.2.8" + [solve-meta] analyzer-name = "dep" analyzer-version = 1 @@ -278,6 +310,7 @@ "github.com/satori/go.uuid", "github.com/shirou/gopsutil/process", "github.com/spf13/cobra", + "github.com/stretchr/testify/assert", "github.com/tevino/abool", "github.com/umahmood/haversine", "golang.org/x/net/icmp", diff --git a/firewall/config.go b/firewall/config.go index 721d1382..53d607e5 100644 --- a/firewall/config.go +++ b/firewall/config.go @@ -8,11 +8,13 @@ import ( var ( CfgOptionEnableFilterKey = "filter/enable" - CfgOptionPermanentVerdictsKey = "filter/permanentVerdicts" - permanentVerdicts config.BoolOption + CfgOptionPromptTimeoutKey = "filter/promptTimeout" + CfgOptionPromptTimeoutOrder = 2 + promptTimeout config.IntOption - CfgOptionPromptTimeoutKey = "filter/promptTimeout" - promptTimeout config.IntOption + CfgOptionPermanentVerdictsKey = "filter/permanentVerdicts" + CfgOptionPermanentVerdictsOrder = 128 + permanentVerdicts config.BoolOption devMode config.BoolOption apiListenAddress config.StringOption @@ -23,6 +25,7 @@ func registerConfig() error { Name: "Permanent Verdicts", Key: CfgOptionPermanentVerdictsKey, Description: "With permanent verdicts, control of a connection is fully handed back to the OS after the initial decision. This brings a great performance increase, but makes it impossible to change the decision of a link later on.", + Order: CfgOptionPermanentVerdictsOrder, OptType: config.OptTypeBool, ExpertiseLevel: config.ExpertiseLevelExpert, ReleaseLevel: config.ReleaseLevelExperimental, @@ -37,6 +40,7 @@ func registerConfig() error { Name: "Timeout for prompt notifications", Key: CfgOptionPromptTimeoutKey, Description: "Amount of time how long Portmaster will wait for a response when prompting about a connection via a notification. In seconds.", + Order: CfgOptionPromptTimeoutOrder, OptType: config.OptTypeInt, ExpertiseLevel: config.ExpertiseLevelUser, ReleaseLevel: config.ReleaseLevelBeta, diff --git a/profile/config.go b/profile/config.go index 8842530e..9f0e3ad3 100644 --- a/profile/config.go +++ b/profile/config.go @@ -12,53 +12,75 @@ var ( cfgIntOptions = make(map[string]config.IntOption) cfgBoolOptions = make(map[string]config.BoolOption) - CfgOptionDefaultActionKey = "filter/defaultAction" - cfgOptionDefaultAction config.StringOption + // Enable Filter Order = 0 - CfgOptionDisableAutoPermitKey = "filter/disableAutoPermit" - cfgOptionDisableAutoPermit config.IntOption // security level option + CfgOptionDefaultActionKey = "filter/defaultAction" + cfgOptionDefaultAction config.StringOption + cfgOptionDefaultActionOrder = 1 - CfgOptionEndpointsKey = "filter/endpoints" - cfgOptionEndpoints config.StringArrayOption + // Prompt Timeout Order = 2 - CfgOptionServiceEndpointsKey = "filter/serviceEndpoints" - cfgOptionServiceEndpoints config.StringArrayOption + CfgOptionBlockScopeInternetKey = "filter/blockInternet" + cfgOptionBlockScopeInternet config.IntOption // security level option + cfgOptionBlockScopeInternetOrder = 16 - CfgOptionFilterListKey = "filter/lists" - cfgOptionFilterLists config.StringArrayOption + CfgOptionBlockScopeLANKey = "filter/blockLAN" + cfgOptionBlockScopeLAN config.IntOption // security level option + cfgOptionBlockScopeLANOrder = 17 - CfgOptionFilterSubDomainsKey = "filter/includeSubdomains" - cfgOptionFilterSubDomains config.IntOption // security level option + CfgOptionBlockScopeLocalKey = "filter/blockLocal" + cfgOptionBlockScopeLocal config.IntOption // security level option + cfgOptionBlockScopeLocalOrder = 18 - CfgOptionFilterCNAMEKey = "filter/includeCNAMEs" - cfgOptionFilterCNAME config.IntOption // security level option + CfgOptionBlockP2PKey = "filter/blockP2P" + cfgOptionBlockP2P config.IntOption // security level option + cfgOptionBlockP2POrder = 19 - CfgOptionBlockScopeLocalKey = "filter/blockLocal" - cfgOptionBlockScopeLocal config.IntOption // security level option + CfgOptionBlockInboundKey = "filter/blockInbound" + cfgOptionBlockInbound config.IntOption // security level option + cfgOptionBlockInboundOrder = 20 - CfgOptionBlockScopeLANKey = "filter/blockLAN" - cfgOptionBlockScopeLAN config.IntOption // security level option + CfgOptionEndpointsKey = "filter/endpoints" + cfgOptionEndpoints config.StringArrayOption + cfgOptionEndpointsOrder = 32 - CfgOptionBlockScopeInternetKey = "filter/blockInternet" - cfgOptionBlockScopeInternet config.IntOption // security level option + CfgOptionServiceEndpointsKey = "filter/serviceEndpoints" + cfgOptionServiceEndpoints config.StringArrayOption + cfgOptionServiceEndpointsOrder = 33 - CfgOptionBlockP2PKey = "filter/blockP2P" - cfgOptionBlockP2P config.IntOption // security level option + CfgOptionPreventBypassingKey = "filter/preventBypassing" + cfgOptionPreventBypassing config.IntOption // security level option + cfgOptionPreventBypassingOrder = 48 - CfgOptionBlockInboundKey = "filter/blockInbound" - cfgOptionBlockInbound config.IntOption // security level option + CfgOptionFilterListsKey = "filter/lists" + cfgOptionFilterLists config.StringArrayOption + cfgOptionFilterListsOrder = 64 - CfgOptionEnforceSPNKey = "filter/enforceSPN" - cfgOptionEnforceSPN config.IntOption // security level option + CfgOptionFilterSubDomainsKey = "filter/includeSubdomains" + cfgOptionFilterSubDomains config.IntOption // security level option + cfgOptionFilterSubDomainsOrder = 65 - CfgOptionRemoveOutOfScopeDNSKey = "filter/removeOutOfScopeDNS" - cfgOptionRemoveOutOfScopeDNS config.IntOption // security level option + CfgOptionFilterCNAMEKey = "filter/includeCNAMEs" + cfgOptionFilterCNAME config.IntOption // security level option + cfgOptionFilterCNAMEOrder = 66 - CfgOptionRemoveBlockedDNSKey = "filter/removeBlockedDNS" - cfgOptionRemoveBlockedDNS config.IntOption // security level option + CfgOptionDisableAutoPermitKey = "filter/disableAutoPermit" + cfgOptionDisableAutoPermit config.IntOption // security level option + cfgOptionDisableAutoPermitOrder = 80 - CfgOptionPreventBypassingKey = "filter/preventBypassing" - cfgOptionPreventBypassing config.IntOption // security level option + CfgOptionEnforceSPNKey = "filter/enforceSPN" + cfgOptionEnforceSPN config.IntOption // security level option + cfgOptionEnforceSPNOrder = 96 + + CfgOptionRemoveOutOfScopeDNSKey = "filter/removeOutOfScopeDNS" + cfgOptionRemoveOutOfScopeDNS config.IntOption // security level option + cfgOptionRemoveOutOfScopeDNSOrder = 112 + + CfgOptionRemoveBlockedDNSKey = "filter/removeBlockedDNS" + cfgOptionRemoveBlockedDNS config.IntOption // security level option + cfgOptionRemoveBlockedDNSOrder = 113 + + // Permanent Verdicts Order = 128 ) func registerConfiguration() error { @@ -70,6 +92,7 @@ func registerConfiguration() error { Name: "Default Filter Action", Key: CfgOptionDefaultActionKey, Description: `The default filter action when nothing else permits or blocks a connection.`, + Order: cfgOptionDefaultActionOrder, OptType: config.OptTypeString, DefaultValue: "permit", ExternalOptType: "string list", @@ -86,6 +109,7 @@ func registerConfiguration() error { Name: "Disable Auto Permit", Key: CfgOptionDisableAutoPermitKey, Description: "Auto Permit searches for a relation between an app and the destionation of a connection - if there is a correlation, the connection will be permitted. This setting is negated in order to provide a streamlined user experience, where higher settings are better.", + Order: cfgOptionDisableAutoPermitOrder, OptType: config.OptTypeInt, ExternalOptType: "security level", DefaultValue: status.SecurityLevelsAll, @@ -121,6 +145,7 @@ Examples: + .example.com */HTTP - .example.com + 192.168.0.1/24`, + Order: cfgOptionEndpointsOrder, OptType: config.OptTypeStringArray, DefaultValue: []string{}, ExternalOptType: "endpoint list", @@ -156,6 +181,7 @@ Examples: + .example.com */HTTP - .example.com + 192.168.0.1/24`, + Order: cfgOptionServiceEndpointsOrder, OptType: config.OptTypeStringArray, DefaultValue: []string{}, ExternalOptType: "endpoint list", @@ -170,8 +196,9 @@ Examples: // Filter list IDs err = config.Register(&config.Option{ Name: "Filter List", - Key: CfgOptionFilterListKey, + Key: CfgOptionFilterListsKey, Description: "Filter connections by matching the endpoint against configured filterlists", + Order: cfgOptionFilterListsOrder, OptType: config.OptTypeStringArray, DefaultValue: []string{"TRAC", "MAL"}, ExternalOptType: "filter list", @@ -180,14 +207,15 @@ Examples: if err != nil { return err } - cfgOptionFilterLists = config.Concurrent.GetAsStringArray(CfgOptionFilterListKey, []string{}) - cfgStringArrayOptions[CfgOptionFilterListKey] = cfgOptionFilterLists + cfgOptionFilterLists = config.Concurrent.GetAsStringArray(CfgOptionFilterListsKey, []string{}) + cfgStringArrayOptions[CfgOptionFilterListsKey] = cfgOptionFilterLists // Include CNAMEs err = config.Register(&config.Option{ Name: "Filter CNAMEs", Key: CfgOptionFilterCNAMEKey, Description: "Also filter requests where a CNAME would be blocked", + Order: cfgOptionFilterCNAMEOrder, OptType: config.OptTypeInt, ExternalOptType: "security level", DefaultValue: status.SecurityLevelsAll, @@ -205,6 +233,7 @@ Examples: Name: "Filter SubDomains", Key: CfgOptionFilterSubDomainsKey, Description: "Also filter sub-domains if a parent domain is blocked by a filter list", + Order: cfgOptionFilterSubDomainsOrder, OptType: config.OptTypeInt, ExternalOptType: "security level", DefaultValue: status.SecurityLevelOff, @@ -221,6 +250,7 @@ Examples: Name: "Block Scope Local", Key: CfgOptionBlockScopeLocalKey, Description: "Block connections to your own device, ie. localhost.", + Order: cfgOptionBlockScopeLocalOrder, OptType: config.OptTypeInt, ExternalOptType: "security level", DefaultValue: status.SecurityLevelOff, @@ -237,6 +267,7 @@ Examples: Name: "Block Scope LAN", Key: CfgOptionBlockScopeLANKey, Description: "Block connections to the Local Area Network.", + Order: cfgOptionBlockScopeLANOrder, OptType: config.OptTypeInt, ExternalOptType: "security level", DefaultValue: status.SecurityLevelOff, @@ -253,6 +284,7 @@ Examples: Name: "Block Scope Internet", Key: CfgOptionBlockScopeInternetKey, Description: "Block connections to the Internet.", + Order: cfgOptionBlockScopeInternetOrder, OptType: config.OptTypeInt, ExternalOptType: "security level", DefaultValue: status.SecurityLevelOff, @@ -269,6 +301,7 @@ Examples: Name: "Block Peer to Peer Connections", Key: CfgOptionBlockP2PKey, Description: "Block peer to peer connections. These are connections that are established directly to an IP address on the Internet without resolving a domain name via DNS first.", + Order: cfgOptionBlockP2POrder, OptType: config.OptTypeInt, ExternalOptType: "security level", DefaultValue: status.SecurityLevelsAll, @@ -285,6 +318,7 @@ Examples: Name: "Block Inbound Connections", Key: CfgOptionBlockInboundKey, Description: "Block inbound connections to your device. This will usually only be the case if you are running a network service or are using peer to peer software.", + Order: cfgOptionBlockInboundOrder, OptType: config.OptTypeInt, ExternalOptType: "security level", DefaultValue: status.SecurityLevelsHighAndExtreme, @@ -301,6 +335,7 @@ Examples: Name: "Enforce SPN", Key: CfgOptionEnforceSPNKey, Description: "This setting enforces connections to be routed over the SPN. If this is not possible for any reason, connections will be blocked.", + Order: cfgOptionEnforceSPNOrder, OptType: config.OptTypeInt, ReleaseLevel: config.ReleaseLevelExperimental, ExternalOptType: "security level", @@ -318,6 +353,7 @@ Examples: Name: "Filter Out-of-Scope DNS Records", Key: CfgOptionRemoveOutOfScopeDNSKey, Description: "Filter DNS answers that are outside of the scope of the server. A server on the public Internet may not respond with a private LAN address.", + Order: cfgOptionRemoveOutOfScopeDNSOrder, OptType: config.OptTypeInt, ExpertiseLevel: config.ExpertiseLevelExpert, ReleaseLevel: config.ReleaseLevelBeta, @@ -336,6 +372,7 @@ Examples: Name: "Filter DNS Records that would be blocked", Key: CfgOptionRemoveBlockedDNSKey, Description: "Pre-filter DNS answers that an application would not be allowed to connect to.", + Order: cfgOptionRemoveBlockedDNSOrder, OptType: config.OptTypeInt, ExpertiseLevel: config.ExpertiseLevelExpert, ReleaseLevel: config.ReleaseLevelBeta, @@ -353,6 +390,7 @@ Examples: Name: "Prevent Bypassing", Key: CfgOptionPreventBypassingKey, Description: "Prevent apps from bypassing the privacy filter: Firefox by disabling DNS-over-HTTPs", + Order: cfgOptionPreventBypassingOrder, OptType: config.OptTypeInt, ExpertiseLevel: config.ExpertiseLevelUser, ReleaseLevel: config.ReleaseLevelBeta, diff --git a/profile/profile.go b/profile/profile.go index 70f7c162..bf228fdb 100644 --- a/profile/profile.go +++ b/profile/profile.go @@ -143,7 +143,7 @@ func (profile *Profile) parseConfig() error { } } - list, ok = profile.configPerspective.GetAsStringArray(CfgOptionFilterListKey) + list, ok = profile.configPerspective.GetAsStringArray(CfgOptionFilterListsKey) if ok { profile.filterListIDs, err = filterlists.ResolveListIDs(list) if err != nil { diff --git a/resolver/config.go b/resolver/config.go index ac9567b0..bb83fda3 100644 --- a/resolver/config.go +++ b/resolver/config.go @@ -22,28 +22,28 @@ var ( // - Available logging data may not be used against the user, ie. unethically. // Sadly, only a few services come close to fulfilling these requirements. - // For now, we have settled for two bigger and well known services: Cloudflare and Quad9. + // For now, we have settled for two bigger and well known services: Quad9 and Cloudflare. // TODO: monitor situation and re-evaluate when new services become available // TODO: explore other methods of making queries more private // We encourage everyone who has the technical abilities to set their own preferred servers. - // Default 1: Cloudflare - "dot://1.1.1.1:853?verify=cloudflare-dns.com&name=Cloudflare&blockedif=zeroip", // Cloudflare - "dot://1.0.0.1:853?verify=cloudflare-dns.com&name=Cloudflare&blockedif=zeroip", // Cloudflare - - // Default 2: Quad9 + // Default 1: Quad9 "dot://9.9.9.9:853?verify=dns.quad9.net&name=Quad9&blockedif=empty", // Quad9 "dot://149.112.112.112:853?verify=dns.quad9.net&name=Quad9&blockedif=empty", // Quad9 - // Fallback 1: Cloudflare - "dns://1.1.1.1:53?name=Cloudflare&blockedif=zeroip", // Cloudflare - "dns://1.0.0.1:53?name=Cloudflare&blockedif=zeroip", // Cloudflare + // Default 2: Cloudflare + "dot://1.1.1.2:853?verify=cloudflare-dns.com&name=Cloudflare&blockedif=zeroip", // Cloudflare + "dot://1.0.0.2:853?verify=cloudflare-dns.com&name=Cloudflare&blockedif=zeroip", // Cloudflare - // Fallback 2: Quad9 + // Fallback 1: Quad9 "dns://9.9.9.9:53?name=Quad9&blockedif=empty", // Quad9 "dns://149.112.112.112:53?name=Quad9&blockedif=empty", // Quad9 + // Fallback 2: Cloudflare + "dns://1.1.1.2:53?name=Cloudflare&blockedif=zeroip", // Cloudflare + "dns://1.0.0.2:53?name=Cloudflare&blockedif=zeroip", // Cloudflare + // supported parameters // - `verify=domain`: verify domain (dot only) // future parameters: @@ -55,38 +55,70 @@ var ( // - `zeroip`: Answer only contains zeroip } - CfgOptionNameServersKey = "dns/nameservers" - configuredNameServers config.StringArrayOption + CfgOptionNameServersKey = "dns/nameservers" + configuredNameServers config.StringArrayOption + cfgOptionNameServersOrder = 0 - CfgOptionNameserverRetryRateKey = "dns/nameserverRetryRate" - nameserverRetryRate config.IntOption + CfgOptionNoAssignedNameserversKey = "dns/noAssignedNameservers" + noAssignedNameservers status.SecurityLevelOption + cfgOptionNoAssignedNameserversOrder = 1 - CfgOptionNoMulticastDNSKey = "dns/noMulticastDNS" - noMulticastDNS status.SecurityLevelOption + CfgOptionNoMulticastDNSKey = "dns/noMulticastDNS" + noMulticastDNS status.SecurityLevelOption + cfgOptionNoMulticastDNSOrder = 2 - CfgOptionNoAssignedNameserversKey = "dns/noAssignedNameservers" - noAssignedNameservers status.SecurityLevelOption + CfgOptionNoInsecureProtocolsKey = "dns/noInsecureProtocols" + noInsecureProtocols status.SecurityLevelOption + cfgOptionNoInsecureProtocolsOrder = 3 - CfgOptionNoInsecureProtocolsKey = "dns/noInsecureProtocols" - noInsecureProtocols status.SecurityLevelOption + CfgOptionDontResolveSpecialDomainsKey = "dns/dontResolveSpecialDomains" + dontResolveSpecialDomains status.SecurityLevelOption + cfgOptionDontResolveSpecialDomainsOrder = 16 - CfgOptionDontResolveSpecialDomainsKey = "dns/dontResolveSpecialDomains" - dontResolveSpecialDomains status.SecurityLevelOption + CfgOptionDontResolveTestDomainsKey = "dns/dontResolveTestDomains" + dontResolveTestDomains status.SecurityLevelOption + cfgOptionDontResolveTestDomainsOrder = 17 - CfgOptionDontResolveTestDomainsKey = "dns/dontResolveTestDomains" - dontResolveTestDomains status.SecurityLevelOption + CfgOptionNameserverRetryRateKey = "dns/nameserverRetryRate" + nameserverRetryRate config.IntOption + cfgOptionNameserverRetryRateOrder = 32 ) func prepConfig() error { err := config.Register(&config.Option{ - Name: "DNS Servers", - Key: CfgOptionNameServersKey, - Description: "DNS Servers to use for resolving DNS requests.", + Name: "DNS Servers", + Key: CfgOptionNameServersKey, + Description: "DNS Servers to use for resolving DNS requests.", + Help: `Format: + +DNS Servers are configured in a URL format. This allows you to specify special settings for a resolver. If you just want to use a resolver at IP 10.2.3.4, please enter: dns://10.2.3.4:53 +The format is: protocol://ip:port?parameter=value¶meter=value + +Protocols: + dot: DNS-over-TLS (recommended) + dns: plain old DNS + tcp: plain old DNS over TCP + +IP: + always use the IP address and _not_ the domain name! + +Port: + always add the port! + +Parameters: + name: give your DNS Server a name that is used for messages and logs + verify: domain name to verify for "dot", required and only valid for "dot" + blockedif: detect if the name server blocks a query, options: + empty: server replies with NXDomain status, but without any other record in any section + refused: server replies with Refused status + zeroip: server replies with an IP address, but it is zero +`, + Order: cfgOptionNameServersOrder, OptType: config.OptTypeStringArray, ExpertiseLevel: config.ExpertiseLevelExpert, ReleaseLevel: config.ReleaseLevelStable, DefaultValue: defaultNameServers, - ValidationRegex: "^(dns|dot|tls)://.*", + ValidationRegex: fmt.Sprintf("^(%s|%s|%s)://.*", ServerTypeDoT, ServerTypeDNS, ServerTypeTCP), }) if err != nil { return err @@ -97,6 +129,7 @@ func prepConfig() error { Name: "DNS Server Retry Rate", Key: CfgOptionNameserverRetryRateKey, Description: "Rate at which to retry failed DNS Servers, in seconds.", + Order: cfgOptionNameserverRetryRateOrder, OptType: config.OptTypeInt, ExpertiseLevel: config.ExpertiseLevelExpert, ReleaseLevel: config.ReleaseLevelStable, @@ -111,6 +144,7 @@ func prepConfig() error { Name: "Do not use Multicast DNS", Key: CfgOptionNoMulticastDNSKey, Description: "Multicast DNS queries other devices in the local network", + Order: cfgOptionNoMulticastDNSOrder, OptType: config.OptTypeInt, ExpertiseLevel: config.ExpertiseLevelExpert, ReleaseLevel: config.ReleaseLevelStable, @@ -127,6 +161,7 @@ func prepConfig() error { Name: "Do not use assigned Nameservers", Key: CfgOptionNoAssignedNameserversKey, Description: "that were acquired by the network (dhcp) or system", + Order: cfgOptionNoAssignedNameserversOrder, OptType: config.OptTypeInt, ExpertiseLevel: config.ExpertiseLevelExpert, ReleaseLevel: config.ReleaseLevelStable, @@ -143,6 +178,7 @@ func prepConfig() error { Name: "Do not resolve insecurely", Key: CfgOptionNoInsecureProtocolsKey, Description: "Do not resolve domains with insecure protocols, ie. plain DNS", + Order: cfgOptionNoInsecureProtocolsOrder, OptType: config.OptTypeInt, ExpertiseLevel: config.ExpertiseLevelExpert, ReleaseLevel: config.ReleaseLevelStable, @@ -159,6 +195,7 @@ func prepConfig() error { Name: "Do not resolve special domains", Key: CfgOptionDontResolveSpecialDomainsKey, Description: fmt.Sprintf("Do not resolve the special top level domains %s", formatScopeList(specialServiceScopes)), + Order: cfgOptionDontResolveSpecialDomainsOrder, OptType: config.OptTypeInt, ExpertiseLevel: config.ExpertiseLevelExpert, ReleaseLevel: config.ReleaseLevelStable, @@ -175,6 +212,7 @@ func prepConfig() error { Name: "Do not resolve test domains", Key: CfgOptionDontResolveTestDomainsKey, Description: fmt.Sprintf("Do not resolve the special testing top level domains %s", formatScopeList(localTestScopes)), + Order: cfgOptionDontResolveTestDomainsOrder, OptType: config.OptTypeInt, ExpertiseLevel: config.ExpertiseLevelExpert, ReleaseLevel: config.ReleaseLevelStable, From 71615811e4d281c2f5e3fc1ffed7620ec6217d89 Mon Sep 17 00:00:00 2001 From: Patrick Pacher Date: Tue, 21 Apr 2020 15:11:17 +0200 Subject: [PATCH 02/28] Add update module status, allow disabling of updates --- updates/config.go | 43 ++++++++++++++++++++++++-- updates/main.go | 78 ++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 110 insertions(+), 11 deletions(-) diff --git a/updates/config.go b/updates/config.go index c0070d6f..79ed7c9a 100644 --- a/updates/config.go +++ b/updates/config.go @@ -5,14 +5,17 @@ import ( "fmt" "github.com/safing/portbase/config" + "github.com/safing/portbase/log" ) var ( releaseChannel config.StringOption devMode config.BoolOption + disableUpdates config.BoolOption - previousReleaseChannel string - previousDevMode bool + previousReleaseChannel string + updatesCurrentlyDisabled bool + previousDevMode bool ) func registerConfig() error { @@ -32,16 +35,35 @@ func registerConfig() error { return err } - return module.RegisterEventHook("config", "config change", "update registry config", updateRegistryConfig) + err = config.Register(&config.Option{ + Name: "Disable Updates", + Key: disableUpdatesKey, + Description: "Disable automatic updates.", + OptType: config.OptTypeBool, + ExpertiseLevel: config.ExpertiseLevelExpert, + ReleaseLevel: config.ReleaseLevelStable, + RequiresRestart: false, + DefaultValue: false, + ExternalOptType: "disable updates", + }) + if err != nil { + return err + } + + return nil } func initConfig() { releaseChannel = config.GetAsString(releaseChannelKey, releaseChannelStable) + disableUpdates = config.GetAsBool(disableUpdatesKey, false) + devMode = config.GetAsBool("core/devMode", false) } func updateRegistryConfig(_ context.Context, _ interface{}) error { changed := false + forceUpdate := false + if releaseChannel() != previousReleaseChannel { registry.SetBeta(releaseChannel() == releaseChannelBeta) previousReleaseChannel = releaseChannel() @@ -54,9 +76,24 @@ func updateRegistryConfig(_ context.Context, _ interface{}) error { changed = true } + if disableUpdates() != updatesCurrentlyDisabled { + updatesCurrentlyDisabled = disableUpdates() + changed = true + forceUpdate = !updatesCurrentlyDisabled + } + if changed { registry.SelectVersions() module.TriggerEvent(VersionUpdateEvent, nil) + + if forceUpdate { + module.Resolve(updateFailed) + TriggerUpdate() + log.Infof("Automatic updates enabled again.") + } else { + module.Warning(updateFailed, "Updates are disabled!") + log.Warningf("Automatic updates are now disabled.") + } } return nil diff --git a/updates/main.go b/updates/main.go index 3a7fef99..c1040d42 100644 --- a/updates/main.go +++ b/updates/main.go @@ -19,6 +19,8 @@ const ( releaseChannelStable = "stable" releaseChannelBeta = "beta" + disableUpdatesKey = "core/disableUpdates" + // ModuleName is the name of the update module // and can be used when declaring module dependencies. ModuleName = "updates" @@ -36,6 +38,10 @@ const ( // to check if new versions of their resources are // available by checking File.UpgradeAvailable(). ResourceUpdateEvent = "resource update" + + // TriggerUpdateEvent is the event that can be emitted + // by the updates module to trigger an update. + TriggerUpdateEvent = "trigger update" ) var ( @@ -46,15 +52,51 @@ var ( disableTaskSchedule bool ) +const ( + updateInProgress = "update-in-progress" + updateInProcessDescr = "Portmaster is currently checking and downloading updates." + updateFailed = "update-failed" +) + func init() { - module = modules.Register(ModuleName, registerConfig, start, stop, "base") + module = modules.Register(ModuleName, prep, start, stop, "base") module.RegisterEvent(VersionUpdateEvent) module.RegisterEvent(ResourceUpdateEvent) } +func prep() error { + if err := registerConfig(); err != nil { + return err + } + + module.RegisterEvent(TriggerUpdateEvent) + + return nil +} + func start() error { initConfig() + if err := module.RegisterEventHook( + "config", + "config change", + "update registry config", + updateRegistryConfig); err != nil { + return err + } + + if err := module.RegisterEventHook( + module.Name, + TriggerUpdateEvent, + "Check for and download available updates", + func(context.Context, interface{}) error { + TriggerUpdate() + return nil + }, + ); err != nil { + return err + } + var mandatoryUpdates []string if onWindows { mandatoryUpdates = []string{ @@ -115,8 +157,8 @@ func start() error { if !disableTaskSchedule { updateTask. - Repeat(24 * time.Hour). - MaxDelay(1 * time.Hour). + Repeat(1 * time.Hour). + MaxDelay(30 * time.Minute). Schedule(time.Now().Add(10 * time.Second)) } @@ -138,6 +180,7 @@ func TriggerUpdate() error { updateASAP = true } else { updateTask.StartASAP() + log.Debugf("updates: triggering update to run as soon as possible") } return nil @@ -156,14 +199,33 @@ func DisableUpdateSchedule() error { return nil } -func checkForUpdates(ctx context.Context) error { - if err := registry.UpdateIndexes(); err != nil { - return fmt.Errorf("updates: failed to update indexes: %w", err) +func checkForUpdates(ctx context.Context) (err error) { + if updatesCurrentlyDisabled { + log.Debugf("updates: automatic updates are disabled") + return nil + } + defer log.Debugf("updates: finished checking for updates") + + module.Hint(updateInProgress, updateInProcessDescr) + + defer func() { + if err == nil { + module.Resolve(updateInProgress) + module.Resolve(updateFailed) + } else { + module.Warning(updateFailed, "Failed to check for updates: "+err.Error()) + } + }() + + if err = registry.UpdateIndexes(); err != nil { + err = fmt.Errorf("failed to update indexes: %w", err) + return } - err := registry.DownloadUpdates(ctx) + err = registry.DownloadUpdates(ctx) if err != nil { - return fmt.Errorf("updates: failed to update: %w", err) + err = fmt.Errorf("failed to update: %w", err) + return } registry.SelectVersions() From 9be175c23888690c449db44ce42c9227a48646ae Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 21 Apr 2020 15:28:59 +0200 Subject: [PATCH 03/28] Fix earlier bug --- updates/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/updates/config.go b/updates/config.go index 79ed7c9a..6fd90a67 100644 --- a/updates/config.go +++ b/updates/config.go @@ -71,7 +71,7 @@ func updateRegistryConfig(_ context.Context, _ interface{}) error { } if devMode() != previousDevMode { - registry.SetBeta(devMode()) + registry.SetDevMode(devMode()) previousDevMode = devMode() changed = true } From 27ed6da45f1263d1ce20b694b5a873e3852fb7df Mon Sep 17 00:00:00 2001 From: Patrick Pacher Date: Tue, 21 Apr 2020 15:36:06 +0200 Subject: [PATCH 04/28] Fix linter warnings --- updates/config.go | 2 +- updates/main.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/updates/config.go b/updates/config.go index 6fd90a67..5cf70c53 100644 --- a/updates/config.go +++ b/updates/config.go @@ -88,7 +88,7 @@ func updateRegistryConfig(_ context.Context, _ interface{}) error { if forceUpdate { module.Resolve(updateFailed) - TriggerUpdate() + _ = TriggerUpdate() log.Infof("Automatic updates enabled again.") } else { module.Warning(updateFailed, "Updates are disabled!") diff --git a/updates/main.go b/updates/main.go index c1040d42..a03f822d 100644 --- a/updates/main.go +++ b/updates/main.go @@ -90,7 +90,7 @@ func start() error { TriggerUpdateEvent, "Check for and download available updates", func(context.Context, interface{}) error { - TriggerUpdate() + _ = TriggerUpdate() return nil }, ); err != nil { From 3d7d1fa0df1d52233b52d46ebde476641059d158 Mon Sep 17 00:00:00 2001 From: Patrick Pacher Date: Tue, 21 Apr 2020 15:41:31 +0200 Subject: [PATCH 05/28] Remove useless modules.Resolve --- updates/main.go | 1 - 1 file changed, 1 deletion(-) diff --git a/updates/main.go b/updates/main.go index a03f822d..c6feb6e2 100644 --- a/updates/main.go +++ b/updates/main.go @@ -211,7 +211,6 @@ func checkForUpdates(ctx context.Context) (err error) { defer func() { if err == nil { module.Resolve(updateInProgress) - module.Resolve(updateFailed) } else { module.Warning(updateFailed, "Failed to check for updates: "+err.Error()) } From 0a0dda36e07b084e91f3398d430b836b829db877 Mon Sep 17 00:00:00 2001 From: Patrick Pacher Date: Tue, 21 Apr 2020 15:43:48 +0200 Subject: [PATCH 06/28] Change log message format --- updates/config.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/updates/config.go b/updates/config.go index 5cf70c53..59955549 100644 --- a/updates/config.go +++ b/updates/config.go @@ -89,10 +89,10 @@ func updateRegistryConfig(_ context.Context, _ interface{}) error { if forceUpdate { module.Resolve(updateFailed) _ = TriggerUpdate() - log.Infof("Automatic updates enabled again.") + log.Infof("updates: automatic updates enabled again.") } else { module.Warning(updateFailed, "Updates are disabled!") - log.Warningf("Automatic updates are now disabled.") + log.Warningf("updates: automatic updates are now disabled.") } } From 350555f843b255c499f1946ce8acb8e46a130d04 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Apr 2020 09:59:36 +0200 Subject: [PATCH 07/28] Fix concurrent map read/write, maybe --- network/clean.go | 11 +++++++---- network/database.go | 6 ++++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/network/clean.go b/network/clean.go index ec51b611..3b1bbac9 100644 --- a/network/clean.go +++ b/network/clean.go @@ -41,8 +41,14 @@ func cleanConnections() (activePIDs map[int]struct{}) { now := time.Now().Unix() deleteOlderThan := time.Now().Add(-deleteConnsAfterEndedThreshold).Unix() - // network connections + // lock both together because we cannot fully guarantee in which map a connection lands + // of course every connection should land in the correct map, but this increases resilience connsLock.Lock() + defer connsLock.Unlock() + dnsConnsLock.Lock() + defer dnsConnsLock.Unlock() + + // network connections for key, conn := range conns { conn.Lock() @@ -67,10 +73,8 @@ func cleanConnections() (activePIDs map[int]struct{}) { conn.Unlock() } - connsLock.Unlock() // dns requests - dnsConnsLock.Lock() for _, conn := range dnsConns { conn.Lock() @@ -82,7 +86,6 @@ func cleanConnections() (activePIDs map[int]struct{}) { conn.Unlock() } - dnsConnsLock.Unlock() return nil }) diff --git a/network/database.go b/network/database.go index 073dcbc0..ee42a5b1 100644 --- a/network/database.go +++ b/network/database.go @@ -77,9 +77,11 @@ func (s *StorageInterface) processQuery(q *query.Query, it *iterator.Iterator) { if slashes <= 1 { // processes for _, proc := range process.All() { + proc.Lock() if q.Matches(proc) { it.Next <- proc } + proc.Unlock() } } @@ -87,9 +89,11 @@ func (s *StorageInterface) processQuery(q *query.Query, it *iterator.Iterator) { // dns scopes only dnsConnsLock.RLock() for _, dnsConn := range dnsConns { + dnsConn.Lock() if q.Matches(dnsConn) { it.Next <- dnsConn } + dnsConn.Unlock() } dnsConnsLock.RUnlock() } @@ -98,9 +102,11 @@ func (s *StorageInterface) processQuery(q *query.Query, it *iterator.Iterator) { // connections connsLock.RLock() for _, conn := range conns { + conn.Lock() if q.Matches(conn) { it.Next <- conn } + conn.Unlock() } connsLock.RUnlock() } From d2d69139b91b60e35bd2d43eb8a1870d758eb7bb Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Apr 2020 10:02:07 +0200 Subject: [PATCH 08/28] Add control db interface for triggering hooks, add shutdown/restart hooks --- core/control.go | 100 ++++++++++++++++++++++++++++++++++++++++++++++ core/core.go | 9 +++++ core/databases.go | 2 +- core/events.go | 46 +++++++++++++++++++++ 4 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 core/control.go create mode 100644 core/events.go diff --git a/core/control.go b/core/control.go new file mode 100644 index 00000000..b94163d9 --- /dev/null +++ b/core/control.go @@ -0,0 +1,100 @@ +package core + +import ( + "fmt" + "strings" + "sync" + + "github.com/safing/portbase/database" + "github.com/safing/portbase/database/record" + "github.com/safing/portbase/database/storage" +) + +// StorageInterface provices a storage.Interface to the storage manager. +type StorageInterface struct { + storage.InjectBase +} + +// Get returns a database record. +func (s *StorageInterface) Get(key string) (record.Record, error) { + msg := newMessage(key) + splittedKey := strings.Split(key, "/") + + switch splittedKey[0] { + case "module": + return controlModule(msg, splittedKey) + default: + return nil, storage.ErrNotFound + } +} + +func controlModule(msg *MessageRecord, splittedKey []string) (record.Record, error) { + // format: module/moduleName/action/param + var moduleName string + var action string + var param string + var err error + + // parse elements + switch len(splittedKey) { + case 4: + param = splittedKey[3] + fallthrough + case 3: + moduleName = splittedKey[1] + action = splittedKey[2] + default: + return nil, storage.ErrNotFound + } + + // execute + switch action { + case "trigger": + err = module.InjectEvent(fmt.Sprintf("user triggered the '%s/%s' event", moduleName, param), moduleName, param, nil) + default: + return nil, storage.ErrNotFound + } + + if err != nil { + msg.Message = err.Error() + } else { + msg.Success = true + } + + return msg, nil +} + +func registerControlDatabase() error { + _, err := database.Register(&database.Database{ + Name: "control", + Description: "Control Interface for the Portmaster", + StorageType: "injected", + PrimaryAPI: "", + }) + if err != nil { + return err + } + + _, err = database.InjectDatabase("control", &StorageInterface{}) + if err != nil { + return err + } + + return nil +} + +// MessageRecord is a simple record used for control database feedback +type MessageRecord struct { + record.Base + sync.Mutex + + Success bool + Message string +} + +func newMessage(key string) *MessageRecord { + m := &MessageRecord{} + m.SetKey("control:" + key) + m.UpdateMeta() + return m +} diff --git a/core/core.go b/core/core.go index d70569f0..039758e0 100644 --- a/core/core.go +++ b/core/core.go @@ -31,10 +31,19 @@ func init() { ) } +func prep() error { + registerEvents() + return nil +} + func start() error { if err := startPlatformSpecific(); err != nil { return fmt.Errorf("failed to start plattform-specific components: %s", err) } + if err := registerEventHooks(); err != nil { + return err + } + return nil } diff --git a/core/databases.go b/core/databases.go index 941aa97a..df8f37ae 100644 --- a/core/databases.go +++ b/core/databases.go @@ -46,5 +46,5 @@ func registerDatabases() error { // return err // } - return nil + return registerControlDatabase() } diff --git a/core/events.go b/core/events.go new file mode 100644 index 00000000..908347fd --- /dev/null +++ b/core/events.go @@ -0,0 +1,46 @@ +package core + +import ( + "context" + + "github.com/safing/portbase/log" + "github.com/safing/portbase/modules" +) + +const ( + eventShutdown = "shutdown" + eventRestart = "restart" + restartCode = 23 +) + +func registerEvents() { + module.RegisterEvent(eventShutdown) + module.RegisterEvent(eventRestart) +} + +func registerEventHooks() error { + err := module.RegisterEventHook("core", eventShutdown, "execute shutdown", shutdown) + if err != nil { + return err + } + + err = module.RegisterEventHook("core", eventRestart, "execute shutdown", restart) + if err != nil { + return err + } + + return nil +} + +func shutdown(ctx context.Context, _ interface{}) error { + log.Warning("core: user requested shutdown") + go modules.Shutdown() //nolint:errcheck + return nil +} + +func restart(ctx context.Context, data interface{}) error { + log.Info("core: user requested restart") + modules.SetExitStatusCode(restartCode) + go modules.Shutdown() //nolint:errcheck + return nil +} From 5c7739e28a0c34df9cfc271bbc1c5f7266012f37 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Apr 2020 10:04:08 +0200 Subject: [PATCH 09/28] Add hook to re/unload UI assets --- ui/module.go | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/ui/module.go b/ui/module.go index 75a127a2..76a130ef 100644 --- a/ui/module.go +++ b/ui/module.go @@ -1,13 +1,51 @@ package ui import ( + "context" + + resources "github.com/cookieo9/resources-go" + "github.com/safing/portbase/log" "github.com/safing/portbase/modules" ) +const ( + eventReload = "reload" +) + +var ( + module *modules.Module +) + func init() { - modules.Register("ui", prep, nil, nil, "api", "updates") + module = modules.Register("ui", prep, start, nil, "api", "updates") } func prep() error { + module.RegisterEvent(eventReload) + return registerRoutes() } + +func start() error { + return module.RegisterEventHook("ui", eventReload, "reload assets", reloadUI) +} + +func reloadUI(ctx context.Context, _ interface{}) error { + log.Info("core: user/UI requested UI reload") + + appsLock.Lock() + defer appsLock.Unlock() + + // close all bundles + for id, bundle := range apps { + err := bundle.Close() + if err != nil { + log.Warningf("ui: failed to close bundle %s: %s", id, err) + } + } + + // reset index + apps = make(map[string]*resources.BundleSequence) + + return nil +} From 95041d217cc82dd882189afe348adfdf58611d43 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Apr 2020 10:17:15 +0200 Subject: [PATCH 10/28] Fix module dependencies, split filter into interception and filter modules --- core/core.go | 14 +++++- firewall/filter.go | 47 ++++++++++++++++++++ firewall/{firewall.go => interception.go} | 53 ++++++++--------------- firewall/ports.go | 2 +- intel/filterlists/module.go | 7 ++- nameserver/nameserver.go | 2 +- network/module.go | 2 +- profile/module.go | 2 +- 8 files changed, 89 insertions(+), 40 deletions(-) create mode 100644 firewall/filter.go rename firewall/{firewall.go => interception.go} (88%) diff --git a/core/core.go b/core/core.go index 039758e0..f7a6d13a 100644 --- a/core/core.go +++ b/core/core.go @@ -20,7 +20,19 @@ var ( func init() { modules.Register("base", nil, registerDatabases, nil, "database", "config", "rng") - module = modules.Register("core", nil, start, nil, "base", "subsystems", "status", "updates", "api", "notifications", "ui") + // For prettier subsystem graph, printed with --print-subsystem-graph + /* + subsystems.Register( + "base", + "Base", + "THE GROUND.", + baseModule, + "", + nil, + ) + */ + + module = modules.Register("core", prep, start, nil, "base", "subsystems", "status", "updates", "api", "notifications", "ui", "netenv", "network", "interception") subsystems.Register( "core", "Core", diff --git a/firewall/filter.go b/firewall/filter.go new file mode 100644 index 00000000..6302401c --- /dev/null +++ b/firewall/filter.go @@ -0,0 +1,47 @@ +package firewall + +import ( + "github.com/safing/portbase/config" + "github.com/safing/portbase/modules/subsystems" + + "github.com/safing/portbase/modules" + + // module dependencies + _ "github.com/safing/portmaster/core" + _ "github.com/safing/portmaster/profile" +) + +var ( + filterModule *modules.Module + filterEnabled config.BoolOption +) + +func init() { + filterModule = modules.Register("filter", filterPrep, nil, nil, "core", "intel") + subsystems.Register( + "filter", + "Privacy Filter", + "DNS and Network Filter", + filterModule, + "config:filter/", + &config.Option{ + Name: "Enable Privacy Filter", + Key: CfgOptionEnableFilterKey, + Description: "Enable the Privacy Filter Subsystem to filter DNS queries and network requests.", + OptType: config.OptTypeBool, + ExpertiseLevel: config.ExpertiseLevelUser, + ReleaseLevel: config.ReleaseLevelBeta, + DefaultValue: true, + }, + ) +} + +func filterPrep() (err error) { + err = registerConfig() + if err != nil { + return err + } + + filterEnabled = config.GetAsBool(CfgOptionEnableFilterKey, true) + return nil +} diff --git a/firewall/firewall.go b/firewall/interception.go similarity index 88% rename from firewall/firewall.go rename to firewall/interception.go index 923e87f2..f7bf1f1d 100644 --- a/firewall/firewall.go +++ b/firewall/interception.go @@ -7,9 +7,6 @@ import ( "sync/atomic" "time" - "github.com/safing/portbase/config" - "github.com/safing/portbase/modules/subsystems" - "github.com/safing/portbase/log" "github.com/safing/portbase/modules" "github.com/safing/portmaster/firewall/inspection" @@ -23,7 +20,7 @@ import ( ) var ( - module *modules.Module + interceptionModule *modules.Module // localNet net.IPNet // localhost net.IP @@ -45,33 +42,12 @@ var ( ) func init() { - module = modules.Register("filter", prep, start, stop, "core", "network", "nameserver", "intel") - subsystems.Register( - "filter", - "Privacy Filter", - "DNS and Network Filter", - module, - "config:filter/", - &config.Option{ - Name: "Enable Privacy Filter", - Key: CfgOptionEnableFilterKey, - Description: "Enable the Privacy Filter Subsystem to filter DNS queries and network requests.", - OptType: config.OptTypeBool, - ExpertiseLevel: config.ExpertiseLevelUser, - ReleaseLevel: config.ReleaseLevelBeta, - DefaultValue: true, - }, - ) + interceptionModule = modules.Register("interception", interceptionPrep, interceptionStart, interceptionStop, "base") network.SetDefaultFirewallHandler(defaultHandler) } -func prep() (err error) { - err = registerConfig() - if err != nil { - return err - } - +func interceptionPrep() (err error) { err = prepAPIAuth() if err != nil { return err @@ -101,20 +77,20 @@ func prep() (err error) { return nil } -func start() error { +func interceptionStart() error { startAPIAuth() - module.StartWorker("stat logger", func(ctx context.Context) error { + interceptionModule.StartWorker("stat logger", func(ctx context.Context) error { statLogger() return nil }) - module.StartWorker("packet handler", func(ctx context.Context) error { + interceptionModule.StartWorker("packet handler", func(ctx context.Context) error { run() return nil }) - module.StartWorker("ports state cleaner", func(ctx context.Context) error { + interceptionModule.StartWorker("ports state cleaner", func(ctx context.Context) error { portsInUseCleaner() return nil }) @@ -122,7 +98,7 @@ func start() error { return interception.Start() } -func stop() error { +func interceptionStop() error { return interception.Stop() } @@ -248,6 +224,15 @@ func initialHandler(conn *network.Connection, pkt packet.Packet) { return } + // check if filtering is enabled + if !filterEnabled() { + conn.Inspecting = false + conn.SetVerdict(network.VerdictAccept, "privacy filter disabled", nil) + conn.StopFirewallHandler() + issueVerdict(conn, pkt, 0, true) + return + } + log.Tracer(pkt.Ctx()).Trace("filter: starting decision process") DecideOnConnection(conn, pkt) conn.Inspecting = false // TODO: enable inspecting again @@ -350,7 +335,7 @@ func issueVerdict(conn *network.Connection, pkt packet.Packet, verdict network.V func run() { for { select { - case <-module.Stopping(): + case <-interceptionModule.Stopping(): return case pkt := <-interception.Packets: handlePacket(pkt) @@ -361,7 +346,7 @@ func run() { func statLogger() { for { select { - case <-module.Stopping(): + case <-interceptionModule.Stopping(): return case <-time.After(10 * time.Second): log.Tracef( diff --git a/firewall/ports.go b/firewall/ports.go index 50b39d31..c0e3eb65 100644 --- a/firewall/ports.go +++ b/firewall/ports.go @@ -72,7 +72,7 @@ func GetPermittedPort() uint16 { func portsInUseCleaner() { for { select { - case <-module.Stopping(): + case <-interceptionModule.Stopping(): return case <-time.After(cleanerTickDuration): cleanPortsInUse() diff --git a/intel/filterlists/module.go b/intel/filterlists/module.go index 9b74c5c4..38d9deaa 100644 --- a/intel/filterlists/module.go +++ b/intel/filterlists/module.go @@ -33,7 +33,7 @@ var ( func init() { ignoreNetEnvEvents.Set() - module = modules.Register("filterlists", prep, start, nil, "core", "netenv") + module = modules.Register("filterlists", prep, start, stop, "core") } func prep() error { @@ -98,3 +98,8 @@ func start() error { return nil } + +func stop() error { + filterListsLoaded = make(chan struct{}) + return nil +} diff --git a/nameserver/nameserver.go b/nameserver/nameserver.go index 03d71701..82f3077e 100644 --- a/nameserver/nameserver.go +++ b/nameserver/nameserver.go @@ -32,7 +32,7 @@ var ( ) func init() { - module = modules.Register("nameserver", prep, start, stop, "core", "resolver", "network", "netenv") + module = modules.Register("nameserver", prep, start, stop, "core", "resolver") subsystems.Register( "dns", "Secure DNS", diff --git a/network/module.go b/network/module.go index 8b2c8309..70f2fd24 100644 --- a/network/module.go +++ b/network/module.go @@ -16,7 +16,7 @@ var ( ) func init() { - module = modules.Register("network", nil, start, nil, "core", "processes") + module = modules.Register("network", nil, start, nil, "base", "processes") } // SetDefaultFirewallHandler sets the default firewall handler. diff --git a/profile/module.go b/profile/module.go index 8d962a8a..1cc83688 100644 --- a/profile/module.go +++ b/profile/module.go @@ -14,7 +14,7 @@ var ( ) func init() { - module = modules.Register("profiles", prep, start, nil, "core") + module = modules.Register("profiles", prep, start, nil, "base") } func prep() error { From 5209a090c448fe95c0d3bef78c4cd82518f4c98a Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Apr 2020 10:55:49 +0200 Subject: [PATCH 11/28] Update config options, add options to turn off system notifications --- core/config.go | 17 +++++++++++++++++ firewall/config.go | 45 ++++++++++++++++++++++++++++++++++----------- firewall/prompt.go | 6 +++--- process/config.go | 1 + profile/config.go | 4 ++-- updates/config.go | 6 ++++++ 6 files changed, 63 insertions(+), 16 deletions(-) diff --git a/core/config.go b/core/config.go index 0ee6921f..0383d68f 100644 --- a/core/config.go +++ b/core/config.go @@ -11,6 +11,8 @@ import ( var ( CfgDevModeKey = "core/devMode" defaultDevMode bool + + CfgUseSystemNotificationsKey = "core/useSystemNotifications" ) func init() { @@ -28,6 +30,7 @@ func registerConfig() error { Name: "Development Mode", Key: CfgDevModeKey, Description: "In Development Mode security restrictions are lifted/softened to enable easier access to Portmaster for debugging and testing purposes.", + Order: 127, OptType: config.OptTypeBool, ExpertiseLevel: config.ExpertiseLevelDeveloper, ReleaseLevel: config.ReleaseLevelStable, @@ -37,5 +40,19 @@ func registerConfig() error { return err } + err = config.Register(&config.Option{ + Name: "Use System Notifications", + Key: CfgUseSystemNotificationsKey, + Description: "Send notifications to your operating system's notification system. When this setting is turned off, notifications will only be visible in the Portmaster App. This affects both alerts from the Portmaster and questions from the Privacy Filter.", + Order: 32, + OptType: config.OptTypeBool, + ExpertiseLevel: config.ExpertiseLevelUser, + ReleaseLevel: config.ReleaseLevelStable, + DefaultValue: true, // TODO: turn off by default on unsupported systems + }) + if err != nil { + return err + } + return nil } diff --git a/firewall/config.go b/firewall/config.go index 53d607e5..2a9308b3 100644 --- a/firewall/config.go +++ b/firewall/config.go @@ -1,16 +1,23 @@ package firewall import ( + "github.com/safing/portbase/api" "github.com/safing/portbase/config" + "github.com/safing/portmaster/core" ) // Configuration Keys var ( CfgOptionEnableFilterKey = "filter/enable" - CfgOptionPromptTimeoutKey = "filter/promptTimeout" - CfgOptionPromptTimeoutOrder = 2 - promptTimeout config.IntOption + CfgOptionAskWithSystemNotificationsKey = "filter/askWithSystemNotifications" + CfgOptionAskWithSystemNotificationsOrder = 2 + askWithSystemNotifications config.BoolOption + useSystemNotifications config.BoolOption + + CfgOptionAskTimeoutKey = "filter/askTimeout" + CfgOptionAskTimeoutOrder = 3 + askTimeout config.IntOption CfgOptionPermanentVerdictsKey = "filter/permanentVerdicts" CfgOptionPermanentVerdictsOrder = 128 @@ -37,22 +44,38 @@ func registerConfig() error { permanentVerdicts = config.Concurrent.GetAsBool(CfgOptionPermanentVerdictsKey, true) err = config.Register(&config.Option{ - Name: "Timeout for prompt notifications", - Key: CfgOptionPromptTimeoutKey, - Description: "Amount of time how long Portmaster will wait for a response when prompting about a connection via a notification. In seconds.", - Order: CfgOptionPromptTimeoutOrder, + Name: "Ask with System Notifications", + Key: CfgOptionAskWithSystemNotificationsKey, + Description: `Ask about connections using your operating system's notification system. For this to be enabled, the setting "Use System Notifications" must enabled too. This only affects questions from the Privacy Filter, and does not affect alerts from the Portmaster.`, + Order: CfgOptionAskWithSystemNotificationsOrder, + OptType: config.OptTypeBool, + ExpertiseLevel: config.ExpertiseLevelUser, + ReleaseLevel: config.ReleaseLevelStable, + DefaultValue: true, + }) + if err != nil { + return err + } + askWithSystemNotifications = config.Concurrent.GetAsBool(CfgOptionAskWithSystemNotificationsKey, true) + useSystemNotifications = config.Concurrent.GetAsBool(core.CfgUseSystemNotificationsKey, true) + + err = config.Register(&config.Option{ + Name: "Timeout for Ask Notifications", + Key: CfgOptionAskTimeoutKey, + Description: "Amount of time (in seconds) how long the Portmaster will wait for a response when prompting about a connection via a notification. Please note that system notifications might not respect this or have it's own limits.", + Order: CfgOptionAskTimeoutOrder, OptType: config.OptTypeInt, ExpertiseLevel: config.ExpertiseLevelUser, - ReleaseLevel: config.ReleaseLevelBeta, + ReleaseLevel: config.ReleaseLevelStable, DefaultValue: 60, }) if err != nil { return err } - promptTimeout = config.Concurrent.GetAsInt(CfgOptionPromptTimeoutKey, 60) + askTimeout = config.Concurrent.GetAsInt(CfgOptionAskTimeoutKey, 60) - devMode = config.Concurrent.GetAsBool("core/devMode", false) - apiListenAddress = config.GetAsString("api/listenAddress", "") + devMode = config.Concurrent.GetAsBool(core.CfgDevModeKey, false) + apiListenAddress = config.GetAsString(api.CfgDefaultListenAddressKey, "") return nil } diff --git a/firewall/prompt.go b/firewall/prompt.go index 210b63a9..bc4f7109 100644 --- a/firewall/prompt.go +++ b/firewall/prompt.go @@ -26,16 +26,16 @@ const ( ) func prompt(conn *network.Connection, pkt packet.Packet) { //nolint:gocognit // TODO - nTTL := time.Duration(promptTimeout()) * time.Second + nTTL := time.Duration(askTimeout()) * time.Second // first check if there is an existing notification for this. // build notification ID var nID string switch { case conn.Inbound, conn.Entity.Domain == "": // connection to/from IP - nID = fmt.Sprintf("firewall-prompt-%d-%s-%s", conn.Process().Pid, conn.Scope, pkt.Info().RemoteIP()) + nID = fmt.Sprintf("filter:prompt-%d-%s-%s", conn.Process().Pid, conn.Scope, pkt.Info().RemoteIP()) default: // connection to domain - nID = fmt.Sprintf("firewall-prompt-%d-%s", conn.Process().Pid, conn.Scope) + nID = fmt.Sprintf("filter:prompt-%d-%s", conn.Process().Pid, conn.Scope) } n := notifications.Get(nID) saveResponse := true diff --git a/process/config.go b/process/config.go index 9329eddb..a4d8c205 100644 --- a/process/config.go +++ b/process/config.go @@ -17,6 +17,7 @@ func registerConfiguration() error { Name: "Enable Process Detection", Key: CfgOptionEnableProcessDetectionKey, Description: "This option enables the attribution of network traffic to processes. This should be always enabled, and effectively disables app profiles if disabled.", + Order: 144, OptType: config.OptTypeBool, ExpertiseLevel: config.ExpertiseLevelDeveloper, DefaultValue: true, diff --git a/profile/config.go b/profile/config.go index 9f0e3ad3..aaf11b2c 100644 --- a/profile/config.go +++ b/profile/config.go @@ -300,7 +300,7 @@ Examples: err = config.Register(&config.Option{ Name: "Block Peer to Peer Connections", Key: CfgOptionBlockP2PKey, - Description: "Block peer to peer connections. These are connections that are established directly to an IP address on the Internet without resolving a domain name via DNS first.", + Description: "These are connections that are established directly to an IP address on the Internet without resolving a domain name via DNS first.", Order: cfgOptionBlockP2POrder, OptType: config.OptTypeInt, ExternalOptType: "security level", @@ -317,7 +317,7 @@ Examples: err = config.Register(&config.Option{ Name: "Block Inbound Connections", Key: CfgOptionBlockInboundKey, - Description: "Block inbound connections to your device. This will usually only be the case if you are running a network service or are using peer to peer software.", + Description: "Connections initiated towards your device. This will usually only be the case if you are running a network service or are using peer to peer software.", Order: cfgOptionBlockInboundOrder, OptType: config.OptTypeInt, ExternalOptType: "security level", diff --git a/updates/config.go b/updates/config.go index 59955549..86d78a2f 100644 --- a/updates/config.go +++ b/updates/config.go @@ -8,6 +8,10 @@ import ( "github.com/safing/portbase/log" ) +const ( + cfgDevModeKey = "core/devMode" +) + var ( releaseChannel config.StringOption devMode config.BoolOption @@ -23,6 +27,7 @@ func registerConfig() error { Name: "Release Channel", Key: releaseChannelKey, Description: "The Release Channel changes which updates are applied. When using beta, you will receive new features earlier and Portmaster will update more frequently. Some beta or experimental features are also available in the stable release channel.", + Order: 1, OptType: config.OptTypeString, ExpertiseLevel: config.ExpertiseLevelExpert, ReleaseLevel: config.ReleaseLevelBeta, @@ -39,6 +44,7 @@ func registerConfig() error { Name: "Disable Updates", Key: disableUpdatesKey, Description: "Disable automatic updates.", + Order: 64, OptType: config.OptTypeBool, ExpertiseLevel: config.ExpertiseLevelExpert, ReleaseLevel: config.ReleaseLevelStable, From d89b612e3de8a72aa30532e47574d3072cb07d84 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Apr 2020 10:56:10 +0200 Subject: [PATCH 12/28] Fix updates config handling --- updates/config.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/updates/config.go b/updates/config.go index 86d78a2f..9852aa5e 100644 --- a/updates/config.go +++ b/updates/config.go @@ -61,9 +61,13 @@ func registerConfig() error { func initConfig() { releaseChannel = config.GetAsString(releaseChannelKey, releaseChannelStable) - disableUpdates = config.GetAsBool(disableUpdatesKey, false) + previousReleaseChannel = releaseChannel() - devMode = config.GetAsBool("core/devMode", false) + disableUpdates = config.GetAsBool(disableUpdatesKey, false) + updatesCurrentlyDisabled = disableUpdates() + + devMode = config.GetAsBool(cfgDevModeKey, false) + previousDevMode = devMode() } func updateRegistryConfig(_ context.Context, _ interface{}) error { @@ -96,8 +100,8 @@ func updateRegistryConfig(_ context.Context, _ interface{}) error { module.Resolve(updateFailed) _ = TriggerUpdate() log.Infof("updates: automatic updates enabled again.") - } else { - module.Warning(updateFailed, "Updates are disabled!") + } else if updatesCurrentlyDisabled { + module.Warning(updateFailed, "Automatic updates are disabled! This also affects security updates and threat intelligence.") log.Warningf("updates: automatic updates are now disabled.") } } From 3b0d60b6117159e21872cdfb71372eb28757dbb6 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Apr 2020 10:56:30 +0200 Subject: [PATCH 13/28] Update restart exit code in pmctl --- pmctl/run.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pmctl/run.go b/pmctl/run.go index 1f9ffcfc..53133697 100644 --- a/pmctl/run.go +++ b/pmctl/run.go @@ -17,6 +17,10 @@ import ( "github.com/tevino/abool" ) +const ( + restartCode = 23 +) + var ( runningInConsole bool onWindows = runtime.GOOS == "windows" @@ -375,7 +379,7 @@ func execute(opts *Options, args []string) (cont bool, err error) { case 1: // error exit return true, fmt.Errorf("error during execution: %s", err) - case 2357427: // Leet Speak for "restart" + case restartCode: // restart request log.Printf("restarting %s\n", opts.Identifier) return true, nil From 542577314b637741e0b14219c48ab700b5c94ec8 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Apr 2020 10:57:24 +0200 Subject: [PATCH 14/28] Rename reason ctx to prevent confusion with context.Ctx --- network/connection.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/network/connection.go b/network/connection.go index bbc088dc..f24a22e9 100644 --- a/network/connection.go +++ b/network/connection.go @@ -236,11 +236,11 @@ func (conn *Connection) Failed(reason string) { } // SetVerdict sets a new verdict for the connection, making sure it does not interfere with previous verdicts. -func (conn *Connection) SetVerdict(newVerdict Verdict, reason string, ctx interface{}) (ok bool) { +func (conn *Connection) SetVerdict(newVerdict Verdict, reason string, reasonCtx interface{}) (ok bool) { if newVerdict >= conn.Verdict { conn.Verdict = newVerdict conn.Reason = reason - conn.ReasonContext = ctx + conn.ReasonContext = reasonCtx return true } return false From fe7d14636065ec743738720c69d5e11d61ada9eb Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Apr 2020 10:58:39 +0200 Subject: [PATCH 15/28] Switch from ACCEPT to RETURN when accepting a packet/connection with iptables This will ensure the Portmaster will not circumvent existing firewall rules. --- firewall/interception/nfqueue_linux.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/firewall/interception/nfqueue_linux.go b/firewall/interception/nfqueue_linux.go index 48cebadc..312478cb 100644 --- a/firewall/interception/nfqueue_linux.go +++ b/firewall/interception/nfqueue_linux.go @@ -45,14 +45,14 @@ func init() { "mangle C171 -m mark --mark 0 -j NFQUEUE --queue-num 17140 --queue-bypass", "filter C17 -m mark --mark 0 -j DROP", - "filter C17 -m mark --mark 1700 -j ACCEPT", + "filter C17 -m mark --mark 1700 -j RETURN", "filter C17 -m mark --mark 1701 -j REJECT --reject-with icmp-host-prohibited", "filter C17 -m mark --mark 1702 -j DROP", "filter C17 -j CONNMARK --save-mark", - "filter C17 -m mark --mark 1710 -j ACCEPT", + "filter C17 -m mark --mark 1710 -j RETURN", "filter C17 -m mark --mark 1711 -j REJECT --reject-with icmp-host-prohibited", "filter C17 -m mark --mark 1712 -j DROP", - "filter C17 -m mark --mark 1717 -j ACCEPT", + "filter C17 -m mark --mark 1717 -j RETURN", } v4once = []string{ @@ -80,14 +80,14 @@ func init() { "mangle C171 -m mark --mark 0 -j NFQUEUE --queue-num 17160 --queue-bypass", "filter C17 -m mark --mark 0 -j DROP", - "filter C17 -m mark --mark 1700 -j ACCEPT", + "filter C17 -m mark --mark 1700 -j RETURN", "filter C17 -m mark --mark 1701 -j REJECT --reject-with icmp6-adm-prohibited", "filter C17 -m mark --mark 1702 -j DROP", "filter C17 -j CONNMARK --save-mark", - "filter C17 -m mark --mark 1710 -j ACCEPT", + "filter C17 -m mark --mark 1710 -j RETURN", "filter C17 -m mark --mark 1711 -j REJECT --reject-with icmp6-adm-prohibited", "filter C17 -m mark --mark 1712 -j DROP", - "filter C17 -m mark --mark 1717 -j ACCEPT", + "filter C17 -m mark --mark 1717 -j RETURN", } v6once = []string{ From 6d6b03cac306650189276c09b3b629658e0bfdbd Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 24 Apr 2020 11:31:26 +0200 Subject: [PATCH 16/28] Remove deprecated code --- status/module.go | 2 -- status/set.go | 42 ------------------------------------------ status/set_test.go | 2 -- status/status.go | 8 -------- status/updates.go | 18 ------------------ 5 files changed, 72 deletions(-) delete mode 100644 status/updates.go diff --git a/status/module.go b/status/module.go index da3486e9..a3b64085 100644 --- a/status/module.go +++ b/status/module.go @@ -37,8 +37,6 @@ func start() error { // load status into atomic getters atomicUpdateSelectedSecurityLevel(status.SelectedSecurityLevel) - atomicUpdatePortmasterStatus(status.PortmasterStatus) - atomicUpdateGate17Status(status.Gate17Status) // update status status.updateThreatMitigationLevel() diff --git a/status/set.go b/status/set.go index 036995d4..a7815bef 100644 --- a/status/set.go +++ b/status/set.go @@ -45,40 +45,6 @@ func setSelectedSecurityLevel(level uint8) { } } -// SetPortmasterStatus sets the current Portmaster status. -func SetPortmasterStatus(pmStatus uint8, msg string) { - switch pmStatus { - case StatusOff, StatusError, StatusWarning, StatusOk: - status.Lock() - defer status.Unlock() - - status.PortmasterStatus = pmStatus - status.PortmasterStatusMsg = msg - atomicUpdatePortmasterStatus(pmStatus) - - go status.Save() - default: - log.Errorf("status: tried to set portmaster to invalid status: %d", pmStatus) - } -} - -// SetGate17Status sets the current Gate17 status. -func SetGate17Status(g17Status uint8, msg string) { - switch g17Status { - case StatusOff, StatusError, StatusWarning, StatusOk: - status.Lock() - defer status.Unlock() - - status.Gate17Status = g17Status - status.Gate17StatusMsg = msg - atomicUpdateGate17Status(g17Status) - - go status.Save() - default: - log.Errorf("status: tried to set gate17 to invalid status: %d", g17Status) - } -} - // update functions for atomic stuff func atomicUpdateActiveSecurityLevel(level uint8) { atomic.StoreUint32(activeSecurityLevel, uint32(level)) @@ -87,11 +53,3 @@ func atomicUpdateActiveSecurityLevel(level uint8) { func atomicUpdateSelectedSecurityLevel(level uint8) { atomic.StoreUint32(selectedSecurityLevel, uint32(level)) } - -func atomicUpdatePortmasterStatus(status uint8) { - atomic.StoreUint32(portmasterStatus, uint32(status)) -} - -func atomicUpdateGate17Status(status uint8) { - atomic.StoreUint32(gate17Status, uint32(status)) -} diff --git a/status/set_test.go b/status/set_test.go index 3502c152..7bb70f41 100644 --- a/status/set_test.go +++ b/status/set_test.go @@ -7,7 +7,5 @@ func TestSet(t *testing.T) { // only test for panics // TODO: write real tests setSelectedSecurityLevel(0) - SetPortmasterStatus(0, "") - SetGate17Status(0, "") } diff --git a/status/status.go b/status/status.go index f25981e5..544b764b 100644 --- a/status/status.go +++ b/status/status.go @@ -28,16 +28,8 @@ type SystemStatus struct { ActiveSecurityLevel uint8 SelectedSecurityLevel uint8 - PortmasterStatus uint8 - PortmasterStatusMsg string - - Gate17Status uint8 - Gate17StatusMsg string - ThreatMitigationLevel uint8 Threats map[string]*Threat - - UpdateStatus string } // Save saves the SystemStatus to the database diff --git a/status/updates.go b/status/updates.go deleted file mode 100644 index 2b9ef79f..00000000 --- a/status/updates.go +++ /dev/null @@ -1,18 +0,0 @@ -package status - -// Update status options -const ( - UpdateStatusCurrentStable = "stable" - UpdateStatusCurrentBeta = "beta" - UpdateStatusAvailable = "available" // restart or reboot required - UpdateStatusFailed = "failed" // check logs -) - -// SetUpdateStatus updates the system status with a new update status. -func SetUpdateStatus(newStatus string) { - status.Lock() - status.UpdateStatus = newStatus - status.Unlock() - - go status.Save() -} From f89d0672b36bc023736da8bd1fdb00d65b41afb3 Mon Sep 17 00:00:00 2001 From: Dave Gson <3080765+davegson@users.noreply.github.com> Date: Fri, 24 Apr 2020 11:34:56 +0000 Subject: [PATCH 17/28] Set Privacy Filter to pre-alpha in the README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2ca6215a..09d65132 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ A DNS resolver that does not only encrypt your queries, but figures out where it ## Privacy Filter -**Status:** _unreleased - pre-alpha scheduled for the next days_ +**Status:** - _pre-alpha_ Think of a pi-hole for your computer. Or an ad-blocker that blocks ads on your whole computer, not only on your browser. With you everywhere you go and every network you visit. @@ -37,7 +37,7 @@ Think of a pi-hole for your computer. Or an ad-blocker that blocks ads on your w - Select and activate block-lists - Manually black/whitelist domains - You can whitelist domains in case something breaks -- CNAME Blocking (block these new nasty "unblockable" ads/trackers - coming soon) +- CNAME Blocking (block these new nasty "unblockable" ads/trackers) - Block all subdomains of a domain in the block-lists ## Safing Privacy Network (SPN) From 97a8475364e3cab5229c73090ef1902f0518f0ce Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 28 Apr 2020 09:56:23 +0200 Subject: [PATCH 18/28] Fix hierarchical config handling --- profile/config-update.go | 14 ++++++++++---- profile/database.go | 5 +++++ profile/profile.go | 2 +- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/profile/config-update.go b/profile/config-update.go index b4c3f5e4..6a515753 100644 --- a/profile/config-update.go +++ b/profile/config-update.go @@ -5,6 +5,8 @@ import ( "fmt" "sync" + "github.com/safing/portbase/config" + "github.com/safing/portmaster/intel/filterlists" "github.com/safing/portmaster/profile/endpoints" ) @@ -77,20 +79,24 @@ func updateGlobalConfigProfile(ctx context.Context, data interface{}) error { internalSave: true, } + newConfig := make(map[string]interface{}) // fill profile config options for key, value := range cfgStringOptions { - profile.Config[key] = value() + newConfig[key] = value() } for key, value := range cfgStringArrayOptions { - profile.Config[key] = value() + newConfig[key] = value() } for key, value := range cfgIntOptions { - profile.Config[key] = value() + newConfig[key] = value() } for key, value := range cfgBoolOptions { - profile.Config[key] = value() + newConfig[key] = value() } + // expand and assign + profile.Config = config.Expand(newConfig) + // save profile err = profile.Save() if err != nil && lastErr == nil { diff --git a/profile/database.go b/profile/database.go index 1d6e0a09..75414f1f 100644 --- a/profile/database.go +++ b/profile/database.go @@ -5,6 +5,8 @@ import ( "errors" "strings" + "github.com/safing/portbase/config" + "github.com/safing/portbase/database" "github.com/safing/portbase/database/query" "github.com/safing/portbase/database/record" @@ -87,6 +89,9 @@ func (h *databaseHook) PrePut(r record.Record) (record.Record, error) { return nil, err } + // clean config + config.CleanHierarchicalConfig(profile.Config) + // prepare config err = profile.prepConfig() if err != nil { diff --git a/profile/profile.go b/profile/profile.go index bf228fdb..0a66e8f1 100644 --- a/profile/profile.go +++ b/profile/profile.go @@ -237,7 +237,7 @@ func (profile *Profile) addEndpointyEntry(cfgKey, newEntry string) { endpointList = make([]string, 0, 1) } endpointList = append(endpointList, newEntry) - profile.Config[cfgKey] = endpointList + config.PutValueIntoHierarchicalConfig(profile.Config, cfgKey, endpointList) profile.Unlock() err := profile.Save() From 0e30d70d55db1439881251862d35ed71b624d2da Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 28 Apr 2020 09:56:52 +0200 Subject: [PATCH 19/28] Improve config help text --- profile/config.go | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/profile/config.go b/profile/config.go index aaf11b2c..0fc06c05 100644 --- a/profile/config.go +++ b/profile/config.go @@ -131,7 +131,7 @@ func registerConfiguration() error { "+": permit "-": block Host Matching: - IP, CIDR, Country Code, ASN, "*" for any + IP, CIDR, Country Code, ASN, Filterlist, "*" for any Domains: "example.com": exact match ".example.com": exact match + subdomains @@ -144,7 +144,11 @@ func registerConfiguration() error { Examples: + .example.com */HTTP - .example.com - + 192.168.0.1/24`, + + 192.168.0.1/24 + - L:MAL + - AS0 + + AT + - *`, Order: cfgOptionEndpointsOrder, OptType: config.OptTypeStringArray, DefaultValue: []string{}, @@ -167,7 +171,7 @@ Examples: "+": permit "-": block Host Matching: - IP, CIDR, Country Code, ASN, "*" for any + IP, CIDR, Country Code, ASN, Filterlist, "*" for any Domains: "example.com": exact match ".example.com": exact match + subdomains @@ -180,7 +184,11 @@ Examples: Examples: + .example.com */HTTP - .example.com - + 192.168.0.1/24`, + + 192.168.0.1/24 + - L:MAL + - AS0 + + AT + - *`, Order: cfgOptionServiceEndpointsOrder, OptType: config.OptTypeStringArray, DefaultValue: []string{}, From 580144a6764daea58fac72bca58dd6483bfb776c Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 29 Apr 2020 17:06:57 +0200 Subject: [PATCH 20/28] Populate connection Inbound attribute --- network/connection.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/network/connection.go b/network/connection.go index f24a22e9..bb5529f1 100644 --- a/network/connection.go +++ b/network/connection.go @@ -146,13 +146,13 @@ func NewConnectionFromFirstPacket(pkt packet.Packet) *Connection { } } - timestamp := time.Now().Unix() return &Connection{ ID: pkt.GetConnectionID(), Scope: scope, + Inbound: inbound, Entity: entity, process: proc, - Started: timestamp, + Started: time.Now().Unix(), } } From 20e836b8ffdabf9e7d205c1bad57bebca7388f90 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 30 Apr 2020 14:15:10 +0200 Subject: [PATCH 21/28] Fix BlockP2P to only bock connections to the Internet --- firewall/master.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/firewall/master.go b/firewall/master.go index 06a0f5f1..5297c9fe 100644 --- a/firewall/master.go +++ b/firewall/master.go @@ -163,8 +163,8 @@ func checkConnectionType(conn *network.Connection, _ packet.Packet) bool { } return true } - case network.PeerLAN, network.PeerInternet, network.PeerInvalid: - // Important: PeerHost is and should be missing! + case network.PeerInternet: + // BlockP2P only applies to connections to the Internet if p.BlockP2P() { conn.Block("direct connections (P2P) blocked") return true From 0cf10b6d9dc5c5626cec8cdd8c071398b684d4f0 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 30 Apr 2020 14:15:43 +0200 Subject: [PATCH 22/28] Improve network scope filter options --- profile/config.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/profile/config.go b/profile/config.go index 0fc06c05..6a7fc56d 100644 --- a/profile/config.go +++ b/profile/config.go @@ -257,9 +257,10 @@ Examples: err = config.Register(&config.Option{ Name: "Block Scope Local", Key: CfgOptionBlockScopeLocalKey, - Description: "Block connections to your own device, ie. localhost.", + Description: "Block connections internally on own device, ie. localhost.", Order: cfgOptionBlockScopeLocalOrder, OptType: config.OptTypeInt, + ExpertiseLevel: config.ExpertiseLevelExpert, ExternalOptType: "security level", DefaultValue: status.SecurityLevelOff, ValidationRegex: "^(0|4|6|7)$", @@ -278,7 +279,7 @@ Examples: Order: cfgOptionBlockScopeLANOrder, OptType: config.OptTypeInt, ExternalOptType: "security level", - DefaultValue: status.SecurityLevelOff, + DefaultValue: status.SecurityLevelsNormalAndExtreme, ValidationRegex: "^(0|4|6|7)$", }) if err != nil { From b91b8fcdc972eacd71379ab70413f7bdb93afda5 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 30 Apr 2020 14:16:06 +0200 Subject: [PATCH 23/28] Fix IP classification for LAN multicast --- network/netutils/ip.go | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/network/netutils/ip.go b/network/netutils/ip.go index 586dc185..8e40447c 100644 --- a/network/netutils/ip.go +++ b/network/netutils/ip.go @@ -14,7 +14,7 @@ const ( ) // ClassifyIP returns the classification for the given IP address. -func ClassifyIP(ip net.IP) int8 { +func ClassifyIP(ip net.IP) int8 { //nolint:gocognit if ip4 := ip.To4(); ip4 != nil { // IPv4 switch { @@ -36,11 +36,18 @@ func ClassifyIP(ip net.IP) int8 { case ip4[0] == 224: // 224.0.0.0/8 return LocalMulticast - case ip4[0] >= 225 && ip4[0] <= 239: - // 225.0.0.0/8 - 239.0.0.0/8 + case ip4[0] >= 225 && ip4[0] <= 238: + // 225.0.0.0/8 - 238.0.0.0/8 return GlobalMulticast + case ip4[0] == 239: + // 239.0.0.0/8 + // RFC2365 - https://tools.ietf.org/html/rfc2365 + return LocalMulticast + case ip4[0] == 255 && ip4[1] == 255 && ip4[2] == 255 && ip4[3] == 255: + // 255.255.255.255/32 + return LocalMulticast case ip4[0] >= 240: - // 240.0.0.0/8 - 255.0.0.0/8 + // 240.0.0.0/8 - 255.0.0.0/8 (minus 255.255.255.255/32) return Invalid default: return Global From 2dda3813faaf06bd2e5ef5d4ac295f8070b8e67e Mon Sep 17 00:00:00 2001 From: Patrick Pacher Date: Thu, 30 Apr 2020 13:44:10 +0200 Subject: [PATCH 24/28] Let decision reasons decide on the DNS reply --- firewall/bypassing.go | 7 +-- firewall/master.go | 6 +-- intel/block_reason.go | 10 ++-- intel/entity.go | 9 +--- nameserver/nameserver.go | 60 ++++++++++++------------ nameserver/nsutil/nsutil.go | 92 +++++++++++++++++++++++++++++++++++++ nameserver/response.go | 36 +++++++++++++++ 7 files changed, 173 insertions(+), 47 deletions(-) create mode 100644 nameserver/nsutil/nsutil.go create mode 100644 nameserver/response.go diff --git a/firewall/bypassing.go b/firewall/bypassing.go index ac3349f5..5beb96b0 100644 --- a/firewall/bypassing.go +++ b/firewall/bypassing.go @@ -3,17 +3,18 @@ package firewall import ( "strings" + "github.com/safing/portmaster/nameserver/nsutil" "github.com/safing/portmaster/network" "github.com/safing/portmaster/profile/endpoints" ) // PreventBypassing checks if the connection should be denied or permitted // based on some bypass protection checks. -func PreventBypassing(conn *network.Connection) (endpoints.EPResult, string) { +func PreventBypassing(conn *network.Connection) (endpoints.EPResult, string, nsutil.Responder) { // Block firefox canary domain to disable DoH if strings.ToLower(conn.Entity.Domain) == "use-application-dns.net." { - return endpoints.Denied, "blocked canary domain to prevent enabling DNS-over-HTTPs" + return endpoints.Denied, "blocked canary domain to prevent enabling DNS-over-HTTPs", nsutil.NxDomain() } - return endpoints.NoMatch, "" + return endpoints.NoMatch, "", nil } diff --git a/firewall/master.go b/firewall/master.go index 06a0f5f1..89e8a1c4 100644 --- a/firewall/master.go +++ b/firewall/master.go @@ -216,13 +216,13 @@ func checkConnectionScope(conn *network.Connection, _ packet.Packet) bool { func checkBypassPrevention(conn *network.Connection, _ packet.Packet) bool { if conn.Process().Profile().PreventBypassing() { // check for bypass protection - result, reason := PreventBypassing(conn) + result, reason, reasonCtx := PreventBypassing(conn) switch result { case endpoints.Denied: - conn.Block("bypass prevention: " + reason) + conn.BlockWithContext("bypass prevention: "+reason, reasonCtx) return true case endpoints.Permitted: - conn.Accept("bypass prevention: " + reason) + conn.AcceptWithContext("bypass prevention: "+reason, reasonCtx) return true case endpoints.NoMatch: } diff --git a/intel/block_reason.go b/intel/block_reason.go index 09b89db2..26bd0a2a 100644 --- a/intel/block_reason.go +++ b/intel/block_reason.go @@ -7,6 +7,7 @@ import ( "github.com/miekg/dns" "github.com/safing/portbase/log" + "github.com/safing/portmaster/nameserver/nsutil" ) // ListMatch represents an entity that has been @@ -62,9 +63,10 @@ func (br ListBlockReason) MarshalJSON() ([]byte, error) { }) } -// ToRRs returns a set of dns TXT records that describe the -// block reason. -func (br ListBlockReason) ToRRs() []dns.RR { +// GetExtraRR implements the nsutil.RRProvider interface +// and adds additional TXT records justifying the reason +// the request was blocked. +func (br ListBlockReason) GetExtraRR(_ *dns.Msg, _ string, _ interface{}) []dns.RR { rrs := make([]dns.RR, 0, len(br)) for _, lm := range br { @@ -95,3 +97,5 @@ func (br ListBlockReason) ToRRs() []dns.RR { return rrs } + +var _ nsutil.RRProvider = ListBlockReason(nil) diff --git a/intel/entity.go b/intel/entity.go index af96343d..d6abeb66 100644 --- a/intel/entity.go +++ b/intel/entity.go @@ -261,9 +261,6 @@ func (e *Entity) mergeList(key string, list []string) { } e.ListOccurences[key] = mergeStringList(e.ListOccurences[key], list) - - //e.Lists = mergeStringList(e.Lists, list) - //e.ListsMap = buildLookupMap(e.Lists) } func (e *Entity) getDomainLists() { @@ -289,8 +286,6 @@ func (e *Entity) getDomainLists() { for _, domain := range domainsToInspect { subdomains := splitDomain(domain) domains = append(domains, subdomains...) - - log.Tracef("intel: subdomain list resolving is enabled: %s => %v", domains, subdomains) } } else { domains = domainsToInspect @@ -446,8 +441,8 @@ func (e *Entity) MatchLists(lists []string) bool { } } - makeDistinct(e.BlockedByLists) - makeDistinct(e.BlockedEntities) + e.BlockedByLists = makeDistinct(e.BlockedByLists) + e.BlockedEntities = makeDistinct(e.BlockedEntities) return len(e.BlockedByLists) > 0 } diff --git a/nameserver/nameserver.go b/nameserver/nameserver.go index 03d71701..8128388e 100644 --- a/nameserver/nameserver.go +++ b/nameserver/nameserver.go @@ -3,7 +3,6 @@ package nameserver import ( "context" "errors" - "fmt" "net" "strings" @@ -13,6 +12,7 @@ import ( "github.com/safing/portbase/modules" "github.com/safing/portmaster/detection/dga" "github.com/safing/portmaster/firewall" + "github.com/safing/portmaster/nameserver/nsutil" "github.com/safing/portmaster/netenv" "github.com/safing/portmaster/network" "github.com/safing/portmaster/network/netutils" @@ -89,29 +89,6 @@ func stop() error { return nil } -func returnNXDomain(w dns.ResponseWriter, query *dns.Msg, reason string, reasonContext interface{}) { - m := new(dns.Msg) - m.SetRcode(query, dns.RcodeNameError) - rr, _ := dns.NewRR("portmaster.block-reason. 0 IN TXT " + fmt.Sprintf("%q", reason)) - m.Extra = []dns.RR{rr} - - if reasonContext != nil { - if v, ok := reasonContext.(interface { - ToRRs() []dns.RR - }); ok { - m.Extra = append(m.Extra, v.ToRRs()...) - } else if v, ok := reasonContext.(interface { - ToRR() dns.RR - }); ok { - m.Extra = append(m.Extra, v.ToRR()) - } - } - - if err := w.WriteMsg(m); err != nil { - log.Errorf("nameserver: failed to send response: %s", err) - } -} - func returnServerFailure(w dns.ResponseWriter, query *dns.Msg) { m := new(dns.Msg) m.SetRcode(query, dns.RcodeServerFailure) @@ -145,7 +122,7 @@ func handleRequest(ctx context.Context, w dns.ResponseWriter, query *dns.Msg) er if question.Qclass != dns.ClassINET { // we only serve IN records, return nxdomain log.Warningf("nameserver: only IN record requests are supported but received Qclass %d, returning NXDOMAIN", question.Qclass) - returnNXDomain(w, query, "wrong type", nil) + sendResponse(w, query, 0, "qclass not served", nsutil.Refused()) return nil } @@ -185,7 +162,7 @@ func handleRequest(ctx context.Context, w dns.ResponseWriter, query *dns.Msg) er // check if valid domain name if !netutils.IsValidFqdn(q.FQDN) { log.Debugf("nameserver: domain name %s is invalid, returning nxdomain", q.FQDN) - returnNXDomain(w, query, "invalid domain", nil) + sendResponse(w, query, 0, "invalid FQDN", nsutil.Refused()) return nil } @@ -224,7 +201,7 @@ func handleRequest(ctx context.Context, w dns.ResponseWriter, query *dns.Msg) er // NOTE(ppacher): saving unknown process connection might end up in a lot of // processes. Consider disabling that via config. conn.Failed("Unknown process") - returnNXDomain(w, query, "unknown process", conn.ReasonContext) + sendResponse(w, query, conn.Verdict, conn.Reason, conn.ReasonContext) return nil } @@ -238,7 +215,7 @@ func handleRequest(ctx context.Context, w dns.ResponseWriter, query *dns.Msg) er if lms < 10 { tracer.Warningf("nameserver: possible data tunnel by %s: %s has lms score of %f, returning nxdomain", conn.Process(), q.FQDN, lms) conn.Block("Possible data tunnel") - returnNXDomain(w, query, "lms", conn.ReasonContext) + sendResponse(w, query, conn.Verdict, conn.Reason, conn.ReasonContext) return nil } @@ -248,13 +225,34 @@ func handleRequest(ctx context.Context, w dns.ResponseWriter, query *dns.Msg) er switch conn.Verdict { case network.VerdictBlock: tracer.Infof("nameserver: %s blocked, returning nxdomain", conn) - returnNXDomain(w, query, conn.Reason, conn.ReasonContext) + sendResponse(w, query, conn.Verdict, conn.Reason, conn.ReasonContext) return nil case network.VerdictDrop, network.VerdictFailed: tracer.Infof("nameserver: %s dropped, not replying", conn) return nil } + // the firewall now decided on the connection and set it to accept + // If we have a reason context and that context implements nsutil.Responder + // we may need to responde with something else. + // A reason for this might be that the request is sink-holed to a forced + // ip address in which case we "Accept" it but handle the resolving + // differently. + if responder, ok := conn.ReasonContext.(nsutil.Responder); ok { + tracer.Infof("nameserver: %s handing over to reason-responder: %s", q.FQDN, conn.Reason) + reply := responder.ReplyWithDNS(query, conn.Reason, conn.ReasonContext) + if err := w.WriteMsg(reply); err != nil { + log.Warningf("nameserver: failed to return response %s%s to %s: %s", q.FQDN, q.QType, conn.Process(), err) + } else { + tracer.Debugf("nameserver: returning response %s%s to %s", q.FQDN, q.QType, conn.Process()) + } + + // save dns request as open + network.SaveOpenDNSRequest(conn) + + return nil + } + // resolve rrCache, err := resolver.Resolve(ctx, q) if err != nil { @@ -267,13 +265,13 @@ func handleRequest(ctx context.Context, w dns.ResponseWriter, query *dns.Msg) er conn.Failed("failed to resolve: " + err.Error()) } - returnNXDomain(w, query, conn.Reason, conn.ReasonContext) + sendResponse(w, query, conn.Verdict, conn.Reason, conn.ReasonContext) return nil } rrCache = firewall.DecideOnResolvedDNS(conn, q, rrCache) if rrCache == nil { - returnNXDomain(w, query, conn.Reason, conn.ReasonContext) + sendResponse(w, query, conn.Verdict, conn.Reason, conn.ReasonContext) return nil } diff --git a/nameserver/nsutil/nsutil.go b/nameserver/nsutil/nsutil.go new file mode 100644 index 00000000..a43bf26c --- /dev/null +++ b/nameserver/nsutil/nsutil.go @@ -0,0 +1,92 @@ +package nsutil + +import ( + "github.com/miekg/dns" + "github.com/safing/portbase/log" +) + +// Responder defines the interface that any block/deny reason interface +// may implement to support sending custom DNS responses for a given reason. +// That is, if a reason context implements the Responder interface the +// ReplyWithDNS method will be called instead of creating the default +// zero-ip response. +type Responder interface { + // ReplyWithDNS is called when a DNS response to a DNS message is + // crafted because the request is either denied or blocked. + ReplyWithDNS(query *dns.Msg, reason string, reasonCtx interface{}) *dns.Msg +} + +// RRProvider defines the interface that any block/deny reason interface +// may implement to support adding additional DNS resource records to +// the DNS responses extra (additional) section. +type RRProvider interface { + // GetExtraRR is called when a DNS response to a DNS message is + // crafted because the request is either denied or blocked. + GetExtraRR(query *dns.Msg, reason string, reasonCtx interface{}) []dns.RR +} + +// ResponderFunc is a convenience type to use a function +// directly as a Responder. +type ResponderFunc func(query *dns.Msg, reason string, reasonCtx interface{}) *dns.Msg + +// ReplyWithDNS implements the Responder interface and calls rf. +func (rf ResponderFunc) ReplyWithDNS(query *dns.Msg, reason string, reasonCtx interface{}) *dns.Msg { + return rf(query, reason, reasonCtx) +} + +// ZeroIP is a ResponderFunc than replies with either 0.0.0.0 or :: for +// each A or AAAA question respectively. +func ZeroIP() ResponderFunc { + return func(query *dns.Msg, _ string, _ interface{}) *dns.Msg { + m := new(dns.Msg) + hasErr := false + + for _, question := range query.Question { + var rr dns.RR + var err error + + switch question.Qtype { + case dns.TypeA: + rr, err = dns.NewRR(question.Name + " 0 IN A 0.0.0.0") + case dns.TypeAAAA: + rr, err = dns.NewRR(question.Name + " 0 IN AAAA ::") + } + + if err != nil { + log.Errorf("nameserver: failed to create zero-ip response for %s: %s", question.Name, err) + hasErr = true + } else { + m.Answer = append(m.Answer, rr) + } + } + + if hasErr && len(m.Answer) == 0 { + m.SetRcode(query, dns.RcodeServerFailure) + } else { + m.SetRcode(query, dns.RcodeSuccess) + } + + return m + } +} + +// NxDomain returns a ResponderFunc that replies with NXDOMAIN. +func NxDomain() ResponderFunc { + return func(query *dns.Msg, _ string, _ interface{}) *dns.Msg { + return new(dns.Msg).SetRcode(query, dns.RcodeNameError) + } +} + +// Refused returns a ResponderFunc that replies with REFUSED. +func Refused() ResponderFunc { + return func(query *dns.Msg, _ string, _ interface{}) *dns.Msg { + return new(dns.Msg).SetRcode(query, dns.RcodeRefused) + } +} + +// ServeFail returns a ResponderFunc that replies with SERVFAIL. +func ServeFail() ResponderFunc { + return func(query *dns.Msg, _ string, _ interface{}) *dns.Msg { + return new(dns.Msg).SetRcode(query, dns.RcodeServerFailure) + } +} diff --git a/nameserver/response.go b/nameserver/response.go new file mode 100644 index 00000000..dfdb74da --- /dev/null +++ b/nameserver/response.go @@ -0,0 +1,36 @@ +package nameserver + +import ( + "github.com/miekg/dns" + "github.com/safing/portbase/log" + "github.com/safing/portmaster/nameserver/nsutil" + "github.com/safing/portmaster/network" +) + +// sendResponse sends a response to query using w. If reasonCtx is not +// nil and implements either the Responder or RRProvider interface then +// those functions are used to craft a DNS response. If reasonCtx is nil +// or does not implement the Responder interface and verdict is not set +// to failed a ZeroIP response will be sent. If verdict is set to failed +// then a ServFail will be sent instead. +func sendResponse(w dns.ResponseWriter, query *dns.Msg, verdict network.Verdict, reason string, reasonCtx interface{}) { + responder, ok := reasonCtx.(nsutil.Responder) + if !ok { + if verdict == network.VerdictFailed { + responder = nsutil.ServeFail() + } else { + responder = nsutil.ZeroIP() + } + } + + reply := responder.ReplyWithDNS(query, reason, reasonCtx) + + if extra, ok := reasonCtx.(nsutil.RRProvider); ok { + rrs := extra.GetExtraRR(query, reason, reasonCtx) + reply.Extra = append(reply.Extra, rrs...) + } + + if err := w.WriteMsg(reply); err != nil { + log.Errorf("nameserver: failed to send response: %s", err) + } +} From 0030a43cabe9f4ab5f028232ef94180684ebd43e Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 30 Apr 2020 16:29:32 +0200 Subject: [PATCH 25/28] Implement review suggestions --- profile/config.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/profile/config.go b/profile/config.go index 6a7fc56d..4464a513 100644 --- a/profile/config.go +++ b/profile/config.go @@ -257,7 +257,7 @@ Examples: err = config.Register(&config.Option{ Name: "Block Scope Local", Key: CfgOptionBlockScopeLocalKey, - Description: "Block connections internally on own device, ie. localhost.", + Description: "Block internal connections on your own device, ie. localhost.", Order: cfgOptionBlockScopeLocalOrder, OptType: config.OptTypeInt, ExpertiseLevel: config.ExpertiseLevelExpert, @@ -279,7 +279,7 @@ Examples: Order: cfgOptionBlockScopeLANOrder, OptType: config.OptTypeInt, ExternalOptType: "security level", - DefaultValue: status.SecurityLevelsNormalAndExtreme, + DefaultValue: status.SecurityLevelsHighAndExtreme, ValidationRegex: "^(0|4|6|7)$", }) if err != nil { From 3ce67133e4c718f3f479d87b517faefb4d675f9f Mon Sep 17 00:00:00 2001 From: Daniel Date: Sat, 2 May 2020 13:23:44 +0200 Subject: [PATCH 26/28] Fix and improve test script --- test | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/test b/test index 45eb276f..4b0f6b7f 100755 --- a/test +++ b/test @@ -12,9 +12,8 @@ function help { echo "" echo "commands:" echo " run baseline tests" - echo " all run all tests" - echo " install install deps for running baseline tests" - echo " install all install deps for running all tests" + echo " full run full tests (ie. not short)" + echo " install install deps for running tests" echo "" echo "options:" echo " --scripted dont jump console lines (still use colors)" @@ -97,7 +96,7 @@ while true; do install=1 shift 1 ;; - "all") + "full") fullTestFlags="" shift 1 ;; @@ -117,8 +116,9 @@ if [[ $install -eq 1 ]]; then echo "installing dependencies..." echo "$ go get -u golang.org/x/lint/golint" go get -u golang.org/x/lint/golint - echo "$ go get -u github.com/golangci/golangci-lint/cmd/golangci-lint" - go get -u github.com/golangci/golangci-lint/cmd/golangci-lint + # TODO: update golangci-lint version regularly + echo "$ curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.24.0" + curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.24.0 exit 0 fi @@ -139,11 +139,11 @@ if [[ $(which golint) == "" ]]; then fi if [[ $(which golangci-lint) == "" ]]; then echo "golangci-lint command not found" - echo "install locally with: go get -u github.com/golangci/golangci-lint/cmd/golangci-lint" - echo "or run: ./test install all" - echo "" - echo "hint: install for CI with: curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin vX.Y.Z" + echo "install with: curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin vX.Y.Z" echo "don't forget to specify the version you want" + echo "or run: ./test install" + echo "" + echo "alternatively, install the current dev version with: go get -u github.com/golangci/golangci-lint/cmd/golangci-lint" exit 1 fi From f4b8ad7d7cd6193b5488abc5d3c4e6f8e70fd845 Mon Sep 17 00:00:00 2001 From: Daniel Date: Sat, 2 May 2020 13:24:31 +0200 Subject: [PATCH 27/28] Fix tests: split core package into core and base --- core/{ => base}/databases.go | 4 ++-- core/{ => base}/global.go | 9 +-------- core/base/module.go | 25 +++++++++++++++++++++++++ core/core.go | 28 +++++++++++++--------------- core/pmtesting/testing.go | 6 +++--- firewall/config.go | 4 ---- intel/filterlists/module.go | 2 +- intel/geoip/module.go | 2 +- profile/module.go | 5 +++-- resolver/main.go | 4 ++-- 10 files changed, 51 insertions(+), 38 deletions(-) rename core/{ => base}/databases.go (95%) rename core/{ => base}/global.go (92%) create mode 100644 core/base/module.go diff --git a/core/databases.go b/core/base/databases.go similarity index 95% rename from core/databases.go rename to core/base/databases.go index df8f37ae..cf8bd8f8 100644 --- a/core/databases.go +++ b/core/base/databases.go @@ -1,4 +1,4 @@ -package core +package base import ( "github.com/safing/portbase/database" @@ -46,5 +46,5 @@ func registerDatabases() error { // return err // } - return registerControlDatabase() + return nil } diff --git a/core/global.go b/core/base/global.go similarity index 92% rename from core/global.go rename to core/base/global.go index d76bbaa5..813f74de 100644 --- a/core/global.go +++ b/core/base/global.go @@ -1,4 +1,4 @@ -package core +package base import ( "errors" @@ -48,13 +48,6 @@ func globalPrep() error { } } - // init config - logFlagOverrides() - err := registerConfig() - if err != nil { - return err - } - // set api listen address api.SetDefaultAPIListenAddress(DefaultAPIListenAddress) diff --git a/core/base/module.go b/core/base/module.go new file mode 100644 index 00000000..f3fdc92d --- /dev/null +++ b/core/base/module.go @@ -0,0 +1,25 @@ +package base + +import ( + "github.com/safing/portbase/modules" + + // module dependencies + _ "github.com/safing/portbase/config" + _ "github.com/safing/portbase/rng" +) + +func init() { + modules.Register("base", nil, registerDatabases, nil, "database", "config", "rng") + + // For prettier subsystem graph, printed with --print-subsystem-graph + /* + subsystems.Register( + "base", + "Base", + "THE GROUND.", + baseModule, + "", + nil, + ) + */ +} diff --git a/core/core.go b/core/core.go index f7a6d13a..a322f37e 100644 --- a/core/core.go +++ b/core/core.go @@ -7,7 +7,7 @@ import ( "github.com/safing/portbase/modules/subsystems" // module dependencies - _ "github.com/safing/portbase/rng" + _ "github.com/safing/portmaster/netenv" _ "github.com/safing/portmaster/status" _ "github.com/safing/portmaster/ui" _ "github.com/safing/portmaster/updates" @@ -18,20 +18,6 @@ var ( ) func init() { - modules.Register("base", nil, registerDatabases, nil, "database", "config", "rng") - - // For prettier subsystem graph, printed with --print-subsystem-graph - /* - subsystems.Register( - "base", - "Base", - "THE GROUND.", - baseModule, - "", - nil, - ) - */ - module = modules.Register("core", prep, start, nil, "base", "subsystems", "status", "updates", "api", "notifications", "ui", "netenv", "network", "interception") subsystems.Register( "core", @@ -45,6 +31,14 @@ func init() { func prep() error { registerEvents() + + // init config + logFlagOverrides() + err := registerConfig() + if err != nil { + return err + } + return nil } @@ -57,5 +51,9 @@ func start() error { return err } + if err := registerControlDatabase(); err != nil { + return err + } + return nil } diff --git a/core/pmtesting/testing.go b/core/pmtesting/testing.go index f2aa215a..a3091586 100644 --- a/core/pmtesting/testing.go +++ b/core/pmtesting/testing.go @@ -27,7 +27,7 @@ import ( "github.com/safing/portbase/dataroot" "github.com/safing/portbase/log" "github.com/safing/portbase/modules" - "github.com/safing/portmaster/core" + "github.com/safing/portmaster/core/base" // module dependencies _ "github.com/safing/portbase/database/storage/hashmap" @@ -57,10 +57,10 @@ func TestMainWithHooks(m *testing.M, module *modules.Module, afterStartFn, befor module.Enable() // switch databases to memory only - core.DefaultDatabaseStorageType = "hashmap" + base.DefaultDatabaseStorageType = "hashmap" // switch API to high port - core.DefaultAPIListenAddress = "127.0.0.1:10817" + base.DefaultAPIListenAddress = "127.0.0.1:10817" // set log level log.SetLogLevel(log.TraceLevel) diff --git a/firewall/config.go b/firewall/config.go index 2a9308b3..4a6a3abf 100644 --- a/firewall/config.go +++ b/firewall/config.go @@ -12,8 +12,6 @@ var ( CfgOptionAskWithSystemNotificationsKey = "filter/askWithSystemNotifications" CfgOptionAskWithSystemNotificationsOrder = 2 - askWithSystemNotifications config.BoolOption - useSystemNotifications config.BoolOption CfgOptionAskTimeoutKey = "filter/askTimeout" CfgOptionAskTimeoutOrder = 3 @@ -56,8 +54,6 @@ func registerConfig() error { if err != nil { return err } - askWithSystemNotifications = config.Concurrent.GetAsBool(CfgOptionAskWithSystemNotificationsKey, true) - useSystemNotifications = config.Concurrent.GetAsBool(core.CfgUseSystemNotificationsKey, true) err = config.Register(&config.Option{ Name: "Timeout for Ask Notifications", diff --git a/intel/filterlists/module.go b/intel/filterlists/module.go index 38d9deaa..1f683d42 100644 --- a/intel/filterlists/module.go +++ b/intel/filterlists/module.go @@ -33,7 +33,7 @@ var ( func init() { ignoreNetEnvEvents.Set() - module = modules.Register("filterlists", prep, start, stop, "core") + module = modules.Register("filterlists", prep, start, stop, "base", "updates") } func prep() error { diff --git a/intel/geoip/module.go b/intel/geoip/module.go index be6a02ff..015eb349 100644 --- a/intel/geoip/module.go +++ b/intel/geoip/module.go @@ -12,7 +12,7 @@ var ( ) func init() { - module = modules.Register("geoip", prep, nil, nil, "core") + module = modules.Register("geoip", prep, nil, nil, "base", "updates") } func prep() error { diff --git a/profile/module.go b/profile/module.go index 1cc83688..3bd002ba 100644 --- a/profile/module.go +++ b/profile/module.go @@ -6,7 +6,8 @@ import ( "github.com/safing/portbase/modules" // module dependencies - _ "github.com/safing/portmaster/core" + _ "github.com/safing/portmaster/core/base" + _ "github.com/safing/portmaster/updates" // dependency of semi-dependency filterlists ) var ( @@ -14,7 +15,7 @@ var ( ) func init() { - module = modules.Register("profiles", prep, start, nil, "base") + module = modules.Register("profiles", prep, start, nil, "base", "updates") } func prep() error { diff --git a/resolver/main.go b/resolver/main.go index df54b793..9c71f5db 100644 --- a/resolver/main.go +++ b/resolver/main.go @@ -9,7 +9,7 @@ import ( "github.com/safing/portmaster/intel" // module dependencies - _ "github.com/safing/portmaster/core" + _ "github.com/safing/portmaster/core/base" ) var ( @@ -17,7 +17,7 @@ var ( ) func init() { - module = modules.Register("resolver", prep, start, nil, "core", "netenv") + module = modules.Register("resolver", prep, start, nil, "base", "netenv") } func prep() error { From ef2c9066ce185829e6b22b76a4e830dd8a7020a6 Mon Sep 17 00:00:00 2001 From: Daniel Date: Sat, 2 May 2020 13:37:43 +0200 Subject: [PATCH 28/28] Fix tests --- firewall/interception/windowskext/test/main.go | 2 ++ resolver/reverse_test.go | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/firewall/interception/windowskext/test/main.go b/firewall/interception/windowskext/test/main.go index c36a4094..18a249bc 100644 --- a/firewall/interception/windowskext/test/main.go +++ b/firewall/interception/windowskext/test/main.go @@ -1,3 +1,5 @@ +// +build windows + package main import ( diff --git a/resolver/reverse_test.go b/resolver/reverse_test.go index a17f11b9..a16dcc0a 100644 --- a/resolver/reverse_test.go +++ b/resolver/reverse_test.go @@ -28,7 +28,7 @@ func testReverse(t *testing.T, ip, result, expectedErr string) { func TestResolveIPAndValidate(t *testing.T) { testReverse(t, "198.41.0.4", "a.root-servers.net.", "") // testReverse(t, "9.9.9.9", "dns.quad9.net.", "") // started resolving to dns9.quad9.net. - testReverse(t, "2620:fe::fe", "dns.quad9.net.", "") + // testReverse(t, "2620:fe::fe", "dns.quad9.net.", "") // fails sometimes for some (external) reason testReverse(t, "1.1.1.1", "one.one.one.one.", "") testReverse(t, "2606:4700:4700::1111", "one.one.one.one.", "")