k3s/vendor/github.com/skynetservices/skydns/server/dnssec.go

178 lines
4.6 KiB
Go

// Copyright (c) 2013 Erik St. Martin, Brian Ketelsen. All rights reserved.
// Use of this source code is governed by The MIT License (MIT) that can be
// found in the LICENSE file.
package server
import (
"crypto"
"crypto/ecdsa"
"crypto/rsa"
"os"
"time"
"github.com/skynetservices/skydns/cache"
"github.com/skynetservices/skydns/metrics"
"github.com/skynetservices/skydns/singleflight"
"github.com/miekg/dns"
)
var (
inflight = &singleflight.Group{}
)
// ParseKeyFile read a DNSSEC keyfile as generated by dnssec-keygen or other
// utilities. It add ".key" for the public key and ".private" for the private key.
func ParseKeyFile(file string) (*dns.DNSKEY, crypto.Signer, error) {
f, e := os.Open(file + ".key")
if e != nil {
return nil, nil, e
}
k, e := dns.ReadRR(f, file+".key")
if e != nil {
return nil, nil, e
}
f, e = os.Open(file + ".private")
if e != nil {
return nil, nil, e
}
p, e := k.(*dns.DNSKEY).ReadPrivateKey(f, file+".private")
if e != nil {
return nil, nil, e
}
if v, ok := p.(*rsa.PrivateKey); ok {
return k.(*dns.DNSKEY), v, nil
}
if v, ok := p.(*ecdsa.PrivateKey); ok {
return k.(*dns.DNSKEY), v, nil
}
return k.(*dns.DNSKEY), nil, nil
}
// Sign signs a message m, it takes care of negative or nodata responses as
// well by synthesising NSEC3 records. It will also cache the signatures, using
// a hash of the signed data as a key.
// We also fake the origin TTL in the signature, because we don't want to
// throw away signatures when services decide to have longer TTL. So we just
// set the origTTL to 60.
// TODO(miek): revisit origTTL
func (s *server) Sign(m *dns.Msg, bufsize uint16) {
now := time.Now().UTC()
incep := uint32(now.Add(-3 * time.Hour).Unix()) // 2+1 hours, be sure to catch daylight saving time and such
expir := uint32(now.Add(7 * 24 * time.Hour).Unix()) // sign for a week
for _, r := range rrSets(m.Answer) {
if r[0].Header().Rrtype == dns.TypeRRSIG {
continue
}
if !dns.IsSubDomain(s.config.Domain, r[0].Header().Name) {
continue
}
if sig, err := s.signSet(r, now, incep, expir); err == nil {
m.Answer = append(m.Answer, sig)
}
}
for _, r := range rrSets(m.Ns) {
if r[0].Header().Rrtype == dns.TypeRRSIG {
continue
}
if !dns.IsSubDomain(s.config.Domain, r[0].Header().Name) {
continue
}
if sig, err := s.signSet(r, now, incep, expir); err == nil {
m.Ns = append(m.Ns, sig)
}
}
for _, r := range rrSets(m.Extra) {
if r[0].Header().Rrtype == dns.TypeRRSIG || r[0].Header().Rrtype == dns.TypeOPT {
continue
}
if !dns.IsSubDomain(s.config.Domain, r[0].Header().Name) {
continue
}
if sig, err := s.signSet(r, now, incep, expir); err == nil {
m.Extra = append(m.Extra, sig)
}
}
o := new(dns.OPT)
o.Hdr.Name = "."
o.Hdr.Rrtype = dns.TypeOPT
o.SetDo()
o.SetUDPSize(4096) // TODO(miek): echo client
m.Extra = append(m.Extra, o)
return
}
func (s *server) signSet(r []dns.RR, now time.Time, incep, expir uint32) (*dns.RRSIG, error) {
key := cache.KeyRRset(r)
if m, exp, hit := s.scache.Search(key); hit { // There can only be one sig in this cache.
// Is it still valid 24 hours from now?
if now.Add(+24*time.Hour).Sub(exp) < -24*time.Hour {
return m.Answer[0].(*dns.RRSIG), nil
}
s.scache.Remove(key)
}
if s.config.Verbose {
logf("scache miss for %s type %d", r[0].Header().Name, r[0].Header().Rrtype)
}
metrics.ReportCacheMiss("signature")
sig, err := inflight.Do(key, func() (interface{}, error) {
sig1 := s.NewRRSIG(incep, expir)
sig1.Header().Ttl = r[0].Header().Ttl
if r[0].Header().Rrtype == dns.TypeTXT {
sig1.OrigTtl = 0
}
e := sig1.Sign(s.config.PrivKey, r)
if e != nil {
logf("failed to sign: %s", e.Error())
}
return sig1, e
})
if err != nil {
return nil, err
}
s.scache.InsertSignature(key, sig.(*dns.RRSIG))
return dns.Copy(sig.(*dns.RRSIG)).(*dns.RRSIG), nil
}
func (s *server) NewRRSIG(incep, expir uint32) *dns.RRSIG {
sig := new(dns.RRSIG)
sig.Hdr.Rrtype = dns.TypeRRSIG
sig.Hdr.Ttl = s.config.Ttl
sig.OrigTtl = s.config.Ttl
sig.Algorithm = s.config.PubKey.Algorithm
sig.KeyTag = s.config.KeyTag
sig.Inception = incep
sig.Expiration = expir
sig.SignerName = s.config.PubKey.Hdr.Name
return sig
}
type rrset struct {
qname string
qtype uint16
}
func rrSets(rrs []dns.RR) map[rrset][]dns.RR {
m := make(map[rrset][]dns.RR)
for _, r := range rrs {
if s, ok := m[rrset{r.Header().Name, r.Header().Rrtype}]; ok {
s = append(s, r)
m[rrset{r.Header().Name, r.Header().Rrtype}] = s
} else {
s := make([]dns.RR, 1, 3)
s[0] = r
m[rrset{r.Header().Name, r.Header().Rrtype}] = s
}
}
if len(m) > 0 {
return m
}
return nil
}