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
commit 972f8fcfc3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
33 changed files with 427 additions and 166 deletions

View file

@ -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
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() {
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
}

View file

@ -46,5 +46,5 @@ func registerDatabases() error {
// 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
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
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"
"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(

View file

@ -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{

View file

@ -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

View file

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

View file

@ -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

View file

@ -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
}

View file

@ -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",

View file

@ -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
})

View file

@ -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

View file

@ -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()
}

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -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,

View file

@ -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 {

View file

@ -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",

View file

@ -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 {

View file

@ -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 {

View file

@ -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()

View file

@ -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()

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
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))
}

View file

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

View file

@ -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

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
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
}

View file

@ -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.")
}
}