mirror of
https://github.com/coredns/coredns.git
synced 2025-10-27 08:14:18 -04:00
plugin/dnssec; insert and sign DS records (#1153)
* plugin/dnssec; insert and sign DS records Sign a delegation as well and insert DS records. Fixes #698 * better
This commit is contained in:
@@ -15,9 +15,10 @@ import (
|
||||
|
||||
// DNSKEY holds a DNSSEC public and private key used for on-the-fly signing.
|
||||
type DNSKEY struct {
|
||||
K *dns.DNSKEY
|
||||
s crypto.Signer
|
||||
keytag uint16
|
||||
K *dns.DNSKEY
|
||||
D *dns.DS
|
||||
s crypto.Signer
|
||||
tag uint16
|
||||
}
|
||||
|
||||
// ParseKeyFile read a DNSSEC keyfile as generated by dnssec-keygen or other
|
||||
@@ -36,18 +37,20 @@ func ParseKeyFile(pubFile, privFile string) (*DNSKEY, error) {
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
p, e := k.(*dns.DNSKEY).ReadPrivateKey(f, privFile)
|
||||
|
||||
dk := k.(*dns.DNSKEY)
|
||||
p, e := dk.ReadPrivateKey(f, privFile)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
|
||||
if v, ok := p.(*rsa.PrivateKey); ok {
|
||||
return &DNSKEY{k.(*dns.DNSKEY), v, k.(*dns.DNSKEY).KeyTag()}, nil
|
||||
if s, ok := p.(*rsa.PrivateKey); ok {
|
||||
return &DNSKEY{K: dk, D: dk.ToDS(dns.SHA256), s: s, tag: dk.KeyTag()}, nil
|
||||
}
|
||||
if v, ok := p.(*ecdsa.PrivateKey); ok {
|
||||
return &DNSKEY{k.(*dns.DNSKEY), v, k.(*dns.DNSKEY).KeyTag()}, nil
|
||||
if s, ok := p.(*ecdsa.PrivateKey); ok {
|
||||
return &DNSKEY{K: dk, D: dk.ToDS(dns.SHA256), s: s, tag: dk.KeyTag()}, nil
|
||||
}
|
||||
return &DNSKEY{k.(*dns.DNSKEY), nil, 0}, errors.New("no known? private key found")
|
||||
return &DNSKEY{K: dk, D: dk.ToDS(dns.SHA256), s: nil, tag: 0}, errors.New("no known private key found")
|
||||
}
|
||||
|
||||
// getDNSKEY returns the correct DNSKEY to the client. Signatures are added when do is true.
|
||||
|
||||
@@ -35,20 +35,30 @@ func New(zones []string, keys []*DNSKEY, next plugin.Handler, c *cache.Cache) Dn
|
||||
}
|
||||
|
||||
// Sign signs the message in state. it takes care of negative or nodata responses. It
|
||||
// uses NSEC black lies for authenticated denial of existence. Signatures
|
||||
// creates will be cached for a short while. By default we sign for 8 days,
|
||||
// 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,
|
||||
// starting 3 hours ago.
|
||||
func (d Dnssec) Sign(state request.Request, zone string, now time.Time) *dns.Msg {
|
||||
req := state.Req
|
||||
|
||||
incep, expir := incepExpir(now)
|
||||
|
||||
mt, _ := response.Typify(req, time.Now().UTC()) // TODO(miek): need opt record here?
|
||||
if mt == response.Delegation {
|
||||
// TODO(miek): uh, signing DS record?!?!
|
||||
ttl := req.Ns[0].Header().Ttl
|
||||
|
||||
ds := []dns.RR{}
|
||||
for i := range d.keys {
|
||||
ds = append(ds, d.keys[i].D)
|
||||
}
|
||||
if sigs, err := d.sign(ds, zone, ttl, incep, expir); err == nil {
|
||||
req.Ns = append(req.Ns, ds...)
|
||||
req.Ns = append(req.Ns, sigs...)
|
||||
}
|
||||
return req
|
||||
}
|
||||
|
||||
incep, expir := incepExpir(now)
|
||||
|
||||
if mt == response.NameError || mt == response.NoData {
|
||||
if req.Ns[0].Header().Rrtype != dns.TypeSOA || len(req.Ns) > 1 {
|
||||
return req
|
||||
|
||||
@@ -21,10 +21,10 @@ func TestZoneSigning(t *testing.T) {
|
||||
|
||||
m = d.Sign(state, "miek.nl.", time.Now().UTC())
|
||||
if !section(m.Answer, 1) {
|
||||
t.Errorf("answer section should have 1 sig")
|
||||
t.Errorf("Answer section should have 1 RRSIG")
|
||||
}
|
||||
if !section(m.Ns, 1) {
|
||||
t.Errorf("authority section should have 1 sig")
|
||||
t.Errorf("Authority section should have 1 RRSIG")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ func TestZoneSigningDouble(t *testing.T) {
|
||||
|
||||
key1, err := ParseKeyFile(fPub1, fPriv1)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to parse key: %v\n", err)
|
||||
t.Fatalf("Failed to parse key: %v\n", err)
|
||||
}
|
||||
d.keys = append(d.keys, key1)
|
||||
|
||||
@@ -48,10 +48,10 @@ func TestZoneSigningDouble(t *testing.T) {
|
||||
state := request.Request{Req: m}
|
||||
m = d.Sign(state, "miek.nl.", time.Now().UTC())
|
||||
if !section(m.Answer, 2) {
|
||||
t.Errorf("answer section should have 1 sig")
|
||||
t.Errorf("Answer section should have 1 RRSIG")
|
||||
}
|
||||
if !section(m.Ns, 2) {
|
||||
t.Errorf("authority section should have 1 sig")
|
||||
t.Errorf("Authority section should have 1 RRSIG")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ func TestSigningDifferentZone(t *testing.T) {
|
||||
|
||||
key, err := ParseKeyFile(fPub, fPriv)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to parse key: %v\n", err)
|
||||
t.Fatalf("Failed to parse key: %v\n", err)
|
||||
}
|
||||
|
||||
m := testMsgEx()
|
||||
@@ -73,11 +73,11 @@ func TestSigningDifferentZone(t *testing.T) {
|
||||
d := New([]string{"example.org."}, []*DNSKEY{key}, nil, c)
|
||||
m = d.Sign(state, "example.org.", time.Now().UTC())
|
||||
if !section(m.Answer, 1) {
|
||||
t.Errorf("answer section should have 1 sig")
|
||||
t.Errorf("Answer section should have 1 RRSIG")
|
||||
t.Logf("%+v\n", m)
|
||||
}
|
||||
if !section(m.Ns, 1) {
|
||||
t.Errorf("authority section should have 1 sig")
|
||||
t.Errorf("Authority section should have 1 RRSIG")
|
||||
t.Logf("%+v\n", m)
|
||||
}
|
||||
}
|
||||
@@ -91,7 +91,7 @@ func TestSigningCname(t *testing.T) {
|
||||
state := request.Request{Req: m}
|
||||
m = d.Sign(state, "miek.nl.", time.Now().UTC())
|
||||
if !section(m.Answer, 1) {
|
||||
t.Errorf("answer section should have 1 sig")
|
||||
t.Errorf("Answer section should have 1 RRSIG")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,12 +103,24 @@ func TestZoneSigningDelegation(t *testing.T) {
|
||||
m := testDelegationMsg()
|
||||
state := request.Request{Req: m}
|
||||
m = d.Sign(state, "miek.nl.", time.Now().UTC())
|
||||
if !section(m.Ns, 0) {
|
||||
t.Errorf("authority section should have 0 sig")
|
||||
if !section(m.Ns, 1) {
|
||||
t.Errorf("Authority section should have 1 RRSIG")
|
||||
t.Logf("%v\n", m)
|
||||
}
|
||||
|
||||
ds := 0
|
||||
for i := range m.Ns {
|
||||
if _, ok := m.Ns[i].(*dns.DS); ok {
|
||||
ds++
|
||||
}
|
||||
}
|
||||
if ds != 1 {
|
||||
t.Errorf("Authority section should have 1 DS")
|
||||
t.Logf("%v\n", m)
|
||||
|
||||
}
|
||||
if !section(m.Extra, 0) {
|
||||
t.Errorf("answer section should have 0 sig")
|
||||
t.Errorf("Answer section should have 0 RRSIGs")
|
||||
t.Logf("%v\n", m)
|
||||
}
|
||||
}
|
||||
@@ -123,7 +135,7 @@ func TestSigningDname(t *testing.T) {
|
||||
// We sign *everything* we see, also the synthesized CNAME.
|
||||
m = d.Sign(state, "miek.nl.", time.Now().UTC())
|
||||
if !section(m.Answer, 3) {
|
||||
t.Errorf("answer section should have 3 sig")
|
||||
t.Errorf("Answer section should have 3 RRSIGs")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,7 +149,7 @@ func TestSigningEmpty(t *testing.T) {
|
||||
state := request.Request{Req: m}
|
||||
m = d.Sign(state, "miek.nl.", time.Now().UTC())
|
||||
if !section(m.Ns, 2) {
|
||||
t.Errorf("authority section should have 2 sig")
|
||||
t.Errorf("Authority section should have 2 RRSIGs")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,8 +22,7 @@ func (d *ResponseWriter) WriteMsg(res *dns.Msg) error {
|
||||
// which zone it should be.
|
||||
state := request.Request{W: d.ResponseWriter, Req: res}
|
||||
|
||||
qname := state.Name()
|
||||
zone := plugin.Zones(d.d.zones).Matches(qname)
|
||||
zone := plugin.Zones(d.d.zones).Matches(state.Name())
|
||||
if zone == "" {
|
||||
return d.ResponseWriter.WriteMsg(res)
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ func (k *DNSKEY) newRRSIG(signerName string, ttl, incep, expir uint32) *dns.RRSI
|
||||
|
||||
sig.Hdr.Rrtype = dns.TypeRRSIG
|
||||
sig.Algorithm = k.K.Algorithm
|
||||
sig.KeyTag = k.keytag
|
||||
sig.KeyTag = k.tag
|
||||
sig.SignerName = signerName
|
||||
sig.Hdr.Ttl = ttl
|
||||
sig.OrigTtl = origTTL
|
||||
|
||||
@@ -77,7 +77,7 @@ func dnssecParse(c *caddy.Controller) ([]string, []*DNSKEY, int, error) {
|
||||
zones[i] = plugin.Host(zones[i]).Normalize()
|
||||
}
|
||||
|
||||
// Check if each keys owner name can actually sign the zones we want them to sign
|
||||
// Check if each keys owner name can actually sign the zones we want them to sign.
|
||||
for _, k := range keys {
|
||||
kname := plugin.Name(k.K.Header().Name)
|
||||
ok := false
|
||||
@@ -88,7 +88,7 @@ func dnssecParse(c *caddy.Controller) ([]string, []*DNSKEY, int, error) {
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
return zones, keys, capacity, fmt.Errorf("key %s (keyid: %d) can not sign any of the zones", string(kname), k.keytag)
|
||||
return zones, keys, capacity, fmt.Errorf("key %s (keyid: %d) can not sign any of the zones", string(kname), k.tag)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user