Make normalize return multiple "hosts" (= reverse zones) when a non-octet boundary cidr is given. Added pkg/cidr package that holds the cidr calculation routines; felt they didn't really fit dnsutil. This change means the IPNet return parameter isn't needed, the hosts are all correct. The tests that tests this is also removed: TestSplitHostPortReverse The fallout was that zoneAddr _also_ doesn't need the IPNet member, that in turn make it visible that zoneAddr in address.go duplicated a bunch of stuff from register.go; removed/refactored that too. Created a plugin.OriginsFromArgsOrServerBlock to help plugins do the right things, by consuming ZONE arguments; this now expands reverse zones correctly. This is mostly mechanical. Remove the reverse test in plugin/kubernetes which is a copy-paste from a core test (which has since been fixed). Remove MustNormalize as it has no plugin users. This change is not backwards compatible to plugins that have a ZONE argument that they parse in the setup util. All in-tree plugins have been updated. Signed-off-by: Miek Gieben <miek@miek.nl>
152 lines
3.6 KiB
Go
152 lines
3.6 KiB
Go
package acl
|
|
|
|
import (
|
|
"net"
|
|
"strings"
|
|
|
|
"github.com/coredns/caddy"
|
|
"github.com/coredns/coredns/core/dnsserver"
|
|
"github.com/coredns/coredns/plugin"
|
|
|
|
"github.com/infobloxopen/go-trees/iptree"
|
|
"github.com/miekg/dns"
|
|
)
|
|
|
|
const pluginName = "acl"
|
|
|
|
func init() { plugin.Register(pluginName, setup) }
|
|
|
|
func newDefaultFilter() *iptree.Tree {
|
|
defaultFilter := iptree.NewTree()
|
|
_, IPv4All, _ := net.ParseCIDR("0.0.0.0/0")
|
|
_, IPv6All, _ := net.ParseCIDR("::/0")
|
|
defaultFilter.InplaceInsertNet(IPv4All, struct{}{})
|
|
defaultFilter.InplaceInsertNet(IPv6All, struct{}{})
|
|
return defaultFilter
|
|
}
|
|
|
|
func setup(c *caddy.Controller) error {
|
|
a, err := parse(c)
|
|
if err != nil {
|
|
return plugin.Error(pluginName, err)
|
|
}
|
|
|
|
dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler {
|
|
a.Next = next
|
|
return a
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
func parse(c *caddy.Controller) (ACL, error) {
|
|
a := ACL{}
|
|
for c.Next() {
|
|
r := rule{}
|
|
args := c.RemainingArgs()
|
|
r.zones = plugin.OriginsFromArgsOrServerBlock(args, c.ServerBlockKeys)
|
|
|
|
for c.NextBlock() {
|
|
p := policy{}
|
|
|
|
action := strings.ToLower(c.Val())
|
|
if action == "allow" {
|
|
p.action = actionAllow
|
|
} else if action == "block" {
|
|
p.action = actionBlock
|
|
} else if action == "filter" {
|
|
p.action = actionFilter
|
|
} else {
|
|
return a, c.Errf("unexpected token %q; expect 'allow', 'block', or 'filter'", c.Val())
|
|
}
|
|
|
|
p.qtypes = make(map[uint16]struct{})
|
|
p.filter = iptree.NewTree()
|
|
|
|
hasTypeSection := false
|
|
hasNetSection := false
|
|
|
|
remainingTokens := c.RemainingArgs()
|
|
for len(remainingTokens) > 0 {
|
|
if !isPreservedIdentifier(remainingTokens[0]) {
|
|
return a, c.Errf("unexpected token %q; expect 'type | net'", remainingTokens[0])
|
|
}
|
|
section := strings.ToLower(remainingTokens[0])
|
|
|
|
i := 1
|
|
var tokens []string
|
|
for ; i < len(remainingTokens) && !isPreservedIdentifier(remainingTokens[i]); i++ {
|
|
tokens = append(tokens, remainingTokens[i])
|
|
}
|
|
remainingTokens = remainingTokens[i:]
|
|
|
|
if len(tokens) == 0 {
|
|
return a, c.Errf("no token specified in %q section", section)
|
|
}
|
|
|
|
switch section {
|
|
case "type":
|
|
hasTypeSection = true
|
|
for _, token := range tokens {
|
|
if token == "*" {
|
|
p.qtypes[dns.TypeNone] = struct{}{}
|
|
break
|
|
}
|
|
qtype, ok := dns.StringToType[token]
|
|
if !ok {
|
|
return a, c.Errf("unexpected token %q; expect legal QTYPE", token)
|
|
}
|
|
p.qtypes[qtype] = struct{}{}
|
|
}
|
|
case "net":
|
|
hasNetSection = true
|
|
for _, token := range tokens {
|
|
if token == "*" {
|
|
p.filter = newDefaultFilter()
|
|
break
|
|
}
|
|
token = normalize(token)
|
|
_, source, err := net.ParseCIDR(token)
|
|
if err != nil {
|
|
return a, c.Errf("illegal CIDR notation %q", token)
|
|
}
|
|
p.filter.InplaceInsertNet(source, struct{}{})
|
|
}
|
|
default:
|
|
return a, c.Errf("unexpected token %q; expect 'type | net'", section)
|
|
}
|
|
}
|
|
|
|
// optional `type` section means all record types.
|
|
if !hasTypeSection {
|
|
p.qtypes[dns.TypeNone] = struct{}{}
|
|
}
|
|
|
|
// optional `net` means all ip addresses.
|
|
if !hasNetSection {
|
|
p.filter = newDefaultFilter()
|
|
}
|
|
|
|
r.policies = append(r.policies, p)
|
|
}
|
|
a.Rules = append(a.Rules, r)
|
|
}
|
|
return a, nil
|
|
}
|
|
|
|
func isPreservedIdentifier(token string) bool {
|
|
identifier := strings.ToLower(token)
|
|
return identifier == "type" || identifier == "net"
|
|
}
|
|
|
|
// normalize appends '/32' for any single IPv4 address and '/128' for IPv6.
|
|
func normalize(rawNet string) string {
|
|
if idx := strings.IndexAny(rawNet, "/"); idx >= 0 {
|
|
return rawNet
|
|
}
|
|
|
|
if idx := strings.IndexAny(rawNet, ":"); idx >= 0 {
|
|
return rawNet + "/128"
|
|
}
|
|
return rawNet + "/32"
|
|
}
|