mirror of
https://github.com/safing/portbase
synced 2025-09-01 18:19:57 +00:00
Merge branch 'feature/config' of Safing/portbase into develop
This commit is contained in:
commit
b890583a17
10 changed files with 327 additions and 539 deletions
57
config/get.go
Normal file
57
config/get.go
Normal file
|
@ -0,0 +1,57 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/tevino/abool"
|
||||
)
|
||||
|
||||
var (
|
||||
validityFlag = abool.NewBool(true)
|
||||
validityFlagLock sync.RWMutex
|
||||
|
||||
tableLock sync.RWMutex
|
||||
|
||||
stringTable map[string]string
|
||||
intTable map[string]int
|
||||
boolTable map[string]bool
|
||||
)
|
||||
|
||||
func getValidityFlag() *abool.AtomicBool {
|
||||
validityFlagLock.RLock()
|
||||
defer validityFlagLock.RUnlock()
|
||||
return validityFlag
|
||||
}
|
||||
|
||||
func resetValidityFlag() {
|
||||
validityFlagLock.Lock()
|
||||
defer validityFlagLock.Unlock()
|
||||
validityFlag.SetTo(false)
|
||||
validityFlag = abool.NewBool(true)
|
||||
}
|
||||
|
||||
// GetAsString returns a function that returns the wanted string with high performance.
|
||||
func GetAsString(name string, fallback string) func() string {
|
||||
valid := getValidityFlag()
|
||||
value := findStringValue(name, fallback)
|
||||
return func() string {
|
||||
if !valid.IsSet() {
|
||||
valid = getValidityFlag()
|
||||
value = findStringValue(name, fallback)
|
||||
}
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
// GetAsInt returns a function that returns the wanted int with high performance.
|
||||
func GetAsInt(name string, fallback int64) func() int64 {
|
||||
valid := getValidityFlag()
|
||||
value := findIntValue(name, fallback)
|
||||
return func() int64 {
|
||||
if !valid.IsSet() {
|
||||
valid = getValidityFlag()
|
||||
value = findIntValue(name, fallback)
|
||||
}
|
||||
return value
|
||||
}
|
||||
}
|
137
config/get_test.go
Normal file
137
config/get_test.go
Normal file
|
@ -0,0 +1,137 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
|
||||
err := SetConfig(`
|
||||
{
|
||||
"monkey": "1",
|
||||
"elephant": 2
|
||||
}
|
||||
`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = SetDefaultConfig(`
|
||||
{
|
||||
"monkey": "0",
|
||||
"snake": "0",
|
||||
"elephant": 0
|
||||
}
|
||||
`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
monkey := GetAsString("monkey", "none")
|
||||
elephant := GetAsInt("elephant", -1)
|
||||
if monkey() != "1" {
|
||||
t.Fatalf("monkey should be 1, is %s", monkey())
|
||||
}
|
||||
if elephant() != 2 {
|
||||
t.Fatalf("elephant should be 2, is %d", elephant())
|
||||
}
|
||||
|
||||
err = SetConfig(`
|
||||
{
|
||||
"monkey": "3"
|
||||
}
|
||||
`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if monkey() != "3" {
|
||||
t.Fatalf("monkey should be 0, is %s", monkey())
|
||||
}
|
||||
if elephant() != 0 {
|
||||
t.Fatalf("elephant should be 0, is %d", elephant())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func BenchmarkGetAsStringCached(b *testing.B) {
|
||||
// Setup
|
||||
err := SetConfig(`
|
||||
{
|
||||
"monkey": "banana"
|
||||
}
|
||||
`)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
monkey := GetAsString("monkey", "no banana")
|
||||
|
||||
// Reset timer for precise results
|
||||
b.ResetTimer()
|
||||
|
||||
// Start benchmark
|
||||
for i := 0; i < b.N; i++ {
|
||||
monkey()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkGetAsStringRefetch(b *testing.B) {
|
||||
// Setup
|
||||
err := SetConfig(`
|
||||
{
|
||||
"monkey": "banana"
|
||||
}
|
||||
`)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
// Reset timer for precise results
|
||||
b.ResetTimer()
|
||||
|
||||
// Start benchmark
|
||||
for i := 0; i < b.N; i++ {
|
||||
findStringValue("monkey", "no banana")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkGetAsIntCached(b *testing.B) {
|
||||
// Setup
|
||||
err := SetConfig(`
|
||||
{
|
||||
"monkey": 1
|
||||
}
|
||||
`)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
monkey := GetAsInt("monkey", -1)
|
||||
|
||||
// Reset timer for precise results
|
||||
b.ResetTimer()
|
||||
|
||||
// Start benchmark
|
||||
for i := 0; i < b.N; i++ {
|
||||
monkey()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkGetAsIntRefetch(b *testing.B) {
|
||||
// Setup
|
||||
err := SetConfig(`
|
||||
{
|
||||
"monkey": 1
|
||||
}
|
||||
`)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
// Reset timer for precise results
|
||||
b.ResetTimer()
|
||||
|
||||
// Start benchmark
|
||||
for i := 0; i < b.N; i++ {
|
||||
findIntValue("monkey", 1)
|
||||
}
|
||||
}
|
82
config/layers.go
Normal file
82
config/layers.go
Normal file
|
@ -0,0 +1,82 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
var (
|
||||
configLock sync.RWMutex
|
||||
|
||||
userConfig = ""
|
||||
defaultConfig = ""
|
||||
|
||||
// ErrInvalidJSON is returned by SetConfig and SetDefaultConfig if they receive invalid json.
|
||||
ErrInvalidJSON = errors.New("json string invalid")
|
||||
)
|
||||
|
||||
// SetConfig sets the (prioritized) user defined config.
|
||||
func SetConfig(json string) error {
|
||||
if !gjson.Valid(json) {
|
||||
return ErrInvalidJSON
|
||||
}
|
||||
|
||||
configLock.Lock()
|
||||
defer configLock.Unlock()
|
||||
userConfig = json
|
||||
resetValidityFlag()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetDefaultConfig sets the (fallback) default config.
|
||||
func SetDefaultConfig(json string) error {
|
||||
if !gjson.Valid(json) {
|
||||
return ErrInvalidJSON
|
||||
}
|
||||
|
||||
configLock.Lock()
|
||||
defer configLock.Unlock()
|
||||
defaultConfig = json
|
||||
resetValidityFlag()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// findValue find the correct value in the user or default config
|
||||
func findValue(name string) (result gjson.Result) {
|
||||
configLock.RLock()
|
||||
defer configLock.RUnlock()
|
||||
|
||||
result = gjson.Get(userConfig, name)
|
||||
if !result.Exists() {
|
||||
result = gjson.Get(defaultConfig, name)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// findStringValue validates and return the value with the given name
|
||||
func findStringValue(name string, fallback string) (value string) {
|
||||
result := findValue(name)
|
||||
if !result.Exists() {
|
||||
return fallback
|
||||
}
|
||||
if result.Type != gjson.String {
|
||||
return fallback
|
||||
}
|
||||
return result.String()
|
||||
}
|
||||
|
||||
// findIntValue validates and return the value with the given name
|
||||
func findIntValue(name string, fallback int64) (value int64) {
|
||||
result := findValue(name)
|
||||
if !result.Exists() {
|
||||
return fallback
|
||||
}
|
||||
if result.Type != gjson.Number {
|
||||
return fallback
|
||||
}
|
||||
return result.Int()
|
||||
}
|
51
config/layers_test.go
Normal file
51
config/layers_test.go
Normal file
|
@ -0,0 +1,51 @@
|
|||
package config
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestLayers(t *testing.T) {
|
||||
|
||||
err := SetConfig("{invalid json")
|
||||
if err == nil {
|
||||
t.Fatal("expected error")
|
||||
}
|
||||
|
||||
err = SetDefaultConfig("{invalid json")
|
||||
if err == nil {
|
||||
t.Fatal("expected error")
|
||||
}
|
||||
|
||||
err = SetConfig(`
|
||||
{
|
||||
"monkey": "banana",
|
||||
"elephant": 3
|
||||
}
|
||||
`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Test missing values
|
||||
|
||||
missingString := GetAsString("missing", "fallback")
|
||||
if missingString() != "fallback" {
|
||||
t.Fatal("expected fallback value: fallback")
|
||||
}
|
||||
|
||||
missingInt := GetAsInt("missing", -1)
|
||||
if missingInt() != -1 {
|
||||
t.Fatal("expected fallback value: -1")
|
||||
}
|
||||
|
||||
// Test value mismatch
|
||||
|
||||
notString := GetAsString("elephant", "fallback")
|
||||
if notString() != "fallback" {
|
||||
t.Fatal("expected fallback value: fallback")
|
||||
}
|
||||
|
||||
notInt := GetAsInt("monkey", -1)
|
||||
if notInt() != -1 {
|
||||
t.Fatal("expected fallback value: -1")
|
||||
}
|
||||
|
||||
}
|
|
@ -1,96 +0,0 @@
|
|||
// Copyright Safing ICS Technologies GmbH. Use of this source code is governed by the AGPL license that can be found in the LICENSE file.
|
||||
|
||||
package configuration
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/Safing/safing-core/database"
|
||||
|
||||
datastore "github.com/ipfs/go-datastore"
|
||||
)
|
||||
|
||||
type SecurityLevelBoolean int8
|
||||
|
||||
func (slb SecurityLevelBoolean) IsSet() bool {
|
||||
return int8(atomic.LoadInt32(securityLevel)) >= int8(slb)
|
||||
}
|
||||
|
||||
func (slb SecurityLevelBoolean) IsSetWithLevel(customSecurityLevel int8) bool {
|
||||
return customSecurityLevel >= int8(slb) || int8(atomic.LoadInt32(securityLevel)) >= int8(slb)
|
||||
}
|
||||
|
||||
func (slb SecurityLevelBoolean) Level() int8 {
|
||||
return int8(slb)
|
||||
}
|
||||
|
||||
type Configuration struct {
|
||||
database.Base
|
||||
|
||||
// Security Config
|
||||
EnforceCT SecurityLevelBoolean `json:",omitempty bson:",omitempty"` // Hardfail on Certificate Transparency
|
||||
EnforceRevocation SecurityLevelBoolean `json:",omitempty bson:",omitempty"` // Hardfail on Certificate Revokation
|
||||
DenyInsecureTLS SecurityLevelBoolean `json:",omitempty bson:",omitempty"` // Block TLS connections, that use insecure TLS versions, cipher suites, ...
|
||||
DenyTLSWithoutSNI SecurityLevelBoolean `json:",omitempty bson:",omitempty"` // Block TLS connections that do not use SNI, connections without SNI cannot be verified as well as connections with SNI.
|
||||
DoNotUseAssignedDNS SecurityLevelBoolean `json:",omitempty bson:",omitempty"` // Do not use DNS Servers assigned by DHCP
|
||||
DoNotUseMDNS SecurityLevelBoolean `json:",omitempty bson:",omitempty"` // Do not use mDNS
|
||||
DoNotForwardSpecialDomains SecurityLevelBoolean `json:",omitempty bson:",omitempty"` // Do not resolve special domains with assigned DNS Servers
|
||||
AlwaysPromptAtNewProfile SecurityLevelBoolean `json:",omitempty bson:",omitempty"` // Always prompt user to review new profiles
|
||||
DenyNetworkUntilProfileApproved SecurityLevelBoolean `json:",omitempty bson:",omitempty"` // Deny network communication until a new profile is actively approved by the user
|
||||
|
||||
// Generic Config
|
||||
CompetenceLevel int8 `json:",omitempty bson:",omitempty"` // Select CompetenceLevel
|
||||
Beta bool `json:",omitempty bson:",omitempty"` // Take part in Beta
|
||||
PermanentVerdicts bool `json:",omitempty bson:",omitempty"` // As soon as work on a link is finished, leave it to the system for performance and stability
|
||||
DNSServers []string `json:",omitempty bson:",omitempty"` // DNS Servers to use for name resolution. Please refer to the user guide for further help.
|
||||
// regex: ^(DoH|DNS|TDNS)\|[A-Za-z0-9\.:\[\]]+(\|[A-Za-z0-9\.:]+)?$
|
||||
DNSServerRetryRate int64 `json:",omitempty bson:",omitempty"` // Amount of seconds to wait until failing DNS Servers may be retried.
|
||||
CountryBlacklist []string `json:",omitempty bson:",omitempty"` // Do not connect to servers in these countries
|
||||
ASBlacklist []uint32 `json:",omitempty bson:",omitempty"` // Do not connect to server in these AS
|
||||
|
||||
LocalPort17Node bool `json:",omitempty bson:",omitempty"` // Serve as local Port17 Node
|
||||
PublicPort17Node bool `json:",omitempty bson:",omitempty"` // Serve as public Port17 Node
|
||||
}
|
||||
|
||||
var (
|
||||
configurationModel *Configuration // only use this as parameter for database.EnsureModel-like functions
|
||||
configurationInstanceName = "config"
|
||||
defaultConfigurationInstanceName = "default"
|
||||
)
|
||||
|
||||
func initConfigurationModel() {
|
||||
database.RegisterModel(configurationModel, func() database.Model { return new(Configuration) })
|
||||
}
|
||||
|
||||
// Create saves Configuration with the provided name in the default namespace.
|
||||
func (m *Configuration) Create(name string) error {
|
||||
return m.CreateObject(&database.Me, name, m)
|
||||
}
|
||||
|
||||
// CreateInNamespace saves Configuration with the provided name in the provided namespace.
|
||||
func (m *Configuration) CreateInNamespace(namespace *datastore.Key, name string) error {
|
||||
return m.CreateObject(namespace, name, m)
|
||||
}
|
||||
|
||||
// Save saves Configuration.
|
||||
func (m *Configuration) Save() error {
|
||||
return m.SaveObject(m)
|
||||
}
|
||||
|
||||
// GetConfiguration fetches Configuration with the provided name in the default namespace.
|
||||
func GetConfiguration(name string) (*Configuration, error) {
|
||||
return GetConfigurationFromNamespace(&database.Me, name)
|
||||
}
|
||||
|
||||
// GetConfigurationFromNamespace fetches Configuration with the provided name in the provided namespace.
|
||||
func GetConfigurationFromNamespace(namespace *datastore.Key, name string) (*Configuration, error) {
|
||||
object, err := database.GetAndEnsureModel(namespace, name, configurationModel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
model, ok := object.(*Configuration)
|
||||
if !ok {
|
||||
return nil, database.NewMismatchError(object, configurationModel)
|
||||
}
|
||||
return model, nil
|
||||
}
|
|
@ -1,238 +0,0 @@
|
|||
// Copyright Safing ICS Technologies GmbH. Use of this source code is governed by the AGPL license that can be found in the LICENSE file.
|
||||
|
||||
package configuration
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/Safing/safing-core/database"
|
||||
"github.com/Safing/safing-core/log"
|
||||
"github.com/Safing/safing-core/modules"
|
||||
)
|
||||
|
||||
// think about:
|
||||
// config changes validation (e.g. if on in secure mode, must be on in fortress mode)
|
||||
// config switches
|
||||
// small codebase
|
||||
// nice api
|
||||
// be static as much as possible
|
||||
|
||||
const (
|
||||
SecurityLevelOff int8 = 0
|
||||
SecurityLevelDynamic int8 = 1
|
||||
SecurityLevelSecure int8 = 2
|
||||
SecurityLevelFortress int8 = 3
|
||||
|
||||
CompetenceLevelNone int8 = 0
|
||||
CompetenceLevelBasic int8 = 1
|
||||
CompetenceLevelPowerUser int8 = 2
|
||||
CompetenceLevelExpert int8 = 3
|
||||
|
||||
StatusOk int8 = 0
|
||||
StatusWarning int8 = 1
|
||||
StatusError int8 = 2
|
||||
)
|
||||
|
||||
var (
|
||||
configurationModule *modules.Module
|
||||
|
||||
lastChange *int64
|
||||
securityLevel *int32
|
||||
|
||||
lock sync.RWMutex
|
||||
status *SystemStatus
|
||||
currentConfig *Configuration
|
||||
)
|
||||
|
||||
func init() {
|
||||
configurationModule = modules.Register("Configuration", 128)
|
||||
|
||||
initDefaultConfig()
|
||||
initSystemStatusModel()
|
||||
initConfigurationModel()
|
||||
|
||||
lastChangeValue := time.Now().Unix()
|
||||
lastChange = &lastChangeValue
|
||||
|
||||
var securityLevelValue int32
|
||||
securityLevel = &securityLevelValue
|
||||
|
||||
var err error
|
||||
config, err := GetConfiguration(configurationInstanceName)
|
||||
if err != nil {
|
||||
log.Warningf("configuration: could not load configuration: %s", err)
|
||||
loadedConfig := defaultConfig
|
||||
config = &loadedConfig
|
||||
err = config.Create(configurationInstanceName)
|
||||
if err != nil {
|
||||
log.Warningf("configuration: could not save new configuration: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
status, err = GetSystemStatus()
|
||||
if err != nil {
|
||||
log.Warningf("configuration: could not load status: %s", err)
|
||||
status = &SystemStatus{
|
||||
CurrentSecurityLevel: 1,
|
||||
SelectedSecurityLevel: 1,
|
||||
}
|
||||
err = status.Create()
|
||||
if err != nil {
|
||||
log.Warningf("configuration: could not save new status: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
log.Infof("configuration: initial security level is [%s]", status.FmtSecurityLevel())
|
||||
// atomic.StoreInt32(securityLevel, int32(status.CurrentSecurityLevel))
|
||||
|
||||
updateConfig(config)
|
||||
|
||||
go configChangeListener()
|
||||
go statusChangeListener()
|
||||
}
|
||||
|
||||
func configChangeListener() {
|
||||
sub := database.NewSubscription()
|
||||
sub.Subscribe(fmt.Sprintf("%s/Configuration:%s", database.Me.String(), configurationInstanceName))
|
||||
for {
|
||||
var receivedModel database.Model
|
||||
|
||||
select {
|
||||
case <-configurationModule.Stop:
|
||||
configurationModule.StopComplete()
|
||||
return
|
||||
case receivedModel = <-sub.Updated:
|
||||
case receivedModel = <-sub.Created:
|
||||
}
|
||||
|
||||
config, ok := database.SilentEnsureModel(receivedModel, configurationModel).(*Configuration)
|
||||
if !ok {
|
||||
log.Warning("configuration: received config update, but was not of type *Configuration")
|
||||
continue
|
||||
}
|
||||
|
||||
updateConfig(config)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func updateConfig(update *Configuration) {
|
||||
new := &Configuration{}
|
||||
|
||||
if update.EnforceCT > 0 && update.EnforceCT < 4 {
|
||||
new.EnforceCT = update.EnforceCT
|
||||
} else {
|
||||
new.EnforceCT = defaultConfig.EnforceCT
|
||||
}
|
||||
if update.EnforceRevocation > 0 && update.EnforceRevocation < 4 {
|
||||
new.EnforceRevocation = update.EnforceRevocation
|
||||
} else {
|
||||
new.EnforceRevocation = defaultConfig.EnforceRevocation
|
||||
}
|
||||
if update.DenyInsecureTLS > 0 && update.DenyInsecureTLS < 4 {
|
||||
new.DenyInsecureTLS = update.DenyInsecureTLS
|
||||
} else {
|
||||
new.DenyInsecureTLS = defaultConfig.DenyInsecureTLS
|
||||
}
|
||||
if update.DenyTLSWithoutSNI > 0 && update.DenyTLSWithoutSNI < 4 {
|
||||
new.DenyTLSWithoutSNI = update.DenyTLSWithoutSNI
|
||||
} else {
|
||||
new.DenyTLSWithoutSNI = defaultConfig.DenyTLSWithoutSNI
|
||||
}
|
||||
if update.DoNotUseAssignedDNS > 0 && update.DoNotUseAssignedDNS < 4 {
|
||||
new.DoNotUseAssignedDNS = update.DoNotUseAssignedDNS
|
||||
} else {
|
||||
new.DoNotUseAssignedDNS = defaultConfig.DoNotUseAssignedDNS
|
||||
}
|
||||
if update.DoNotUseMDNS > 0 && update.DoNotUseMDNS < 4 {
|
||||
new.DoNotUseMDNS = update.DoNotUseMDNS
|
||||
} else {
|
||||
new.DoNotUseMDNS = defaultConfig.DoNotUseMDNS
|
||||
}
|
||||
if update.DoNotForwardSpecialDomains > 0 && update.DoNotForwardSpecialDomains < 4 {
|
||||
new.DoNotForwardSpecialDomains = update.DoNotForwardSpecialDomains
|
||||
} else {
|
||||
new.DoNotForwardSpecialDomains = defaultConfig.DoNotForwardSpecialDomains
|
||||
}
|
||||
if update.AlwaysPromptAtNewProfile > 0 && update.AlwaysPromptAtNewProfile < 4 {
|
||||
new.AlwaysPromptAtNewProfile = update.AlwaysPromptAtNewProfile
|
||||
} else {
|
||||
new.AlwaysPromptAtNewProfile = defaultConfig.AlwaysPromptAtNewProfile
|
||||
}
|
||||
if update.DenyNetworkUntilProfileApproved > 0 && update.DenyNetworkUntilProfileApproved < 4 {
|
||||
new.DenyNetworkUntilProfileApproved = update.DenyNetworkUntilProfileApproved
|
||||
} else {
|
||||
new.DenyNetworkUntilProfileApproved = defaultConfig.DenyNetworkUntilProfileApproved
|
||||
}
|
||||
|
||||
// generic configuration
|
||||
if update.CompetenceLevel >= 0 && update.CompetenceLevel <= 3 {
|
||||
new.CompetenceLevel = update.CompetenceLevel
|
||||
} else {
|
||||
new.CompetenceLevel = 3
|
||||
// TODO: maybe notify user?
|
||||
}
|
||||
|
||||
if len(update.DNSServers) != 0 {
|
||||
new.DNSServers = update.DNSServers
|
||||
} else {
|
||||
new.DNSServers = defaultConfig.DNSServers
|
||||
}
|
||||
|
||||
if update.DNSServerRetryRate != 0 {
|
||||
new.DNSServerRetryRate = update.DNSServerRetryRate
|
||||
} else {
|
||||
new.DNSServerRetryRate = defaultConfig.DNSServerRetryRate
|
||||
}
|
||||
if len(update.CountryBlacklist) != 0 {
|
||||
new.CountryBlacklist = update.CountryBlacklist
|
||||
} else {
|
||||
new.CountryBlacklist = defaultConfig.CountryBlacklist
|
||||
}
|
||||
if len(update.ASBlacklist) != 0 {
|
||||
new.ASBlacklist = update.ASBlacklist
|
||||
} else {
|
||||
new.ASBlacklist = defaultConfig.ASBlacklist
|
||||
}
|
||||
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
// set new config and update timestamp
|
||||
currentConfig = new
|
||||
atomic.StoreInt64(lastChange, time.Now().UnixNano())
|
||||
|
||||
// update status with new values
|
||||
// status.CurrentSecurityLevel = currentConfig.SecurityLevel
|
||||
// status.Save()
|
||||
|
||||
// update atomic securityLevel
|
||||
// atomic.StoreInt32(securityLevel, int32(currentConfig.SecurityLevel))
|
||||
}
|
||||
|
||||
func statusChangeListener() {
|
||||
sub := database.NewSubscription()
|
||||
sub.Subscribe(fmt.Sprintf("%s/SystemStatus:%s", database.Me.String(), systemStatusInstanceName))
|
||||
for {
|
||||
var receivedModel database.Model
|
||||
|
||||
select {
|
||||
case <-configurationModule.Stop:
|
||||
configurationModule.StopComplete()
|
||||
return
|
||||
case receivedModel = <-sub.Updated:
|
||||
case receivedModel = <-sub.Created:
|
||||
}
|
||||
|
||||
status, ok := database.SilentEnsureModel(receivedModel, systemStatusModel).(*SystemStatus)
|
||||
if !ok {
|
||||
log.Warning("configuration: received system status update, but was not of type *SystemStatus")
|
||||
continue
|
||||
}
|
||||
|
||||
atomic.StoreInt32(securityLevel, int32(status.CurrentSecurityLevel))
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
// Copyright Safing ICS Technologies GmbH. Use of this source code is governed by the AGPL license that can be found in the LICENSE file.
|
||||
|
||||
package configuration
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestConfiguration(t *testing.T) {
|
||||
|
||||
config1 := Get()
|
||||
fmt.Printf("%v", config1)
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
config1.Changed()
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
config1.Save()
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
config1.Changed()
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
// Copyright Safing ICS Technologies GmbH. Use of this source code is governed by the AGPL license that can be found in the LICENSE file.
|
||||
|
||||
package configuration
|
||||
|
||||
import (
|
||||
"github.com/Safing/safing-core/log"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultConfig Configuration
|
||||
)
|
||||
|
||||
func initDefaultConfig() {
|
||||
defaultConfig = Configuration{
|
||||
// based on security level
|
||||
EnforceCT: 3,
|
||||
EnforceRevocation: 3,
|
||||
DenyInsecureTLS: 2,
|
||||
DenyTLSWithoutSNI: 2,
|
||||
DoNotUseAssignedDNS: 3,
|
||||
DoNotUseMDNS: 2,
|
||||
DoNotForwardSpecialDomains: 2,
|
||||
AlwaysPromptAtNewProfile: 3,
|
||||
DenyNetworkUntilProfileApproved: 3,
|
||||
|
||||
// generic configuration
|
||||
CompetenceLevel: 0,
|
||||
PermanentVerdicts: true,
|
||||
// Possible values: DNS, DoH (DNS over HTTPS - using Google's syntax: https://developers.google.com/speed/public-dns/docs/dns-over-https)
|
||||
// DNSServers: []string{"DoH|dns.google.com:443|df:www.google.com"},
|
||||
DNSServers: []string{"DNS|1.1.1.1:53", "DNS|1.0.0.1:53", "DNS|[2606:4700:4700::1111]:53", "DNS|[2606:4700:4700::1001]:53", "DNS|8.8.8.8:53", "DNS|8.8.4.4:53", "DNS|[2001:4860:4860::8888]:53", "DNS|[2001:4860:4860::8844]:53", "DNS|208.67.222.222:53", "DNS|208.67.220.220:53"},
|
||||
// DNSServers: []string{"DNS|[2001:4860:4860::8888]:53", "DNS|[2001:4860:4860::8844]:53"},
|
||||
// DNSServers: []string{"DoH|dns.google.com:443|df:www.google.com", "DNS|8.8.8.8:53", "DNS|8.8.4.4:53", "DNS|172.30.30.1:53", "DNS|172.20.30.2:53"},
|
||||
// DNSServers: []string{"DNS|208.67.222.222:53", "DNS|208.67.220.220:53", "DNS|8.8.8.8:53", "DNS|8.8.4.4:53"},
|
||||
// Amount of seconds to wait until failing DNS Servers may be retried.
|
||||
DNSServerRetryRate: 120,
|
||||
// CountryBlacklist []string
|
||||
// ASBlacklist []uint32
|
||||
LocalPort17Node: false,
|
||||
PublicPort17Node: true,
|
||||
}
|
||||
err := defaultConfig.Create(defaultConfigurationInstanceName)
|
||||
if err != nil {
|
||||
log.Warningf("configuration: could not save default configuration: %s", err)
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
// Copyright Safing ICS Technologies GmbH. Use of this source code is governed by the AGPL license that can be found in the LICENSE file.
|
||||
|
||||
package configuration
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
type Interface struct {
|
||||
*Configuration
|
||||
|
||||
LastChange int64
|
||||
ConfigLock sync.RWMutex
|
||||
}
|
||||
|
||||
func Get() *Interface {
|
||||
lock.RLock()
|
||||
defer lock.RUnlock()
|
||||
return &Interface{
|
||||
Configuration: currentConfig,
|
||||
LastChange: atomic.LoadInt64(lastChange),
|
||||
}
|
||||
}
|
||||
|
||||
func (lc *Interface) RLock() {
|
||||
lc.ConfigLock.RLock()
|
||||
}
|
||||
|
||||
func (lc *Interface) RUnlock() {
|
||||
lc.ConfigLock.RUnlock()
|
||||
}
|
||||
|
||||
func (lc *Interface) Changed() bool {
|
||||
lastGlobalChange := atomic.LoadInt64(lastChange)
|
||||
if lc.LastChange != lastGlobalChange {
|
||||
lc.ConfigLock.Lock()
|
||||
lock.RLock()
|
||||
lc.Configuration = currentConfig
|
||||
lc.LastChange = lastGlobalChange
|
||||
lock.RUnlock()
|
||||
lc.ConfigLock.Unlock()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (lc *Interface) SecurityLevel() int8 {
|
||||
return int8(atomic.LoadInt32(securityLevel))
|
||||
}
|
|
@ -1,86 +0,0 @@
|
|||
// Copyright Safing ICS Technologies GmbH. Use of this source code is governed by the AGPL license that can be found in the LICENSE file.
|
||||
|
||||
package configuration
|
||||
|
||||
import (
|
||||
"github.com/Safing/safing-core/database"
|
||||
|
||||
datastore "github.com/ipfs/go-datastore"
|
||||
)
|
||||
|
||||
// SystemStatus saves basic information about the current system status.
|
||||
type SystemStatus struct {
|
||||
database.Base
|
||||
CurrentSecurityLevel int8
|
||||
SelectedSecurityLevel int8
|
||||
|
||||
ThreatLevel int8 `json:",omitempty" bson:",omitempty"`
|
||||
ThreatReason string `json:",omitempty" bson:",omitempty"`
|
||||
|
||||
PortmasterStatus int8 `json:",omitempty" bson:",omitempty"`
|
||||
PortmasterStatusMsg string `json:",omitempty" bson:",omitempty"`
|
||||
|
||||
Port17Status int8 `json:",omitempty" bson:",omitempty"`
|
||||
Port17StatusMsg string `json:",omitempty" bson:",omitempty"`
|
||||
}
|
||||
|
||||
var (
|
||||
systemStatusModel *SystemStatus // only use this as parameter for database.EnsureModel-like functions
|
||||
systemStatusInstanceName = "status"
|
||||
)
|
||||
|
||||
func initSystemStatusModel() {
|
||||
database.RegisterModel(systemStatusModel, func() database.Model { return new(SystemStatus) })
|
||||
}
|
||||
|
||||
// Create saves SystemStatus with the provided name in the default namespace.
|
||||
func (m *SystemStatus) Create() error {
|
||||
return m.CreateObject(&database.Me, systemStatusInstanceName, m)
|
||||
}
|
||||
|
||||
// CreateInNamespace saves SystemStatus with the provided name in the provided namespace.
|
||||
func (m *SystemStatus) CreateInNamespace(namespace *datastore.Key) error {
|
||||
return m.CreateObject(namespace, systemStatusInstanceName, m)
|
||||
}
|
||||
|
||||
// Save saves SystemStatus.
|
||||
func (m *SystemStatus) Save() error {
|
||||
return m.SaveObject(m)
|
||||
}
|
||||
|
||||
// FmtSecurityLevel returns the current security level as a string.
|
||||
func (m *SystemStatus) FmtSecurityLevel() string {
|
||||
var s string
|
||||
switch m.CurrentSecurityLevel {
|
||||
case SecurityLevelOff:
|
||||
s = "Off"
|
||||
case SecurityLevelDynamic:
|
||||
s = "Dynamic"
|
||||
case SecurityLevelSecure:
|
||||
s = "Secure"
|
||||
case SecurityLevelFortress:
|
||||
s = "Fortress"
|
||||
}
|
||||
if m.CurrentSecurityLevel != m.SelectedSecurityLevel {
|
||||
s += "*"
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// GetSystemStatus fetches SystemStatus with the provided name in the default namespace.
|
||||
func GetSystemStatus() (*SystemStatus, error) {
|
||||
return GetSystemStatusFromNamespace(&database.Me)
|
||||
}
|
||||
|
||||
// GetSystemStatusFromNamespace fetches SystemStatus with the provided name in the provided namespace.
|
||||
func GetSystemStatusFromNamespace(namespace *datastore.Key) (*SystemStatus, error) {
|
||||
object, err := database.GetAndEnsureModel(namespace, systemStatusInstanceName, systemStatusModel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
model, ok := object.(*SystemStatus)
|
||||
if !ok {
|
||||
return nil, database.NewMismatchError(object, systemStatusModel)
|
||||
}
|
||||
return model, nil
|
||||
}
|
Loading…
Add table
Reference in a new issue