1
// Copyright (c) 2013 Erik St. Martin, Brian Ketelsen. All rights reserved.
2
// Use of this source code is governed by The MIT License (MIT) that can be
3
// found in the LICENSE file.
14
"github.com/miekg/dns"
15
"github.com/skynetservices/skydns/cache"
18
// ParseKeyFile read a DNSSEC keyfile as generated by dnssec-keygen or other
19
// utilities. It add ".key" for the public key and ".private" for the private key.
20
func ParseKeyFile(file string) (*dns.DNSKEY, crypto.Signer, error) {
21
f, e := os.Open(file + ".key")
25
k, e := dns.ReadRR(f, file+".key")
29
f, e = os.Open(file + ".private")
33
p, e := k.(*dns.DNSKEY).ReadPrivateKey(f, file+".private")
38
if v, ok := p.(*rsa.PrivateKey); ok {
39
return k.(*dns.DNSKEY), v, nil
41
if v, ok := p.(*ecdsa.PrivateKey); ok {
42
return k.(*dns.DNSKEY), v, nil
44
return k.(*dns.DNSKEY), nil, nil
47
// Sign signs a message m, it takes care of negative or nodata responses as
48
// well by synthesising NSEC3 records. It will also cache the signatures, using
49
// a hash of the signed data as a key.
50
// We also fake the origin TTL in the signature, because we don't want to
51
// throw away signatures when services decide to have longer TTL. So we just
52
// set the origTTL to 60.
53
// TODO(miek): revisit origTTL
54
func (s *server) Sign(m *dns.Msg, bufsize uint16) {
55
now := time.Now().UTC()
56
incep := uint32(now.Add(-3 * time.Hour).Unix()) // 2+1 hours, be sure to catch daylight saving time and such
57
expir := uint32(now.Add(7 * 24 * time.Hour).Unix()) // sign for a week
59
for _, r := range rrSets(m.Answer) {
60
if r[0].Header().Rrtype == dns.TypeRRSIG {
63
if !dns.IsSubDomain(s.config.Domain, r[0].Header().Name) {
66
if sig, err := s.signSet(r, now, incep, expir); err == nil {
67
m.Answer = append(m.Answer, sig)
70
for _, r := range rrSets(m.Ns) {
71
if r[0].Header().Rrtype == dns.TypeRRSIG {
74
if !dns.IsSubDomain(s.config.Domain, r[0].Header().Name) {
77
if sig, err := s.signSet(r, now, incep, expir); err == nil {
78
m.Ns = append(m.Ns, sig)
81
for _, r := range rrSets(m.Extra) {
82
if r[0].Header().Rrtype == dns.TypeRRSIG || r[0].Header().Rrtype == dns.TypeOPT {
85
if !dns.IsSubDomain(s.config.Domain, r[0].Header().Name) {
88
if sig, err := s.signSet(r, now, incep, expir); err == nil {
89
m.Extra = append(m.Extra, sig)
95
o.Hdr.Rrtype = dns.TypeOPT
97
o.SetUDPSize(4096) // TODO(miek): echo client
98
m.Extra = append(m.Extra, o)
102
func (s *server) signSet(r []dns.RR, now time.Time, incep, expir uint32) (*dns.RRSIG, error) {
103
key := cache.KeyRRset(r)
104
if m, exp, hit := s.scache.Search(key); hit { // There can only be one sig in this cache.
105
// Is it still valid 24 hours from now?
106
if now.Add(+24*time.Hour).Sub(exp) < -24*time.Hour {
107
return m.Answer[0].(*dns.RRSIG), nil
111
if s.config.Verbose {
112
logf("scache miss for %s type %d", r[0].Header().Name, r[0].Header().Rrtype)
115
StatsDnssecCacheMiss.Inc(1)
116
promCacheMiss.WithLabelValues("signature").Inc()
118
sig, err, shared := inflight.Do(key, func() (*dns.RRSIG, error) {
119
sig1 := s.NewRRSIG(incep, expir)
120
sig1.Header().Ttl = r[0].Header().Ttl
121
if r[0].Header().Rrtype == dns.TypeTXT {
124
e := sig1.Sign(s.config.PrivKey, r)
126
logf("failed to sign: %s", e.Error())
134
s.scache.InsertSignature(key, sig)
136
return dns.Copy(sig).(*dns.RRSIG), nil
139
func (s *server) NewRRSIG(incep, expir uint32) *dns.RRSIG {
140
sig := new(dns.RRSIG)
141
sig.Hdr.Rrtype = dns.TypeRRSIG
142
sig.Hdr.Ttl = s.config.Ttl
143
sig.OrigTtl = s.config.Ttl
144
sig.Algorithm = s.config.PubKey.Algorithm
145
sig.KeyTag = s.config.KeyTag
146
sig.Inception = incep
147
sig.Expiration = expir
148
sig.SignerName = s.config.PubKey.Hdr.Name
157
func rrSets(rrs []dns.RR) map[rrset][]dns.RR {
158
m := make(map[rrset][]dns.RR)
159
for _, r := range rrs {
160
if s, ok := m[rrset{r.Header().Name, r.Header().Rrtype}]; ok {
162
m[rrset{r.Header().Name, r.Header().Rrtype}] = s
164
s := make([]dns.RR, 1, 3)
166
m[rrset{r.Header().Name, r.Header().Rrtype}] = s