| 
									
										
										
										
											2017-09-14 09:36:06 +01:00
										 |  |  | // Package dnssec implements a plugin that signs responses on-the-fly using
 | 
					
						
							| 
									
										
										
										
											2016-09-25 08:39:20 +01:00
										 |  |  | // NSEC black lies.
 | 
					
						
							| 
									
										
										
										
											2016-04-26 17:57:11 +01:00
										 |  |  | package dnssec
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import (
 | 
					
						
							|  |  |  | 	"time"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-14 09:36:06 +01:00
										 |  |  | 	"github.com/coredns/coredns/plugin"
 | 
					
						
							|  |  |  | 	"github.com/coredns/coredns/plugin/pkg/cache"
 | 
					
						
							|  |  |  | 	"github.com/coredns/coredns/plugin/pkg/response"
 | 
					
						
							|  |  |  | 	"github.com/coredns/coredns/plugin/pkg/singleflight"
 | 
					
						
							| 
									
										
										
										
											2017-02-21 22:51:47 -08:00
										 |  |  | 	"github.com/coredns/coredns/request"
 | 
					
						
							| 
									
										
										
										
											2016-04-26 17:57:11 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/miekg/dns"
 | 
					
						
							|  |  |  | )
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-23 09:14:12 +01:00
										 |  |  | // Dnssec signs the reply on-the-fly.
 | 
					
						
							| 
									
										
										
										
											2016-04-26 17:57:11 +01:00
										 |  |  | type Dnssec struct {
 | 
					
						
							| 
									
										
										
										
											2017-09-14 09:36:06 +01:00
										 |  |  | 	Next plugin.Handler
 | 
					
						
							| 
									
										
										
										
											2016-08-19 17:14:17 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-20 17:35:59 +02:00
										 |  |  | 	zones     []string
 | 
					
						
							|  |  |  | 	keys      []*DNSKEY
 | 
					
						
							|  |  |  | 	splitkeys bool
 | 
					
						
							|  |  |  | 	inflight  *singleflight.Group
 | 
					
						
							|  |  |  | 	cache     *cache.Cache
 | 
					
						
							| 
									
										
										
										
											2016-04-26 17:57:11 +01:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-23 09:14:12 +01:00
										 |  |  | // New returns a new Dnssec.
 | 
					
						
							| 
									
										
										
										
											2018-10-20 17:35:59 +02:00
										 |  |  | func New(zones []string, keys []*DNSKEY, splitkeys bool, next plugin.Handler, c *cache.Cache) Dnssec {
 | 
					
						
							| 
									
										
										
										
											2016-04-26 17:57:11 +01:00
										 |  |  | 	return Dnssec{Next: next,
 | 
					
						
							| 
									
										
										
										
											2018-10-20 17:35:59 +02:00
										 |  |  | 		zones:     zones,
 | 
					
						
							|  |  |  | 		keys:      keys,
 | 
					
						
							|  |  |  | 		splitkeys: splitkeys,
 | 
					
						
							|  |  |  | 		cache:     c,
 | 
					
						
							|  |  |  | 		inflight:  new(singleflight.Group),
 | 
					
						
							| 
									
										
										
										
											2016-04-26 17:57:11 +01:00
										 |  |  | 	}
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-07 11:10:16 +01:00
										 |  |  | // Sign signs the message in state. it takes care of negative or nodata responses. It
 | 
					
						
							| 
									
										
										
										
											2017-10-20 09:22:02 +01:00
										 |  |  | // uses NSEC black lies for authenticated denial of existence. For delegations it
 | 
					
						
							|  |  |  | // will insert DS records and sign those.
 | 
					
						
							|  |  |  | // Signatures will be cached for a short while. By default we sign for 8 days,
 | 
					
						
							| 
									
										
										
										
											2016-04-26 17:57:11 +01:00
										 |  |  | // starting 3 hours ago.
 | 
					
						
							| 
									
										
										
										
											2018-04-27 19:37:31 +01:00
										 |  |  | func (d Dnssec) Sign(state request.Request, now time.Time, server string) *dns.Msg {
 | 
					
						
							| 
									
										
										
										
											2016-04-26 17:57:11 +01:00
										 |  |  | 	req := state.Req
 | 
					
						
							| 
									
										
										
										
											2016-09-07 11:10:16 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-20 09:22:02 +01:00
										 |  |  | 	incep, expir := incepExpir(now)
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-29 15:06:42 +01:00
										 |  |  | 	mt, _ := response.Typify(req, time.Now().UTC()) // TODO(miek): need opt record here?
 | 
					
						
							| 
									
										
										
										
											2016-09-07 11:10:16 +01:00
										 |  |  | 	if mt == response.Delegation {
 | 
					
						
							| 
									
										
										
										
											2016-04-26 17:57:11 +01:00
										 |  |  | 		return req
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-08 13:28:35 +02:00
										 |  |  | 	if mt == response.NameError || mt == response.NoData {
 | 
					
						
							| 
									
										
										
										
											2016-04-26 17:57:11 +01:00
										 |  |  | 		if req.Ns[0].Header().Rrtype != dns.TypeSOA || len(req.Ns) > 1 {
 | 
					
						
							|  |  |  | 			return req
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		ttl := req.Ns[0].Header().Ttl
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-27 19:37:31 +01:00
										 |  |  | 		if sigs, err := d.sign(req.Ns, state.Zone, ttl, incep, expir, server); err == nil {
 | 
					
						
							| 
									
										
										
										
											2016-04-26 17:57:11 +01:00
										 |  |  | 			req.Ns = append(req.Ns, sigs...)
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							| 
									
										
										
										
											2018-04-27 19:37:31 +01:00
										 |  |  | 		if sigs, err := d.nsec(state, mt, ttl, incep, expir, server); err == nil {
 | 
					
						
							| 
									
										
										
										
											2016-04-26 17:57:11 +01:00
										 |  |  | 			req.Ns = append(req.Ns, sigs...)
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		if len(req.Ns) > 1 { // actually added nsec and sigs, reset the rcode
 | 
					
						
							|  |  |  | 			req.Rcode = dns.RcodeSuccess
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		return req
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, r := range rrSets(req.Answer) {
 | 
					
						
							|  |  |  | 		ttl := r[0].Header().Ttl
 | 
					
						
							| 
									
										
										
										
											2018-04-27 19:37:31 +01:00
										 |  |  | 		if sigs, err := d.sign(r, state.Zone, ttl, incep, expir, server); err == nil {
 | 
					
						
							| 
									
										
										
										
											2016-04-26 17:57:11 +01:00
										 |  |  | 			req.Answer = append(req.Answer, sigs...)
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 	for _, r := range rrSets(req.Ns) {
 | 
					
						
							|  |  |  | 		ttl := r[0].Header().Ttl
 | 
					
						
							| 
									
										
										
										
											2018-04-27 19:37:31 +01:00
										 |  |  | 		if sigs, err := d.sign(r, state.Zone, ttl, incep, expir, server); err == nil {
 | 
					
						
							| 
									
										
										
										
											2016-04-26 17:57:11 +01:00
										 |  |  | 			req.Ns = append(req.Ns, sigs...)
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 	for _, r := range rrSets(req.Extra) {
 | 
					
						
							|  |  |  | 		ttl := r[0].Header().Ttl
 | 
					
						
							| 
									
										
										
										
											2018-04-27 19:37:31 +01:00
										 |  |  | 		if sigs, err := d.sign(r, state.Zone, ttl, incep, expir, server); err == nil {
 | 
					
						
							| 
									
										
										
										
											2018-08-29 12:26:22 +01:00
										 |  |  | 			req.Extra = append(req.Extra, sigs...)
 | 
					
						
							| 
									
										
										
										
											2016-04-26 17:57:11 +01:00
										 |  |  | 		}
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 	return req
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-27 19:37:31 +01:00
										 |  |  | func (d Dnssec) sign(rrs []dns.RR, signerName string, ttl, incep, expir uint32, server string) ([]dns.RR, error) {
 | 
					
						
							| 
									
										
										
										
											2017-06-13 12:39:10 -07:00
										 |  |  | 	k := hash(rrs)
 | 
					
						
							| 
									
										
										
										
											2018-04-27 19:37:31 +01:00
										 |  |  | 	sgs, ok := d.get(k, server)
 | 
					
						
							| 
									
										
										
										
											2016-04-26 17:57:11 +01:00
										 |  |  | 	if ok {
 | 
					
						
							|  |  |  | 		return sgs, nil
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	sigs, err := d.inflight.Do(k, func() (interface{}, error) {
 | 
					
						
							| 
									
										
										
										
											2018-10-20 17:35:59 +02:00
										 |  |  | 		var sigs []dns.RR
 | 
					
						
							|  |  |  | 		for _, k := range d.keys {
 | 
					
						
							|  |  |  | 			if d.splitkeys {
 | 
					
						
							|  |  |  | 				if len(rrs) > 0 && rrs[0].Header().Rrtype == dns.TypeDNSKEY {
 | 
					
						
							|  |  |  | 					// We are signing a DNSKEY RRSet. With split keys, we need to use a KSK here.
 | 
					
						
							|  |  |  | 					if !k.isKSK() {
 | 
					
						
							|  |  |  | 						continue
 | 
					
						
							|  |  |  | 					}
 | 
					
						
							|  |  |  | 				} else {
 | 
					
						
							|  |  |  | 					// For non-DNSKEY RRSets, we want to use a ZSK.
 | 
					
						
							|  |  |  | 					if !k.isZSK() {
 | 
					
						
							|  |  |  | 						continue
 | 
					
						
							|  |  |  | 					}
 | 
					
						
							|  |  |  | 				}
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							| 
									
										
										
										
											2016-09-23 09:14:12 +01:00
										 |  |  | 			sig := k.newRRSIG(signerName, ttl, incep, expir)
 | 
					
						
							| 
									
										
										
										
											2018-10-20 17:35:59 +02:00
										 |  |  | 			if e := sig.Sign(k.s, rrs); e != nil {
 | 
					
						
							|  |  |  | 				return sigs, e
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							|  |  |  | 			sigs = append(sigs, sig)
 | 
					
						
							| 
									
										
										
										
											2016-04-26 17:57:11 +01:00
										 |  |  | 		}
 | 
					
						
							|  |  |  | 		d.set(k, sigs)
 | 
					
						
							| 
									
										
										
										
											2018-10-20 17:35:59 +02:00
										 |  |  | 		return sigs, nil
 | 
					
						
							| 
									
										
										
										
											2016-04-26 17:57:11 +01:00
										 |  |  | 	})
 | 
					
						
							|  |  |  | 	return sigs.([]dns.RR), err
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-31 17:26:43 -04:00
										 |  |  | func (d Dnssec) set(key uint64, sigs []dns.RR) { d.cache.Add(key, sigs) }
 | 
					
						
							| 
									
										
										
										
											2016-04-26 17:57:11 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-31 17:26:43 -04:00
										 |  |  | func (d Dnssec) get(key uint64, server string) ([]dns.RR, bool) {
 | 
					
						
							| 
									
										
										
										
											2016-04-26 17:57:11 +01:00
										 |  |  | 	if s, ok := d.cache.Get(key); ok {
 | 
					
						
							| 
									
										
										
										
											2018-01-18 10:39:22 +00:00
										 |  |  | 		// we sign for 8 days, check if a signature in the cache reached 3/4 of that
 | 
					
						
							| 
									
										
										
										
											2021-05-14 04:49:16 -04:00
										 |  |  | 		is75 := time.Now().UTC().Add(twoDays)
 | 
					
						
							| 
									
										
										
										
											2018-01-18 10:39:22 +00:00
										 |  |  | 		for _, rr := range s.([]dns.RR) {
 | 
					
						
							|  |  |  | 			if !rr.(*dns.RRSIG).ValidityPeriod(is75) {
 | 
					
						
							| 
									
										
										
										
											2018-04-27 19:37:31 +01:00
										 |  |  | 				cacheMisses.WithLabelValues(server).Inc()
 | 
					
						
							| 
									
										
										
										
											2018-01-18 10:39:22 +00:00
										 |  |  | 				return nil, false
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-27 19:37:31 +01:00
										 |  |  | 		cacheHits.WithLabelValues(server).Inc()
 | 
					
						
							| 
									
										
										
										
											2016-04-26 17:57:11 +01:00
										 |  |  | 		return s.([]dns.RR), true
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2018-04-27 19:37:31 +01:00
										 |  |  | 	cacheMisses.WithLabelValues(server).Inc()
 | 
					
						
							| 
									
										
										
										
											2016-04-26 17:57:11 +01:00
										 |  |  | 	return nil, false
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func incepExpir(now time.Time) (uint32, uint32) {
 | 
					
						
							|  |  |  | 	incep := uint32(now.Add(-3 * time.Hour).Unix()) // -(2+1) hours, be sure to catch daylight saving time and such
 | 
					
						
							|  |  |  | 	expir := uint32(now.Add(eightDays).Unix())      // sign for 8 days
 | 
					
						
							|  |  |  | 	return incep, expir
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const (
 | 
					
						
							| 
									
										
										
										
											2016-10-17 05:04:36 -07:00
										 |  |  | 	eightDays  = 8 * 24 * time.Hour
 | 
					
						
							| 
									
										
										
										
											2021-05-14 04:49:16 -04:00
										 |  |  | 	twoDays    = 2 * 24 * time.Hour
 | 
					
						
							| 
									
										
										
										
											2016-10-17 05:04:36 -07:00
										 |  |  | 	defaultCap = 10000 // default capacity of the cache.
 | 
					
						
							| 
									
										
										
										
											2016-04-26 17:57:11 +01:00
										 |  |  | )
 |