diff options
author | 2019-09-04 23:43:45 +0800 | |
---|---|---|
committer | 2019-09-04 08:43:45 -0700 | |
commit | 79f37a1460cc52ce6c63110f4df33316a36af3a5 (patch) | |
tree | 35b3ddcc68ba82eabb9393cb166b5de76a42bb29 /plugin/acl/acl.go | |
parent | 7894154bfd2f1960c6842318d8ee99c194a04179 (diff) | |
download | coredns-79f37a1460cc52ce6c63110f4df33316a36af3a5.tar.gz coredns-79f37a1460cc52ce6c63110f4df33316a36af3a5.tar.zst coredns-79f37a1460cc52ce6c63110f4df33316a36af3a5.zip |
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>
Diffstat (limited to 'plugin/acl/acl.go')
-rw-r--r-- | plugin/acl/acl.go | 115 |
1 files changed, 115 insertions, 0 deletions
diff --git a/plugin/acl/acl.go b/plugin/acl/acl.go new file mode 100644 index 000000000..b25138a30 --- /dev/null +++ b/plugin/acl/acl.go @@ -0,0 +1,115 @@ +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" +} |