safing-portmaster/spn/access/zones.go
Daniel Hååvi 80664d1a27
Restructure modules ()
* 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

251 lines
7 KiB
Go

package access
import (
"errors"
"fmt"
"os"
"strings"
"github.com/tevino/abool"
"github.com/safing/jess/lhash"
"github.com/safing/portmaster/base/log"
"github.com/safing/portmaster/spn/access/token"
"github.com/safing/portmaster/spn/conf"
"github.com/safing/portmaster/spn/terminal"
)
var (
// ExpandAndConnectZones are the zones that grant access to the expand and
// connect operations.
ExpandAndConnectZones = []string{"pblind1", "alpha2", "fallback1"}
zonePermissions = map[string]terminal.Permission{
"pblind1": terminal.AddPermissions(terminal.MayExpand, terminal.MayConnect),
"alpha2": terminal.AddPermissions(terminal.MayExpand, terminal.MayConnect),
"fallback1": terminal.AddPermissions(terminal.MayExpand, terminal.MayConnect),
}
persistentZones = ExpandAndConnectZones
enableTestMode = abool.New()
)
// EnableTestMode enables the test mode, leading the access module to only
// register a test zone.
// This should not be used to test the access module itself.
func EnableTestMode() {
enableTestMode.Set()
}
// InitializeZones initialized the permission zones.
// It initializes the test zones, if EnableTestMode was called before.
// Must only be called once.
func InitializeZones() error {
// Check if we are testing.
if enableTestMode.IsSet() {
return initializeTestZone()
}
// Special client zone config.
var requestSignalHandler func(token.Handler)
if conf.Integrated() {
requestSignalHandler = shouldRequestTokensHandler
}
// Register pblind1 as the first primary zone.
ph, err := token.NewPBlindHandler(token.PBlindOptions{
Zone: "pblind1",
CurveName: "P-256",
PublicKey: "eXoJXzXbM66UEsM2eVi9HwyBPLMfVnNrC7gNrsfMUJDs",
UseSerials: true,
BatchSize: 1000,
RandomizeOrder: true,
SignalShouldRequest: requestSignalHandler,
})
if err != nil {
return fmt.Errorf("failed to create pblind1 token handler: %w", err)
}
err = token.RegisterPBlindHandler(ph)
if err != nil {
return fmt.Errorf("failed to register pblind1 token handler: %w", err)
}
// Register fallback1 zone as fallback when the issuer is not available.
sh, err := token.NewScrambleHandler(token.ScrambleOptions{
Zone: "fallback1",
Algorithm: lhash.BLAKE2b_256,
InitialVerifiers: []string{"ZwkQoaAttVBMURzeLzNXokFBMAMUUwECfM1iHojcVKBmjk"},
Fallback: true,
})
if err != nil {
return fmt.Errorf("failed to create fallback1 token handler: %w", err)
}
err = token.RegisterScrambleHandler(sh)
if err != nil {
return fmt.Errorf("failed to register fallback1 token handler: %w", err)
}
// Register alpha2 zone for transition phase.
sh, err = token.NewScrambleHandler(token.ScrambleOptions{
Zone: "alpha2",
Algorithm: lhash.BLAKE2b_256,
InitialVerifiers: []string{"ZwojEvXZmAv7SZdNe7m94Xzu7F9J8vULqKf7QYtoTpN2tH"},
})
if err != nil {
return fmt.Errorf("failed to create alpha2 token handler: %w", err)
}
err = token.RegisterScrambleHandler(sh)
if err != nil {
return fmt.Errorf("failed to register alpha2 token handler: %w", err)
}
return nil
}
func initializeTestZone() error {
// Safeguard checks if we should really enable the test zone.
if !strings.HasSuffix(os.Args[0], ".test") {
return errors.New("tried to enable test mode, but no test binary was detected")
}
if token.RegistrySize() > 0 {
return fmt.Errorf("tried to enable test zone, but %d handlers are already registered", token.RegistrySize())
}
// Reset zones.
token.ResetRegistry()
// Set eligible zones.
ExpandAndConnectZones = []string{"unittest"}
zonePermissions = map[string]terminal.Permission{
"unittest": terminal.AddPermissions(terminal.MayExpand, terminal.MayConnect),
}
// Register unittest zone as for testing.
sh, err := token.NewScrambleHandler(token.ScrambleOptions{
Zone: "unittest",
Algorithm: lhash.BLAKE2b_256,
InitialTokens: []string{"6jFqLA93uSLL52utGKrvctG3ZfopSQ8WFqjsRK1c2Svt"},
InitialVerifiers: []string{"ZwoEoL59sr81s7WnF2vydGzjeejE3u8CqVafig1NTQzUr7"},
})
if err != nil {
return fmt.Errorf("failed to create unittest token handler: %w", err)
}
err = token.RegisterScrambleHandler(sh)
if err != nil {
return fmt.Errorf("failed to register unittest token handler: %w", err)
}
return nil
}
func shouldRequestTokensHandler(_ token.Handler) {
// Run the account update task as now.
module.updateAccountWorkerMgr.Go()
}
// GetTokenAmount returns the amount of tokens for the given zones.
func GetTokenAmount(zones []string) (regular, fallback int) {
handlerLoop:
for _, zone := range zones {
// Get handler and check if it should be used.
handler, ok := token.GetHandler(zone)
if !ok {
log.Warningf("spn/access: use of non-registered zone %q", zone)
continue handlerLoop
}
if handler.IsFallback() {
fallback += handler.Amount()
} else {
regular += handler.Amount()
}
}
return
}
// ShouldRequest returns whether tokens should be requested for the given zones.
func ShouldRequest(zones []string) (shouldRequest bool) {
handlerLoop:
for _, zone := range zones {
// Get handler and check if it should be used.
handler, ok := token.GetHandler(zone)
if !ok {
log.Warningf("spn/access: use of non-registered zone %q", zone)
continue handlerLoop
}
// Go through all handlers every time as this will be the case anyway most
// of the time and will help us better catch zone misconfiguration.
if handler.ShouldRequest() {
shouldRequest = true
}
}
return shouldRequest
}
// GetToken returns a token of one of the given zones.
func GetToken(zones []string) (t *token.Token, err error) {
handlerSelection:
for _, zone := range zones {
// Get handler and check if it should be used.
handler, ok := token.GetHandler(zone)
switch {
case !ok:
log.Warningf("spn/access: use of non-registered zone %q", zone)
continue handlerSelection
case handler.IsFallback() && !TokenIssuerIsFailing():
// Skip fallback zone if everything works.
continue handlerSelection
}
// Get token from handler.
t, err = token.GetToken(zone)
if err == nil {
return t, nil
}
}
// Return existing error, if exists.
if err != nil {
return nil, err
}
return nil, token.ErrEmpty
}
// VerifyRawToken verifies a raw token.
func VerifyRawToken(data []byte) (granted terminal.Permission, err error) {
t, err := token.ParseRawToken(data)
if err != nil {
return 0, fmt.Errorf("failed to parse token: %w", err)
}
return VerifyToken(t)
}
// VerifyToken verifies a token.
func VerifyToken(t *token.Token) (granted terminal.Permission, err error) {
handler, ok := token.GetHandler(t.Zone)
if !ok {
return terminal.NoPermission, token.ErrZoneUnknown
}
// Check if the token is a fallback token.
if handler.IsFallback() && !healthCheck() {
return terminal.NoPermission, ErrFallbackNotAvailable
}
// Verify token.
err = handler.Verify(t)
if err != nil {
return 0, fmt.Errorf("failed to verify token: %w", err)
}
// Return permission of zone.
granted, ok = zonePermissions[t.Zone]
if !ok {
return terminal.NoPermission, nil
}
return granted, nil
}