aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--plugin/dnssec/black_lies.go55
-rw-r--r--plugin/dnssec/black_lies_test.go4
-rw-r--r--plugin/dnssec/cache_test.go4
-rw-r--r--plugin/dnssec/dnssec.go12
-rw-r--r--plugin/dnssec/dnssec_test.go28
-rw-r--r--plugin/dnssec/handler.go2
-rw-r--r--plugin/dnssec/handler_test.go33
-rw-r--r--plugin/dnssec/responsewriter.go3
8 files changed, 108 insertions, 33 deletions
diff --git a/plugin/dnssec/black_lies.go b/plugin/dnssec/black_lies.go
index 527b2fc3e..d5541da79 100644
--- a/plugin/dnssec/black_lies.go
+++ b/plugin/dnssec/black_lies.go
@@ -1,24 +1,65 @@
package dnssec
-import "github.com/miekg/dns"
+import (
+ "github.com/coredns/coredns/plugin/pkg/response"
+ "github.com/coredns/coredns/request"
+
+ "github.com/miekg/dns"
+)
// nsec returns an NSEC useful for NXDOMAIN respsones.
// See https://tools.ietf.org/html/draft-valsorda-dnsop-black-lies-00
// For example, a request for the non-existing name a.example.com would
// cause the following NSEC record to be generated:
-// a.example.com. 3600 IN NSEC \000.a.example.com. ( RRSIG NSEC )
+// a.example.com. 3600 IN NSEC \000.a.example.com. ( RRSIG NSEC ... )
// This inturn makes every NXDOMAIN answer a NODATA one, don't forget to flip
// the header rcode to NOERROR.
-func (d Dnssec) nsec(name, zone string, ttl, incep, expir uint32) ([]dns.RR, error) {
+func (d Dnssec) nsec(state request.Request, mt response.Type, ttl, incep, expir uint32) ([]dns.RR, error) {
nsec := &dns.NSEC{}
- nsec.Hdr = dns.RR_Header{Name: name, Ttl: ttl, Class: dns.ClassINET, Rrtype: dns.TypeNSEC}
- nsec.NextDomain = "\\000." + name
- nsec.TypeBitMap = []uint16{dns.TypeRRSIG, dns.TypeNSEC}
+ nsec.Hdr = dns.RR_Header{Name: state.QName(), Ttl: ttl, Class: dns.ClassINET, Rrtype: dns.TypeNSEC}
+ nsec.NextDomain = "\\000." + state.QName()
+ if state.Name() == state.Zone {
+ nsec.TypeBitMap = filter18(state.QType(), apexBitmap, mt)
+ } else {
+ nsec.TypeBitMap = filter14(state.QType(), zoneBitmap, mt)
+ }
- sigs, err := d.sign([]dns.RR{nsec}, zone, ttl, incep, expir)
+ sigs, err := d.sign([]dns.RR{nsec}, state.Zone, ttl, incep, expir)
if err != nil {
return nil, err
}
return append(sigs, nsec), nil
}
+
+// The NSEC bit maps we return.
+var (
+ zoneBitmap = [...]uint16{dns.TypeA, dns.TypeHINFO, dns.TypeTXT, dns.TypeAAAA, dns.TypeLOC, dns.TypeSRV, dns.TypeCERT, dns.TypeSSHFP, dns.TypeRRSIG, dns.TypeNSEC, dns.TypeTLSA, dns.TypeHIP, dns.TypeOPENPGPKEY, dns.TypeSPF}
+ apexBitmap = [...]uint16{dns.TypeA, dns.TypeNS, dns.TypeSOA, dns.TypeHINFO, dns.TypeMX, dns.TypeTXT, dns.TypeAAAA, dns.TypeLOC, dns.TypeSRV, dns.TypeCERT, dns.TypeSSHFP, dns.TypeRRSIG, dns.TypeNSEC, dns.TypeDNSKEY, dns.TypeTLSA, dns.TypeHIP, dns.TypeOPENPGPKEY, dns.TypeSPF}
+)
+
+// filter14 filters out t from bitmap (if it exists). If mt is not an NODATA response, just
+// return the entire bitmap.
+func filter14(t uint16, bitmap [14]uint16, mt response.Type) []uint16 {
+ if mt != response.NoData {
+ return zoneBitmap[:]
+ }
+ for i := range bitmap {
+ if bitmap[i] == t {
+ return append(bitmap[:i], bitmap[i+1:]...)
+ }
+ }
+ return zoneBitmap[:] // make a slice
+}
+
+func filter18(t uint16, bitmap [18]uint16, mt response.Type) []uint16 {
+ if mt != response.NoData {
+ return apexBitmap[:]
+ }
+ for i := range bitmap {
+ if bitmap[i] == t {
+ return append(bitmap[:i], bitmap[i+1:]...)
+ }
+ }
+ return apexBitmap[:] // make a slice
+}
diff --git a/plugin/dnssec/black_lies_test.go b/plugin/dnssec/black_lies_test.go
index 80c2ce484..851ead483 100644
--- a/plugin/dnssec/black_lies_test.go
+++ b/plugin/dnssec/black_lies_test.go
@@ -16,8 +16,8 @@ func TestZoneSigningBlackLies(t *testing.T) {
defer rm2()
m := testNxdomainMsg()
- state := request.Request{Req: m}
- m = d.Sign(state, "miek.nl.", time.Now().UTC())
+ state := request.Request{Req: m, Zone: "miek.nl."}
+ m = d.Sign(state, time.Now().UTC())
if !section(m.Ns, 2) {
t.Errorf("authority section should have 2 sig")
}
diff --git a/plugin/dnssec/cache_test.go b/plugin/dnssec/cache_test.go
index b978df244..ccf588d8e 100644
--- a/plugin/dnssec/cache_test.go
+++ b/plugin/dnssec/cache_test.go
@@ -22,10 +22,10 @@ func TestCacheSet(t *testing.T) {
c := cache.New(defaultCap)
m := testMsg()
- state := request.Request{Req: m}
+ state := request.Request{Req: m, Zone: "miek.nl."}
k := hash(m.Answer) // calculate *before* we add the sig
d := New([]string{"miek.nl."}, []*DNSKEY{dnskey}, nil, c)
- d.Sign(state, "miek.nl.", time.Now().UTC())
+ d.Sign(state, time.Now().UTC())
_, ok := d.get(k)
if !ok {
diff --git a/plugin/dnssec/dnssec.go b/plugin/dnssec/dnssec.go
index 6451182ff..83e034e6c 100644
--- a/plugin/dnssec/dnssec.go
+++ b/plugin/dnssec/dnssec.go
@@ -39,7 +39,7 @@ func New(zones []string, keys []*DNSKEY, next plugin.Handler, c *cache.Cache) Dn
// will insert DS records and sign those.
// Signatures will be cached for a short while. By default we sign for 8 days,
// starting 3 hours ago.
-func (d Dnssec) Sign(state request.Request, zone string, now time.Time) *dns.Msg {
+func (d Dnssec) Sign(state request.Request, now time.Time) *dns.Msg {
req := state.Req
incep, expir := incepExpir(now)
@@ -71,10 +71,10 @@ func (d Dnssec) Sign(state request.Request, zone string, now time.Time) *dns.Msg
ttl := req.Ns[0].Header().Ttl
- if sigs, err := d.sign(req.Ns, zone, ttl, incep, expir); err == nil {
+ if sigs, err := d.sign(req.Ns, state.Zone, ttl, incep, expir); err == nil {
req.Ns = append(req.Ns, sigs...)
}
- if sigs, err := d.nsec(state.Name(), zone, ttl, incep, expir); err == nil {
+ if sigs, err := d.nsec(state, mt, ttl, incep, expir); err == nil {
req.Ns = append(req.Ns, sigs...)
}
if len(req.Ns) > 1 { // actually added nsec and sigs, reset the rcode
@@ -85,19 +85,19 @@ func (d Dnssec) Sign(state request.Request, zone string, now time.Time) *dns.Msg
for _, r := range rrSets(req.Answer) {
ttl := r[0].Header().Ttl
- if sigs, err := d.sign(r, zone, ttl, incep, expir); err == nil {
+ if sigs, err := d.sign(r, state.Zone, ttl, incep, expir); err == nil {
req.Answer = append(req.Answer, sigs...)
}
}
for _, r := range rrSets(req.Ns) {
ttl := r[0].Header().Ttl
- if sigs, err := d.sign(r, zone, ttl, incep, expir); err == nil {
+ if sigs, err := d.sign(r, state.Zone, ttl, incep, expir); err == nil {
req.Ns = append(req.Ns, sigs...)
}
}
for _, r := range rrSets(req.Extra) {
ttl := r[0].Header().Ttl
- if sigs, err := d.sign(r, zone, ttl, incep, expir); err == nil {
+ if sigs, err := d.sign(r, state.Zone, ttl, incep, expir); err == nil {
req.Extra = append(sigs, req.Extra...) // prepend to leave OPT alone
}
}
diff --git a/plugin/dnssec/dnssec_test.go b/plugin/dnssec/dnssec_test.go
index 090642acf..112299d79 100644
--- a/plugin/dnssec/dnssec_test.go
+++ b/plugin/dnssec/dnssec_test.go
@@ -17,9 +17,9 @@ func TestZoneSigning(t *testing.T) {
defer rm2()
m := testMsg()
- state := request.Request{Req: m}
+ state := request.Request{Req: m, Zone: "miek.nl."}
- m = d.Sign(state, "miek.nl.", time.Now().UTC())
+ m = d.Sign(state, time.Now().UTC())
if !section(m.Answer, 1) {
t.Errorf("Answer section should have 1 RRSIG")
}
@@ -45,8 +45,8 @@ func TestZoneSigningDouble(t *testing.T) {
d.keys = append(d.keys, key1)
m := testMsg()
- state := request.Request{Req: m}
- m = d.Sign(state, "miek.nl.", time.Now().UTC())
+ state := request.Request{Req: m, Zone: "miek.nl."}
+ m = d.Sign(state, time.Now().UTC())
if !section(m.Answer, 2) {
t.Errorf("Answer section should have 1 RRSIG")
}
@@ -68,10 +68,10 @@ func TestSigningDifferentZone(t *testing.T) {
}
m := testMsgEx()
- state := request.Request{Req: m}
+ state := request.Request{Req: m, Zone: "example.org."}
c := cache.New(defaultCap)
d := New([]string{"example.org."}, []*DNSKEY{key}, nil, c)
- m = d.Sign(state, "example.org.", time.Now().UTC())
+ m = d.Sign(state, time.Now().UTC())
if !section(m.Answer, 1) {
t.Errorf("Answer section should have 1 RRSIG")
t.Logf("%+v\n", m)
@@ -88,8 +88,8 @@ func TestSigningCname(t *testing.T) {
defer rm2()
m := testMsgCname()
- state := request.Request{Req: m}
- m = d.Sign(state, "miek.nl.", time.Now().UTC())
+ state := request.Request{Req: m, Zone: "miek.nl."}
+ m = d.Sign(state, time.Now().UTC())
if !section(m.Answer, 1) {
t.Errorf("Answer section should have 1 RRSIG")
}
@@ -102,8 +102,8 @@ func testZoneSigningDelegation(t *testing.T) {
defer rm2()
m := testDelegationMsg()
- state := request.Request{Req: m}
- m = d.Sign(state, "miek.nl.", time.Now().UTC())
+ state := request.Request{Req: m, Zone: "miek.nl."}
+ m = d.Sign(state, time.Now().UTC())
if !section(m.Ns, 1) {
t.Errorf("Authority section should have 1 RRSIG")
t.Logf("%v\n", m)
@@ -132,9 +132,9 @@ func TestSigningDname(t *testing.T) {
defer rm2()
m := testMsgDname()
- state := request.Request{Req: m}
+ state := request.Request{Req: m, Zone: "miek.nl."}
// We sign *everything* we see, also the synthesized CNAME.
- m = d.Sign(state, "miek.nl.", time.Now().UTC())
+ m = d.Sign(state, time.Now().UTC())
if !section(m.Answer, 3) {
t.Errorf("Answer section should have 3 RRSIGs")
}
@@ -147,8 +147,8 @@ func TestSigningEmpty(t *testing.T) {
m := testEmptyMsg()
m.SetQuestion("a.miek.nl.", dns.TypeA)
- state := request.Request{Req: m}
- m = d.Sign(state, "miek.nl.", time.Now().UTC())
+ state := request.Request{Req: m, Zone: "miek.nl."}
+ m = d.Sign(state, time.Now().UTC())
if !section(m.Ns, 2) {
t.Errorf("Authority section should have 2 RRSIGs")
}
diff --git a/plugin/dnssec/handler.go b/plugin/dnssec/handler.go
index 0fde35dd7..0bef73afa 100644
--- a/plugin/dnssec/handler.go
+++ b/plugin/dnssec/handler.go
@@ -23,6 +23,8 @@ func (d Dnssec) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg)
return plugin.NextOrFailure(d.Name(), d.Next, ctx, w, r)
}
+ state.Zone = zone
+
// Intercept queries for DNSKEY, but only if one of the zones matches the qname, otherwise we let
// the query through.
if qtype == dns.TypeDNSKEY {
diff --git a/plugin/dnssec/handler_test.go b/plugin/dnssec/handler_test.go
index ba24a45d1..77bf36f32 100644
--- a/plugin/dnssec/handler_test.go
+++ b/plugin/dnssec/handler_test.go
@@ -73,9 +73,29 @@ var dnsTestCases = []test.Case{
Extra: []dns.RR{test.OPT(4096, true)},
},
{
+ Qname: "wwwww.miek.nl.", Qtype: dns.TypeAAAA, Do: true,
+ Ns: []dns.RR{
+ test.RRSIG("miek.nl. 1800 IN RRSIG SOA 13 2 3600 20171220135446 20171212105446 18512 miek.nl. hCRzzjYz6w=="),
+ test.SOA("miek.nl. 1800 IN SOA linode.atoom.net. miek.miek.nl. 1282630057 14400 3600 604800 14400"),
+ test.NSEC("wwwww.miek.nl. 1800 IN NSEC \\000.wwwww.miek.nl. A HINFO TXT LOC SRV CERT SSHFP RRSIG NSEC TLSA HIP OPENPGPKEY SPF"),
+ test.RRSIG("wwwww.miek.nl. 1800 IN RRSIG NSEC 13 3 3600 20171220135446 20171212105446 18512 miek.nl. cVUQWs8xw=="),
+ },
+ Extra: []dns.RR{test.OPT(4096, true)},
+ },
+ {
+ Qname: "miek.nl.", Qtype: dns.TypeHINFO, Do: true,
+ Ns: []dns.RR{
+ test.NSEC("miek.nl. 1800 IN NSEC \\000.miek.nl. A NS SOA MX TXT AAAA LOC SRV CERT SSHFP RRSIG NSEC DNSKEY TLSA HIP OPENPGPKEY SPF"),
+ test.RRSIG("miek.nl. 1800 IN RRSIG NSEC 13 2 3600 20171220141741 20171212111741 18512 miek.nl. GuXROL7Uu+UiPcg=="),
+ test.RRSIG("miek.nl. 1800 IN RRSIG SOA 13 2 3600 20171220141741 20171212111741 18512 miek.nl. 8bLTReqmuQtw=="),
+ test.SOA("miek.nl. 1800 IN SOA linode.atoom.net. miek.miek.nl. 1282630057 14400 3600 604800 14400"),
+ },
+ Extra: []dns.RR{test.OPT(4096, true)},
+ },
+ {
Qname: "www.example.org.", Qtype: dns.TypeAAAA, Do: true,
Rcode: dns.RcodeServerFailure,
- // Extra: []dns.RR{test.OPT(4096, true)}, // test.ErrorHandler is a simple handler that does not do EDNS.
+ // Extra: []dns.RR{test.OPT(4096, true)}, // test.ErrorHandler is a simple handler that does not do EDNS on ServerFailure
},
}
@@ -131,6 +151,17 @@ func TestLookupDNSKEY(t *testing.T) {
}
test.SortAndCheck(t, resp, tc)
+
+ // If there is an NSEC present in authority section check if the bitmap does not have the qtype set.
+ for _, rr := range resp.Ns {
+ if n, ok := rr.(*dns.NSEC); ok {
+ for i := range n.TypeBitMap {
+ if n.TypeBitMap[i] == tc.Qtype {
+ t.Errorf("bitmap contains qtype: %d", tc.Qtype)
+ }
+ }
+ }
+ }
}
}
diff --git a/plugin/dnssec/responsewriter.go b/plugin/dnssec/responsewriter.go
index 5a38abac7..c50850aba 100644
--- a/plugin/dnssec/responsewriter.go
+++ b/plugin/dnssec/responsewriter.go
@@ -26,9 +26,10 @@ func (d *ResponseWriter) WriteMsg(res *dns.Msg) error {
if zone == "" {
return d.ResponseWriter.WriteMsg(res)
}
+ state.Zone = zone
if state.Do() {
- res = d.d.Sign(state, zone, time.Now().UTC())
+ res = d.d.Sign(state, time.Now().UTC())
cacheSize.WithLabelValues("signature").Set(float64(d.d.cache.Len()))
}