safing-portmaster/spn/captain/bootstrap.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

152 lines
4.1 KiB
Go

package captain
import (
"errors"
"flag"
"fmt"
"io/fs"
"os"
"github.com/safing/portmaster/base/log"
"github.com/safing/portmaster/spn/conf"
"github.com/safing/portmaster/spn/hub"
"github.com/safing/portmaster/spn/navigator"
"github.com/safing/structures/dsd"
)
// BootstrapFile is used for sideloading bootstrap data.
type BootstrapFile struct {
Main BootstrapFileEntry
}
// BootstrapFileEntry is the bootstrap data structure for one map.
type BootstrapFileEntry struct {
Hubs []string
}
var (
bootstrapHubFlag string
bootstrapFileFlag string
)
func init() {
flag.StringVar(&bootstrapHubFlag, "bootstrap-hub", "", "transport address of hub for bootstrapping with the hub ID in the fragment")
flag.StringVar(&bootstrapFileFlag, "bootstrap-file", "", "bootstrap file containing bootstrap hubs - will be initialized if running a public hub and it doesn't exist")
}
// prepBootstrapHubFlag checks the bootstrap-hub argument if it is valid.
func prepBootstrapHubFlag() error {
if bootstrapHubFlag != "" {
_, _, _, err := hub.ParseBootstrapHub(bootstrapHubFlag)
return err
}
return nil
}
// processBootstrapHubFlag processes the bootstrap-hub argument.
func processBootstrapHubFlag() error {
if bootstrapHubFlag != "" {
return navigator.Main.AddBootstrapHubs([]string{bootstrapHubFlag})
}
return nil
}
// processBootstrapFileFlag processes the bootstrap-file argument.
func processBootstrapFileFlag() error {
if bootstrapFileFlag == "" {
return nil
}
_, err := os.Stat(bootstrapFileFlag)
if err != nil {
if errors.Is(err, fs.ErrNotExist) {
return createBootstrapFile(bootstrapFileFlag)
}
return fmt.Errorf("failed to access bootstrap hub file: %w", err)
}
return loadBootstrapFile(bootstrapFileFlag)
}
// bootstrapWithUpdates loads bootstrap hubs from the updates server and imports them.
func bootstrapWithUpdates() error {
if bootstrapFileFlag != "" {
return errors.New("using the bootstrap-file argument disables bootstrapping via the update system")
}
return updateSPNIntel(module.mgr.Ctx(), nil)
}
// loadBootstrapFile loads a file with bootstrap hub entries and imports them.
func loadBootstrapFile(filename string) (err error) {
// Load bootstrap file from disk and parse it.
data, err := os.ReadFile(filename)
if err != nil {
return fmt.Errorf("failed to load bootstrap file: %w", err)
}
bootstrapFile := &BootstrapFile{}
_, err = dsd.Load(data, bootstrapFile)
if err != nil {
return fmt.Errorf("failed to parse bootstrap file: %w", err)
}
if len(bootstrapFile.Main.Hubs) == 0 {
return errors.New("bootstrap holds no hubs for main map")
}
// Add Hubs to map.
err = navigator.Main.AddBootstrapHubs(bootstrapFile.Main.Hubs)
if err == nil {
log.Infof("spn/captain: loaded bootstrap file %s", filename)
}
return err
}
// createBootstrapFile save a bootstrap hub file with an entry of the public identity.
func createBootstrapFile(filename string) error {
if !conf.PublicHub() {
log.Infof("spn/captain: skipped writing a bootstrap hub file, as this is not a public hub")
return nil
}
// create bootstrap hub
if len(publicIdentity.Hub.Info.Transports) == 0 {
return errors.New("public identity has no transports available")
}
// parse first transport
t, err := hub.ParseTransport(publicIdentity.Hub.Info.Transports[0])
if err != nil {
return fmt.Errorf("failed to parse transport of public identity: %w", err)
}
// add IP address
switch {
case publicIdentity.Hub.Info.IPv4 != nil:
t.Domain = publicIdentity.Hub.Info.IPv4.String()
case publicIdentity.Hub.Info.IPv6 != nil:
t.Domain = "[" + publicIdentity.Hub.Info.IPv6.String() + "]"
default:
return errors.New("public identity has no IP address available")
}
// add Hub ID
t.Option = publicIdentity.Hub.ID
// put together
bs := &BootstrapFile{
Main: BootstrapFileEntry{
Hubs: []string{t.String()},
},
}
// serialize
fileData, err := dsd.Dump(bs, dsd.JSON)
if err != nil {
return err
}
// save to disk
err = os.WriteFile(filename, fileData, 0o0664) //nolint:gosec // Should be able to be read by others.
if err != nil {
return err
}
log.Infof("spn/captain: created bootstrap file %s", filename)
return nil
}