Merge pull request #52 from safing/feature/firewall-resolver-improvements

Firewall and Resolver improvements
This commit is contained in:
Patrick Pacher
2020-05-20 18:07:48 +02:00
committed by GitHub
70 changed files with 2288 additions and 1549 deletions

View File

@@ -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",

View File

@@ -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:

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

View File

@@ -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

View File

@@ -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")

View File

@@ -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
}

View File

@@ -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()