Revamp Profile Domains and Ports to Endpoints and ServiceEndpoints

This commit is contained in:
Daniel 2019-01-17 10:55:06 +01:00
parent 4017de7dac
commit bde81d815d
13 changed files with 417 additions and 249 deletions

38
intel/main_test.go Normal file
View file

@ -0,0 +1,38 @@
package intel
import (
"os"
"testing"
"github.com/Safing/portbase/database/dbmodule"
"github.com/Safing/portbase/log"
"github.com/Safing/portbase/modules"
)
func TestMain(m *testing.M) {
// setup
testDir := os.TempDir()
dbmodule.SetDatabaseLocation(testDir)
err := modules.Start()
if err != nil {
if err == modules.ErrCleanExit {
os.Exit(0)
} else {
err = modules.Shutdown()
if err != nil {
log.Shutdown()
}
os.Exit(1)
}
}
// run tests
rv := m.Run()
// teardown
modules.Shutdown()
os.RemoveAll(testDir)
// exit with test run return value
os.Exit(rv)
}

72
intel/reverse.go Normal file
View file

@ -0,0 +1,72 @@
package intel
import (
"errors"
"strings"
"github.com/Safing/portbase/log"
"github.com/miekg/dns"
)
// ResolveIPAndValidate finds (reverse DNS), validates (forward DNS) and returns the domain name assigned to the given IP.
func ResolveIPAndValidate(ip string, securityLevel uint8) (domain string, err error) {
// get reversed DNS address
rQ, err := dns.ReverseAddr(ip)
if err != nil {
log.Tracef("intel: failed to get reverse address of %s: %s", ip, err)
return "", err
}
// get PTR record
rrCache := Resolve(rQ, dns.Type(dns.TypePTR), securityLevel)
if rrCache == nil {
return "", errors.New("querying for PTR record failed (may be NXDomain)")
}
// get result from record
var ptrName string
for _, rr := range rrCache.Answer {
ptrRec, ok := rr.(*dns.PTR)
if ok {
ptrName = ptrRec.Ptr
break
}
}
// check for nxDomain
if ptrName == "" {
return "", errors.New("no PTR record for IP (nxDomain)")
}
log.Infof("ptrName: %s", ptrName)
// get forward record
if strings.Contains(ip, ":") {
rrCache = Resolve(ptrName, dns.Type(dns.TypeAAAA), securityLevel)
} else {
rrCache = Resolve(ptrName, dns.Type(dns.TypeA), securityLevel)
}
if rrCache == nil {
return "", errors.New("querying for A/AAAA record failed (may be NXDomain)")
}
// check for matching A/AAAA record
log.Infof("rr: %s", rrCache)
for _, rr := range rrCache.Answer {
switch v := rr.(type) {
case *dns.A:
log.Infof("A: %s", v.A.String())
if ip == v.A.String() {
return ptrName, nil
}
case *dns.AAAA:
log.Infof("AAAA: %s", v.AAAA.String())
if ip == v.AAAA.String() {
return ptrName, nil
}
}
}
// no match
return "", errors.New("validation failed")
}

28
intel/reverse_test.go Normal file
View file

@ -0,0 +1,28 @@
package intel
import "testing"
func testReverse(t *testing.T, ip, result, expectedErr string) {
domain, err := ResolveIPAndValidate(ip, 0)
if err != nil {
if expectedErr == "" || err.Error() != expectedErr {
t.Errorf("reverse-validating %s: unexpected error: %s", ip, err)
}
return
}
if domain != result {
t.Errorf("reverse-validating %s: unexpected result: %s", ip, domain)
}
}
func TestResolveIPAndValidate(t *testing.T) {
testReverse(t, "198.41.0.4", "a.root-servers.net.", "")
testReverse(t, "9.9.9.9", "dns.quad9.net.", "")
testReverse(t, "2620:fe::fe", "dns.quad9.net.", "")
testReverse(t, "1.1.1.1", "one.one.one.one.", "")
testReverse(t, "2606:4700:4700::1111", "one.one.one.one.", "")
testReverse(t, "93.184.216.34", "example.com.", "no PTR record for IP (nxDomain)")
testReverse(t, "185.199.109.153", "sites.github.io.", "no PTR record for IP (nxDomain)")
}

View file

