mirror of
https://github.com/safing/portmaster
synced 2025-09-01 18:19:12 +00:00
Merge pull request #33 from safing/fix/nameserver-unknown-process
Add VerdictFailed and check for unknown process in nameserver
This commit is contained in:
commit
3d4c7311ff
5 changed files with 79 additions and 15 deletions
|
@ -31,6 +31,7 @@ var (
|
|||
packetsAccepted *uint64
|
||||
packetsBlocked *uint64
|
||||
packetsDropped *uint64
|
||||
packetsFailed *uint64
|
||||
|
||||
// localNet4 *net.IPNet
|
||||
|
||||
|
@ -92,12 +93,10 @@ func prep() (err error) {
|
|||
// return fmt.Errorf("filter: failed to parse cidr fd17::/64: %s", err)
|
||||
// }
|
||||
|
||||
var pA uint64
|
||||
packetsAccepted = &pA
|
||||
var pB uint64
|
||||
packetsBlocked = &pB
|
||||
var pD uint64
|
||||
packetsDropped = &pD
|
||||
packetsAccepted = new(uint64)
|
||||
packetsBlocked = new(uint64)
|
||||
packetsDropped = new(uint64)
|
||||
packetsFailed = new(uint64)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -321,6 +320,9 @@ func issueVerdict(conn *network.Connection, pkt packet.Packet, verdict network.V
|
|||
err = pkt.RerouteToNameserver()
|
||||
case network.VerdictRerouteToTunnel:
|
||||
err = pkt.RerouteToTunnel()
|
||||
case network.VerdictFailed:
|
||||
atomic.AddUint64(packetsFailed, 1)
|
||||
fallthrough
|
||||
default:
|
||||
atomic.AddUint64(packetsDropped, 1)
|
||||
err = pkt.Drop()
|
||||
|
@ -361,10 +363,17 @@ func statLogger() {
|
|||
case <-module.Stopping():
|
||||
return
|
||||
case <-time.After(10 * time.Second):
|
||||
log.Tracef("filter: packets accepted %d, blocked %d, dropped %d", atomic.LoadUint64(packetsAccepted), atomic.LoadUint64(packetsBlocked), atomic.LoadUint64(packetsDropped))
|
||||
log.Tracef(
|
||||
"filter: packets accepted %d, blocked %d, dropped %d, failed %d",
|
||||
atomic.LoadUint64(packetsAccepted),
|
||||
atomic.LoadUint64(packetsBlocked),
|
||||
atomic.LoadUint64(packetsDropped),
|
||||
atomic.LoadUint64(packetsFailed),
|
||||
)
|
||||
atomic.StoreUint64(packetsAccepted, 0)
|
||||
atomic.StoreUint64(packetsBlocked, 0)
|
||||
atomic.StoreUint64(packetsDropped, 0)
|
||||
atomic.StoreUint64(packetsFailed, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,11 +34,16 @@ import (
|
|||
// is called with the first packet of a network connection.
|
||||
|
||||
// DecideOnConnection makes a decision about a connection.
|
||||
// When called, the connection and profile is already locked.
|
||||
func DecideOnConnection(conn *network.Connection, pkt packet.Packet) { //nolint:gocognit,gocyclo // TODO
|
||||
// update profiles and check if communication needs reevaluation
|
||||
if conn.UpdateAndCheck() {
|
||||
log.Infof("filter: re-evaluating verdict on %s", conn)
|
||||
conn.Verdict = network.VerdictUndecided
|
||||
|
||||
if conn.Entity != nil {
|
||||
//conn.Entity.ResetLists()
|
||||
}
|
||||
}
|
||||
|
||||
// grant self
|
||||
|
@ -158,7 +163,7 @@ func DecideOnConnection(conn *network.Connection, pkt packet.Packet) { //nolint:
|
|||
result, reason = p.MatchFilterLists(conn.Entity)
|
||||
switch result {
|
||||
case endpoints.Denied:
|
||||
conn.Deny("endpoint in filterlist: " + reason)
|
||||
conn.Deny("endpoint in filterlists: " + reason)
|
||||
return
|
||||
case endpoints.NoMatch:
|
||||
// nothing to do
|
||||
|
|
|
@ -125,6 +125,7 @@ func handleRequest(ctx context.Context, w dns.ResponseWriter, query *dns.Msg) er
|
|||
// check class
|
||||
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)
|
||||
return nil
|
||||
}
|
||||
|
@ -134,7 +135,9 @@ func handleRequest(ctx context.Context, w dns.ResponseWriter, query *dns.Msg) er
|
|||
m := new(dns.Msg)
|
||||
m.SetReply(query)
|
||||
m.Answer = localhostRRs
|
||||
_ = w.WriteMsg(m)
|
||||
if err := w.WriteMsg(m); err != nil {
|
||||
log.Warningf("nameserver: failed to handle request to %s: %s", q.FQDN, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -176,6 +179,35 @@ func handleRequest(ctx context.Context, w dns.ResponseWriter, query *dns.Msg) er
|
|||
// get connection
|
||||
conn := network.NewConnectionFromDNSRequest(ctx, q.FQDN, remoteAddr.IP, uint16(remoteAddr.Port))
|
||||
|
||||
// once we decided on the connection we might need to save it to the database
|
||||
// so we defer that check right now.
|
||||
defer func() {
|
||||
switch conn.Verdict {
|
||||
// we immediately save blocked, dropped or failed verdicts so
|
||||
// the pop up in the UI.
|
||||
case network.VerdictBlock, network.VerdictDrop, network.VerdictFailed:
|
||||
conn.Save()
|
||||
|
||||
// for undecided or accepted connections we don't save them yet because
|
||||
// that will happen later anyway.
|
||||
case network.VerdictUndecided, network.VerdictAccept:
|
||||
return
|
||||
|
||||
// FIXME(ppacher): how to handle undeterminable and the SPN re-routing here?
|
||||
default:
|
||||
log.Warningf("nameserver: unexpected verdict %s for connection %s, not saving", conn.Verdict, conn)
|
||||
}
|
||||
}()
|
||||
|
||||
if conn.Process().Profile() == nil {
|
||||
tracer.Infof("nameserver: failed to find process for request %s, returning NXDOMAIN", conn)
|
||||
returnNXDomain(w, query)
|
||||
// FIXME(ppacher): if we save the connection (by marking it as failed)
|
||||
// we might collect A LOT of connections for the UI.
|
||||
//conn.Failed("Unknown process")
|
||||
return nil
|
||||
}
|
||||
|
||||
// save security level to query
|
||||
q.SecurityLevel = conn.Process().Profile().SecurityLevel()
|
||||
|
||||
|
@ -186,20 +218,20 @@ 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)
|
||||
returnNXDomain(w, query)
|
||||
conn.Block("Possible data tunnel")
|
||||
return nil
|
||||
}
|
||||
|
||||
// check profile before we even get intel and rr
|
||||
firewall.DecideOnConnection(conn, nil)
|
||||
|
||||
switch conn.Verdict {
|
||||
case network.VerdictBlock:
|
||||
tracer.Infof("nameserver: %s blocked, returning nxdomain", conn)
|
||||
returnNXDomain(w, query)
|
||||
conn.Save() // save blocked request
|
||||
return nil
|
||||
case network.VerdictDrop:
|
||||
case network.VerdictDrop, network.VerdictFailed:
|
||||
tracer.Infof("nameserver: %s dropped, not replying", conn)
|
||||
conn.Save() // save dropped request
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -209,6 +241,7 @@ func handleRequest(ctx context.Context, w dns.ResponseWriter, query *dns.Msg) er
|
|||
// TODO: analyze nxdomain requests, malware could be trying DGA-domains
|
||||
tracer.Warningf("nameserver: %s requested %s%s: %s", conn.Process(), q.FQDN, q.QType, err)
|
||||
returnNXDomain(w, query)
|
||||
conn.Failed("failed to resolve: " + err.Error())
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -218,7 +251,7 @@ func handleRequest(ctx context.Context, w dns.ResponseWriter, query *dns.Msg) er
|
|||
if rrCache == nil {
|
||||
tracer.Infof("nameserver: %s implicitly denied by filtering the dns response, returning nxdomain", conn)
|
||||
returnNXDomain(w, query)
|
||||
conn.Save() // save blocked request
|
||||
conn.Block("DNS response filtered")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -262,8 +295,12 @@ func handleRequest(ctx context.Context, w dns.ResponseWriter, query *dns.Msg) er
|
|||
m.Answer = rrCache.Answer
|
||||
m.Ns = rrCache.Ns
|
||||
m.Extra = rrCache.Extra
|
||||
_ = w.WriteMsg(m)
|
||||
tracer.Debugf("nameserver: returning response %s%s to %s", q.FQDN, q.QType, conn.Process())
|
||||
|
||||
if err := w.WriteMsg(m); err != nil {
|
||||
log.Warningf("nameserver: failed to return reponse %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)
|
||||
|
|
|
@ -198,6 +198,16 @@ func (conn *Connection) Deny(reason string) {
|
|||
}
|
||||
}
|
||||
|
||||
// Failed marks the connection with VerdictFailed and stores the reason.
|
||||
func (conn *Connection) Failed(reason string) {
|
||||
if conn.SetVerdict(VerdictFailed) {
|
||||
conn.Reason = reason
|
||||
log.Infof("filter: dropping connection %s because of an internal error: %s", conn, reason)
|
||||
} else {
|
||||
log.Warningf("filter: tried to drop %s due to error but current verdict is %s", conn, conn.Verdict)
|
||||
}
|
||||
}
|
||||
|
||||
// SetVerdict sets a new verdict for the connection, making sure it does not interfere with previous verdicts.
|
||||
func (conn *Connection) SetVerdict(newVerdict Verdict) (ok bool) {
|
||||
if newVerdict >= conn.Verdict {
|
||||
|
|
|
@ -13,6 +13,7 @@ const (
|
|||
VerdictDrop Verdict = 4
|
||||
VerdictRerouteToNameserver Verdict = 5
|
||||
VerdictRerouteToTunnel Verdict = 6
|
||||
VerdictFailed Verdict = 7
|
||||
)
|
||||
|
||||
func (v Verdict) String() string {
|
||||
|
@ -31,6 +32,8 @@ func (v Verdict) String() string {
|
|||
return "RerouteToNameserver"
|
||||
case VerdictRerouteToTunnel:
|
||||
return "RerouteToTunnel"
|
||||
case VerdictFailed:
|
||||
return "Failed"
|
||||
default:
|
||||
return "<INVALID VERDICT>"
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue