mirror of
				https://github.com/coredns/coredns.git
				synced 2025-10-28 16:54:15 -04:00 
			
		
		
		
	middleware/{file, auto}: resolve external CNAMEs
Do the same thing as in etcd and give the option of externally resolving CNAME. This is needed when CoreDNS is a proxy as well is serving zones.
This commit is contained in:
		| @@ -13,6 +13,8 @@ zonefile. New zones or changed zone are automatically picked up from disk. | ||||
| ~~~ | ||||
| auto [ZONES...] { | ||||
|     directory DIR [REGEXP ORIGIN_TEMPLATE [TIMEOUT]] | ||||
|     no_reload | ||||
|     upstream ADDRESS... | ||||
| } | ||||
| ~~~ | ||||
|  | ||||
| @@ -26,6 +28,10 @@ are used. | ||||
|   name `db.example.com`, the extracted origin will be `example.com`. **TIMEOUT** specifies how often | ||||
|   CoreDNS should scan the directory, the default is every 60 seconds. This value is in seconds. | ||||
|   The minimum value is 1 second. | ||||
| * `no_reload` by default CoreDNS will reload a zone from disk whenever it detects a change to the | ||||
|   file. This option disables that behavior. | ||||
| * `upstream` defines upstream resolvers to be used resolve external names found (think CNAMEs) | ||||
|   pointing to external names. | ||||
|  | ||||
| All directives from the *file* middleware are supported. Note that *auto* will load all zones found, | ||||
| even though the directive might only receive queries for a specific zone. I.e: | ||||
|   | ||||
| @@ -9,6 +9,7 @@ import ( | ||||
| 	"github.com/miekg/coredns/middleware" | ||||
| 	"github.com/miekg/coredns/middleware/file" | ||||
| 	"github.com/miekg/coredns/middleware/metrics" | ||||
| 	"github.com/miekg/coredns/middleware/proxy" | ||||
| 	"github.com/miekg/coredns/request" | ||||
|  | ||||
| 	"github.com/miekg/dns" | ||||
| @@ -33,6 +34,7 @@ type ( | ||||
| 		// In the future this should be something like ZoneMeta that contains all this stuff. | ||||
| 		transferTo []string | ||||
| 		noReload   bool | ||||
| 		Proxy      proxy.Proxy // Proxy for looking up names during the resolution process | ||||
|  | ||||
| 		duration time.Duration | ||||
| 	} | ||||
|   | ||||
| @@ -2,6 +2,7 @@ package auto | ||||
|  | ||||
| import ( | ||||
| 	"log" | ||||
| 	"net" | ||||
| 	"os" | ||||
| 	"path" | ||||
| 	"regexp" | ||||
| @@ -12,6 +13,7 @@ import ( | ||||
| 	"github.com/miekg/coredns/middleware" | ||||
| 	"github.com/miekg/coredns/middleware/file" | ||||
| 	"github.com/miekg/coredns/middleware/metrics" | ||||
| 	"github.com/miekg/coredns/middleware/proxy" | ||||
|  | ||||
| 	"github.com/mholt/caddy" | ||||
| ) | ||||
| @@ -142,6 +144,19 @@ func autoParse(c *caddy.Controller) (Auto, error) { | ||||
| 				case "no_reload": | ||||
| 					a.loader.noReload = true | ||||
|  | ||||
| 				case "upstream": | ||||
| 					args := c.RemainingArgs() | ||||
| 					if len(args) == 0 { | ||||
| 						return a, false, c.ArgErr() | ||||
| 					} | ||||
| 					for i := 0; i < len(args); i++ { | ||||
| 						h, p, e := net.SplitHostPort(args[i]) | ||||
| 						if e != nil && p == "" { | ||||
| 							args[i] = h + ":53" | ||||
| 						} | ||||
| 					} | ||||
| 					a.loader.Proxy = proxy.New(args) | ||||
|  | ||||
| 				default: | ||||
| 					t, _, e := file.TransferParse(c, false) | ||||
| 					if e != nil { | ||||
|   | ||||
| @@ -35,8 +35,8 @@ etcd [ZONES...] { | ||||
|     under the *first* zone specified. | ||||
| * **PATH** the path inside etcd. Defaults to "/skydns". | ||||
| * **ENDPOINT** the etcd endpoints. Defaults to "http://localhost:2397". | ||||
| * `upstream` upstream resolvers to be used resolve external names found in etcd (think CNAMEs) | ||||
|   pointing to external names. If you want CoreDNS to act as a proxy for clients, you'll need to add | ||||
| * `upstream` defines upstream resolvers to be used resolve external names found (think CNAMEs) | ||||
|   pointing to external names. If you want CoreDNS also to act as a proxy for clients, you'll need to add | ||||
|   the proxy middleware. | ||||
| * `tls` followed the cert, key and the CA's cert filenames. | ||||
| * `debug` allows for debug queries. Prefix the name with `o-o.debug.` to retrieve extra information in the | ||||
|   | ||||
| @@ -9,7 +9,6 @@ import ( | ||||
| 	"testing" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/mholt/caddy" | ||||
| 	"github.com/miekg/coredns/middleware/etcd/msg" | ||||
| 	"github.com/miekg/coredns/middleware/pkg/dnsrecorder" | ||||
| 	"github.com/miekg/coredns/middleware/pkg/singleflight" | ||||
| @@ -17,6 +16,7 @@ import ( | ||||
| 	"github.com/miekg/coredns/middleware/test" | ||||
|  | ||||
| 	etcdc "github.com/coreos/etcd/client" | ||||
| 	"github.com/mholt/caddy" | ||||
| 	"github.com/miekg/dns" | ||||
| 	"golang.org/x/net/context" | ||||
| ) | ||||
|   | ||||
| @@ -27,6 +27,7 @@ TSIG key information, something like `transfer out [ADDRESS...] key [NAME[:ALG]] | ||||
| file DBFILE [ZONES... ] { | ||||
|     transfer to ADDRESS... | ||||
|     no_reload | ||||
|     upstream ADDRESS... | ||||
| } | ||||
| ~~~ | ||||
|  | ||||
| @@ -36,6 +37,8 @@ file DBFILE [ZONES... ] { | ||||
|   When an address is specified a notify message will be send whenever the zone is reloaded. | ||||
| * `no_reload` by default CoreDNS will reload a zone from disk whenever it detects a change to the | ||||
|   file. This option disables that behavior. | ||||
| * `upstream` defines upstream resolvers to be used resolve external names found (think CNAMEs) | ||||
|   pointing to external names. | ||||
|  | ||||
| ## Examples | ||||
|  | ||||
|   | ||||
| @@ -6,6 +6,7 @@ import ( | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/miekg/coredns/middleware/pkg/dnsrecorder" | ||||
| 	"github.com/miekg/coredns/middleware/proxy" | ||||
| 	"github.com/miekg/coredns/middleware/test" | ||||
|  | ||||
| 	"github.com/miekg/dns" | ||||
| @@ -68,6 +69,12 @@ var cnameTestCases = []test.Case{ | ||||
| 			test.CNAME("www3.example.org. 1800	IN	CNAME www2.example.org."), | ||||
| 		}, | ||||
| 	}, | ||||
| 	{ | ||||
| 		Qname: "dangling.example.org.", Qtype: dns.TypeA, | ||||
| 		Answer: []dns.RR{ | ||||
| 			test.CNAME("dangling.example.org. 1800	IN	CNAME foo.example.org."), | ||||
| 		}, | ||||
| 	}, | ||||
| 	{ | ||||
| 		Qname: "www3.example.org.", Qtype: dns.TypeA, | ||||
| 		Answer: []dns.RR{ | ||||
| @@ -80,6 +87,59 @@ var cnameTestCases = []test.Case{ | ||||
| 	}, | ||||
| } | ||||
|  | ||||
| func TestLookupCNAMEExternal(t *testing.T) { | ||||
| 	name := "example.org." | ||||
| 	zone, err := Parse(strings.NewReader(dbExampleCNAME), name, "stdin") | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("Expected no error when reading zone, got %q", err) | ||||
| 	} | ||||
| 	zone.Proxy = proxy.New([]string{"8.8.8.8:53"}) // TODO(point to local instance) | ||||
|  | ||||
| 	fm := File{Next: test.ErrorHandler(), Zones: Zones{Z: map[string]*Zone{name: zone}, Names: []string{name}}} | ||||
| 	ctx := context.TODO() | ||||
|  | ||||
| 	for _, tc := range exernalTestCases { | ||||
| 		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) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| var exernalTestCases = []test.Case{ | ||||
| 	{ | ||||
| 		Qname: "external.example.org.", Qtype: dns.TypeA, | ||||
| 		Answer: []dns.RR{ | ||||
| 			test.CNAME("external.example.org. 1800	CNAME	www.example.net."), | ||||
| 		}, | ||||
| 	}, | ||||
| } | ||||
|  | ||||
| const dbExampleCNAME = ` | ||||
| $TTL    30M | ||||
| $ORIGIN example.org. | ||||
| @@ -95,4 +155,5 @@ www3            IN      CNAME   www2 | ||||
| www2            IN      CNAME   www1 | ||||
| www1            IN      CNAME   www | ||||
| www             IN      CNAME   a | ||||
| dangling        IN      CNAME   foo` | ||||
| dangling        IN      CNAME   foo | ||||
| external        IN      CNAME   www.example.net.` | ||||
|   | ||||
| @@ -2,6 +2,7 @@ package file | ||||
|  | ||||
| import ( | ||||
| 	"github.com/miekg/coredns/middleware/file/tree" | ||||
| 	"github.com/miekg/coredns/request" | ||||
|  | ||||
| 	"github.com/miekg/dns" | ||||
| ) | ||||
| @@ -118,7 +119,7 @@ func (z *Zone) Lookup(qname string, qtype uint16, do bool) ([]dns.RR, []dns.RR, | ||||
| 	// Found entire name. | ||||
| 	if found && shot { | ||||
|  | ||||
| 		// DNAME... | ||||
| 		// DNAME...? | ||||
| 		if rrs := elem.Types(dns.TypeCNAME); len(rrs) > 0 && qtype != dns.TypeCNAME { | ||||
| 			return z.searchCNAME(elem, rrs, qtype, do) | ||||
| 		} | ||||
| @@ -260,8 +261,16 @@ func (z *Zone) searchCNAME(elem *tree.Elem, rrs []dns.RR, qtype uint16, do bool) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	elem, _ = z.Tree.Search(rrs[0].(*dns.CNAME).Target) | ||||
| 	targetName := rrs[0].(*dns.CNAME).Target | ||||
| 	elem, _ = z.Tree.Search(targetName) | ||||
| 	println(targetName) | ||||
| 	if elem == nil { | ||||
| 		if !dns.IsSubDomain(z.origin, targetName) { | ||||
| 			println(targetName, "is not a child of", z.origin) | ||||
| 		} | ||||
| 		st := request.Request{} | ||||
| 		z.Proxy.Lookup(st, targetName, qtype) | ||||
|  | ||||
| 		return rrs, nil, nil, Success | ||||
| 	} | ||||
|  | ||||
| @@ -279,8 +288,12 @@ Redo: | ||||
| 				rrs = append(rrs, sigs...) | ||||
| 			} | ||||
| 		} | ||||
| 		elem, _ = z.Tree.Search(cname[0].(*dns.CNAME).Target) | ||||
| 		targetName := cname[0].(*dns.CNAME).Target | ||||
| 		elem, _ = z.Tree.Search(targetName) | ||||
| 		if elem == nil { | ||||
| 			if !dns.IsSubDomain(z.origin, targetName) { | ||||
| 				println(targetName, "is not a child of", z.origin) | ||||
| 			} | ||||
| 			return rrs, nil, nil, Success | ||||
| 		} | ||||
|  | ||||
|   | ||||
| @@ -8,6 +8,7 @@ import ( | ||||
|  | ||||
| 	"github.com/miekg/coredns/core/dnsserver" | ||||
| 	"github.com/miekg/coredns/middleware" | ||||
| 	"github.com/miekg/coredns/middleware/proxy" | ||||
|  | ||||
| 	"github.com/mholt/caddy" | ||||
| ) | ||||
| @@ -90,6 +91,7 @@ func fileParse(c *caddy.Controller) (Zones, error) { | ||||
| 			} | ||||
|  | ||||
| 			noReload := false | ||||
| 			prxy := proxy.Proxy{} | ||||
| 			for c.NextBlock() { | ||||
| 				t, _, e := TransferParse(c, false) | ||||
| 				if e != nil { | ||||
| @@ -98,6 +100,19 @@ func fileParse(c *caddy.Controller) (Zones, error) { | ||||
| 				switch c.Val() { | ||||
| 				case "no_reload": | ||||
| 					noReload = true | ||||
|  | ||||
| 				case "upstream": | ||||
| 					args := c.RemainingArgs() | ||||
| 					if len(args) == 0 { | ||||
| 						return Zones{}, c.ArgErr() | ||||
| 					} | ||||
| 					for i := 0; i < len(args); i++ { | ||||
| 						h, p, e := net.SplitHostPort(args[i]) | ||||
| 						if e != nil && p == "" { | ||||
| 							args[i] = h + ":53" | ||||
| 						} | ||||
| 					} | ||||
| 					prxy = proxy.New(args) | ||||
| 				} | ||||
|  | ||||
| 				for _, origin := range origins { | ||||
| @@ -105,6 +120,7 @@ func fileParse(c *caddy.Controller) (Zones, error) { | ||||
| 						z[origin].TransferTo = append(z[origin].TransferTo, t...) | ||||
| 					} | ||||
| 					z[origin].NoReload = noReload | ||||
| 					z[origin].Proxy = prxy | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|   | ||||
| @@ -9,6 +9,7 @@ import ( | ||||
| 	"sync" | ||||
|  | ||||
| 	"github.com/miekg/coredns/middleware/file/tree" | ||||
| 	"github.com/miekg/coredns/middleware/proxy" | ||||
| 	"github.com/miekg/coredns/request" | ||||
|  | ||||
| 	"github.com/fsnotify/fsnotify" | ||||
| @@ -31,6 +32,7 @@ type Zone struct { | ||||
| 	NoReload       bool | ||||
| 	reloadMu       sync.RWMutex | ||||
| 	ReloadShutdown chan bool | ||||
| 	Proxy          proxy.Proxy // Proxy for looking up names during the resolution process | ||||
| } | ||||
|  | ||||
| // Apex contains the apex records of a zone: SOA, NS and their potential signatures. | ||||
|   | ||||
		Reference in New Issue
	
	Block a user