Merge pull request #52 from safing/feature/firewall-resolver-improvements
Firewall and Resolver improvements
This commit is contained in:
@@ -94,6 +94,7 @@ func registerConfiguration() error {
|
||||
Description: `The default filter action when nothing else permits or blocks a connection.`,
|
||||
Order: cfgOptionDefaultActionOrder,
|
||||
OptType: config.OptTypeString,
|
||||
ReleaseLevel: config.ReleaseLevelExperimental,
|
||||
DefaultValue: "permit",
|
||||
ExternalOptType: "string list",
|
||||
ValidationRegex: "^(permit|ask|block)$",
|
||||
@@ -121,17 +122,12 @@ func registerConfiguration() error {
|
||||
cfgOptionDisableAutoPermit = config.Concurrent.GetAsInt(CfgOptionDisableAutoPermitKey, int64(status.SecurityLevelsAll))
|
||||
cfgIntOptions[CfgOptionDisableAutoPermitKey] = cfgOptionDisableAutoPermit
|
||||
|
||||
// Endpoint Filter List
|
||||
err = config.Register(&config.Option{
|
||||
Name: "Endpoint Filter List",
|
||||
Key: CfgOptionEndpointsKey,
|
||||
Description: "Filter outgoing connections by matching the destination endpoint. Network Scope restrictions still apply.",
|
||||
Help: `Format:
|
||||
filterListHelp := `Format:
|
||||
Permission:
|
||||
"+": permit
|
||||
"-": block
|
||||
Host Matching:
|
||||
IP, CIDR, Country Code, ASN, Filterlist, "*" for any
|
||||
IP, CIDR, Country Code, ASN, Filterlist, Network Scope, "*" for any
|
||||
Domains:
|
||||
"example.com": exact match
|
||||
".example.com": exact match + subdomains
|
||||
@@ -144,11 +140,20 @@ func registerConfiguration() error {
|
||||
Examples:
|
||||
+ .example.com */HTTP
|
||||
- .example.com
|
||||
+ 192.168.0.1/24
|
||||
+ 192.168.0.1
|
||||
+ 192.168.1.1/24
|
||||
+ Localhost,LAN
|
||||
- AS123456789
|
||||
- L:MAL
|
||||
- AS0
|
||||
+ AT
|
||||
- *`,
|
||||
- *`
|
||||
|
||||
// Endpoint Filter List
|
||||
err = config.Register(&config.Option{
|
||||
Name: "Endpoint Filter List",
|
||||
Key: CfgOptionEndpointsKey,
|
||||
Description: "Filter outgoing connections by matching the destination endpoint. Network Scope restrictions still apply.",
|
||||
Help: filterListHelp,
|
||||
Order: cfgOptionEndpointsOrder,
|
||||
OptType: config.OptTypeStringArray,
|
||||
DefaultValue: []string{},
|
||||
@@ -163,35 +168,13 @@ Examples:
|
||||
|
||||
// Service Endpoint Filter List
|
||||
err = config.Register(&config.Option{
|
||||
Name: "Service Endpoint Filter List",
|
||||
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.",
|
||||
Help: `Format:
|
||||
Permission:
|
||||
"+": permit
|
||||
"-": block
|
||||
Host Matching:
|
||||
IP, CIDR, Country Code, ASN, Filterlist, "*" for any
|
||||
Domains:
|
||||
"example.com": exact match
|
||||
".example.com": exact match + subdomains
|
||||
"*xample.com": prefix wildcard
|
||||
"example.*": suffix wildcard
|
||||
"*example*": prefix and suffix wildcard
|
||||
Protocol and Port Matching (optional):
|
||||
<protocol>/<port>
|
||||
|
||||
Examples:
|
||||
+ .example.com */HTTP
|
||||
- .example.com
|
||||
+ 192.168.0.1/24
|
||||
- L:MAL
|
||||
- AS0
|
||||
+ AT
|
||||
- *`,
|
||||
Name: "Service Endpoint Filter List",
|
||||
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.",
|
||||
Help: filterListHelp,
|
||||
Order: cfgOptionServiceEndpointsOrder,
|
||||
OptType: config.OptTypeStringArray,
|
||||
DefaultValue: []string{},
|
||||
DefaultValue: []string{"+ Localhost"},
|
||||
ExternalOptType: "endpoint list",
|
||||
ValidationRegex: `^(\+|\-) [A-z0-9\.:\-*/]+( [A-z0-9/]+)?$`,
|
||||
})
|
||||
@@ -313,7 +296,7 @@ Examples:
|
||||
Order: cfgOptionBlockP2POrder,
|
||||
OptType: config.OptTypeInt,
|
||||
ExternalOptType: "security level",
|
||||
DefaultValue: status.SecurityLevelsAll,
|
||||
DefaultValue: status.SecurityLevelExtreme,
|
||||
ValidationRegex: "^(4|6|7)$",
|
||||
})
|
||||
if err != nil {
|
||||
@@ -326,7 +309,7 @@ Examples:
|
||||
err = config.Register(&config.Option{
|
||||
Name: "Block Inbound Connections",
|
||||
Key: CfgOptionBlockInboundKey,
|
||||
Description: "Connections initiated towards your device. This will usually only be the case if you are running a network service or are using peer to peer software.",
|
||||
Description: "Connections initiated towards your device from the LAN or Internet. This will usually only be the case if you are running a network service or are using peer to peer software.",
|
||||
Order: cfgOptionBlockInboundOrder,
|
||||
OptType: config.OptTypeInt,
|
||||
ExternalOptType: "security level",
|
||||
|
||||
@@ -31,7 +31,7 @@ type EndpointDomain struct {
|
||||
}
|
||||
|
||||
func (ep *EndpointDomain) check(entity *intel.Entity, domain string) (EPResult, Reason) {
|
||||
result, reason := ep.match(ep, entity, ep.Domain, "domain matches")
|
||||
result, reason := ep.match(ep, entity, ep.OriginalValue, "domain matches")
|
||||
|
||||
switch ep.MatchType {
|
||||
case domainMatchTypeExact:
|
||||
|
||||
106
profile/endpoints/endpoint-scopes.go
Normal file
106
profile/endpoints/endpoint-scopes.go
Normal file
@@ -0,0 +1,106 @@
|
||||
package endpoints
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/safing/portmaster/network/netutils"
|
||||
|
||||
"github.com/safing/portmaster/intel"
|
||||
)
|
||||
|
||||
const (
|
||||
scopeLocalhost = 1
|
||||
scopeLocalhostName = "Localhost"
|
||||
scopeLocalhostMatcher = "localhost"
|
||||
|
||||
scopeLAN = 2
|
||||
scopeLANName = "LAN"
|
||||
scopeLANMatcher = "lan"
|
||||
|
||||
scopeInternet = 4
|
||||
scopeInternetName = "Internet"
|
||||
scopeInternetMatcher = "internet"
|
||||
)
|
||||
|
||||
// EndpointScope matches network scopes.
|
||||
type EndpointScope struct {
|
||||
EndpointBase
|
||||
|
||||
scopes uint8
|
||||
}
|
||||
|
||||
// Matches checks whether the given entity matches this endpoint definition.
|
||||
func (ep *EndpointScope) Matches(entity *intel.Entity) (EPResult, Reason) {
|
||||
if entity.IP == nil {
|
||||
return Undeterminable, nil
|
||||
}
|
||||
|
||||
classification := netutils.ClassifyIP(entity.IP)
|
||||
var scope uint8
|
||||
switch classification {
|
||||
case netutils.HostLocal:
|
||||
scope = scopeLocalhost
|
||||
case netutils.LinkLocal:
|
||||
scope = scopeLAN
|
||||
case netutils.SiteLocal:
|
||||
scope = scopeLAN
|
||||
case netutils.Global:
|
||||
scope = scopeInternet
|
||||
case netutils.LocalMulticast:
|
||||
scope = scopeLAN
|
||||
case netutils.GlobalMulticast:
|
||||
scope = scopeInternet
|
||||
}
|
||||
|
||||
if ep.scopes&scope > 0 {
|
||||
return ep.match(ep, entity, ep.Scopes(), "scope matches")
|
||||
}
|
||||
return NoMatch, nil
|
||||
}
|
||||
|
||||
// Scopes returns the string representation of all scopes.
|
||||
func (ep *EndpointScope) Scopes() string {
|
||||
// single scope
|
||||
switch ep.scopes {
|
||||
case scopeLocalhost:
|
||||
return scopeLocalhostName
|
||||
case scopeLAN:
|
||||
return scopeLANName
|
||||
case scopeInternet:
|
||||
return scopeInternetName
|
||||
}
|
||||
|
||||
// multiple scopes
|
||||
var s []string
|
||||
if ep.scopes&scopeLocalhost > 0 {
|
||||
s = append(s, scopeLocalhostName)
|
||||
}
|
||||
if ep.scopes&scopeLAN > 0 {
|
||||
s = append(s, scopeLANName)
|
||||
}
|
||||
if ep.scopes&scopeInternet > 0 {
|
||||
s = append(s, scopeInternetName)
|
||||
}
|
||||
return strings.Join(s, ",")
|
||||
}
|
||||
|
||||
func (ep *EndpointScope) String() string {
|
||||
return ep.renderPPP(ep.Scopes())
|
||||
}
|
||||
|
||||
func parseTypeScope(fields []string) (Endpoint, error) {
|
||||
ep := &EndpointScope{}
|
||||
for _, val := range strings.Split(strings.ToLower(fields[1]), ",") {
|
||||
switch val {
|
||||
case scopeLocalhostMatcher:
|
||||
ep.scopes ^= scopeLocalhost
|
||||
case scopeLANMatcher:
|
||||
ep.scopes ^= scopeLAN
|
||||
case scopeInternetMatcher:
|
||||
ep.scopes ^= scopeInternet
|
||||
default:
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
return ep.parsePPP(ep, fields)
|
||||
}
|
||||
@@ -201,7 +201,7 @@ func invalidDefinitionError(fields []string, msg string) error {
|
||||
return fmt.Errorf(`invalid endpoint definition: "%s" - %s`, strings.Join(fields, " "), msg)
|
||||
}
|
||||
|
||||
func parseEndpoint(value string) (endpoint Endpoint, err error) {
|
||||
func parseEndpoint(value string) (endpoint Endpoint, err error) { //nolint:gocognit
|
||||
fields := strings.Fields(value)
|
||||
if len(fields) < 2 {
|
||||
return nil, fmt.Errorf(`invalid endpoint definition: "%s"`, value)
|
||||
@@ -231,6 +231,10 @@ func parseEndpoint(value string) (endpoint Endpoint, err error) {
|
||||
if endpoint, err = parseTypeASN(fields); endpoint != nil || err != nil {
|
||||
return
|
||||
}
|
||||
// scopes
|
||||
if endpoint, err = parseTypeScope(fields); endpoint != nil || err != nil {
|
||||
return
|
||||
}
|
||||
// lists
|
||||
if endpoint, err = parseTypeList(fields); endpoint != nil || err != nil {
|
||||
return
|
||||
|
||||
@@ -43,6 +43,12 @@ func TestEndpointParsing(t *testing.T) {
|
||||
testParsing(t, "+ AS1234")
|
||||
testParsing(t, "+ AS12345")
|
||||
|
||||
// network scope
|
||||
testParsing(t, "+ Localhost")
|
||||
testParsing(t, "+ LAN")
|
||||
testParsing(t, "+ Internet")
|
||||
testParsing(t, "+ Localhost,LAN,Internet")
|
||||
|
||||
// protocol and ports
|
||||
testParsing(t, "+ * TCP/1-1024")
|
||||
testParsing(t, "+ * */DNS")
|
||||
|
||||
@@ -342,7 +342,26 @@ func TestEndpointMatching(t *testing.T) {
|
||||
IP: net.ParseIP("151.101.1.164"), // nytimes.com
|
||||
}).Init(), NoMatch)
|
||||
|
||||
// Scope
|
||||
|
||||
ep, err = parseEndpoint("+ Localhost,LAN")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
testEndpointMatch(t, ep, (&intel.Entity{
|
||||
IP: net.ParseIP("192.168.0.1"),
|
||||
}).Init(), Permitted)
|
||||
testEndpointMatch(t, ep, (&intel.Entity{
|
||||
IP: net.ParseIP("151.101.1.164"), // nytimes.com
|
||||
}).Init(), NoMatch)
|
||||
|
||||
// Lists
|
||||
|
||||
_, err = parseEndpoint("+ L:A,B,C")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// TODO: write test for lists matcher
|
||||
|
||||
}
|
||||
|
||||
@@ -126,6 +126,18 @@ func (lp *LayeredProfile) getValidityFlag() *abool.AtomicBool {
|
||||
return lp.validityFlag
|
||||
}
|
||||
|
||||
// RevisionCnt returns the current profile revision counter.
|
||||
func (lp *LayeredProfile) RevisionCnt() (revisionCounter uint64) {
|
||||
if lp == nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
lp.lock.Lock()
|
||||
defer lp.lock.Unlock()
|
||||
|
||||
return lp.revisionCounter
|
||||
}
|
||||
|
||||
// Update checks for updated profiles and replaces any outdated profiles.
|
||||
func (lp *LayeredProfile) Update() (revisionCounter uint64) {
|
||||
lp.lock.Lock()
|
||||
|
||||
Reference in New Issue
Block a user