mirror of
				https://github.com/coredns/coredns.git
				synced 2025-11-04 03:03:14 -05:00 
			
		
		
		
	* Fixing #5376 by adding a check to parse out Zone information Signed-off-by: Tintin <samrath.sodi@gmail.com> * using IndexByte instead of strings.Split() Signed-off-by: Tintin <samrath.sodi@gmail.com> * using plugin logger for logging parsing failure Signed-off-by: Tintin <samrath.sodi@gmail.com> * using var keywork instead of short declaration operator Signed-off-by: Tintin <samrath.sodi@gmail.com> * reordering imports Signed-off-by: Tintin <samrath.sodi@gmail.com>
		
			
				
	
	
		
			140 lines
		
	
	
		
			3.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			140 lines
		
	
	
		
			3.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package acl
 | 
						|
 | 
						|
import (
 | 
						|
	"context"
 | 
						|
	"net"
 | 
						|
	"strings"
 | 
						|
 | 
						|
	"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"
 | 
						|
)
 | 
						|
 | 
						|
// 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
 | 
						|
	// actionFilter returns empty sets for queries towards protected DNS zones.
 | 
						|
	actionFilter
 | 
						|
)
 | 
						|
 | 
						|
var log = clog.NewWithPlugin("acl")
 | 
						|
 | 
						|
// 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
 | 
						|
			}
 | 
						|
		case actionFilter:
 | 
						|
			{
 | 
						|
				m := new(dns.Msg)
 | 
						|
				m.SetRcode(r, dns.RcodeSuccess)
 | 
						|
				w.WriteMsg(m)
 | 
						|
				RequestFilterCount.WithLabelValues(metrics.WithServer(ctx), zone).Inc()
 | 
						|
				return dns.RcodeSuccess, nil
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
	}
 | 
						|
 | 
						|
	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 against the query.
 | 
						|
func matchWithPolicies(policies []policy, w dns.ResponseWriter, r *dns.Msg) action {
 | 
						|
	state := request.Request{W: w, Req: r}
 | 
						|
 | 
						|
	var ip net.IP
 | 
						|
	if idx := strings.IndexByte(state.IP(), '%'); idx >= 0 {
 | 
						|
		ip = net.ParseIP(state.IP()[:idx])
 | 
						|
	} else {
 | 
						|
		ip = net.ParseIP(state.IP())
 | 
						|
	}
 | 
						|
 | 
						|
	// if the parsing did not return a proper response then we simply return 'actionBlock' to
 | 
						|
	// block the query
 | 
						|
	if ip == nil {
 | 
						|
		log.Errorf("Blocking request. Unable to parse source address: %v", state.IP())
 | 
						|
		return actionBlock
 | 
						|
	}
 | 
						|
	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"
 | 
						|
}
 |