issue new verdict on configuration change

This commit is contained in:
Vladimir Stoilov 2022-09-05 17:14:45 +02:00 committed by Daniel
parent 0423dfbbbf
commit fdc8ef5698
18 changed files with 298 additions and 83 deletions

View file

@ -182,12 +182,12 @@ func FilterResolvedDNS(
return rrCache return rrCache
} }
// Only filter criticial things if request comes from the system resolver. // Only filter critical things if request comes from the system resolver.
sysResolver := conn.Process().IsSystemResolver() sysResolver := conn.Process().IsSystemResolver()
// Filter dns records and return if the query is blocked. // Filter dns records and return if the query is blocked.
rrCache = filterDNSResponse(ctx, conn, layeredProfile, rrCache, sysResolver) rrCache = filterDNSResponse(ctx, conn, layeredProfile, rrCache, sysResolver)
if conn.Verdict == network.VerdictBlock { if conn.Verdict.Current == network.VerdictBlock {
return rrCache return rrCache
} }

View file

@ -64,7 +64,7 @@ func RunInspectors(conn *network.Connection, pkt packet.Packet) (network.Verdict
} }
// check if the current verdict is already past the inspection criteria. // check if the current verdict is already past the inspection criteria.
if conn.Verdict > inspectVerdicts[key] { if conn.Verdict.Current > inspectVerdicts[key] {
activeInspectors[key] = true activeInspectors[key] = true
continue continue
} }
@ -86,11 +86,11 @@ func RunInspectors(conn *network.Connection, pkt packet.Packet) (network.Verdict
continueInspection = true continueInspection = true
case BLOCK_CONN: case BLOCK_CONN:
conn.SetVerdict(network.VerdictBlock, "", "", nil) conn.SetVerdict(network.VerdictBlock, "", "", nil)
verdict = conn.Verdict verdict = conn.Verdict.Current
activeInspectors[key] = true activeInspectors[key] = true
case DROP_CONN: case DROP_CONN:
conn.SetVerdict(network.VerdictDrop, "", "", nil) conn.SetVerdict(network.VerdictDrop, "", "", nil)
verdict = conn.Verdict verdict = conn.Verdict.Current
activeInspectors[key] = true activeInspectors[key] = true
case STOP_INSPECTING: case STOP_INSPECTING:
activeInspectors[key] = true activeInspectors[key] = true

View file

@ -44,18 +44,46 @@ var (
ownPID = os.Getpid() ownPID = os.Getpid()
) )
const configChangeEvent = "config change"
const profileConfigChangeEvent = "profile config change"
func init() { func init() {
// TODO: Move interception module to own package (dir). // TODO: Move interception module to own package (dir).
interceptionModule = modules.Register("interception", interceptionPrep, interceptionStart, interceptionStop, "base", "updates", "network", "notifications") interceptionModule = modules.Register("interception", interceptionPrep, interceptionStart, interceptionStop, "base", "updates", "network", "notifications", "profiles")
network.SetDefaultFirewallHandler(defaultHandler) network.SetDefaultFirewallHandler(defaultHandler)
captain.PreConnect = func(ctx context.Context) {
interception.CloseAllConnections() // setup event callback when spn has connected
}
captain.ResetConnections = resetAllConnectionsVerdict captain.ResetConnections = resetAllConnectionsVerdict
} }
func interceptionPrep() error { func interceptionPrep() error {
err := interceptionModule.RegisterEventHook(
"config",
configChangeEvent,
"firewall config change event",
func(ctx context.Context, _ interface{}) error {
resetAllConnections()
return nil
},
)
if err != nil {
_ = fmt.Errorf("failed registering event hook: %w", err)
}
err = interceptionModule.RegisterEventHook(
"profiles",
profileConfigChangeEvent,
"firewall config change event",
func(ctx context.Context, _ interface{}) error {
resetAllConnections()
return nil
},
)
if err != nil {
_ = fmt.Errorf("failed registering event hook: %w", err)
}
if err := registerConfig(); err != nil { if err := registerConfig(); err != nil {
return err return err
} }
@ -63,6 +91,29 @@ func interceptionPrep() error {
return prepAPIAuth() return prepAPIAuth()
} }
func resetAllConnections() {
log.Critical("Reseting all connections")
log.Info("interception: resetting all connections")
err := interception.DeleteAllConnections()
if err != nil {
log.Criticalf("failed to run ResetAllExternalConnections: %q", err)
}
for _, id := range network.GetAllIDs() {
conn, err := getConnectionByID(id)
if err != nil {
continue
}
if !captain.IsExcepted(conn.Entity.IP) {
conn.SetFirewallHandler(initialHandler)
// Reset entity if it exists.
if conn.Entity != nil {
conn.Entity.ResetLists()
}
}
}
}
func interceptionStart() error { func interceptionStart() error {
getConfig() getConfig()
@ -168,10 +219,32 @@ func getConnection(pkt packet.Packet) (*network.Connection, error) {
return conn, nil return conn, nil
} }
func getConnectionByID(id string) (*network.Connection, error) {
// Create or get connection in single inflight lock in order to prevent duplicates.
connPtr, _, _ := getConnectionSingleInflight.Do(id, func() (interface{}, error) {
// First, check for an existing connection.
conn, ok := network.GetConnection(id)
if ok {
return conn, nil
}
// Else return nil
return nil, nil
})
if connPtr == nil {
return nil, errors.New("connection does not exist")
}
connection := connPtr.(*network.Connection)
return connection, nil
}
func resetAllConnectionsVerdict(ctx context.Context) { func resetAllConnectionsVerdict(ctx context.Context) {
resetAllConnections()
// interception.CloseAllConnections() // interception.CloseAllConnections()
network.ClearConnections() // network.ClearConnections()
log.Critical("Clearing connections") // log.Critical("Clearing connections")
// interception.CloseAllConnections() // interception.CloseAllConnections()
// ids := network.GetAllIDs() // ids := network.GetAllIDs()
// for _, id := range ids { // for _, id := range ids {
@ -389,7 +462,7 @@ func initialHandler(conn *network.Connection, pkt packet.Packet) {
conn.Entity.IPScope == netutils.LocalMulticast): conn.Entity.IPScope == netutils.LocalMulticast):
// Reroute rogue dns queries back to Portmaster. // Reroute rogue dns queries back to Portmaster.
conn.Verdict = network.VerdictRerouteToNameserver conn.SetVerdictDirectly(network.VerdictRerouteToNameserver)
conn.Reason.Msg = "redirecting rogue dns query" conn.Reason.Msg = "redirecting rogue dns query"
conn.Internal = true conn.Internal = true
// End directly, as no other processing is necessary. // End directly, as no other processing is necessary.
@ -424,6 +497,8 @@ func initialHandler(conn *network.Connection, pkt packet.Packet) {
// Check if connection should be tunneled. // Check if connection should be tunneled.
checkTunneling(pkt.Ctx(), conn, pkt) checkTunneling(pkt.Ctx(), conn, pkt)
updateVerdictBasedOnPreviousState(conn, pkt)
switch { switch {
case conn.Inspecting: case conn.Inspecting:
log.Tracer(pkt.Ctx()).Trace("filter: start inspecting") log.Tracer(pkt.Ctx()).Trace("filter: start inspecting")
@ -462,8 +537,8 @@ func issueVerdict(conn *network.Connection, pkt packet.Packet, verdict network.V
} }
// do not allow to circumvent decision: e.g. to ACCEPT packets from a DROP-ed connection // do not allow to circumvent decision: e.g. to ACCEPT packets from a DROP-ed connection
if verdict < conn.Verdict { if verdict < conn.Verdict.Current {
verdict = conn.Verdict verdict = conn.Verdict.Current
} }
var err error var err error
@ -509,6 +584,18 @@ func issueVerdict(conn *network.Connection, pkt packet.Packet, verdict network.V
} }
} }
func updateVerdictBasedOnPreviousState(conn *network.Connection, pkt packet.Packet) {
if conn.Verdict.Current == network.VerdictAccept {
if conn.Verdict.Previous == network.VerdictRerouteToTunnel && !conn.Tunneled {
conn.SetVerdictDirectly(network.VerdictBlock)
} else if conn.Verdict.Previous == network.VerdictAccept && conn.Tunneled {
conn.SetVerdictDirectly(network.VerdictBlock)
} else if conn.Tunneled {
conn.SetVerdictDirectly(network.VerdictRerouteToTunnel)
}
}
}
// func tunnelHandler(pkt packet.Packet) { // func tunnelHandler(pkt packet.Packet) {
// tunnelInfo := GetTunnelInfo(pkt.Info().Dst) // tunnelInfo := GetTunnelInfo(pkt.Info().Dst)
// if tunnelInfo == nil { // if tunnelInfo == nil {

View file

@ -0,0 +1,95 @@
package interception
import (
"encoding/binary"
"fmt"
"net"
ct "github.com/florianl/go-conntrack"
"github.com/safing/portbase/log"
"github.com/safing/portmaster/firewall/interception/nfq"
)
// CloseAllConnections closes all active connection on conntrack.
func CloseAllConnections() error {
nfct, err := ct.Open(&ct.Config{})
if err != nil {
return err
}
defer func() { _ = nfct.Close() }()
connections, err := nfct.Dump(ct.Conntrack, ct.IPv4)
if err != nil {
return err
}
log.Criticalf("Number of connections: %d", len(connections))
for _, connection := range connections {
fmt.Printf("[%2d] %s - %s\n", connection.Origin.Proto.Number, connection.Origin.Src, connection.Origin.Dst)
err := nfct.Delete(ct.Conntrack, ct.IPv4, connection)
log.Errorf("Error deleting connection %q", err)
}
return nil
}
// DeleteAllConnections deletes all entries from conntrack table.
func DeleteAllConnections() error {
nfct, err := ct.Open(&ct.Config{})
if err != nil {
return err
}
defer func() { _ = nfct.Close() }()
connections, err := getAllPermanentConnections(nfct)
for _, connection := range connections {
_ = nfct.Delete(ct.Conntrack, ct.IPv4, connection)
}
return err
}
// DeleteConnection deletes a specific connection.
func DeleteConnection(sourceIP net.IP, sourcePort uint16, destinationIP net.IP, destinationPort uint16) error {
nfct, err := ct.Open(&ct.Config{})
if err != nil {
return err
}
defer func() { _ = nfct.Close() }()
filter := &ct.IPTuple{Src: &sourceIP, Dst: &destinationIP, Proto: &ct.ProtoTuple{SrcPort: &sourcePort, DstPort: &destinationPort}}
connectionFilter := ct.Con{
Origin: filter,
}
connections, _ := nfct.Get(ct.Conntrack, ct.IPv4, connectionFilter)
for _, connection := range connections {
_ = nfct.Delete(ct.Conntrack, ct.IPv4, connection)
}
connectionFilter.Origin = nil
connectionFilter.Reply = filter
connections, err = nfct.Get(ct.Conntrack, ct.IPv4, connectionFilter)
for _, connection := range connections {
_ = nfct.Delete(ct.Conntrack, ct.IPv4, connection)
}
return err
}
func getAllPermanentConnections(nfct *ct.Nfct) ([]ct.Con, error) {
permanentFlags := []uint32{nfq.MarkAccept, nfq.MarkBlock, nfq.MarkDrop, nfq.MarkAcceptAlways, nfq.MarkBlockAlways, nfq.MarkDropAlways, nfq.MarkRerouteSPN}
filter := ct.FilterAttr{}
filter.MarkMask = []byte{0xFF, 0xFF, 0xFF, 0xFF}
filter.Mark = []byte{0x00, 0x00, 0x00, 0x00} // 4 zeros starting value
connections := make([]ct.Con, 0)
for _, mark := range permanentFlags {
binary.BigEndian.PutUint32(filter.Mark, mark) // Little endian is in reverse not sure why. BigEndian makes it in correct order.
currentConnections, err := nfct.Query(ct.Conntrack, ct.IPv4, filter)
if err != nil {
return nil, err
}
connections = append(connections, currentConnections...)
}
return connections, nil
}

View file

@ -2,9 +2,6 @@ package interception
import ( import (
"flag" "flag"
"fmt"
ct "github.com/florianl/go-conntrack"
"github.com/safing/portbase/log" "github.com/safing/portbase/log"
"github.com/safing/portmaster/network/packet" "github.com/safing/portmaster/network/packet"
@ -52,24 +49,3 @@ func Stop() error {
return stop() return stop()
} }
func CloseAllConnections() error {
nfct, err := ct.Open(&ct.Config{})
if err != nil {
return err
}
defer func() { _ = nfct.Close() }()
connections, err := nfct.Dump(ct.Conntrack, ct.IPv4)
if err != nil {
return err
}
log.Criticalf("Number of connections: %d", len(connections))
for _, connection := range connections {
fmt.Printf("[%2d] %s - %s\n", connection.Origin.Proto.Number, connection.Origin.Src, connection.Origin.Dst)
err := nfct.Delete(ct.Conntrack, ct.IPv4, connection)
log.Errorf("Error deleting connection %q", err)
}
return nil
}

View file

@ -76,7 +76,7 @@ func DecideOnConnection(ctx context.Context, conn *network.Connection, pkt packe
// Reset verdict for connection. // Reset verdict for connection.
log.Tracer(ctx).Infof("filter: re-evaluating verdict on %s", conn) log.Tracer(ctx).Infof("filter: re-evaluating verdict on %s", conn)
conn.Verdict = network.VerdictUndecided // conn.SetVerdictDirectly(network.VerdictUndecided)
// Reset entity if it exists. // Reset entity if it exists.
if conn.Entity != nil { if conn.Entity != nil {

View file

@ -28,7 +28,7 @@ func checkTunneling(ctx context.Context, conn *network.Connection, pkt packet.Pa
case conn.Inbound: case conn.Inbound:
// Can't tunnel incoming connections. // Can't tunnel incoming connections.
return return
case conn.Verdict != network.VerdictAccept: case conn.Verdict.Current != network.VerdictAccept:
// Connection will be blocked. // Connection will be blocked.
return return
case conn.Process().Pid == ownPID: case conn.Process().Pid == ownPID:
@ -156,7 +156,7 @@ func checkTunneling(ctx context.Context, conn *network.Connection, pkt packet.Pa
conn.Failed("failed to request tunneling", "") conn.Failed("failed to request tunneling", "")
} else { } else {
//log.Tracer(pkt.Ctx()).Trace("filter: tunneling requested") //log.Tracer(pkt.Ctx()).Trace("filter: tunneling requested")
conn.Verdict = network.VerdictRerouteToTunnel //conn.SetVerdictDirectly(network.VerdictRerouteToTunnel)
conn.Tunneled = true conn.Tunneled = true
} }
} }

View file

@ -189,7 +189,7 @@ func handleRequest(ctx context.Context, w dns.ResponseWriter, request *dns.Msg)
conn.Resolver = rrCache.Resolver conn.Resolver = rrCache.Resolver
} }
switch conn.Verdict { switch conn.Verdict.Current {
// We immediately save blocked, dropped or failed verdicts so // We immediately save blocked, dropped or failed verdicts so
// they pop up in the UI. // they pop up in the UI.
case network.VerdictBlock, network.VerdictDrop, network.VerdictFailed, network.VerdictRerouteToNameserver, network.VerdictRerouteToTunnel: case network.VerdictBlock, network.VerdictDrop, network.VerdictFailed, network.VerdictRerouteToNameserver, network.VerdictRerouteToTunnel:
@ -235,11 +235,11 @@ func handleRequest(ctx context.Context, w dns.ResponseWriter, request *dns.Msg)
} }
// Check if there is a Verdict to act upon. // Check if there is a Verdict to act upon.
switch conn.Verdict { //nolint:exhaustive // Only checking for specific values. switch conn.Verdict.Current { //nolint:exhaustive // Only checking for specific values.
case network.VerdictBlock, network.VerdictDrop, network.VerdictFailed: case network.VerdictBlock, network.VerdictDrop, network.VerdictFailed:
tracer.Infof( tracer.Infof(
"nameserver: returning %s response for %s to %s", "nameserver: returning %s response for %s to %s",
conn.Verdict.Verb(), conn.Verdict.Current.Verb(),
q.ID(), q.ID(),
conn.Process(), conn.Process(),
) )
@ -315,11 +315,11 @@ func handleRequest(ctx context.Context, w dns.ResponseWriter, request *dns.Msg)
} }
// Check if there is a Verdict to act upon. // Check if there is a Verdict to act upon.
switch conn.Verdict { //nolint:exhaustive // Only checking for specific values. switch conn.Verdict.Current { //nolint:exhaustive // Only checking for specific values.
case network.VerdictBlock, network.VerdictDrop, network.VerdictFailed: case network.VerdictBlock, network.VerdictDrop, network.VerdictFailed:
tracer.Infof( tracer.Infof(
"nameserver: returning %s response for %s to %s", "nameserver: returning %s response for %s to %s",
conn.Verdict.Verb(), conn.Verdict.Current.Verb(),
q.ID(), q.ID(),
conn.Process(), conn.Process(),
) )
@ -338,7 +338,7 @@ func handleRequest(ctx context.Context, w dns.ResponseWriter, request *dns.Msg)
} }
tracer.Infof( tracer.Infof(
"nameserver: returning %s response (%s%s) for %s to %s", "nameserver: returning %s response (%s%s) for %s to %s",
conn.Verdict.Verb(), conn.Verdict.Current.Verb(),
dns.RcodeToString[rrCache.RCode], dns.RcodeToString[rrCache.RCode],
noAnswerIndicator, noAnswerIndicator,
q.ID(), q.ID(),

View file

@ -155,7 +155,7 @@ func convertConnection(conn *network.Connection) (*Conn, error) {
IPProtocol: conn.IPProtocol, IPProtocol: conn.IPProtocol,
LocalIP: conn.LocalIP.String(), LocalIP: conn.LocalIP.String(),
LocalPort: conn.LocalPort, LocalPort: conn.LocalPort,
Verdict: conn.Verdict, Verdict: conn.Verdict.User,
Started: time.Unix(conn.Started, 0), Started: time.Unix(conn.Started, 0),
Tunneled: conn.Tunneled, Tunneled: conn.Tunneled,
Encrypted: conn.Encrypted, Encrypted: conn.Encrypted,
@ -177,7 +177,7 @@ func convertConnection(conn *network.Connection) (*Conn, error) {
c.Type = "" c.Type = ""
} }
switch conn.Verdict { switch conn.Verdict.User {
case network.VerdictAccept, network.VerdictRerouteToNameserver, network.VerdictRerouteToTunnel: case network.VerdictAccept, network.VerdictRerouteToNameserver, network.VerdictRerouteToTunnel:
accepted := true accepted := true
c.Allowed = &accepted c.Allowed = &accepted

View file

@ -152,7 +152,7 @@ func AddNetworkDebugData(di *debug.Info, profile, where string) {
// Count. // Count.
total++ total++
switch conn.Verdict { //nolint:exhaustive switch conn.Verdict.Current { //nolint:exhaustive
case VerdictAccept, case VerdictAccept,
VerdictRerouteToNameserver, VerdictRerouteToNameserver,
VerdictRerouteToTunnel: VerdictRerouteToTunnel:
@ -232,7 +232,7 @@ func (conn *Connection) debugInfoLine() string {
return fmt.Sprintf( return fmt.Sprintf(
"% 14s %s%- 25s %s-%s P#%d [%s] %s - by %s @ %s", "% 14s %s%- 25s %s-%s P#%d [%s] %s - by %s @ %s",
conn.Verdict.Verb(), conn.Verdict.Current.Verb(),
connectionData, connectionData,
conn.fmtDomainComponent(), conn.fmtDomainComponent(),
time.Unix(conn.Started, 0).Format("15:04:05"), time.Unix(conn.Started, 0).Format("15:04:05"),

View file

@ -40,7 +40,15 @@ var connectionTestData = []*Connection{
Country: "", Country: "",
ASN: 0, ASN: 0,
}, },
Verdict: 4, Verdict: struct {
Current Verdict
Previous Verdict
User Verdict
}{
Current: 2,
Previous: 2,
User: 2,
},
Reason: Reason{ Reason: Reason{
Msg: "incoming connection blocked by default", Msg: "incoming connection blocked by default",
OptionKey: "filter/serviceEndpoints", OptionKey: "filter/serviceEndpoints",
@ -80,7 +88,15 @@ var connectionTestData = []*Connection{
Country: "DE", Country: "DE",
ASN: 16509, ASN: 16509,
}, },
Verdict: 2, Verdict: struct {
Current Verdict
Previous Verdict
User Verdict
}{
Current: 2,
Previous: 2,
User: 2,
},
Reason: Reason{ Reason: Reason{
Msg: "default permit", Msg: "default permit",
OptionKey: "filter/defaultAction", OptionKey: "filter/defaultAction",
@ -123,7 +139,15 @@ var connectionTestData = []*Connection{
Country: "US", Country: "US",
ASN: 15169, ASN: 15169,
}, },
Verdict: 2, Verdict: struct {
Current Verdict
Previous Verdict
User Verdict
}{
Current: 2,
Previous: 2,
User: 2,
},
Reason: Reason{ Reason: Reason{
Msg: "default permit", Msg: "default permit",
OptionKey: "filter/defaultAction", OptionKey: "filter/defaultAction",

View file

@ -107,16 +107,23 @@ type Connection struct { //nolint:maligned // TODO: fix alignment
// Resolver holds information about the resolver used to resolve // Resolver holds information about the resolver used to resolve
// Entity.Domain. // Entity.Domain.
Resolver *resolver.ResolverInfo Resolver *resolver.ResolverInfo
// Verdict is the final decision that has been made for a connection. // Verdict holds decisions that are made for a connection
// The verdict may change so any access to it must be guarded by the // The verdict may change so any access to it must be guarded by the
// connection lock. // connection lock.
Verdict Verdict Verdict struct {
// Current is the current decision that has been made for a connection.
Current Verdict
// PreviousVerdict holds the previous verdict value, if there wasn't previous it will hold VerdictUndecided
Previous Verdict
// UserVerdict holds the verdict that should be displayed in the user interface
User Verdict
}
// Reason holds information justifying the verdict, as well as additional // Reason holds information justifying the verdict, as well as additional
// information about the reason. // information about the reason.
// Access to Reason must be guarded by the connection lock. // Access to Reason must be guarded by the connection lock.
Reason Reason Reason Reason
// Started holds the number of seconds in UNIX epoch time at which // Started holds the number of seconds in UNIX epoch time at which
// the connection has been initated and first seen by the portmaster. // the connection has been initiated and first seen by the portmaster.
// Started is only ever set when creating a new connection object // Started is only ever set when creating a new connection object
// and is considered immutable afterwards. // and is considered immutable afterwards.
Started int64 Started int64
@ -142,7 +149,7 @@ type Connection struct { //nolint:maligned // TODO: fix alignment
// TunnelOpts holds options for tunneling the connection. // TunnelOpts holds options for tunneling the connection.
TunnelOpts *navigator.Options TunnelOpts *navigator.Options
// ProcessContext holds additional information about the process // ProcessContext holds additional information about the process
// that iniated the connection. It is set once when the connection // that initiated the connection. It is set once when the connection
// object is created and is considered immutable afterwards. // object is created and is considered immutable afterwards.
ProcessContext ProcessContext ProcessContext ProcessContext
// DNSContext holds additional information about the DNS request that was // DNSContext holds additional information about the DNS request that was
@ -159,7 +166,7 @@ type Connection struct { //nolint:maligned // TODO: fix alignment
// points and access to it must be guarded by the connection lock. // points and access to it must be guarded by the connection lock.
Internal bool Internal bool
// process holds a reference to the actor process. That is, the // process holds a reference to the actor process. That is, the
// process instance that initated the connection. // process instance that initiated the connection.
process *process.Process process *process.Process
// pkgQueue is used to serialize packet handling for a single // pkgQueue is used to serialize packet handling for a single
// connection and is served by the connections packetHandler. // connection and is served by the connections packetHandler.
@ -167,7 +174,7 @@ type Connection struct { //nolint:maligned // TODO: fix alignment
// firewallHandler is the firewall handler that is called for // firewallHandler is the firewall handler that is called for
// each packet sent to pktQueue. // each packet sent to pktQueue.
firewallHandler FirewallHandler firewallHandler FirewallHandler
// saveWhenFinished can be set to drue during the life-time of // saveWhenFinished can be set to true during the life-time of
// a connection and signals the firewallHandler that a Save() // a connection and signals the firewallHandler that a Save()
// should be issued after processing the connection. // should be issued after processing the connection.
saveWhenFinished bool saveWhenFinished bool
@ -519,8 +526,11 @@ func (conn *Connection) Failed(reason, reasonOptionKey string) {
conn.FailedWithContext(reason, reasonOptionKey, nil) conn.FailedWithContext(reason, reasonOptionKey, nil)
} }
// Reset resets all values of the connection.
func (conn *Connection) Reset(reason, reasonOptionKey string) { func (conn *Connection) Reset(reason, reasonOptionKey string) {
conn.Verdict = VerdictUndecided conn.Verdict.Current = VerdictUndecided
conn.Verdict.Previous = VerdictUndecided
conn.Verdict.User = VerdictUndecided
conn.Reason.Msg = reason conn.Reason.Msg = reason
conn.Reason.Context = nil conn.Reason.Context = nil
@ -534,8 +544,9 @@ func (conn *Connection) Reset(reason, reasonOptionKey string) {
// SetVerdict sets a new verdict for the connection, making sure it does not interfere with previous verdicts. // SetVerdict sets a new verdict for the connection, making sure it does not interfere with previous verdicts.
func (conn *Connection) SetVerdict(newVerdict Verdict, reason, reasonOptionKey string, reasonCtx interface{}) (ok bool) { func (conn *Connection) SetVerdict(newVerdict Verdict, reason, reasonOptionKey string, reasonCtx interface{}) (ok bool) {
if newVerdict >= conn.Verdict { // if newVerdict >= conn.Verdict.Current {
conn.Verdict = newVerdict conn.SetVerdictDirectly(newVerdict)
conn.Reason.Msg = reason conn.Reason.Msg = reason
conn.Reason.Context = reasonCtx conn.Reason.Context = reasonCtx
@ -547,8 +558,23 @@ func (conn *Connection) SetVerdict(newVerdict Verdict, reason, reasonOptionKey s
} }
return true return true
// }
// return false
}
// SetVerdictDirectly sets the new verdict and stores the previous value.
func (conn *Connection) SetVerdictDirectly(newVerdict Verdict) {
if newVerdict == conn.Verdict.Current {
return
}
// Save previous verdict and set new one
conn.Verdict.Previous = conn.Verdict.Current
conn.Verdict.Current = newVerdict
// if a connection is accepted once it should always show as accepted
if conn.Verdict.User != VerdictAccept {
conn.Verdict.User = newVerdict
} }
return false
} }
// Process returns the connection's process. // Process returns the connection's process.
@ -675,7 +701,7 @@ func packetHandlerHandleConn(conn *Connection, pkt packet.Packet) {
} }
// Log verdict. // Log verdict.
log.Tracer(pkt.Ctx()).Infof("filter: connection %s %s: %s", conn, conn.Verdict.Verb(), conn.Reason.Msg) log.Tracer(pkt.Ctx()).Infof("filter: connection %s %s: %s", conn, conn.Verdict.Current.Verb(), conn.Reason.Msg)
// Submit trace logs. // Submit trace logs.
log.Tracer(pkt.Ctx()).Submit() log.Tracer(pkt.Ctx()).Submit()

View file

@ -115,7 +115,7 @@ func writeOpenDNSRequestsToDB() {
// ReplyWithDNS creates a new reply to the given request with the data from the RRCache, and additional informational records. // ReplyWithDNS creates a new reply to the given request with the data from the RRCache, and additional informational records.
func (conn *Connection) ReplyWithDNS(ctx context.Context, request *dns.Msg) *dns.Msg { func (conn *Connection) ReplyWithDNS(ctx context.Context, request *dns.Msg) *dns.Msg {
// Select request responder. // Select request responder.
switch conn.Verdict { switch conn.Verdict.Current {
case VerdictBlock: case VerdictBlock:
return nsutil.BlockIP().ReplyWithDNS(ctx, request) return nsutil.BlockIP().ReplyWithDNS(ctx, request)
case VerdictDrop: case VerdictDrop:
@ -136,7 +136,7 @@ func (conn *Connection) ReplyWithDNS(ctx context.Context, request *dns.Msg) *dns
func (conn *Connection) GetExtraRRs(ctx context.Context, request *dns.Msg) []dns.RR { func (conn *Connection) GetExtraRRs(ctx context.Context, request *dns.Msg) []dns.RR {
// Select level to add the verdict record with. // Select level to add the verdict record with.
var level log.Severity var level log.Severity
switch conn.Verdict { switch conn.Verdict.Current {
case VerdictFailed: case VerdictFailed:
level = log.ErrorLevel level = log.ErrorLevel
case VerdictUndecided, VerdictUndeterminable, case VerdictUndecided, VerdictUndeterminable,
@ -148,7 +148,7 @@ func (conn *Connection) GetExtraRRs(ctx context.Context, request *dns.Msg) []dns
} }
// Create resource record with verdict and reason. // Create resource record with verdict and reason.
rr, err := nsutil.MakeMessageRecord(level, fmt.Sprintf("%s: %s", conn.Verdict.Verb(), conn.Reason.Msg)) rr, err := nsutil.MakeMessageRecord(level, fmt.Sprintf("%s: %s", conn.Verdict.Current.Verb(), conn.Reason.Msg))
if err != nil { if err != nil {
log.Tracer(ctx).Warningf("filter: failed to add informational record to reply: %s", err) log.Tracer(ctx).Warningf("filter: failed to add informational record to reply: %s", err)
return nil return nil

View file

@ -119,7 +119,7 @@ func (conn *Connection) addToMetrics() {
} }
// Check the verdict. // Check the verdict.
switch conn.Verdict { //nolint:exhaustive // Not critical. switch conn.Verdict.Current { //nolint:exhaustive // Not critical.
case VerdictBlock, VerdictDrop: case VerdictBlock, VerdictDrop:
blockedOutConnCounter.Inc() blockedOutConnCounter.Inc()
conn.addedToMetrics = true conn.addedToMetrics = true

View file

@ -224,7 +224,7 @@ func (pkt *Base) FmtRemoteAddress() string {
return fmt.Sprintf("%s:%s:%s", pkt.info.Protocol.String(), pkt.FmtRemoteIP(), pkt.FmtRemotePort()) return fmt.Sprintf("%s:%s:%s", pkt.info.Protocol.String(), pkt.FmtRemoteIP(), pkt.FmtRemotePort())
} }
// Packet is an interface to a network packet to provide object behaviour the same across all systems. // Packet is an interface to a network packet to provide object behavior the same across all systems.
type Packet interface { type Packet interface {
// Verdicts. // Verdicts.
Accept() error Accept() error

View file

@ -91,6 +91,7 @@ func startProfileUpdateChecker() error {
if err == nil { if err == nil {
newProfile.layeredProfile.Update() newProfile.layeredProfile.Update()
} }
module.TriggerEvent(profileConfigChange, nil)
} }
// Always increase the revision counter of the layer profile. // Always increase the revision counter of the layer profile.
@ -104,6 +105,7 @@ func startProfileUpdateChecker() error {
receivedProfile, err := EnsureProfile(r) receivedProfile, err := EnsureProfile(r)
if err != nil || !receivedProfile.savedInternally { if err != nil || !receivedProfile.savedInternally {
activeProfile.outdated.Set() activeProfile.outdated.Set()
module.TriggerEvent(profileConfigChange, nil)
} }
case <-ctx.Done(): case <-ctx.Done():
return nil return nil

View file

@ -16,8 +16,13 @@ var (
updatesPath string updatesPath string
) )
const (
profileConfigChange = "profile config change"
)
func init() { func init() {
module = modules.Register("profiles", prep, start, nil, "base", "updates") module = modules.Register("profiles", prep, start, nil, "base", "updates")
module.RegisterEvent(profileConfigChange, true)
} }
func prep() error { func prep() error {

View file

@ -322,15 +322,15 @@ func (profile *Profile) GetServiceEndpoints() endpoints.Endpoints {
// AddEndpoint adds an endpoint to the endpoint list, saves the profile and reloads the configuration. // AddEndpoint adds an endpoint to the endpoint list, saves the profile and reloads the configuration.
func (profile *Profile) AddEndpoint(newEntry string) { func (profile *Profile) AddEndpoint(newEntry string) {
profile.addEndpointyEntry(CfgOptionEndpointsKey, newEntry) profile.addEndpointEntry(CfgOptionEndpointsKey, newEntry)
} }
// AddServiceEndpoint adds a service endpoint to the endpoint list, saves the profile and reloads the configuration. // AddServiceEndpoint adds a service endpoint to the endpoint list, saves the profile and reloads the configuration.
func (profile *Profile) AddServiceEndpoint(newEntry string) { func (profile *Profile) AddServiceEndpoint(newEntry string) {
profile.addEndpointyEntry(CfgOptionServiceEndpointsKey, newEntry) profile.addEndpointEntry(CfgOptionServiceEndpointsKey, newEntry)
} }
func (profile *Profile) addEndpointyEntry(cfgKey, newEntry string) { func (profile *Profile) addEndpointEntry(cfgKey, newEntry string) {
changed := false changed := false
// When finished, save the profile. // When finished, save the profile.
@ -365,7 +365,7 @@ func (profile *Profile) addEndpointyEntry(cfgKey, newEntry string) {
if entry == newEntry { if entry == newEntry {
// An identical entry is already in the list, abort. // An identical entry is already in the list, abort.
log.Debugf("profile: ingoring new endpoint rule for %s, as identical is already present: %s", profile, newEntry) log.Debugf("profile: ignoring new endpoint rule for %s, as identical is already present: %s", profile, newEntry)
return return
} }
} }