@ -1,8 +1,6 @@
package profile package profile
import ( import (
"time"
"github.com/Safing/portmaster/status" "github.com/Safing/portmaster/status"
) )
@ -32,50 +30,14 @@ func makeDefaultFallbackProfile() *Profile {
Related: status.SecurityLevelDynamic, Related: status.SecurityLevelDynamic,
PeerToPeer: status.SecurityLevelDynamic, PeerToPeer: status.SecurityLevelDynamic,
}, },
Ports: map[int16][]*Port{ ServiceEndpoints: []*EndpointPermission{
6: []*Port{ &EndpointPermission{
&Port{ // SSH DomainOrIP: "",
Permit: true, Wildcard: true,
Created: time.Now().Unix(), Protocol: 0,
Start: 22, StartPort: 0,
End: 22, EndPort: 0,
}, Permit: false,
&Port{ // HTTP
Permit: true,
Created: time.Now().Unix(),
Start: 80,
End: 80,
},
&Port{ // HTTPS
Permit: true,
Created: time.Now().Unix(),
Start: 443,
End: 443,
},
&Port{ // SMTP (TLS)
Permit: true,
Created: time.Now().Unix(),
Start: 465,
End: 465,
},
&Port{ // SMTP (STARTTLS)
Permit: true,
Created: time.Now().Unix(),
Start: 587,
End: 587,
},
&Port{ // IMAP (TLS)
Permit: true,
Created: time.Now().Unix(),
Start: 993,
End: 993,
},
&Port{ // IMAP (STARTTLS)
Permit: true,
Created: time.Now().Unix(),
Start: 143,
End: 143,
},
}, },
}, },
} }

View file

