diff options
Diffstat (limited to 'plugin/etcd')
-rw-r--r-- | plugin/etcd/README.md | 18 | ||||
-rw-r--r-- | plugin/etcd/etcd.go | 4 | ||||
-rw-r--r-- | plugin/etcd/handler.go | 14 | ||||
-rw-r--r-- | plugin/etcd/lookup_test.go | 4 | ||||
-rw-r--r-- | plugin/etcd/setup.go | 40 | ||||
-rw-r--r-- | plugin/etcd/setup_test.go | 2 | ||||
-rw-r--r-- | plugin/etcd/stub.go | 81 | ||||
-rw-r--r-- | plugin/etcd/stub_handler.go | 83 | ||||
-rw-r--r-- | plugin/etcd/stub_test.go | 87 |
9 files changed, 21 insertions, 312 deletions
diff --git a/plugin/etcd/README.md b/plugin/etcd/README.md index 7a85fa60d..e0e33bbfc 100644 --- a/plugin/etcd/README.md +++ b/plugin/etcd/README.md @@ -28,28 +28,22 @@ If you want to `round robin` A and AAAA responses look at the `loadbalance` plug ~~~ etcd [ZONES...] { - stubzones fallthrough [ZONES...] path PATH endpoint ENDPOINT... - upstream [ADDRESS...] + upstream tls CERT KEY CACERT } ~~~ -* `stubzones` enables the stub zones feature. The stubzone is *only* done in the etcd tree located - under the *first* zone specified. * `fallthrough` If zone matches but no record can be generated, pass request to the next plugin. If **[ZONES...]** is omitted, then fallthrough happens for all zones for which the plugin is authoritative. If specific zones are listed (for example `in-addr.arpa` and `ip6.arpa`), then only queries for those zones will be subject to fallthrough. * **PATH** the path inside etcd. Defaults to "/skydns". * **ENDPOINT** the etcd endpoints. Defaults to "http://localhost:2379". -* `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 - the proxy plugin. If no **ADDRESS** is given, CoreDNS will resolve CNAMEs against itself. - **ADDRESS** can be an IP address, and IP:port or a string pointing to a file that is structured - as /etc/resolv.conf. +* `upstream` resolve names found in etcd (think CNAMEs) If you want CoreDNS to act as a proxy for clients, + you'll need to add the forward plugin. CoreDNS will resolve CNAMEs against itself. * `tls` followed by: * no arguments, if the server certificate is signed by a system-installed CA and no client cert is needed @@ -79,10 +73,9 @@ This is the default SkyDNS setup, with everything specified in full: ~~~ corefile . { etcd skydns.local { - stubzones path /skydns endpoint http://localhost:2379 - upstream 8.8.8.8:53 8.8.4.4:53 + upstream } prometheus cache 160 skydns.local @@ -98,7 +91,7 @@ when resolving external pointing CNAMEs. . { etcd skydns.local { path /skydns - upstream /etc/resolv.conf + upstream } cache 160 skydns.local proxy . /etc/resolv.conf @@ -125,7 +118,6 @@ need to add the zone `0.0.10.in-addr.arpa` to the list of zones. Showing a snipp ~~~ etcd skydns.local 10.0.0.0/24 { - stubzones ... ~~~ diff --git a/plugin/etcd/etcd.go b/plugin/etcd/etcd.go index 9b37ee458..4734fe344 100644 --- a/plugin/etcd/etcd.go +++ b/plugin/etcd/etcd.go @@ -12,7 +12,6 @@ import ( "github.com/coredns/coredns/plugin" "github.com/coredns/coredns/plugin/etcd/msg" "github.com/coredns/coredns/plugin/pkg/fall" - "github.com/coredns/coredns/plugin/proxy" "github.com/coredns/coredns/request" "github.com/coredns/coredns/plugin/pkg/upstream" @@ -35,10 +34,9 @@ type Etcd struct { Fall fall.F Zones []string PathPrefix string - Upstream upstream.Upstream // Proxy for looking up names during the resolution process + Upstream *upstream.Upstream Client *etcdcv3.Client Ctx context.Context - Stubmap *map[string]proxy.Proxy // list of proxies for stub resolving. endpoints []string // Stored here as well, to aid in testing. } diff --git a/plugin/etcd/handler.go b/plugin/etcd/handler.go index 088993cf2..d9c6de4c9 100644 --- a/plugin/etcd/handler.go +++ b/plugin/etcd/handler.go @@ -14,20 +14,6 @@ func (e *Etcd) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) ( opt := plugin.Options{} state := request.Request{W: w, Req: r, Context: ctx} - name := state.Name() - - // We need to check stubzones first, because we may get a request for a zone we - // are not auth. for *but* do have a stubzone forward for. If we do the stubzone - // handler will handle the request. - if e.Stubmap != nil && len(*e.Stubmap) > 0 { - for zone := range *e.Stubmap { - if plugin.Name(zone).Matches(name) { - stub := Stub{Etcd: e, Zone: zone} - return stub.ServeDNS(ctx, w, r) - } - } - } - zone := plugin.Zones(e.Zones).Matches(state.Name()) if zone == "" { return plugin.NextOrFailure(e.Name(), e.Next, ctx, w, r) diff --git a/plugin/etcd/lookup_test.go b/plugin/etcd/lookup_test.go index d82b4bf18..3b97fe66c 100644 --- a/plugin/etcd/lookup_test.go +++ b/plugin/etcd/lookup_test.go @@ -12,7 +12,6 @@ import ( "github.com/coredns/coredns/plugin/pkg/dnstest" "github.com/coredns/coredns/plugin/pkg/tls" "github.com/coredns/coredns/plugin/pkg/upstream" - "github.com/coredns/coredns/plugin/proxy" "github.com/coredns/coredns/plugin/test" "github.com/miekg/dns" @@ -283,9 +282,8 @@ func newEtcdPlugin() *Etcd { tlsc, _ := tls.NewTLSConfigFromArgs() client, _ := newEtcdClient(endpoints, tlsc) - p := proxy.NewLookup([]string{"8.8.8.8:53"}) return &Etcd{ - Upstream: upstream.Upstream{Forward: &p}, + Upstream: upstream.New(), PathPrefix: "skydns", Ctx: context.Background(), Zones: []string{"skydns.test.", "skydns_extra.test.", "skydns_zonea.test.", "skydns_zoneb.test.", "skydns_zonec.test.", "skydns_zoned.test.", "in-addr.arpa."}, diff --git a/plugin/etcd/setup.go b/plugin/etcd/setup.go index f753a0f1a..68d5f147d 100644 --- a/plugin/etcd/setup.go +++ b/plugin/etcd/setup.go @@ -9,7 +9,6 @@ import ( clog "github.com/coredns/coredns/plugin/pkg/log" mwtls "github.com/coredns/coredns/plugin/pkg/tls" "github.com/coredns/coredns/plugin/pkg/upstream" - "github.com/coredns/coredns/plugin/proxy" etcdcv3 "github.com/coreos/etcd/clientv3" "github.com/mholt/caddy" @@ -25,18 +24,11 @@ func init() { } func setup(c *caddy.Controller) error { - e, stubzones, err := etcdParse(c) + e, err := etcdParse(c) if err != nil { return plugin.Error("etcd", err) } - if stubzones { - c.OnStartup(func() error { - e.UpdateStubZones() - return nil - }) - } - dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler { e.Next = next return e @@ -45,20 +37,17 @@ func setup(c *caddy.Controller) error { return nil } -func etcdParse(c *caddy.Controller) (*Etcd, bool, error) { - stub := make(map[string]proxy.Proxy) +func etcdParse(c *caddy.Controller) (*Etcd, error) { etc := Etcd{ // Don't default to a proxy for lookups. // Proxy: proxy.NewLookup([]string{"8.8.8.8:53", "8.8.4.4:53"}), PathPrefix: "skydns", Ctx: context.Background(), - Stubmap: &stub, } var ( tlsConfig *tls.Config err error endpoints = []string{defaultEndpoint} - stubzones = false ) for c.Next() { etc.Zones = c.RemainingArgs() @@ -74,38 +63,35 @@ func etcdParse(c *caddy.Controller) (*Etcd, bool, error) { for { switch c.Val() { case "stubzones": - stubzones = true + // ignored, remove later. case "fallthrough": etc.Fall.SetZonesFromArgs(c.RemainingArgs()) case "debug": /* it is a noop now */ case "path": if !c.NextArg() { - return &Etcd{}, false, c.ArgErr() + return &Etcd{}, c.ArgErr() } etc.PathPrefix = c.Val() case "endpoint": args := c.RemainingArgs() if len(args) == 0 { - return &Etcd{}, false, c.ArgErr() + return &Etcd{}, c.ArgErr() } endpoints = args case "upstream": - args := c.RemainingArgs() - u, err := upstream.New(args) - if err != nil { - return nil, false, err - } - etc.Upstream = u + // check args != 0 and error in the future + c.RemainingArgs() // clear buffer + etc.Upstream = upstream.New() case "tls": // cert key cacertfile args := c.RemainingArgs() tlsConfig, err = mwtls.NewTLSConfigFromArgs(args...) if err != nil { - return &Etcd{}, false, err + return &Etcd{}, err } default: if c.Val() != "}" { - return &Etcd{}, false, c.Errf("unknown property '%s'", c.Val()) + return &Etcd{}, c.Errf("unknown property '%s'", c.Val()) } } @@ -117,14 +103,14 @@ func etcdParse(c *caddy.Controller) (*Etcd, bool, error) { } client, err := newEtcdClient(endpoints, tlsConfig) if err != nil { - return &Etcd{}, false, err + return &Etcd{}, err } etc.Client = client etc.endpoints = endpoints - return &etc, stubzones, nil + return &etc, nil } - return &Etcd{}, false, nil + return &Etcd{}, nil } func newEtcdClient(endpoints []string, cc *tls.Config) (*etcdcv3.Client, error) { diff --git a/plugin/etcd/setup_test.go b/plugin/etcd/setup_test.go index 620460cc1..0696dc3e3 100644 --- a/plugin/etcd/setup_test.go +++ b/plugin/etcd/setup_test.go @@ -56,7 +56,7 @@ func TestSetupEtcd(t *testing.T) { for i, test := range tests { c := caddy.NewTestController("dns", test.input) - etcd, _ /*stubzones*/, err := etcdParse(c) + etcd, err := etcdParse(c) if test.shouldErr && err == nil { t.Errorf("Test %d: Expected error but found %s for input %s", i, err, test.input) diff --git a/plugin/etcd/stub.go b/plugin/etcd/stub.go deleted file mode 100644 index 37e426ac4..000000000 --- a/plugin/etcd/stub.go +++ /dev/null @@ -1,81 +0,0 @@ -package etcd - -import ( - "net" - "strconv" - "time" - - "github.com/coredns/coredns/plugin/etcd/msg" - "github.com/coredns/coredns/plugin/pkg/dnsutil" - "github.com/coredns/coredns/plugin/proxy" - "github.com/coredns/coredns/request" - - "github.com/miekg/dns" -) - -// UpdateStubZones checks etcd for an update on the stubzones. -func (e *Etcd) UpdateStubZones() { - go func() { - for { - e.updateStubZones() - time.Sleep(15 * time.Second) - } - }() -} - -// Look in .../dns/stub/<zone>/xx for msg.Services. Loop through them -// extract <zone> and add them as forwarders (ip:port-combos) for -// the stub zones. Only numeric (i.e. IP address) hosts are used. -// Only the first zone configured on e is used for the lookup. -func (e *Etcd) updateStubZones() { - zone := e.Zones[0] - - fakeState := request.Request{W: nil, Req: new(dns.Msg)} - fakeState.Req.SetQuestion(stubDomain+"."+zone, dns.TypeA) - - services, err := e.Records(fakeState, false) - if err != nil { - return - } - - stubmap := make(map[string]proxy.Proxy) - // track the nameservers on a per domain basis, but allow a list on the domain. - nameservers := map[string][]string{} - -Services: - for _, serv := range services { - if serv.Port == 0 { - serv.Port = 53 - } - ip := net.ParseIP(serv.Host) - if ip == nil { - log.Warningf("Non IP address stub nameserver: %s", serv.Host) - continue - } - - domain := msg.Domain(serv.Key) - labels := dns.SplitDomainName(domain) - - // If the remaining name equals any of the zones we have, we ignore it. - for _, z := range e.Zones { - // Chop of left most label, because that is used as the nameserver place holder - // and drop the right most labels that belong to zone. - // We must *also* chop of dns.stub. which means cutting two more labels. - domain = dnsutil.Join(labels[1 : len(labels)-dns.CountLabel(z)-2]...) - if domain == z { - log.Warningf("Skipping nameserver for domain we are authoritative for: %s", domain) - continue Services - } - } - nameservers[domain] = append(nameservers[domain], net.JoinHostPort(serv.Host, strconv.Itoa(serv.Port))) - } - - for domain, nss := range nameservers { - stubmap[domain] = proxy.NewLookup(nss) - } - // atomic swap (at least that's what we hope it is) - if len(stubmap) > 0 { - e.Stubmap = &stubmap - } - return -} diff --git a/plugin/etcd/stub_handler.go b/plugin/etcd/stub_handler.go deleted file mode 100644 index 1282a8263..000000000 --- a/plugin/etcd/stub_handler.go +++ /dev/null @@ -1,83 +0,0 @@ -package etcd - -import ( - "context" - "errors" - - "github.com/coredns/coredns/request" - - "github.com/miekg/dns" -) - -// Stub wraps an Etcd. We have this type so that it can have a ServeDNS method. -type Stub struct { - *Etcd - Zone string // for what zone (and thus what nameservers are we called) -} - -// ServeDNS implements the plugin.Handler interface. -func (s Stub) ServeDNS(ctx context.Context, w dns.ResponseWriter, req *dns.Msg) (int, error) { - if hasStubEdns0(req) { - log.Warningf("Forwarding cycle detected, refusing msg: %s", req.Question[0].Name) - return dns.RcodeRefused, errors.New("stub forward cycle") - } - req = addStubEdns0(req) - proxy, ok := (*s.Etcd.Stubmap)[s.Zone] - if !ok { // somebody made a mistake.. - return dns.RcodeServerFailure, nil - } - - state := request.Request{W: w, Req: req} - m, e := proxy.Forward(state) - if e != nil { - return dns.RcodeServerFailure, e - } - w.WriteMsg(m) - return dns.RcodeSuccess, nil -} - -// hasStubEdns0 checks if the message is carrying our special edns0 zero option. -func hasStubEdns0(m *dns.Msg) bool { - option := m.IsEdns0() - if option == nil { - return false - } - for _, o := range option.Option { - if o.Option() == ednsStubCode && len(o.(*dns.EDNS0_LOCAL).Data) == 1 && - o.(*dns.EDNS0_LOCAL).Data[0] == 1 { - return true - } - } - return false -} - -// addStubEdns0 adds our special option to the message's OPT record. -func addStubEdns0(m *dns.Msg) *dns.Msg { - option := m.IsEdns0() - // Add a custom EDNS0 option to the packet, so we can detect loops when 2 stubs are forwarding to each other. - if option != nil { - option.Option = append(option.Option, &dns.EDNS0_LOCAL{Code: ednsStubCode, Data: []byte{1}}) - return m - } - - m.Extra = append(m.Extra, ednsStub) - return m -} - -const ( - ednsStubCode = dns.EDNS0LOCALSTART + 10 - stubDomain = "stub.dns" -) - -var ednsStub = func() *dns.OPT { - o := new(dns.OPT) - o.Hdr.Name = "." - o.Hdr.Rrtype = dns.TypeOPT - o.SetUDPSize(4096) - - e := new(dns.EDNS0_LOCAL) - e.Code = ednsStubCode - e.Data = []byte{1} - o.Option = append(o.Option, e) - return o -}() diff --git a/plugin/etcd/stub_test.go b/plugin/etcd/stub_test.go deleted file mode 100644 index c01100bdf..000000000 --- a/plugin/etcd/stub_test.go +++ /dev/null @@ -1,87 +0,0 @@ -// +build etcd - -package etcd - -import ( - "net" - "strconv" - "testing" - - "github.com/coredns/coredns/plugin/etcd/msg" - "github.com/coredns/coredns/plugin/pkg/dnstest" - "github.com/coredns/coredns/plugin/test" - - "github.com/miekg/dns" -) - -func fakeStubServerExampleNet(t *testing.T) (*dns.Server, string) { - server, addr, err := test.UDPServer("127.0.0.1:0") - if err != nil { - t.Fatalf("Failed to create a UDP server: %s", err) - } - // add handler for example.net - dns.HandleFunc("example.net.", func(w dns.ResponseWriter, r *dns.Msg) { - m := new(dns.Msg) - m.SetReply(r) - m.Answer = []dns.RR{test.A("example.net. 86400 IN A 93.184.216.34")} - w.WriteMsg(m) - }) - - return server, addr -} - -func TestStubLookup(t *testing.T) { - server, addr := fakeStubServerExampleNet(t) - defer server.Shutdown() - - host, p, _ := net.SplitHostPort(addr) - port, _ := strconv.Atoi(p) - exampleNetStub := &msg.Service{Host: host, Port: port, Key: "a.example.net.stub.dns.skydns.test."} - servicesStub = append(servicesStub, exampleNetStub) - - etc := newEtcdPlugin() - - for _, serv := range servicesStub { - set(t, etc, serv.Key, 0, serv) - defer delete(t, etc, serv.Key) - } - - etc.updateStubZones() - - for _, tc := range dnsTestCasesStub { - m := tc.Msg() - - rec := dnstest.NewRecorder(&test.ResponseWriter{}) - _, err := etc.ServeDNS(ctxt, rec, m) - if err != nil && m.Question[0].Name == "example.org." { - // This is OK, we expect this backend to *not* work. - continue - } - if err != nil { - t.Errorf("Expected no error, got %v for %s\n", err, m.Question[0].Name) - } - resp := rec.Msg - if resp == nil { - // etcd not running? - continue - } - - test.SortAndCheck(t, resp, tc) - } -} - -var servicesStub = []*msg.Service{ - // Two tests, ask a question that should return servfail because remote it no accessible - // and one with edns0 option added, that should return refused. - {Host: "127.0.0.1", Port: 666, Key: "b.example.org.stub.dns.skydns.test."}, -} - -var dnsTestCasesStub = []test.Case{ - { - Qname: "example.org.", Qtype: dns.TypeA, Rcode: dns.RcodeServerFailure, - }, - { - Qname: "example.net.", Qtype: dns.TypeA, - Answer: []dns.RR{test.A("example.net. 86400 IN A 93.184.216.34")}, - }, -} |