mirror of
https://github.com/safing/portmaster
synced 2025-09-01 10:09:11 +00:00
Merge pull request #44 from safing/feature/pre-alpha-finalizing
Pre alpha finalizing
This commit is contained in:
commit
972f8fcfc3
33 changed files with 427 additions and 166 deletions
|
@ -11,6 +11,8 @@ import (
|
|||
var (
|
||||
CfgDevModeKey = "core/devMode"
|
||||
defaultDevMode bool
|
||||
|
||||
CfgUseSystemNotificationsKey = "core/useSystemNotifications"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -28,6 +30,7 @@ func registerConfig() error {
|
|||
Name: "Development Mode",
|
||||
Key: CfgDevModeKey,
|
||||
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,
|
||||
ExpertiseLevel: config.ExpertiseLevelDeveloper,
|
||||
ReleaseLevel: config.ReleaseLevelStable,
|
||||
|
@ -37,5 +40,19 @@ func registerConfig() error {
|
|||
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
|
||||
}
|
||||
|
|
100
core/control.go
Normal file
100
core/control.go
Normal 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
|
||||
}
|
23
core/core.go
23
core/core.go
|
@ -20,7 +20,19 @@ var (
|
|||
func init() {
|
||||
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(
|
||||
"core",
|
||||
"Core",
|
||||
|
@ -31,10 +43,19 @@ func init() {
|
|||
)
|
||||
}
|
||||
|
||||
func prep() error {
|
||||
registerEvents()
|
||||
return nil
|
||||
}
|
||||
|
||||
func start() error {
|
||||
if err := startPlatformSpecific(); err != nil {
|
||||
return fmt.Errorf("failed to start plattform-specific components: %s", err)
|
||||
}
|
||||
|
||||
if err := registerEventHooks(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -46,5 +46,5 @@ func registerDatabases() error {
|
|||
// return err
|
||||
// }
|
||||
|
||||
return nil
|
||||
return registerControlDatabase()
|
||||
}
|
||||
|
|
46
core/events.go
Normal file
46
core/events.go
Normal 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
|
||||
}
|
|
@ -1,16 +1,23 @@
|
|||
package firewall
|
||||
|
||||
import (
|
||||
"github.com/safing/portbase/api"
|
||||
"github.com/safing/portbase/config"
|
||||
"github.com/safing/portmaster/core"
|
||||
)
|
||||
|
||||
// Configuration Keys
|
||||
var (
|
||||
CfgOptionEnableFilterKey = "filter/enable"
|
||||
|
||||
CfgOptionPromptTimeoutKey = "filter/promptTimeout"
|
||||
CfgOptionPromptTimeoutOrder = 2
|
||||
promptTimeout config.IntOption
|
||||
CfgOptionAskWithSystemNotificationsKey = "filter/askWithSystemNotifications"
|
||||
CfgOptionAskWithSystemNotificationsOrder = 2
|
||||
askWithSystemNotifications config.BoolOption
|
||||
useSystemNotifications config.BoolOption
|
||||
|
||||
CfgOptionAskTimeoutKey = "filter/askTimeout"
|
||||
CfgOptionAskTimeoutOrder = 3
|
||||
askTimeout config.IntOption
|
||||
|
||||
CfgOptionPermanentVerdictsKey = "filter/permanentVerdicts"
|
||||
CfgOptionPermanentVerdictsOrder = 128
|
||||
|
@ -37,22 +44,38 @@ func registerConfig() error {
|
|||
permanentVerdicts = config.Concurrent.GetAsBool(CfgOptionPermanentVerdictsKey, true)
|
||||
|
||||
err = config.Register(&config.Option{
|
||||
Name: "Timeout for prompt notifications",
|
||||
Key: CfgOptionPromptTimeoutKey,
|
||||
Description: "Amount of time how long Portmaster will wait for a response when prompting about a connection via a notification. In seconds.",
|
||||
Order: CfgOptionPromptTimeoutOrder,
|
||||
Name: "Ask with System Notifications",
|
||||
Key: CfgOptionAskWithSystemNotificationsKey,
|
||||
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: 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,
|
||||
ExpertiseLevel: config.ExpertiseLevelUser,
|
||||
ReleaseLevel: config.ReleaseLevelBeta,
|
||||
ReleaseLevel: config.ReleaseLevelStable,
|
||||
DefaultValue: 60,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
promptTimeout = config.Concurrent.GetAsInt(CfgOptionPromptTimeoutKey, 60)
|
||||
askTimeout = config.Concurrent.GetAsInt(CfgOptionAskTimeoutKey, 60)
|
||||
|
||||
devMode = config.Concurrent.GetAsBool("core/devMode", false)
|
||||
apiListenAddress = config.GetAsString("api/listenAddress", "")
|
||||
devMode = config.Concurrent.GetAsBool(core.CfgDevModeKey, false)
|
||||
apiListenAddress = config.GetAsString(api.CfgDefaultListenAddressKey, "")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
47
firewall/filter.go
Normal file
47
firewall/filter.go
Normal 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
|
||||
}
|
|
@ -7,9 +7,6 @@ import (
|
|||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/config"
|
||||
"github.com/safing/portbase/modules/subsystems"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portbase/modules"
|
||||
"github.com/safing/portmaster/firewall/inspection"
|
||||
|
@ -23,7 +20,7 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
module *modules.Module
|
||||
interceptionModule *modules.Module
|
||||
|
||||
// localNet net.IPNet
|
||||
// localhost net.IP
|
||||
|
@ -45,33 +42,12 @@ var (
|
|||
)
|
||||
|
||||
func init() {
|
||||
module = modules.Register("filter", prep, start, stop, "core", "network", "nameserver", "intel")
|
||||
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,
|
||||
},
|
||||
)
|
||||
interceptionModule = modules.Register("interception", interceptionPrep, interceptionStart, interceptionStop, "base")
|
||||
|
||||
network.SetDefaultFirewallHandler(defaultHandler)
|
||||
}
|
||||
|
||||
func prep() (err error) {
|
||||
err = registerConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
func interceptionPrep() (err error) {
|
||||
err = prepAPIAuth()
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -101,20 +77,20 @@ func prep() (err error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
func start() error {
|
||||
func interceptionStart() error {
|
||||
startAPIAuth()
|
||||
|
||||
module.StartWorker("stat logger", func(ctx context.Context) error {
|
||||
interceptionModule.StartWorker("stat logger", func(ctx context.Context) error {
|
||||
statLogger()
|
||||
return nil
|
||||
})
|
||||
|
||||
module.StartWorker("packet handler", func(ctx context.Context) error {
|
||||
interceptionModule.StartWorker("packet handler", func(ctx context.Context) error {
|
||||
run()
|
||||
return nil
|
||||
})
|
||||
|
||||
module.StartWorker("ports state cleaner", func(ctx context.Context) error {
|
||||
interceptionModule.StartWorker("ports state cleaner", func(ctx context.Context) error {
|
||||
portsInUseCleaner()
|
||||
return nil
|
||||
})
|
||||
|
@ -122,7 +98,7 @@ func start() error {
|
|||
return interception.Start()
|
||||
}
|
||||
|
||||
func stop() error {
|
||||
func interceptionStop() error {
|
||||
return interception.Stop()
|
||||
}
|
||||
|
||||
|
@ -248,6 +224,15 @@ func initialHandler(conn *network.Connection, pkt packet.Packet) {
|
|||
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")
|
||||
DecideOnConnection(conn, pkt)
|
||||
conn.Inspecting = false // TODO: enable inspecting again
|
||||
|
@ -350,7 +335,7 @@ func issueVerdict(conn *network.Connection, pkt packet.Packet, verdict network.V
|
|||
func run() {
|
||||
for {
|
||||
select {
|
||||
case <-module.Stopping():
|
||||
case <-interceptionModule.Stopping():
|
||||
return
|
||||
case pkt := <-interception.Packets:
|
||||
handlePacket(pkt)
|
||||
|
@ -361,7 +346,7 @@ func run() {
|
|||
func statLogger() {
|
||||
for {
|
||||
select {
|
||||
case <-module.Stopping():
|
||||
case <-interceptionModule.Stopping():
|
||||
return
|
||||
case <-time.After(10 * time.Second):
|
||||
log.Tracef(
|
|
@ -45,14 +45,14 @@ func init() {
|
|||
"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 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 1702 -j DROP",
|
||||
"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 1712 -j DROP",
|
||||
"filter C17 -m mark --mark 1717 -j ACCEPT",
|
||||
"filter C17 -m mark --mark 1717 -j RETURN",
|
||||
}
|
||||
|
||||
v4once = []string{
|
||||
|
@ -80,14 +80,14 @@ func init() {
|
|||
"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 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 1702 -j DROP",
|
||||
"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 1712 -j DROP",
|
||||
"filter C17 -m mark --mark 1717 -j ACCEPT",
|
||||
"filter C17 -m mark --mark 1717 -j RETURN",
|
||||
}
|
||||
|
||||
v6once = []string{
|
||||
|
|
|
@ -163,8 +163,8 @@ func checkConnectionType(conn *network.Connection, _ packet.Packet) bool {
|
|||
}
|
||||
return true
|
||||
}
|
||||
case network.PeerLAN, network.PeerInternet, network.PeerInvalid:
|
||||
// Important: PeerHost is and should be missing!
|
||||
case network.PeerInternet:
|
||||
// BlockP2P only applies to connections to the Internet
|
||||
if p.BlockP2P() {
|
||||
conn.Block("direct connections (P2P) blocked")
|
||||
return true
|
||||
|
|
|
@ -72,7 +72,7 @@ func GetPermittedPort() uint16 {
|
|||
func portsInUseCleaner() {
|
||||
for {
|
||||
select {
|
||||
case <-module.Stopping():
|
||||
case <-interceptionModule.Stopping():
|
||||
return
|
||||
case <-time.After(cleanerTickDuration):
|
||||
cleanPortsInUse()
|
||||
|
|
|
@ -26,16 +26,16 @@ const (
|
|||
)
|
||||
|
||||
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.
|
||||
// build notification ID
|
||||
var nID string
|
||||
switch {
|
||||
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
|
||||
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)
|
||||
saveResponse := true
|
||||
|
|
|
@ -33,7 +33,7 @@ var (
|
|||
func init() {
|
||||
ignoreNetEnvEvents.Set()
|
||||
|
||||
module = modules.Register("filterlists", prep, start, nil, "core", "netenv")
|
||||
module = modules.Register("filterlists", prep, start, stop, "core")
|
||||
}
|
||||
|
||||
func prep() error {
|
||||
|
@ -98,3 +98,8 @@ func start() error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func stop() error {
|
||||
filterListsLoaded = make(chan struct{})
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ var (
|
|||
)
|
||||
|
||||
func init() {
|
||||
module = modules.Register("nameserver", prep, start, stop, "core", "resolver", "network", "netenv")
|
||||
module = modules.Register("nameserver", prep, start, stop, "core", "resolver")
|
||||
subsystems.Register(
|
||||
"dns",
|
||||
"Secure DNS",
|
||||
|
|
|
@ -41,8 +41,14 @@ func cleanConnections() (activePIDs map[int]struct{}) {
|
|||
now := time.Now().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()
|
||||
defer connsLock.Unlock()
|
||||
dnsConnsLock.Lock()
|
||||
defer dnsConnsLock.Unlock()
|
||||
|
||||
// network connections
|
||||
for key, conn := range conns {
|
||||
conn.Lock()
|
||||
|
||||
|
@ -67,10 +73,8 @@ func cleanConnections() (activePIDs map[int]struct{}) {
|
|||
|
||||
conn.Unlock()
|
||||
}
|
||||
connsLock.Unlock()
|
||||
|
||||
// dns requests
|
||||
dnsConnsLock.Lock()
|
||||
for _, conn := range dnsConns {
|
||||
conn.Lock()
|
||||
|
||||
|
@ -82,7 +86,6 @@ func cleanConnections() (activePIDs map[int]struct{}) {
|
|||
|
||||
conn.Unlock()
|
||||
}
|
||||
dnsConnsLock.Unlock()
|
||||
|
||||
return nil
|
||||
})
|
||||
|
|
|
@ -146,13 +146,13 @@ func NewConnectionFromFirstPacket(pkt packet.Packet) *Connection {
|
|||
}
|
||||
}
|
||||
|
||||
timestamp := time.Now().Unix()
|
||||
return &Connection{
|
||||
ID: pkt.GetConnectionID(),
|
||||
Scope: scope,
|
||||
Inbound: inbound,
|
||||
Entity: entity,
|
||||
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.
|
||||
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 {
|
||||
conn.Verdict = newVerdict
|
||||
conn.Reason = reason
|
||||
conn.ReasonContext = ctx
|
||||
conn.ReasonContext = reasonCtx
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
|
|
@ -77,9 +77,11 @@ func (s *StorageInterface) processQuery(q *query.Query, it *iterator.Iterator) {
|
|||
if slashes <= 1 {
|
||||
// processes
|
||||
for _, proc := range process.All() {
|
||||
proc.Lock()
|
||||
if q.Matches(proc) {
|
||||
it.Next <- proc
|
||||
}
|
||||
proc.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -87,9 +89,11 @@ func (s *StorageInterface) processQuery(q *query.Query, it *iterator.Iterator) {
|
|||
// dns scopes only
|
||||
dnsConnsLock.RLock()
|
||||
for _, dnsConn := range dnsConns {
|
||||
dnsConn.Lock()
|
||||
if q.Matches(dnsConn) {
|
||||
it.Next <- dnsConn
|
||||
}
|
||||
dnsConn.Unlock()
|
||||
}
|
||||
dnsConnsLock.RUnlock()
|
||||
}
|
||||
|
@ -98,9 +102,11 @@ func (s *StorageInterface) processQuery(q *query.Query, it *iterator.Iterator) {
|
|||
// connections
|
||||
connsLock.RLock()
|
||||
for _, conn := range conns {
|
||||
conn.Lock()
|
||||
if q.Matches(conn) {
|
||||
it.Next <- conn
|
||||
}
|
||||
conn.Unlock()
|
||||
}
|
||||
connsLock.RUnlock()
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ var (
|
|||
)
|
||||
|
||||
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.
|
||||
|
|
|
@ -14,7 +14,7 @@ const (
|
|||
)
|
||||
|
||||
// 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 {
|
||||
// IPv4
|
||||
switch {
|
||||
|
@ -36,11 +36,18 @@ func ClassifyIP(ip net.IP) int8 {
|
|||
case ip4[0] == 224:
|
||||
// 224.0.0.0/8
|
||||
return LocalMulticast
|
||||
case ip4[0] >= 225 && ip4[0] <= 239:
|
||||
// 225.0.0.0/8 - 239.0.0.0/8
|
||||
case ip4[0] >= 225 && ip4[0] <= 238:
|
||||
// 225.0.0.0/8 - 238.0.0.0/8
|
||||
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:
|
||||
// 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
|
||||
default:
|
||||
return Global
|
||||
|
|
|
@ -17,6 +17,10 @@ import (
|
|||
"github.com/tevino/abool"
|
||||
)
|
||||
|
||||
const (
|
||||
restartCode = 23
|
||||
)
|
||||
|
||||
var (
|
||||
runningInConsole bool
|
||||
onWindows = runtime.GOOS == "windows"
|
||||
|
@ -375,7 +379,7 @@ func execute(opts *Options, args []string) (cont bool, err error) {
|
|||
case 1:
|
||||
// error exit
|
||||
return true, fmt.Errorf("error during execution: %s", err)
|
||||
case 2357427: // Leet Speak for "restart"
|
||||
case restartCode:
|
||||
// restart request
|
||||
log.Printf("restarting %s\n", opts.Identifier)
|
||||
return true, nil
|
||||
|
|
|
@ -17,6 +17,7 @@ func registerConfiguration() error {
|
|||
Name: "Enable Process Detection",
|
||||
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.",
|
||||
Order: 144,
|
||||
OptType: config.OptTypeBool,
|
||||
ExpertiseLevel: config.ExpertiseLevelDeveloper,
|
||||
DefaultValue: true,
|
||||
|
|
|
@ -5,6 +5,8 @@ import (
|
|||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/safing/portbase/config"
|
||||
|
||||
"github.com/safing/portmaster/intel/filterlists"
|
||||
"github.com/safing/portmaster/profile/endpoints"
|
||||
)
|
||||
|
@ -77,20 +79,24 @@ func updateGlobalConfigProfile(ctx context.Context, data interface{}) error {
|
|||
internalSave: true,
|
||||
}
|
||||
|
||||
newConfig := make(map[string]interface{})
|
||||
// fill profile config options
|
||||
for key, value := range cfgStringOptions {
|
||||
profile.Config[key] = value()
|
||||
newConfig[key] = value()
|
||||
}
|
||||
for key, value := range cfgStringArrayOptions {
|
||||
profile.Config[key] = value()
|
||||
newConfig[key] = value()
|
||||
}
|
||||
for key, value := range cfgIntOptions {
|
||||
profile.Config[key] = value()
|
||||
newConfig[key] = value()
|
||||
}
|
||||
for key, value := range cfgBoolOptions {
|
||||
profile.Config[key] = value()
|
||||
newConfig[key] = value()
|
||||
}
|
||||
|
||||
// expand and assign
|
||||
profile.Config = config.Expand(newConfig)
|
||||
|
||||
// save profile
|
||||
err = profile.Save()
|
||||
if err != nil && lastErr == nil {
|
||||
|
|
|
@ -131,7 +131,7 @@ func registerConfiguration() error {
|
|||
"+": permit
|
||||
"-": block
|
||||
Host Matching:
|
||||
IP, CIDR, Country Code, ASN, "*" for any
|
||||
IP, CIDR, Country Code, ASN, Filterlist, "*" for any
|
||||
Domains:
|
||||
"example.com": exact match
|
||||
".example.com": exact match + subdomains
|
||||
|
@ -144,7 +144,11 @@ func registerConfiguration() error {
|
|||
Examples:
|
||||
+ .example.com */HTTP
|
||||
- .example.com
|
||||
+ 192.168.0.1/24`,
|
||||
+ 192.168.0.1/24
|
||||
- L:MAL
|
||||
- AS0
|
||||
+ AT
|
||||
- *`,
|
||||
Order: cfgOptionEndpointsOrder,
|
||||
OptType: config.OptTypeStringArray,
|
||||
DefaultValue: []string{},
|
||||
|
@ -167,7 +171,7 @@ Examples:
|
|||
"+": permit
|
||||
"-": block
|
||||
Host Matching:
|
||||
IP, CIDR, Country Code, ASN, "*" for any
|
||||
IP, CIDR, Country Code, ASN, Filterlist, "*" for any
|
||||
Domains:
|
||||
"example.com": exact match
|
||||
".example.com": exact match + subdomains
|
||||
|
@ -180,7 +184,11 @@ Examples:
|
|||
Examples:
|
||||
+ .example.com */HTTP
|
||||
- .example.com
|
||||
+ 192.168.0.1/24`,
|
||||
+ 192.168.0.1/24
|
||||
- L:MAL
|
||||
- AS0
|
||||
+ AT
|
||||
- *`,
|
||||
Order: cfgOptionServiceEndpointsOrder,
|
||||
OptType: config.OptTypeStringArray,
|
||||
DefaultValue: []string{},
|
||||
|
@ -249,9 +257,10 @@ Examples:
|
|||
err = config.Register(&config.Option{
|
||||
Name: "Block Scope Local",
|
||||
Key: CfgOptionBlockScopeLocalKey,
|
||||
Description: "Block connections to your own device, ie. localhost.",
|
||||
Description: "Block internal connections on your own device, ie. localhost.",
|
||||
Order: cfgOptionBlockScopeLocalOrder,
|
||||
OptType: config.OptTypeInt,
|
||||
ExpertiseLevel: config.ExpertiseLevelExpert,
|
||||
ExternalOptType: "security level",
|
||||
DefaultValue: status.SecurityLevelOff,
|
||||
ValidationRegex: "^(0|4|6|7)$",
|
||||
|
@ -270,7 +279,7 @@ Examples:
|
|||
Order: cfgOptionBlockScopeLANOrder,
|
||||
OptType: config.OptTypeInt,
|
||||
ExternalOptType: "security level",
|
||||
DefaultValue: status.SecurityLevelOff,
|
||||
DefaultValue: status.SecurityLevelsHighAndExtreme,
|
||||
ValidationRegex: "^(0|4|6|7)$",
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -300,7 +309,7 @@ Examples:
|
|||
err = config.Register(&config.Option{
|
||||
Name: "Block Peer to Peer Connections",
|
||||
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,
|
||||
OptType: config.OptTypeInt,
|
||||
ExternalOptType: "security level",
|
||||
|
@ -317,7 +326,7 @@ Examples:
|
|||
err = config.Register(&config.Option{
|
||||
Name: "Block Inbound Connections",
|
||||
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,
|
||||
OptType: config.OptTypeInt,
|
||||
ExternalOptType: "security level",
|
||||
|
|
|
@ -5,6 +5,8 @@ import (
|
|||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/safing/portbase/config"
|
||||
|
||||
"github.com/safing/portbase/database"
|
||||
"github.com/safing/portbase/database/query"
|
||||
"github.com/safing/portbase/database/record"
|
||||
|
@ -87,6 +89,9 @@ func (h *databaseHook) PrePut(r record.Record) (record.Record, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// clean config
|
||||
config.CleanHierarchicalConfig(profile.Config)
|
||||
|
||||
// prepare config
|
||||
err = profile.prepConfig()
|
||||
if err != nil {
|
||||
|
|
|
@ -14,7 +14,7 @@ var (
|
|||
)
|
||||
|
||||
func init() {
|
||||
module = modules.Register("profiles", prep, start, nil, "core")
|
||||
module = modules.Register("profiles", prep, start, nil, "base")
|
||||
}
|
||||
|
||||
func prep() error {
|
||||
|
|
|
@ -237,7 +237,7 @@ func (profile *Profile) addEndpointyEntry(cfgKey, newEntry string) {
|
|||
endpointList = make([]string, 0, 1)
|
||||
}
|
||||
endpointList = append(endpointList, newEntry)
|
||||
profile.Config[cfgKey] = endpointList
|
||||
config.PutValueIntoHierarchicalConfig(profile.Config, cfgKey, endpointList)
|
||||
|
||||
profile.Unlock()
|
||||
err := profile.Save()
|
||||
|
|
|
@ -37,8 +37,6 @@ func start() error {
|
|||
|
||||
// load status into atomic getters
|
||||
atomicUpdateSelectedSecurityLevel(status.SelectedSecurityLevel)
|
||||
atomicUpdatePortmasterStatus(status.PortmasterStatus)
|
||||
atomicUpdateGate17Status(status.Gate17Status)
|
||||
|
||||
// update status
|
||||
status.updateThreatMitigationLevel()
|
||||
|
|
|
@ -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
|
||||
func atomicUpdateActiveSecurityLevel(level uint8) {
|
||||
atomic.StoreUint32(activeSecurityLevel, uint32(level))
|
||||
|
@ -87,11 +53,3 @@ func atomicUpdateActiveSecurityLevel(level uint8) {
|
|||
func atomicUpdateSelectedSecurityLevel(level uint8) {
|
||||
atomic.StoreUint32(selectedSecurityLevel, uint32(level))
|
||||
}
|
||||
|
||||
func atomicUpdatePortmasterStatus(status uint8) {
|
||||
atomic.StoreUint32(portmasterStatus, uint32(status))
|
||||
}
|
||||
|
||||
func atomicUpdateGate17Status(status uint8) {
|
||||
atomic.StoreUint32(gate17Status, uint32(status))
|
||||
}
|
||||
|
|
|
@ -7,7 +7,5 @@ func TestSet(t *testing.T) {
|
|||
// only test for panics
|
||||
// TODO: write real tests
|
||||
setSelectedSecurityLevel(0)
|
||||
SetPortmasterStatus(0, "")
|
||||
SetGate17Status(0, "")
|
||||
|
||||
}
|
||||
|
|
|
@ -28,16 +28,8 @@ type SystemStatus struct {
|
|||
ActiveSecurityLevel uint8
|
||||
SelectedSecurityLevel uint8
|
||||
|
||||
PortmasterStatus uint8
|
||||
PortmasterStatusMsg string
|
||||
|
||||
Gate17Status uint8
|
||||
Gate17StatusMsg string
|
||||
|
||||
ThreatMitigationLevel uint8
|
||||
Threats map[string]*Threat
|
||||
|
||||
UpdateStatus string
|
||||
}
|
||||
|
||||
// Save saves the SystemStatus to the database
|
||||
|
|
|
@ -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()
|
||||
}
|
40
ui/module.go
40
ui/module.go
|
@ -1,13 +1,51 @@
|
|||
package ui
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
resources "github.com/cookieo9/resources-go"
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portbase/modules"
|
||||
)
|
||||
|
||||
const (
|
||||
eventReload = "reload"
|
||||
)
|
||||
|
||||
var (
|
||||
module *modules.Module
|
||||
)
|
||||
|
||||
func init() {
|
||||
modules.Register("ui", prep, nil, nil, "api", "updates")
|
||||
module = modules.Register("ui", prep, start, nil, "api", "updates")
|
||||
}
|
||||
|
||||
func prep() error {
|
||||
module.RegisterEvent(eventReload)
|
||||
|
||||
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
|
||||
}
|
||||
|
|
|
@ -8,6 +8,10 @@ import (
|
|||
"github.com/safing/portbase/log"
|
||||
)
|
||||
|
||||
const (
|
||||
cfgDevModeKey = "core/devMode"
|
||||
)
|
||||
|
||||
var (
|
||||
releaseChannel config.StringOption
|
||||
devMode config.BoolOption
|
||||
|
@ -23,6 +27,7 @@ func registerConfig() error {
|
|||
Name: "Release Channel",
|
||||
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.",
|
||||
Order: 1,
|
||||
OptType: config.OptTypeString,
|
||||
ExpertiseLevel: config.ExpertiseLevelExpert,
|
||||
ReleaseLevel: config.ReleaseLevelBeta,
|
||||
|
@ -39,6 +44,7 @@ func registerConfig() error {
|
|||
Name: "Disable Updates",
|
||||
Key: disableUpdatesKey,
|
||||
Description: "Disable automatic updates.",
|
||||
Order: 64,
|
||||
OptType: config.OptTypeBool,
|
||||
ExpertiseLevel: config.ExpertiseLevelExpert,
|
||||
ReleaseLevel: config.ReleaseLevelStable,
|
||||
|
@ -55,9 +61,13 @@ func registerConfig() error {
|
|||
|
||||
func initConfig() {
|
||||
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 {
|
||||
|
@ -90,8 +100,8 @@ func updateRegistryConfig(_ context.Context, _ interface{}) error {
|
|||
module.Resolve(updateFailed)
|
||||
_ = TriggerUpdate()
|
||||
log.Infof("updates: automatic updates enabled again.")
|
||||
} else {
|
||||
module.Warning(updateFailed, "Updates are disabled!")
|
||||
} else if updatesCurrentlyDisabled {
|
||||
module.Warning(updateFailed, "Automatic updates are disabled! This also affects security updates and threat intelligence.")
|
||||
log.Warningf("updates: automatic updates are now disabled.")
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue