mirror of https://github.com/k3s-io/k3s
178 lines
4.6 KiB
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
|
|
}
|