@ -3,6 +3,9 @@ package profile
import ( import (
"fmt" "fmt"
"strconv" "strconv"
"strings"
"github.com/Safing/portmaster/intel"
) )
// Endpoints is a list of permitted or denied endpoints. // Endpoints is a list of permitted or denied endpoints.
@ -10,13 +13,13 @@ type Endpoints []*EndpointPermission
// EndpointPermission holds a decision about an endpoint. // EndpointPermission holds a decision about an endpoint.
type EndpointPermission struct { type EndpointPermission struct {
DomainOrIP string DomainOrIP string
IncludeSubdomains bool Wildcard bool
Protocol uint8 Protocol uint8
PortStart uint16 StartPort uint16
PortEnd uint16 EndPort uint16
Permit bool Permit bool
Created int64 Created int64
} }
// IsSet returns whether the Endpoints object is "set". // IsSet returns whether the Endpoints object is "set".
@ -28,59 +31,105 @@ func (e Endpoints) IsSet() bool {
} }
// Check checks if the given domain is governed in the list of domains and returns whether it is permitted. // Check checks if the given domain is governed in the list of domains and returns whether it is permitted.
func (e Endpoints) Check(domainOrIP string, protocol uint8, port uint16) (permit, ok bool) { // If getDomainOfIP (returns reverse and forward dns matching domain name) is supplied, an IP will be resolved to a domain, if necessary.
// check for exact domain func (e Endpoints) Check(domainOrIP string, protocol uint8, port uint16, checkReverseIP bool, securityLevel uint8) (permit bool, reason string, ok bool) {
ed, ok := d[domain]
if ok {
return ed.Permit, true
}
for _, entry := range e { // ip resolving
if entry.Matches(domainOrIP, protocol, port) { var cachedGetDomainOfIP func() string
return entry.Permit, true if checkReverseIP {
var ipResolved bool
var ipName string
// setup caching wrapper
cachedGetDomainOfIP = func() string {
if !ipResolved {
result, err := intel.ResolveIPAndValidate(domainOrIP, securityLevel)
if err != nil {
// log.Debug()
ipName = result
}
ipResolved = true
}
return ipName
} }
} }
return false, false isDomain := strings.HasSuffix(domainOrIP, ".")
for _, entry := range e {
if ok, reason := entry.Matches(domainOrIP, protocol, port, isDomain, cachedGetDomainOfIP); ok {
return entry.Permit, reason, true
}
}
return false, "", false
} }
// Matches checks whether a port object matches the given port. func isSubdomainOf(domain, subdomain string) bool {
func (ep EndpointPermission) Matches(domainOrIP string, protocol uint8, port uint16) bool { dotPrefixedDomain := "." + domain
if domainOrIP != ep.DomainOrIP { return strings.HasSuffix(subdomain, dotPrefixedDomain)
return false }
}
// Matches checks whether the given endpoint has a managed permission. If getDomainOfIP (returns reverse and forward dns matching domain name) is supplied, this declares an incoming connection.
func (ep EndpointPermission) Matches(domainOrIP string, protocol uint8, port uint16, isDomain bool, getDomainOfIP func() string) (match bool, reason string) {
if ep.Protocol > 0 && protocol != ep.Protocol { if ep.Protocol > 0 && protocol != ep.Protocol {
return false return false, ""
} }
if ep.PortStart > 0 && (port < ep.PortStart || port > ep.PortEnd) { if ep.StartPort > 0 && (port < ep.StartPort || port > ep.EndPort) {
return false return false, ""
} }
return true switch {
case ep.Wildcard && len(ep.DomainOrIP) == 0:
// host wildcard
return true, fmt.Sprintf("%s matches %s", domainOrIP, ep)
case domainOrIP == ep.DomainOrIP:
// host match
return true, fmt.Sprintf("%s matches %s", domainOrIP, ep)
case isDomain && ep.Wildcard && isSubdomainOf(ep.DomainOrIP, domainOrIP):
// subdomain match
return true, fmt.Sprintf("%s matches %s", domainOrIP, ep)
case !isDomain && getDomainOfIP != nil && getDomainOfIP() == ep.DomainOrIP:
// resolved IP match
return true, fmt.Sprintf("%s->%s matches %s", domainOrIP, getDomainOfIP(), ep)
case !isDomain && getDomainOfIP != nil && ep.Wildcard && isSubdomainOf(ep.DomainOrIP, getDomainOfIP()):
// resolved IP subdomain match
return true, fmt.Sprintf("%s->%s matches %s", domainOrIP, getDomainOfIP(), ep)
default:
// no match
return false, ""
}
}
func (e Endpoints) String() string {
var s []string
for _, entry := range e {
s = append(s, entry.String())
}
return fmt.Sprintf("[%s]", strings.Join(s, ", "))
} }
func (ep EndpointPermission) String() string { func (ep EndpointPermission) String() string {
s := ep.DomainOrIP s := ep.DomainOrIP
if ep.Protocol > 0 || ep.Start { s += " "
s += " "
}
if ep.Protocol > 0 { if ep.Protocol > 0 {
s += strconv.Itoa(int(ep.Protocol)) s += strconv.Itoa(int(ep.Protocol))
if ep.Start > 0 { } else {
s += "/" s += "*"
}
} }
if ep.Start > 0 { s += "/"
if p.Start == p.End {
s += strconv.Itoa(int(ep.Start)) if ep.StartPort > 0 {
if ep.StartPort == ep.EndPort {
s += strconv.Itoa(int(ep.StartPort))
} else { } else {
s += fmt.Sprintf("%d-%d", ep.Start, ep.End) s += fmt.Sprintf("%d-%d", ep.StartPort, ep.EndPort)
} }
} else {
s += "*"
} }
return s return s

View file

@ -2,47 +2,60 @@ package profile
import ( import (
"testing" "testing"
"time"
) )
func TestPorts(t *testing.T) { // TODO: RETIRED
var ports Ports // func testdeMatcher(t *testing.T, value string, expectedResult bool) {
ports = map[int16][]*Port{ // if domainEndingMatcher.MatchString(value) != expectedResult {
6: []*Port{ // if expectedResult {
&Port{ // SSH // t.Errorf("domainEndingMatcher should match %s", value)
Permit: true, // } else {
Created: time.Now().Unix(), // t.Errorf("domainEndingMatcher should not match %s", value)
Start: 22, // }
End: 22, // }
}, // }
//
// func TestdomainEndingMatcher(t *testing.T) {
// testdeMatcher(t, "example.com", true)
// testdeMatcher(t, "com", true)
// testdeMatcher(t, "example.xn--lgbbat1ad8j", true)
// testdeMatcher(t, "xn--lgbbat1ad8j", true)
// testdeMatcher(t, "fe80::beef", false)
// testdeMatcher(t, "fe80::dead:beef", false)
// testdeMatcher(t, "10.2.3.4", false)
// testdeMatcher(t, "4", false)
// }
func TestEPString(t *testing.T) {
var endpoints Endpoints
endpoints = []*EndpointPermission{
&EndpointPermission{
DomainOrIP: "example.com",
Wildcard: false,
Protocol: 6,
Permit: true,
}, },
-17: []*Port{ &EndpointPermission{
&Port{ // HTTP DomainOrIP: "8.8.8.8",
Permit: false, Protocol: 17, // TCP
Created: time.Now().Unix(), StartPort: 53, // DNS
Start: 80, EndPort: 53,
End: 81, Permit: false,
},
}, },
93: []*Port{ &EndpointPermission{
&Port{ // HTTP DomainOrIP: "google.com",
Permit: true, Wildcard: true,
Created: time.Now().Unix(), Permit: false,
Start: 93,
End: 93,
},
}, },
} }
if ports.String() != "TCP:[permit:22], <UDP:[deny:80-81], 93:[permit:93]" && if endpoints.String() != "[example.com 6/*, 8.8.8.8 17/53, google.com */*]" {
ports.String() != "93:[permit:93], TCP:[permit:22], <UDP:[deny:80-81]" && t.Errorf("unexpected result: %s", endpoints.String())
ports.String() != "<UDP:[deny:80-81], 93:[permit:93], TCP:[permit:22]" {
t.Errorf("unexpected result: %s", ports.String())
} }
var noPorts Ports var noEndpoints Endpoints
noPorts = map[int16][]*Port{} noEndpoints = []*EndpointPermission{}
if noPorts.String() != "None" { if noEndpoints.String() != "[]" {
t.Errorf("unexpected result: %s", ports.String()) t.Errorf("unexpected result: %s", noEndpoints.String())
} }
} }

View file

@ -2,6 +2,7 @@ package profile
import ( import (
"errors" "errors"
"fmt"
"strings" "strings"
"github.com/Safing/portmaster/status" "github.com/Safing/portmaster/status"
@ -115,7 +116,7 @@ func (flags Flags) String() string {
markedFlags = append(markedFlags, s) markedFlags = append(markedFlags, s)
} }
} }
return strings.Join(markedFlags, ", ") return fmt.Sprintf("[%s]", strings.Join(markedFlags, ", "))
} }
// Add adds a flag to the Flags with the given level. // Add adds a flag to the Flags with the given level.

