mirror of
https://github.com/safing/portmaster
synced 2025-09-02 10:39:22 +00:00
Merge pull request #35 from safing/feature/filter-subdomains
Add support to filter sub-domains as well
This commit is contained in:
commit
eb6bf7eae9
5 changed files with 107 additions and 15 deletions
|
@ -41,9 +41,9 @@ func DecideOnConnection(conn *network.Connection, pkt packet.Packet) { //nolint:
|
||||||
log.Infof("filter: re-evaluating verdict on %s", conn)
|
log.Infof("filter: re-evaluating verdict on %s", conn)
|
||||||
conn.Verdict = network.VerdictUndecided
|
conn.Verdict = network.VerdictUndecided
|
||||||
|
|
||||||
//if conn.Entity != nil {
|
if conn.Entity != nil {
|
||||||
//conn.Entity.ResetLists()
|
conn.Entity.ResetLists()
|
||||||
//}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// grant self
|
// grant self
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/safing/portbase/log"
|
"github.com/safing/portbase/log"
|
||||||
|
@ -12,6 +13,7 @@ import (
|
||||||
"github.com/safing/portmaster/intel/geoip"
|
"github.com/safing/portmaster/intel/geoip"
|
||||||
"github.com/safing/portmaster/network/netutils"
|
"github.com/safing/portmaster/network/netutils"
|
||||||
"github.com/safing/portmaster/status"
|
"github.com/safing/portmaster/status"
|
||||||
|
"golang.org/x/net/publicsuffix"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Entity describes a remote endpoint in many different ways.
|
// Entity describes a remote endpoint in many different ways.
|
||||||
|
@ -29,6 +31,7 @@ type Entity struct {
|
||||||
countryListLoaded bool
|
countryListLoaded bool
|
||||||
asnListLoaded bool
|
asnListLoaded bool
|
||||||
reverseResolveEnabled bool
|
reverseResolveEnabled bool
|
||||||
|
resolveSubDomainLists bool
|
||||||
|
|
||||||
Protocol uint8
|
Protocol uint8
|
||||||
Port uint16
|
Port uint16
|
||||||
|
@ -63,6 +66,34 @@ func (e *Entity) FetchData() {
|
||||||
e.getLists()
|
e.getLists()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ResetLists resets the current list data and forces
|
||||||
|
// all list sources to be re-acquired when calling GetLists().
|
||||||
|
func (e *Entity) ResetLists() {
|
||||||
|
// TODO(ppacher): our actual goal is to reset the domain
|
||||||
|
// list right now so we could be more efficient by keeping
|
||||||
|
// the other lists around.
|
||||||
|
e.Lists = nil
|
||||||
|
e.ListsMap = nil
|
||||||
|
e.domainListLoaded = false
|
||||||
|
e.ipListLoaded = false
|
||||||
|
e.countryListLoaded = false
|
||||||
|
e.asnListLoaded = false
|
||||||
|
e.resolveSubDomainLists = false
|
||||||
|
e.loadDomainListOnce = sync.Once{}
|
||||||
|
e.loadIPListOnce = sync.Once{}
|
||||||
|
e.loadCoutryListOnce = sync.Once{}
|
||||||
|
e.loadAsnListOnce = sync.Once{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResolveSubDomainLists enables or disables list lookups for
|
||||||
|
// sub-domains.
|
||||||
|
func (e *Entity) ResolveSubDomainLists(enabled bool) {
|
||||||
|
if e.domainListLoaded {
|
||||||
|
log.Warningf("intel/filterlists: tried to change sub-domain resolving for %s but lists are already fetched", e.Domain)
|
||||||
|
}
|
||||||
|
e.resolveSubDomainLists = enabled
|
||||||
|
}
|
||||||
|
|
||||||
// Domain and IP
|
// Domain and IP
|
||||||
|
|
||||||
// EnableReverseResolving enables reverse resolving the domain from the IP on demand.
|
// EnableReverseResolving enables reverse resolving the domain from the IP on demand.
|
||||||
|
@ -190,19 +221,53 @@ func (e *Entity) getDomainLists() {
|
||||||
}
|
}
|
||||||
|
|
||||||
e.loadDomainListOnce.Do(func() {
|
e.loadDomainListOnce.Do(func() {
|
||||||
log.Debugf("intel: loading domain list for %s", domain)
|
var domains = []string{domain}
|
||||||
list, err := filterlists.LookupDomain(domain)
|
if e.resolveSubDomainLists {
|
||||||
if err != nil {
|
domains = splitDomain(domain)
|
||||||
log.Errorf("intel: failed to get domain blocklists for %s: %s", domain, err)
|
log.Debugf("intel: subdomain list resolving is enabled, checking %v", domains)
|
||||||
e.loadDomainListOnce = sync.Once{}
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, d := range domains {
|
||||||
|
log.Debugf("intel: loading domain list for %s", d)
|
||||||
|
list, err := filterlists.LookupDomain(d)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("intel: failed to get domain blocklists for %s: %s", d, err)
|
||||||
|
e.loadDomainListOnce = sync.Once{}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
e.mergeList(list)
|
||||||
|
}
|
||||||
e.domainListLoaded = true
|
e.domainListLoaded = true
|
||||||
e.mergeList(list)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func splitDomain(domain string) []string {
|
||||||
|
domain = strings.Trim(domain, ".")
|
||||||
|
suffix, _ := publicsuffix.PublicSuffix(domain)
|
||||||
|
if suffix == domain {
|
||||||
|
return []string{domain}
|
||||||
|
}
|
||||||
|
|
||||||
|
domainWithoutSuffix := domain[:len(domain)-len(suffix)]
|
||||||
|
domainWithoutSuffix = strings.Trim(domainWithoutSuffix, ".")
|
||||||
|
|
||||||
|
splitted := strings.FieldsFunc(domainWithoutSuffix, func(r rune) bool {
|
||||||
|
return r == '.'
|
||||||
|
})
|
||||||
|
|
||||||
|
domains := make([]string, 0, len(splitted))
|
||||||
|
for idx := range splitted {
|
||||||
|
|
||||||
|
d := strings.Join(splitted[idx:], ".") + "." + suffix
|
||||||
|
if d[len(d)-1] != '.' {
|
||||||
|
d += "."
|
||||||
|
}
|
||||||
|
domains = append(domains, d)
|
||||||
|
}
|
||||||
|
return domains
|
||||||
|
}
|
||||||
|
|
||||||
func (e *Entity) getASNLists() {
|
func (e *Entity) getASNLists() {
|
||||||
if e.asnListLoaded {
|
if e.asnListLoaded {
|
||||||
return
|
return
|
||||||
|
|
|
@ -201,11 +201,13 @@ func triggerOnlineStatusInvestigation() {
|
||||||
|
|
||||||
func monitorOnlineStatus(ctx context.Context) error {
|
func monitorOnlineStatus(ctx context.Context) error {
|
||||||
for {
|
for {
|
||||||
timeout := time.Minute
|
timeout := 5 * time.Minute
|
||||||
if GetOnlineStatus() != StatusOnline {
|
/*
|
||||||
timeout = time.Second
|
if GetOnlineStatus() != StatusOnline {
|
||||||
log.Debugf("checking online status again in %s because current status is %s", timeout, GetOnlineStatus())
|
timeout = time.Second
|
||||||
}
|
log.Debugf("checking online status again in %s because current status is %s", timeout, GetOnlineStatus())
|
||||||
|
}
|
||||||
|
*/
|
||||||
// wait for trigger
|
// wait for trigger
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
|
|
|
@ -27,6 +27,9 @@ var (
|
||||||
CfgOptionFilterListKey = "filter/lists"
|
CfgOptionFilterListKey = "filter/lists"
|
||||||
cfgOptionFilterLists config.StringArrayOption
|
cfgOptionFilterLists config.StringArrayOption
|
||||||
|
|
||||||
|
CfgOptionFilterSubDomainsKey = "filter/includeSubdomains"
|
||||||
|
cfgOptionFilterSubDomains config.IntOption // security level option
|
||||||
|
|
||||||
CfgOptionBlockScopeLocalKey = "filter/blockLocal"
|
CfgOptionBlockScopeLocalKey = "filter/blockLocal"
|
||||||
cfgOptionBlockScopeLocal config.IntOption // security level option
|
cfgOptionBlockScopeLocal config.IntOption // security level option
|
||||||
|
|
||||||
|
@ -155,6 +158,21 @@ Examples:
|
||||||
cfgOptionFilterLists = config.Concurrent.GetAsStringArray(CfgOptionFilterListKey, []string{})
|
cfgOptionFilterLists = config.Concurrent.GetAsStringArray(CfgOptionFilterListKey, []string{})
|
||||||
cfgStringArrayOptions[CfgOptionFilterListKey] = cfgOptionFilterLists
|
cfgStringArrayOptions[CfgOptionFilterListKey] = cfgOptionFilterLists
|
||||||
|
|
||||||
|
err = config.Register(&config.Option{
|
||||||
|
Name: "Filter SubDomains",
|
||||||
|
Key: CfgOptionFilterSubDomainsKey,
|
||||||
|
Description: "Also filter sub-domains if a parent domain is blocked by a filter list",
|
||||||
|
OptType: config.OptTypeInt,
|
||||||
|
ExternalOptType: "security level",
|
||||||
|
DefaultValue: status.SecurityLevelOff,
|
||||||
|
ValidationRegex: "^(0|4|6|7)$",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cfgOptionFilterSubDomains = config.Concurrent.GetAsInt(CfgOptionFilterSubDomainsKey, int64(status.SecurityLevelOff))
|
||||||
|
cfgIntOptions[CfgOptionFilterSubDomainsKey] = cfgOptionFilterSubDomains
|
||||||
|
|
||||||
// Block Scope Local
|
// Block Scope Local
|
||||||
err = config.Register(&config.Option{
|
err = config.Register(&config.Option{
|
||||||
Name: "Block Scope Local",
|
Name: "Block Scope Local",
|
||||||
|
|
|
@ -42,6 +42,7 @@ type LayeredProfile struct {
|
||||||
EnforceSPN config.BoolOption
|
EnforceSPN config.BoolOption
|
||||||
RemoveOutOfScopeDNS config.BoolOption
|
RemoveOutOfScopeDNS config.BoolOption
|
||||||
RemoveBlockedDNS config.BoolOption
|
RemoveBlockedDNS config.BoolOption
|
||||||
|
FilterSubDomains config.BoolOption
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewLayeredProfile returns a new layered profile based on the given local profile.
|
// NewLayeredProfile returns a new layered profile based on the given local profile.
|
||||||
|
@ -93,6 +94,10 @@ func NewLayeredProfile(localProfile *Profile) *LayeredProfile {
|
||||||
CfgOptionRemoveBlockedDNSKey,
|
CfgOptionRemoveBlockedDNSKey,
|
||||||
cfgOptionRemoveBlockedDNS,
|
cfgOptionRemoveBlockedDNS,
|
||||||
)
|
)
|
||||||
|
new.FilterSubDomains = new.wrapSecurityLevelOption(
|
||||||
|
CfgOptionFilterSubDomainsKey,
|
||||||
|
cfgOptionFilterSubDomains,
|
||||||
|
)
|
||||||
|
|
||||||
// TODO: load linked profiles.
|
// TODO: load linked profiles.
|
||||||
|
|
||||||
|
@ -220,6 +225,8 @@ func (lp *LayeredProfile) MatchServiceEndpoint(entity *intel.Entity) (result end
|
||||||
// MatchFilterLists matches the entity against the set of filter
|
// MatchFilterLists matches the entity against the set of filter
|
||||||
// lists.
|
// lists.
|
||||||
func (lp *LayeredProfile) MatchFilterLists(entity *intel.Entity) (result endpoints.EPResult, reason string) {
|
func (lp *LayeredProfile) MatchFilterLists(entity *intel.Entity) (result endpoints.EPResult, reason string) {
|
||||||
|
entity.ResolveSubDomainLists(lp.FilterSubDomains())
|
||||||
|
|
||||||
lookupMap, hasLists := entity.GetListsMap()
|
lookupMap, hasLists := entity.GetListsMap()
|
||||||
if !hasLists {
|
if !hasLists {
|
||||||
return endpoints.NoMatch, ""
|
return endpoints.NoMatch, ""
|
||||||
|
|
Loading…
Add table
Reference in a new issue