diff --git a/middleware/etcd/README.md b/middleware/etcd/README.md index c3717ae58..a424244c6 100644 --- a/middleware/etcd/README.md +++ b/middleware/etcd/README.md @@ -72,3 +72,48 @@ This is the default SkyDNS setup, with everying specified in full: proxy . 8.8.8.8:53 8.8.4.4:53 } ~~~ + +### Reverse zones + +Reverse zones are supported. You need to make CoreDNS aware of the fact that you are also +authoritative for the reverse. For instance if you want to add the reverse for 10.0.0.0/24, you'll +need to add the zone `10.in-addr.arpa` to the list of zones (the fun starts with reverse IPv6 zones +in the ip6.arpa domain). Showing a snippet of a Corefile: + +~~~ + etcd skydns.local 10.in-addr.arpa { + stubzones + ... +~~~ + +Next you'll need to populate the zone with reverse records, here we add a reverse for +10.0.0.127 pointing to reverse.skydns.local. + +~~~ +% curl -XPUT http://127.0.0.1:4001/v2/keys/skydns/arpa/in-addr/10/0/0/127 \ + -d value='{"host":"reverse.skydns.local."}' +~~~ + +Querying with dig: + +~~~ +% dig @localhost -x 10.0.0.127 +short +reverse.atoom.net. +~~~ + +Or with *debug* queries enabled: + +~~~ +% dig @localhost -p 1053 o-o.debug.127.0.0.10.in-addr.arpa. PTR + +;; OPT PSEUDOSECTION: +; EDNS: version: 0, flags:; udp: 4096 +;; QUESTION SECTION: +;o-o.debug.127.0.0.10.in-addr.arpa. IN PTR + +;; ANSWER SECTION: +127.0.0.10.in-addr.arpa. 300 IN PTR reverse.atoom.net. + +;; ADDITIONAL SECTION: +127.0.0.10.in-addr.arpa. 300 CH TXT "reverse.atoom.net.:0(10,0,,false)[0,]" +~~~ diff --git a/middleware/etcd/handler.go b/middleware/etcd/handler.go index d27b274cd..a270e1983 100644 --- a/middleware/etcd/handler.go +++ b/middleware/etcd/handler.go @@ -58,6 +58,8 @@ func (e Etcd) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (i records, debug, err = e.TXT(zone, state) case "CNAME": records, debug, err = e.CNAME(zone, state) + case "PTR": + records, debug, err = e.PTR(zone, state) case "MX": records, extra, debug, err = e.MX(zone, state) case "SRV": diff --git a/middleware/etcd/lookup.go b/middleware/etcd/lookup.go index 1223f85a4..5d7d89ce3 100644 --- a/middleware/etcd/lookup.go +++ b/middleware/etcd/lookup.go @@ -305,6 +305,21 @@ func (e Etcd) CNAME(zone string, state middleware.State) (records []dns.RR, debu return records, debug, nil } +// PTR returns the PTR records, only services that have a domain name as host are included. +func (e Etcd) PTR(zone string, state middleware.State) (records []dns.RR, debug []msg.Service, err error) { + services, debug, err := e.records(state, true) + if err != nil { + return nil, debug, err + } + + for _, serv := range services { + if ip := net.ParseIP(serv.Host); ip == nil { + records = append(records, serv.NewPTR(state.QName(), serv.Host)) + } + } + return records, debug, nil +} + func (e Etcd) TXT(zone string, state middleware.State) (records []dns.RR, debug []msg.Service, err error) { services, debug, err := e.records(state, false) if err != nil { diff --git a/middleware/etcd/lookup_test.go b/middleware/etcd/lookup_test.go index ffc8c737e..14b9d0c98 100644 --- a/middleware/etcd/lookup_test.go +++ b/middleware/etcd/lookup_test.go @@ -15,20 +15,22 @@ var services = []*msg.Service{ {Host: "10.0.0.1", Port: 8080, Key: "a.server1.prod.region1.skydns.test."}, {Host: "10.0.0.2", Port: 8080, Key: "b.server1.prod.region1.skydns.test."}, {Host: "::1", Port: 8080, Key: "b.server6.prod.region1.skydns.test."}, - // Unresolvable internal name + // Unresolvable internal name. {Host: "unresolvable.skydns.test", Key: "cname.prod.region1.skydns.test."}, - // priority + // Priority. {Host: "priority.server1", Priority: 333, Port: 8080, Key: "priority.skydns.test."}, - // Subdomain + // Subdomain. {Host: "sub.server1", Port: 0, Key: "a.sub.region1.skydns.test."}, {Host: "sub.server2", Port: 80, Key: "b.sub.region1.skydns.test."}, {Host: "10.0.0.1", Port: 8080, Key: "c.sub.region1.skydns.test."}, - // Cname loop + // Cname loop. {Host: "a.cname.skydns.test", Key: "b.cname.skydns.test."}, {Host: "b.cname.skydns.test", Key: "a.cname.skydns.test."}, // Nameservers. {Host: "10.0.0.2", Key: "a.ns.dns.skydns.test."}, {Host: "10.0.0.3", Key: "b.ns.dns.skydns.test."}, + // Reverse. + {Host: "reverse.example.com", Key: "1.0.0.10.in-addr.arpa."}, // 10.0.0.1 } var dnsTestCases = []test.Case{ @@ -198,4 +200,9 @@ var dnsTestCases = []test.Case{ Qname: "skydns_extra.test.", Qtype: dns.TypeSOA, Answer: []dns.RR{test.SOA("skydns_extra.test. 300 IN SOA ns.dns.skydns_extra.test. hostmaster.skydns_extra.test. 1460498836 14400 3600 604800 60")}, }, + // Reverse lookup + { + Qname: "1.0.0.10.in-addr.arpa.", Qtype: dns.TypePTR, + Answer: []dns.RR{test.PTR("1.0.0.10.in-addr.arpa. 300 PTR reverse.example.com.")}, + }, } diff --git a/middleware/etcd/msg/service.go b/middleware/etcd/msg/service.go index 76d74a463..059b3263b 100644 --- a/middleware/etcd/msg/service.go +++ b/middleware/etcd/msg/service.go @@ -102,6 +102,11 @@ func (s *Service) NewTXT(name string) *dns.TXT { return &dns.TXT{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeTXT, Class: dns.ClassINET, Ttl: s.Ttl}, Txt: split255(s.Text)} } +// NewPTR returns a new PTR record based on the Service. +func (s *Service) NewPTR(name string, target string) *dns.PTR { + return &dns.PTR{Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypePTR, Class: dns.ClassINET, Ttl: s.Ttl}, Ptr: dns.Fqdn(target)} +} + // NewNS returns a new NS record based on the Service. func (s *Service) NewNS(name string) *dns.NS { host := targetStrip(dns.Fqdn(s.Host), s.TargetStrip) diff --git a/middleware/etcd/setup_test.go b/middleware/etcd/setup_test.go index 1810e8e70..b1a7b9615 100644 --- a/middleware/etcd/setup_test.go +++ b/middleware/etcd/setup_test.go @@ -37,7 +37,7 @@ func init() { PathPrefix: "skydns", Ctx: context.Background(), Inflight: &singleflight.Group{}, - Zones: []string{"skydns.test.", "skydns_extra.test."}, + Zones: []string{"skydns.test.", "skydns_extra.test.", "in-addr.arpa."}, Client: etcdc.NewKeysAPI(cli), } }