diff options
author | 2019-03-03 23:32:38 -0800 | |
---|---|---|
committer | 2019-03-04 07:32:38 +0000 | |
commit | 9dd288943a778c19d5a798e1c1535cd0ba5d53c0 (patch) | |
tree | 2f555f01f74f41c005f75aa5a27659d6c5133410 /plugin | |
parent | dfa413af096646e04882bb8312eef27fe1a160ef (diff) | |
download | coredns-9dd288943a778c19d5a798e1c1535cd0ba5d53c0.tar.gz coredns-9dd288943a778c19d5a798e1c1535cd0ba5d53c0.tar.zst coredns-9dd288943a778c19d5a798e1c1535cd0ba5d53c0.zip |
Move *proxy* to external (#2651)
* Move *proxy* to external
move the proxy plugin into coredns/proxy and remove it as a default
plugin. Link the proxy to deprecated in plugin.cfg
coredns/proxy doesn't compile because of the vendoring :(
Signed-off-by: Miek Gieben <miek@miek.nl>
* Add github.com/coredns/proxy
Signed-off-by: Yong Tang <yong.tang.github@outlook.com>
Diffstat (limited to 'plugin')
-rw-r--r-- | plugin/cache/README.md | 5 | ||||
-rw-r--r-- | plugin/etcd/README.md | 8 | ||||
-rw-r--r-- | plugin/etcd/setup.go | 2 | ||||
-rw-r--r-- | plugin/kubernetes/README.md | 23 | ||||
-rw-r--r-- | plugin/loop/README.md | 12 | ||||
-rw-r--r-- | plugin/proxy/OWNERS | 10 | ||||
-rw-r--r-- | plugin/proxy/README.md | 160 | ||||
-rw-r--r-- | plugin/proxy/dns.go | 100 | ||||
-rw-r--r-- | plugin/proxy/dnstap.go | 56 | ||||
-rw-r--r-- | plugin/proxy/dnstap_test.go | 57 | ||||
-rw-r--r-- | plugin/proxy/down.go | 18 | ||||
-rw-r--r-- | plugin/proxy/exchanger.go | 23 | ||||
-rw-r--r-- | plugin/proxy/fuzz.go | 21 | ||||
-rw-r--r-- | plugin/proxy/grpc.go | 99 | ||||
-rw-r--r-- | plugin/proxy/grpc_test.go | 220 | ||||
-rw-r--r-- | plugin/proxy/log_test.go | 5 | ||||
-rw-r--r-- | plugin/proxy/metrics.go | 36 | ||||
-rw-r--r-- | plugin/proxy/proxy.go | 183 | ||||
-rw-r--r-- | plugin/proxy/proxy_test.go | 72 | ||||
-rw-r--r-- | plugin/proxy/response.go | 21 | ||||
-rw-r--r-- | plugin/proxy/setup.go | 53 | ||||
-rw-r--r-- | plugin/proxy/upstream.go | 203 | ||||
-rw-r--r-- | plugin/proxy/upstream_test.go | 327 | ||||
-rw-r--r-- | plugin/template/README.md | 14 | ||||
-rw-r--r-- | plugin/tls/README.md | 7 |
25 files changed, 36 insertions, 1699 deletions
diff --git a/plugin/cache/README.md b/plugin/cache/README.md index 924a7543e..8b2bdf075 100644 --- a/plugin/cache/README.md +++ b/plugin/cache/README.md @@ -88,13 +88,14 @@ Proxy to Google Public DNS and only cache responses for example.org (or below). ~~~ corefile . { - proxy . 8.8.8.8:53 + forward . 8.8.8.8:53 cache example.org } ~~~ Enable caching for all zones, keep a positive cache size of 5000 and a negative cache size of 2500: - ~~~ corefile + +~~~ corefile . { cache { success 5000 diff --git a/plugin/etcd/README.md b/plugin/etcd/README.md index 6ac267b51..393806a9f 100644 --- a/plugin/etcd/README.md +++ b/plugin/etcd/README.md @@ -10,7 +10,7 @@ The data in etcd instance has to be encoded as a [message](https://github.com/skynetservices/skydns/blob/2fcff74cdc9f9a7dd64189a447ef27ac354b725f/msg/service.go#L26) like [SkyDNS](https://github.com/skynetservices/skydns). It should also work just like SkyDNS. -The etcd plugin makes extensive use of the proxy plugin to forward and query other servers in the +The etcd plugin makes extensive use of the forward plugin to forward and query other servers in the network. ## Syntax @@ -46,7 +46,7 @@ etcd [ZONES...] { * `credentials` is used to set the **USERNAME** and **PASSWORD** for accessing the etcd cluster. * `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. + the *forward* 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. * `tls` followed by: @@ -85,7 +85,7 @@ This is the default SkyDNS setup, with everything specified in full: prometheus cache 160 skydns.local loadbalance - proxy . 8.8.8.8:53 8.8.4.4:53 + forward . 8.8.8.8:53 8.8.4.4:53 } ~~~ @@ -99,7 +99,7 @@ when resolving external pointing CNAMEs. upstream } cache 160 skydns.local - proxy . /etc/resolv.conf + forward . /etc/resolv.conf } ~~~ diff --git a/plugin/etcd/setup.go b/plugin/etcd/setup.go index d0cdbc705..3dfe6a49a 100644 --- a/plugin/etcd/setup.go +++ b/plugin/etcd/setup.go @@ -39,8 +39,6 @@ func setup(c *caddy.Controller) error { 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(), } diff --git a/plugin/kubernetes/README.md b/plugin/kubernetes/README.md index 021b6553b..552050b0b 100644 --- a/plugin/kubernetes/README.md +++ b/plugin/kubernetes/README.md @@ -14,7 +14,7 @@ cluster. See the [deployment](https://github.com/coredns/deployment) repository to deploy CoreDNS in Kubernetes](https://github.com/coredns/deployment/tree/master/kubernetes). [stubDomains and upstreamNameservers](https://kubernetes.io/blog/2017/04/configuring-private-dns-zones-upstream-nameservers-kubernetes/) -are implemented via the *proxy* plugin and kubernetes *upstream*. See example below. +are implemented via the *forward* plugin and kubernetes *upstream*. See the examples below. This plugin can only be used once per Server Block. @@ -147,28 +147,33 @@ kubernetes cluster.local { ## stubDomains and upstreamNameservers -Here we use the *proxy* plugin to implement a stubDomain that forwards `example.local` to the nameserver `10.100.0.10:53`. +Here we use the *forward* plugin to implement a stubDomain that forwards `example.local` to the nameserver `10.100.0.10:53`. The *upstream* option in the *kubernetes* plugin means that ExternalName services (CNAMEs) will be resolved using the respective proxy. Also configured is an upstreamNameserver `8.8.8.8:53` that will be used for resolving names that do not fall in `cluster.local` or `example.local`. ~~~ txt -.:53 { +cluster.local:53 { kubernetes cluster.local { upstream } - proxy example.local 10.100.0.10:53 - proxy . 8.8.8.8:53 +} +example.local { + forward . 10.100.0.10:53 +} + +. { + forward . 8.8.8.8:53 } ~~~ The configuration above represents the following Kube-DNS stubDomains and upstreamNameservers configuration. ~~~ txt - stubDomains: | - {“example.local”: [“10.100.0.10:53”]} - upstreamNameservers: | - [“8.8.8.8:53”] +stubDomains: | + {“example.local”: [“10.100.0.10:53”]} +upstreamNameservers: | + [“8.8.8.8:53”] ~~~ ## AutoPath diff --git a/plugin/loop/README.md b/plugin/loop/README.md index ee5029fd7..c893ed050 100644 --- a/plugin/loop/README.md +++ b/plugin/loop/README.md @@ -60,10 +60,10 @@ A forwarding loop is usually caused by: * Most commonly, CoreDNS forwarding requests directly to itself. e.g. via a loopback address such as `127.0.0.1`, `::1` or `127.0.0.53` * Less commonly, CoreDNS forwarding to an upstream server that in turn, forwards requests back to CoreDNS. -To troubleshoot this problem, look in your Corefile for any `proxy` or `forward` to the zone +To troubleshoot this problem, look in your Corefile for any `forward`s to the zone in which the loop was detected. Make sure that they are not forwarding to a local address or -to another DNS server that is forwarding requests back to CoreDNS. If `proxy` or `forward` are - using a file (e.g. `/etc/resolv.conf`), make sure that file does not contain local addresses. +to another DNS server that is forwarding requests back to CoreDNS. If `forward` is +using a file (e.g. `/etc/resolv.conf`), make sure that file does not contain local addresses. ### Troubleshooting Loops In Kubernetes Clusters @@ -75,7 +75,7 @@ on the host node (e.g. `systemd-resolved`). For example, in certain configurati put the loopback address `127.0.0.53` as a nameserver into `/etc/resolv.conf`. Kubernetes (via `kubelet`) by default will pass this `/etc/resolv.conf` file to all Pods using the `default` dnsPolicy rendering them unable to make DNS lookups (this includes CoreDNS Pods). CoreDNS uses this `/etc/resolv.conf` -as a list of upstreams to proxy/forward requests to. Since it contains a loopback address, CoreDNS ends up forwarding +as a list of upstreams to forward requests to. Since it contains a loopback address, CoreDNS ends up forwarding requests to itself. There are many ways to work around this issue, some are listed here: @@ -86,6 +86,6 @@ There are many ways to work around this issue, some are listed here: `/run/systemd/resolve/resolv.conf` is typically the location of the "real" `resolv.conf`, although this can be different depending on your distribution. * Disable the local DNS cache on host nodes, and restore `/etc/resolv.conf` to the original. -* A quick and dirty fix is to edit your Corefile, replacing `proxy . /etc/resolv.conf` with -the ip address of your upstream DNS, for example `proxy . 8.8.8.8`. But this only fixes the issue for CoreDNS, +* A quick and dirty fix is to edit your Corefile, replacing `forward . /etc/resolv.conf` with +the ip address of your upstream DNS, for example `forward . 8.8.8.8`. But this only fixes the issue for CoreDNS, kubelet will continue to forward the invalid `resolv.conf` to all `default` dnsPolicy Pods, leaving them unable to resolve DNS. diff --git a/plugin/proxy/OWNERS b/plugin/proxy/OWNERS deleted file mode 100644 index fb365f401..000000000 --- a/plugin/proxy/OWNERS +++ /dev/null @@ -1,10 +0,0 @@ -reviewers: - - fturib - - grobie - - johnbelamaric - - miekg -approvers: - - fturib - - grobie - - johnbelamaric - - miekg diff --git a/plugin/proxy/README.md b/plugin/proxy/README.md deleted file mode 100644 index ead80548a..000000000 --- a/plugin/proxy/README.md +++ /dev/null @@ -1,160 +0,0 @@ -# proxy - -## Name - -*proxy* - facilitates both a basic reverse proxy and a robust load balancer. - -## Description - -The proxy has support for multiple backends. The load balancing features include multiple policies, -health checks, and failovers. If all hosts fail their health check the proxy plugin will fail -back to randomly selecting a target and sending packets to it. - -## Syntax - -In its most basic form, a simple reverse proxy uses this syntax: - -~~~ -proxy FROM TO -~~~ - -* **FROM** is the base domain to match for the request to be proxied. -* **TO** is the destination endpoint to proxy to. - -However, advanced features including load balancing can be utilized with an expanded syntax: - -~~~ -proxy FROM TO... { - policy random|least_conn|round_robin|sequential - fail_timeout DURATION - max_fails INTEGER - health_check PATH:PORT [DURATION] - except IGNORED_NAMES... - spray - protocol [dns [force_tcp]|grpc [insecure|CACERT|KEY CERT|KEY CERT CACERT]] -} -~~~ - -* **FROM** is the name to match for the request to be proxied. -* **TO** is the destination endpoint to proxy to. At least one is required, but multiple may be - specified. **TO** may be an IP:Port pair, or may reference a file in resolv.conf format -* `policy` is the load balancing policy to use; applies only with multiple backends. May be one of - random, least_conn, round_robin or sequential. Default is random. -* `fail_timeout` specifies how long to consider a backend as down after it has failed. While it is - down, requests will not be routed to that backend. A backend is "down" if CoreDNS fails to - communicate with it. The default value is 2 seconds ("2s"). -* `max_fails` is the number of failures within fail_timeout that are needed before considering - a backend to be down. If 0, the backend will never be marked as down. Default is 1. -* `health_check` will check **PATH** (on **PORT**) on each backend. If a backend returns a status code of - 200-399, then that backend is marked healthy for double the healthcheck duration. If it doesn't, - it is marked as unhealthy and no requests are routed to it. If this option is not provided then - health checks are disabled. The default duration is 4 seconds ("4s"). -* **IGNORED_NAMES** in `except` is a space-separated list of domains to exclude from proxying. - Requests that match none of these names will be passed through. -* `spray` when all backends are unhealthy, randomly pick one to send the traffic to. (This is - a failsafe.) -* `protocol` specifies what protocol to use to speak to an upstream, `dns` (the default) is plain - old DNS. The `grpc` option will talk to a server that has implemented - the [DnsService](https://github.com/coredns/coredns/blob/master/pb/dns.proto). - -## Policies - -There are four load-balancing policies available: - -* `random` (default) - Randomly select a backend -* `least_conn` - Select the backend with the fewest active connections -* `round_robin` - Select the backend in round-robin fashion -* `sequential` - Select the first available backend looking by order of declaration from left to right -* `first` - Deprecated. Use sequential instead - - -All polices implement randomly spraying packets to backend hosts when *no healthy* hosts are -available. This is to preempt the case where the healthchecking (as a mechanism) fails. - -## Upstream Protocols - -`dns` -: uses the standard DNS exchange. You can pass `force_tcp` to make sure that the proxied connection is performed - over TCP, regardless of the inbound request's protocol. - -`grpc` -: extra options are used to control how the TLS connection is made to the gRPC server. - - * None - No client authentication is used, and the system CAs are used to verify the server certificate. - * `insecure` - TLS is not used, the connection is made in plaintext (not good in production). - * **CACERT** - No client authentication is used, and the file **CACERT** is used to verify the server certificate. - * **KEY** **CERT** - Client authentication is used with the specified key/cert pair. The server - certificate is verified with the system CAs. - * **KEY** **CERT** **CACERT** - Client authentication is used with the specified key/cert pair. The - server certificate is verified using the **CACERT** file. - -## Metrics - -If monitoring is enabled (via the *prometheus* directive) then the following metric is exported: - -* `coredns_proxy_request_duration_seconds{server, proto, proto_proxy, family, to}` - duration per - upstream interaction. -* `coredns_proxy_request_count_total{server, proto, proto_proxy, family, to}` - query count per - upstream. - -Where `proxy_proto` is the protocol used (`dns` or `grpc`) and `to` is **TO** -specified in the config, `proto` is the protocol used by the incoming query ("tcp" or "udp"), family -the transport family ("1" for IPv4, and "2" for IPv6). `Server` is the server responsible for the -request (and metric). See the documentation in the metrics plugin. - -## Examples - -Proxy all requests within example.org. to a backend system: - -~~~ -proxy example.org 127.0.0.1:9005 -~~~ - -Load-balance all requests between three backends (using random policy): - -~~~ corefile -. { - proxy . 10.0.0.10:53 10.0.0.11:1053 10.0.0.12 -} -~~~ - -Same as above, but round-robin style: - -~~~ corefile -. { - proxy . 10.0.0.10:53 10.0.0.11:1053 10.0.0.12 { - policy round_robin - } -} -~~~ - -With health checks and proxy headers to pass hostname, IP, and scheme upstream: - -~~~ corefile -. { - proxy . 10.0.0.11:53 10.0.0.11:53 10.0.0.12:53 { - policy round_robin - health_check /health:8080 - } -} -~~~ - -Proxy everything except requests to miek.nl or example.org - -~~~ -. { - proxy . 10.0.0.10:1234 { - except miek.nl example.org - } -} -~~~ - -Proxy everything except `example.org` using the host's `resolv.conf`'s nameservers: - -~~~ corefile -. { - proxy . /etc/resolv.conf { - except example.org - } -} -~~~ diff --git a/plugin/proxy/dns.go b/plugin/proxy/dns.go deleted file mode 100644 index a96762c1d..000000000 --- a/plugin/proxy/dns.go +++ /dev/null @@ -1,100 +0,0 @@ -package proxy - -import ( - "context" - "net" - "time" - - "github.com/coredns/coredns/request" - - "github.com/miekg/dns" -) - -type dnsEx struct { - Timeout time.Duration - Options -} - -// Options define the options understood by dns.Exchange. -type Options struct { - ForceTCP bool // If true use TCP for upstream no matter what -} - -func newDNSEx() *dnsEx { - return newDNSExWithOption(Options{}) -} - -func newDNSExWithOption(opt Options) *dnsEx { - return &dnsEx{Timeout: defaultTimeout * time.Second, Options: opt} -} - -func (d *dnsEx) Transport() string { - if d.Options.ForceTCP { - return "tcp" - } - - // The protocol will be determined by `state.Proto()` during Exchange. - return "" -} -func (d *dnsEx) Protocol() string { return "dns" } -func (d *dnsEx) OnShutdown(p *Proxy) error { return nil } -func (d *dnsEx) OnStartup(p *Proxy) error { return nil } - -// Exchange implements the Exchanger interface. -func (d *dnsEx) Exchange(ctx context.Context, addr string, state request.Request) (*dns.Msg, error) { - proto := state.Proto() - if d.Options.ForceTCP { - proto = "tcp" - } - co, err := net.DialTimeout(proto, addr, d.Timeout) - if err != nil { - return nil, err - } - - reply, _, err := d.ExchangeConn(state.Req, co) - - co.Close() - - if reply != nil && reply.Truncated { - // Suppress proxy error for truncated responses - err = nil - } - - if err != nil { - return nil, err - } - reply.Id = state.Req.Id - - return reply, nil -} - -func (d *dnsEx) ExchangeConn(m *dns.Msg, co net.Conn) (*dns.Msg, time.Duration, error) { - start := time.Now() - r, err := exchange(m, co) - rtt := time.Since(start) - - return r, rtt, err -} - -func exchange(m *dns.Msg, co net.Conn) (*dns.Msg, error) { - opt := m.IsEdns0() - - udpsize := uint16(dns.MinMsgSize) - // If EDNS0 is used use that for size. - if opt != nil && opt.UDPSize() >= dns.MinMsgSize { - udpsize = opt.UDPSize() - } - - dnsco := &dns.Conn{Conn: co, UDPSize: udpsize} - - writeDeadline := time.Now().Add(defaultTimeout) - dnsco.SetWriteDeadline(writeDeadline) - if err := dnsco.WriteMsg(m); err != nil { - log.Debugf("Failed to send message: %v", err) - return nil, err - } - - readDeadline := time.Now().Add(defaultTimeout) - co.SetReadDeadline(readDeadline) - return dnsco.ReadMsg() -} diff --git a/plugin/proxy/dnstap.go b/plugin/proxy/dnstap.go deleted file mode 100644 index 4e5a8bd40..000000000 --- a/plugin/proxy/dnstap.go +++ /dev/null @@ -1,56 +0,0 @@ -package proxy - -import ( - "context" - "time" - - "github.com/coredns/coredns/plugin/dnstap" - "github.com/coredns/coredns/plugin/dnstap/msg" - "github.com/coredns/coredns/request" - - tap "github.com/dnstap/golang-dnstap" - "github.com/miekg/dns" -) - -func toDnstap(ctx context.Context, host string, ex Exchanger, state request.Request, reply *dns.Msg, start time.Time) error { - tapper := dnstap.TapperFromContext(ctx) - if tapper == nil { - return nil - } - - // Query - b := msg.New().Time(start).HostPort(host) - - t := ex.Transport() - if t == "" { - t = state.Proto() - } - if t == "tcp" { - b.SocketProto = tap.SocketProtocol_TCP - } else { - b.SocketProto = tap.SocketProtocol_UDP - } - - if tapper.Pack() { - b.Msg(state.Req) - } - m, err := b.ToOutsideQuery(tap.Message_FORWARDER_QUERY) - if err != nil { - return err - } - tapper.TapMessage(m) - - // Response - if reply != nil { - if tapper.Pack() { - b.Msg(reply) - } - m, err := b.Time(time.Now()).ToOutsideResponse(tap.Message_FORWARDER_RESPONSE) - if err != nil { - return err - } - tapper.TapMessage(m) - } - - return nil -} diff --git a/plugin/proxy/dnstap_test.go b/plugin/proxy/dnstap_test.go deleted file mode 100644 index b07b081bc..000000000 --- a/plugin/proxy/dnstap_test.go +++ /dev/null @@ -1,57 +0,0 @@ -package proxy - -import ( - "context" - "testing" - "time" - - "github.com/coredns/coredns/plugin/dnstap/msg" - "github.com/coredns/coredns/plugin/dnstap/test" - mwtest "github.com/coredns/coredns/plugin/test" - "github.com/coredns/coredns/request" - - tap "github.com/dnstap/golang-dnstap" - "github.com/miekg/dns" -) - -func testCase(t *testing.T, ex Exchanger, q, r *dns.Msg, datq, datr *msg.Builder) { - tapq, _ := datq.ToOutsideQuery(tap.Message_FORWARDER_QUERY) - tapr, _ := datr.ToOutsideResponse(tap.Message_FORWARDER_RESPONSE) - ctx := test.Context{} - err := toDnstap(&ctx, "10.240.0.1:40212", ex, - request.Request{W: &mwtest.ResponseWriter{}, Req: q}, r, time.Now()) - if err != nil { - t.Fatal(err) - } - if len(ctx.Trap) != 2 { - t.Fatalf("Messages: %d", len(ctx.Trap)) - } - if !test.MsgEqual(ctx.Trap[0], tapq) { - t.Errorf("Want: %v\nhave: %v", tapq, ctx.Trap[0]) - } - if !test.MsgEqual(ctx.Trap[1], tapr) { - t.Errorf("Want: %v\nhave: %v", tapr, ctx.Trap[1]) - } -} - -func TestDnstap(t *testing.T) { - q := mwtest.Case{Qname: "example.org", Qtype: dns.TypeA}.Msg() - r := mwtest.Case{ - Qname: "example.org.", Qtype: dns.TypeA, - Answer: []dns.RR{ - mwtest.A("example.org. 3600 IN A 10.0.0.1"), - }, - }.Msg() - tapq, tapr := test.TestingData(), test.TestingData() - testCase(t, newDNSEx(), q, r, tapq, tapr) - tapq.SocketProto = tap.SocketProtocol_TCP - tapr.SocketProto = tap.SocketProtocol_TCP - testCase(t, newDNSExWithOption(Options{ForceTCP: true}), q, r, tapq, tapr) -} - -func TestNoDnstap(t *testing.T) { - err := toDnstap(context.TODO(), "", nil, request.Request{}, nil, time.Now()) - if err != nil { - t.Fatal(err) - } -} diff --git a/plugin/proxy/down.go b/plugin/proxy/down.go deleted file mode 100644 index 11f839b46..000000000 --- a/plugin/proxy/down.go +++ /dev/null @@ -1,18 +0,0 @@ -package proxy - -import ( - "sync/atomic" - - "github.com/coredns/coredns/plugin/pkg/healthcheck" -) - -// Default CheckDown functions for use in the proxy plugin. -var checkDownFunc = func(upstream *staticUpstream) healthcheck.UpstreamHostDownFunc { - return func(uh *healthcheck.UpstreamHost) bool { - fails := atomic.LoadInt32(&uh.Fails) - if fails >= upstream.MaxFails && upstream.MaxFails != 0 { - return true - } - return false - } -} diff --git a/plugin/proxy/exchanger.go b/plugin/proxy/exchanger.go deleted file mode 100644 index e96dac5a4..000000000 --- a/plugin/proxy/exchanger.go +++ /dev/null @@ -1,23 +0,0 @@ -package proxy - -import ( - "context" - - "github.com/coredns/coredns/request" - - "github.com/miekg/dns" -) - -// Exchanger is an interface that specifies a type implementing a DNS resolver that -// can use whatever transport it likes. -type Exchanger interface { - Exchange(ctx context.Context, addr string, state request.Request) (*dns.Msg, error) - Protocol() string - - // Transport returns the only transport protocol used by this Exchanger or "". - // If the return value is "", Exchange must use `state.Proto()`. - Transport() string - - OnStartup(*Proxy) error - OnShutdown(*Proxy) error -} diff --git a/plugin/proxy/fuzz.go b/plugin/proxy/fuzz.go deleted file mode 100644 index bb8e07f8a..000000000 --- a/plugin/proxy/fuzz.go +++ /dev/null @@ -1,21 +0,0 @@ -// +build fuzz - -package proxy - -import ( - "github.com/coredns/coredns/plugin/pkg/fuzz" - - "github.com/mholt/caddy" -) - -// Fuzz fuzzes proxy. -func Fuzz(data []byte) int { - c := caddy.NewTestController("dns", "proxy . 8.8.8.8:53") - up, err := NewStaticUpstreams(&c.Dispenser) - if err != nil { - return 0 - } - p := &Proxy{Upstreams: &up} - - return fuzz.Do(p, data) -} diff --git a/plugin/proxy/grpc.go b/plugin/proxy/grpc.go deleted file mode 100644 index dc388c91e..000000000 --- a/plugin/proxy/grpc.go +++ /dev/null @@ -1,99 +0,0 @@ -package proxy - -import ( - "context" - "crypto/tls" - "fmt" - - "github.com/coredns/coredns/pb" - "github.com/coredns/coredns/plugin/pkg/trace" - "github.com/coredns/coredns/request" - - "github.com/grpc-ecosystem/grpc-opentracing/go/otgrpc" - "github.com/miekg/dns" - opentracing "github.com/opentracing/opentracing-go" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials" -) - -type grpcClient struct { - dialOpts []grpc.DialOption - clients map[string]pb.DnsServiceClient - conns []*grpc.ClientConn - upstream *staticUpstream -} - -func newGrpcClient(tls *tls.Config, u *staticUpstream) *grpcClient { - g := &grpcClient{upstream: u} - - if tls == nil { - g.dialOpts = append(g.dialOpts, grpc.WithInsecure()) - } else { - g.dialOpts = append(g.dialOpts, grpc.WithTransportCredentials(credentials.NewTLS(tls))) - } - g.clients = map[string]pb.DnsServiceClient{} - - return g -} - -func (g *grpcClient) Exchange(ctx context.Context, addr string, state request.Request) (*dns.Msg, error) { - msg, err := state.Req.Pack() - if err != nil { - return nil, err - } - - if cl, ok := g.clients[addr]; ok { - reply, err := cl.Query(ctx, &pb.DnsPacket{Msg: msg}) - if err != nil { - return nil, err - } - d := new(dns.Msg) - err = d.Unpack(reply.Msg) - if err != nil { - return nil, err - } - return d, nil - } - return nil, fmt.Errorf("grpc exchange - no connection available for host: %s ", addr) -} - -func (g *grpcClient) Transport() string { return "tcp" } - -func (g *grpcClient) Protocol() string { return "grpc" } - -func (g *grpcClient) OnShutdown(p *Proxy) error { - g.clients = map[string]pb.DnsServiceClient{} - for i, conn := range g.conns { - err := conn.Close() - if err != nil { - log.Warningf("Error closing connection %d: %s\n", i, err) - } - } - g.conns = []*grpc.ClientConn{} - return nil -} - -func (g *grpcClient) OnStartup(p *Proxy) error { - dialOpts := g.dialOpts - if p.Trace != nil { - if t, ok := p.Trace.(trace.Trace); ok { - onlyIfParent := func(parentSpanCtx opentracing.SpanContext, method string, req, resp interface{}) bool { - return parentSpanCtx != nil - } - intercept := otgrpc.OpenTracingClientInterceptor(t.Tracer(), otgrpc.IncludingSpans(onlyIfParent)) - dialOpts = append(dialOpts, grpc.WithUnaryInterceptor(intercept)) - } else { - log.Warningf("Wrong type for trace plugin reference: %s", p.Trace) - } - } - for _, host := range g.upstream.Hosts { - conn, err := grpc.Dial(host.Name, dialOpts...) - if err != nil { - log.Warningf("Skipping gRPC host '%s' due to Dial error: %s\n", host.Name, err) - } else { - g.clients[host.Name] = pb.NewDnsServiceClient(conn) - g.conns = append(g.conns, conn) - } - } - return nil -} diff --git a/plugin/proxy/grpc_test.go b/plugin/proxy/grpc_test.go deleted file mode 100644 index ec54375d3..000000000 --- a/plugin/proxy/grpc_test.go +++ /dev/null @@ -1,220 +0,0 @@ -package proxy - -import ( - "context" - "fmt" - "testing" - - "github.com/coredns/coredns/plugin/pkg/healthcheck" - "github.com/coredns/coredns/plugin/pkg/tls" - "github.com/coredns/coredns/plugin/test" - "github.com/coredns/coredns/request" - - "github.com/miekg/dns" - "google.golang.org/grpc/grpclog" -) - -func init() { - grpclog.SetLoggerV2(discardV2{}) -} - -func buildPool(size int) ([]*healthcheck.UpstreamHost, func(), error) { - ups := make([]*healthcheck.UpstreamHost, size) - srvs := []*dns.Server{} - errs := []error{} - for i := 0; i < size; i++ { - srv, addr, err := test.TCPServer("localhost:0") - if err != nil { - errs = append(errs, err) - continue - } - ups[i] = &healthcheck.UpstreamHost{Name: addr} - srvs = append(srvs, srv) - } - stopIt := func() { - for _, s := range srvs { - s.Shutdown() - } - } - if len(errs) > 0 { - go stopIt() - valErr := "" - for _, e := range errs { - valErr += fmt.Sprintf("%v\n", e) - } - return nil, nil, fmt.Errorf("error at allocation of the pool : %v", valErr) - } - return ups, stopIt, nil -} - -func TestGRPCStartupShutdown(t *testing.T) { - - pool, closePool, err := buildPool(2) - if err != nil { - t.Fatalf("Error creating the pool of upstream for the test : %s", err) - } - defer closePool() - - upstream := &staticUpstream{ - from: ".", - HealthCheck: healthcheck.HealthCheck{ - Hosts: pool, - }, - } - g := newGrpcClient(nil, upstream) - upstream.ex = g - - p := &Proxy{} - p.Upstreams = &[]Upstream{upstream} - - err = g.OnStartup(p) - if err != nil { - t.Fatalf("Error starting grpc client exchanger: %s", err) - } - if len(g.clients) != len(pool) { - t.Fatalf("Expected %d grpc clients but found %d", len(pool), len(g.clients)) - } - - err = g.OnShutdown(p) - if err != nil { - t.Fatalf("Error stopping grpc client exchanger: %s", err) - } - if len(g.clients) != 0 { - t.Errorf("Shutdown didn't remove clients, found %d", len(g.clients)) - } - if len(g.conns) != 0 { - t.Errorf("Shutdown didn't remove conns, found %d", len(g.conns)) - } -} - -func TestGRPCRunAQuery(t *testing.T) { - - pool, closePool, err := buildPool(2) - if err != nil { - t.Fatalf("Error creating the pool of upstream for the test : %s", err) - } - defer closePool() - - upstream := &staticUpstream{ - from: ".", - HealthCheck: healthcheck.HealthCheck{ - Hosts: pool, - }, - } - g := newGrpcClient(nil, upstream) - upstream.ex = g - - p := &Proxy{} - p.Upstreams = &[]Upstream{upstream} - - err = g.OnStartup(p) - if err != nil { - t.Fatalf("Error starting grpc client exchanger: %s", err) - } - // verify the client is usable, or an error is properly raised - state := request.Request{W: &test.ResponseWriter{}, Req: new(dns.Msg)} - g.Exchange(context.TODO(), "localhost:10053", state) - - // verify that you have proper error if the hostname is unknwn or not registered - _, err = g.Exchange(context.TODO(), "invalid:10055", state) - if err == nil { - t.Errorf("Expecting a proper error when querying gRPC client with invalid hostname : %s", err) - } - - err = g.OnShutdown(p) - if err != nil { - t.Fatalf("Error stopping grpc client exchanger: %s", err) - } -} - -func TestGRPCRunAQueryOnSecureLinkWithInvalidCert(t *testing.T) { - - pool, closePool, err := buildPool(1) - if err != nil { - t.Fatalf("Error creating the pool of upstream for the test : %s", err) - } - defer closePool() - - upstream := &staticUpstream{ - from: ".", - HealthCheck: healthcheck.HealthCheck{ - Hosts: pool, - }, - } - - filename, rmFunc, err := test.TempFile("", aCert) - if err != nil { - t.Errorf("Error saving file : %s", err) - return - } - defer rmFunc() - - tls, _ := tls.NewTLSClientConfig(filename) - // ignore error as the certificate is known valid - - g := newGrpcClient(tls, upstream) - upstream.ex = g - - p := &Proxy{} - p.Upstreams = &[]Upstream{upstream} - - // Although dial will not work, it is not expected to have an error - err = g.OnStartup(p) - if err != nil { - t.Fatalf("Error starting grpc client exchanger: %s", err) - } - - // verify that you have proper error if the hostname is unknwn or not registered - state := request.Request{W: &test.ResponseWriter{}, Req: new(dns.Msg)} - _, err = g.Exchange(context.TODO(), pool[0].Name+"-whatever", state) - if err == nil { - t.Errorf("Error in Exchange process : %s ", err) - } - - err = g.OnShutdown(p) - if err != nil { - t.Fatalf("Error stopping grpc client exchanger: %s", err) - } -} - -// discard is a Logger that outputs nothing. -type discardV2 struct{} - -func (d discardV2) Info(args ...interface{}) {} -func (d discardV2) Infoln(args ...interface{}) {} -func (d discardV2) Infof(format string, args ...interface{}) {} -func (d discardV2) Warning(args ...interface{}) {} -func (d discardV2) Warningln(args ...interface{}) {} -func (d discardV2) Warningf(format string, args ...interface{}) {} -func (d discardV2) Error(args ...interface{}) {} -func (d discardV2) Errorln(args ...interface{}) {} -func (d discardV2) Errorf(format string, args ...interface{}) {} -func (d discardV2) Fatal(args ...interface{}) {} -func (d discardV2) Fatalln(args ...interface{}) {} -func (d discardV2) Fatalf(format string, args ...interface{}) {} -func (d discardV2) V(l int) bool { return true } - -const ( - aCert = `-----BEGIN CERTIFICATE----- - MIIDlDCCAnygAwIBAgIJAPaRnBJUE/FVMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV -BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX -aWRnaXRzIFB0eSBMdGQwHhcNMTcxMTI0MTM0OTQ3WhcNMTgxMTI0MTM0OTQ3WjBF -MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 -ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB -CgKCAQEAuTDeAoWS6tdZVcp/Vh3FlagbC+9Ohi5VjRXgkpcn9JopbcF5s2jpl1v+ -cRpqkrmNNKLh8qOhmgdZQdh185VNe/iZ94H42qwKZ48vvnC5hLkk3MdgUT2ewgup -vZhy/Bb1bX+buCWkQa1u8SIilECMIPZHhBP4TuBUKJWK8bBEFAeUnxB5SCkX+un4 -pctRlcfg8sX/ghADnp4e//YYDqex+1wQdFqM5zWhWDZAzc5Kdkyy9r+xXNfo4s1h -fI08f6F4skz1koxG2RXOzQ7OK4YxFwT2J6V72iyzUIlRGZTbYDvair/zm1kjTF1R -B1B+XLJF9oIB4BMZbekf033ZVaQ8YwIDAQABo4GGMIGDMDMGA1UdEQQsMCqHBH8A -AAGHBDR3AQGHBDR3AQCHBDR3KmSHBDR3KGSHBDR3KmWHBDR3KtIwHQYDVR0OBBYE -FFAEccLm7D/rN3fEe1fwzH7p0spAMB8GA1UdIwQYMBaAFFAEccLm7D/rN3fEe1fw -zH7p0spAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAF4zqaucNcK2 -GwYfijwbbtgMqPEvbReUEXsC65riAPjksJQ9L2YxQ7K0RIugRizuD1DNQam+FSb0 -cZEMEKzvMUIexbhZNFINWXY2X9yUS/oZd5pWP0WYIhn6qhmLvzl9XpxNPVzBXYWe -duMECCigU2x5tAGmFa6g/pXXOoZCBRzFXwXiuNhSyhJEEwODjLZ6vgbySuU2jso3 -va4FKFDdVM16s1/RYOK5oM48XytCMB/JoYoSJHPfpt8LpVNAQEHMvPvHwuZBON/z -q8HFtDjT4pBpB8AfuzwtUZ/zJ5atwxa5+ahcqRnK2kX2RSINfyEy43FZjLlvjcGa -UIRTUJK1JKg= ------END CERTIFICATE-----` -) diff --git a/plugin/proxy/log_test.go b/plugin/proxy/log_test.go deleted file mode 100644 index e0c50773b..000000000 --- a/plugin/proxy/log_test.go +++ /dev/null @@ -1,5 +0,0 @@ -package proxy - -import clog "github.com/coredns/coredns/plugin/pkg/log" - -func init() { clog.Discard() } diff --git a/plugin/proxy/metrics.go b/plugin/proxy/metrics.go deleted file mode 100644 index 0bea5ae4b..000000000 --- a/plugin/proxy/metrics.go +++ /dev/null @@ -1,36 +0,0 @@ -package proxy - -import ( - "github.com/coredns/coredns/plugin" - - "github.com/prometheus/client_golang/prometheus" -) - -// Metrics the proxy plugin exports. -var ( - RequestCount = prometheus.NewCounterVec(prometheus.CounterOpts{ - Namespace: plugin.Namespace, - Subsystem: "proxy", - Name: "request_count_total", - Help: "Counter of requests made per protocol, proxy protocol, family and upstream.", - }, []string{"server", "proto", "proxy_proto", "family", "to"}) - RequestDuration = prometheus.NewHistogramVec(prometheus.HistogramOpts{ - Namespace: plugin.Namespace, - Subsystem: "proxy", - Name: "request_duration_seconds", - Buckets: plugin.TimeBuckets, - Help: "Histogram of the time (in seconds) each request took.", - }, []string{"server", "proto", "proxy_proto", "family", "to"}) -) - -// familyToString returns the string form of either 1, or 2. Returns -// empty string is not a known family -func familyToString(f int) string { - if f == 1 { - return "1" - } - if f == 2 { - return "2" - } - return "" -} diff --git a/plugin/proxy/proxy.go b/plugin/proxy/proxy.go deleted file mode 100644 index ad333fbd1..000000000 --- a/plugin/proxy/proxy.go +++ /dev/null @@ -1,183 +0,0 @@ -// Package proxy is plugin that proxies requests. -package proxy - -import ( - "context" - "errors" - "fmt" - "net" - "sync/atomic" - "time" - - "github.com/coredns/coredns/plugin" - "github.com/coredns/coredns/plugin/metrics" - "github.com/coredns/coredns/plugin/pkg/healthcheck" - "github.com/coredns/coredns/request" - - "github.com/miekg/dns" - ot "github.com/opentracing/opentracing-go" -) - -var ( - errUnreachable = errors.New("unreachable backend") - errInvalidProtocol = errors.New("invalid protocol") - errInvalidDomain = errors.New("invalid path for proxy") -) - -// Proxy represents a plugin instance that can proxy requests to another (DNS) server. -type Proxy struct { - Next plugin.Handler - - // Upstreams is a pointer to a slice, so we can update the upstream (used for Google) - // midway. - - Upstreams *[]Upstream - - // Trace is the Trace plugin, if it is installed - // This is used by the grpc exchanger to trace through the grpc calls - Trace plugin.Handler -} - -// Upstream manages a pool of proxy upstream hosts. Select should return a -// suitable upstream host, or nil if no such hosts are available. -type Upstream interface { - // The domain name this upstream host should be routed on. - From() string - // Selects an upstream host to be routed to. - Select() *healthcheck.UpstreamHost - // Checks if subdomain is not an ignored. - IsAllowedDomain(string) bool - // Exchanger returns the exchanger to be used for this upstream. - Exchanger() Exchanger - // Stops the upstream from proxying requests to shutdown goroutines cleanly. - Stop() error -} - -// tryDuration is how long to try upstream hosts; failures result in -// immediate retries until this duration ends or we get a nil host. -var tryDuration = 16 * time.Second - -// ServeDNS satisfies the plugin.Handler interface. -func (p Proxy) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { - var span, child ot.Span - span = ot.SpanFromContext(ctx) - state := request.Request{W: w, Req: r} - - upstream := p.match(state) - if upstream == nil { - return plugin.NextOrFailure(p.Name(), p.Next, ctx, w, r) - } - - for { - start := time.Now() - var reply *dns.Msg - var backendErr error - - // Since Select() should give us "up" hosts, keep retrying - // hosts until timeout (or until we get a nil host). - for time.Since(start) < tryDuration { - host := upstream.Select() - if host == nil { - return dns.RcodeServerFailure, fmt.Errorf("%s: %s", errUnreachable, "no upstream host") - } - - if span != nil { - child = span.Tracer().StartSpan("exchange", ot.ChildOf(span.Context())) - ctx = ot.ContextWithSpan(ctx, child) - } - - atomic.AddInt64(&host.Conns, 1) - - RequestCount.WithLabelValues(metrics.WithServer(ctx), state.Proto(), upstream.Exchanger().Protocol(), familyToString(state.Family()), host.Name).Add(1) - - reply, backendErr = upstream.Exchanger().Exchange(ctx, host.Name, state) - - atomic.AddInt64(&host.Conns, -1) - - if child != nil { - child.Finish() - } - - taperr := toDnstap(ctx, host.Name, upstream.Exchanger(), state, reply, start) - - if backendErr == nil { - - // Check if the reply is correct; if not return FormErr. - if !state.Match(reply) { - formerr := state.ErrorMessage(dns.RcodeFormatError) - w.WriteMsg(formerr) - return 0, taperr - } - - w.WriteMsg(reply) - - RequestDuration.WithLabelValues(metrics.WithServer(ctx), state.Proto(), upstream.Exchanger().Protocol(), familyToString(state.Family()), host.Name).Observe(time.Since(start).Seconds()) - - return 0, taperr - } - - // A "ANY isc.org" query is being dropped by ISC's nameserver, we see this as a i/o timeout, but - // would then mark our upstream is being broken. We should not do this if we consider the error temporary. - // Of course it could really be that our upstream is broken - if oe, ok := backendErr.(*net.OpError); ok { - // Note this keeps looping and trying until tryDuration is hit, at which point our client - // might be long gone... - if oe.Timeout() { - // Our upstream's upstream is probably messing up, continue with next selected - // host - which my be the *same* one as we don't set any uh.Fails. - continue - } - } - - timeout := host.FailTimeout - if timeout == 0 { - timeout = defaultFailTimeout - } - - atomic.AddInt32(&host.Fails, 1) - fails := atomic.LoadInt32(&host.Fails) - - go func(host *healthcheck.UpstreamHost, timeout time.Duration) { - time.Sleep(timeout) - // we may go negative here, should be rectified by the HC. - atomic.AddInt32(&host.Fails, -1) - if fails%failureCheck == 0 { // Kick off healthcheck on every third failure. - host.HealthCheckURL() - } - }(host, timeout) - } - - return dns.RcodeServerFailure, fmt.Errorf("%s: %s", errUnreachable, backendErr) - } -} - -func (p Proxy) match(state request.Request) (u Upstream) { - if p.Upstreams == nil { - return nil - } - - longestMatch := 0 - for _, upstream := range *p.Upstreams { - from := upstream.From() - - if !plugin.Name(from).Matches(state.Name()) || !upstream.IsAllowedDomain(state.Name()) { - continue - } - - if lf := len(from); lf > longestMatch { - longestMatch = lf - u = upstream - } - } - return u - -} - -// Name implements the Handler interface. -func (p Proxy) Name() string { return "proxy" } - -const ( - defaultFailTimeout = 2 * time.Second - defaultTimeout = 5 * time.Second - failureCheck = 3 -) diff --git a/plugin/proxy/proxy_test.go b/plugin/proxy/proxy_test.go deleted file mode 100644 index 0d29c2329..000000000 --- a/plugin/proxy/proxy_test.go +++ /dev/null @@ -1,72 +0,0 @@ -package proxy - -import ( - "fmt" - "net/http" - "net/http/httptest" - "strings" - "sync/atomic" - "testing" - "time" - - "github.com/mholt/caddy/caddyfile" -) - -func TestStop(t *testing.T) { - config := "proxy . %s {\n health_check /healthcheck:%s %dms \n}" - tests := []struct { - intervalInMilliseconds int - numHealthcheckIntervals int - }{ - {5, 1}, - {5, 2}, - {5, 3}, - } - - for i, test := range tests { - t.Run(fmt.Sprintf("Test %d", i), func(t *testing.T) { - - // Set up proxy. - var counter int64 - backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - r.Body.Close() - atomic.AddInt64(&counter, 1) - })) - - defer backend.Close() - - port := backend.URL[17:] // Remove all crap up to the port - back := backend.URL[7:] // Remove http:// - c := caddyfile.NewDispenser("Testfile", strings.NewReader(fmt.Sprintf(config, back, port, test.intervalInMilliseconds))) - upstreams, err := NewStaticUpstreams(&c) - if err != nil { - t.Errorf("Test %d, expected no error. Got: %s", i, err) - } - - // Give some time for healthchecks to hit the server. - time.Sleep(time.Duration(test.intervalInMilliseconds*test.numHealthcheckIntervals) * time.Millisecond) - - for _, upstream := range upstreams { - if err := upstream.Stop(); err != nil { - t.Errorf("Test %d, expected no error stopping upstream, got: %s", i, err) - } - } - - counterAfterShutdown := atomic.LoadInt64(&counter) - - // Give some time to see if healthchecks are still hitting the server. - time.Sleep(time.Duration(test.intervalInMilliseconds*test.numHealthcheckIntervals) * time.Millisecond) - - if counterAfterShutdown == 0 { - t.Errorf("Test %d, Expected healthchecks to hit test server, got none", i) - } - - // health checks are in a go routine now, so one may well occur after we shutdown, - // but we only ever expect one more - counterAfterWaiting := atomic.LoadInt64(&counter) - if counterAfterWaiting > (counterAfterShutdown + 1) { - t.Errorf("Test %d, expected no more healthchecks after shutdown. got: %d healthchecks after shutdown", i, counterAfterWaiting-counterAfterShutdown) - } - }) - } -} diff --git a/plugin/proxy/response.go b/plugin/proxy/response.go deleted file mode 100644 index 2ad553c41..000000000 --- a/plugin/proxy/response.go +++ /dev/null @@ -1,21 +0,0 @@ -package proxy - -import ( - "net" - - "github.com/miekg/dns" -) - -type fakeBootWriter struct { - dns.ResponseWriter -} - -func (w *fakeBootWriter) LocalAddr() net.Addr { - local := net.ParseIP("127.0.0.1") - return &net.UDPAddr{IP: local, Port: 53} // Port is not used here -} - -func (w *fakeBootWriter) RemoteAddr() net.Addr { - remote := net.ParseIP("8.8.8.8") - return &net.UDPAddr{IP: remote, Port: 53} // Port is not used here -} diff --git a/plugin/proxy/setup.go b/plugin/proxy/setup.go deleted file mode 100644 index 2f3d7dd62..000000000 --- a/plugin/proxy/setup.go +++ /dev/null @@ -1,53 +0,0 @@ -package proxy - -import ( - "github.com/coredns/coredns/core/dnsserver" - "github.com/coredns/coredns/plugin" - "github.com/coredns/coredns/plugin/metrics" - clog "github.com/coredns/coredns/plugin/pkg/log" - - "github.com/mholt/caddy" -) - -var log = clog.NewWithPlugin("proxy") - -func init() { - caddy.RegisterPlugin("proxy", caddy.Plugin{ - ServerType: "dns", - Action: setup, - }) -} - -func setup(c *caddy.Controller) error { - upstreams, err := NewStaticUpstreams(&c.Dispenser) - if err != nil { - return plugin.Error("proxy", err) - } - - t := dnsserver.GetConfig(c).Handler("trace") - P := &Proxy{Trace: t} - dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler { - P.Next = next - P.Upstreams = &upstreams - return P - }) - - c.OnStartup(func() error { - metrics.MustRegister(c, RequestCount, RequestDuration) - return nil - }) - - for i := range upstreams { - u := upstreams[i] - c.OnStartup(func() error { - return u.Exchanger().OnStartup(P) - }) - c.OnShutdown(func() error { - return u.Exchanger().OnShutdown(P) - }) - // Register shutdown handlers. - c.OnShutdown(u.Stop) - } - - return nil -} diff --git a/plugin/proxy/upstream.go b/plugin/proxy/upstream.go deleted file mode 100644 index 0d62f45e2..000000000 --- a/plugin/proxy/upstream.go +++ /dev/null @@ -1,203 +0,0 @@ -package proxy - -import ( - "fmt" - "net" - "strconv" - "time" - - "github.com/coredns/coredns/plugin" - "github.com/coredns/coredns/plugin/pkg/healthcheck" - "github.com/coredns/coredns/plugin/pkg/parse" - "github.com/coredns/coredns/plugin/pkg/tls" - "github.com/mholt/caddy/caddyfile" - "github.com/miekg/dns" -) - -type staticUpstream struct { - from string - - healthcheck.HealthCheck - - IgnoredSubDomains []string - ex Exchanger -} - -// NewStaticUpstreams parses the configuration input and sets up -// static upstreams for the proxy plugin. -func NewStaticUpstreams(c *caddyfile.Dispenser) ([]Upstream, error) { - var upstreams []Upstream - for c.Next() { - u, err := NewStaticUpstream(c) - if err != nil { - return upstreams, err - } - upstreams = append(upstreams, u) - } - return upstreams, nil -} - -// NewStaticUpstream parses the configuration of a single upstream -// starting from the FROM -func NewStaticUpstream(c *caddyfile.Dispenser) (Upstream, error) { - upstream := &staticUpstream{ - from: ".", - HealthCheck: healthcheck.HealthCheck{ - FailTimeout: 5 * time.Second, - MaxFails: 3, - }, - ex: newDNSEx(), - } - - if !c.Args(&upstream.from) { - return upstream, c.ArgErr() - } - upstream.from = plugin.Host(upstream.from).Normalize() - - to := c.RemainingArgs() - if len(to) == 0 { - return upstream, c.ArgErr() - } - - // process the host list, substituting in any nameservers in files - toHosts, err := parse.HostPortOrFile(to...) - if err != nil { - return upstream, err - } - - if len(toHosts) > max { - return upstream, fmt.Errorf("more than %d TOs configured: %d", max, len(toHosts)) - } - - for c.NextBlock() { - if err := parseBlock(c, upstream); err != nil { - return upstream, err - } - } - - upstream.Hosts = make([]*healthcheck.UpstreamHost, len(toHosts)) - - for i, host := range toHosts { - uh := &healthcheck.UpstreamHost{ - Name: host, - FailTimeout: upstream.FailTimeout, - CheckDown: checkDownFunc(upstream), - } - upstream.Hosts[i] = uh - } - upstream.Start() - - return upstream, nil -} - -func parseBlock(c *caddyfile.Dispenser, u *staticUpstream) error { - switch c.Val() { - case "policy": - if !c.NextArg() { - return c.ArgErr() - } - policyCreateFunc, ok := healthcheck.SupportedPolicies[c.Val()] - if !ok { - return c.ArgErr() - } - u.Policy = policyCreateFunc() - case "fail_timeout": - if !c.NextArg() { - return c.ArgErr() - } - dur, err := time.ParseDuration(c.Val()) - if err != nil { - return err - } - u.FailTimeout = dur - case "max_fails": - if !c.NextArg() { - return c.ArgErr() - } - n, err := strconv.Atoi(c.Val()) - if err != nil { - return err - } - u.MaxFails = int32(n) - case "health_check": - if !c.NextArg() { - return c.ArgErr() - } - var err error - u.HealthCheck.Path, u.HealthCheck.Port, err = net.SplitHostPort(c.Val()) - if err != nil { - return err - } - u.HealthCheck.Interval = 4 * time.Second - if c.NextArg() { - dur, err := time.ParseDuration(c.Val()) - if err != nil { - return err - } - u.HealthCheck.Interval = dur - } - case "except": - ignoredDomains := c.RemainingArgs() - if len(ignoredDomains) == 0 { - return c.ArgErr() - } - for i := 0; i < len(ignoredDomains); i++ { - ignoredDomains[i] = plugin.Host(ignoredDomains[i]).Normalize() - } - u.IgnoredSubDomains = ignoredDomains - case "spray": - u.Spray = &healthcheck.Spray{} - case "protocol": - encArgs := c.RemainingArgs() - if len(encArgs) == 0 { - return c.ArgErr() - } - switch encArgs[0] { - case "dns": - if len(encArgs) > 1 { - if encArgs[1] == "force_tcp" { - opts := Options{ForceTCP: true} - u.ex = newDNSExWithOption(opts) - } else { - return fmt.Errorf("only force_tcp allowed as parameter to dns") - } - } else { - u.ex = newDNSEx() - } - case "grpc": - if len(encArgs) == 2 && encArgs[1] == "insecure" { - u.ex = newGrpcClient(nil, u) - return nil - } - tls, err := tls.NewTLSConfigFromArgs(encArgs[1:]...) - if err != nil { - return err - } - u.ex = newGrpcClient(tls, u) - default: - return fmt.Errorf("%s: %s", errInvalidProtocol, encArgs[0]) - } - - default: - return c.Errf("unknown property '%s'", c.Val()) - } - return nil -} - -func (u *staticUpstream) IsAllowedDomain(name string) bool { - if dns.Name(name) == dns.Name(u.From()) { - return true - } - - for _, ignoredSubDomain := range u.IgnoredSubDomains { - if plugin.Name(ignoredSubDomain).Matches(name) { - return false - } - } - return true -} - -func (u *staticUpstream) Exchanger() Exchanger { return u.ex } -func (u *staticUpstream) From() string { return u.from } - -const max = 15 diff --git a/plugin/proxy/upstream_test.go b/plugin/proxy/upstream_test.go deleted file mode 100644 index 3c788b882..000000000 --- a/plugin/proxy/upstream_test.go +++ /dev/null @@ -1,327 +0,0 @@ -package proxy - -import ( - "path/filepath" - "strings" - "testing" - - "github.com/coredns/coredns/plugin/test" - - "github.com/mholt/caddy" -) - -func TestAllowedDomain(t *testing.T) { - upstream := &staticUpstream{ - from: "miek.nl.", - IgnoredSubDomains: []string{"download.miek.nl.", "static.miek.nl."}, // closing dot mandatory - } - tests := []struct { - name string - expected bool - }{ - {"miek.nl.", true}, - {"download.miek.nl.", false}, - {"static.miek.nl.", false}, - {"blaat.miek.nl.", true}, - } - - for i, test := range tests { - isAllowed := upstream.IsAllowedDomain(test.name) - if test.expected != isAllowed { - t.Errorf("Test %d: expected %v found %v for %s", i+1, test.expected, isAllowed, test.name) - } - } -} - -func TestProxyParse(t *testing.T) { - rmFunc, cert, key, ca := getPEMFiles(t) - defer rmFunc() - - grpc1 := "proxy . 8.8.8.8:53 {\n protocol grpc " + ca + "\n}" - grpc2 := "proxy . 8.8.8.8:53 {\n protocol grpc " + cert + " " + key + "\n}" - grpc3 := "proxy . 8.8.8.8:53 {\n protocol grpc " + cert + " " + key + " " + ca + "\n}" - grpc4 := "proxy . 8.8.8.8:53 {\n protocol grpc " + key + "\n}" - - tests := []struct { - inputUpstreams string - shouldErr bool - }{ - { - `proxy . 8.8.8.8:53`, - false, - }, - { - `proxy 10.0.0.0/24 8.8.8.8:53`, - false, - }, - { - ` -proxy . 8.8.8.8:53 { - policy round_robin -}`, - false, - }, - { - ` -proxy . 8.8.8.8:53 { - fail_timeout 5s -}`, - false, - }, - { - ` -proxy . 8.8.8.8:53 { - max_fails 10 -}`, - false, - }, - { - ` -proxy . 8.8.8.8:53 { - health_check /health:8080 -}`, - false, - }, - { - ` -proxy . 8.8.8.8:53 { - except miek.nl example.org 10.0.0.0/24 -}`, - false, - }, - { - ` -proxy . 8.8.8.8:53 { - spray -}`, - false, - }, - { - ` -proxy . 8.8.8.8:53 { - error_option -}`, - true, - }, - { - ` -proxy . some_bogus_filename`, - true, - }, - { - ` -proxy . 8.8.8.8:53 { - protocol dns -}`, - false, - }, - { - ` -proxy . 8.8.8.8:53 { - protocol grpc -}`, - false, - }, - { - ` -proxy . 8.8.8.8:53 { - protocol grpc insecure -}`, - false, - }, - { - ` -proxy . 8.8.8.8:53 { - protocol dns force_tcp -}`, - false, - }, - { - ` -proxy . 8.8.8.8:53 { - protocol grpc a b c d -}`, - true, - }, - { - grpc1, - false, - }, - { - grpc2, - false, - }, - { - grpc3, - false, - }, - { - grpc4, - true, - }, - { - ` -proxy . 8.8.8.8:53 { - protocol foobar -}`, - true, - }, - { - `proxy`, - true, - }, - { - ` -proxy . 8.8.8.8:53 { - protocol foobar -}`, - true, - }, - { - ` -proxy . 8.8.8.8:53 { - policy -}`, - true, - }, - { - ` -proxy . 8.8.8.8:53 { - fail_timeout -}`, - true, - }, - { - ` -proxy . 8.8.8.8:53 { - fail_timeout junky -}`, - true, - }, - { - ` -proxy . 8.8.8.8:53 { - health_check -}`, - true, - }, - { - ` -proxy . 8.8.8.8:53 { - protocol dns force -}`, - true, - }, - } - for i, test := range tests { - c := caddy.NewTestController("dns", test.inputUpstreams) - _, err := NewStaticUpstreams(&c.Dispenser) - if (err != nil) != test.shouldErr { - t.Errorf("Test %d expected no error, got %v for %s", i+1, err, test.inputUpstreams) - } - } -} - -func TestResolvParse(t *testing.T) { - tests := []struct { - inputUpstreams string - filedata string - shouldErr bool - expected []string - }{ - { - ` -proxy . FILE -`, - ` -nameserver 1.2.3.4 -nameserver 4.3.2.1 -`, - false, - []string{"1.2.3.4:53", "4.3.2.1:53"}, - }, - { - ` -proxy example.com 1.1.1.1:5000 -proxy . FILE -proxy example.org 2.2.2.2:1234 -`, - ` -nameserver 1.2.3.4 -`, - false, - []string{"1.1.1.1:5000", "1.2.3.4:53", "2.2.2.2:1234"}, - }, - { - ` -proxy example.com 1.1.1.1:5000 -proxy . FILE -proxy example.org 2.2.2.2:1234 -`, - ` -junky resolv.conf -`, - false, - []string{"1.1.1.1:5000", "2.2.2.2:1234"}, - }, - } - for i, tc := range tests { - - path, rm, err := test.TempFile(".", tc.filedata) - if err != nil { - t.Fatalf("Test %d could not create temp file %v", i, err) - } - defer rm() - - config := strings.Replace(tc.inputUpstreams, "FILE", path, -1) - c := caddy.NewTestController("dns", config) - upstreams, err := NewStaticUpstreams(&c.Dispenser) - if (err != nil) != tc.shouldErr { - t.Errorf("Test %d expected no error, got %v", i+1, err) - } - var hosts []string - for _, u := range upstreams { - for _, h := range u.(*staticUpstream).Hosts { - hosts = append(hosts, h.Name) - } - } - if !tc.shouldErr { - if len(hosts) != len(tc.expected) { - t.Errorf("Test %d expected %d hosts got %d", i+1, len(tc.expected), len(upstreams)) - } else { - ok := true - for i, v := range tc.expected { - if v != hosts[i] { - ok = false - } - } - if !ok { - t.Errorf("Test %d expected %v got %v", i+1, tc.expected, upstreams) - } - } - } - } -} - -func TestMaxTo(t *testing.T) { - // Has 16 IP addresses. - config := `proxy . 1.1.1.1 1.1.1.1 1.1.1.1 1.1.1.1 1.1.1.1 1.1.1.1 1.1.1.1 1.1.1.1 1.1.1.1 1.1.1.1 1.1.1.1 1.1.1.1 1.1.1.1 1.1.1.1 1.1.1.1 1.1.1.1` - c := caddy.NewTestController("dns", config) - _, err := NewStaticUpstreams(&c.Dispenser) - if err == nil { - t.Error("Expected to many TOs configured, but nil") - } -} - -func getPEMFiles(t *testing.T) (rmFunc func(), cert, key, ca string) { - tempDir, rmFunc, err := test.WritePEMFiles("") - if err != nil { - t.Fatalf("Could not write PEM files: %s", err) - } - - cert = filepath.Join(tempDir, "cert.pem") - key = filepath.Join(tempDir, "key.pem") - ca = filepath.Join(tempDir, "ca.pem") - - return -} diff --git a/plugin/template/README.md b/plugin/template/README.md index be099e70c..d52b51656 100644 --- a/plugin/template/README.md +++ b/plugin/template/README.md @@ -92,7 +92,7 @@ The `.invalid` domain is a reserved TLD (see [RFC 2606 Reserved Top Level DNS Na ~~~ corefile . { - proxy . 8.8.8.8 + forward . 8.8.8.8 template ANY ANY invalid { rcode NXDOMAIN @@ -116,7 +116,7 @@ path (`dc1.example.com`) added. ~~~ corefile . { - proxy . 8.8.8.8 + forward . 8.8.8.8 template IN ANY example.com.dc1.example.com { rcode NXDOMAIN @@ -129,7 +129,7 @@ A more verbose regex based equivalent would be ~~~ corefile . { - proxy . 8.8.8.8 + forward . 8.8.8.8 template IN ANY example.com { match "example\.com\.(dc1\.example\.com\.)$" @@ -146,7 +146,7 @@ The regex-based version can do more complex matching/templating while zone-based ~~~ corefile . { - proxy . 8.8.8.8 + forward . 8.8.8.8 # ip-a-b-c-d.example A a.b.c.d @@ -178,7 +178,7 @@ Fallthrough is needed for mixed domains where only some responses are templated. ~~~ corefile . { - proxy . 8.8.8.8 + forward . 8.8.8.8 template IN A example { match "^ip-(?P<a>10)-(?P<b>[0-9]*)-(?P<c>[0-9]*)-(?P<d>[0-9]*)[.]dc[.]example[.]$" @@ -195,7 +195,7 @@ Named capture groups can be used to template one response for multiple patterns. ~~~ corefile . { - proxy . 8.8.8.8 + forward . 8.8.8.8 template IN A example { match ^ip-10-(?P<b>[0-9]*)-(?P<c>[0-9]*)-(?P<d>[0-9]*)[.]example[.]$ @@ -215,7 +215,7 @@ Named capture groups can be used to template one response for multiple patterns. ~~~ corefile . { - proxy . 8.8.8.8 + forward . 8.8.8.8 template IN A example { match ^ip-10-(?P<b>[0-9]*)-(?P<c>[0-9]*)-(?P<d>[0-9]*)[.]example[.]$ diff --git a/plugin/tls/README.md b/plugin/tls/README.md index 8a985cec7..244984750 100644 --- a/plugin/tls/README.md +++ b/plugin/tls/README.md @@ -10,9 +10,6 @@ CoreDNS supports queries that are encrypted using TLS (DNS over Transport Layer or are using gRPC (https://grpc.io/, not an IETF standard). Normally DNS traffic isn't encrypted at all (DNSSEC only signs resource records). -The *proxy* plugin also support gRPC (`protocol gRPC`), meaning you can chain CoreDNS servers -using this protocol. - The *tls* "plugin" allows you to configure the cryptographic keys that are needed for both DNS-over-TLS and DNS-over-gRPC. If the `tls` directive is omitted, then no encryption takes place. @@ -35,7 +32,7 @@ nameservers defined in `/etc/resolv.conf` to resolve the query. This proxy path ~~~ tls://.:5553 { tls cert.pem key.pem ca.pem - proxy . /etc/resolv.conf + forward . /etc/resolv.conf } ~~~ @@ -45,7 +42,7 @@ incoming queries. ~~~ grpc://. { tls cert.pem key.pem ca.pem - proxy . /etc/resolv.conf + forward . /etc/resolv.conf } ~~~ |