mirror of
https://github.com/safing/portmaster
synced 2025-09-04 19:49:15 +00:00
Streamline configuration
This commit is contained in:
parent
77d7a63bc3
commit
279ab67c7e
12 changed files with 354 additions and 191 deletions
|
@ -8,6 +8,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
CfgDevModeKey = "core/devMode"
|
||||||
defaultDevMode bool
|
defaultDevMode bool
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -24,7 +25,7 @@ func logFlagOverrides() {
|
||||||
func registerConfig() error {
|
func registerConfig() error {
|
||||||
err := config.Register(&config.Option{
|
err := config.Register(&config.Option{
|
||||||
Name: "Development Mode",
|
Name: "Development Mode",
|
||||||
Key: "core/devMode",
|
Key: CfgDevModeKey,
|
||||||
Description: "In Development Mode security restrictions are lifted/softened to enable easier access to Portmaster for debugging and testing purposes.",
|
Description: "In Development Mode security restrictions are lifted/softened to enable easier access to Portmaster for debugging and testing purposes.",
|
||||||
OptType: config.OptTypeBool,
|
OptType: config.OptTypeBool,
|
||||||
ExpertiseLevel: config.ExpertiseLevelDeveloper,
|
ExpertiseLevel: config.ExpertiseLevelDeveloper,
|
||||||
|
|
|
@ -2,14 +2,16 @@ package firewall
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/safing/portbase/config"
|
"github.com/safing/portbase/config"
|
||||||
"github.com/safing/portmaster/status"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
permanentVerdicts config.BoolOption
|
CfgOptionEnableFilterKey = "filter/enable"
|
||||||
filterDNSByScope status.SecurityLevelOption
|
|
||||||
filterDNSByProfile status.SecurityLevelOption
|
CfgOptionPermanentVerdictsKey = "filter/permanentVerdicts"
|
||||||
promptTimeout config.IntOption
|
permanentVerdicts config.BoolOption
|
||||||
|
|
||||||
|
CfgOptionPromptTimeoutKey = "filter/promptTimeout"
|
||||||
|
promptTimeout config.IntOption
|
||||||
|
|
||||||
devMode config.BoolOption
|
devMode config.BoolOption
|
||||||
apiListenAddress config.StringOption
|
apiListenAddress config.StringOption
|
||||||
|
@ -18,7 +20,7 @@ var (
|
||||||
func registerConfig() error {
|
func registerConfig() error {
|
||||||
err := config.Register(&config.Option{
|
err := config.Register(&config.Option{
|
||||||
Name: "Permanent Verdicts",
|
Name: "Permanent Verdicts",
|
||||||
Key: "firewall/permanentVerdicts",
|
Key: CfgOptionPermanentVerdictsKey,
|
||||||
Description: "With permanent verdicts, control of a connection is fully handed back to the OS after the initial decision. This brings a great performance increase, but makes it impossible to change the decision of a link later on.",
|
Description: "With permanent verdicts, control of a connection is fully handed back to the OS after the initial decision. This brings a great performance increase, but makes it impossible to change the decision of a link later on.",
|
||||||
OptType: config.OptTypeBool,
|
OptType: config.OptTypeBool,
|
||||||
ExpertiseLevel: config.ExpertiseLevelExpert,
|
ExpertiseLevel: config.ExpertiseLevelExpert,
|
||||||
|
@ -28,43 +30,11 @@ func registerConfig() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
permanentVerdicts = config.Concurrent.GetAsBool("firewall/permanentVerdicts", true)
|
permanentVerdicts = config.Concurrent.GetAsBool(CfgOptionPermanentVerdictsKey, true)
|
||||||
|
|
||||||
err = config.Register(&config.Option{
|
|
||||||
Name: "Filter DNS Responses by Server Scope",
|
|
||||||
Key: "firewall/filterDNSByScope",
|
|
||||||
Description: "This option will filter out DNS answers that are outside of the scope of the server. A server on the public Internet may not respond with a private LAN address.",
|
|
||||||
OptType: config.OptTypeInt,
|
|
||||||
ExpertiseLevel: config.ExpertiseLevelExpert,
|
|
||||||
ReleaseLevel: config.ReleaseLevelBeta,
|
|
||||||
ExternalOptType: "security level",
|
|
||||||
DefaultValue: 7,
|
|
||||||
ValidationRegex: "^(7|6|4)$",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
filterDNSByScope = status.ConfigIsActiveConcurrent("firewall/filterDNSByScope")
|
|
||||||
|
|
||||||
err = config.Register(&config.Option{
|
|
||||||
Name: "Filter DNS Responses by Application Profile",
|
|
||||||
Key: "firewall/filterDNSByProfile",
|
|
||||||
Description: "This option will filter out DNS answers that an application would not be allowed to connect, based on its profile.",
|
|
||||||
OptType: config.OptTypeInt,
|
|
||||||
ExpertiseLevel: config.ExpertiseLevelExpert,
|
|
||||||
ReleaseLevel: config.ReleaseLevelBeta,
|
|
||||||
ExternalOptType: "security level",
|
|
||||||
DefaultValue: 7,
|
|
||||||
ValidationRegex: "^(7|6|4)$",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
filterDNSByProfile = status.ConfigIsActiveConcurrent("firewall/filterDNSByProfile")
|
|
||||||
|
|
||||||
err = config.Register(&config.Option{
|
err = config.Register(&config.Option{
|
||||||
Name: "Timeout for prompt notifications",
|
Name: "Timeout for prompt notifications",
|
||||||
Key: "firewall/promptTimeout",
|
Key: CfgOptionPromptTimeoutKey,
|
||||||
Description: "Amount of time how long Portmaster will wait for a response when prompting about a connection via a notification. In seconds.",
|
Description: "Amount of time how long Portmaster will wait for a response when prompting about a connection via a notification. In seconds.",
|
||||||
OptType: config.OptTypeInt,
|
OptType: config.OptTypeInt,
|
||||||
ExpertiseLevel: config.ExpertiseLevelUser,
|
ExpertiseLevel: config.ExpertiseLevelUser,
|
||||||
|
@ -74,9 +44,9 @@ func registerConfig() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
promptTimeout = config.Concurrent.GetAsInt("firewall/promptTimeout", 30)
|
promptTimeout = config.Concurrent.GetAsInt(CfgOptionPromptTimeoutKey, 60)
|
||||||
|
|
||||||
devMode = config.Concurrent.GetAsBool("firewall/permanentVerdicts", false)
|
devMode = config.Concurrent.GetAsBool("core/devMode", false)
|
||||||
apiListenAddress = config.GetAsString("api/listenAddress", "")
|
apiListenAddress = config.GetAsString("api/listenAddress", "")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -19,7 +19,7 @@ var (
|
||||||
func registerConfigUpdater() error {
|
func registerConfigUpdater() error {
|
||||||
return module.RegisterEventHook(
|
return module.RegisterEventHook(
|
||||||
"config",
|
"config",
|
||||||
"config changed",
|
"config change",
|
||||||
"update global config profile",
|
"update global config profile",
|
||||||
updateGlobalConfigProfile,
|
updateGlobalConfigProfile,
|
||||||
)
|
)
|
||||||
|
|
|
@ -10,35 +10,41 @@ var (
|
||||||
cfgIntOptions = make(map[string]config.IntOption)
|
cfgIntOptions = make(map[string]config.IntOption)
|
||||||
cfgBoolOptions = make(map[string]config.BoolOption)
|
cfgBoolOptions = make(map[string]config.BoolOption)
|
||||||
|
|
||||||
cfgOptionDefaultActionKey = "filter/mode"
|
CfgOptionDefaultActionKey = "filter/defaultAction"
|
||||||
cfgOptionDefaultAction config.StringOption
|
cfgOptionDefaultAction config.StringOption
|
||||||
|
|
||||||
cfgOptionDisableAutoPermitKey = "filter/disableAutoPermit"
|
CfgOptionDisableAutoPermitKey = "filter/disableAutoPermit"
|
||||||
cfgOptionDisableAutoPermit config.IntOption // security level option
|
cfgOptionDisableAutoPermit config.IntOption // security level option
|
||||||
|
|
||||||
cfgOptionEndpointsKey = "filter/endpoints"
|
CfgOptionEndpointsKey = "filter/endpoints"
|
||||||
cfgOptionEndpoints config.StringArrayOption
|
cfgOptionEndpoints config.StringArrayOption
|
||||||
|
|
||||||
cfgOptionServiceEndpointsKey = "filter/serviceEndpoints"
|
CfgOptionServiceEndpointsKey = "filter/serviceEndpoints"
|
||||||
cfgOptionServiceEndpoints config.StringArrayOption
|
cfgOptionServiceEndpoints config.StringArrayOption
|
||||||
|
|
||||||
cfgOptionBlockScopeLocalKey = "filter/blockLocal"
|
CfgOptionBlockScopeLocalKey = "filter/blockLocal"
|
||||||
cfgOptionBlockScopeLocal config.IntOption // security level option
|
cfgOptionBlockScopeLocal config.IntOption // security level option
|
||||||
|
|
||||||
cfgOptionBlockScopeLANKey = "filter/blockLAN"
|
CfgOptionBlockScopeLANKey = "filter/blockLAN"
|
||||||
cfgOptionBlockScopeLAN config.IntOption // security level option
|
cfgOptionBlockScopeLAN config.IntOption // security level option
|
||||||
|
|
||||||
cfgOptionBlockScopeInternetKey = "filter/blockInternet"
|
CfgOptionBlockScopeInternetKey = "filter/blockInternet"
|
||||||
cfgOptionBlockScopeInternet config.IntOption // security level option
|
cfgOptionBlockScopeInternet config.IntOption // security level option
|
||||||
|
|
||||||
cfgOptionBlockP2PKey = "filter/blockP2P"
|
CfgOptionBlockP2PKey = "filter/blockP2P"
|
||||||
cfgOptionBlockP2P config.IntOption // security level option
|
cfgOptionBlockP2P config.IntOption // security level option
|
||||||
|
|
||||||
cfgOptionBlockInboundKey = "filter/blockInbound"
|
CfgOptionBlockInboundKey = "filter/blockInbound"
|
||||||
cfgOptionBlockInbound config.IntOption // security level option
|
cfgOptionBlockInbound config.IntOption // security level option
|
||||||
|
|
||||||
cfgOptionEnforceSPNKey = "filter/enforceSPN"
|
CfgOptionEnforceSPNKey = "filter/enforceSPN"
|
||||||
cfgOptionEnforceSPN config.IntOption // security level option
|
cfgOptionEnforceSPN config.IntOption // security level option
|
||||||
|
|
||||||
|
CfgOptionRemoveOutOfScopeDNSKey = "filter/removeOutOfScopeDNS"
|
||||||
|
cfgOptionRemoveOutOfScopeDNS config.IntOption // security level option
|
||||||
|
|
||||||
|
CfgOptionRemoveBlockedDNSKey = "filter/removeBlockedDNS"
|
||||||
|
cfgOptionRemoveBlockedDNS config.IntOption // security level option
|
||||||
)
|
)
|
||||||
|
|
||||||
func registerConfiguration() error {
|
func registerConfiguration() error {
|
||||||
|
@ -48,7 +54,7 @@ func registerConfiguration() error {
|
||||||
// block - whitelist mode: everything is blocked unless permitted
|
// block - whitelist mode: everything is blocked unless permitted
|
||||||
err := config.Register(&config.Option{
|
err := config.Register(&config.Option{
|
||||||
Name: "Default Filter Action",
|
Name: "Default Filter Action",
|
||||||
Key: cfgOptionDefaultActionKey,
|
Key: CfgOptionDefaultActionKey,
|
||||||
Description: `The default filter action when nothing else permits or blocks a connection.`,
|
Description: `The default filter action when nothing else permits or blocks a connection.`,
|
||||||
OptType: config.OptTypeString,
|
OptType: config.OptTypeString,
|
||||||
DefaultValue: "permit",
|
DefaultValue: "permit",
|
||||||
|
@ -57,13 +63,13 @@ func registerConfiguration() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
cfgOptionDefaultAction = config.Concurrent.GetAsString(cfgOptionDefaultActionKey, "permit")
|
cfgOptionDefaultAction = config.Concurrent.GetAsString(CfgOptionDefaultActionKey, "permit")
|
||||||
cfgStringOptions[cfgOptionDefaultActionKey] = cfgOptionDefaultAction
|
cfgStringOptions[CfgOptionDefaultActionKey] = cfgOptionDefaultAction
|
||||||
|
|
||||||
// Disable Auto Permit
|
// Disable Auto Permit
|
||||||
err = config.Register(&config.Option{
|
err = config.Register(&config.Option{
|
||||||
Name: "Disable Auto Permit",
|
Name: "Disable Auto Permit",
|
||||||
Key: cfgOptionDisableAutoPermitKey,
|
Key: CfgOptionDisableAutoPermitKey,
|
||||||
Description: "Auto Permit searches for a relation between an app and the destionation of a connection - if there is a correlation, the connection will be permitted. This setting is negated in order to provide a streamlined user experience, where higher settings are better.",
|
Description: "Auto Permit searches for a relation between an app and the destionation of a connection - if there is a correlation, the connection will be permitted. This setting is negated in order to provide a streamlined user experience, where higher settings are better.",
|
||||||
OptType: config.OptTypeInt,
|
OptType: config.OptTypeInt,
|
||||||
ExternalOptType: "security level",
|
ExternalOptType: "security level",
|
||||||
|
@ -73,13 +79,13 @@ func registerConfiguration() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
cfgOptionDisableAutoPermit = config.Concurrent.GetAsInt(cfgOptionDisableAutoPermitKey, 4)
|
cfgOptionDisableAutoPermit = config.Concurrent.GetAsInt(CfgOptionDisableAutoPermitKey, 4)
|
||||||
cfgIntOptions[cfgOptionDisableAutoPermitKey] = cfgOptionDisableAutoPermit
|
cfgIntOptions[CfgOptionDisableAutoPermitKey] = cfgOptionDisableAutoPermit
|
||||||
|
|
||||||
// Endpoint Filter List
|
// Endpoint Filter List
|
||||||
err = config.Register(&config.Option{
|
err = config.Register(&config.Option{
|
||||||
Name: "Endpoint Filter List",
|
Name: "Endpoint Filter List",
|
||||||
Key: cfgOptionEndpointsKey,
|
Key: CfgOptionEndpointsKey,
|
||||||
Description: "Filter outgoing connections by matching the destination endpoint. Network Scope restrictions still apply.",
|
Description: "Filter outgoing connections by matching the destination endpoint. Network Scope restrictions still apply.",
|
||||||
Help: `Format:
|
Help: `Format:
|
||||||
Permission:
|
Permission:
|
||||||
|
@ -101,36 +107,36 @@ Examples:
|
||||||
- .example.com
|
- .example.com
|
||||||
+ 192.168.0.1/24`,
|
+ 192.168.0.1/24`,
|
||||||
OptType: config.OptTypeStringArray,
|
OptType: config.OptTypeStringArray,
|
||||||
DefaultValue: nil,
|
DefaultValue: []string{},
|
||||||
ExternalOptType: "endpoint list",
|
ExternalOptType: "endpoint list",
|
||||||
ValidationRegex: `^(+|-) [A-z0-9\.:-*/]+( [A-z0-9/]+)?$`,
|
ValidationRegex: `^(\+|\-) [A-z0-9\.:\-*/]+( [A-z0-9/]+)?$`,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
cfgOptionEndpoints = config.Concurrent.GetAsStringArray(cfgOptionEndpointsKey, nil)
|
cfgOptionEndpoints = config.Concurrent.GetAsStringArray(CfgOptionEndpointsKey, []string{})
|
||||||
cfgStringArrayOptions[cfgOptionEndpointsKey] = cfgOptionEndpoints
|
cfgStringArrayOptions[CfgOptionEndpointsKey] = cfgOptionEndpoints
|
||||||
|
|
||||||
// Service Endpoint Filter List
|
// Service Endpoint Filter List
|
||||||
err = config.Register(&config.Option{
|
err = config.Register(&config.Option{
|
||||||
Name: "Service Endpoint Filter List",
|
Name: "Service Endpoint Filter List",
|
||||||
Key: cfgOptionServiceEndpointsKey,
|
Key: CfgOptionServiceEndpointsKey,
|
||||||
Description: "Filter incoming connections by matching the source endpoint. Network Scope restrictions and the inbound permission still apply. Also not that the implicit default action of this list is to always block.",
|
Description: "Filter incoming connections by matching the source endpoint. Network Scope restrictions and the inbound permission still apply. Also not that the implicit default action of this list is to always block.",
|
||||||
OptType: config.OptTypeStringArray,
|
OptType: config.OptTypeStringArray,
|
||||||
DefaultValue: nil,
|
DefaultValue: []string{},
|
||||||
ExternalOptType: "endpoint list",
|
ExternalOptType: "endpoint list",
|
||||||
ValidationRegex: `^(+|-) [A-z0-9\.:-*/]+( [A-z0-9/]+)?$`,
|
ValidationRegex: `^(\+|\-) [A-z0-9\.:\-*/]+( [A-z0-9/]+)?$`,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
cfgOptionServiceEndpoints = config.Concurrent.GetAsStringArray(cfgOptionServiceEndpointsKey, nil)
|
cfgOptionServiceEndpoints = config.Concurrent.GetAsStringArray(CfgOptionServiceEndpointsKey, []string{})
|
||||||
cfgStringArrayOptions[cfgOptionServiceEndpointsKey] = cfgOptionServiceEndpoints
|
cfgStringArrayOptions[CfgOptionServiceEndpointsKey] = cfgOptionServiceEndpoints
|
||||||
|
|
||||||
// Block Scope Local
|
// Block Scope Local
|
||||||
err = config.Register(&config.Option{
|
err = config.Register(&config.Option{
|
||||||
Name: "Block Scope Local",
|
Name: "Block Scope Local",
|
||||||
Key: cfgOptionBlockScopeLocalKey,
|
Key: CfgOptionBlockScopeLocalKey,
|
||||||
Description: "Block connections to your own device, ie. localhost.",
|
Description: "Block connections to your own device, ie. localhost.",
|
||||||
OptType: config.OptTypeInt,
|
OptType: config.OptTypeInt,
|
||||||
ExternalOptType: "security level",
|
ExternalOptType: "security level",
|
||||||
|
@ -140,13 +146,13 @@ Examples:
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
cfgOptionBlockScopeLocal = config.Concurrent.GetAsInt(cfgOptionBlockScopeLocalKey, 0)
|
cfgOptionBlockScopeLocal = config.Concurrent.GetAsInt(CfgOptionBlockScopeLocalKey, 0)
|
||||||
cfgIntOptions[cfgOptionBlockScopeLocalKey] = cfgOptionBlockScopeLocal
|
cfgIntOptions[CfgOptionBlockScopeLocalKey] = cfgOptionBlockScopeLocal
|
||||||
|
|
||||||
// Block Scope LAN
|
// Block Scope LAN
|
||||||
err = config.Register(&config.Option{
|
err = config.Register(&config.Option{
|
||||||
Name: "Block Scope LAN",
|
Name: "Block Scope LAN",
|
||||||
Key: cfgOptionBlockScopeLANKey,
|
Key: CfgOptionBlockScopeLANKey,
|
||||||
Description: "Block connections to the Local Area Network.",
|
Description: "Block connections to the Local Area Network.",
|
||||||
OptType: config.OptTypeInt,
|
OptType: config.OptTypeInt,
|
||||||
ExternalOptType: "security level",
|
ExternalOptType: "security level",
|
||||||
|
@ -156,13 +162,13 @@ Examples:
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
cfgOptionBlockScopeLAN = config.Concurrent.GetAsInt(cfgOptionBlockScopeLANKey, 0)
|
cfgOptionBlockScopeLAN = config.Concurrent.GetAsInt(CfgOptionBlockScopeLANKey, 0)
|
||||||
cfgIntOptions[cfgOptionBlockScopeLANKey] = cfgOptionBlockScopeLAN
|
cfgIntOptions[CfgOptionBlockScopeLANKey] = cfgOptionBlockScopeLAN
|
||||||
|
|
||||||
// Block Scope Internet
|
// Block Scope Internet
|
||||||
err = config.Register(&config.Option{
|
err = config.Register(&config.Option{
|
||||||
Name: "Block Scope Internet",
|
Name: "Block Scope Internet",
|
||||||
Key: cfgOptionBlockScopeInternetKey,
|
Key: CfgOptionBlockScopeInternetKey,
|
||||||
Description: "Block connections to the Internet.",
|
Description: "Block connections to the Internet.",
|
||||||
OptType: config.OptTypeInt,
|
OptType: config.OptTypeInt,
|
||||||
ExternalOptType: "security level",
|
ExternalOptType: "security level",
|
||||||
|
@ -172,13 +178,13 @@ Examples:
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
cfgOptionBlockScopeInternet = config.Concurrent.GetAsInt(cfgOptionBlockScopeInternetKey, 0)
|
cfgOptionBlockScopeInternet = config.Concurrent.GetAsInt(CfgOptionBlockScopeInternetKey, 0)
|
||||||
cfgIntOptions[cfgOptionBlockScopeInternetKey] = cfgOptionBlockScopeInternet
|
cfgIntOptions[CfgOptionBlockScopeInternetKey] = cfgOptionBlockScopeInternet
|
||||||
|
|
||||||
// Block Peer to Peer Connections
|
// Block Peer to Peer Connections
|
||||||
err = config.Register(&config.Option{
|
err = config.Register(&config.Option{
|
||||||
Name: "Block Peer to Peer Connections",
|
Name: "Block Peer to Peer Connections",
|
||||||
Key: cfgOptionBlockP2PKey,
|
Key: CfgOptionBlockP2PKey,
|
||||||
Description: "Block peer to peer connections. These are connections that are established directly to an IP address on the Internet without resolving a domain name via DNS first.",
|
Description: "Block peer to peer connections. These are connections that are established directly to an IP address on the Internet without resolving a domain name via DNS first.",
|
||||||
OptType: config.OptTypeInt,
|
OptType: config.OptTypeInt,
|
||||||
ExternalOptType: "security level",
|
ExternalOptType: "security level",
|
||||||
|
@ -188,13 +194,13 @@ Examples:
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
cfgOptionBlockP2P = config.Concurrent.GetAsInt(cfgOptionBlockP2PKey, 7)
|
cfgOptionBlockP2P = config.Concurrent.GetAsInt(CfgOptionBlockP2PKey, 7)
|
||||||
cfgIntOptions[cfgOptionBlockP2PKey] = cfgOptionBlockP2P
|
cfgIntOptions[CfgOptionBlockP2PKey] = cfgOptionBlockP2P
|
||||||
|
|
||||||
// Block Inbound Connections
|
// Block Inbound Connections
|
||||||
err = config.Register(&config.Option{
|
err = config.Register(&config.Option{
|
||||||
Name: "Block Inbound Connections",
|
Name: "Block Inbound Connections",
|
||||||
Key: cfgOptionBlockInboundKey,
|
Key: CfgOptionBlockInboundKey,
|
||||||
Description: "Block inbound connections to your device. This will usually only be the case if you are running a network service or are using peer to peer software.",
|
Description: "Block inbound connections to your device. This will usually only be the case if you are running a network service or are using peer to peer software.",
|
||||||
OptType: config.OptTypeInt,
|
OptType: config.OptTypeInt,
|
||||||
ExternalOptType: "security level",
|
ExternalOptType: "security level",
|
||||||
|
@ -204,13 +210,13 @@ Examples:
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
cfgOptionBlockInbound = config.Concurrent.GetAsInt(cfgOptionBlockInboundKey, 6)
|
cfgOptionBlockInbound = config.Concurrent.GetAsInt(CfgOptionBlockInboundKey, 6)
|
||||||
cfgIntOptions[cfgOptionBlockInboundKey] = cfgOptionBlockInbound
|
cfgIntOptions[CfgOptionBlockInboundKey] = cfgOptionBlockInbound
|
||||||
|
|
||||||
// Enforce SPN
|
// Enforce SPN
|
||||||
err = config.Register(&config.Option{
|
err = config.Register(&config.Option{
|
||||||
Name: "Enforce SPN",
|
Name: "Enforce SPN",
|
||||||
Key: cfgOptionEnforceSPNKey,
|
Key: CfgOptionEnforceSPNKey,
|
||||||
Description: "This setting enforces connections to be routed over the SPN. If this is not possible for any reason, connections will be blocked.",
|
Description: "This setting enforces connections to be routed over the SPN. If this is not possible for any reason, connections will be blocked.",
|
||||||
OptType: config.OptTypeInt,
|
OptType: config.OptTypeInt,
|
||||||
ReleaseLevel: config.ReleaseLevelExperimental,
|
ReleaseLevel: config.ReleaseLevelExperimental,
|
||||||
|
@ -221,8 +227,44 @@ Examples:
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
cfgOptionEnforceSPN = config.Concurrent.GetAsInt(cfgOptionEnforceSPNKey, 0)
|
cfgOptionEnforceSPN = config.Concurrent.GetAsInt(CfgOptionEnforceSPNKey, 0)
|
||||||
cfgIntOptions[cfgOptionEnforceSPNKey] = cfgOptionEnforceSPN
|
cfgIntOptions[CfgOptionEnforceSPNKey] = cfgOptionEnforceSPN
|
||||||
|
|
||||||
|
// Filter Out-of-Scope DNS Records
|
||||||
|
err = config.Register(&config.Option{
|
||||||
|
Name: "Filter Out-of-Scope DNS Records",
|
||||||
|
Key: CfgOptionRemoveOutOfScopeDNSKey,
|
||||||
|
Description: "Filter DNS answers that are outside of the scope of the server. A server on the public Internet may not respond with a private LAN address.",
|
||||||
|
OptType: config.OptTypeInt,
|
||||||
|
ExpertiseLevel: config.ExpertiseLevelExpert,
|
||||||
|
ReleaseLevel: config.ReleaseLevelBeta,
|
||||||
|
ExternalOptType: "security level",
|
||||||
|
DefaultValue: 7,
|
||||||
|
ValidationRegex: "^(7|6|4)$",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cfgOptionRemoveOutOfScopeDNS = config.Concurrent.GetAsInt(CfgOptionRemoveOutOfScopeDNSKey, 7)
|
||||||
|
cfgIntOptions[CfgOptionRemoveOutOfScopeDNSKey] = cfgOptionEnforceSPN
|
||||||
|
|
||||||
|
// Filter DNS Records that would be blocked
|
||||||
|
err = config.Register(&config.Option{
|
||||||
|
Name: "Filter DNS Records that would be blocked",
|
||||||
|
Key: CfgOptionRemoveBlockedDNSKey,
|
||||||
|
Description: "Pre-filter DNS answers that an application would not be allowed to connect to.",
|
||||||
|
OptType: config.OptTypeInt,
|
||||||
|
ExpertiseLevel: config.ExpertiseLevelExpert,
|
||||||
|
ReleaseLevel: config.ReleaseLevelBeta,
|
||||||
|
ExternalOptType: "security level",
|
||||||
|
DefaultValue: 7,
|
||||||
|
ValidationRegex: "^(7|6|4)$",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cfgOptionRemoveBlockedDNS = config.Concurrent.GetAsInt(CfgOptionRemoveBlockedDNSKey, 7)
|
||||||
|
cfgIntOptions[CfgOptionRemoveBlockedDNSKey] = cfgOptionEnforceSPN
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
54
profile/find.go
Normal file
54
profile/find.go
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
package profile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/safing/portbase/database/query"
|
||||||
|
"github.com/safing/portbase/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func FindOrCreateLocalProfileByPath(fullPath string) (profile *Profile, new bool, err error) {
|
||||||
|
// find local profile
|
||||||
|
it, err := profileDB.Query(
|
||||||
|
query.New(makeProfileKey(SourceLocal, "")).Where(
|
||||||
|
query.Where("LinkedPath", query.SameAs, fullPath),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// get first result
|
||||||
|
r := <-it.Next
|
||||||
|
// cancel immediately
|
||||||
|
it.Cancel()
|
||||||
|
|
||||||
|
// return new if none was found
|
||||||
|
if r == nil {
|
||||||
|
profile = New()
|
||||||
|
profile.LinkedPath = fullPath
|
||||||
|
return profile, true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure its a profile
|
||||||
|
profile, err = EnsureProfile(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepare config
|
||||||
|
err = profile.prepConfig()
|
||||||
|
if err != nil {
|
||||||
|
log.Warningf("profiles: profile %s has (partly) invalid configuration: %s", profile.ID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse config
|
||||||
|
err = profile.parseConfig()
|
||||||
|
if err != nil {
|
||||||
|
log.Warningf("profiles: profile %s has (partly) invalid configuration: %s", profile.ID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// mark active
|
||||||
|
markProfileActive(profile)
|
||||||
|
|
||||||
|
// return parsed profile
|
||||||
|
return profile, false, nil
|
||||||
|
}
|
|
@ -1,6 +1,8 @@
|
||||||
package profile
|
package profile
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/safing/portbase/log"
|
||||||
|
|
||||||
"github.com/safing/portbase/modules"
|
"github.com/safing/portbase/modules"
|
||||||
|
|
||||||
// module dependencies
|
// module dependencies
|
||||||
|
@ -40,5 +42,10 @@ func start() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = updateGlobalConfigProfile(module.Ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Warningf("profile: error during loading global profile from configuration: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,8 +23,9 @@ var (
|
||||||
type LayeredProfile struct {
|
type LayeredProfile struct {
|
||||||
lock sync.Mutex
|
lock sync.Mutex
|
||||||
|
|
||||||
localProfile *Profile
|
localProfile *Profile
|
||||||
layers []*Profile
|
layers []*Profile
|
||||||
|
revisionCounter uint64
|
||||||
|
|
||||||
validityFlag *abool.AtomicBool
|
validityFlag *abool.AtomicBool
|
||||||
validityFlagLock sync.Mutex
|
validityFlagLock sync.Mutex
|
||||||
|
@ -32,13 +33,15 @@ type LayeredProfile struct {
|
||||||
|
|
||||||
securityLevel *uint32
|
securityLevel *uint32
|
||||||
|
|
||||||
DisableAutoPermit config.BoolOption
|
DisableAutoPermit config.BoolOption
|
||||||
BlockScopeLocal config.BoolOption
|
BlockScopeLocal config.BoolOption
|
||||||
BlockScopeLAN config.BoolOption
|
BlockScopeLAN config.BoolOption
|
||||||
BlockScopeInternet config.BoolOption
|
BlockScopeInternet config.BoolOption
|
||||||
BlockP2P config.BoolOption
|
BlockP2P config.BoolOption
|
||||||
BlockInbound config.BoolOption
|
BlockInbound config.BoolOption
|
||||||
EnforceSPN config.BoolOption
|
EnforceSPN config.BoolOption
|
||||||
|
RemoveOutOfScopeDNS config.BoolOption
|
||||||
|
RemoveBlockedDNS config.BoolOption
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewLayeredProfile returns a new layered profile based on the given local profile.
|
// NewLayeredProfile returns a new layered profile based on the given local profile.
|
||||||
|
@ -48,39 +51,48 @@ func NewLayeredProfile(localProfile *Profile) *LayeredProfile {
|
||||||
new := &LayeredProfile{
|
new := &LayeredProfile{
|
||||||
localProfile: localProfile,
|
localProfile: localProfile,
|
||||||
layers: make([]*Profile, 0, len(localProfile.LinkedProfiles)+1),
|
layers: make([]*Profile, 0, len(localProfile.LinkedProfiles)+1),
|
||||||
|
revisionCounter: 0,
|
||||||
validityFlag: abool.NewBool(true),
|
validityFlag: abool.NewBool(true),
|
||||||
globalValidityFlag: config.NewValidityFlag(),
|
globalValidityFlag: config.NewValidityFlag(),
|
||||||
securityLevel: &securityLevelVal,
|
securityLevel: &securityLevelVal,
|
||||||
}
|
}
|
||||||
|
|
||||||
new.DisableAutoPermit = new.wrapSecurityLevelOption(
|
new.DisableAutoPermit = new.wrapSecurityLevelOption(
|
||||||
cfgOptionDisableAutoPermitKey,
|
CfgOptionDisableAutoPermitKey,
|
||||||
cfgOptionDisableAutoPermit,
|
cfgOptionDisableAutoPermit,
|
||||||
)
|
)
|
||||||
new.BlockScopeLocal = new.wrapSecurityLevelOption(
|
new.BlockScopeLocal = new.wrapSecurityLevelOption(
|
||||||
cfgOptionBlockScopeLocalKey,
|
CfgOptionBlockScopeLocalKey,
|
||||||
cfgOptionBlockScopeLocal,
|
cfgOptionBlockScopeLocal,
|
||||||
)
|
)
|
||||||
new.BlockScopeLAN = new.wrapSecurityLevelOption(
|
new.BlockScopeLAN = new.wrapSecurityLevelOption(
|
||||||
cfgOptionBlockScopeLANKey,
|
CfgOptionBlockScopeLANKey,
|
||||||
cfgOptionBlockScopeLAN,
|
cfgOptionBlockScopeLAN,
|
||||||
)
|
)
|
||||||
new.BlockScopeInternet = new.wrapSecurityLevelOption(
|
new.BlockScopeInternet = new.wrapSecurityLevelOption(
|
||||||
cfgOptionBlockScopeInternetKey,
|
CfgOptionBlockScopeInternetKey,
|
||||||
cfgOptionBlockScopeInternet,
|
cfgOptionBlockScopeInternet,
|
||||||
)
|
)
|
||||||
new.BlockP2P = new.wrapSecurityLevelOption(
|
new.BlockP2P = new.wrapSecurityLevelOption(
|
||||||
cfgOptionBlockP2PKey,
|
CfgOptionBlockP2PKey,
|
||||||
cfgOptionBlockP2P,
|
cfgOptionBlockP2P,
|
||||||
)
|
)
|
||||||
new.BlockInbound = new.wrapSecurityLevelOption(
|
new.BlockInbound = new.wrapSecurityLevelOption(
|
||||||
cfgOptionBlockInboundKey,
|
CfgOptionBlockInboundKey,
|
||||||
cfgOptionBlockInbound,
|
cfgOptionBlockInbound,
|
||||||
)
|
)
|
||||||
new.EnforceSPN = new.wrapSecurityLevelOption(
|
new.EnforceSPN = new.wrapSecurityLevelOption(
|
||||||
cfgOptionEnforceSPNKey,
|
CfgOptionEnforceSPNKey,
|
||||||
cfgOptionEnforceSPN,
|
cfgOptionEnforceSPN,
|
||||||
)
|
)
|
||||||
|
new.RemoveOutOfScopeDNS = new.wrapSecurityLevelOption(
|
||||||
|
CfgOptionRemoveOutOfScopeDNSKey,
|
||||||
|
cfgOptionRemoveOutOfScopeDNS,
|
||||||
|
)
|
||||||
|
new.RemoveBlockedDNS = new.wrapSecurityLevelOption(
|
||||||
|
CfgOptionRemoveBlockedDNSKey,
|
||||||
|
cfgOptionRemoveBlockedDNS,
|
||||||
|
)
|
||||||
|
|
||||||
// TODO: load referenced profiles
|
// TODO: load referenced profiles
|
||||||
|
|
||||||
|
@ -100,9 +112,9 @@ func (lp *LayeredProfile) getValidityFlag() *abool.AtomicBool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update checks for updated profiles and replaces any outdated profiles.
|
// Update checks for updated profiles and replaces any outdated profiles.
|
||||||
func (lp *LayeredProfile) Update() {
|
func (lp *LayeredProfile) Update() (revisionCounter uint64) {
|
||||||
lp.lock.Lock()
|
lp.lock.Lock()
|
||||||
defer lp.lock.Lock()
|
defer lp.lock.Unlock()
|
||||||
|
|
||||||
var changed bool
|
var changed bool
|
||||||
for i, layer := range lp.layers {
|
for i, layer := range lp.layers {
|
||||||
|
@ -130,8 +142,14 @@ func (lp *LayeredProfile) Update() {
|
||||||
// get global config validity flag
|
// get global config validity flag
|
||||||
lp.globalValidityFlag.Refresh()
|
lp.globalValidityFlag.Refresh()
|
||||||
|
|
||||||
|
// update cached data fields
|
||||||
lp.updateCaches()
|
lp.updateCaches()
|
||||||
|
|
||||||
|
// bump revision counter
|
||||||
|
lp.revisionCounter++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return lp.revisionCounter
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lp *LayeredProfile) updateCaches() {
|
func (lp *LayeredProfile) updateCaches() {
|
||||||
|
@ -156,12 +174,14 @@ func (lp *LayeredProfile) SecurityLevel() uint8 {
|
||||||
func (lp *LayeredProfile) DefaultAction() uint8 {
|
func (lp *LayeredProfile) DefaultAction() uint8 {
|
||||||
for _, layer := range lp.layers {
|
for _, layer := range lp.layers {
|
||||||
if layer.defaultAction > 0 {
|
if layer.defaultAction > 0 {
|
||||||
|
log.Tracef("profile: default action by layer = %d", layer.defaultAction)
|
||||||
return layer.defaultAction
|
return layer.defaultAction
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cfgLock.RLock()
|
cfgLock.RLock()
|
||||||
defer cfgLock.RUnlock()
|
defer cfgLock.RUnlock()
|
||||||
|
log.Tracef("profile: default action from global = %d", cfgDefaultAction)
|
||||||
return cfgDefaultAction
|
return cfgDefaultAction
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,38 +219,15 @@ func (lp *LayeredProfile) MatchServiceEndpoint(entity *intel.Entity) (result end
|
||||||
return cfgServiceEndpoints.Match(entity)
|
return cfgServiceEndpoints.Match(entity)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// AddEndpoint adds an endpoint to the local endpoint list, saves the local profile and reloads the configuration.
|
||||||
func (lp *LayeredProfile) wrapSecurityLevelOption(configKey string, globalConfig config.IntOption) config.BoolOption {
|
func (lp *LayeredProfile) AddEndpoint(newEntry string) {
|
||||||
valid := no
|
lp.localProfile.AddEndpoint(newEntry)
|
||||||
var activeAtLevels uint8
|
}
|
||||||
|
|
||||||
return func() bool {
|
// AddServiceEndpoint adds a service endpoint to the local endpoint list, saves the local profile and reloads the configuration.
|
||||||
if !valid.IsSet() {
|
func (lp *LayeredProfile) AddServiceEndpoint(newEntry string) {
|
||||||
valid = lp.getValidityFlag()
|
lp.localProfile.AddServiceEndpoint(newEntry)
|
||||||
|
|
||||||
found := false
|
|
||||||
layerLoop:
|
|
||||||
for _, layer := range lp.layers {
|
|
||||||
layerLevel, ok := layer.configPerspective.GetAsInt(configKey)
|
|
||||||
if ok {
|
|
||||||
found = true
|
|
||||||
// TODO: add instead?
|
|
||||||
activeAtLevels = uint8(layerLevel)
|
|
||||||
break layerLoop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !found {
|
|
||||||
activeAtLevels = uint8(globalConfig())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return activeAtLevels&max(
|
|
||||||
lp.SecurityLevel(), // layered profile security level
|
|
||||||
status.ActiveSecurityLevel(), // global security level
|
|
||||||
) > 0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
func (lp *LayeredProfile) wrapSecurityLevelOption(configKey string, globalConfig config.IntOption) config.BoolOption {
|
func (lp *LayeredProfile) wrapSecurityLevelOption(configKey string, globalConfig config.IntOption) config.BoolOption {
|
||||||
activeAtLevels := lp.wrapIntOption(configKey, globalConfig)
|
activeAtLevels := lp.wrapIntOption(configKey, globalConfig)
|
||||||
|
|
|
@ -90,18 +90,13 @@ type Profile struct { //nolint:maligned // not worth the effort
|
||||||
}
|
}
|
||||||
|
|
||||||
func (profile *Profile) prepConfig() (err error) {
|
func (profile *Profile) prepConfig() (err error) {
|
||||||
profile.Lock()
|
|
||||||
defer profile.Unlock()
|
|
||||||
|
|
||||||
// prepare configuration
|
// prepare configuration
|
||||||
profile.configPerspective, err = config.NewPerspective(profile.Config)
|
profile.configPerspective, err = config.NewPerspective(profile.Config)
|
||||||
|
profile.oudated = abool.New()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (profile *Profile) parseConfig() error {
|
func (profile *Profile) parseConfig() error {
|
||||||
profile.Lock()
|
|
||||||
defer profile.Unlock()
|
|
||||||
|
|
||||||
if profile.configPerspective == nil {
|
if profile.configPerspective == nil {
|
||||||
return errors.New("config not prepared")
|
return errors.New("config not prepared")
|
||||||
}
|
}
|
||||||
|
@ -115,7 +110,7 @@ func (profile *Profile) parseConfig() error {
|
||||||
var err error
|
var err error
|
||||||
var lastErr error
|
var lastErr error
|
||||||
|
|
||||||
action, ok := profile.configPerspective.GetAsString(cfgOptionDefaultActionKey)
|
action, ok := profile.configPerspective.GetAsString(CfgOptionBlockInboundKey)
|
||||||
if ok {
|
if ok {
|
||||||
switch action {
|
switch action {
|
||||||
case "permit":
|
case "permit":
|
||||||
|
@ -129,7 +124,7 @@ func (profile *Profile) parseConfig() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
list, ok := profile.configPerspective.GetAsStringArray(cfgOptionEndpointsKey)
|
list, ok := profile.configPerspective.GetAsStringArray(CfgOptionEndpointsKey)
|
||||||
if ok {
|
if ok {
|
||||||
profile.endpoints, err = endpoints.ParseEndpoints(list)
|
profile.endpoints, err = endpoints.ParseEndpoints(list)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -137,7 +132,7 @@ func (profile *Profile) parseConfig() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
list, ok = profile.configPerspective.GetAsStringArray(cfgOptionServiceEndpointsKey)
|
list, ok = profile.configPerspective.GetAsStringArray(CfgOptionServiceEndpointsKey)
|
||||||
if ok {
|
if ok {
|
||||||
profile.serviceEndpoints, err = endpoints.ParseEndpoints(list)
|
profile.serviceEndpoints, err = endpoints.ParseEndpoints(list)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -150,11 +145,18 @@ func (profile *Profile) parseConfig() error {
|
||||||
|
|
||||||
// New returns a new Profile.
|
// New returns a new Profile.
|
||||||
func New() *Profile {
|
func New() *Profile {
|
||||||
return &Profile{
|
profile := &Profile{
|
||||||
ID: uuid.NewV4().String(),
|
ID: uuid.NewV4().String(),
|
||||||
Source: SourceLocal,
|
Source: SourceLocal,
|
||||||
Created: time.Now().Unix(),
|
Created: time.Now().Unix(),
|
||||||
|
Config: make(map[string]interface{}),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// create placeholders
|
||||||
|
_ = profile.prepConfig()
|
||||||
|
_ = profile.parseConfig()
|
||||||
|
|
||||||
|
return profile
|
||||||
}
|
}
|
||||||
|
|
||||||
// ScopedID returns the scoped ID (Source + ID) of the profile.
|
// ScopedID returns the scoped ID (Source + ID) of the profile.
|
||||||
|
@ -192,6 +194,41 @@ func (profile *Profile) String() string {
|
||||||
return profile.Name
|
return profile.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddEndpoint adds an endpoint to the endpoint list, saves the profile and reloads the configuration.
|
||||||
|
func (profile *Profile) AddEndpoint(newEntry string) {
|
||||||
|
profile.addEndpointyEntry(CfgOptionEndpointsKey, newEntry)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddServiceEndpoint adds a service endpoint to the endpoint list, saves the profile and reloads the configuration.
|
||||||
|
func (profile *Profile) AddServiceEndpoint(newEntry string) {
|
||||||
|
profile.addEndpointyEntry(CfgOptionServiceEndpointsKey, newEntry)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (profile *Profile) addEndpointyEntry(cfgKey, newEntry string) {
|
||||||
|
profile.Lock()
|
||||||
|
// get, update, save endpoints list
|
||||||
|
endpointList, ok := profile.configPerspective.GetAsStringArray(cfgKey)
|
||||||
|
if !ok {
|
||||||
|
endpointList = make([]string, 0, 1)
|
||||||
|
}
|
||||||
|
endpointList = append(endpointList, newEntry)
|
||||||
|
profile.Config[cfgKey] = endpointList
|
||||||
|
|
||||||
|
// save without full reload
|
||||||
|
profile.internalSave = true
|
||||||
|
profile.Unlock()
|
||||||
|
err := profile.Save()
|
||||||
|
if err != nil {
|
||||||
|
log.Warningf("profile: failed to save profile after adding endpoint: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// reload manually
|
||||||
|
err = profile.parseConfig()
|
||||||
|
if err != nil {
|
||||||
|
log.Warningf("profile: failed to parse profile config after adding endpoint: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// GetProfile loads a profile from the database.
|
// GetProfile loads a profile from the database.
|
||||||
func GetProfile(source, id string) (*Profile, error) {
|
func GetProfile(source, id string) (*Profile, error) {
|
||||||
return GetProfileByScopedID(makeScopedID(source, id))
|
return GetProfileByScopedID(makeScopedID(source, id))
|
||||||
|
@ -217,6 +254,10 @@ func GetProfileByScopedID(scopedID string) (*Profile, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// lock for prepping
|
||||||
|
profile.Lock()
|
||||||
|
defer profile.Unlock()
|
||||||
|
|
||||||
// prepare config
|
// prepare config
|
||||||
err = profile.prepConfig()
|
err = profile.prepConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -9,35 +9,83 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
configuredNameServers config.StringArrayOption
|
defaultNameServers = []string{
|
||||||
defaultNameServers = []string{
|
// Collection of default DNS Servers
|
||||||
|
|
||||||
|
// Default servers should be:
|
||||||
|
// Anycast:
|
||||||
|
// - Servers should be reachable from anywhere with reasonable latency.
|
||||||
|
// - Servers should be near to the user for geo-content to work correctly.
|
||||||
|
// Private:
|
||||||
|
// - Servers should not do any or only minimal logging.
|
||||||
|
// - Available logging data may not be used against the user, ie. unethically.
|
||||||
|
|
||||||
|
// Sadly, only a few services come close to fulfilling these requirements.
|
||||||
|
// For now, we have settled for two bigger and well known services: Cloudflare and Quad9.
|
||||||
|
// TODO: monitor situation and re-evaluate when new services become available
|
||||||
|
// TODO: explore other methods of making queries more private
|
||||||
|
|
||||||
|
// We encourage everyone who has the technical abilities to set their own preferred servers.
|
||||||
|
|
||||||
|
// Default 1: Cloudflare
|
||||||
|
"dot|1.1.1.1:853|cloudflare-dns.com", // Cloudflare
|
||||||
|
"dot|1.0.0.1:853|cloudflare-dns.com", // Cloudflare
|
||||||
|
|
||||||
|
// Default 2: Quad9
|
||||||
|
"dot|9.9.9.9:853|dns.quad9.net", // Quad9
|
||||||
|
"dot|149.112.112.112:853|dns.quad9.net", // Quad9
|
||||||
|
|
||||||
|
// Fallback 1: Cloudflare
|
||||||
|
"dns|1.1.1.1:53", // Cloudflare
|
||||||
|
"dns|1.0.0.1:53", // Cloudflare
|
||||||
|
|
||||||
|
// Fallback 2: Quad9
|
||||||
|
"dns|9.9.9.9:53", // Quad9
|
||||||
|
"dns|149.112.112.112:53", // Quad9
|
||||||
|
|
||||||
|
// Configuration:
|
||||||
|
// protocol: dns, dot
|
||||||
|
// : IP + Port
|
||||||
|
// parameters
|
||||||
|
// - `name=name`: human readable name for resolver
|
||||||
|
// - `verify=domain`: verify domain (dot only)
|
||||||
|
// - `blockedif=baredns`: how to detect if the dns service blocked something
|
||||||
|
// - `baredns`: NXDomain result, but without any other record in any section
|
||||||
|
|
||||||
|
// Possible future format:
|
||||||
// "dot://9.9.9.9:853?verify=dns.quad9.net&", // Quad9
|
// "dot://9.9.9.9:853?verify=dns.quad9.net&", // Quad9
|
||||||
// "dot|149.112.112.112:853|dns.quad9.net", // Quad9
|
// "dot|149.112.112.112:853|dns.quad9.net", // Quad9
|
||||||
// "dot://[2620:fe::fe]:853?verify=dns.quad9.net&name=Quad9" // Quad9
|
// "dot://[2620:fe::fe]:853?verify=dns.quad9.net&name=Quad9" // Quad9
|
||||||
// "dot://[2620:fe::9]:853?verify=dns.quad9.net&name=Quad9" // Quad9
|
// "dot://[2620:fe::9]:853?verify=dns.quad9.net&name=Quad9" // Quad9
|
||||||
|
|
||||||
"dot|1.1.1.1:853|cloudflare-dns.com", // Cloudflare
|
|
||||||
"dot|1.0.0.1:853|cloudflare-dns.com", // Cloudflare
|
|
||||||
"dns|9.9.9.9:53", // Quad9
|
|
||||||
"dns|149.112.112.112:53", // Quad9
|
|
||||||
"dns|1.1.1.1:53", // Cloudflare
|
|
||||||
"dns|1.0.0.1:53", // Cloudflare
|
|
||||||
// "doh|cloudflare-dns.com/dns-query", // DoH still experimental
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nameserverRetryRate config.IntOption
|
CfgOptionNameServersKey = "dns/nameservers"
|
||||||
doNotUseMulticastDNS status.SecurityLevelOption
|
configuredNameServers config.StringArrayOption
|
||||||
doNotUseAssignedNameservers status.SecurityLevelOption
|
|
||||||
doNotUseInsecureProtocols status.SecurityLevelOption
|
CfgOptionNameserverRetryRateKey = "dns/nameserverRetryRate"
|
||||||
doNotResolveSpecialDomains status.SecurityLevelOption
|
nameserverRetryRate config.IntOption
|
||||||
doNotResolveTestDomains status.SecurityLevelOption
|
|
||||||
|
CfgOptionNoMulticastDNSKey = "dns/noMulticastDNS"
|
||||||
|
noMulticastDNS status.SecurityLevelOption
|
||||||
|
|
||||||
|
CfgOptionNoAssignedNameserversKey = "dns/noAssignedNameservers"
|
||||||
|
noAssignedNameservers status.SecurityLevelOption
|
||||||
|
|
||||||
|
CfgOptionNoInsecureProtocolsKey = "dns/noInsecureProtocols"
|
||||||
|
noInsecureProtocols status.SecurityLevelOption
|
||||||
|
|
||||||
|
CfgOptionDontResolveSpecialDomainsKey = "dns/dontResolveSpecialDomains"
|
||||||
|
dontResolveSpecialDomains status.SecurityLevelOption
|
||||||
|
|
||||||
|
CfgOptionDontResolveTestDomainsKey = "dns/dontResolveTestDomains"
|
||||||
|
dontResolveTestDomains status.SecurityLevelOption
|
||||||
)
|
)
|
||||||
|
|
||||||
func prepConfig() error {
|
func prepConfig() error {
|
||||||
err := config.Register(&config.Option{
|
err := config.Register(&config.Option{
|
||||||
Name: "Nameservers (DNS)",
|
Name: "DNS Servers",
|
||||||
Key: "intel/nameservers",
|
Key: CfgOptionNameServersKey,
|
||||||
Description: "Nameserver to use for resolving DNS requests.",
|
Description: "DNS Servers to use for resolving DNS requests.",
|
||||||
OptType: config.OptTypeStringArray,
|
OptType: config.OptTypeStringArray,
|
||||||
ExpertiseLevel: config.ExpertiseLevelExpert,
|
ExpertiseLevel: config.ExpertiseLevelExpert,
|
||||||
ReleaseLevel: config.ReleaseLevelStable,
|
ReleaseLevel: config.ReleaseLevelStable,
|
||||||
|
@ -47,12 +95,12 @@ func prepConfig() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
configuredNameServers = config.Concurrent.GetAsStringArray("intel/nameservers", defaultNameServers)
|
configuredNameServers = config.Concurrent.GetAsStringArray(CfgOptionNameServersKey, defaultNameServers)
|
||||||
|
|
||||||
err = config.Register(&config.Option{
|
err = config.Register(&config.Option{
|
||||||
Name: "Nameserver Retry Rate",
|
Name: "DNS Server Retry Rate",
|
||||||
Key: "intel/nameserverRetryRate",
|
Key: CfgOptionNameserverRetryRateKey,
|
||||||
Description: "Rate at which to retry failed nameservers, in seconds.",
|
Description: "Rate at which to retry failed DNS Servers, in seconds.",
|
||||||
OptType: config.OptTypeInt,
|
OptType: config.OptTypeInt,
|
||||||
ExpertiseLevel: config.ExpertiseLevelExpert,
|
ExpertiseLevel: config.ExpertiseLevelExpert,
|
||||||
ReleaseLevel: config.ReleaseLevelStable,
|
ReleaseLevel: config.ReleaseLevelStable,
|
||||||
|
@ -61,11 +109,11 @@ func prepConfig() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
nameserverRetryRate = config.Concurrent.GetAsInt("intel/nameserverRetryRate", 0)
|
nameserverRetryRate = config.Concurrent.GetAsInt(CfgOptionNameserverRetryRateKey, 600)
|
||||||
|
|
||||||
err = config.Register(&config.Option{
|
err = config.Register(&config.Option{
|
||||||
Name: "Do not use Multicast DNS",
|
Name: "Do not use Multicast DNS",
|
||||||
Key: "intel/doNotUseMulticastDNS",
|
Key: CfgOptionNoMulticastDNSKey,
|
||||||
Description: "Multicast DNS queries other devices in the local network",
|
Description: "Multicast DNS queries other devices in the local network",
|
||||||
OptType: config.OptTypeInt,
|
OptType: config.OptTypeInt,
|
||||||
ExpertiseLevel: config.ExpertiseLevelExpert,
|
ExpertiseLevel: config.ExpertiseLevelExpert,
|
||||||
|
@ -77,11 +125,11 @@ func prepConfig() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
doNotUseMulticastDNS = status.ConfigIsActiveConcurrent("intel/doNotUseMulticastDNS")
|
noMulticastDNS = status.ConfigIsActiveConcurrent(CfgOptionNoMulticastDNSKey)
|
||||||
|
|
||||||
err = config.Register(&config.Option{
|
err = config.Register(&config.Option{
|
||||||
Name: "Do not use assigned Nameservers",
|
Name: "Do not use assigned Nameservers",
|
||||||
Key: "intel/doNotUseAssignedNameservers",
|
Key: CfgOptionNoAssignedNameserversKey,
|
||||||
Description: "that were acquired by the network (dhcp) or system",
|
Description: "that were acquired by the network (dhcp) or system",
|
||||||
OptType: config.OptTypeInt,
|
OptType: config.OptTypeInt,
|
||||||
ExpertiseLevel: config.ExpertiseLevelExpert,
|
ExpertiseLevel: config.ExpertiseLevelExpert,
|
||||||
|
@ -93,27 +141,27 @@ func prepConfig() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
doNotUseAssignedNameservers = status.ConfigIsActiveConcurrent("intel/doNotUseAssignedNameservers")
|
noAssignedNameservers = status.ConfigIsActiveConcurrent(CfgOptionNoAssignedNameserversKey)
|
||||||
|
|
||||||
err = config.Register(&config.Option{
|
err = config.Register(&config.Option{
|
||||||
Name: "Do not resolve insecurely",
|
Name: "Do not resolve insecurely",
|
||||||
Key: "intel/doNotUseInsecureProtocols",
|
Key: CfgOptionNoInsecureProtocolsKey,
|
||||||
Description: "Do not resolve domains with insecure protocols, ie. plain DNS",
|
Description: "Do not resolve domains with insecure protocols, ie. plain DNS",
|
||||||
OptType: config.OptTypeInt,
|
OptType: config.OptTypeInt,
|
||||||
ExpertiseLevel: config.ExpertiseLevelExpert,
|
ExpertiseLevel: config.ExpertiseLevelExpert,
|
||||||
ReleaseLevel: config.ReleaseLevelStable,
|
ReleaseLevel: config.ReleaseLevelStable,
|
||||||
ExternalOptType: "security level",
|
ExternalOptType: "security level",
|
||||||
DefaultValue: 4,
|
DefaultValue: 6,
|
||||||
ValidationRegex: "^(7|6|4)$",
|
ValidationRegex: "^(7|6|4)$",
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
doNotUseInsecureProtocols = status.ConfigIsActiveConcurrent("intel/doNotUseInsecureProtocols")
|
noInsecureProtocols = status.ConfigIsActiveConcurrent(CfgOptionNoInsecureProtocolsKey)
|
||||||
|
|
||||||
err = config.Register(&config.Option{
|
err = config.Register(&config.Option{
|
||||||
Name: "Do not resolve special domains",
|
Name: "Do not resolve special domains",
|
||||||
Key: "intel/doNotResolveSpecialDomains",
|
Key: CfgOptionDontResolveSpecialDomainsKey,
|
||||||
Description: fmt.Sprintf("Do not resolve the special top level domains %s", formatScopeList(specialServiceScopes)),
|
Description: fmt.Sprintf("Do not resolve the special top level domains %s", formatScopeList(specialServiceScopes)),
|
||||||
OptType: config.OptTypeInt,
|
OptType: config.OptTypeInt,
|
||||||
ExpertiseLevel: config.ExpertiseLevelExpert,
|
ExpertiseLevel: config.ExpertiseLevelExpert,
|
||||||
|
@ -125,11 +173,11 @@ func prepConfig() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
doNotResolveSpecialDomains = status.ConfigIsActiveConcurrent("intel/doNotResolveSpecialDomains")
|
dontResolveSpecialDomains = status.ConfigIsActiveConcurrent(CfgOptionDontResolveSpecialDomainsKey)
|
||||||
|
|
||||||
err = config.Register(&config.Option{
|
err = config.Register(&config.Option{
|
||||||
Name: "Do not resolve test domains",
|
Name: "Do not resolve test domains",
|
||||||
Key: "intel/doNotResolveTestDomains",
|
Key: CfgOptionDontResolveTestDomainsKey,
|
||||||
Description: fmt.Sprintf("Do not resolve the special testing top level domains %s", formatScopeList(localTestScopes)),
|
Description: fmt.Sprintf("Do not resolve the special testing top level domains %s", formatScopeList(localTestScopes)),
|
||||||
OptType: config.OptTypeInt,
|
OptType: config.OptTypeInt,
|
||||||
ExpertiseLevel: config.ExpertiseLevelExpert,
|
ExpertiseLevel: config.ExpertiseLevelExpert,
|
||||||
|
@ -141,7 +189,7 @@ func prepConfig() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
doNotResolveTestDomains = status.ConfigIsActiveConcurrent("intel/doNotResolveTestDomains")
|
dontResolveTestDomains = status.ConfigIsActiveConcurrent(CfgOptionDontResolveTestDomainsKey)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -158,13 +158,14 @@ func deduplicateRequest(ctx context.Context, q *Query) (finishRequest func()) {
|
||||||
dupKey := fmt.Sprintf("%s%s", q.FQDN, q.QType.String())
|
dupKey := fmt.Sprintf("%s%s", q.FQDN, q.QType.String())
|
||||||
|
|
||||||
dupReqLock.Lock()
|
dupReqLock.Lock()
|
||||||
defer dupReqLock.Unlock()
|
|
||||||
|
|
||||||
// get duplicate request waitgroup
|
// get duplicate request waitgroup
|
||||||
wg, requestActive := dupReqMap[dupKey]
|
wg, requestActive := dupReqMap[dupKey]
|
||||||
|
|
||||||
// someone else is already on it!
|
// someone else is already on it!
|
||||||
if requestActive {
|
if requestActive {
|
||||||
|
dupReqLock.Unlock()
|
||||||
|
|
||||||
// log that we are waiting
|
// log that we are waiting
|
||||||
log.Tracer(ctx).Tracef("intel: waiting for duplicate query for %s to complete", dupKey)
|
log.Tracer(ctx).Tracef("intel: waiting for duplicate query for %s to complete", dupKey)
|
||||||
// wait
|
// wait
|
||||||
|
@ -182,6 +183,8 @@ func deduplicateRequest(ctx context.Context, q *Query) (finishRequest func()) {
|
||||||
// add to registry
|
// add to registry
|
||||||
dupReqMap[dupKey] = wg
|
dupReqMap[dupKey] = wg
|
||||||
|
|
||||||
|
dupReqLock.Unlock()
|
||||||
|
|
||||||
// return function to mark request as finished
|
// return function to mark request as finished
|
||||||
return func() {
|
return func() {
|
||||||
dupReqLock.Lock()
|
dupReqLock.Lock()
|
||||||
|
@ -222,7 +225,7 @@ resolveLoop:
|
||||||
rrCache, err = resolver.Conn.Query(ctx, q)
|
rrCache, err = resolver.Conn.Query(ctx, q)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
||||||
// FIXME: check if we are online?
|
// TODO: check if we are online?
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case errors.Is(err, ErrNotFound):
|
case errors.Is(err, ErrNotFound):
|
||||||
|
|
|
@ -114,7 +114,7 @@ func domainInScope(dotPrefixedFQDN string, scopeList []string) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetResolversInScope returns all resolvers that are in scope the resolve the given query and options.
|
// GetResolversInScope returns all resolvers that are in scope the resolve the given query and options.
|
||||||
func GetResolversInScope(ctx context.Context, q *Query) (selected []*Resolver) {
|
func GetResolversInScope(ctx context.Context, q *Query) (selected []*Resolver) { //nolint:gocognit // TODO
|
||||||
resolversLock.RLock()
|
resolversLock.RLock()
|
||||||
defer resolversLock.RUnlock()
|
defer resolversLock.RUnlock()
|
||||||
|
|
||||||
|
@ -226,13 +226,13 @@ func (q *Query) checkCompliance() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// special TLDs
|
// special TLDs
|
||||||
if doNotResolveSpecialDomains(q.SecurityLevel) &&
|
if dontResolveSpecialDomains(q.SecurityLevel) &&
|
||||||
domainInScope(q.dotPrefixedFQDN, specialServiceScopes) {
|
domainInScope(q.dotPrefixedFQDN, specialServiceScopes) {
|
||||||
return ErrSpecialDomainsDisabled
|
return ErrSpecialDomainsDisabled
|
||||||
}
|
}
|
||||||
|
|
||||||
// testing TLDs
|
// testing TLDs
|
||||||
if doNotResolveTestDomains(q.SecurityLevel) &&
|
if dontResolveTestDomains(q.SecurityLevel) &&
|
||||||
domainInScope(q.dotPrefixedFQDN, localTestScopes) {
|
domainInScope(q.dotPrefixedFQDN, localTestScopes) {
|
||||||
return ErrTestDomainsDisabled
|
return ErrTestDomainsDisabled
|
||||||
}
|
}
|
||||||
|
@ -245,7 +245,7 @@ func (resolver *Resolver) checkCompliance(_ context.Context, q *Query) error {
|
||||||
return errSkip
|
return errSkip
|
||||||
}
|
}
|
||||||
|
|
||||||
if doNotUseInsecureProtocols(q.SecurityLevel) {
|
if noInsecureProtocols(q.SecurityLevel) {
|
||||||
switch resolver.ServerType {
|
switch resolver.ServerType {
|
||||||
case ServerTypeDNS:
|
case ServerTypeDNS:
|
||||||
return errInsecureProtocol
|
return errInsecureProtocol
|
||||||
|
@ -260,13 +260,13 @@ func (resolver *Resolver) checkCompliance(_ context.Context, q *Query) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if doNotUseAssignedNameservers(q.SecurityLevel) {
|
if noAssignedNameservers(q.SecurityLevel) {
|
||||||
if resolver.Source == ServerSourceAssigned {
|
if resolver.Source == ServerSourceAssigned {
|
||||||
return errAssignedServer
|
return errAssignedServer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if doNotUseMulticastDNS(q.SecurityLevel) {
|
if noMulticastDNS(q.SecurityLevel) {
|
||||||
if resolver.Source == ServerSourceMDNS {
|
if resolver.Source == ServerSourceMDNS {
|
||||||
return errMulticastDNS
|
return errMulticastDNS
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@ func (resolver *Resolver) String() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResolverConn is an interface to implement different types of query backends.
|
// ResolverConn is an interface to implement different types of query backends.
|
||||||
type ResolverConn interface {
|
type ResolverConn interface { //nolint:go-lint // TODO
|
||||||
Query(ctx context.Context, q *Query) (*RRCache, error)
|
Query(ctx context.Context, q *Query) (*RRCache, error)
|
||||||
MarkFailed()
|
MarkFailed()
|
||||||
LastFail() time.Time
|
LastFail() time.Time
|
||||||
|
|
Loading…
Add table
Reference in a new issue