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
import (
"time"
"github.com/Safing/portmaster/status"
)
@ -32,50 +30,14 @@ func makeDefaultFallbackProfile() *Profile {
Related: status.SecurityLevelDynamic,
PeerToPeer: status.SecurityLevelDynamic,
},
Ports: map[int16][]*Port{
6: []*Port{
&Port{ // SSH
Permit: true,
Created: time.Now().Unix(),
Start: 22,
End: 22,
},
&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,
},
ServiceEndpoints: []*EndpointPermission{
&EndpointPermission{
DomainOrIP: "",
Wildcard: true,
Protocol: 0,
StartPort: 0,
EndPort: 0,
Permit: false,
},
},
}

View file

@ -3,6 +3,9 @@ package profile
import (
"fmt"
"strconv"
"strings"
"github.com/Safing/portmaster/intel"
)
// Endpoints is a list of permitted or denied endpoints.
@ -10,13 +13,13 @@ type Endpoints []*EndpointPermission
// EndpointPermission holds a decision about an endpoint.
type EndpointPermission struct {
DomainOrIP string
IncludeSubdomains bool
Protocol uint8
PortStart uint16
PortEnd uint16
Permit bool
Created int64
DomainOrIP string
Wildcard bool
Protocol uint8
StartPort uint16
EndPort uint16
Permit bool
Created int64
}
// 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.
func (e Endpoints) Check(domainOrIP string, protocol uint8, port uint16) (permit, ok bool) {
// check for exact domain
ed, ok := d[domain]
if ok {
return ed.Permit, true
}
// If getDomainOfIP (returns reverse and forward dns matching domain name) is supplied, an IP will be resolved to a domain, if necessary.
func (e Endpoints) Check(domainOrIP string, protocol uint8, port uint16, checkReverseIP bool, securityLevel uint8) (permit bool, reason string, ok bool) {
for _, entry := range e {
if entry.Matches(domainOrIP, protocol, port) {
return entry.Permit, true
// ip resolving
var cachedGetDomainOfIP func() string
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 (ep EndpointPermission) Matches(domainOrIP string, protocol uint8, port uint16) bool {
if domainOrIP != ep.DomainOrIP {
return false
}
func isSubdomainOf(domain, subdomain string) bool {
dotPrefixedDomain := "." + domain
return strings.HasSuffix(subdomain, dotPrefixedDomain)
}
// 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 {
return false
return false, ""
}
if ep.PortStart > 0 && (port < ep.PortStart || port > ep.PortEnd) {
return false
if ep.StartPort > 0 && (port < ep.StartPort || port > ep.EndPort) {
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 {
s := ep.DomainOrIP
if ep.Protocol > 0 || ep.Start {
s += " "
}
s += " "
if ep.Protocol > 0 {
s += strconv.Itoa(int(ep.Protocol))
if ep.Start > 0 {
s += "/"
}
} else {
s += "*"
}
if ep.Start > 0 {
if p.Start == p.End {
s += strconv.Itoa(int(ep.Start))
s += "/"
if ep.StartPort > 0 {
if ep.StartPort == ep.EndPort {
s += strconv.Itoa(int(ep.StartPort))
} else {
s += fmt.Sprintf("%d-%d", ep.Start, ep.End)
s += fmt.Sprintf("%d-%d", ep.StartPort, ep.EndPort)
}
} else {
s += "*"
}
return s

View file

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

View file

@ -2,6 +2,7 @@ package profile
import (
"errors"
"fmt"
"strings"
"github.com/Safing/portmaster/status"
@ -115,7 +116,7 @@ func (flags Flags) String() string {
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.

View file

@ -31,7 +31,7 @@ func TestProfileFlags(t *testing.T) {
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())
}

View file

@ -37,10 +37,10 @@ type Profile struct {
Fingerprints []*Fingerprint
// The mininum security level to apply to connections made with this profile
SecurityLevel uint8
Flags Flags
Domains Domains
Ports Ports
SecurityLevel uint8
Flags Flags
Endpoints Endpoints
ServiceEndpoints Endpoints
// 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"`
@ -97,7 +97,7 @@ func (profile *Profile) String() string {
// DetailedString returns a more detailed string representation of theProfile.
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.

View file

@ -8,7 +8,6 @@ import (
var (
emptyFlags = Flags{}
emptyPorts = Ports{}
)
// Set handles Profile chaining.
@ -120,8 +119,8 @@ func (set *Set) CheckFlag(flag uint8) (active bool) {
return false
}
// CheckDomain checks if the given domain is governed in any the lists of domains and returns whether it is permitted.
func (set *Set) CheckDomain(domain string) (permit, ok bool) {
// 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) CheckEndpoint(domainOrIP string, protocol uint8, port uint16, inbound bool) (permit bool, reason string, ok bool) {
set.Lock()
defer set.Unlock()
@ -131,39 +130,19 @@ func (set *Set) CheckDomain(domain string) (permit, ok bool) {
}
if profile != nil {
permit, ok = profile.Domains.Check(domain)
if ok {
return
if inbound {
if permit, reason, ok = profile.ServiceEndpoints.Check(domainOrIP, protocol, port, inbound, set.combinedSecurityLevel); ok {
return
}
} else {
if permit, reason, ok = profile.Endpoints.Check(domainOrIP, protocol, port, inbound, set.combinedSecurityLevel); ok {
return
}
}
}
}
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
return false, "", false
}
// getSecurityLevel returns the highest prioritized security level.

View file

@ -26,26 +26,33 @@ func init() {
Flags: map[uint8]uint8{
Independent: status.SecurityLevelFortress,
},
Domains: map[string]*DomainDecision{
"example.com": &DomainDecision{
Permit: true,
Created: time.Now().Unix(),
IncludeSubdomains: false,
Endpoints: []*EndpointPermission{
&EndpointPermission{
DomainOrIP: "good.bad.example.com.",
Wildcard: false,
Permit: true,
Created: time.Now().Unix(),
},
"bad.example.com": &DomainDecision{
Permit: false,
Created: time.Now().Unix(),
IncludeSubdomains: true,
&EndpointPermission{
DomainOrIP: "bad.example.com.",
Wildcard: true,
Permit: false,
Created: time.Now().Unix(),
},
},
Ports: map[int16][]*Port{
6: []*Port{
&Port{
Permit: true,
Created: time.Now().Unix(),
Start: 22000,
End: 22000,
},
&EndpointPermission{
DomainOrIP: "example.com.",
Wildcard: false,
Permit: true,
Created: time.Now().Unix(),
},
&EndpointPermission{
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",
Name: "Unit Test Stamp Profile",
SecurityLevel: status.SecurityLevelFortress,
Flags: map[uint8]uint8{
Internet: status.SecurityLevelsAll,
},
Domains: map[string]*DomainDecision{
"bad2.example.com": &DomainDecision{
Permit: false,
Created: time.Now().Unix(),
IncludeSubdomains: true,
// Flags: map[uint8]uint8{
// Internet: status.SecurityLevelsAll,
// },
Endpoints: []*EndpointPermission{
&EndpointPermission{
DomainOrIP: "bad2.example.com.",
Wildcard: true,
Permit: false,
Created: time.Now().Unix(),
},
"good.bad.example.com": &DomainDecision{
Permit: true,
Created: time.Now().Unix(),
IncludeSubdomains: false,
&EndpointPermission{
DomainOrIP: "",
Wildcard: true,
Permit: true,
Protocol: 6,
StartPort: 80,
EndPort: 80,
Created: time.Now().Unix(),
},
},
Ports: map[int16][]*Port{
6: []*Port{
&Port{
Permit: false,
Created: time.Now().Unix(),
Start: 80,
End: 80,
},
ServiceEndpoints: []*EndpointPermission{
&EndpointPermission{
DomainOrIP: "",
Wildcard: true,
Permit: true,
Protocol: 17,
StartPort: 12345,
EndPort: 12347,
Created: time.Now().Unix(),
},
-17: []*Port{
&Port{
Permit: true,
Created: time.Now().Unix(),
Start: 12345,
End: 12347,
},
&EndpointPermission{ // default deny
DomainOrIP: "",
Wildcard: true,
Permit: false,
Created: time.Now().Unix(),
},
},
}
@ -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) {
permitted, ok := set.CheckDomain(domain)
func testEndpoint(t *testing.T, set *Set, domainOrIP string, protocol uint8, port uint16, inbound bool, shouldBePermitted bool) {
var permitted, ok bool
permitted, _, ok = set.CheckEndpoint(domainOrIP, protocol, port, inbound)
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 {
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) {
_, ok := set.CheckDomain(domain)
func testUnregulatedEndpoint(t *testing.T, set *Set, domainOrIP string, protocol uint8, port uint16, inbound bool) {
_, _, ok := set.CheckEndpoint(domainOrIP, protocol, port, inbound)
if ok {
t.Errorf("domain %s should not be in test profile set", domain)
}
}
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)
t.Errorf("endpoint %s/%d/%d/%v should not be in test profile set", domainOrIP, protocol, port, inbound)
}
}
@ -137,31 +132,29 @@ func TestProfileSet(t *testing.T) {
set.Update(status.SecurityLevelDynamic)
testFlag(t, set, Whitelist, false)
testFlag(t, set, Internet, true)
testDomain(t, set, "example.com", true)
testDomain(t, set, "bad.example.com", false)
testDomain(t, set, "other.bad.example.com", false)
testDomain(t, set, "good.bad.example.com", false)
testDomain(t, set, "bad2.example.com", false)
testPort(t, set, false, 6, 443, true)
testPort(t, set, false, 6, 143, true)
testPort(t, set, false, 6, 22, true)
testPort(t, set, false, 6, 80, false)
testPort(t, set, false, 6, 80, false)
testPort(t, set, true, 17, 12345, true)
testPort(t, set, true, 17, 12346, true)
testPort(t, set, true, 17, 12347, true)
testUnregulatedDomain(t, set, "other.example.com")
testUnregulatedPort(t, set, false, 17, 53)
testUnregulatedPort(t, set, false, 17, 443)
testUnregulatedPort(t, set, true, 6, 443)
// testFlag(t, set, Internet, true)
testEndpoint(t, set, "example.com.", 0, 0, false, true)
testEndpoint(t, set, "bad.example.com.", 0, 0, false, false)
testEndpoint(t, set, "other.bad.example.com.", 0, 0, false, false)
testEndpoint(t, set, "good.bad.example.com.", 0, 0, false, true)
testEndpoint(t, set, "bad2.example.com.", 0, 0, false, false)
testEndpoint(t, set, "10.2.3.4", 6, 22000, false, true)
testEndpoint(t, set, "fd00::1", 6, 22000, false, true)
testEndpoint(t, set, "test.local.", 6, 22000, false, true)
testUnregulatedEndpoint(t, set, "other.example.com.", 0, 0, false)
testUnregulatedEndpoint(t, set, "10.2.3.4", 17, 53, false)
testUnregulatedEndpoint(t, set, "10.2.3.4", 17, 443, false)
testUnregulatedEndpoint(t, set, "10.2.3.4", 6, 12346, false)
testEndpoint(t, set, "10.2.3.4", 17, 12345, true, true)
testEndpoint(t, set, "fd00::1", 17, 12347, true, true)
set.Update(status.SecurityLevelSecure)
testFlag(t, set, Internet, true)
// testFlag(t, set, Internet, true)
set.Update(status.SecurityLevelFortress) // Independent!
testFlag(t, set, Internet, false)
testPort(t, set, false, 6, 80, true)
testUnregulatedDomain(t, set, "bad2.example.com")
testUnregulatedPort(t, set, true, 17, 12346)
testFlag(t, set, Whitelist, true)
testEndpoint(t, set, "10.2.3.4", 17, 12345, true, false)
testEndpoint(t, set, "fd00::1", 17, 12347, true, false)
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
}
fallbackProfile = makeDefaultFallbackProfile()
ensureServiceEndpointsDenyAll(fallbackProfile)
fallbackProfile.Save(SpecialNamespace)
}
ensureServiceEndpointsDenyAll(fallbackProfile)
return nil
}
@ -42,3 +44,26 @@ func initSpecialProfiles() (err error) {
func getSpecialProfile(ID string) (*Profile, error) {
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
}
switch profile.ID {
case "global":
switch profile.DatabaseKey() {
case "profiles/special/global":
specialProfileLock.Lock()
globalProfile = profile
specialProfileLock.Unlock()
case "fallback":
case "profiles/special/fallback":
profile.Lock()
if ensureServiceEndpointsDenyAll(profile) {
profile.Unlock()
profile.Save(SpecialNamespace)
continue
}
profile.Unlock()
specialProfileLock.Lock()
fallbackProfile = profile
specialProfileLock.Unlock()