diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index e5d1ca0b..d1fa2da5 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -20,14 +20,14 @@ jobs: - uses: actions/setup-go@v3 with: - go-version: '^1.18' + go-version: '^1.19' - name: Run golangci-lint uses: golangci/golangci-lint-action@v3 with: - version: v1.45.1 + version: v1.49.0 only-new-issues: true - args: -c ./.golangci.yml + args: -c ./.golangci.yml --timeout 15m - name: Get dependencies run: go mod download @@ -45,7 +45,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v3 with: - go-version: '^1.18' + go-version: '^1.19' - name: Get dependencies run: go mod download diff --git a/.golangci.yml b/.golangci.yml index 6c348ac6..8202fead 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -8,6 +8,7 @@ linters: - contextcheck - cyclop - exhaustivestruct + - exhaustruct - forbidigo - funlen - gochecknoglobals @@ -17,6 +18,7 @@ linters: - goerr113 - gomnd - ifshort + - interfacebloat - interfacer - ireturn - lll @@ -24,6 +26,9 @@ linters: - nilnil - nlreturn - noctx + - nolintlint + - nonamedreturns + - nosnakecase - revive - tagliatelle - testpackage @@ -31,7 +36,6 @@ linters: - whitespace - wrapcheck - wsl - - nolintlint linters-settings: revive: diff --git a/cmds/portmaster-core/main.go b/cmds/portmaster-core/main.go index d4958d71..67c0efef 100644 --- a/cmds/portmaster-core/main.go +++ b/cmds/portmaster-core/main.go @@ -1,6 +1,7 @@ +//nolint:gci,nolintlint package main -import ( //nolint:gci,nolintlint +import ( "os" "github.com/safing/portbase/info" diff --git a/core/pmtesting/testing.go b/core/pmtesting/testing.go index 1f4324fa..9b597c83 100644 --- a/core/pmtesting/testing.go +++ b/core/pmtesting/testing.go @@ -2,18 +2,17 @@ // // Usage: // -// package name +// package name // -// import ( -// "testing" +// import ( +// "testing" // -// "github.com/safing/portmaster/core/pmtesting" -// ) -// -// func TestMain(m *testing.M) { -// pmtesting.TestMain(m, module) -// } +// "github.com/safing/portmaster/core/pmtesting" +// ) // +// func TestMain(m *testing.M) { +// pmtesting.TestMain(m, module) +// } package pmtesting import ( diff --git a/firewall/config.go b/firewall/config.go index 702b6c95..b2f23486 100644 --- a/firewall/config.go +++ b/firewall/config.go @@ -1,10 +1,9 @@ package firewall import ( - "github.com/safing/portbase/api" "github.com/safing/portbase/config" "github.com/safing/portbase/notifications" - "github.com/safing/portmaster/core" + "github.com/safing/spn/captain" ) // Configuration Keys. @@ -26,9 +25,6 @@ var ( CfgOptionDNSQueryInterceptionKey = "filter/dnsQueryInterception" cfgOptionDNSQueryInterceptionOrder = 97 dnsQueryInterception config.BoolOption - - devMode config.BoolOption - apiListenAddress config.StringOption ) func registerConfig() error { @@ -108,8 +104,17 @@ func registerConfig() error { } askTimeout = config.Concurrent.GetAsInt(CfgOptionAskTimeoutKey, 60) - devMode = config.Concurrent.GetAsBool(core.CfgDevModeKey, false) - apiListenAddress = config.GetAsString(api.CfgDefaultListenAddressKey, "") - return nil } + +var ( + filterEnabled config.BoolOption + tunnelEnabled config.BoolOption + useCommunityNodes config.BoolOption +) + +func getConfig() { + filterEnabled = config.Concurrent.GetAsBool(CfgOptionEnableFilterKey, true) + tunnelEnabled = config.Concurrent.GetAsBool(captain.CfgOptionEnableSPNKey, false) + useCommunityNodes = config.Concurrent.GetAsBool(captain.CfgOptionUseCommunityNodesKey, true) +} diff --git a/firewall/filter.go b/firewall/filter.go index 390ef3f3..e5c4aa1e 100644 --- a/firewall/filter.go +++ b/firewall/filter.go @@ -2,22 +2,12 @@ package firewall import ( "github.com/safing/portbase/config" - "github.com/safing/portbase/log" "github.com/safing/portbase/modules" "github.com/safing/portbase/modules/subsystems" _ "github.com/safing/portmaster/core" - "github.com/safing/portmaster/intel/filterlists" - "github.com/safing/spn/captain" ) -var ( - filterModule *modules.Module - filterEnabled config.BoolOption - tunnelEnabled config.BoolOption - - unbreakFilterListIDs = []string{"UNBREAK"} - resolvedUnbreakFilterListIDs []string -) +var filterModule *modules.Module func init() { filterModule = modules.Register("filter", filterPrep, filterStart, nil, "core", "intel") @@ -48,18 +38,11 @@ func filterPrep() (err error) { return err } - filterEnabled = config.Concurrent.GetAsBool(CfgOptionEnableFilterKey, true) - tunnelEnabled = config.Concurrent.GetAsBool(captain.CfgOptionEnableSPNKey, false) return nil } func filterStart() error { - // TODO: Re-resolve IDs when filterlist index changes. - resolvedIDs, err := filterlists.ResolveListIDs(unbreakFilterListIDs) - if err != nil { - log.Warningf("filter: failed to resolve unbreak filter list IDs: %s", err) - } else { - resolvedUnbreakFilterListIDs = resolvedIDs - } + getConfig() + return nil } diff --git a/firewall/interception.go b/firewall/interception.go index ed582ba1..3ef6a62b 100644 --- a/firewall/interception.go +++ b/firewall/interception.go @@ -13,9 +13,12 @@ import ( "github.com/tevino/abool" "golang.org/x/sync/singleflight" + "github.com/safing/portbase/api" + "github.com/safing/portbase/config" "github.com/safing/portbase/log" "github.com/safing/portbase/modules" "github.com/safing/portmaster/compat" + "github.com/safing/portmaster/core" _ "github.com/safing/portmaster/core/base" "github.com/safing/portmaster/firewall/inspection" "github.com/safing/portmaster/firewall/interception" @@ -43,7 +46,14 @@ var ( ownPID = os.Getpid() ) +// Config variables for interception module. +var ( + devMode config.BoolOption + apiListenAddress config.StringOption +) + func init() { + // TODO: Move interception module to own package (dir). interceptionModule = modules.Register("interception", interceptionPrep, interceptionStart, interceptionStop, "base", "updates", "network", "notifications") network.SetDefaultFirewallHandler(defaultHandler) @@ -54,6 +64,9 @@ func interceptionPrep() error { } func interceptionStart() error { + devMode = config.Concurrent.GetAsBool(core.CfgDevModeKey, false) + apiListenAddress = config.GetAsString(api.CfgDefaultListenAddressKey, "") + if err := registerMetrics(); err != nil { return err } diff --git a/firewall/interception/nfq/packet.go b/firewall/interception/nfq/packet.go index 528f2e3c..f3ede898 100644 --- a/firewall/interception/nfq/packet.go +++ b/firewall/interception/nfq/packet.go @@ -71,15 +71,15 @@ func (pkt *packet) LoadPacketData() error { } // TODO(ppacher): revisit the following behavior: -// The legacy implementation of nfqueue (and the interception) module -// always accept a packet but may mark it so that a subsequent rule in -// the C17 chain drops, rejects or modifies it. // -// For drop/return we could use the actual nfQueue verdicts Drop and Stop. -// Re-routing to local NS or SPN can be done by modifying the packet here -// and using SetVerdictModPacket and reject can be implemented using a simple -// raw-socket. +// The legacy implementation of nfqueue (and the interception) module +// always accept a packet but may mark it so that a subsequent rule in +// the C17 chain drops, rejects or modifies it. // +// For drop/return we could use the actual nfQueue verdicts Drop and Stop. +// Re-routing to local NS or SPN can be done by modifying the packet here +// and using SetVerdictModPacket and reject can be implemented using a simple +// raw-socket. func (pkt *packet) mark(mark int) (err error) { if pkt.verdictPending.SetToIf(false, true) { defer close(pkt.verdictSet) diff --git a/firewall/master.go b/firewall/master.go index 67e4f491..33e96305 100644 --- a/firewall/master.go +++ b/firewall/master.go @@ -14,6 +14,7 @@ import ( "github.com/safing/portbase/log" "github.com/safing/portmaster/detection/dga" "github.com/safing/portmaster/intel/customlists" + "github.com/safing/portmaster/intel/filterlists" "github.com/safing/portmaster/netenv" "github.com/safing/portmaster/network" "github.com/safing/portmaster/network/netutils" @@ -435,6 +436,7 @@ func checkFilterLists(ctx context.Context, conn *network.Connection, p *profile. switch result { case endpoints.Denied: // If the connection matches a filter list, check if the "unbreak" list matches too and abort blocking. + resolvedUnbreakFilterListIDs := filterlists.GetUnbreakFilterListIDs() for _, blockedListID := range conn.Entity.BlockedByLists { for _, unbreakListID := range resolvedUnbreakFilterListIDs { if blockedListID == unbreakListID { diff --git a/firewall/tunnel.go b/firewall/tunnel.go index 28f0e37d..f335bca4 100644 --- a/firewall/tunnel.go +++ b/firewall/tunnel.go @@ -114,6 +114,11 @@ func checkTunneling(ctx context.Context, conn *network.Connection, pkt packet.Pa RoutingProfile: layeredProfile.SPNRoutingAlgorithm(), } + // Add required verified owners if community nodes should not be used. + if !useCommunityNodes() { + conn.TunnelOpts.RequireVerifiedOwners = captain.NonCommunityVerifiedOwners + } + // If we have any exit hub policies, we need to raise the routing algorithm at least to single-hop. if conn.TunnelOpts.RoutingProfile == navigator.RoutingProfileHomeID && conn.TunnelOpts.HubPoliciesAreSet() { diff --git a/go.mod b/go.mod index b335daa2..cc6bc90e 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,9 @@ module github.com/safing/portmaster -go 1.18 +go 1.19 require ( github.com/agext/levenshtein v1.2.3 - github.com/cookieo9/resources-go v0.0.0-20150225115733-d27c04069d0d github.com/coreos/go-iptables v0.6.0 github.com/florianl/go-nfqueue v1.3.1 github.com/ghodss/yaml v1.0.0 @@ -16,16 +15,17 @@ require ( github.com/miekg/dns v1.1.50 github.com/oschwald/maxminddb-golang v1.10.0 github.com/safing/portbase v0.15.1 - github.com/safing/spn v0.4.15 + github.com/safing/spn v0.5.0 github.com/shirou/gopsutil v3.21.11+incompatible github.com/spf13/cobra v1.5.0 + github.com/spkg/zipfs v0.7.1 github.com/stretchr/testify v1.8.0 github.com/tannerryan/ring v1.1.2 github.com/tevino/abool v1.2.0 github.com/umahmood/haversine v0.0.0-20151105152445-808ab04add26 - golang.org/x/net v0.0.0-20220822230855-b0a4917ee28c + golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde - golang.org/x/sys v0.0.0-20220823224334-20c2bfdbfe24 + golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 zombiezen.com/go/sqlite v0.10.1 ) @@ -47,7 +47,6 @@ require ( github.com/hashicorp/errwrap v1.1.0 // indirect github.com/inconshreveable/mousetrap v1.0.1 // indirect github.com/josharian/native v1.0.0 // indirect - github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect github.com/mattn/go-isatty v0.0.16 // indirect github.com/mdlayher/netlink v1.6.0 // indirect github.com/mdlayher/socket v0.2.3 // indirect @@ -76,13 +75,13 @@ require ( github.com/x448/float16 v0.8.4 // indirect github.com/yusufpapurcu/wmi v1.2.2 // indirect go.etcd.io/bbolt v1.3.6 // indirect - golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8 // indirect + golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 // indirect golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect golang.org/x/tools v0.1.12 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - modernc.org/libc v1.17.0 // indirect + modernc.org/libc v1.17.1 // indirect modernc.org/mathutil v1.5.0 // indirect - modernc.org/memory v1.2.0 // indirect + modernc.org/memory v1.2.1 // indirect modernc.org/sqlite v1.18.1 // indirect ) diff --git a/go.sum b/go.sum index 18c0996e..8a0c5294 100644 --- a/go.sum +++ b/go.sum @@ -236,7 +236,6 @@ github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= -github.com/cookieo9/resources-go v0.0.0-20150225115733-d27c04069d0d h1:O+gcIbHv8EocDRI8swPGYI6XPJDbdZ66jeXqfoXifLE= github.com/cookieo9/resources-go v0.0.0-20150225115733-d27c04069d0d/go.mod h1:Da90oEbCMTyeoWRBoWQHAmajIlLPjji2U2w7HJGAnuY= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -602,7 +601,6 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1 github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA= github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= @@ -832,7 +830,6 @@ github.com/safing/portbase v0.14.2/go.mod h1:z9sRR/vqohAGdYSSx2B+o8tND4WVvcxPL6X github.com/safing/portbase v0.14.3/go.mod h1:z9sRR/vqohAGdYSSx2B+o8tND4WVvcxPL6XBBtN3bDI= github.com/safing/portbase v0.14.4/go.mod h1:z9sRR/vqohAGdYSSx2B+o8tND4WVvcxPL6XBBtN3bDI= github.com/safing/portbase v0.14.5/go.mod h1:z9sRR/vqohAGdYSSx2B+o8tND4WVvcxPL6XBBtN3bDI= -github.com/safing/portbase v0.15.0 h1:WwJpnERbNfzqDkEHt39l1TbBhnvf/oB2ZKj6fkaMSTM= github.com/safing/portbase v0.15.0/go.mod h1:BinbSUlbOdsHTBSE8+WkKbR1bXNMlsbhhAW12EBxsUo= github.com/safing/portbase v0.15.1 h1:s4AzyMSF26/b0CPmyHvKJSG9nW+u42+eIxlIKyp+J1U= github.com/safing/portbase v0.15.1/go.mod h1:5bHi99fz7Hh/wOsZUOI631WF9ePSHk57c4fdlOMS91Y= @@ -878,6 +875,8 @@ github.com/safing/spn v0.4.13/go.mod h1:rBeimIc1FHQOhX7lTh/LaFGRotmnwZIDWUSsPyeI github.com/safing/spn v0.4.14/go.mod h1:GwGVU+GSmPQQXorKVDXIgieJZ16e70aBV5CcG0nAVRg= github.com/safing/spn v0.4.15 h1:hiGAQgkfxm3yfakkt7mVmkUfYxtE0pgODH9LyloEr/o= github.com/safing/spn v0.4.15/go.mod h1:mgVGd4sUdem78irl/pSx/JqOM/Ow85pJqKKqPpj1Ikk= +github.com/safing/spn v0.5.0 h1:gOkcz1io7/4A4rZislqlm3w+gzVqO5861tbbib0EBUM= +github.com/safing/spn v0.5.0/go.mod h1:BGzLv966GfYZXc4EUFk04QfrfkpzDdaNObkKTRPbdpk= github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig= github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= @@ -935,6 +934,8 @@ github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DM github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM= +github.com/spkg/zipfs v0.7.1 h1:+2X5lvNHTybnDMQZAIHgedRXZK1WXdc+94R/P5v2XWE= +github.com/spkg/zipfs v0.7.1/go.mod h1:48LW+/Rh1G7aAav1ew1PdlYn52T+LM+ARmSHfDNJvg8= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -1120,8 +1121,8 @@ golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.0.0-20220513210258-46612604a0f9/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8 h1:GIAS/yBem/gq2MUqgNIzUHW7cJMmx3TGZOrnyYaNQ6c= -golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM= +golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1246,8 +1247,8 @@ golang.org/x/net v0.0.0-20220513224357-95641704303c/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220708220712-1185a9018129/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220728211354-c7608f3a8462/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.0.0-20220822230855-b0a4917ee28c h1:JVAXQ10yGGVbSyoer5VILysz6YKjdNT2bsvlayjqhes= -golang.org/x/net v0.0.0-20220822230855-b0a4917ee28c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b h1:ZmngSVLe/wycRns9MKikG9OWIEjGcGAkacif7oYQaUY= +golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1415,8 +1416,8 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220823224334-20c2bfdbfe24 h1:TyKJRhyo17yWxOMCTHKWrc5rddHORMlnZ/j57umaUd8= -golang.org/x/sys v0.0.0-20220823224334-20c2bfdbfe24/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 h1:v6hYoSR9T5oet+pMXwUWkbiVqx/63mlHjefrHmxwfeY= +golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -1750,15 +1751,15 @@ modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU= modernc.org/libc v1.16.7/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU= modernc.org/libc v1.16.17/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU= modernc.org/libc v1.16.19/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= -modernc.org/libc v1.17.0 h1:nbL2Lv0I323wLc1GmTh/AqVtI9JeBVc7Nhapdg9EONs= -modernc.org/libc v1.17.0/go.mod h1:XsgLldpP4aWlPlsjqKRdHPqCxCjISdHfM/yeWC5GyW0= +modernc.org/libc v1.17.1 h1:Q8/Cpi36V/QBfuQaFVeisEBs3WqoGAJprZzmf7TfEYI= +modernc.org/libc v1.17.1/go.mod h1:FZ23b+8LjxZs7XtFMbSzL/EhPxNbfZbErxEHc7cbD9s= modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ= modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= -modernc.org/memory v1.2.0 h1:zXehBrt9n+Pjn+4RoRCZ0KqRA/0ePFqcecxZ/hXCIVw= -modernc.org/memory v1.2.0/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= +modernc.org/memory v1.2.1 h1:dkRh86wgmq/bJu2cAS2oqBCz/KsMZU7TUM4CibQ7eBs= +modernc.org/memory v1.2.1/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= modernc.org/sqlite v1.17.3/go.mod h1:10hPVYar9C0kfXuTWGz8s0XtB8uAGymUy51ZzStYe3k= modernc.org/sqlite v1.18.0/go.mod h1:B9fRWZacNxJBHoCJZQr1R54zhVn3fjfl0aszflrTSxY= diff --git a/intel/entity.go b/intel/entity.go index 19596255..0394d96c 100644 --- a/intel/entity.go +++ b/intel/entity.go @@ -237,8 +237,8 @@ func (e *Entity) GetIP() (net.IP, bool) { func (e *Entity) getLocation(ctx context.Context) { e.fetchLocationOnce.Do(func() { - // need IP! - if e.IP == nil { + // Only check if we have a global IP address. + if e.IP == nil || !e.IPScope.IsGlobal() { return } @@ -254,6 +254,31 @@ func (e *Entity) getLocation(ctx context.Context) { e.Coordinates = &loc.Coordinates e.ASN = loc.AutonomousSystemNumber e.ASOrg = loc.AutonomousSystemOrganization + + // Log result. + if log.GetLogLevel() == log.TraceLevel { + // Build flags + var flags string + if loc.IsAnycast { + flags += " anycast" + } + if loc.IsSatelliteProvider { + flags += " satellite" + } + if loc.IsAnonymousProxy { + flags += " anonymous" + } + + // Log location + log.Tracer(ctx).Tracef( + "intel: located %s in %s (AS%d by %s)%s", + e.IP, + loc.Country.ISOCode, + loc.AutonomousSystemNumber, + loc.AutonomousSystemOrganization, + flags, + ) + } }) } @@ -318,7 +343,6 @@ func (e *Entity) getDomainLists(ctx context.Context) { return } - log.Tracer(ctx).Tracef("intel: loading domain list for %s", domain) e.loadDomainListOnce.Do(func() { domainsToInspect := []string{domain} @@ -347,7 +371,10 @@ func (e *Entity) getDomainLists(ctx context.Context) { return } - e.mergeList(d, list) + if len(list) > 0 { + log.Tracer(ctx).Tracef("intel: loaded domain lists for %s: %s", d, strings.Join(list, ", ")) + e.mergeList(d, list) + } } e.domainListLoaded = true }) @@ -389,7 +416,6 @@ func (e *Entity) getASNLists(ctx context.Context) { return } - log.Tracer(ctx).Tracef("intel: loading ASN list for %d", asn) e.loadAsnListOnce.Do(func() { asnStr := fmt.Sprintf("%d", asn) list, err := filterlists.LookupASNString(asnStr) @@ -399,8 +425,12 @@ func (e *Entity) getASNLists(ctx context.Context) { return } + if len(list) > 0 { + log.Tracer(ctx).Tracef("intel: loaded ASN lists for %s: %s", asnStr, strings.Join(list, ", ")) + e.mergeList(asnStr, list) + } + e.asnListLoaded = true - e.mergeList(asnStr, list) }) } @@ -414,7 +444,6 @@ func (e *Entity) getCountryLists(ctx context.Context) { return } - log.Tracer(ctx).Tracef("intel: loading country list for %s", country) e.loadCountryListOnce.Do(func() { list, err := filterlists.LookupCountry(country) if err != nil { @@ -423,8 +452,12 @@ func (e *Entity) getCountryLists(ctx context.Context) { return } + if len(list) > 0 { + log.Tracer(ctx).Tracef("intel: loaded country lists for %s: %s", country, strings.Join(list, ", ")) + e.mergeList(country, list) + } + e.countryListLoaded = true - e.mergeList(country, list) }) } @@ -443,7 +476,6 @@ func (e *Entity) getIPLists(ctx context.Context) { return } - log.Tracer(ctx).Tracef("intel: loading IP list for %s", ip) e.loadIPListOnce.Do(func() { list, err := filterlists.LookupIP(ip) if err != nil { @@ -452,8 +484,12 @@ func (e *Entity) getIPLists(ctx context.Context) { return } + if len(list) > 0 { + log.Tracer(ctx).Tracef("intel: loaded IP lists for %s: %s", ip.String(), strings.Join(list, ", ")) + e.mergeList(ip.String(), list) + } + e.ipListLoaded = true - e.mergeList(ip.String(), list) }) } diff --git a/intel/filterlists/index.go b/intel/filterlists/index.go index f0c511e7..a7081805 100644 --- a/intel/filterlists/index.go +++ b/intel/filterlists/index.go @@ -196,8 +196,12 @@ func updateListIndex() error { listIndexUpdate.Version(), ) default: - log.Debug("filterlists: index is up to date") // List is in cache and current, there is nothing to do. + log.Debug("filterlists: index is up to date") + + // Update the unbreak filter list IDs on initial load. + updateUnbreakFilterListIDs() + return nil } case listIndexUpdate.UpgradeAvailable(): @@ -225,6 +229,9 @@ func updateListIndex() error { } log.Debugf("intel/filterlists: updated list index in cache to %s", index.Version) + // Update the unbreak filter list IDs after an update. + updateUnbreakFilterListIDs() + return nil } @@ -252,3 +259,30 @@ func ResolveListIDs(ids []string) ([]string, error) { return resolved, nil } + +var ( + unbreakCategoryIDs = []string{"UNBREAK"} + + unbreakIDs []string + unbreakIDsLock sync.Mutex +) + +// GetUnbreakFilterListIDs returns the resolved list of all unbreak filter lists. +func GetUnbreakFilterListIDs() []string { + unbreakIDsLock.Lock() + defer unbreakIDsLock.Unlock() + + return unbreakIDs +} + +func updateUnbreakFilterListIDs() { + unbreakIDsLock.Lock() + defer unbreakIDsLock.Unlock() + + resolvedIDs, err := ResolveListIDs(unbreakCategoryIDs) + if err != nil { + log.Warningf("filter: failed to resolve unbreak filter list IDs: %s", err) + } else { + unbreakIDs = resolvedIDs + } +} diff --git a/intel/geoip/location.go b/intel/geoip/location.go index 91fb5164..c202a1f0 100644 --- a/intel/geoip/location.go +++ b/intel/geoip/location.go @@ -26,6 +26,9 @@ type Location struct { Coordinates Coordinates `maxminddb:"location"` AutonomousSystemNumber uint `maxminddb:"autonomous_system_number"` AutonomousSystemOrganization string `maxminddb:"autonomous_system_organization"` + IsAnycast bool `maxminddb:"is_anycast"` + IsSatelliteProvider bool `maxminddb:"is_satellite_provider"` + IsAnonymousProxy bool `maxminddb:"is_anonymous_proxy"` } // Coordinates holds geographic coordinates and their estimated accuracy. diff --git a/netenv/online-status.go b/netenv/online-status.go index 0ef612fd..cc9cb376 100644 --- a/netenv/online-status.go +++ b/netenv/online-status.go @@ -468,7 +468,7 @@ func checkOnlineStatus(ctx context.Context) { } request := (&http.Request{ - Method: "GET", + Method: http.MethodGet, URL: parsedPortalTestURL, Close: true, }).WithContext(ctx) @@ -495,7 +495,7 @@ func checkOnlineStatus(ctx context.Context) { } // direct response - if response.StatusCode == 200 { + if response.StatusCode == http.StatusOK { updateOnlineStatus(StatusPortal, &url.URL{ Scheme: "http", Host: SpecialCaptivePortalDomain, diff --git a/netquery/database.go b/netquery/database.go index f75899a8..c7d8befd 100644 --- a/netquery/database.go +++ b/netquery/database.go @@ -10,10 +10,10 @@ import ( "sync" "time" + "github.com/jackc/puddle/v2" "zombiezen.com/go/sqlite" "zombiezen.com/go/sqlite/sqlitex" - "github.com/jackc/puddle/v2" "github.com/safing/portbase/log" "github.com/safing/portmaster/netquery/orm" "github.com/safing/portmaster/network" @@ -108,7 +108,6 @@ type ( // (see Execute). To perform database writes use either Save() or ExecuteWrite(). // Note that write connections are serialized by the Database object before being // handed over to SQLite. -// func New(path string) (*Database, error) { constructor := func(ctx context.Context) (*sqlite.Conn, error) { c, err := sqlite.OpenConn( @@ -331,7 +330,7 @@ func (db *Database) Save(ctx context.Context, conn Conn) error { for key := range connMap { keys = append(keys, key) } - sort.Sort(sort.StringSlice(keys)) + sort.Strings(keys) for _, key := range keys { value := connMap[key] diff --git a/netquery/orm/decoder.go b/netquery/orm/decoder.go index 0387f9de..24126e97 100644 --- a/netquery/orm/decoder.go +++ b/netquery/orm/decoder.go @@ -63,7 +63,6 @@ type ( // Decoding hooks configured in cfg are executed before trying to decode basic types and may // be specified to provide support for special types. // See DatetimeDecoder() for an example of a DecodeHook that handles graceful time.Time conversion. -// func DecodeStmt(ctx context.Context, schema *TableSchema, stmt Stmt, result interface{}, cfg DecodeConfig) error { // make sure we got something to decode into ... if result == nil { diff --git a/netquery/orm/query_runner.go b/netquery/orm/query_runner.go index 2d5f01a3..55bafe30 100644 --- a/netquery/orm/query_runner.go +++ b/netquery/orm/query_runner.go @@ -94,13 +94,12 @@ func WithDecodeConfig(cfg DecodeConfig) QueryOption { // // Example: // -// var result []struct{ -// Count int `sqlite:"rowCount"` -// } -// -// err := RunQuery(ctx, conn, "SELECT COUNT(*) AS rowCount FROM table", WithResult(&result)) -// fmt.Println(result[0].Count) +// var result []struct{ +// Count int `sqlite:"rowCount"` +// } // +// err := RunQuery(ctx, conn, "SELECT COUNT(*) AS rowCount FROM table", WithResult(&result)) +// fmt.Println(result[0].Count) func RunQuery(ctx context.Context, conn *sqlite.Conn, sql string, modifiers ...QueryOption) error { args := queryOpts{ DecodeConfig: DefaultDecodeConfig, diff --git a/netquery/query.go b/netquery/query.go index 7ccda769..83dbc217 100644 --- a/netquery/query.go +++ b/netquery/query.go @@ -17,6 +17,7 @@ import ( // Collection of Query and Matcher types. // NOTE: whenever adding support for new operators make sure // to update UnmarshalJSON as well. +// //nolint:golint type ( Query map[string][]Matcher diff --git a/network/packet/packet.go b/network/packet/packet.go index b35c9f0b..78ae237c 100644 --- a/network/packet/packet.go +++ b/network/packet/packet.go @@ -125,10 +125,12 @@ func (pkt *Base) createConnectionID() { // MatchesAddress checks if a the packet matches a given endpoint (remote or local) in protocol, network and port. // // Comparison matrix: -// IN OUT +// +// ====== IN OUT +// // Local Dst Src // Remote Src Dst -//. +// . func (pkt *Base) MatchesAddress(remote bool, protocol IPProtocol, network *net.IPNet, port uint16) bool { if pkt.info.Protocol != protocol { return false @@ -154,10 +156,12 @@ func (pkt *Base) MatchesAddress(remote bool, protocol IPProtocol, network *net.I // MatchesIP checks if a the packet matches a given endpoint (remote or local) IP. // // Comparison matrix: -// IN OUT +// +// ====== IN OUT +// // Local Dst Src // Remote Src Dst -//. +// . func (pkt *Base) MatchesIP(endpoint bool, network *net.IPNet) bool { if pkt.info.Inbound != endpoint { if network.Contains(pkt.info.Src) { diff --git a/process/doc.go b/process/doc.go index 6db99ce2..6a3992fc 100644 --- a/process/doc.go +++ b/process/doc.go @@ -1,7 +1,3 @@ -/* - -Package process fetches process and socket information from the operating system. -It can find the process owning a network connection. - -*/ +// Package process fetches process and socket information from the operating system. +// It can find the process owning a network connection. package process diff --git a/resolver/doc.go b/resolver/doc.go index c188850b..f1c47d01 100644 --- a/resolver/doc.go +++ b/resolver/doc.go @@ -1,22 +1,22 @@ /* Package resolver is responsible for querying DNS. -DNS Servers +# DNS Servers Internal lists of resolvers to use are built on start and rebuilt on every config or network change. Configured DNS servers are prioritized over servers assigned by dhcp. Domain and search options (here referred to as "search scopes") are being considered. -Security +# Security Usage of DNS Servers can be regulated using the configuration: - DoNotUseAssignedDNS // Do not use DNS servers assigned by DHCP - DoNotUseMDNS // Do not use mDNS - DoNotForwardSpecialDomains // Do not forward special domains to local resolvers, except if they have a search scope for it + DoNotUseAssignedDNS // Do not use DNS servers assigned by DHCP + DoNotUseMDNS // Do not use mDNS + DoNotForwardSpecialDomains // Do not forward special domains to local resolvers, except if they have a search scope for it Note: The DHCP options "domain" and "search" are ignored for servers assigned by DHCP that do not reside within local address space. -Resolving DNS +# Resolving DNS Various different queries require the resolver to behave in different manner: @@ -24,6 +24,5 @@ Queries for "localhost." are immediately responded with 127.0.0.1 and ::1, for A Reverse lookups on local address ranges (10/8, 172.16/12, 192.168/16, fe80::/7) will be tried against every local resolver and finally mDNS until a successful, non-NXDomain answer is received. Special domains ("example.", "example.com.", "example.net.", "example.org.", "invalid.", "test.", "onion.") are resolved using search scopes and local resolvers. All other domains are resolved using search scopes and all available resolvers. - */ package resolver diff --git a/resolver/resolver-https.go b/resolver/resolver-https.go index 9776c609..be9b0d4b 100644 --- a/resolver/resolver-https.go +++ b/resolver/resolver-https.go @@ -90,7 +90,9 @@ func (hr *HTTPSResolver) Query(ctx context.Context, q *Query) (*RRCache, error) if err != nil { return nil, err } - defer resp.Body.Close() + defer func() { + _ = resp.Body.Close() + }() // Try to read the result body, err := ioutil.ReadAll(resp.Body) diff --git a/resolver/resolver.go b/resolver/resolver.go index 764b24fa..1e965688 100644 --- a/resolver/resolver.go +++ b/resolver/resolver.go @@ -30,7 +30,7 @@ const ( ServerSourceEnv = "env" ) -// DNS Resolver alias +// DNS resolver scheme aliases. const ( HTTPSProtocol = "https" TLSProtocol = "tls" @@ -117,14 +117,14 @@ func (info *ResolverInfo) ID() string { case ServerTypeEnv: info.id = ServerTypeEnv case ServerTypeDoH: - info.id = fmt.Sprintf( + info.id = fmt.Sprintf( //nolint:nosprintfhostport // Not used as URL. "https://%s:%d#%s", info.Domain, info.Port, info.Source, ) case ServerTypeDoT: - info.id = fmt.Sprintf( + info.id = fmt.Sprintf( //nolint:nosprintfhostport // Not used as URL. "dot://%s:%d#%s", info.Domain, info.Port, diff --git a/resolver/resolvers.go b/resolver/resolvers.go index bc6ecbf6..1da8cbd0 100644 --- a/resolver/resolvers.go +++ b/resolver/resolvers.go @@ -10,9 +10,9 @@ import ( "strings" "sync" + "github.com/miekg/dns" "golang.org/x/net/publicsuffix" - "github.com/miekg/dns" "github.com/safing/portbase/log" "github.com/safing/portbase/utils" "github.com/safing/portmaster/netenv" diff --git a/resolver/rrcache.go b/resolver/rrcache.go index c9e591c1..d14179c2 100644 --- a/resolver/rrcache.go +++ b/resolver/rrcache.go @@ -16,7 +16,8 @@ import ( // RRCache is a single-use structure to hold a DNS response. // Persistence is handled through NameRecords because of a limitation of the // underlying dns library. -//nolint:maligned // TODO +// +//nolint:maligned type RRCache struct { // Respnse Header Domain string diff --git a/status/threat.go b/status/threat.go index c30013e5..c7592858 100644 --- a/status/threat.go +++ b/status/threat.go @@ -37,14 +37,13 @@ type ThreatPayload struct { // // Example: // -// threat := NewThreat("portscan", "Someone is scanning you"). -// SetData(portscanResult). -// SetMitigationLevel(SecurityLevelExtreme). -// Publish() -// -// // Once you're done, delete the threat -// threat.Delete().Publish() +// threat := NewThreat("portscan", "Someone is scanning you"). +// SetData(portscanResult). +// SetMitigationLevel(SecurityLevelExtreme). +// Publish() // +// Once you're done, delete the threat +// threat.Delete().Publish() func NewThreat(id, title, msg string) *Threat { t := &Threat{ Notification: ¬ifications.Notification{ diff --git a/ui/api.go b/ui/api.go index 68c9fbe9..9eb5f6e1 100644 --- a/ui/api.go +++ b/ui/api.go @@ -1,8 +1,6 @@ package ui import ( - resources "github.com/cookieo9/resources-go" - "github.com/safing/portbase/api" "github.com/safing/portbase/log" ) @@ -22,16 +20,18 @@ func reloadUI(_ *api.Request) (msg string, err error) { appsLock.Lock() defer appsLock.Unlock() - // close all bundles. - for id, bundle := range apps { - err := bundle.Close() + // Close all archives. + for id, archiveFS := range apps { + err := archiveFS.Close() if err != nil { - log.Warningf("ui: failed to close bundle %s: %s", id, err) + log.Warningf("ui: failed to close archive %s: %s", id, err) } } // Reset index. - apps = make(map[string]*resources.BundleSequence) + for key := range apps { + delete(apps, key) + } - return "all ui bundles successfully reloaded", nil + return "all ui archives successfully reloaded", nil } diff --git a/ui/serve.go b/ui/serve.go index 64509f30..1d80a059 100644 --- a/ui/serve.go +++ b/ui/serve.go @@ -6,11 +6,12 @@ import ( "io" "net/http" "net/url" + "os" "path/filepath" "strings" "sync" - resources "github.com/cookieo9/resources-go" + "github.com/spkg/zipfs" "github.com/safing/portbase/api" "github.com/safing/portbase/log" @@ -20,7 +21,7 @@ import ( ) var ( - apps = make(map[string]*resources.BundleSequence) + apps = make(map[string]*zipfs.FileSystem) appsLock sync.RWMutex ) @@ -28,7 +29,7 @@ func registerRoutes() error { // Server assets. api.RegisterHandler( "/assets/{resPath:[a-zA-Z0-9/\\._-]+}", - &bundleServer{defaultModuleName: "assets"}, + &archiveServer{defaultModuleName: "assets"}, ) // Add slash to plain module namespaces. @@ -38,7 +39,7 @@ func registerRoutes() error { ) // Serve modules. - srv := &bundleServer{} + srv := &archiveServer{} api.RegisterHandler("/ui/modules/{moduleName:[a-z]+}/", srv) api.RegisterHandler("/ui/modules/{moduleName:[a-z]+}/{resPath:[a-zA-Z0-9/\\._-]+}", srv) @@ -51,17 +52,17 @@ func registerRoutes() error { return nil } -type bundleServer struct { +type archiveServer struct { defaultModuleName string } -func (bs *bundleServer) BelongsTo() *modules.Module { return module } +func (bs *archiveServer) BelongsTo() *modules.Module { return module } -func (bs *bundleServer) ReadPermission(*http.Request) api.Permission { return api.PermitAnyone } +func (bs *archiveServer) ReadPermission(*http.Request) api.Permission { return api.PermitAnyone } -func (bs *bundleServer) WritePermission(*http.Request) api.Permission { return api.NotSupported } +func (bs *archiveServer) WritePermission(*http.Request) api.Permission { return api.NotSupported } -func (bs *bundleServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { +func (bs *archiveServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { // Get request context. ar := api.GetAPIRequest(r) if ar == nil { @@ -85,10 +86,10 @@ func (bs *bundleServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { } appsLock.RLock() - bundle, ok := apps[moduleName] + archiveFS, ok := apps[moduleName] appsLock.RUnlock() if ok { - ServeFileFromBundle(w, r, moduleName, bundle, resPath) + ServeFileFromArchive(w, r, moduleName, archiveFS, resPath) return } @@ -105,39 +106,38 @@ func (bs *bundleServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { return } - // open bundle - newBundle, err := resources.OpenZip(zipFile.Path()) + // Open archive from disk. + archiveFS, err = zipfs.New(zipFile.Path()) if err != nil { log.Tracef("ui: error prepping module %s: %s", moduleName, err) http.Error(w, err.Error(), http.StatusInternalServerError) return } - bundle = &resources.BundleSequence{newBundle} appsLock.Lock() - apps[moduleName] = bundle + apps[moduleName] = archiveFS appsLock.Unlock() - ServeFileFromBundle(w, r, moduleName, bundle, resPath) + ServeFileFromArchive(w, r, moduleName, archiveFS, resPath) } -// ServeFileFromBundle serves a file from the given bundle. -func ServeFileFromBundle(w http.ResponseWriter, r *http.Request, bundleName string, bundle *resources.BundleSequence, path string) { - readCloser, err := bundle.Open(path) +// ServeFileFromArchive serves a file from the given archive. +func ServeFileFromArchive(w http.ResponseWriter, r *http.Request, archiveName string, archiveFS *zipfs.FileSystem, path string) { + readCloser, err := archiveFS.Open(path) if err != nil { - if errors.Is(err, resources.ErrNotFound) { + if os.IsNotExist(err) { // Check if there is a base index.html file we can serve instead. var indexErr error path = "index.html" - readCloser, indexErr = bundle.Open(path) + readCloser, indexErr = archiveFS.Open(path) if indexErr != nil { // If we cannot get an index, continue with handling the original error. - log.Tracef("ui: requested resource \"%s\" not found in bundle %s: %s", path, bundleName, err) + log.Tracef("ui: requested resource \"%s\" not found in archive %s: %s", path, archiveName, err) http.Error(w, err.Error(), http.StatusNotFound) return } } else { - log.Tracef("ui: error opening module %s: %s", bundleName, err) + log.Tracef("ui: error opening module %s: %s", archiveName, err) http.Error(w, err.Error(), http.StatusInternalServerError) return } @@ -152,12 +152,8 @@ func ServeFileFromBundle(w http.ResponseWriter, r *http.Request, bundleName stri } } - // TODO: Set content security policy - // For some reason, this breaks the ui client - // w.Header().Set("Content-Security-Policy", "default-src 'self'") - w.WriteHeader(http.StatusOK) - if r.Method != "HEAD" { + if r.Method != http.MethodHead { _, err = io.Copy(w, readCloser) if err != nil { log.Errorf("ui: failed to serve file: %s", err) diff --git a/updates/main.go b/updates/main.go index b5586a43..434256cf 100644 --- a/updates/main.go +++ b/updates/main.go @@ -5,6 +5,7 @@ import ( "flag" "fmt" "runtime" + "sync/atomic" "time" "github.com/safing/portbase/database" @@ -209,6 +210,8 @@ func DisableUpdateSchedule() error { return nil } +var updateFailedCnt = new(atomic.Int32) + func checkForUpdates(ctx context.Context) (err error) { if !forceUpdate.SetToIf(true, false) && !enableUpdates() { log.Warningf("updates: automatic updates are disabled") @@ -216,7 +219,9 @@ func checkForUpdates(ctx context.Context) (err error) { } defer func() { + // Resolve any error and and send succes notification. if err == nil { + updateFailedCnt.Store(0) log.Infof("updates: successfully checked for updates") module.Resolve(updateFailed) notifications.Notify(¬ifications.Notification{ @@ -232,7 +237,14 @@ func checkForUpdates(ctx context.Context) (err error) { }, }, }) - } else { + return + } + + // Log error in any case. + log.Errorf("updates: check failed: %s", err) + + // Do not alert user if update failed for only a few times. + if updateFailedCnt.Add(1) > 3 { notifications.NotifyWarn( updateFailed, "Update Check Failed", diff --git a/updates/upgrader.go b/updates/upgrader.go index 8af21eb7..dab2c116 100644 --- a/updates/upgrader.go +++ b/updates/upgrader.go @@ -35,8 +35,7 @@ var ( pmCtrlUpdate *updater.File pmCoreUpdate *updater.File - spnHubUpdate *updater.File - hubUpgradeStarted bool + spnHubUpdate *updater.File rawVersionRegex = regexp.MustCompile(`^[0-9]+\.[0-9]+\.[0-9]+b?\*?$`) )