ACL engine & tests

This commit is contained in:
Toby 2020-04-25 22:56:49 -07:00
parent 6cd960ea41
commit ee8558f2fb
3 changed files with 174 additions and 3 deletions

View file

@ -1,5 +1,51 @@
package acl package acl
import (
"bufio"
"net"
"os"
"strings"
)
type Engine struct { type Engine struct {
Entries []Entry DefaultAction Action
Entries []Entry
}
func LoadFromFile(filename string) (*Engine, error) {
f, err := os.Open(filename)
if err != nil {
return nil, err
}
defer f.Close()
scanner := bufio.NewScanner(f)
entries := make([]Entry, 0, 1024)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if len(line) == 0 || strings.HasPrefix(line, "#") {
// Ignore empty lines & comments
continue
}
entry, err := ParseEntry(line)
if err != nil {
return nil, err
}
entries = append(entries, entry)
}
return &Engine{
DefaultAction: ActionProxy,
Entries: entries,
}, nil
}
func (e *Engine) Lookup(domain string, ip net.IP) (Action, string) {
if len(domain) == 0 && ip == nil {
return e.DefaultAction, ""
}
for _, entry := range e.Entries {
if entry.Match(domain, ip) {
return entry.Action, entry.ActionArg
}
}
return e.DefaultAction, ""
} }

107
pkg/acl/engine_test.go Normal file
View file

@ -0,0 +1,107 @@
package acl
import (
"net"
"testing"
)
func TestEngine_Lookup(t *testing.T) {
e := &Engine{
DefaultAction: ActionDirect,
Entries: []Entry{
{
Net: nil,
Domain: "google.com",
Suffix: false,
All: false,
Action: ActionProxy,
ActionArg: "",
},
{
Net: nil,
Domain: "evil.corp",
Suffix: true,
All: false,
Action: ActionHijack,
ActionArg: "good.org",
},
{
Net: &net.IPNet{
IP: net.ParseIP("10.0.0.0"),
Mask: net.CIDRMask(8, 32),
},
Domain: "",
Suffix: false,
All: false,
Action: ActionProxy,
ActionArg: "",
},
{
Net: nil,
Domain: "",
Suffix: false,
All: true,
Action: ActionBlock,
ActionArg: "",
},
},
}
type args struct {
domain string
ip net.IP
}
tests := []struct {
name string
args args
want Action
want1 string
}{
{
name: "domain direct",
args: args{"google.com", nil},
want: ActionProxy,
want1: "",
},
{
name: "domain suffix 1",
args: args{"evil.corp", nil},
want: ActionHijack,
want1: "good.org",
},
{
name: "domain suffix 2",
args: args{"notevil.corp", nil},
want: ActionBlock,
want1: "",
},
{
name: "domain suffix 3",
args: args{"im.real.evil.corp", nil},
want: ActionHijack,
want1: "good.org",
},
{
name: "ip match",
args: args{"", net.ParseIP("10.2.3.4")},
want: ActionProxy,
want1: "",
},
{
name: "ip mismatch",
args: args{"", net.ParseIP("100.5.6.0")},
want: ActionBlock,
want1: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, got1 := e.Lookup(tt.args.domain, tt.args.ip)
if got != tt.want {
t.Errorf("Lookup() got = %v, want %v", got, tt.want)
}
if got1 != tt.want1 {
t.Errorf("Lookup() got1 = %v, want %v", got1, tt.want1)
}
})
}
}

View file

@ -25,6 +25,24 @@ type Entry struct {
ActionArg string ActionArg string
} }
func (e Entry) Match(domain string, ip net.IP) bool {
if e.All {
return true
}
if e.Net != nil && ip != nil {
return e.Net.Contains(ip)
}
if len(e.Domain) > 0 && len(domain) > 0 {
ld := strings.ToLower(domain)
if e.Suffix {
return e.Domain == ld || strings.HasSuffix(ld, "."+e.Domain)
} else {
return e.Domain == ld
}
}
return false
}
// Format: action cond_type cond arg // Format: action cond_type cond arg
// Examples: // Examples:
// proxy domain-suffix google.com // proxy domain-suffix google.com
@ -75,12 +93,12 @@ func parseCond(typ, cond string) (*net.IPNet, string, bool, bool, error) {
if len(cond) == 0 { if len(cond) == 0 {
return nil, "", false, false, errors.New("empty domain") return nil, "", false, false, errors.New("empty domain")
} }
return nil, cond, false, false, nil return nil, strings.ToLower(cond), false, false, nil
case "domain-suffix": case "domain-suffix":
if len(cond) == 0 { if len(cond) == 0 {
return nil, "", false, false, errors.New("empty domain suffix") return nil, "", false, false, errors.New("empty domain suffix")
} }
return nil, cond, true, false, nil return nil, strings.ToLower(cond), true, false, nil
case "cidr": case "cidr":
_, ipNet, err := net.ParseCIDR(cond) _, ipNet, err := net.ParseCIDR(cond)
if err != nil { if err != nil {