mirror of
https://github.com/safing/portmaster
synced 2025-09-15 17:29:42 +00:00
issue new verdict on configuration change
This commit is contained in:
parent
0423dfbbbf
commit
fdc8ef5698
18 changed files with 298 additions and 83 deletions
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
95
firewall/interception/connection_manager_linux.go
Normal file
95
firewall/interception/connection_manager_linux.go
Normal 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
|
||||||
|
}
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"),
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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()
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue