mirror of
https://github.com/safing/portmaster
synced 2025-04-17 17:39:10 +00:00
Move LMS scoring from nameserver to firewall
This commit is contained in:
parent
b3657e17ce
commit
85e4beafa1
8 changed files with 53 additions and 42 deletions
|
@ -35,8 +35,8 @@ Think of a pi-hole for your computer. Or an ad-blocker that blocks ads on your w
|
|||
**Features/Settings:**
|
||||
|
||||
- Select and activate block-lists
|
||||
- Manually black/whitelist domains
|
||||
- You can whitelist domains in case something breaks
|
||||
- Manually block/allow domains
|
||||
- You can allow domains in case something breaks
|
||||
- CNAME Blocking (block these new nasty "unblockable" ads/trackers)
|
||||
- Block all subdomains of a domain in the block-lists
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/safing/portmaster/detection/dga"
|
||||
"github.com/safing/portmaster/netenv"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
|
@ -58,6 +59,7 @@ func DecideOnConnection(ctx context.Context, conn *network.Connection, pkt packe
|
|||
checkBypassPrevention,
|
||||
checkFilterLists,
|
||||
checkInbound,
|
||||
checkLMSScore,
|
||||
checkDefaultPermit,
|
||||
checkAutoPermitRelated,
|
||||
checkDefaultAction,
|
||||
|
@ -70,7 +72,7 @@ func DecideOnConnection(ctx context.Context, conn *network.Connection, pkt packe
|
|||
}
|
||||
|
||||
// DefaultAction == DefaultActionBlock
|
||||
conn.Deny("endpoint is not whitelisted (default=block)")
|
||||
conn.Deny("endpoint is not allowed (default=block)")
|
||||
}
|
||||
|
||||
// checkPortmasterConnection allows all connection that originate from
|
||||
|
@ -281,10 +283,26 @@ func checkFilterLists(ctx context.Context, conn *network.Connection, pkt packet.
|
|||
return false
|
||||
}
|
||||
|
||||
func checkLMSScore(ctx context.Context, conn *network.Connection, _ packet.Packet) bool {
|
||||
if conn.Entity.Domain == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
// check for possible DNS tunneling / data transmission
|
||||
lms := dga.LmsScoreOfDomain(conn.Entity.Domain)
|
||||
if lms < 10 {
|
||||
log.Tracer(ctx).Warningf("nameserver: possible data tunnel by %s: %s has lms score of %f, returning nxdomain", conn.Process(), conn.Entity.Domain, lms)
|
||||
conn.BlockWithContext("Possible data tunnel", conn.ReasonContext)
|
||||
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func checkInbound(_ context.Context, conn *network.Connection, _ packet.Packet) bool {
|
||||
// implicit default=block for inbound
|
||||
if conn.Inbound {
|
||||
conn.Drop("endpoint is not whitelisted (incoming is always default=block)")
|
||||
conn.Drop("endpoint is not allowed (incoming is always default=block)")
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
@ -294,7 +312,7 @@ func checkDefaultPermit(_ context.Context, conn *network.Connection, _ packet.Pa
|
|||
// check default action
|
||||
p := conn.Process().Profile()
|
||||
if p.DefaultAction() == profile.DefaultActionPermit {
|
||||
conn.Accept("endpoint is not blacklisted (default=permit)")
|
||||
conn.Accept("endpoint is not blocked (default=permit)")
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
|
|
@ -47,14 +47,14 @@ func lookupBlockLists(entity, value string) ([]string, error) {
|
|||
}
|
||||
|
||||
// LookupCountry returns a list of sources that mark the country
|
||||
// as blacklisted. If country is not stored in the cache database
|
||||
// as blocked. If country is not stored in the cache database
|
||||
// a nil slice is returned.
|
||||
func LookupCountry(country string) ([]string, error) {
|
||||
return lookupBlockLists("country", country)
|
||||
}
|
||||
|
||||
// LookupDomain returns a list of sources that mark the domain
|
||||
// as blacklisted. If domain is not stored in the cache database
|
||||
// as blocked. If domain is not stored in the cache database
|
||||
// a nil slice is returned.
|
||||
func LookupDomain(domain string) ([]string, error) {
|
||||
// make sure we only fully qualified domains
|
||||
|
@ -67,13 +67,13 @@ func LookupDomain(domain string) ([]string, error) {
|
|||
}
|
||||
|
||||
// LookupASNString returns a list of sources that mark the ASN
|
||||
// as blacklisted. If ASN is not stored in the cache database
|
||||
// as blocked. If ASN is not stored in the cache database
|
||||
// a nil slice is returned.
|
||||
func LookupASNString(asn string) ([]string, error) {
|
||||
return lookupBlockLists("asn", asn)
|
||||
}
|
||||
|
||||
// LookupIP returns a list of blacklist sources that contain
|
||||
// LookupIP returns a list of block sources that contain
|
||||
// a reference to ip. LookupIP automatically checks the IPv4 or
|
||||
// IPv6 lists respectively.
|
||||
func LookupIP(ip net.IP) ([]string, error) {
|
||||
|
@ -95,7 +95,7 @@ func LookupIPString(ipStr string) ([]string, error) {
|
|||
return LookupIP(ip)
|
||||
}
|
||||
|
||||
// LookupIPv4String returns a list of blacklist sources that
|
||||
// LookupIPv4String returns a list of block sources that
|
||||
// contain a reference to ip. If the IP is not stored in the
|
||||
// cache database a nil slice is returned.
|
||||
func LookupIPv4String(ipv4 string) ([]string, error) {
|
||||
|
@ -113,7 +113,7 @@ func LookupIPv4(ipv4 net.IP) ([]string, error) {
|
|||
return LookupIPv4String(ip.String())
|
||||
}
|
||||
|
||||
// LookupIPv6String returns a list of blacklist sources that
|
||||
// LookupIPv6String returns a list of block sources that
|
||||
// contain a reference to ip. If the IP is not stored in the
|
||||
// cache database a nil slice is returned.
|
||||
func LookupIPv6String(ipv6 string) ([]string, error) {
|
||||
|
|
|
@ -12,7 +12,6 @@ import (
|
|||
|
||||
"github.com/safing/portbase/log"
|
||||
"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"
|
||||
|
@ -211,17 +210,6 @@ func handleRequest(ctx context.Context, w dns.ResponseWriter, query *dns.Msg) er
|
|||
// save security level to query
|
||||
q.SecurityLevel = conn.Process().Profile().SecurityLevel()
|
||||
|
||||
// check for possible DNS tunneling / data transmission
|
||||
// TODO: improve this
|
||||
lms := dga.LmsScoreOfDomain(q.FQDN)
|
||||
// log.Tracef("nameserver: domain %s has lms score of %f", fqdn, lms)
|
||||
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")
|
||||
sendResponse(w, query, conn.Verdict, conn.Reason, conn.ReasonContext)
|
||||
return nil
|
||||
}
|
||||
|
||||
// check profile before we even get intel and rr
|
||||
firewall.DecideOnConnection(ctx, conn, nil)
|
||||
|
||||
|
|
|
@ -87,10 +87,10 @@ func NewConnectionFromDNSRequest(ctx context.Context, fqdn string, cnames []stri
|
|||
timestamp := time.Now().Unix()
|
||||
dnsConn := &Connection{
|
||||
Scope: fqdn,
|
||||
Entity: (&intel.Entity{
|
||||
Entity: &intel.Entity{
|
||||
Domain: fqdn,
|
||||
CNAME: cnames,
|
||||
}),
|
||||
},
|
||||
process: proc,
|
||||
Started: timestamp,
|
||||
Ended: timestamp,
|
||||
|
@ -123,20 +123,20 @@ func NewConnectionFromFirstPacket(pkt packet.Packet) *Connection {
|
|||
default: // netutils.Invalid
|
||||
scope = IncomingInvalid
|
||||
}
|
||||
entity = (&intel.Entity{
|
||||
entity = &intel.Entity{
|
||||
IP: pkt.Info().Src,
|
||||
Protocol: uint8(pkt.Info().Protocol),
|
||||
Port: pkt.Info().SrcPort,
|
||||
})
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// outbound connection
|
||||
entity = (&intel.Entity{
|
||||
entity = &intel.Entity{
|
||||
IP: pkt.Info().Dst,
|
||||
Protocol: uint8(pkt.Info().Protocol),
|
||||
Port: pkt.Info().DstPort,
|
||||
})
|
||||
}
|
||||
|
||||
// check if we can find a domain for that IP
|
||||
ipinfo, err := resolver.GetIPInfo(pkt.Info().Dst.String())
|
||||
|
|
|
@ -23,15 +23,22 @@ var (
|
|||
unidentifiedProcessScopePrefix = strconv.Itoa(process.UnidentifiedProcessID) + "/"
|
||||
)
|
||||
|
||||
func getDNSRequestCacheKey(pid int, fqdn string) string {
|
||||
return strconv.Itoa(pid) + "/" + fqdn
|
||||
}
|
||||
|
||||
func removeOpenDNSRequest(pid int, fqdn string) {
|
||||
openDNSRequestsLock.Lock()
|
||||
defer openDNSRequestsLock.Unlock()
|
||||
|
||||
key := strconv.Itoa(pid) + "/" + fqdn
|
||||
key := getDNSRequestCacheKey(pid, fqdn)
|
||||
_, ok := openDNSRequests[key]
|
||||
if ok {
|
||||
delete(openDNSRequests, key)
|
||||
} else if pid != process.UnidentifiedProcessID {
|
||||
return
|
||||
}
|
||||
|
||||
if pid != process.UnidentifiedProcessID {
|
||||
// check if there is an open dns request from an unidentified process
|
||||
delete(openDNSRequests, unidentifiedProcessScopePrefix+fqdn)
|
||||
}
|
||||
|
@ -42,26 +49,24 @@ func SaveOpenDNSRequest(conn *Connection) {
|
|||
openDNSRequestsLock.Lock()
|
||||
defer openDNSRequestsLock.Unlock()
|
||||
|
||||
key := strconv.Itoa(conn.process.Pid) + "/" + conn.Scope
|
||||
|
||||
existingConn, ok := openDNSRequests[key]
|
||||
if ok {
|
||||
key := getDNSRequestCacheKey(conn.process.Pid, conn.Scope)
|
||||
if existingConn, ok := openDNSRequests[key]; ok {
|
||||
existingConn.Lock()
|
||||
defer existingConn.Unlock()
|
||||
|
||||
existingConn.Ended = conn.Started
|
||||
} else {
|
||||
openDNSRequests[key] = conn
|
||||
return
|
||||
}
|
||||
|
||||
openDNSRequests[key] = conn
|
||||
}
|
||||
|
||||
func openDNSRequestWriter(ctx context.Context) error {
|
||||
ticker := time.NewTicker(writeOpenDNSRequestsTickDuration)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
ticker.Stop()
|
||||
return nil
|
||||
case <-ticker.C:
|
||||
writeOpenDNSRequestsToDB()
|
||||
|
|
|
@ -85,9 +85,9 @@ var (
|
|||
|
||||
func registerConfiguration() error {
|
||||
// Default Filter Action
|
||||
// permit - blacklist mode: everything is permitted unless blocked
|
||||
// permit - blocklist mode: everything is permitted unless blocked
|
||||
// ask - ask mode: if not verdict is found, the user is consulted
|
||||
// block - whitelist mode: everything is blocked unless permitted
|
||||
// block - allowlist mode: everything is blocked unless permitted
|
||||
err := config.Register(&config.Option{
|
||||
Name: "Default Filter Action",
|
||||
Key: CfgOptionDefaultActionKey,
|
||||
|
|
|
@ -23,7 +23,7 @@ type reason struct {
|
|||
func (r *reason) String() string {
|
||||
prefix := "endpoint in blocklist: "
|
||||
if r.Permitted {
|
||||
prefix = "endpoint in whitelist: "
|
||||
prefix = "endpoint in allowlist: "
|
||||
}
|
||||
|
||||
return prefix + r.description + " " + r.Value
|
||||
|
|
Loading…
Add table
Reference in a new issue