mirror of
https://github.com/safing/portmaster
synced 2025-09-02 02:29:12 +00:00
Fix handling of connectivity / captive portal domains
Also, improve handling of queries during being captive.
This commit is contained in:
parent
a6e161e0a1
commit
68c2d23c1b
15 changed files with 223 additions and 63 deletions
|
@ -1,9 +1,11 @@
|
||||||
package firewall
|
package firewall
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
"github.com/safing/portbase/database"
|
"github.com/safing/portbase/database"
|
||||||
|
@ -77,18 +79,13 @@ func filterDNSSection(entries []dns.RR, p *profile.LayeredProfile, scope int8) (
|
||||||
}
|
}
|
||||||
|
|
||||||
func filterDNSResponse(conn *network.Connection, rrCache *resolver.RRCache) *resolver.RRCache {
|
func filterDNSResponse(conn *network.Connection, rrCache *resolver.RRCache) *resolver.RRCache {
|
||||||
|
p := conn.Process().Profile()
|
||||||
|
|
||||||
// do not modify own queries
|
// do not modify own queries
|
||||||
if conn.Process().Pid == os.Getpid() {
|
if conn.Process().Pid == os.Getpid() {
|
||||||
return rrCache
|
return rrCache
|
||||||
}
|
}
|
||||||
|
|
||||||
// get profile
|
|
||||||
p := conn.Process().Profile()
|
|
||||||
if p == nil {
|
|
||||||
conn.Block("no profile")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if DNS response filtering is completely turned off
|
// check if DNS response filtering is completely turned off
|
||||||
if !p.RemoveOutOfScopeDNS() && !p.RemoveBlockedDNS() {
|
if !p.RemoveOutOfScopeDNS() && !p.RemoveBlockedDNS() {
|
||||||
return rrCache
|
return rrCache
|
||||||
|
@ -112,6 +109,31 @@ func filterDNSResponse(conn *network.Connection, rrCache *resolver.RRCache) *res
|
||||||
rrCache.Filtered = true
|
rrCache.Filtered = true
|
||||||
if validIPs == 0 {
|
if validIPs == 0 {
|
||||||
conn.Block("no addresses returned for this domain are permitted")
|
conn.Block("no addresses returned for this domain are permitted")
|
||||||
|
|
||||||
|
// If all entries are filtered, this could mean that these are broken/bogus resource records.
|
||||||
|
if rrCache.Expired() {
|
||||||
|
// If the entry is expired, force delete it.
|
||||||
|
err := resolver.DeleteNameRecord(rrCache.Domain, rrCache.Question.String())
|
||||||
|
if err != nil && err != database.ErrNotFound {
|
||||||
|
log.Warningf(
|
||||||
|
"filter: failed to delete fully filtered name cache for %s: %s",
|
||||||
|
rrCache.ID(),
|
||||||
|
err,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else if rrCache.TTL > time.Now().Add(10*time.Second).Unix() {
|
||||||
|
// Set a low TTL of 10 seconds if TTL is higher than that.
|
||||||
|
rrCache.TTL = time.Now().Add(10 * time.Second).Unix()
|
||||||
|
err := rrCache.Save()
|
||||||
|
if err != nil {
|
||||||
|
log.Debugf(
|
||||||
|
"filter: failed to set shorter TTL on fully filtered name cache for %s: %s",
|
||||||
|
rrCache.ID(),
|
||||||
|
err,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,7 +144,25 @@ func filterDNSResponse(conn *network.Connection, rrCache *resolver.RRCache) *res
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecideOnResolvedDNS filters a dns response according to the application profile and settings.
|
// DecideOnResolvedDNS filters a dns response according to the application profile and settings.
|
||||||
func DecideOnResolvedDNS(conn *network.Connection, q *resolver.Query, rrCache *resolver.RRCache) *resolver.RRCache {
|
func DecideOnResolvedDNS(
|
||||||
|
ctx context.Context,
|
||||||
|
conn *network.Connection,
|
||||||
|
q *resolver.Query,
|
||||||
|
rrCache *resolver.RRCache,
|
||||||
|
) *resolver.RRCache {
|
||||||
|
|
||||||
|
// check profile
|
||||||
|
if checkProfileExists(ctx, conn, nil) {
|
||||||
|
// returns true if check triggered
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// special grant for connectivity domains
|
||||||
|
if checkConnectivityDomain(ctx, conn, nil) {
|
||||||
|
// returns true if check triggered
|
||||||
|
return rrCache
|
||||||
|
}
|
||||||
|
|
||||||
updatedRR := filterDNSResponse(conn, rrCache)
|
updatedRR := filterDNSResponse(conn, rrCache)
|
||||||
if updatedRR == nil {
|
if updatedRR == nil {
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -52,7 +52,7 @@ func DecideOnConnection(ctx context.Context, conn *network.Connection, pkt packe
|
||||||
checkSelfCommunication,
|
checkSelfCommunication,
|
||||||
checkProfileExists,
|
checkProfileExists,
|
||||||
checkConnectionType,
|
checkConnectionType,
|
||||||
checkCaptivePortal,
|
checkConnectivityDomain,
|
||||||
checkConnectionScope,
|
checkConnectionScope,
|
||||||
checkEndpointLists,
|
checkEndpointLists,
|
||||||
checkBypassPrevention,
|
checkBypassPrevention,
|
||||||
|
@ -181,10 +181,17 @@ func checkConnectionType(ctx context.Context, conn *network.Connection, _ packet
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkCaptivePortal(_ context.Context, conn *network.Connection, _ packet.Packet) bool {
|
func checkConnectivityDomain(_ context.Context, conn *network.Connection, _ packet.Packet) bool {
|
||||||
if netenv.GetOnlineStatus() == netenv.StatusPortal &&
|
p := conn.Process().Profile()
|
||||||
conn.Entity.Domain == netenv.GetCaptivePortal().Domain {
|
|
||||||
conn.Accept("captive portal access permitted")
|
if !p.BlockScopeInternet() {
|
||||||
|
// Special grant only applies if application is allowed to connect to the Internet.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if netenv.GetOnlineStatus() <= netenv.StatusPortal &&
|
||||||
|
netenv.IsConnectivityDomain(conn.Entity.Domain) {
|
||||||
|
conn.Accept("special grant for connectivity domain during network bootstrap")
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -274,7 +274,7 @@ func handleRequest(ctx context.Context, w dns.ResponseWriter, query *dns.Msg) er
|
||||||
}
|
}
|
||||||
|
|
||||||
tracer.Trace("nameserver: deciding on resolved dns")
|
tracer.Trace("nameserver: deciding on resolved dns")
|
||||||
rrCache = firewall.DecideOnResolvedDNS(conn, q, rrCache)
|
rrCache = firewall.DecideOnResolvedDNS(ctx, conn, q, rrCache)
|
||||||
if rrCache == nil {
|
if rrCache == nil {
|
||||||
sendResponse(w, query, conn.Verdict, conn.Reason, conn.ReasonContext)
|
sendResponse(w, query, conn.Verdict, conn.Reason, conn.ReasonContext)
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -11,6 +11,10 @@ import (
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/safing/portbase/database"
|
||||||
|
|
||||||
|
"github.com/safing/portbase/notifications"
|
||||||
|
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
|
|
||||||
"github.com/safing/portbase/log"
|
"github.com/safing/portbase/log"
|
||||||
|
@ -67,8 +71,41 @@ func init() {
|
||||||
// IsConnectivityDomain checks whether the given domain (fqdn) is used for any connectivity related network connections and should always be resolved using the network assigned DNS server.
|
// IsConnectivityDomain checks whether the given domain (fqdn) is used for any connectivity related network connections and should always be resolved using the network assigned DNS server.
|
||||||
func IsConnectivityDomain(domain string) bool {
|
func IsConnectivityDomain(domain string) bool {
|
||||||
switch domain {
|
switch domain {
|
||||||
case "detectportal.firefox.com.",
|
case "one.one.one.one.", // Internal DNS Check
|
||||||
"one.one.one.one.",
|
|
||||||
|
// Windows
|
||||||
|
"dns.msftncsi.com.", // DNS Check
|
||||||
|
"msftncsi.com.", // Older
|
||||||
|
"www.msftncsi.com.",
|
||||||
|
"microsoftconnecttest.com.", // Newer
|
||||||
|
"www.microsoftconnecttest.com.",
|
||||||
|
"ipv6.microsoftconnecttest.com.",
|
||||||
|
// https://de.wikipedia.org/wiki/Captive_Portal
|
||||||
|
// https://docs.microsoft.com/en-us/windows-hardware/drivers/mobilebroadband/captive-portals
|
||||||
|
// TODO: read value from registry: HKLM:\SYSTEM\CurrentControlSet\Services\NlaSvc\Parameters\Internet
|
||||||
|
|
||||||
|
// Apple
|
||||||
|
"captive.apple.com.",
|
||||||
|
// https://de.wikipedia.org/wiki/Captive_Portal
|
||||||
|
|
||||||
|
// Linux
|
||||||
|
"connectivity-check.ubuntu.com.", // Ubuntu
|
||||||
|
"nmcheck.gnome.org.", // Gnome DE
|
||||||
|
"network-test.debian.org.", // Debian
|
||||||
|
// There are probably a lot more domains for all the Linux Distro/DE Variants. Please raise issues and/or submit PRs!
|
||||||
|
// https://github.com/solus-project/budgie-desktop/issues/807
|
||||||
|
// https://www.lguruprasad.in/blog/2015/07/21/enabling-captive-portal-detection-in-gnome-3-14-on-debian-jessie/
|
||||||
|
// TODO: read value from NetworkManager config: /etc/NetworkManager/conf.d/*.conf
|
||||||
|
|
||||||
|
// Android
|
||||||
|
"connectivitycheck.gstatic.com.",
|
||||||
|
// https://de.wikipedia.org/wiki/Captive_Portal
|
||||||
|
|
||||||
|
// Other
|
||||||
|
"neverssl.com.", // Common Community Service
|
||||||
|
"detectportal.firefox.com.", // Firefox
|
||||||
|
|
||||||
|
// Redirected Domain
|
||||||
GetCaptivePortal().Domain:
|
GetCaptivePortal().Domain:
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -108,6 +145,7 @@ var (
|
||||||
|
|
||||||
captivePortal = &CaptivePortal{}
|
captivePortal = &CaptivePortal{}
|
||||||
captivePortalLock sync.Mutex
|
captivePortalLock sync.Mutex
|
||||||
|
captivePortalNotification *notifications.Notification
|
||||||
)
|
)
|
||||||
|
|
||||||
// CaptivePortal holds information about a detected captive portal.
|
// CaptivePortal holds information about a detected captive portal.
|
||||||
|
@ -180,9 +218,27 @@ func setCaptivePortal(portalURL string) {
|
||||||
// delete
|
// delete
|
||||||
if portalURL == "" {
|
if portalURL == "" {
|
||||||
captivePortal = &CaptivePortal{}
|
captivePortal = &CaptivePortal{}
|
||||||
|
if captivePortalNotification != nil {
|
||||||
|
err := captivePortalNotification.Delete()
|
||||||
|
if err != nil && err != database.ErrNotFound {
|
||||||
|
log.Warningf("netenv: failed to delete old captive portal notification: %s", err)
|
||||||
|
}
|
||||||
|
captivePortalNotification = nil
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// return if unchanged
|
||||||
|
if portalURL == captivePortal.URL {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// notify
|
||||||
|
defer notifications.NotifyInfo(
|
||||||
|
"netenv:captive-portal:"+captivePortal.Domain,
|
||||||
|
"Portmaster detected a captive portal at "+captivePortal.Domain,
|
||||||
|
)
|
||||||
|
|
||||||
// set
|
// set
|
||||||
captivePortal = &CaptivePortal{
|
captivePortal = &CaptivePortal{
|
||||||
URL: portalURL,
|
URL: portalURL,
|
||||||
|
@ -378,17 +434,17 @@ func checkOnlineStatus(ctx context.Context) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warningf("network: failed to read http body of captive portal testing response: %s", err)
|
log.Warningf("network: failed to read http body of captive portal testing response: %s", err)
|
||||||
// assume we are online nonetheless
|
// assume we are online nonetheless
|
||||||
|
// TODO: improve handling this case
|
||||||
updateOnlineStatus(StatusOnline, "", "http request succeeded, albeit failing later")
|
updateOnlineStatus(StatusOnline, "", "http request succeeded, albeit failing later")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// check body contents
|
// check body contents
|
||||||
if strings.TrimSpace(string(data)) == HTTPExpectedContent {
|
if strings.TrimSpace(string(data)) != HTTPExpectedContent {
|
||||||
updateOnlineStatus(StatusOnline, "", "http request succeeded")
|
// Something is interfering with the website content.
|
||||||
} else {
|
// This probably is a captive portal, just direct the user there.
|
||||||
// something is interfering with the website content
|
|
||||||
// this might be a weird captive portal, just direct the user there
|
|
||||||
updateOnlineStatus(StatusPortal, "detectportal.firefox.com", "http request succeeded, response content not as expected")
|
updateOnlineStatus(StatusPortal, "detectportal.firefox.com", "http request succeeded, response content not as expected")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
// close the body now as we plan to reuse the http.Client
|
// close the body now as we plan to reuse the http.Client
|
||||||
response.Body.Close()
|
response.Body.Close()
|
||||||
|
|
|
@ -180,6 +180,11 @@ func checkCache(ctx context.Context, q *Query) *RRCache {
|
||||||
|
|
||||||
// check if expired
|
// check if expired
|
||||||
if rrCache.Expired() {
|
if rrCache.Expired() {
|
||||||
|
if netenv.IsConnectivityDomain(rrCache.Domain) {
|
||||||
|
// do not use cache, resolve immediately
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
rrCache.Lock()
|
rrCache.Lock()
|
||||||
rrCache.requestingNew = true
|
rrCache.requestingNew = true
|
||||||
rrCache.Unlock()
|
rrCache.Unlock()
|
||||||
|
|
|
@ -7,6 +7,8 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/safing/portmaster/netenv"
|
||||||
|
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -75,9 +77,17 @@ func (rrCache *RRCache) Clean(minExpires uint32) {
|
||||||
lowestTTL = minExpires
|
lowestTTL = minExpires
|
||||||
}
|
}
|
||||||
|
|
||||||
// shorten NXDomain caching
|
// shorten caching
|
||||||
if len(rrCache.Answer) == 0 {
|
switch {
|
||||||
|
case rrCache.IsNXDomain():
|
||||||
|
// NXDomain
|
||||||
lowestTTL = 10
|
lowestTTL = 10
|
||||||
|
case netenv.IsConnectivityDomain(rrCache.Domain):
|
||||||
|
// Responses from these domains might change very quickly depending on the environment.
|
||||||
|
lowestTTL = 3
|
||||||
|
case !netenv.Online():
|
||||||
|
// Not being fully online could mean that we get funny responses.
|
||||||
|
lowestTTL = 60
|
||||||
}
|
}
|
||||||
|
|
||||||
// log.Tracef("lowest TTL is %d", lowestTTL)
|
// log.Tracef("lowest TTL is %d", lowestTTL)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package status
|
package status
|
||||||
|
|
||||||
// Definitions of Security and Status Levels
|
// Definitions of Security and Status Levels.
|
||||||
const (
|
const (
|
||||||
SecurityLevelOff uint8 = 0
|
SecurityLevelOff uint8 = 0
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package status
|
package status
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
"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"
|
||||||
|
@ -35,7 +37,10 @@ func (sh *statusHook) PrePut(r record.Record) (record.Record, error) {
|
||||||
|
|
||||||
// apply applicable settings
|
// apply applicable settings
|
||||||
if SelectedSecurityLevel() != newStatus.SelectedSecurityLevel {
|
if SelectedSecurityLevel() != newStatus.SelectedSecurityLevel {
|
||||||
go setSelectedSecurityLevel(newStatus.SelectedSecurityLevel)
|
module.StartWorker("set selected security level", func(_ context.Context) error {
|
||||||
|
setSelectedSecurityLevel(newStatus.SelectedSecurityLevel)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: allow setting of Gate17 status (on/off)
|
// TODO: allow setting of Gate17 status (on/off)
|
||||||
|
|
|
@ -7,22 +7,16 @@ import (
|
||||||
var (
|
var (
|
||||||
activeSecurityLevel *uint32
|
activeSecurityLevel *uint32
|
||||||
selectedSecurityLevel *uint32
|
selectedSecurityLevel *uint32
|
||||||
portmasterStatus *uint32
|
|
||||||
gate17Status *uint32
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
var (
|
var (
|
||||||
activeSecurityLevelValue uint32
|
activeSecurityLevelValue uint32
|
||||||
selectedSecurityLevelValue uint32
|
selectedSecurityLevelValue uint32
|
||||||
portmasterStatusValue uint32
|
|
||||||
gate17StatusValue uint32
|
|
||||||
)
|
)
|
||||||
|
|
||||||
activeSecurityLevel = &activeSecurityLevelValue
|
activeSecurityLevel = &activeSecurityLevelValue
|
||||||
selectedSecurityLevel = &selectedSecurityLevelValue
|
selectedSecurityLevel = &selectedSecurityLevelValue
|
||||||
portmasterStatus = &portmasterStatusValue
|
|
||||||
gate17Status = &gate17StatusValue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ActiveSecurityLevel returns the current security level.
|
// ActiveSecurityLevel returns the current security level.
|
||||||
|
@ -34,13 +28,3 @@ func ActiveSecurityLevel() uint8 {
|
||||||
func SelectedSecurityLevel() uint8 {
|
func SelectedSecurityLevel() uint8 {
|
||||||
return uint8(atomic.LoadUint32(selectedSecurityLevel))
|
return uint8(atomic.LoadUint32(selectedSecurityLevel))
|
||||||
}
|
}
|
||||||
|
|
||||||
// PortmasterStatus returns the current Portmaster status.
|
|
||||||
func PortmasterStatus() uint8 {
|
|
||||||
return uint8(atomic.LoadUint32(portmasterStatus))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gate17Status returns the current Gate17 status.
|
|
||||||
func Gate17Status() uint8 {
|
|
||||||
return uint8(atomic.LoadUint32(gate17Status))
|
|
||||||
}
|
|
||||||
|
|
|
@ -8,8 +8,6 @@ func TestGet(t *testing.T) {
|
||||||
// TODO: write real tests
|
// TODO: write real tests
|
||||||
ActiveSecurityLevel()
|
ActiveSecurityLevel()
|
||||||
SelectedSecurityLevel()
|
SelectedSecurityLevel()
|
||||||
PortmasterStatus()
|
|
||||||
Gate17Status()
|
|
||||||
option := ConfigIsActive("invalid")
|
option := ConfigIsActive("invalid")
|
||||||
option(0)
|
option(0)
|
||||||
option = ConfigIsActiveConcurrent("invalid")
|
option = ConfigIsActiveConcurrent("invalid")
|
||||||
|
|
|
@ -6,21 +6,40 @@ import (
|
||||||
"github.com/safing/portbase/modules"
|
"github.com/safing/portbase/modules"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
module *modules.Module
|
||||||
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
modules.Register("status", nil, start, stop, "base")
|
module = modules.Register("status", nil, start, stop, "base")
|
||||||
}
|
}
|
||||||
|
|
||||||
func start() error {
|
func start() error {
|
||||||
var loadedStatus *SystemStatus
|
err := initSystemStatus()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = startNetEnvHooking()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
status.Save()
|
||||||
|
|
||||||
|
return initStatusHook()
|
||||||
|
}
|
||||||
|
|
||||||
|
func initSystemStatus() error {
|
||||||
// load status from database
|
// load status from database
|
||||||
r, err := statusDB.Get(statusDBKey)
|
r, err := statusDB.Get(statusDBKey)
|
||||||
switch err {
|
switch err {
|
||||||
case nil:
|
case nil:
|
||||||
loadedStatus, err = EnsureSystemStatus(r)
|
loadedStatus, err := EnsureSystemStatus(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Criticalf("status: failed to unwrap system status: %s", err)
|
log.Criticalf("status: failed to unwrap system status: %s", err)
|
||||||
loadedStatus = nil
|
} else {
|
||||||
|
status = loadedStatus
|
||||||
}
|
}
|
||||||
case database.ErrNotFound:
|
case database.ErrNotFound:
|
||||||
// create new status
|
// create new status
|
||||||
|
@ -28,10 +47,6 @@ func start() error {
|
||||||
log.Criticalf("status: failed to load system status: %s", err)
|
log.Criticalf("status: failed to load system status: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// activate loaded status, if available
|
|
||||||
if loadedStatus != nil {
|
|
||||||
status = loadedStatus
|
|
||||||
}
|
|
||||||
status.Lock()
|
status.Lock()
|
||||||
defer status.Unlock()
|
defer status.Unlock()
|
||||||
|
|
||||||
|
@ -41,10 +56,9 @@ func start() error {
|
||||||
// update status
|
// update status
|
||||||
status.updateThreatMitigationLevel()
|
status.updateThreatMitigationLevel()
|
||||||
status.autopilot()
|
status.autopilot()
|
||||||
|
status.updateOnlineStatus()
|
||||||
|
|
||||||
go status.Save()
|
return nil
|
||||||
|
|
||||||
return initStatusHook()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func stop() error {
|
func stop() error {
|
||||||
|
|
28
status/netenv.go
Normal file
28
status/netenv.go
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
package status
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/safing/portmaster/netenv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// startNetEnvHooking starts the listener for online status changes.
|
||||||
|
func startNetEnvHooking() error {
|
||||||
|
return module.RegisterEventHook(
|
||||||
|
"netenv",
|
||||||
|
netenv.OnlineStatusChangedEvent,
|
||||||
|
"update online status in system status",
|
||||||
|
func(_ context.Context, _ interface{}) error {
|
||||||
|
status.Lock()
|
||||||
|
status.updateOnlineStatus()
|
||||||
|
status.Unlock()
|
||||||
|
status.Save()
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SystemStatus) updateOnlineStatus() {
|
||||||
|
s.OnlineStatus = netenv.GetOnlineStatus()
|
||||||
|
s.CaptivePortal = netenv.GetCaptivePortal()
|
||||||
|
}
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"github.com/safing/portbase/log"
|
"github.com/safing/portbase/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// autopilot automatically adjusts the security level as needed
|
// autopilot automatically adjusts the security level as needed.
|
||||||
func (s *SystemStatus) autopilot() {
|
func (s *SystemStatus) autopilot() {
|
||||||
// check if users is overruling
|
// check if users is overruling
|
||||||
if s.SelectedSecurityLevel > SecurityLevelOff {
|
if s.SelectedSecurityLevel > SecurityLevelOff {
|
||||||
|
@ -33,19 +33,18 @@ func setSelectedSecurityLevel(level uint8) {
|
||||||
switch level {
|
switch level {
|
||||||
case SecurityLevelOff, SecurityLevelNormal, SecurityLevelHigh, SecurityLevelExtreme:
|
case SecurityLevelOff, SecurityLevelNormal, SecurityLevelHigh, SecurityLevelExtreme:
|
||||||
status.Lock()
|
status.Lock()
|
||||||
defer status.Unlock()
|
|
||||||
|
|
||||||
status.SelectedSecurityLevel = level
|
status.SelectedSecurityLevel = level
|
||||||
atomicUpdateSelectedSecurityLevel(level)
|
atomicUpdateSelectedSecurityLevel(level)
|
||||||
status.autopilot()
|
status.autopilot()
|
||||||
|
|
||||||
go status.Save()
|
status.Unlock()
|
||||||
|
status.Save()
|
||||||
default:
|
default:
|
||||||
log.Errorf("status: tried to set selected security level to invalid value: %d", level)
|
log.Errorf("status: tried to set selected security level to invalid value: %d", level)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// update functions for atomic stuff
|
|
||||||
func atomicUpdateActiveSecurityLevel(level uint8) {
|
func atomicUpdateActiveSecurityLevel(level uint8) {
|
||||||
atomic.StoreUint32(activeSecurityLevel, uint32(level))
|
atomic.StoreUint32(activeSecurityLevel, uint32(level))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
package status
|
package status
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/safing/portmaster/netenv"
|
||||||
|
|
||||||
"github.com/safing/portbase/database/record"
|
"github.com/safing/portbase/database/record"
|
||||||
"github.com/safing/portbase/log"
|
"github.com/safing/portbase/log"
|
||||||
)
|
)
|
||||||
|
@ -28,11 +31,22 @@ type SystemStatus struct {
|
||||||
ActiveSecurityLevel uint8
|
ActiveSecurityLevel uint8
|
||||||
SelectedSecurityLevel uint8
|
SelectedSecurityLevel uint8
|
||||||
|
|
||||||
|
OnlineStatus netenv.OnlineStatus
|
||||||
|
CaptivePortal *netenv.CaptivePortal
|
||||||
|
|
||||||
ThreatMitigationLevel uint8
|
ThreatMitigationLevel uint8
|
||||||
Threats map[string]*Threat
|
Threats map[string]*Threat
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save saves the SystemStatus to the database
|
// SaveAsync saves the SystemStatus to the database asynchronously.
|
||||||
|
func (s *SystemStatus) SaveAsync() {
|
||||||
|
module.StartWorker("save system status", func(_ context.Context) error {
|
||||||
|
s.Save()
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save saves the SystemStatus to the database.
|
||||||
func (s *SystemStatus) Save() {
|
func (s *SystemStatus) Save() {
|
||||||
err := statusDB.Put(s)
|
err := statusDB.Put(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -26,7 +26,7 @@ func AddOrUpdateThreat(new *Threat) {
|
||||||
status.updateThreatMitigationLevel()
|
status.updateThreatMitigationLevel()
|
||||||
status.autopilot()
|
status.autopilot()
|
||||||
|
|
||||||
go status.Save()
|
status.SaveAsync()
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteThreat deletes a threat from the system status.
|
// DeleteThreat deletes a threat from the system status.
|
||||||
|
@ -38,7 +38,7 @@ func DeleteThreat(id string) {
|
||||||
status.updateThreatMitigationLevel()
|
status.updateThreatMitigationLevel()
|
||||||
status.autopilot()
|
status.autopilot()
|
||||||
|
|
||||||
go status.Save()
|
status.SaveAsync()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetThreats returns all threats who's IDs are prefixed by the given string, and also a locker for editing them.
|
// GetThreats returns all threats who's IDs are prefixed by the given string, and also a locker for editing them.
|
||||||
|
|
Loading…
Add table
Reference in a new issue