Merge pull request #44 from safing/feature/pre-alpha-finalizing

Pre alpha finalizing
This commit is contained in:
Patrick Pacher
2020-04-30 16:42:09 +02:00
committed by GitHub
33 changed files with 427 additions and 166 deletions

View File

@@ -11,6 +11,8 @@ import (
var ( var (
CfgDevModeKey = "core/devMode" CfgDevModeKey = "core/devMode"
defaultDevMode bool defaultDevMode bool
CfgUseSystemNotificationsKey = "core/useSystemNotifications"
) )
func init() { func init() {
@@ -28,6 +30,7 @@ func registerConfig() error {
Name: "Development Mode", Name: "Development Mode",
Key: CfgDevModeKey, Key: CfgDevModeKey,
Description: "In Development Mode security restrictions are lifted/softened to enable easier access to Portmaster for debugging and testing purposes.", Description: "In Development Mode security restrictions are lifted/softened to enable easier access to Portmaster for debugging and testing purposes.",
Order: 127,
OptType: config.OptTypeBool, OptType: config.OptTypeBool,
ExpertiseLevel: config.ExpertiseLevelDeveloper, ExpertiseLevel: config.ExpertiseLevelDeveloper,
ReleaseLevel: config.ReleaseLevelStable, ReleaseLevel: config.ReleaseLevelStable,
@@ -37,5 +40,19 @@ func registerConfig() error {
return err return err
} }
err = config.Register(&config.Option{
Name: "Use System Notifications",
Key: CfgUseSystemNotificationsKey,
Description: "Send notifications to your operating system's notification system. When this setting is turned off, notifications will only be visible in the Portmaster App. This affects both alerts from the Portmaster and questions from the Privacy Filter.",
Order: 32,
OptType: config.OptTypeBool,
ExpertiseLevel: config.ExpertiseLevelUser,
ReleaseLevel: config.ReleaseLevelStable,
DefaultValue: true, // TODO: turn off by default on unsupported systems
})
if err != nil {
return err
}
return nil return nil
} }

100
core/control.go Normal file
View File

@@ -0,0 +1,100 @@
package core
import (
"fmt"
"strings"
"sync"
"github.com/safing/portbase/database"
"github.com/safing/portbase/database/record"
"github.com/safing/portbase/database/storage"
)
// StorageInterface provices a storage.Interface to the storage manager.
type StorageInterface struct {
storage.InjectBase
}
// Get returns a database record.
func (s *StorageInterface) Get(key string) (record.Record, error) {
msg := newMessage(key)
splittedKey := strings.Split(key, "/")
switch splittedKey[0] {
case "module":
return controlModule(msg, splittedKey)
default:
return nil, storage.ErrNotFound
}
}
func controlModule(msg *MessageRecord, splittedKey []string) (record.Record, error) {
// format: module/moduleName/action/param
var moduleName string
var action string
var param string
var err error
// parse elements
switch len(splittedKey) {
case 4:
param = splittedKey[3]
fallthrough
case 3:
moduleName = splittedKey[1]
action = splittedKey[2]
default:
return nil, storage.ErrNotFound
}
// execute
switch action {
case "trigger":
err = module.InjectEvent(fmt.Sprintf("user triggered the '%s/%s' event", moduleName, param), moduleName, param, nil)
default:
return nil, storage.ErrNotFound
}
if err != nil {
msg.Message = err.Error()
} else {
msg.Success = true
}
return msg, nil
}
func registerControlDatabase() error {
_, err := database.Register(&database.Database{
Name: "control",
Description: "Control Interface for the Portmaster",
StorageType: "injected",
PrimaryAPI: "",
})
if err != nil {
return err
}
_, err = database.InjectDatabase("control", &StorageInterface{})
if err != nil {
return err
}
return nil
}
// MessageRecord is a simple record used for control database feedback
type MessageRecord struct {
record.Base
sync.Mutex
Success bool
Message string
}
func newMessage(key string) *MessageRecord {
m := &MessageRecord{}
m.SetKey("control:" + key)
m.UpdateMeta()
return m
}

View File

@@ -20,7 +20,19 @@ var (
func init() { func init() {
modules.Register("base", nil, registerDatabases, nil, "database", "config", "rng") modules.Register("base", nil, registerDatabases, nil, "database", "config", "rng")
module = modules.Register("core", nil, start, nil, "base", "subsystems", "status", "updates", "api", "notifications", "ui") // For prettier subsystem graph, printed with --print-subsystem-graph
/*
subsystems.Register(
"base",
"Base",
"THE GROUND.",
baseModule,
"",
nil,
)
*/
module = modules.Register("core", prep, start, nil, "base", "subsystems", "status", "updates", "api", "notifications", "ui", "netenv", "network", "interception")
subsystems.Register( subsystems.Register(
"core", "core",
"Core", "Core",
@@ -31,10 +43,19 @@ func init() {
) )
} }
func prep() error {
registerEvents()
return nil
}
func start() error { func start() error {
if err := startPlatformSpecific(); err != nil { if err := startPlatformSpecific(); err != nil {
return fmt.Errorf("failed to start plattform-specific components: %s", err) return fmt.Errorf("failed to start plattform-specific components: %s", err)
} }
if err := registerEventHooks(); err != nil {
return err
}
return nil return nil
} }

View File

@@ -46,5 +46,5 @@ func registerDatabases() error {
// return err // return err
// } // }
return nil return registerControlDatabase()
} }

46
core/events.go Normal file
View File

@@ -0,0 +1,46 @@
package core
import (
"context"
"github.com/safing/portbase/log"
"github.com/safing/portbase/modules"
)
const (
eventShutdown = "shutdown"
eventRestart = "restart"
restartCode = 23
)
func registerEvents() {
module.RegisterEvent(eventShutdown)
module.RegisterEvent(eventRestart)
}
func registerEventHooks() error {
err := module.RegisterEventHook("core", eventShutdown, "execute shutdown", shutdown)
if err != nil {
return err
}
err = module.RegisterEventHook("core", eventRestart, "execute shutdown", restart)
if err != nil {
return err
}
return nil
}
func shutdown(ctx context.Context, _ interface{}) error {
log.Warning("core: user requested shutdown")
go modules.Shutdown() //nolint:errcheck
return nil
}
func restart(ctx context.Context, data interface{}) error {
log.Info("core: user requested restart")
modules.SetExitStatusCode(restartCode)
go modules.Shutdown() //nolint:errcheck
return nil
}

View File

@@ -1,16 +1,23 @@
package firewall package firewall
import ( import (
"github.com/safing/portbase/api"
"github.com/safing/portbase/config" "github.com/safing/portbase/config"
"github.com/safing/portmaster/core"
) )
// Configuration Keys // Configuration Keys
var ( var (
CfgOptionEnableFilterKey = "filter/enable" CfgOptionEnableFilterKey = "filter/enable"
CfgOptionPromptTimeoutKey = "filter/promptTimeout" CfgOptionAskWithSystemNotificationsKey = "filter/askWithSystemNotifications"
CfgOptionPromptTimeoutOrder = 2 CfgOptionAskWithSystemNotificationsOrder = 2
promptTimeout config.IntOption askWithSystemNotifications config.BoolOption
useSystemNotifications config.BoolOption
CfgOptionAskTimeoutKey = "filter/askTimeout"
CfgOptionAskTimeoutOrder = 3
askTimeout config.IntOption
CfgOptionPermanentVerdictsKey = "filter/permanentVerdicts" CfgOptionPermanentVerdictsKey = "filter/permanentVerdicts"
CfgOptionPermanentVerdictsOrder = 128 CfgOptionPermanentVerdictsOrder = 128
@@ -37,22 +44,38 @@ func registerConfig() error {
permanentVerdicts = config.Concurrent.GetAsBool(CfgOptionPermanentVerdictsKey, true) permanentVerdicts = config.Concurrent.GetAsBool(CfgOptionPermanentVerdictsKey, true)
err = config.Register(&config.Option{ err = config.Register(&config.Option{
Name: "Timeout for prompt notifications", Name: "Ask with System Notifications",
Key: CfgOptionPromptTimeoutKey, Key: CfgOptionAskWithSystemNotificationsKey,
Description: "Amount of time how long Portmaster will wait for a response when prompting about a connection via a notification. In seconds.", Description: `Ask about connections using your operating system's notification system. For this to be enabled, the setting "Use System Notifications" must enabled too. This only affects questions from the Privacy Filter, and does not affect alerts from the Portmaster.`,
Order: CfgOptionPromptTimeoutOrder, Order: CfgOptionAskWithSystemNotificationsOrder,
OptType: config.OptTypeBool,
ExpertiseLevel: config.ExpertiseLevelUser,
ReleaseLevel: config.ReleaseLevelStable,
DefaultValue: true,
})
if err != nil {
return err
}
askWithSystemNotifications = config.Concurrent.GetAsBool(CfgOptionAskWithSystemNotificationsKey, true)
useSystemNotifications = config.Concurrent.GetAsBool(core.CfgUseSystemNotificationsKey, true)
err = config.Register(&config.Option{
Name: "Timeout for Ask Notifications",
Key: CfgOptionAskTimeoutKey,
Description: "Amount of time (in seconds) how long the Portmaster will wait for a response when prompting about a connection via a notification. Please note that system notifications might not respect this or have it's own limits.",
Order: CfgOptionAskTimeoutOrder,
OptType: config.OptTypeInt, OptType: config.OptTypeInt,
ExpertiseLevel: config.ExpertiseLevelUser, ExpertiseLevel: config.ExpertiseLevelUser,
ReleaseLevel: config.ReleaseLevelBeta, ReleaseLevel: config.ReleaseLevelStable,
DefaultValue: 60, DefaultValue: 60,
}) })
if err != nil { if err != nil {
return err return err
} }
promptTimeout = config.Concurrent.GetAsInt(CfgOptionPromptTimeoutKey, 60) askTimeout = config.Concurrent.GetAsInt(CfgOptionAskTimeoutKey, 60)
devMode = config.Concurrent.GetAsBool("core/devMode", false) devMode = config.Concurrent.GetAsBool(core.CfgDevModeKey, false)
apiListenAddress = config.GetAsString("api/listenAddress", "") apiListenAddress = config.GetAsString(api.CfgDefaultListenAddressKey, "")
return nil return nil
} }

47
firewall/filter.go Normal file
View File

@@ -0,0 +1,47 @@
package firewall
import (
"github.com/safing/portbase/config"
"github.com/safing/portbase/modules/subsystems"
"github.com/safing/portbase/modules"
// module dependencies
_ "github.com/safing/portmaster/core"
_ "github.com/safing/portmaster/profile"
)
var (
filterModule *modules.Module
filterEnabled config.BoolOption
)
func init() {
filterModule = modules.Register("filter", filterPrep, nil, nil, "core", "intel")
subsystems.Register(
"filter",
"Privacy Filter",
"DNS and Network Filter",
filterModule,
"config:filter/",
&config.Option{
Name: "Enable Privacy Filter",
Key: CfgOptionEnableFilterKey,
Description: "Enable the Privacy Filter Subsystem to filter DNS queries and network requests.",
OptType: config.OptTypeBool,
ExpertiseLevel: config.ExpertiseLevelUser,
ReleaseLevel: config.ReleaseLevelBeta,
DefaultValue: true,
},
)
}
func filterPrep() (err error) {
err = registerConfig()
if err != nil {
return err
}
filterEnabled = config.GetAsBool(CfgOptionEnableFilterKey, true)
return nil
}

View File

@@ -7,9 +7,6 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/safing/portbase/config"
"github.com/safing/portbase/modules/subsystems"
"github.com/safing/portbase/log" "github.com/safing/portbase/log"
"github.com/safing/portbase/modules" "github.com/safing/portbase/modules"
"github.com/safing/portmaster/firewall/inspection" "github.com/safing/portmaster/firewall/inspection"
@@ -23,7 +20,7 @@ import (
) )
var ( var (
module *modules.Module interceptionModule *modules.Module
// localNet net.IPNet // localNet net.IPNet
// localhost net.IP // localhost net.IP
@@ -45,33 +42,12 @@ var (
) )
func init() { func init() {
module = modules.Register("filter", prep, start, stop, "core", "network", "nameserver", "intel") interceptionModule = modules.Register("interception", interceptionPrep, interceptionStart, interceptionStop, "base")
subsystems.Register(
"filter",
"Privacy Filter",
"DNS and Network Filter",
module,
"config:filter/",
&config.Option{
Name: "Enable Privacy Filter",
Key: CfgOptionEnableFilterKey,
Description: "Enable the Privacy Filter Subsystem to filter DNS queries and network requests.",
OptType: config.OptTypeBool,
ExpertiseLevel: config.ExpertiseLevelUser,
ReleaseLevel: config.ReleaseLevelBeta,
DefaultValue: true,
},
)
network.SetDefaultFirewallHandler(defaultHandler) network.SetDefaultFirewallHandler(defaultHandler)
} }
func prep() (err error) { func interceptionPrep() (err error) {
err = registerConfig()
if err != nil {
return err
}
err = prepAPIAuth() err = prepAPIAuth()
if err != nil { if err != nil {
return err return err
@@ -101,20 +77,20 @@ func prep() (err error) {
return nil return nil
} }
func start() error { func interceptionStart() error {
startAPIAuth() startAPIAuth()
module.StartWorker("stat logger", func(ctx context.Context) error { interceptionModule.StartWorker("stat logger", func(ctx context.Context) error {
statLogger() statLogger()
return nil return nil
}) })
module.StartWorker("packet handler", func(ctx context.Context) error { interceptionModule.StartWorker("packet handler", func(ctx context.Context) error {
run() run()
return nil return nil
}) })
module.StartWorker("ports state cleaner", func(ctx context.Context) error { interceptionModule.StartWorker("ports state cleaner", func(ctx context.Context) error {
portsInUseCleaner() portsInUseCleaner()
return nil return nil
}) })
@@ -122,7 +98,7 @@ func start() error {
return interception.Start() return interception.Start()
} }
func stop() error { func interceptionStop() error {
return interception.Stop() return interception.Stop()
} }
@@ -248,6 +224,15 @@ func initialHandler(conn *network.Connection, pkt packet.Packet) {
return return
} }
// check if filtering is enabled
if !filterEnabled() {
conn.Inspecting = false
conn.SetVerdict(network.VerdictAccept, "privacy filter disabled", nil)
conn.StopFirewallHandler()
issueVerdict(conn, pkt, 0, true)
return
}
log.Tracer(pkt.Ctx()).Trace("filter: starting decision process") log.Tracer(pkt.Ctx()).Trace("filter: starting decision process")
DecideOnConnection(conn, pkt) DecideOnConnection(conn, pkt)
conn.Inspecting = false // TODO: enable inspecting again conn.Inspecting = false // TODO: enable inspecting again
@@ -350,7 +335,7 @@ func issueVerdict(conn *network.Connection, pkt packet.Packet, verdict network.V
func run() { func run() {
for { for {
select { select {
case <-module.Stopping(): case <-interceptionModule.Stopping():
return return
case pkt := <-interception.Packets: case pkt := <-interception.Packets:
handlePacket(pkt) handlePacket(pkt)
@@ -361,7 +346,7 @@ func run() {
func statLogger() { func statLogger() {
for { for {
select { select {
case <-module.Stopping(): case <-interceptionModule.Stopping():
return return
case <-time.After(10 * time.Second): case <-time.After(10 * time.Second):
log.Tracef( log.Tracef(

View File

@@ -45,14 +45,14 @@ func init() {
"mangle C171 -m mark --mark 0 -j NFQUEUE --queue-num 17140 --queue-bypass", "mangle C171 -m mark --mark 0 -j NFQUEUE --queue-num 17140 --queue-bypass",
"filter C17 -m mark --mark 0 -j DROP", "filter C17 -m mark --mark 0 -j DROP",
"filter C17 -m mark --mark 1700 -j ACCEPT", "filter C17 -m mark --mark 1700 -j RETURN",
"filter C17 -m mark --mark 1701 -j REJECT --reject-with icmp-host-prohibited", "filter C17 -m mark --mark 1701 -j REJECT --reject-with icmp-host-prohibited",
"filter C17 -m mark --mark 1702 -j DROP", "filter C17 -m mark --mark 1702 -j DROP",
"filter C17 -j CONNMARK --save-mark", "filter C17 -j CONNMARK --save-mark",
"filter C17 -m mark --mark 1710 -j ACCEPT", "filter C17 -m mark --mark 1710 -j RETURN",
"filter C17 -m mark --mark 1711 -j REJECT --reject-with icmp-host-prohibited", "filter C17 -m mark --mark 1711 -j REJECT --reject-with icmp-host-prohibited",
"filter C17 -m mark --mark 1712 -j DROP", "filter C17 -m mark --mark 1712 -j DROP",
"filter C17 -m mark --mark 1717 -j ACCEPT", "filter C17 -m mark --mark 1717 -j RETURN",
} }
v4once = []string{ v4once = []string{
@@ -80,14 +80,14 @@ func init() {
"mangle C171 -m mark --mark 0 -j NFQUEUE --queue-num 17160 --queue-bypass", "mangle C171 -m mark --mark 0 -j NFQUEUE --queue-num 17160 --queue-bypass",
"filter C17 -m mark --mark 0 -j DROP", "filter C17 -m mark --mark 0 -j DROP",
"filter C17 -m mark --mark 1700 -j ACCEPT", "filter C17 -m mark --mark 1700 -j RETURN",
"filter C17 -m mark --mark 1701 -j REJECT --reject-with icmp6-adm-prohibited", "filter C17 -m mark --mark 1701 -j REJECT --reject-with icmp6-adm-prohibited",
"filter C17 -m mark --mark 1702 -j DROP", "filter C17 -m mark --mark 1702 -j DROP",
"filter C17 -j CONNMARK --save-mark", "filter C17 -j CONNMARK --save-mark",
"filter C17 -m mark --mark 1710 -j ACCEPT", "filter C17 -m mark --mark 1710 -j RETURN",
"filter C17 -m mark --mark 1711 -j REJECT --reject-with icmp6-adm-prohibited", "filter C17 -m mark --mark 1711 -j REJECT --reject-with icmp6-adm-prohibited",
"filter C17 -m mark --mark 1712 -j DROP", "filter C17 -m mark --mark 1712 -j DROP",
"filter C17 -m mark --mark 1717 -j ACCEPT", "filter C17 -m mark --mark 1717 -j RETURN",
} }
v6once = []string{ v6once = []string{

View File

@@ -163,8 +163,8 @@ func checkConnectionType(conn *network.Connection, _ packet.Packet) bool {
} }
return true return true
} }
case network.PeerLAN, network.PeerInternet, network.PeerInvalid: case network.PeerInternet:
// Important: PeerHost is and should be missing! // BlockP2P only applies to connections to the Internet
if p.BlockP2P() { if p.BlockP2P() {
conn.Block("direct connections (P2P) blocked") conn.Block("direct connections (P2P) blocked")
return true return true

View File

@@ -72,7 +72,7 @@ func GetPermittedPort() uint16 {
func portsInUseCleaner() { func portsInUseCleaner() {
for { for {
select { select {
case <-module.Stopping(): case <-interceptionModule.Stopping():
return return
case <-time.After(cleanerTickDuration): case <-time.After(cleanerTickDuration):
cleanPortsInUse() cleanPortsInUse()

View File

@@ -26,16 +26,16 @@ const (
) )
func prompt(conn *network.Connection, pkt packet.Packet) { //nolint:gocognit // TODO func prompt(conn *network.Connection, pkt packet.Packet) { //nolint:gocognit // TODO
nTTL := time.Duration(promptTimeout()) * time.Second nTTL := time.Duration(askTimeout()) * time.Second
// first check if there is an existing notification for this. // first check if there is an existing notification for this.
// build notification ID // build notification ID
var nID string var nID string
switch { switch {
case conn.Inbound, conn.Entity.Domain == "": // connection to/from IP case conn.Inbound, conn.Entity.Domain == "": // connection to/from IP
nID = fmt.Sprintf("firewall-prompt-%d-%s-%s", conn.Process().Pid, conn.Scope, pkt.Info().RemoteIP()) nID = fmt.Sprintf("filter:prompt-%d-%s-%s", conn.Process().Pid, conn.Scope, pkt.Info().RemoteIP())
default: // connection to domain default: // connection to domain
nID = fmt.Sprintf("firewall-prompt-%d-%s", conn.Process().Pid, conn.Scope) nID = fmt.Sprintf("filter:prompt-%d-%s", conn.Process().Pid, conn.Scope)
} }
n := notifications.Get(nID) n := notifications.Get(nID)
saveResponse := true saveResponse := true

View File

@@ -33,7 +33,7 @@ var (
func init() { func init() {
ignoreNetEnvEvents.Set() ignoreNetEnvEvents.Set()
module = modules.Register("filterlists", prep, start, nil, "core", "netenv") module = modules.Register("filterlists", prep, start, stop, "core")
} }
func prep() error { func prep() error {
@@ -98,3 +98,8 @@ func start() error {
return nil return nil
} }
func stop() error {
filterListsLoaded = make(chan struct{})
return nil
}

View File

@@ -32,7 +32,7 @@ var (
) )
func init() { func init() {
module = modules.Register("nameserver", prep, start, stop, "core", "resolver", "network", "netenv") module = modules.Register("nameserver", prep, start, stop, "core", "resolver")
subsystems.Register( subsystems.Register(
"dns", "dns",
"Secure DNS", "Secure DNS",

View File

@@ -41,8 +41,14 @@ func cleanConnections() (activePIDs map[int]struct{}) {
now := time.Now().Unix() now := time.Now().Unix()
deleteOlderThan := time.Now().Add(-deleteConnsAfterEndedThreshold).Unix() deleteOlderThan := time.Now().Add(-deleteConnsAfterEndedThreshold).Unix()
// network connections // lock both together because we cannot fully guarantee in which map a connection lands
// of course every connection should land in the correct map, but this increases resilience
connsLock.Lock() connsLock.Lock()
defer connsLock.Unlock()
dnsConnsLock.Lock()
defer dnsConnsLock.Unlock()
// network connections
for key, conn := range conns { for key, conn := range conns {
conn.Lock() conn.Lock()
@@ -67,10 +73,8 @@ func cleanConnections() (activePIDs map[int]struct{}) {
conn.Unlock() conn.Unlock()
} }
connsLock.Unlock()
// dns requests // dns requests
dnsConnsLock.Lock()
for _, conn := range dnsConns { for _, conn := range dnsConns {
conn.Lock() conn.Lock()
@@ -82,7 +86,6 @@ func cleanConnections() (activePIDs map[int]struct{}) {
conn.Unlock() conn.Unlock()
} }
dnsConnsLock.Unlock()
return nil return nil
}) })

View File

@@ -146,13 +146,13 @@ func NewConnectionFromFirstPacket(pkt packet.Packet) *Connection {
} }
} }
timestamp := time.Now().Unix()
return &Connection{ return &Connection{
ID: pkt.GetConnectionID(), ID: pkt.GetConnectionID(),
Scope: scope, Scope: scope,
Inbound: inbound,
Entity: entity, Entity: entity,
process: proc, process: proc,
Started: timestamp, Started: time.Now().Unix(),
} }
} }
@@ -236,11 +236,11 @@ func (conn *Connection) Failed(reason 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 string, ctx interface{}) (ok bool) { func (conn *Connection) SetVerdict(newVerdict Verdict, reason string, reasonCtx interface{}) (ok bool) {
if newVerdict >= conn.Verdict { if newVerdict >= conn.Verdict {
conn.Verdict = newVerdict conn.Verdict = newVerdict
conn.Reason = reason conn.Reason = reason
conn.ReasonContext = ctx conn.ReasonContext = reasonCtx
return true return true
} }
return false return false

View File

@@ -77,9 +77,11 @@ func (s *StorageInterface) processQuery(q *query.Query, it *iterator.Iterator) {
if slashes <= 1 { if slashes <= 1 {
// processes // processes
for _, proc := range process.All() { for _, proc := range process.All() {
proc.Lock()
if q.Matches(proc) { if q.Matches(proc) {
it.Next <- proc it.Next <- proc
} }
proc.Unlock()
} }
} }
@@ -87,9 +89,11 @@ func (s *StorageInterface) processQuery(q *query.Query, it *iterator.Iterator) {
// dns scopes only // dns scopes only
dnsConnsLock.RLock() dnsConnsLock.RLock()
for _, dnsConn := range dnsConns { for _, dnsConn := range dnsConns {
dnsConn.Lock()
if q.Matches(dnsConn) { if q.Matches(dnsConn) {
it.Next <- dnsConn it.Next <- dnsConn
} }
dnsConn.Unlock()
} }
dnsConnsLock.RUnlock() dnsConnsLock.RUnlock()
} }
@@ -98,9 +102,11 @@ func (s *StorageInterface) processQuery(q *query.Query, it *iterator.Iterator) {
// connections // connections
connsLock.RLock() connsLock.RLock()
for _, conn := range conns { for _, conn := range conns {
conn.Lock()
if q.Matches(conn) { if q.Matches(conn) {
it.Next <- conn it.Next <- conn
} }
conn.Unlock()
} }
connsLock.RUnlock() connsLock.RUnlock()
} }

View File

@@ -16,7 +16,7 @@ var (
) )
func init() { func init() {
module = modules.Register("network", nil, start, nil, "core", "processes") module = modules.Register("network", nil, start, nil, "base", "processes")
} }
// SetDefaultFirewallHandler sets the default firewall handler. // SetDefaultFirewallHandler sets the default firewall handler.

View File

@@ -14,7 +14,7 @@ const (
) )
// ClassifyIP returns the classification for the given IP address. // ClassifyIP returns the classification for the given IP address.
func ClassifyIP(ip net.IP) int8 { func ClassifyIP(ip net.IP) int8 { //nolint:gocognit
if ip4 := ip.To4(); ip4 != nil { if ip4 := ip.To4(); ip4 != nil {
// IPv4 // IPv4
switch { switch {
@@ -36,11 +36,18 @@ func ClassifyIP(ip net.IP) int8 {
case ip4[0] == 224: case ip4[0] == 224:
// 224.0.0.0/8 // 224.0.0.0/8
return LocalMulticast return LocalMulticast
case ip4[0] >= 225 && ip4[0] <= 239: case ip4[0] >= 225 && ip4[0] <= 238:
// 225.0.0.0/8 - 239.0.0.0/8 // 225.0.0.0/8 - 238.0.0.0/8
return GlobalMulticast return GlobalMulticast
case ip4[0] == 239:
// 239.0.0.0/8
// RFC2365 - https://tools.ietf.org/html/rfc2365
return LocalMulticast
case ip4[0] == 255 && ip4[1] == 255 && ip4[2] == 255 && ip4[3] == 255:
// 255.255.255.255/32
return LocalMulticast
case ip4[0] >= 240: case ip4[0] >= 240:
// 240.0.0.0/8 - 255.0.0.0/8 // 240.0.0.0/8 - 255.0.0.0/8 (minus 255.255.255.255/32)
return Invalid return Invalid
default: default:
return Global return Global

View File

@@ -17,6 +17,10 @@ import (
"github.com/tevino/abool" "github.com/tevino/abool"
) )
const (
restartCode = 23
)
var ( var (
runningInConsole bool runningInConsole bool
onWindows = runtime.GOOS == "windows" onWindows = runtime.GOOS == "windows"
@@ -375,7 +379,7 @@ func execute(opts *Options, args []string) (cont bool, err error) {
case 1: case 1:
// error exit // error exit
return true, fmt.Errorf("error during execution: %s", err) return true, fmt.Errorf("error during execution: %s", err)
case 2357427: // Leet Speak for "restart" case restartCode:
// restart request // restart request
log.Printf("restarting %s\n", opts.Identifier) log.Printf("restarting %s\n", opts.Identifier)
return true, nil return true, nil

View File

@@ -17,6 +17,7 @@ func registerConfiguration() error {
Name: "Enable Process Detection", Name: "Enable Process Detection",
Key: CfgOptionEnableProcessDetectionKey, Key: CfgOptionEnableProcessDetectionKey,
Description: "This option enables the attribution of network traffic to processes. This should be always enabled, and effectively disables app profiles if disabled.", Description: "This option enables the attribution of network traffic to processes. This should be always enabled, and effectively disables app profiles if disabled.",
Order: 144,
OptType: config.OptTypeBool, OptType: config.OptTypeBool,
ExpertiseLevel: config.ExpertiseLevelDeveloper, ExpertiseLevel: config.ExpertiseLevelDeveloper,
DefaultValue: true, DefaultValue: true,

View File

@@ -5,6 +5,8 @@ import (
"fmt" "fmt"
"sync" "sync"
"github.com/safing/portbase/config"
"github.com/safing/portmaster/intel/filterlists" "github.com/safing/portmaster/intel/filterlists"
"github.com/safing/portmaster/profile/endpoints" "github.com/safing/portmaster/profile/endpoints"
) )
@@ -77,20 +79,24 @@ func updateGlobalConfigProfile(ctx context.Context, data interface{}) error {
internalSave: true, internalSave: true,
} }
newConfig := make(map[string]interface{})
// fill profile config options // fill profile config options
for key, value := range cfgStringOptions { for key, value := range cfgStringOptions {
profile.Config[key] = value() newConfig[key] = value()
} }
for key, value := range cfgStringArrayOptions { for key, value := range cfgStringArrayOptions {
profile.Config[key] = value() newConfig[key] = value()
} }
for key, value := range cfgIntOptions { for key, value := range cfgIntOptions {
profile.Config[key] = value() newConfig[key] = value()
} }
for key, value := range cfgBoolOptions { for key, value := range cfgBoolOptions {
profile.Config[key] = value() newConfig[key] = value()
} }
// expand and assign
profile.Config = config.Expand(newConfig)
// save profile // save profile
err = profile.Save() err = profile.Save()
if err != nil && lastErr == nil { if err != nil && lastErr == nil {

View File

@@ -131,7 +131,7 @@ func registerConfiguration() error {
"+": permit "+": permit
"-": block "-": block
Host Matching: Host Matching:
IP, CIDR, Country Code, ASN, "*" for any IP, CIDR, Country Code, ASN, Filterlist, "*" for any
Domains: Domains:
"example.com": exact match "example.com": exact match
".example.com": exact match + subdomains ".example.com": exact match + subdomains
@@ -144,7 +144,11 @@ func registerConfiguration() error {
Examples: Examples:
+ .example.com */HTTP + .example.com */HTTP
- .example.com - .example.com
+ 192.168.0.1/24`, + 192.168.0.1/24
- L:MAL
- AS0
+ AT
- *`,
Order: cfgOptionEndpointsOrder, Order: cfgOptionEndpointsOrder,
OptType: config.OptTypeStringArray, OptType: config.OptTypeStringArray,
DefaultValue: []string{}, DefaultValue: []string{},
@@ -167,7 +171,7 @@ Examples:
"+": permit "+": permit
"-": block "-": block
Host Matching: Host Matching:
IP, CIDR, Country Code, ASN, "*" for any IP, CIDR, Country Code, ASN, Filterlist, "*" for any
Domains: Domains:
"example.com": exact match "example.com": exact match
".example.com": exact match + subdomains ".example.com": exact match + subdomains
@@ -180,7 +184,11 @@ Examples:
Examples: Examples:
+ .example.com */HTTP + .example.com */HTTP
- .example.com - .example.com
+ 192.168.0.1/24`, + 192.168.0.1/24
- L:MAL
- AS0
+ AT
- *`,
Order: cfgOptionServiceEndpointsOrder, Order: cfgOptionServiceEndpointsOrder,
OptType: config.OptTypeStringArray, OptType: config.OptTypeStringArray,
DefaultValue: []string{}, DefaultValue: []string{},
@@ -249,9 +257,10 @@ Examples:
err = config.Register(&config.Option{ err = config.Register(&config.Option{
Name: "Block Scope Local", Name: "Block Scope Local",
Key: CfgOptionBlockScopeLocalKey, Key: CfgOptionBlockScopeLocalKey,
Description: "Block connections to your own device, ie. localhost.", Description: "Block internal connections on your own device, ie. localhost.",
Order: cfgOptionBlockScopeLocalOrder, Order: cfgOptionBlockScopeLocalOrder,
OptType: config.OptTypeInt, OptType: config.OptTypeInt,
ExpertiseLevel: config.ExpertiseLevelExpert,
ExternalOptType: "security level", ExternalOptType: "security level",
DefaultValue: status.SecurityLevelOff, DefaultValue: status.SecurityLevelOff,
ValidationRegex: "^(0|4|6|7)$", ValidationRegex: "^(0|4|6|7)$",
@@ -270,7 +279,7 @@ Examples:
Order: cfgOptionBlockScopeLANOrder, Order: cfgOptionBlockScopeLANOrder,
OptType: config.OptTypeInt, OptType: config.OptTypeInt,
ExternalOptType: "security level", ExternalOptType: "security level",
DefaultValue: status.SecurityLevelOff, DefaultValue: status.SecurityLevelsHighAndExtreme,
ValidationRegex: "^(0|4|6|7)$", ValidationRegex: "^(0|4|6|7)$",
}) })
if err != nil { if err != nil {
@@ -300,7 +309,7 @@ Examples:
err = config.Register(&config.Option{ err = config.Register(&config.Option{
Name: "Block Peer to Peer Connections", Name: "Block Peer to Peer Connections",
Key: CfgOptionBlockP2PKey, Key: CfgOptionBlockP2PKey,
Description: "Block peer to peer connections. These are connections that are established directly to an IP address on the Internet without resolving a domain name via DNS first.", Description: "These are connections that are established directly to an IP address on the Internet without resolving a domain name via DNS first.",
Order: cfgOptionBlockP2POrder, Order: cfgOptionBlockP2POrder,
OptType: config.OptTypeInt, OptType: config.OptTypeInt,
ExternalOptType: "security level", ExternalOptType: "security level",
@@ -317,7 +326,7 @@ Examples:
err = config.Register(&config.Option{ err = config.Register(&config.Option{
Name: "Block Inbound Connections", Name: "Block Inbound Connections",
Key: CfgOptionBlockInboundKey, Key: CfgOptionBlockInboundKey,
Description: "Block inbound connections to your device. This will usually only be the case if you are running a network service or are using peer to peer software.", Description: "Connections initiated towards your device. This will usually only be the case if you are running a network service or are using peer to peer software.",
Order: cfgOptionBlockInboundOrder, Order: cfgOptionBlockInboundOrder,
OptType: config.OptTypeInt, OptType: config.OptTypeInt,
ExternalOptType: "security level", ExternalOptType: "security level",

View File

@@ -5,6 +5,8 @@ import (
"errors" "errors"
"strings" "strings"
"github.com/safing/portbase/config"
"github.com/safing/portbase/database" "github.com/safing/portbase/database"
"github.com/safing/portbase/database/query" "github.com/safing/portbase/database/query"
"github.com/safing/portbase/database/record" "github.com/safing/portbase/database/record"
@@ -87,6 +89,9 @@ func (h *databaseHook) PrePut(r record.Record) (record.Record, error) {
return nil, err return nil, err
} }
// clean config
config.CleanHierarchicalConfig(profile.Config)
// prepare config // prepare config
err = profile.prepConfig() err = profile.prepConfig()
if err != nil { if err != nil {

View File

@@ -14,7 +14,7 @@ var (
) )
func init() { func init() {
module = modules.Register("profiles", prep, start, nil, "core") module = modules.Register("profiles", prep, start, nil, "base")
} }
func prep() error { func prep() error {

View File

@@ -237,7 +237,7 @@ func (profile *Profile) addEndpointyEntry(cfgKey, newEntry string) {
endpointList = make([]string, 0, 1) endpointList = make([]string, 0, 1)
} }
endpointList = append(endpointList, newEntry) endpointList = append(endpointList, newEntry)
profile.Config[cfgKey] = endpointList config.PutValueIntoHierarchicalConfig(profile.Config, cfgKey, endpointList)
profile.Unlock() profile.Unlock()
err := profile.Save() err := profile.Save()

View File

@@ -37,8 +37,6 @@ func start() error {
// load status into atomic getters // load status into atomic getters
atomicUpdateSelectedSecurityLevel(status.SelectedSecurityLevel) atomicUpdateSelectedSecurityLevel(status.SelectedSecurityLevel)
atomicUpdatePortmasterStatus(status.PortmasterStatus)
atomicUpdateGate17Status(status.Gate17Status)
// update status // update status
status.updateThreatMitigationLevel() status.updateThreatMitigationLevel()

View File

@@ -45,40 +45,6 @@ func setSelectedSecurityLevel(level uint8) {
} }
} }
// SetPortmasterStatus sets the current Portmaster status.
func SetPortmasterStatus(pmStatus uint8, msg string) {
switch pmStatus {
case StatusOff, StatusError, StatusWarning, StatusOk:
status.Lock()
defer status.Unlock()
status.PortmasterStatus = pmStatus
status.PortmasterStatusMsg = msg
atomicUpdatePortmasterStatus(pmStatus)
go status.Save()
default:
log.Errorf("status: tried to set portmaster to invalid status: %d", pmStatus)
}
}
// SetGate17Status sets the current Gate17 status.
func SetGate17Status(g17Status uint8, msg string) {
switch g17Status {
case StatusOff, StatusError, StatusWarning, StatusOk:
status.Lock()
defer status.Unlock()
status.Gate17Status = g17Status
status.Gate17StatusMsg = msg
atomicUpdateGate17Status(g17Status)
go status.Save()
default:
log.Errorf("status: tried to set gate17 to invalid status: %d", g17Status)
}
}
// update functions for atomic stuff // update functions for atomic stuff
func atomicUpdateActiveSecurityLevel(level uint8) { func atomicUpdateActiveSecurityLevel(level uint8) {
atomic.StoreUint32(activeSecurityLevel, uint32(level)) atomic.StoreUint32(activeSecurityLevel, uint32(level))
@@ -87,11 +53,3 @@ func atomicUpdateActiveSecurityLevel(level uint8) {
func atomicUpdateSelectedSecurityLevel(level uint8) { func atomicUpdateSelectedSecurityLevel(level uint8) {
atomic.StoreUint32(selectedSecurityLevel, uint32(level)) atomic.StoreUint32(selectedSecurityLevel, uint32(level))
} }
func atomicUpdatePortmasterStatus(status uint8) {
atomic.StoreUint32(portmasterStatus, uint32(status))
}
func atomicUpdateGate17Status(status uint8) {
atomic.StoreUint32(gate17Status, uint32(status))
}

View File

@@ -7,7 +7,5 @@ func TestSet(t *testing.T) {
// only test for panics // only test for panics
// TODO: write real tests // TODO: write real tests
setSelectedSecurityLevel(0) setSelectedSecurityLevel(0)
SetPortmasterStatus(0, "")
SetGate17Status(0, "")
} }

View File

@@ -28,16 +28,8 @@ type SystemStatus struct {
ActiveSecurityLevel uint8 ActiveSecurityLevel uint8
SelectedSecurityLevel uint8 SelectedSecurityLevel uint8
PortmasterStatus uint8
PortmasterStatusMsg string
Gate17Status uint8
Gate17StatusMsg string
ThreatMitigationLevel uint8 ThreatMitigationLevel uint8
Threats map[string]*Threat Threats map[string]*Threat
UpdateStatus string
} }
// Save saves the SystemStatus to the database // Save saves the SystemStatus to the database

View File

@@ -1,18 +0,0 @@
package status
// Update status options
const (
UpdateStatusCurrentStable = "stable"
UpdateStatusCurrentBeta = "beta"
UpdateStatusAvailable = "available" // restart or reboot required
UpdateStatusFailed = "failed" // check logs
)
// SetUpdateStatus updates the system status with a new update status.
func SetUpdateStatus(newStatus string) {
status.Lock()
status.UpdateStatus = newStatus
status.Unlock()
go status.Save()
}

View File

@@ -1,13 +1,51 @@
package ui package ui
import ( import (
"context"
resources "github.com/cookieo9/resources-go"
"github.com/safing/portbase/log"
"github.com/safing/portbase/modules" "github.com/safing/portbase/modules"
) )
const (
eventReload = "reload"
)
var (
module *modules.Module
)
func init() { func init() {
modules.Register("ui", prep, nil, nil, "api", "updates") module = modules.Register("ui", prep, start, nil, "api", "updates")
} }
func prep() error { func prep() error {
module.RegisterEvent(eventReload)
return registerRoutes() return registerRoutes()
} }
func start() error {
return module.RegisterEventHook("ui", eventReload, "reload assets", reloadUI)
}
func reloadUI(ctx context.Context, _ interface{}) error {
log.Info("core: user/UI requested UI reload")
appsLock.Lock()
defer appsLock.Unlock()
// close all bundles
for id, bundle := range apps {
err := bundle.Close()
if err != nil {
log.Warningf("ui: failed to close bundle %s: %s", id, err)
}
}
// reset index
apps = make(map[string]*resources.BundleSequence)
return nil
}

View File

@@ -8,6 +8,10 @@ import (
"github.com/safing/portbase/log" "github.com/safing/portbase/log"
) )
const (
cfgDevModeKey = "core/devMode"
)
var ( var (
releaseChannel config.StringOption releaseChannel config.StringOption
devMode config.BoolOption devMode config.BoolOption
@@ -23,6 +27,7 @@ func registerConfig() error {
Name: "Release Channel", Name: "Release Channel",
Key: releaseChannelKey, Key: releaseChannelKey,
Description: "The Release Channel changes which updates are applied. When using beta, you will receive new features earlier and Portmaster will update more frequently. Some beta or experimental features are also available in the stable release channel.", Description: "The Release Channel changes which updates are applied. When using beta, you will receive new features earlier and Portmaster will update more frequently. Some beta or experimental features are also available in the stable release channel.",
Order: 1,
OptType: config.OptTypeString, OptType: config.OptTypeString,
ExpertiseLevel: config.ExpertiseLevelExpert, ExpertiseLevel: config.ExpertiseLevelExpert,
ReleaseLevel: config.ReleaseLevelBeta, ReleaseLevel: config.ReleaseLevelBeta,
@@ -39,6 +44,7 @@ func registerConfig() error {
Name: "Disable Updates", Name: "Disable Updates",
Key: disableUpdatesKey, Key: disableUpdatesKey,
Description: "Disable automatic updates.", Description: "Disable automatic updates.",
Order: 64,
OptType: config.OptTypeBool, OptType: config.OptTypeBool,
ExpertiseLevel: config.ExpertiseLevelExpert, ExpertiseLevel: config.ExpertiseLevelExpert,
ReleaseLevel: config.ReleaseLevelStable, ReleaseLevel: config.ReleaseLevelStable,
@@ -55,9 +61,13 @@ func registerConfig() error {
func initConfig() { func initConfig() {
releaseChannel = config.GetAsString(releaseChannelKey, releaseChannelStable) releaseChannel = config.GetAsString(releaseChannelKey, releaseChannelStable)
disableUpdates = config.GetAsBool(disableUpdatesKey, false) previousReleaseChannel = releaseChannel()
devMode = config.GetAsBool("core/devMode", false) disableUpdates = config.GetAsBool(disableUpdatesKey, false)
updatesCurrentlyDisabled = disableUpdates()
devMode = config.GetAsBool(cfgDevModeKey, false)
previousDevMode = devMode()
} }
func updateRegistryConfig(_ context.Context, _ interface{}) error { func updateRegistryConfig(_ context.Context, _ interface{}) error {
@@ -90,8 +100,8 @@ func updateRegistryConfig(_ context.Context, _ interface{}) error {
module.Resolve(updateFailed) module.Resolve(updateFailed)
_ = TriggerUpdate() _ = TriggerUpdate()
log.Infof("updates: automatic updates enabled again.") log.Infof("updates: automatic updates enabled again.")
} else { } else if updatesCurrentlyDisabled {
module.Warning(updateFailed, "Updates are disabled!") module.Warning(updateFailed, "Automatic updates are disabled! This also affects security updates and threat intelligence.")
log.Warningf("updates: automatic updates are now disabled.") log.Warningf("updates: automatic updates are now disabled.")
} }
} }