View file

@ -31,7 +31,7 @@ func TestProfileFlags(t *testing.T) {
RequireGate17: status.SecurityLevelsSecureAndFortress, RequireGate17: status.SecurityLevelsSecureAndFortress,
} }
if testFlags.String() != "Prompt, Internet++-, LAN++-, Localhost, Related+--, RequireGate17-++" { if testFlags.String() != "[Prompt, Internet++-, LAN++-, Localhost, Related+--, RequireGate17-++]" {
t.Errorf("unexpected output: %s", testFlags.String()) t.Errorf("unexpected output: %s", testFlags.String())
} }

View file

@ -37,10 +37,10 @@ type Profile struct {
Fingerprints []*Fingerprint Fingerprints []*Fingerprint
// The mininum security level to apply to connections made with this profile // The mininum security level to apply to connections made with this profile
SecurityLevel uint8 SecurityLevel uint8
Flags Flags Flags Flags
Domains Domains Endpoints Endpoints
Ports Ports ServiceEndpoints Endpoints
// If a Profile is declared as a Framework (i.e. an Interpreter and the likes), then the real process must be found // If a Profile is declared as a Framework (i.e. an Interpreter and the likes), then the real process must be found
// Framework *Framework `json:",omitempty bson:",omitempty"` // Framework *Framework `json:",omitempty bson:",omitempty"`
@ -97,7 +97,7 @@ func (profile *Profile) String() string {
// DetailedString returns a more detailed string representation of theProfile. // DetailedString returns a more detailed string representation of theProfile.
func (profile *Profile) DetailedString() string { func (profile *Profile) DetailedString() string {
return fmt.Sprintf("%s(SL=%s Flags=[%s] Ports=[%s] #Domains=%d)", profile.Name, status.FmtSecurityLevel(profile.SecurityLevel), profile.Flags.String(), profile.Ports.String(), len(profile.Domains)) return fmt.Sprintf("%s(SL=%s Flags=%s Endpoints=%s)", profile.Name, status.FmtSecurityLevel(profile.SecurityLevel), profile.Flags.String(), profile.Endpoints.String())
} }
// GetUserProfile loads a profile from the database. // GetUserProfile loads a profile from the database.

View file

@ -8,7 +8,6 @@ import (
var ( var (
emptyFlags = Flags{} emptyFlags = Flags{}
emptyPorts = Ports{}
) )
// Set handles Profile chaining. // Set handles Profile chaining.
@ -120,8 +119,8 @@ func (set *Set) CheckFlag(flag uint8) (active bool) {
return false return false
} }
// CheckDomain checks if the given domain is governed in any the lists of domains and returns whether it is permitted. // CheckEndpoint checks if the given protocol and port are governed in any the lists of ports and returns whether it is permitted.
func (set *Set) CheckDomain(domain string) (permit, ok bool) { func (set *Set) CheckEndpoint(domainOrIP string, protocol uint8, port uint16, inbound bool) (permit bool, reason string, ok bool) {
set.Lock() set.Lock()
defer set.Unlock() defer set.Unlock()
@ -131,39 +130,19 @@ func (set *Set) CheckDomain(domain string) (permit, ok bool) {
} }
if profile != nil { if profile != nil {
permit, ok = profile.Domains.Check(domain) if inbound {
if ok { if permit, reason, ok = profile.ServiceEndpoints.Check(domainOrIP, protocol, port, inbound, set.combinedSecurityLevel); ok {
return return
}
} else {
if permit, reason, ok = profile.Endpoints.Check(domainOrIP, protocol, port, inbound, set.combinedSecurityLevel); ok {
return
}
} }
} }
} }
return false, false return false, "", false
}
// CheckPort checks if the given protocol and port are governed in any the lists of ports and returns whether it is permitted.
func (set *Set) CheckPort(listen bool, protocol uint8, port uint16) (permit, ok bool) {
set.Lock()
defer set.Unlock()
signedProtocol := int16(protocol)
if listen {
signedProtocol = -1 * signedProtocol
}
for i, profile := range set.profiles {
if i == 2 && set.independent {
continue
}
if profile != nil {
if permit, ok = profile.Ports.Check(signedProtocol, port); ok {
return
}
}
}
return false, false
} }
// getSecurityLevel returns the highest prioritized security level. // getSecurityLevel returns the highest prioritized security level.

