mirror of
				https://github.com/coredns/coredns.git
				synced 2025-10-30 17:53:21 -04:00 
			
		
		
		
	plugin/sign: fix signing of authoritative data (#3479)
Don't sign data we are not authoritative for. This adds an AuthWalk which skips names we should not authoritative for. Adds a few tests to check this is the case. Generates zones have been compared to dnssec-signzone. A number of changes have been made: * don't add DS records to the apex * NSEC TTL is the SOA's minttl value (copying bind9) * Various cleanups * signer struct was cleaned up: doesn't need ttl, nor expiration or inception. * plugin/sign: remove apex stuff from names() This is never used because we will always have other types in the apex, because we *ADD* them ourselves, before we sign (DNSKEY, CDS and CDNSKEY). Signed-off-by: Miek Gieben <miek@miek.nl> Co-Authored-By: Chris O'Haver <cohaver@infoblox.com>
This commit is contained in:
		
							
								
								
									
										58
									
								
								plugin/file/tree/auth_walk.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								plugin/file/tree/auth_walk.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | ||||
| package tree | ||||
|  | ||||
| import ( | ||||
| 	"github.com/miekg/dns" | ||||
| ) | ||||
|  | ||||
| // AuthWalk performs fn on all authoritative values stored in the tree in | ||||
| // pre-order depth first. If a non-nil error is returned the AuthWalk was interrupted | ||||
| // by an fn returning that error. If fn alters stored values' sort | ||||
| // relationships, future tree operation behaviors are undefined. | ||||
| // | ||||
| // The fn function will be called with 3 arguments, the current element, a map containing all | ||||
| // the RRs for this element and a boolean if this name is considered authoritative. | ||||
| func (t *Tree) AuthWalk(fn func(*Elem, map[uint16][]dns.RR, bool) error) error { | ||||
| 	if t.Root == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return t.Root.authwalk(make(map[string]struct{}), fn) | ||||
| } | ||||
|  | ||||
| func (n *Node) authwalk(ns map[string]struct{}, fn func(*Elem, map[uint16][]dns.RR, bool) error) error { | ||||
| 	if n.Left != nil { | ||||
| 		if err := n.Left.authwalk(ns, fn); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Check if the current name is a subdomain of *any* of the delegated names we've seen, if so, skip this name. | ||||
| 	// The ordering of the tree and how we walk if guarantees we see parents first. | ||||
| 	if n.Elem.Type(dns.TypeNS) != nil { | ||||
| 		ns[n.Elem.Name()] = struct{}{} | ||||
| 	} | ||||
|  | ||||
| 	auth := true | ||||
| 	i := 0 | ||||
| 	for { | ||||
| 		j, end := dns.NextLabel(n.Elem.Name(), i) | ||||
| 		if end { | ||||
| 			break | ||||
| 		} | ||||
| 		if _, ok := ns[n.Elem.Name()[j:]]; ok { | ||||
| 			auth = false | ||||
| 			break | ||||
| 		} | ||||
| 		i++ | ||||
| 	} | ||||
|  | ||||
| 	if err := fn(n.Elem, n.Elem.m, auth); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	if n.Right != nil { | ||||
| 		if err := n.Right.authwalk(ns, fn); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| @@ -2,25 +2,28 @@ package tree | ||||
|  | ||||
| import "github.com/miekg/dns" | ||||
|  | ||||
| // Walk performs fn on all values stored in the tree. If a non-nil error is returned the | ||||
| // Walk was interrupted by an fn returning that error. If fn alters stored values' sort | ||||
| // Walk performs fn on all authoritative values stored in the tree in | ||||
| // in-order depth first. If a non-nil error is returned the Walk was interrupted | ||||
| // by an fn returning that error. If fn alters stored values' sort | ||||
| // relationships, future tree operation behaviors are undefined. | ||||
| func (t *Tree) Walk(fn func(e *Elem, rrs map[uint16][]dns.RR) error) error { | ||||
| func (t *Tree) Walk(fn func(*Elem, map[uint16][]dns.RR) error) error { | ||||
| 	if t.Root == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return t.Root.walk(fn) | ||||
| } | ||||
|  | ||||
| func (n *Node) walk(fn func(e *Elem, rrs map[uint16][]dns.RR) error) error { | ||||
| func (n *Node) walk(fn func(*Elem, map[uint16][]dns.RR) error) error { | ||||
| 	if n.Left != nil { | ||||
| 		if err := n.Left.walk(fn); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if err := fn(n.Elem, n.Elem.m); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	if n.Right != nil { | ||||
| 		if err := n.Right.walk(fn); err != nil { | ||||
| 			return err | ||||
|   | ||||
| @@ -9,8 +9,7 @@ | ||||
| The *sign* plugin is used to sign (see RFC 6781) zones. In this process DNSSEC resource records are | ||||
| added. The signatures that sign the resource records sets have an expiration date, this means the | ||||
| signing process must be repeated before this expiration data is reached. Otherwise the zone's data | ||||
| will go BAD (RFC 4035, Section 5.5). The *sign* plugin takes care of this. *Sign* works, but has | ||||
| a couple of limitations, see the "Bugs" section. | ||||
| will go BAD (RFC 4035, Section 5.5). The *sign* plugin takes care of this. | ||||
|  | ||||
| Only NSEC is supported, *sign* does not support NSEC3. | ||||
|  | ||||
| @@ -32,17 +31,21 @@ it do key or algorithm rollovers - it just signs. | ||||
|  | ||||
|     Both these dates are only checked on the SOA's signature(s). | ||||
|  | ||||
|  *  Create signatures that have an inception of -3 hours (minus a jitter between 0 and 18 hours) | ||||
|  *  Create RRSIGs that have an inception of -3 hours (minus a jitter between 0 and 18 hours) | ||||
|     and a expiration of +32 days for every given DNSKEY. | ||||
|  | ||||
|  *  Add NSEC records for all names in the zone. The TTL for these is the negative cache TTL from the | ||||
|     SOA record. | ||||
|  | ||||
|  *  Add or replace *all* apex CDS/CDNSKEY records with the ones derived from the given keys. For | ||||
|     each key two CDS are created one with SHA1 and another with SHA256. | ||||
|  | ||||
|  *  Update the SOA's serial number to the *Unix epoch* of when the signing happens. This will | ||||
|     overwrite *any* previous serial number. | ||||
|  | ||||
| Thus there are two ways that dictate when a zone is signed. Normally every 6 days (plus jitter) it | ||||
| will be resigned. If for some reason we fail this check, the 14 days before expiring kicks in. | ||||
|  | ||||
| There are two ways that dictate when a zone is signed. Normally every 6 days (plus jitter) it will | ||||
| be resigned. If for some reason we fail this check, the 14 days before expiring kicks in. | ||||
|  | ||||
| Keys are named (following BIND9): `K<name>+<alg>+<id>.key` and `K<name>+<alg>+<id>.private`. | ||||
| The keys **must not** be included in your zone; they will be added by *sign*. These keys can be | ||||
| @@ -144,8 +147,8 @@ example.org example.net { | ||||
| This will lead to `db.example.org` be signed *twice*, as this entire section is parsed twice because | ||||
| you have specified the origins `example.org` and `example.net` in the server block. | ||||
|  | ||||
| Forcibly resigning a zone can be accomplished by removing the signed zone file (CoreDNS will keep on | ||||
| serving it from memory), and sending SIGUSR1 to the process to make it reload and resign the zone | ||||
| Forcibly resigning a zone can be accomplished by removing the signed zone file (CoreDNS will keep | ||||
| on serving it from memory), and sending SIGUSR1 to the process to make it reload and resign the zone | ||||
| file. | ||||
|  | ||||
| ## Also See | ||||
| @@ -153,9 +156,13 @@ file. | ||||
| The DNSSEC RFCs: RFC 4033, RFC 4034 and RFC 4035. And the BCP on DNSSEC, RFC 6781. Further more the | ||||
| manual pages coredns-keygen(1) and dnssec-keygen(8). And the *file* plugin's documentation. | ||||
|  | ||||
| Coredns-keygen can be found at <https://github.com/coredns/coredns-utils> in the coredns-keygen directory. | ||||
| Coredns-keygen can be found at | ||||
| [https://github.com/coredns/coredns-utils](https://github.com/coredns/coredns-utils) in the | ||||
| coredns-keygen directory. | ||||
|  | ||||
| Other useful DNSSEC tools can be found in [ldns](https://nlnetlabs.nl/projects/ldns/about/), e.g. | ||||
| `ldns-key2ds` to create DS records from DNSKEYs. | ||||
|  | ||||
| ## Bugs | ||||
|  | ||||
| `keys directory` is not implemented. Glue records are currently signed, and no DS records are added | ||||
| for child zones. | ||||
| `keys directory` is not implemented. | ||||
|   | ||||
| @@ -9,24 +9,19 @@ import ( | ||||
| 	"github.com/miekg/dns" | ||||
| ) | ||||
|  | ||||
| // names returns the elements of the zone in nsec order. If the returned boolean is true there were | ||||
| // no other apex records than SOA and NS, which are stored separately. | ||||
| func names(origin string, z *file.Zone) ([]string, bool) { | ||||
| 	// if there are no apex records other than NS and SOA we'll miss the origin | ||||
| 	// in this list. Check the first element and if not origin prepend it. | ||||
| // names returns the elements of the zone in nsec order. | ||||
| func names(origin string, z *file.Zone) []string { | ||||
| 	// There will also be apex records other than NS and SOA (who are kept separate), as we | ||||
| 	// are adding DNSKEY and CDS/CDNSKEY records in the apex *before* we sign. | ||||
| 	n := []string{} | ||||
| 	z.Walk(func(e *tree.Elem, _ map[uint16][]dns.RR) error { | ||||
| 	z.AuthWalk(func(e *tree.Elem, _ map[uint16][]dns.RR, auth bool) error { | ||||
| 		if !auth { | ||||
| 			return nil | ||||
| 		} | ||||
| 		n = append(n, e.Name()) | ||||
| 		return nil | ||||
| 	}) | ||||
| 	if len(n) == 0 { | ||||
| 		return nil, false | ||||
| 	} | ||||
| 	if n[0] != origin { | ||||
| 		n = append([]string{origin}, n...) | ||||
| 		return n, true | ||||
| 	} | ||||
| 	return n, false | ||||
| 	return n | ||||
| } | ||||
|  | ||||
| // NSEC returns an NSEC record according to name, next, ttl and bitmap. Note that the bitmap is sorted before use. | ||||
|   | ||||
							
								
								
									
										27
									
								
								plugin/sign/nsec_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								plugin/sign/nsec_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| package sign | ||||
|  | ||||
| import ( | ||||
| 	"os" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/coredns/coredns/plugin/file" | ||||
| ) | ||||
|  | ||||
| func TestNames(t *testing.T) { | ||||
| 	f, err := os.Open("testdata/db.miek.nl_ns") | ||||
| 	if err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
| 	z, err := file.Parse(f, "db.miek.nl_ns", "miek.nl", 0) | ||||
| 	if err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
|  | ||||
| 	names := names("miek.nl.", z) | ||||
| 	expected := []string{"miek.nl.", "child.miek.nl.", "www.miek.nl."} | ||||
| 	for i := range names { | ||||
| 		if names[i] != expected[i] { | ||||
| 			t.Errorf("Expected %s, got %s", expected[i], names[i]) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -13,7 +13,6 @@ func TestResignInception(t *testing.T) { | ||||
| 	if x := resign(zr, then); x != nil { | ||||
| 		t.Errorf("Expected RRSIG to be valid for %s, got invalid: %s", then.Format(timeFmt), x) | ||||
| 	} | ||||
|  | ||||
| 	// inception starts after this date. | ||||
| 	zr = strings.NewReader(`miek.nl.	1800	IN	RRSIG	SOA 13 2 1800 20190808191936 20190731161936 59725 miek.nl. eU6gI1OkSEbyt`) | ||||
| 	if x := resign(zr, then); x == nil { | ||||
| @@ -33,7 +32,6 @@ func TestResignExpire(t *testing.T) { | ||||
| 	if x := resign(zr, then); x != nil { | ||||
| 		t.Errorf("Expected RRSIG to be valid for %s, got invalid: %s", then.Format(timeFmt), x) | ||||
| 	} | ||||
|  | ||||
| 	// expired yesterday | ||||
| 	zr = strings.NewReader(`miek.nl.	1800	IN	RRSIG	SOA 13 2 1800 20190721191936 20190717161936 59725 miek.nl. eU6gI1OkSEbyt`) | ||||
| 	if x := resign(zr, then); x == nil { | ||||
|   | ||||
| @@ -26,10 +26,6 @@ type Signer struct { | ||||
|  | ||||
| 	signedfile string | ||||
| 	stop       chan struct{} | ||||
|  | ||||
| 	expiration uint32 | ||||
| 	inception  uint32 | ||||
| 	ttl        uint32 | ||||
| } | ||||
|  | ||||
| // Sign signs a zone file according to the parameters in s. | ||||
| @@ -44,46 +40,31 @@ func (s *Signer) Sign(now time.Time) (*file.Zone, error) { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	s.inception, s.expiration = lifetime(now, s.jitter) | ||||
|  | ||||
| 	s.ttl = z.Apex.SOA.Header().Ttl | ||||
| 	mttl := z.Apex.SOA.Minttl | ||||
| 	ttl := z.Apex.SOA.Header().Ttl | ||||
| 	inception, expiration := lifetime(now, s.jitter) | ||||
| 	z.Apex.SOA.Serial = uint32(now.Unix()) | ||||
|  | ||||
| 	for _, pair := range s.keys { | ||||
| 		pair.Public.Header().Ttl = s.ttl // set TTL on key so it matches the RRSIG. | ||||
| 		pair.Public.Header().Ttl = ttl // set TTL on key so it matches the RRSIG. | ||||
| 		z.Insert(pair.Public) | ||||
| 		z.Insert(pair.Public.ToDS(dns.SHA1)) | ||||
| 		z.Insert(pair.Public.ToDS(dns.SHA256)) | ||||
| 		z.Insert(pair.Public.ToDS(dns.SHA1).ToCDS()) | ||||
| 		z.Insert(pair.Public.ToDS(dns.SHA256).ToCDS()) | ||||
| 		z.Insert(pair.Public.ToCDNSKEY()) | ||||
| 	} | ||||
|  | ||||
| 	names, apex := names(s.origin, z) | ||||
| 	names := names(s.origin, z) | ||||
| 	ln := len(names) | ||||
|  | ||||
| 	var nsec *dns.NSEC | ||||
| 	if apex { | ||||
| 		nsec = NSEC(s.origin, names[(ln+1)%ln], s.ttl, []uint16{dns.TypeSOA, dns.TypeNS, dns.TypeRRSIG, dns.TypeNSEC}) | ||||
| 		z.Insert(nsec) | ||||
| 	} | ||||
|  | ||||
| 	for _, pair := range s.keys { | ||||
| 		rrsig, err := pair.signRRs([]dns.RR{z.Apex.SOA}, s.origin, s.ttl, s.inception, s.expiration) | ||||
| 		rrsig, err := pair.signRRs([]dns.RR{z.Apex.SOA}, s.origin, ttl, inception, expiration) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		z.Insert(rrsig) | ||||
| 		// NS apex may not be set if RR's have been discarded because the origin doesn't match. | ||||
| 		if len(z.Apex.NS) > 0 { | ||||
| 			rrsig, err = pair.signRRs(z.Apex.NS, s.origin, s.ttl, s.inception, s.expiration) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			z.Insert(rrsig) | ||||
| 		} | ||||
| 		if apex { | ||||
| 			rrsig, err = pair.signRRs([]dns.RR{nsec}, s.origin, s.ttl, s.inception, s.expiration) | ||||
| 			rrsig, err = pair.signRRs(z.Apex.NS, s.origin, ttl, inception, expiration) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| @@ -93,21 +74,27 @@ func (s *Signer) Sign(now time.Time) (*file.Zone, error) { | ||||
|  | ||||
| 	// We are walking the tree in the same direction, so names[] can be used here to indicated the next element. | ||||
| 	i := 1 | ||||
| 	err = z.Walk(func(e *tree.Elem, zrrs map[uint16][]dns.RR) error { | ||||
| 		if !apex && e.Name() == s.origin { | ||||
| 			nsec := NSEC(e.Name(), names[(ln+i)%ln], s.ttl, append(e.Types(), dns.TypeNS, dns.TypeSOA, dns.TypeNSEC, dns.TypeRRSIG)) | ||||
| 	err = z.AuthWalk(func(e *tree.Elem, zrrs map[uint16][]dns.RR, auth bool) error { | ||||
| 		if !auth { | ||||
| 			return nil | ||||
| 		} | ||||
|  | ||||
| 		if e.Name() == s.origin { | ||||
| 			nsec := NSEC(e.Name(), names[(ln+i)%ln], mttl, append(e.Types(), dns.TypeNS, dns.TypeSOA, dns.TypeRRSIG, dns.TypeNSEC)) | ||||
| 			z.Insert(nsec) | ||||
| 		} else { | ||||
| 			nsec := NSEC(e.Name(), names[(ln+i)%ln], s.ttl, append(e.Types(), dns.TypeNSEC, dns.TypeRRSIG)) | ||||
| 			nsec := NSEC(e.Name(), names[(ln+i)%ln], mttl, append(e.Types(), dns.TypeRRSIG, dns.TypeNSEC)) | ||||
| 			z.Insert(nsec) | ||||
| 		} | ||||
|  | ||||
| 		for t, rrs := range zrrs { | ||||
| 			if t == dns.TypeRRSIG { | ||||
| 			// RRSIGs are not signed and NS records are not signed because we are never authoratiative for them. | ||||
| 			// The zone's apex nameservers records are not kept in this tree and are signed separately. | ||||
| 			if t == dns.TypeRRSIG || t == dns.TypeNS { | ||||
| 				continue | ||||
| 			} | ||||
| 			for _, pair := range s.keys { | ||||
| 				rrsig, err := pair.signRRs(rrs, s.origin, s.ttl, s.inception, s.expiration) | ||||
| 				rrsig, err := pair.signRRs(rrs, s.origin, rrs[0].Header().Ttl, inception, expiration) | ||||
| 				if err != nil { | ||||
| 					return err | ||||
| 				} | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| package sign | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"io/ioutil" | ||||
| 	"os" | ||||
| 	"testing" | ||||
| @@ -29,8 +30,8 @@ func TestSign(t *testing.T) { | ||||
| 	} | ||||
|  | ||||
| 	apex, _ := z.Search("miek.nl.") | ||||
| 	if x := apex.Type(dns.TypeDS); len(x) != 2 { | ||||
| 		t.Errorf("Expected %d DS records, got %d", 2, len(x)) | ||||
| 	if x := apex.Type(dns.TypeDS); len(x) != 0 { | ||||
| 		t.Errorf("Expected %d DS records, got %d", 0, len(x)) | ||||
| 	} | ||||
| 	if x := apex.Type(dns.TypeCDS); len(x) != 2 { | ||||
| 		t.Errorf("Expected %d CDS records, got %d", 2, len(x)) | ||||
| @@ -75,14 +76,14 @@ $ORIGIN example.org. | ||||
| 	if x := nsec[0].(*dns.NSEC).NextDomain; x != "example.org." { | ||||
| 		t.Errorf("Expected NSEC NextDomain %s, got %s", "example.org.", x) | ||||
| 	} | ||||
| 	if x := nsec[0].(*dns.NSEC).TypeBitMap; len(x) != 8 { | ||||
| 		t.Errorf("Expected NSEC bitmap to be %d elements, got %d", 8, x) | ||||
| 	if x := nsec[0].(*dns.NSEC).TypeBitMap; len(x) != 7 { | ||||
| 		t.Errorf("Expected NSEC bitmap to be %d elements, got %d", 7, x) | ||||
| 	} | ||||
| 	if x := nsec[0].(*dns.NSEC).TypeBitMap; x[7] != dns.TypeCDNSKEY { | ||||
| 		t.Errorf("Expected NSEC bitmap element 6 to be %d, got %d", dns.TypeCDNSKEY, x[7]) | ||||
| 	if x := nsec[0].(*dns.NSEC).TypeBitMap; x[6] != dns.TypeCDNSKEY { | ||||
| 		t.Errorf("Expected NSEC bitmap element 5 to be %d, got %d", dns.TypeCDNSKEY, x[6]) | ||||
| 	} | ||||
| 	if x := nsec[0].(*dns.NSEC).TypeBitMap; x[5] != dns.TypeDNSKEY { | ||||
| 		t.Errorf("Expected NSEC bitmap element 5 to be %d, got %d", dns.TypeDNSKEY, x[5]) | ||||
| 	if x := nsec[0].(*dns.NSEC).TypeBitMap; x[4] != dns.TypeDNSKEY { | ||||
| 		t.Errorf("Expected NSEC bitmap element 4 to be %d, got %d", dns.TypeDNSKEY, x[4]) | ||||
| 	} | ||||
| 	dnskey := el.Type(dns.TypeDNSKEY) | ||||
| 	if x := dnskey[0].Header().Ttl; x != 1800 { | ||||
| @@ -100,3 +101,82 @@ $ORIGIN example.org. | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestSignGlue(t *testing.T) { | ||||
| 	input := `sign testdata/db.miek.nl miek.nl { | ||||
|                key file testdata/Kmiek.nl.+013+59725 | ||||
|                directory testdata | ||||
|        }` | ||||
| 	c := caddy.NewTestController("dns", input) | ||||
| 	sign, err := parse(c) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 	if len(sign.signers) != 1 { | ||||
| 		t.Fatalf("Expected 1 signer, got %d", len(sign.signers)) | ||||
| 	} | ||||
| 	z, err := sign.signers[0].Sign(time.Now().UTC()) | ||||
| 	if err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
|  | ||||
| 	e, _ := z.Search("ns2.bla.miek.nl.") | ||||
| 	sigs := e.Type(dns.TypeRRSIG) | ||||
| 	if len(sigs) != 0 { | ||||
| 		t.Errorf("Expected no RRSIG for %s, got %d", "ns2.bla.miek.nl.", len(sigs)) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestSignDS(t *testing.T) { | ||||
| 	input := `sign testdata/db.miek.nl_ns miek.nl { | ||||
|                key file testdata/Kmiek.nl.+013+59725 | ||||
|                directory testdata | ||||
|        }` | ||||
| 	c := caddy.NewTestController("dns", input) | ||||
| 	sign, err := parse(c) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 	if len(sign.signers) != 1 { | ||||
| 		t.Fatalf("Expected 1 signer, got %d", len(sign.signers)) | ||||
| 	} | ||||
| 	z, err := sign.signers[0].Sign(time.Now().UTC()) | ||||
| 	if err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
|  | ||||
| 	// dnssec-signzone outputs this for db.miek.nl_ns: | ||||
| 	// | ||||
| 	// child.miek.nl.	1800	IN	NS	ns.child.miek.nl. | ||||
| 	// child.miek.nl.	1800	IN	DS	34385 13 2 fc7397c77afbccb6742fc.... | ||||
| 	// child.miek.nl.	1800	IN	RRSIG	DS 13 3 1800 20191223121229 20191123121229 59725 miek.nl. ZwptLzVVs.... | ||||
| 	// child.miek.nl.	14400	IN	NSEC	www.miek.nl. NS DS RRSIG NSEC | ||||
| 	// child.miek.nl.	14400	IN	RRSIG	NSEC 13 3 14400 20191223121229 20191123121229 59725 miek.nl. w+CcA8... | ||||
|  | ||||
| 	name := "child.miek.nl." | ||||
| 	e, _ := z.Search(name) | ||||
| 	if x := len(e.Types()); x != 4 { // NS DS NSEC and 2x RRSIG | ||||
| 		t.Errorf("Expected 4 records for %s, got %d", name, x) | ||||
| 	} | ||||
|  | ||||
| 	ds := e.Type(dns.TypeDS) | ||||
| 	if len(ds) != 1 { | ||||
| 		t.Errorf("Expected DS for %s, got %d", name, len(ds)) | ||||
| 	} | ||||
| 	sigs := e.Type(dns.TypeRRSIG) | ||||
| 	if len(sigs) != 2 { | ||||
| 		t.Errorf("Expected no RRSIG for %s, got %d", name, len(sigs)) | ||||
| 	} | ||||
| 	nsec := e.Type(dns.TypeNSEC) | ||||
| 	if x := nsec[0].(*dns.NSEC).NextDomain; x != "www.miek.nl." { | ||||
| 		t.Errorf("Expected no NSEC NextDomain to be %s for %s, got %s", "www.miek.nl.", name, x) | ||||
| 	} | ||||
| 	minttl := z.Apex.SOA.Minttl | ||||
| 	if x := nsec[0].Header().Ttl; x != minttl { | ||||
| 		t.Errorf("Expected no NSEC TTL to be %d for %s, got %d", minttl, "www.miek.nl.", x) | ||||
| 	} | ||||
| 	// print zone on error | ||||
| 	buf := &bytes.Buffer{} | ||||
| 	write(buf, z) | ||||
| 	t.Logf("%s\n", buf) | ||||
| } | ||||
|   | ||||
							
								
								
									
										10
									
								
								plugin/sign/testdata/db.miek.nl_ns
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								plugin/sign/testdata/db.miek.nl_ns
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| $TTL    30M | ||||
| $ORIGIN miek.nl. | ||||
| @       IN      SOA     linode.atoom.net. miek.miek.nl. ( 1282630060 4H 1H 7D 4H ) | ||||
|                 NS      linode.atoom.net. | ||||
| 		DNSKEY  257 3 13 sfzRg5nDVxbeUc51su4MzjgwpOpUwnuu81SlRHqJuXe3SOYOeypR69tZ52XLmE56TAmPHsiB8Rgk+NTpf0o1Cw== | ||||
|  | ||||
| www 	        AAAA    ::1 | ||||
| child           NS      ns.child | ||||
| ns.child        AAAA    ::1 | ||||
| child	        DS	34385 13 2 fc7397c77afbccb6742fcff19c7b1410d0044661e7085fc200ae1ab3d15a5842 | ||||
		Reference in New Issue
	
	Block a user