safing-portmaster/service/firewall/module.go
Daniel Hååvi 80664d1a27
Restructure modules (#1572)
* Move portbase into monorepo

* Add new simple module mgr

* [WIP] Switch to new simple module mgr

* Add StateMgr and more worker variants

* [WIP] Switch more modules

* [WIP] Switch more modules

* [WIP] swtich more modules

* [WIP] switch all SPN modules

* [WIP] switch all service modules

* [WIP] Convert all workers to the new module system

* [WIP] add new task system to module manager

* [WIP] Add second take for scheduling workers

* [WIP] Add FIXME for bugs in new scheduler

* [WIP] Add minor improvements to scheduler

* [WIP] Add new worker scheduler

* [WIP] Fix more bug related to new module system

* [WIP] Fix start handing of the new module system

* [WIP] Improve startup process

* [WIP] Fix minor issues

* [WIP] Fix missing subsystem in settings

* [WIP] Initialize managers in constructor

* [WIP] Move module event initialization to constrictors

* [WIP] Fix setting for enabling and disabling the SPN module

* [WIP] Move API registeration into module construction

* [WIP] Update states mgr for all modules

* [WIP] Add CmdLine operation support

* Add state helper methods to module group and instance

* Add notification and module status handling to status package

* Fix starting issues

* Remove pilot widget and update security lock to new status data

* Remove debug logs

* Improve http server shutdown

* Add workaround for cleanly shutting down firewall+netquery

* Improve logging

* Add syncing states with notifications for new module system

* Improve starting, stopping, shutdown; resolve FIXMEs/TODOs

* [WIP] Fix most unit tests

* Review new module system and fix minor issues

* Push shutdown and restart events again via API

* Set sleep mode via interface

* Update example/template module

* [WIP] Fix spn/cabin unit test

* Remove deprecated UI elements

* Make log output more similar for the logging transition phase

* Switch spn hub and observer cmds to new module system

* Fix log sources

* Make worker mgr less error prone

* Fix tests and minor issues

* Fix observation hub

* Improve shutdown and restart handling

* Split up big connection.go source file

* Move varint and dsd packages to structures repo

* Improve expansion test

* Fix linter warnings

* Fix interception module on windows

* Fix linter errors

---------

Co-authored-by: Vladimir Stoilov <vladimir@safing.io>
2024-08-09 18:15:48 +03:00

168 lines
4.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package firewall
import (
"errors"
"flag"
"fmt"
"path/filepath"
"strings"
"sync/atomic"
"time"
"github.com/safing/portmaster/base/config"
"github.com/safing/portmaster/base/log"
_ "github.com/safing/portmaster/service/core"
"github.com/safing/portmaster/service/mgr"
"github.com/safing/portmaster/service/netquery"
"github.com/safing/portmaster/service/network"
"github.com/safing/portmaster/service/profile"
"github.com/safing/portmaster/spn/access"
"github.com/safing/portmaster/spn/captain"
)
type stringSliceFlag []string
func (ss *stringSliceFlag) String() string {
return strings.Join(*ss, ":")
}
func (ss *stringSliceFlag) Set(value string) error {
*ss = append(*ss, filepath.Clean(value))
return nil
}
var allowedClients stringSliceFlag
type Firewall struct {
mgr *mgr.Manager
instance instance
}
func init() {
flag.Var(&allowedClients, "allowed-clients", "A list of binaries that are allowed to connect to the Portmaster API")
}
func (f *Firewall) Manager() *mgr.Manager {
return f.mgr
}
func (f *Firewall) Start() error {
if err := prep(); err != nil {
log.Errorf("Failed to prepare firewall module %q", err)
return err
}
return start()
}
func (f *Firewall) Stop() error {
// Cancel all workers and give them a little time.
// The bandwidth updater can crash the sqlite DB for some reason.
// TODO: Investigate.
f.mgr.Cancel()
time.Sleep(100 * time.Millisecond)
return stop()
}
func prep() error {
network.SetDefaultFirewallHandler(defaultFirewallHandler)
// Reset connections every time configuration changes
// this will be triggered on spn enable/disable
module.instance.Config().EventConfigChange.AddCallback("reset connection verdicts after global config change", func(w *mgr.WorkerCtx, _ struct{}) (bool, error) {
resetAllConnectionVerdicts()
return false, nil
})
module.instance.Profile().EventConfigChange.AddCallback("reset connection verdicts after profile config change",
func(m *mgr.WorkerCtx, profileID string) (bool, error) {
// Expected event data: scoped profile ID.
profileSource, profileID, ok := strings.Cut(profileID, "/")
if !ok {
return false, fmt.Errorf("event data does not seem to be a scoped profile ID: %v", profileID)
}
resetProfileConnectionVerdict(profileSource, profileID)
return false, nil
},
)
// Reset connections when spn is connected
// connect and disconnecting is triggered on config change event but connecting takеs more time
module.instance.Captain().EventSPNConnected.AddCallback("reset connection verdicts on SPN connect", func(wc *mgr.WorkerCtx, s struct{}) (cancel bool, err error) {
resetAllConnectionVerdicts()
return false, err
})
// Reset connections when account is updated.
// This will not change verdicts, but will update the feature flags on connections.
module.instance.Access().EventAccountUpdate.AddCallback("update connection feature flags after account update", func(wc *mgr.WorkerCtx, s struct{}) (cancel bool, err error) {
resetAllConnectionVerdicts()
return false, err
})
module.instance.Network().EventConnectionReattributed.AddCallback("reset connection verdicts after connection re-attribution", func(wc *mgr.WorkerCtx, connID string) (cancel bool, err error) {
// Expected event data: connection ID.
resetSingleConnectionVerdict(connID)
return false, err
})
return nil
}
func start() error {
getConfig()
startAPIAuth()
module.mgr.Go("packet handler", packetHandler)
module.mgr.Go("bandwidth update handler", bandwidthUpdateHandler)
// Start stat logger if logging is set to trace.
if log.GetLogLevel() == log.TraceLevel {
module.mgr.Go("stat logger", statLogger)
}
return nil
}
func stop() error {
return nil
}
var (
module *Firewall
shimLoaded atomic.Bool
)
func New(instance instance) (*Firewall, error) {
if !shimLoaded.CompareAndSwap(false, true) {
return nil, errors.New("only one instance allowed")
}
m := mgr.New("Firewall")
module = &Firewall{
mgr: m,
instance: instance,
}
if err := prepAPIAuth(); err != nil {
return nil, err
}
if err := registerConfig(); err != nil {
return nil, err
}
return module, nil
}
type instance interface {
Config() *config.Config
Profile() *profile.ProfileModule
Captain() *captain.Captain
Access() *access.Access
Network() *network.Network
NetQuery() *netquery.NetQuery
}