View file

@ -26,26 +26,33 @@ func init() {
Flags: map[uint8]uint8{ Flags: map[uint8]uint8{
Independent: status.SecurityLevelFortress, Independent: status.SecurityLevelFortress,
}, },
Domains: map[string]*DomainDecision{ Endpoints: []*EndpointPermission{
"example.com": &DomainDecision{ &EndpointPermission{
Permit: true, DomainOrIP: "good.bad.example.com.",
Created: time.Now().Unix(), Wildcard: false,
IncludeSubdomains: false, Permit: true,
Created: time.Now().Unix(),
}, },
"bad.example.com": &DomainDecision{ &EndpointPermission{
Permit: false, DomainOrIP: "bad.example.com.",
Created: time.Now().Unix(), Wildcard: true,
IncludeSubdomains: true, Permit: false,
Created: time.Now().Unix(),
}, },
}, &EndpointPermission{
Ports: map[int16][]*Port{ DomainOrIP: "example.com.",
6: []*Port{ Wildcard: false,
&Port{ Permit: true,
Permit: true, Created: time.Now().Unix(),
Created: time.Now().Unix(), },
Start: 22000, &EndpointPermission{
End: 22000, DomainOrIP: "",
}, Wildcard: true,
Permit: true,
Protocol: 6,
StartPort: 22000,
EndPort: 22000,
Created: time.Now().Unix(),
}, },
}, },
} }
@ -54,37 +61,41 @@ func init() {
ID: "unit-test-stamp", ID: "unit-test-stamp",
Name: "Unit Test Stamp Profile", Name: "Unit Test Stamp Profile",
SecurityLevel: status.SecurityLevelFortress, SecurityLevel: status.SecurityLevelFortress,
Flags: map[uint8]uint8{ // Flags: map[uint8]uint8{
Internet: status.SecurityLevelsAll, // Internet: status.SecurityLevelsAll,
}, // },
Domains: map[string]*DomainDecision{ Endpoints: []*EndpointPermission{
"bad2.example.com": &DomainDecision{ &EndpointPermission{
Permit: false, DomainOrIP: "bad2.example.com.",
Created: time.Now().Unix(), Wildcard: true,
IncludeSubdomains: true, Permit: false,
Created: time.Now().Unix(),
}, },
"good.bad.example.com": &DomainDecision{ &EndpointPermission{
Permit: true, DomainOrIP: "",
Created: time.Now().Unix(), Wildcard: true,
IncludeSubdomains: false, Permit: true,
Protocol: 6,
StartPort: 80,
EndPort: 80,
Created: time.Now().Unix(),
}, },
}, },
Ports: map[int16][]*Port{ ServiceEndpoints: []*EndpointPermission{
6: []*Port{ &EndpointPermission{
&Port{ DomainOrIP: "",
Permit: false, Wildcard: true,
Created: time.Now().Unix(), Permit: true,
Start: 80, Protocol: 17,
End: 80, StartPort: 12345,
}, EndPort: 12347,
Created: time.Now().Unix(),
}, },
-17: []*Port{ &EndpointPermission{ // default deny
&Port{ DomainOrIP: "",
Permit: true, Wildcard: true,
Created: time.Now().Unix(), Permit: false,
Start: 12345, Created: time.Now().Unix(),
End: 12347,
},
}, },
}, },
} }
@ -97,37 +108,21 @@ func testFlag(t *testing.T, set *Set, flag uint8, shouldBeActive bool) {
} }
} }
func testDomain(t *testing.T, set *Set, domain string, shouldBePermitted bool) { func testEndpoint(t *testing.T, set *Set, domainOrIP string, protocol uint8, port uint16, inbound bool, shouldBePermitted bool) {
permitted, ok := set.CheckDomain(domain) var permitted, ok bool
permitted, _, ok = set.CheckEndpoint(domainOrIP, protocol, port, inbound)
if !ok { if !ok {
t.Errorf("domain %s should be in test profile set", domain) t.Errorf("endpoint %s/%d/%d/%v should be in test profile set", domainOrIP, protocol, port, inbound)
} }
if permitted != shouldBePermitted { if permitted != shouldBePermitted {
t.Errorf("unexpected result: domain %s: permitted=%v, expected=%v", domain, permitted, shouldBePermitted) t.Errorf("unexpected result for endpoint %s/%d/%d/%v: permitted=%v, expected=%v", domainOrIP, protocol, port, inbound, permitted, shouldBePermitted)
} }
} }
func testUnregulatedDomain(t *testing.T, set *Set, domain string) { func testUnregulatedEndpoint(t *testing.T, set *Set, domainOrIP string, protocol uint8, port uint16, inbound bool) {
_, ok := set.CheckDomain(domain) _, _, ok := set.CheckEndpoint(domainOrIP, protocol, port, inbound)
if ok { if ok {
t.Errorf("domain %s should not be in test profile set", domain) t.Errorf("endpoint %s/%d/%d/%v should not be in test profile set", domainOrIP, protocol, port, inbound)
}
}
func testPort(t *testing.T, set *Set, listen bool, protocol uint8, port uint16, shouldBePermitted bool) {
permitted, ok := set.CheckPort(listen, protocol, port)
if !ok {
t.Errorf("port [%v %d %d] should be in test profile set", listen, protocol, port)
}
if permitted != shouldBePermitted {
t.Errorf("unexpected result: port [%v %d %d]: permitted=%v, expected=%v", listen, protocol, port, permitted, shouldBePermitted)
}
}
func testUnregulatedPort(t *testing.T, set *Set, listen bool, protocol uint8, port uint16) {
_, ok := set.CheckPort(listen, protocol, port)
if ok {
t.Errorf("port [%v %d %d] should not be in test profile set", listen, protocol, port)
} }
} }
@ -137,31 +132,29 @@ func TestProfileSet(t *testing.T) {
set.Update(status.SecurityLevelDynamic) set.Update(status.SecurityLevelDynamic)
testFlag(t, set, Whitelist, false) testFlag(t, set, Whitelist, false)
testFlag(t, set, Internet, true) // testFlag(t, set, Internet, true)
testDomain(t, set, "example.com", true) testEndpoint(t, set, "example.com.", 0, 0, false, true)
testDomain(t, set, "bad.example.com", false) testEndpoint(t, set, "bad.example.com.", 0, 0, false, false)
testDomain(t, set, "other.bad.example.com", false) testEndpoint(t, set, "other.bad.example.com.", 0, 0, false, false)
testDomain(t, set, "good.bad.example.com", false) testEndpoint(t, set, "good.bad.example.com.", 0, 0, false, true)
testDomain(t, set, "bad2.example.com", false) testEndpoint(t, set, "bad2.example.com.", 0, 0, false, false)
testPort(t, set, false, 6, 443, true) testEndpoint(t, set, "10.2.3.4", 6, 22000, false, true)
testPort(t, set, false, 6, 143, true) testEndpoint(t, set, "fd00::1", 6, 22000, false, true)
testPort(t, set, false, 6, 22, true) testEndpoint(t, set, "test.local.", 6, 22000, false, true)
testPort(t, set, false, 6, 80, false) testUnregulatedEndpoint(t, set, "other.example.com.", 0, 0, false)
testPort(t, set, false, 6, 80, false) testUnregulatedEndpoint(t, set, "10.2.3.4", 17, 53, false)
testPort(t, set, true, 17, 12345, true) testUnregulatedEndpoint(t, set, "10.2.3.4", 17, 443, false)
testPort(t, set, true, 17, 12346, true) testUnregulatedEndpoint(t, set, "10.2.3.4", 6, 12346, false)
testPort(t, set, true, 17, 12347, true) testEndpoint(t, set, "10.2.3.4", 17, 12345, true, true)
testUnregulatedDomain(t, set, "other.example.com") testEndpoint(t, set, "fd00::1", 17, 12347, true, true)
testUnregulatedPort(t, set, false, 17, 53)
testUnregulatedPort(t, set, false, 17, 443)
testUnregulatedPort(t, set, true, 6, 443)
set.Update(status.SecurityLevelSecure) set.Update(status.SecurityLevelSecure)
testFlag(t, set, Internet, true) // testFlag(t, set, Internet, true)
set.Update(status.SecurityLevelFortress) // Independent! set.Update(status.SecurityLevelFortress) // Independent!
testFlag(t, set, Internet, false) testFlag(t, set, Whitelist, true)
testPort(t, set, false, 6, 80, true) testEndpoint(t, set, "10.2.3.4", 17, 12345, true, false)
testUnregulatedDomain(t, set, "bad2.example.com") testEndpoint(t, set, "fd00::1", 17, 12347, true, false)
testUnregulatedPort(t, set, true, 17, 12346) testUnregulatedEndpoint(t, set, "10.2.3.4", 6, 80, false)
testUnregulatedEndpoint(t, set, "bad2.example.com.", 0, 0, false)
} }

