safing-portbase/configuration/core.go
2018-08-13 14:05:58 +02:00

238 lines
6.5 KiB
Go

// 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))
}
}