cache: default to DNSSEC (#4085)

* cache: default to DNSSEC

This change does away with the DNS/DNSSEC distinction the cache
currently makes. Cache will always make coredns perform a DNSSEC query
and store that result. If a client just needs plain DNS, the DNSSEC
records are stripped from the response.

It should also be more memory efficient, because we store a reply once
and not one DNS and another for DNSSEC.

Fixes: #3836

Signed-off-by: Miek Gieben <miek@miek.nl>

* Change OPT RR when one is present in the msg.

Signed-off-by: Miek Gieben <miek@miek.nl>

* Fix comment for isDNSSEC

Signed-off-by: Miek Gieben <miek@miek.nl>

* Update plugin/cache/handler.go

Co-authored-by: Chris O'Haver <cohaver@infoblox.com>

* Update plugin/cache/item.go

Co-authored-by: Chris O'Haver <cohaver@infoblox.com>

* Code review; fix comment for isDNSSEC

Signed-off-by: Miek Gieben <miek@miek.nl>

* Update doc and set AD to false

Set Authenticated Data to false when DNSSEC was not wanted. Also update
the readme with the new behavior.

Signed-off-by: Miek Gieben <miek@miek.nl>

* Update plugin/cache/handler.go

Co-authored-by: Chris O'Haver <cohaver@infoblox.com>

Co-authored-by: Chris O'Haver <cohaver@infoblox.com>
This commit is contained in:
Miek Gieben 2020-09-17 16:28:43 +02:00 committed by GitHub
parent 22b6846626
commit acf9a0fa19
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 207 additions and 47 deletions

View file

@ -15,6 +15,7 @@ import (
// ServeDNS implements the plugin.Handler interface.
func (c *Cache) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
state := request.Request{W: w, Req: r}
do := state.Do()
zone := plugin.Zones(c.Zones).Matches(state.Name())
if zone == "" {
@ -22,15 +23,24 @@ func (c *Cache) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg)
}
now := c.now().UTC()
server := metrics.WithServer(ctx)
// On cache miss, if the request has the OPT record and the DO bit set we leave the message as-is. If there isn't a DO bit
// set we will modify the request to _add_ one. This means we will always do DNSSEC lookups on cache misses.
// When writing to cache, any DNSSEC RRs in the response are written to cache with the response.
// When sending a response to a non-DNSSEC client, we remove DNSSEC RRs from the response. We use a 2048 buffer size, which is
// less than 4096 (and older default) and more than 1024 which may be too small. We might need to tweaks this
// value to be smaller still to prevent UDP fragmentation?
ttl := 0
i := c.getIgnoreTTL(now, state, server)
if i != nil {
ttl = i.ttl(now)
}
if i == nil {
if !do {
setDo(r)
}
crr := &ResponseWriter{ResponseWriter: w, Cache: c, state: state, server: server}
return plugin.NextOrFailure(c.Name(), c.Next, ctx, crr, r)
}
@ -40,11 +50,14 @@ func (c *Cache) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg)
now = now.Add(time.Duration(ttl) * time.Second)
go func() {
r := r.Copy()
if !do {
setDo(r)
}
crr := &ResponseWriter{Cache: c, state: state, server: server, prefetch: true, remoteAddr: w.LocalAddr()}
plugin.NextOrFailure(c.Name(), c.Next, ctx, crr, r)
}()
}
resp := i.toMsg(r, now)
resp := i.toMsg(r, now, do)
w.WriteMsg(resp)
if c.shouldPrefetch(i, now) {
@ -80,7 +93,7 @@ func (c *Cache) shouldPrefetch(i *item, now time.Time) bool {
func (c *Cache) Name() string { return "cache" }
func (c *Cache) get(now time.Time, state request.Request, server string) (*item, bool) {
k := hash(state.Name(), state.QType(), state.Do())
k := hash(state.Name(), state.QType())
if i, ok := c.ncache.Get(k); ok && i.(*item).ttl(now) > 0 {
cacheHits.WithLabelValues(server, Denial).Inc()
@ -97,7 +110,7 @@ func (c *Cache) get(now time.Time, state request.Request, server string) (*item,
// getIgnoreTTL unconditionally returns an item if it exists in the cache.
func (c *Cache) getIgnoreTTL(now time.Time, state request.Request, server string) *item {
k := hash(state.Name(), state.QType(), state.Do())
k := hash(state.Name(), state.QType())
if i, ok := c.ncache.Get(k); ok {
ttl := i.(*item).ttl(now)
@ -118,7 +131,7 @@ func (c *Cache) getIgnoreTTL(now time.Time, state request.Request, server string
}
func (c *Cache) exists(state request.Request) *item {
k := hash(state.Name(), state.QType(), state.Do())
k := hash(state.Name(), state.QType())
if i, ok := c.ncache.Get(k); ok {
return i.(*item)
}
@ -127,3 +140,22 @@ func (c *Cache) exists(state request.Request) *item {
}
return nil
}
// setDo sets the DO bit and UDP buffer size in the message m.
func setDo(m *dns.Msg) {
o := m.IsEdns0()
if o != nil {
o.SetDo()
o.SetUDPSize(defaultUDPBufSize)
return
}
o = &dns.OPT{Hdr: dns.RR_Header{Name: ".", Rrtype: dns.TypeOPT}}
o.SetDo()
o.SetUDPSize(defaultUDPBufSize)
m.Extra = append(m.Extra, o)
}
// defaultUDPBufsize is the bufsize the cache plugin uses on outgoing requests that don't
// have an OPT RR.
const defaultUDPBufSize = 2048