View file

@ -33,8 +33,10 @@ func initSpecialProfiles() (err error) {
return err return err
} }
fallbackProfile = makeDefaultFallbackProfile() fallbackProfile = makeDefaultFallbackProfile()
ensureServiceEndpointsDenyAll(fallbackProfile)
fallbackProfile.Save(SpecialNamespace) fallbackProfile.Save(SpecialNamespace)
} }
ensureServiceEndpointsDenyAll(fallbackProfile)
return nil return nil
} }
@ -42,3 +44,26 @@ func initSpecialProfiles() (err error) {
func getSpecialProfile(ID string) (*Profile, error) { func getSpecialProfile(ID string) (*Profile, error) {
return getProfile(SpecialNamespace, ID) return getProfile(SpecialNamespace, ID)
} }
func ensureServiceEndpointsDenyAll(p *Profile) (changed bool) {
for _, ep := range p.ServiceEndpoints {
if ep.DomainOrIP == "" &&
ep.Wildcard == true &&
ep.Protocol == 0 &&
ep.StartPort == 0 &&
ep.EndPort == 0 &&
ep.Permit == false {
return false
}
}
p.ServiceEndpoints = append(p.ServiceEndpoints, &EndpointPermission{
DomainOrIP: "",
Wildcard: true,
Protocol: 0,
StartPort: 0,
EndPort: 0,
Permit: false,
})
return true
}

View file

@ -35,12 +35,20 @@ func updateListener(sub *database.Subscription) {
continue continue
} }
switch profile.ID { switch profile.DatabaseKey() {
case "global": case "profiles/special/global":
specialProfileLock.Lock() specialProfileLock.Lock()
globalProfile = profile globalProfile = profile
specialProfileLock.Unlock() specialProfileLock.Unlock()
case "fallback": case "profiles/special/fallback":
profile.Lock()
if ensureServiceEndpointsDenyAll(profile) {
profile.Unlock()
profile.Save(SpecialNamespace)
continue
}
profile.Unlock()
specialProfileLock.Lock() specialProfileLock.Lock()
fallbackProfile = profile fallbackProfile = profile
specialProfileLock.Unlock() specialProfileLock.Unlock()