mirror of
				https://github.com/coredns/coredns.git
				synced 2025-10-31 02:03:20 -04:00 
			
		
		
		
	middleware/file: add DNSSEC support (#697)
* middleware/file: add DNSSEC support Add tests for DNSSEC and check if everything is working. * add signatures * tweak * Add DNSSEC signing tests for DNAME * Just sign it all
This commit is contained in:
		| @@ -43,6 +43,7 @@ func (d Dnssec) Sign(state request.Request, zone string, now time.Time) *dns.Msg | |||||||
|  |  | ||||||
| 	mt, _ := response.Typify(req, time.Now().UTC()) // TODO(miek): need opt record here? | 	mt, _ := response.Typify(req, time.Now().UTC()) // TODO(miek): need opt record here? | ||||||
| 	if mt == response.Delegation { | 	if mt == response.Delegation { | ||||||
|  | 		// TODO(miek): uh, signing DS record?!?! | ||||||
| 		return req | 		return req | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -113,6 +113,20 @@ func TestZoneSigningDelegation(t *testing.T) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func TestSigningDname(t *testing.T) { | ||||||
|  | 	d, rm1, rm2 := newDnssec(t, []string{"miek.nl."}) | ||||||
|  | 	defer rm1() | ||||||
|  | 	defer rm2() | ||||||
|  |  | ||||||
|  | 	m := testMsgDname() | ||||||
|  | 	state := request.Request{Req: m} | ||||||
|  | 	// 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") | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| func section(rss []dns.RR, nrSigs int) bool { | func section(rss []dns.RR, nrSigs int) bool { | ||||||
| 	i := 0 | 	i := 0 | ||||||
| 	for _, r := range rss { | 	for _, r := range rss { | ||||||
| @@ -157,6 +171,16 @@ func testDelegationMsg() *dns.Msg { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func testMsgDname() *dns.Msg { | ||||||
|  | 	return &dns.Msg{ | ||||||
|  | 		Answer: []dns.RR{ | ||||||
|  | 			test.CNAME("a.dname.miek.nl.	1800	IN	CNAME	a.test.miek.nl."), | ||||||
|  | 			test.A("a.test.miek.nl.	1800	IN	A	139.162.196.78"), | ||||||
|  | 			test.DNAME("dname.miek.nl.	1800	IN	DNAME	test.miek.nl."), | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| func newDnssec(t *testing.T, zones []string) (Dnssec, func(), func()) { | func newDnssec(t *testing.T, zones []string) (Dnssec, func(), func()) { | ||||||
| 	k, rm1, rm2 := newKey(t) | 	k, rm1, rm2 := newKey(t) | ||||||
| 	cache, _ := lru.New(defaultCap) | 	cache, _ := lru.New(defaultCap) | ||||||
|   | |||||||
| @@ -130,6 +130,75 @@ func TestLookupDNAME(t *testing.T) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | var dnameDnssecTestCases = []test.Case{ | ||||||
|  | 	{ | ||||||
|  | 		// We have no auth section, because the test zone does not have nameservers. | ||||||
|  | 		Qname: "ns.example.org.", Qtype: dns.TypeA, | ||||||
|  | 		Answer: []dns.RR{ | ||||||
|  | 			test.A("ns.example.org.	1800	IN	A	127.0.0.1"), | ||||||
|  | 		}, | ||||||
|  | 	}, | ||||||
|  | 	{ | ||||||
|  | 		Qname: "dname.example.org.", Qtype: dns.TypeDNAME, | ||||||
|  | 		Do: true, | ||||||
|  | 		Answer: []dns.RR{ | ||||||
|  | 			test.DNAME("dname.example.org.	1800	IN	DNAME	test.example.org."), | ||||||
|  | 			test.RRSIG("dname.example.org.	1800	IN	RRSIG	DNAME 5 3 1800 20170702091734 20170602091734 54282 example.org. HvXtiBM="), | ||||||
|  | 		}, | ||||||
|  | 		Extra: []dns.RR{test.OPT(4096, true)}, | ||||||
|  | 	}, | ||||||
|  | 	{ | ||||||
|  | 		Qname: "a.dname.example.org.", Qtype: dns.TypeA, | ||||||
|  | 		Do: true, | ||||||
|  | 		Answer: []dns.RR{ | ||||||
|  | 			test.CNAME("a.dname.example.org.	1800	IN	CNAME	a.test.example.org."), | ||||||
|  | 			test.DNAME("dname.example.org.	1800	IN	DNAME	test.example.org."), | ||||||
|  | 			test.RRSIG("dname.example.org.	1800	IN	RRSIG	DNAME 5 3 1800 20170702091734 20170602091734 54282 example.org. HvXtiBM="), | ||||||
|  | 		}, | ||||||
|  | 		Extra: []dns.RR{test.OPT(4096, true)}, | ||||||
|  | 	}, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestLookupDNAMEDNSSEC(t *testing.T) { | ||||||
|  | 	zone, err := Parse(strings.NewReader(dbExampleDNAMESigned), testzone, "stdin") | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatalf("Expect no error when reading zone, got %q", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	fm := File{Next: test.ErrorHandler(), Zones: Zones{Z: map[string]*Zone{"example.org.": zone}, Names: []string{"example.org."}}} | ||||||
|  | 	ctx := context.TODO() | ||||||
|  |  | ||||||
|  | 	for _, tc := range dnameDnssecTestCases { | ||||||
|  | 		m := tc.Msg() | ||||||
|  |  | ||||||
|  | 		rec := dnsrecorder.New(&test.ResponseWriter{}) | ||||||
|  | 		_, err := fm.ServeDNS(ctx, rec, m) | ||||||
|  | 		if err != nil { | ||||||
|  | 			t.Errorf("Expected no error, got %v\n", err) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		resp := rec.Msg | ||||||
|  | 		sort.Sort(test.RRSet(resp.Answer)) | ||||||
|  | 		sort.Sort(test.RRSet(resp.Ns)) | ||||||
|  | 		sort.Sort(test.RRSet(resp.Extra)) | ||||||
|  |  | ||||||
|  | 		if !test.Header(t, tc, resp) { | ||||||
|  | 			t.Logf("%v\n", resp) | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		if !test.Section(t, tc, test.Answer, resp.Answer) { | ||||||
|  | 			t.Logf("%v\n", resp) | ||||||
|  | 		} | ||||||
|  | 		if !test.Section(t, tc, test.Ns, resp.Ns) { | ||||||
|  | 			t.Logf("%v\n", resp) | ||||||
|  | 		} | ||||||
|  | 		if !test.Section(t, tc, test.Extra, resp.Extra) { | ||||||
|  | 			t.Logf("%v\n", resp) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| const dbMiekNLDNAME = ` | const dbMiekNLDNAME = ` | ||||||
| $TTL    30M | $TTL    30M | ||||||
| $ORIGIN miek.nl. | $ORIGIN miek.nl. | ||||||
| @@ -157,3 +226,108 @@ dname           IN      DNAME   test | |||||||
| dname           IN      A       127.0.0.1 | dname           IN      A       127.0.0.1 | ||||||
| a.dname         IN      A       127.0.0.1 | a.dname         IN      A       127.0.0.1 | ||||||
| ` | ` | ||||||
|  |  | ||||||
|  | const dbExampleDNAMESigned = ` | ||||||
|  | ; File written on Fri Jun  2 10:17:34 2017 | ||||||
|  | ; dnssec_signzone version 9.10.3-P4-Debian | ||||||
|  | example.org.		1800	IN SOA	a.example.org. b.example.org. ( | ||||||
|  | 					1282630057 ; serial | ||||||
|  | 					14400      ; refresh (4 hours) | ||||||
|  | 					3600       ; retry (1 hour) | ||||||
|  | 					604800     ; expire (1 week) | ||||||
|  | 					14400      ; minimum (4 hours) | ||||||
|  | 					) | ||||||
|  | 			1800	RRSIG	SOA 5 2 1800 ( | ||||||
|  | 					20170702091734 20170602091734 54282 example.org. | ||||||
|  | 					mr5eQtFs1GubgwaCcqrpiF6Cgi822OkESPeV | ||||||
|  | 					X0OJYq3JzthJjHw8TfYAJWQ2yGqhlePHir9h | ||||||
|  | 					FT/uFZdYyytHq+qgIUbJ9IVCrq0gZISZdHML | ||||||
|  | 					Ry1DNffMR9CpD77KocOAUABfopcvH/3UGOHn | ||||||
|  | 					TFxkAr447zPaaoC68JYGxYLfZk8= ) | ||||||
|  | 			1800	NS	ns.example.org. | ||||||
|  | 			1800	RRSIG	NS 5 2 1800 ( | ||||||
|  | 					20170702091734 20170602091734 54282 example.org. | ||||||
|  | 					McM4UdMxkscVQkJnnEbdqwyjpPgq5a/EuOLA | ||||||
|  | 					r2MvG43/cwOaWULiZoNzLi5Rjzhf+GTeVTan | ||||||
|  | 					jw6EsL3gEuYI1nznwlLQ04/G0XAHjbq5VvJc | ||||||
|  | 					rlscBD+dzf774yfaTjRNoeo2xTem6S7nyYPW | ||||||
|  | 					Y+1f6xkrsQPLYJfZ6VZ9QqyupBw= ) | ||||||
|  | 			14400	NSEC	dname.example.org. NS SOA RRSIG NSEC DNSKEY | ||||||
|  | 			14400	RRSIG	NSEC 5 2 14400 ( | ||||||
|  | 					20170702091734 20170602091734 54282 example.org. | ||||||
|  | 					VT+IbjDFajM0doMKFipdX3+UXfCn3iHIxg5x | ||||||
|  | 					LElp4Q/YddTbX+6tZf53+EO+G8Kye3JDLwEl | ||||||
|  | 					o8VceijNeF3igZ+LiZuXCei5Qg/TJ7IAUnAO | ||||||
|  | 					xd85IWwEYwyKkKd6Z2kXbAN2pdcHE8EmboQd | ||||||
|  | 					wfTr9oyWhpZk1Z+pN8vdejPrG0M= ) | ||||||
|  | 			1800	DNSKEY	256 3 5 ( | ||||||
|  | 					AwEAAczLlmTk5bMXUzpBo/Jta6MWSZYy3Nfw | ||||||
|  | 					gz8t/pkfSh4IlFF6vyXZhEqCeQsCBdD7ltkD | ||||||
|  | 					h5qd4A+nFrYOMwsi5XIjoHMlJN15xwFS9EgS | ||||||
|  | 					ZrZmuxePIEiYB5KccEf9JQMgM1t07Iu1FnrY | ||||||
|  | 					02OuAqGWcO4tuyTLaK3QP4MLQOfAgKqf | ||||||
|  | 					) ; ZSK; alg = RSASHA1; key id = 54282 | ||||||
|  | 			1800	RRSIG	DNSKEY 5 2 1800 ( | ||||||
|  | 					20170702091734 20170602091734 54282 example.org. | ||||||
|  | 					MBgSRtZ6idJblLIHxZWpWL/1oqIwImb1mkl7 | ||||||
|  | 					hDFxqV6Hw19yLX06P7gcJEWiisdZBkVEfcOK | ||||||
|  | 					LeMJly05vgKfrMzLgIu2Ry4bL8AMKc8NMXBG | ||||||
|  | 					b1VDCEBW69P2omogj2KnORHDCZQr/BX9+wBU | ||||||
|  | 					5rIMTTKlMSI5sT6ecJHHEymtiac= ) | ||||||
|  | dname.example.org.	1800	IN A	127.0.0.1 | ||||||
|  | 			1800	RRSIG	A 5 3 1800 ( | ||||||
|  | 					20170702091734 20170602091734 54282 example.org. | ||||||
|  | 					LPCK2nLyDdGwvmzGLkUO2atEUjoc+aEspkC3 | ||||||
|  | 					keZCdXZaLnAwBH7dNAjvvXzzy0WrgWeiyDb4 | ||||||
|  | 					+rJ2N0oaKEZicM4QQDHKhugJblKbU5G4qTey | ||||||
|  | 					LSEaV3vvQnzGd0S6dCqnwfPj9czagFN7Zlf5 | ||||||
|  | 					DmLtdxx0aiDPCUpqT0+H/vuGPfk= ) | ||||||
|  | 			1800	DNAME	test.example.org. | ||||||
|  | 			1800	RRSIG	DNAME 5 3 1800 ( | ||||||
|  | 					20170702091734 20170602091734 54282 example.org. | ||||||
|  | 					HvX79T1flWJ8H9/1XZjX6gz8rP/o2jbfPXJ9 | ||||||
|  | 					vC7ids/ZJilSReabLru4DCqcw1IV2DM/CZdE | ||||||
|  | 					tBnED/T2PJXvMut9tnYMrz+ZFPxoV6XyA3Z7 | ||||||
|  | 					bok3B0OuxizzAN2EXdol04VdbMHoWUzjQCzi | ||||||
|  | 					0Ri12zLGRPzDepZ7FolgD+JtiBM= ) | ||||||
|  | 			14400	NSEC	a.dname.example.org. A DNAME RRSIG NSEC | ||||||
|  | 			14400	RRSIG	NSEC 5 3 14400 ( | ||||||
|  | 					20170702091734 20170602091734 54282 example.org. | ||||||
|  | 					U3ZPYMUBJl3wF2SazQv/kBf6ec0CH+7n0Hr9 | ||||||
|  | 					w6lBKkiXz7P9WQzJDVnTHEZOrbDI6UetFGyC | ||||||
|  | 					6qcaADCASZ9Wxc+riyK1Hl4ox+Y/CHJ97WHy | ||||||
|  | 					oS2X//vEf6qmbHQXin0WQtFdU/VCRYF40X5v | ||||||
|  | 					8VfqOmrr8iKiEqXND8XNVf58mTw= ) | ||||||
|  | a.dname.example.org.	1800	IN A	127.0.0.1 | ||||||
|  | 			1800	RRSIG	A 5 4 1800 ( | ||||||
|  | 					20170702091734 20170602091734 54282 example.org. | ||||||
|  | 					y7RHBWZwli8SJQ4BgTmdXmYS3KGHZ7AitJCx | ||||||
|  | 					zXFksMQtNoOfVEQBwnFqjAb8ezcV5u92h1gN | ||||||
|  | 					i1EcuxCFiElML1XFT8dK2GnlPAga9w3oIwd5 | ||||||
|  | 					wzW/YHcnR0P9lF56Sl7RoIt6+jJqOdRfixS6 | ||||||
|  | 					TDoLoXsNbOxQ+qV3B8pU2Tam204= ) | ||||||
|  | 			14400	NSEC	ns.example.org. A RRSIG NSEC | ||||||
|  | 			14400	RRSIG	NSEC 5 4 14400 ( | ||||||
|  | 					20170702091734 20170602091734 54282 example.org. | ||||||
|  | 					Tmu27q3+xfONSZZtZLhejBUVtEw+83ZU1AFb | ||||||
|  | 					Rsxctjry/x5r2JSxw/sgSAExxX/7tx/okZ8J | ||||||
|  | 					oJqtChpsr91Kiw3eEBgINi2lCYIpMJlW4cWz | ||||||
|  | 					8bYlHfR81VsKYgy/cRgrq1RRvBoJnw+nwSty | ||||||
|  | 					mKPIvUtt67LAvLxJheSCEMZLCKI= ) | ||||||
|  | ns.example.org.		1800	IN A	127.0.0.1 | ||||||
|  | 			1800	RRSIG	A 5 3 1800 ( | ||||||
|  | 					20170702091734 20170602091734 54282 example.org. | ||||||
|  | 					mhi1SGaaAt+ndQEg5uKWKCH0HMzaqh/9dUK3 | ||||||
|  | 					p2wWMBrLbTZrcWyz10zRnvehicXDCasbBrer | ||||||
|  | 					ZpDQnz5AgxYYBURvdPfUzx1XbNuRJRE4l5PN | ||||||
|  | 					CEUTlTWcqCXnlSoPKEJE5HRf7v0xg2BrBUfM | ||||||
|  | 					4mZnW2bFLwjrRQ5mm/mAmHmTROk= ) | ||||||
|  | 			14400	NSEC	example.org. A RRSIG NSEC | ||||||
|  | 			14400	RRSIG	NSEC 5 3 14400 ( | ||||||
|  | 					20170702091734 20170602091734 54282 example.org. | ||||||
|  | 					loHcdjX+NIWLAkUDfPSy2371wrfUvrBQTfMO | ||||||
|  | 					17eO2Y9E/6PE935NF5bjQtZBRRghyxzrFJhm | ||||||
|  | 					vY1Ad5ZTb+NLHvdSWbJQJog+eCc7QWp64WzR | ||||||
|  | 					RXpMdvaE6ZDwalWldLjC3h8QDywDoFdndoRY | ||||||
|  | 					eHOsmTvvtWWqtO6Fa5A8gmHT5HA= ) | ||||||
|  | ` | ||||||
|   | |||||||
| @@ -105,14 +105,20 @@ func (z *Zone) Lookup(state request.Request, qname string) ([]dns.RR, []dns.RR, | |||||||
|  |  | ||||||
| 		// If we see DNAME records, we should return those. | 		// If we see DNAME records, we should return those. | ||||||
| 		if dnamerrs := elem.Types(dns.TypeDNAME); dnamerrs != nil { | 		if dnamerrs := elem.Types(dns.TypeDNAME); dnamerrs != nil { | ||||||
| 			// Only one DNAME is allowed per name. We just pick the first one. | 			// Only one DNAME is allowed per name. We just pick the first one to synthesize from. | ||||||
| 			dname := dnamerrs[0] | 			dname := dnamerrs[0] | ||||||
| 			if cname := synthesizeCNAME(state.Name(), dname.(*dns.DNAME)); cname != nil { | 			if cname := synthesizeCNAME(state.Name(), dname.(*dns.DNAME)); cname != nil { | ||||||
| 				answer, ns, extra, rcode := z.searchCNAME(state, elem, []dns.RR{cname}) | 				answer, ns, extra, rcode := z.searchCNAME(state, elem, []dns.RR{cname}) | ||||||
|  |  | ||||||
|  | 				if do { | ||||||
|  | 					sigs := elem.Types(dns.TypeRRSIG) | ||||||
|  | 					sigs = signatureForSubType(sigs, dns.TypeDNAME) | ||||||
|  | 					dnamerrs = append(dnamerrs, sigs...) | ||||||
|  | 				} | ||||||
|  |  | ||||||
| 				// The relevant DNAME RR should be included in the answer section, | 				// The relevant DNAME RR should be included in the answer section, | ||||||
| 				// if the DNAME is being employed as a substitution instruction. | 				// if the DNAME is being employed as a substitution instruction. | ||||||
| 				answer = append([]dns.RR{dname}, answer...) | 				answer = append(dnamerrs, answer...) | ||||||
|  |  | ||||||
| 				return answer, ns, extra, rcode | 				return answer, ns, extra, rcode | ||||||
| 			} | 			} | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user