coredns/plugin/acl/acl.go
An Xiao 79f37a1460 Add plugin ACL for source ip filtering (#3103)
* Add plugin ACL for source ip filtering

Signed-off-by: An Xiao <hac@zju.edu.cn>

* Allow all arguments to be optional and support multiple qtypes in a single policy

Signed-off-by: An Xiao <hac@zju.edu.cn>

* Add newline before third party imports

Signed-off-by: An Xiao <hac@zju.edu.cn>

* Use camel instead of underscore in method name

Signed-off-by: An Xiao <hac@zju.edu.cn>

* Start with an upper case letter in t.Errorf()

Signed-off-by: An Xiao <hac@zju.edu.cn>

* Use the qtype parse logic in miekg/dns

Signed-off-by: An Xiao <hac@zju.edu.cn>

* Use third party trie implementation as the ip filter

Signed-off-by: An Xiao <hac@zju.edu.cn>

* Update based on rdrozhdzh's comment

Signed-off-by: An Xiao <hac@zju.edu.cn>

* Change the type of action to int

Signed-off-by: An Xiao <hac@zju.edu.cn>

* Add IPv6 support

Signed-off-by: An Xiao <hac@zju.edu.cn>

* Update plugin.cfg

Signed-off-by: An Xiao <hac@zju.edu.cn>

* Remove file functionality

Signed-off-by: An Xiao <hac@zju.edu.cn>

* Update

Signed-off-by: Xiao An <hac@zju.edu.cn>

* Update README

Signed-off-by: Xiao An <hac@zju.edu.cn>

* remove comments

Signed-off-by: Xiao An <hac@zju.edu.cn>

* update

Signed-off-by: Xiao An <hac@zju.edu.cn>

* Update dependency

Signed-off-by: Xiao An <hac@zju.edu.cn>

* Update

Signed-off-by: Xiao An <hac@zju.edu.cn>

* Update test

Signed-off-by: Xiao An <hac@zju.edu.cn>

* Add OWNERS

Signed-off-by: Xiao An <hac@zju.edu.cn>

* Refactor shouldBlock and skip useless check

Signed-off-by: Xiao An <hac@zju.edu.cn>

* Introduce ActionNone

Signed-off-by: Xiao An <hac@zju.edu.cn>

* Update label name

Signed-off-by: Xiao An <hac@zju.edu.cn>

* Avoid capitalizing private types

Signed-off-by: Xiao An <hac@zju.edu.cn>
2019-09-04 08:43:45 -07:00

115 lines
2.6 KiB
Go

package acl
import (
"context"
"net"
"github.com/coredns/coredns/plugin"
"github.com/coredns/coredns/plugin/metrics"
clog "github.com/coredns/coredns/plugin/pkg/log"
"github.com/coredns/coredns/request"
"github.com/infobloxopen/go-trees/iptree"
"github.com/miekg/dns"
)
var log = clog.NewWithPlugin("acl")
// ACL enforces access control policies on DNS queries.
type ACL struct {
Next plugin.Handler
Rules []rule
}
// rule defines a list of Zones and some ACL policies which will be
// enforced on them.
type rule struct {
zones []string
policies []policy
}
// action defines the action against queries.
type action int
// policy defines the ACL policy for DNS queries.
// A policy performs the specified action (block/allow) on all DNS queries
// matched by source IP or QTYPE.
type policy struct {
action action
qtypes map[uint16]struct{}
filter *iptree.Tree
}
const (
// actionNone does nothing on the queries.
actionNone = iota
// actionAllow allows authorized queries to recurse.
actionAllow
// actionBlock blocks unauthorized queries towards protected DNS zones.
actionBlock
)
// ServeDNS implements the plugin.Handler interface.
func (a ACL) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
state := request.Request{W: w, Req: r}
RulesCheckLoop:
for _, rule := range a.Rules {
// check zone.
zone := plugin.Zones(rule.zones).Matches(state.Name())
if zone == "" {
continue
}
action := matchWithPolicies(rule.policies, w, r)
switch action {
case actionBlock:
{
m := new(dns.Msg)
m.SetRcode(r, dns.RcodeRefused)
w.WriteMsg(m)
RequestBlockCount.WithLabelValues(metrics.WithServer(ctx), zone).Inc()
return dns.RcodeSuccess, nil
}
case actionAllow:
{
break RulesCheckLoop
}
}
}
RequestAllowCount.WithLabelValues(metrics.WithServer(ctx)).Inc()
return plugin.NextOrFailure(state.Name(), a.Next, ctx, w, r)
}
// matchWithPolicies matches the DNS query with a list of ACL polices and returns suitable
// action agains the query.
func matchWithPolicies(policies []policy, w dns.ResponseWriter, r *dns.Msg) action {
state := request.Request{W: w, Req: r}
ip := net.ParseIP(state.IP())
qtype := state.QType()
for _, policy := range policies {
// dns.TypeNone matches all query types.
_, matchAll := policy.qtypes[dns.TypeNone]
_, match := policy.qtypes[qtype]
if !matchAll && !match {
continue
}
_, contained := policy.filter.GetByIP(ip)
if !contained {
continue
}
// matched.
return policy.action
}
return actionNone
}
// Name implements the plugin.Handler interface.
func (a ACL) Name() string {
return "acl"
}