diff --git a/firewall/master.go b/firewall/master.go index 9650b625..3dd9f8e8 100644 --- a/firewall/master.go +++ b/firewall/master.go @@ -141,9 +141,19 @@ func DecideOnConnection(conn *network.Connection, pkt packet.Packet) { //nolint: } } + // check for bypass protection + result, reason := p.MatchBypassProtection(conn.Entity) + switch result { + case endpoints.Denied: + conn.Block("bypass prevention: " + reason) + return + case endpoints.Permitted: + conn.Accept("bypass prevention: " + reason) + return + case endpoints.NoMatch: + } + // check endpoints list - var result endpoints.EPResult - var reason string if conn.Inbound { result, reason = p.MatchServiceEndpoint(conn.Entity) } else { diff --git a/intel/entity.go b/intel/entity.go index f4412bc3..e667f07d 100644 --- a/intel/entity.go +++ b/intel/entity.go @@ -151,7 +151,6 @@ func (e *Entity) getLocation() { e.fetchLocationOnce.Do(func() { // need IP! if e.IP == nil { - log.Warningf("intel: cannot get location for %s data without IP", e.Domain) return } diff --git a/profile/config.go b/profile/config.go index 9cc341f9..bf0313a0 100644 --- a/profile/config.go +++ b/profile/config.go @@ -53,6 +53,9 @@ var ( CfgOptionRemoveBlockedDNSKey = "filter/removeBlockedDNS" cfgOptionRemoveBlockedDNS config.IntOption // security level option + + CfgOptionBypassProtectionKey = "filter/preventBypassing" + cfgOptionBypassProtection config.IntOption // security level option ) func registerConfiguration() error { @@ -325,5 +328,22 @@ Examples: cfgOptionRemoveBlockedDNS = config.Concurrent.GetAsInt(CfgOptionRemoveBlockedDNSKey, int64(status.SecurityLevelsAll)) cfgIntOptions[CfgOptionRemoveBlockedDNSKey] = cfgOptionRemoveBlockedDNS + err = config.Register(&config.Option{ + Name: "Prevent Bypassing", + Key: CfgOptionBypassProtectionKey, + Description: "Prevent apps from bypassing the privacy filter:\n- Firefox: Disable DNS-over-HTTPs", + OptType: config.OptTypeInt, + ExpertiseLevel: config.ExpertiseLevelUser, + ReleaseLevel: config.ReleaseLevelBeta, + ExternalOptType: "security level", + DefaultValue: status.SecurityLevelsAll, + ValidationRegex: "^(7|6|4|0)", + }) + if err != nil { + return err + } + cfgOptionBypassProtection = config.Concurrent.GetAsInt((CfgOptionBypassProtectionKey), int64(status.SecurityLevelsAll)) + cfgIntOptions[CfgOptionBypassProtectionKey] = cfgOptionBypassProtection + return nil } diff --git a/profile/profile-layered.go b/profile/profile-layered.go index 90f0478b..0fca0bad 100644 --- a/profile/profile-layered.go +++ b/profile/profile-layered.go @@ -1,6 +1,7 @@ package profile import ( + "strings" "sync" "sync/atomic" @@ -43,6 +44,7 @@ type LayeredProfile struct { RemoveOutOfScopeDNS config.BoolOption RemoveBlockedDNS config.BoolOption FilterSubDomains config.BoolOption + PreventBypassing config.BoolOption } // NewLayeredProfile returns a new layered profile based on the given local profile. @@ -98,6 +100,10 @@ func NewLayeredProfile(localProfile *Profile) *LayeredProfile { CfgOptionFilterSubDomainsKey, cfgOptionFilterSubDomains, ) + new.PreventBypassing = new.wrapSecurityLevelOption( + CfgOptionBypassProtectionKey, + cfgOptionBypassProtection, + ) // TODO: load linked profiles. @@ -224,7 +230,7 @@ func (lp *LayeredProfile) MatchServiceEndpoint(entity *intel.Entity) (result end // MatchFilterLists matches the entity against the set of filter // lists. -func (lp *LayeredProfile) MatchFilterLists(entity *intel.Entity) (result endpoints.EPResult, reason string) { +func (lp *LayeredProfile) MatchFilterLists(entity *intel.Entity) (endpoints.EPResult, string) { entity.ResolveSubDomainLists(lp.FilterSubDomains()) lookupMap, hasLists := entity.GetListsMap() @@ -253,6 +259,22 @@ func (lp *LayeredProfile) MatchFilterLists(entity *intel.Entity) (result endpoin return endpoints.NoMatch, "" } +// MatchBypassProtection checks if the entity should be denied or permitted +// based on some bypass protection checks. +func (lp *LayeredProfile) MatchBypassProtection(entity *intel.Entity) (endpoints.EPResult, string) { + if !lp.PreventBypassing() { + return endpoints.NoMatch, "" + } + + // Block firefox canary domain to disable DoH + if strings.ToLower(entity.Domain) == "use-application-dns.net." { + log.Warningf("bypass protection for firefox canary") + return endpoints.Denied, "Firefox canary domain" + } + + return endpoints.NoMatch, "" +} + // AddEndpoint adds an endpoint to the local endpoint list, saves the local profile and reloads the configuration. func (lp *LayeredProfile) AddEndpoint(newEntry string) { lp.localProfile.AddEndpoint(newEntry)