Bug fixes and performence fixes:

Synchronization bug fixed
map reseting performence improvment
Added more notificationa and better wording
better scheduling of file parsing task
This commit is contained in:
Vladimir Stoilov 2022-07-29 16:48:53 +02:00 committed by Daniel
parent cb08bb2931
commit 93367b64df
4 changed files with 99 additions and 30 deletions

View file

@ -51,11 +51,11 @@ var defaultDeciders = []deciderFn{
checkResolverScope, checkResolverScope,
checkConnectivityDomain, checkConnectivityDomain,
checkBypassPrevention, checkBypassPrevention,
checkCustomFilterList,
checkFilterLists, checkFilterLists,
dropInbound, dropInbound,
checkDomainHeuristics, checkDomainHeuristics,
checkAutoPermitRelated, checkAutoPermitRelated,
checkCustomFilterList,
} }
// DecideOnConnection makes a decision about a connection. // DecideOnConnection makes a decision about a connection.
@ -618,17 +618,17 @@ matchLoop:
func checkCustomFilterList(_ context.Context, conn *network.Connection, p *profile.LayeredProfile, _ packet.Packet) bool { func checkCustomFilterList(_ context.Context, conn *network.Connection, p *profile.LayeredProfile, _ packet.Packet) bool {
// block if the domain name appears in the custom filter list (check for subdomains if enabled) // block if the domain name appears in the custom filter list (check for subdomains if enabled)
if conn.Entity.Domain != "" { if conn.Entity.Domain != "" {
if customlists.LookupDomain(conn.Entity.Domain, p.FilterSubDomains()) { if ok, match := customlists.LookupDomain(conn.Entity.Domain, p.FilterSubDomains()); ok {
conn.Block("Domains appears in the custom user list", customlists.CfgOptionCustomListBlockingKey) conn.Deny(fmt.Sprintf("domain %s matched %s in custom filter list", conn.Entity.Domain, match), customlists.CfgOptionCustomListBlockingKey)
return true return true
} }
} }
// block if any of the CNAME appears in the custom filter list (check for subdomains if enabled) // block if any of the CNAME appears in the custom filter list (check for subdomains if enabled)
if len(conn.Entity.CNAME) > 0 && p.FilterCNAMEs() { if p.FilterCNAMEs() {
for _, cname := range conn.Entity.CNAME { for _, cname := range conn.Entity.CNAME {
if customlists.LookupDomain(cname, p.FilterSubDomains()) { if ok, match := customlists.LookupDomain(cname, p.FilterSubDomains()); ok {
conn.Block("CNAME appears in the custom user list", customlists.CfgOptionCustomListBlockingKey) conn.Deny(fmt.Sprintf("domain alias (CNAME) %s matched %s in custom filter list", cname, match), customlists.CfgOptionCustomListBlockingKey)
return true return true
} }
} }
@ -636,8 +636,8 @@ func checkCustomFilterList(_ context.Context, conn *network.Connection, p *profi
// block if ip addresses appears in the custom filter list // block if ip addresses appears in the custom filter list
if conn.Entity.IP != nil { if conn.Entity.IP != nil {
if customlists.LookupIP(&conn.Entity.IP) { if customlists.LookupIP(conn.Entity.IP) {
conn.Block("IP appears in the custom filter list", customlists.CfgOptionCustomListBlockingKey) conn.Deny(fmt.Sprintf("IP address %s appears in the custom filter list", conn.Entity.IP), customlists.CfgOptionCustomListBlockingKey)
return true return true
} }
} }
@ -645,7 +645,7 @@ func checkCustomFilterList(_ context.Context, conn *network.Connection, p *profi
// block autonomous system by its number if it appears in the custom filter list // block autonomous system by its number if it appears in the custom filter list
if conn.Entity.ASN != 0 { if conn.Entity.ASN != 0 {
if customlists.LookupASN(conn.Entity.ASN) { if customlists.LookupASN(conn.Entity.ASN) {
conn.Block("ASN appears in the custom filter list", customlists.CfgOptionCustomListBlockingKey) conn.Deny(fmt.Sprintf("autonomous system with number %d appears in the custom filter list", conn.Entity.ASN), customlists.CfgOptionCustomListBlockingKey)
return true return true
} }
} }
@ -653,7 +653,7 @@ func checkCustomFilterList(_ context.Context, conn *network.Connection, p *profi
// block if the country appears in the custom filter list // block if the country appears in the custom filter list
if conn.Entity.Country != "" { if conn.Entity.Country != "" {
if customlists.LookupCountry(conn.Entity.Country) { if customlists.LookupCountry(conn.Entity.Country) {
conn.Block("Country appears in the custom filter list", customlists.CfgOptionCustomListBlockingKey) conn.Deny(fmt.Sprintf("country code %s appears in the custom filter list", conn.Entity.Country), customlists.CfgOptionCustomListBlockingKey)
return true return true
} }
} }

View file

@ -16,7 +16,7 @@ var (
) )
func registerConfig() error { func registerConfig() error {
help := `Put all domains, Ip addresses, country codes and autonomous system that you want to block in a file in where each entry is on a new line. help := `File that contains list of all domains, Ip addresses, country codes and autonomous system that you want to block, where each entry is on a new line.
Lines that start with a '#' symbol are ignored. Lines that start with a '#' symbol are ignored.
Everything after the first space/tab is ignored. Everything after the first space/tab is ignored.
Example: Example:
@ -53,6 +53,7 @@ AS123
Annotations: config.Annotations{ Annotations: config.Annotations{
config.DisplayOrderAnnotation: cfgOptionCustomListBlockingOrder, config.DisplayOrderAnnotation: cfgOptionCustomListBlockingOrder,
config.CategoryAnnotation: cfgOptionCustomListCategoryAnnotation, config.CategoryAnnotation: cfgOptionCustomListCategoryAnnotation,
config.DisplayHintAnnotation: config.DisplayHintFilePicker,
}, },
}) })
if err != nil { if err != nil {

View file

@ -2,6 +2,7 @@ package customlists
import ( import (
"bufio" "bufio"
"fmt"
"net" "net"
"os" "os"
"strconv" "strconv"
@ -20,14 +21,33 @@ var (
domainsFilterList map[string]struct{} domainsFilterList map[string]struct{}
) )
const numberOfZeroIPsUntilWarning = 100 const (
numberOfZeroIPsUntilWarning = 100
customFilterListStatusNotificationID = "intel/customlists_status"
customFilterListZeroIPNotificationID = "intel/customlists_zeroip"
)
func parseFile(filePath string) error { func initFilterLists() {
// reset all maps, previous (if any) settings will be lost
countryCodesFilterList = make(map[string]struct{}) countryCodesFilterList = make(map[string]struct{})
ipAddressesFilterList = make(map[string]struct{}) ipAddressesFilterList = make(map[string]struct{})
autonomousSystemsFilterList = make(map[uint]struct{}) autonomousSystemsFilterList = make(map[uint]struct{})
domainsFilterList = make(map[string]struct{}) domainsFilterList = make(map[string]struct{})
}
func parseFile(filePath string) error {
// reset all maps, previous (if any) settings will be lost
for key := range countryCodesFilterList {
delete(countryCodesFilterList, key)
}
for key := range ipAddressesFilterList {
delete(ipAddressesFilterList, key)
}
for key := range autonomousSystemsFilterList {
delete(autonomousSystemsFilterList, key)
}
for key := range domainsFilterList {
delete(domainsFilterList, key)
}
// ignore empty file path // ignore empty file path
if filePath == "" { if filePath == "" {
@ -37,7 +57,21 @@ func parseFile(filePath string) error {
// open the file if possible // open the file if possible
file, err := os.Open(filePath) file, err := os.Open(filePath)
if err != nil { if err != nil {
log.Warningf("intel/customlists: failed to parse file: \"%s\"", filePath) log.Warningf("intel/customlists: failed to parse file %q ", err)
// notifications.NotifyWarn("intel/customlists parse failed", "Failed to open custom filter list")
notifications.Notify(&notifications.Notification{
EventID: customFilterListStatusNotificationID,
Type: notifications.Warning,
Title: "Failed to open custom filter list",
Message: err.Error(),
ShowOnSystem: false,
AvailableActions: []*notifications.Action{
{
ID: "ack",
Text: "OK",
},
},
})
return err return err
} }
defer file.Close() defer file.Close()
@ -58,11 +92,24 @@ func parseFile(filePath string) error {
if numberOfZeroIPs >= numberOfZeroIPsUntilWarning { if numberOfZeroIPs >= numberOfZeroIPsUntilWarning {
log.Warning("intel/customlists: Too many zero IP addresses.") log.Warning("intel/customlists: Too many zero IP addresses.")
notifications.NotifyWarn("too_many_zero_ips", "Too many zero IP addresses. Check your custom filter list.", "Hosts file format is not spported.") notifications.NotifyWarn(customFilterListZeroIPNotificationID, "Too many zero IP addresses. Check your custom filter list.", "Hosts file format is not spported.")
} }
log.Infof("intel/customlists: list loaded successful: %s", filePath) log.Infof("intel/customlists: list loaded successful: %s", filePath)
notifications.NotifyInfo(customFilterListStatusNotificationID,
"Custom filter list loaded successfully.",
fmt.Sprintf(`Custom filter list loaded successfully from file %s
%d domains
%d IPs
%d autonomous systems
%d countries`,
filePath,
len(domainsFilterList),
len(ipAddressesFilterList),
len(autonomousSystemsFilterList),
len(domainsFilterList)))
return nil return nil
} }

View file

@ -30,7 +30,8 @@ var (
filterListFilePath string filterListFilePath string
filterListFileModifiedTime time.Time filterListFileModifiedTime time.Time
parseLock sync.RWMutex filterListLock sync.RWMutex
parserTask *modules.Task
) )
func init() { func init() {
@ -38,6 +39,8 @@ func init() {
} }
func prep() error { func prep() error {
initFilterLists()
// register the config in the ui // register the config in the ui
err := registerConfig() err := registerConfig()
if err != nil { if err != nil {
@ -61,23 +64,29 @@ func start() error {
return err return err
} }
// register timer to run every periodically and check for file updates // create parser task and enqueue for execution. "checkAndUpdateFilterList" will schedule the next execution
module.NewTask("intel/customlists file update check", func(context.Context, *modules.Task) error { parserTask = module.NewTask("intel/customlists file update check", func(context.Context, *modules.Task) error {
_ = checkAndUpdateFilterList() _ = checkAndUpdateFilterList()
return nil return nil
}).Repeat(10 * time.Minute) }).Schedule(time.Now().Add(20 * time.Second))
// parse the file at startup
_ = parseFile(getFilePath())
return nil return nil
} }
func checkAndUpdateFilterList() error { func checkAndUpdateFilterList() error {
parseLock.Lock() filterListLock.Lock()
defer parseLock.Unlock() defer filterListLock.Unlock()
// get path and try to get its info // get path and ignore if empty
filePath := getFilePath() filePath := getFilePath()
if filePath == "" {
return nil
}
// schedule next update check
parserTask.Schedule(time.Now().Add(1 * time.Minute))
// try to get file info
modifiedTime := time.Now() modifiedTime := time.Now()
if fileInfo, err := os.Stat(filePath); err == nil { if fileInfo, err := os.Stat(filePath); err == nil {
modifiedTime = fileInfo.ModTime() modifiedTime = fileInfo.ModTime()
@ -96,38 +105,50 @@ func checkAndUpdateFilterList() error {
} }
// LookupIP checks if the IP address is in a custom filter list. // LookupIP checks if the IP address is in a custom filter list.
func LookupIP(ip *net.IP) bool { func LookupIP(ip net.IP) bool {
filterListLock.RLock()
defer filterListLock.RUnlock()
_, ok := ipAddressesFilterList[ip.String()] _, ok := ipAddressesFilterList[ip.String()]
return ok return ok
} }
// LookupDomain checks if the Domain is in a custom filter list. // LookupDomain checks if the Domain is in a custom filter list.
func LookupDomain(fullDomain string, filterSubdomains bool) bool { func LookupDomain(fullDomain string, filterSubdomains bool) (bool, string) {
filterListLock.RLock()
defer filterListLock.RUnlock()
if filterSubdomains { if filterSubdomains {
// check if domain is in the list and all its subdomains // check if domain is in the list and all its subdomains
listOfDomains := splitDomain(fullDomain) listOfDomains := splitDomain(fullDomain)
for _, domain := range listOfDomains { for _, domain := range listOfDomains {
_, ok := domainsFilterList[domain] _, ok := domainsFilterList[domain]
if ok { if ok {
return true return true, domain
} }
} }
} else { } else {
// check only if the domain is in the list // check only if the domain is in the list
_, ok := domainsFilterList[fullDomain] _, ok := domainsFilterList[fullDomain]
return ok return ok, fullDomain
} }
return false return false, ""
} }
// LookupASN checks if the Autonomous system number is in a custom filter list. // LookupASN checks if the Autonomous system number is in a custom filter list.
func LookupASN(number uint) bool { func LookupASN(number uint) bool {
filterListLock.RLock()
defer filterListLock.RUnlock()
_, ok := autonomousSystemsFilterList[number] _, ok := autonomousSystemsFilterList[number]
return ok return ok
} }
// LookupCountry checks if the country code is in a custom filter list. // LookupCountry checks if the country code is in a custom filter list.
func LookupCountry(countryCode string) bool { func LookupCountry(countryCode string) bool {
filterListLock.RLock()
defer filterListLock.RUnlock()
_, ok := countryCodesFilterList[countryCode] _, ok := countryCodesFilterList[countryCode]
return ok return ok
} }