forwardproxy/acl.go
sergeyfrolov 7791846e12
Add Access Control Lists (#31)
We settled on a powerful, but a bit complicated acl design.
This commit makes a couple of cosmetic improvements. It also removes
http.Transport, which was previously used to dial and write http
requests for insecure GET requests. Now we have to dial manually,
so we can check the access control list.
2018-06-26 10:53:24 -04:00

96 lines
2 KiB
Go

package forwardproxy
import (
"errors"
"net"
"strings"
)
type aclDecision uint8
const (
aclDecisionAllow = iota
aclDecisionDeny
aclDecisionNoMatch
)
type aclRule interface {
tryMatch(ip net.IP, domain string) aclDecision
}
type aclIPRule struct {
net net.IPNet
allow bool
}
func (a *aclIPRule) tryMatch(ip net.IP, domain string) aclDecision {
if !a.net.Contains(ip) {
return aclDecisionNoMatch
}
if a.allow {
return aclDecisionAllow
}
return aclDecisionDeny
}
type aclDomainRule struct {
domain string
subdomainsAllowed bool
allow bool
}
func (a *aclDomainRule) tryMatch(ip net.IP, domain string) aclDecision {
if strings.HasSuffix(domain, ".") {
domain = domain[:len(domain)-1]
}
if domain == a.domain ||
a.subdomainsAllowed && strings.HasSuffix(domain, "."+a.domain) {
if a.allow {
return aclDecisionAllow
}
return aclDecisionDeny
}
return aclDecisionNoMatch
}
type aclAllRule struct {
allow bool
}
func (a *aclAllRule) tryMatch(ip net.IP, domain string) aclDecision {
if a.allow {
return aclDecisionAllow
}
return aclDecisionDeny
}
func newAclRule(ruleSubject string, allow bool) (aclRule, error) {
if ruleSubject == "all" {
return &aclAllRule{allow: allow}, nil
}
_, ipNet, err := net.ParseCIDR(ruleSubject)
if err != nil {
ip := net.ParseIP(ruleSubject)
// support specifying just an IP
if ip.To4() != nil {
_, ipNet, err = net.ParseCIDR(ruleSubject + "/32")
} else if ip.To16() != nil {
_, ipNet, err = net.ParseCIDR(ruleSubject + "/128")
}
}
if err == nil {
return &aclIPRule{net: *ipNet, allow: allow}, nil
}
subdomainsAllowed := false
if strings.HasPrefix(ruleSubject, `*.`) {
subdomainsAllowed = true
ruleSubject = ruleSubject[2:]
}
err = isValidDomainLite(ruleSubject)
if err != nil {
return nil, errors.New(ruleSubject + " could not be parsed as either IP, IP network, or domain: " + err.Error())
}
return &aclDomainRule{domain: ruleSubject, subdomainsAllowed: subdomainsAllowed, allow: allow}, nil
}