| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | package proxy
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import (
 | 
					
						
							| 
									
										
										
										
											2017-01-15 08:12:58 +00:00
										 |  |  | 	"fmt"
 | 
					
						
							| 
									
										
										
										
											2016-04-07 17:42:35 +01:00
										 |  |  | 	"net"
 | 
					
						
							| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | 	"strconv"
 | 
					
						
							|  |  |  | 	"time"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-14 09:36:06 +01:00
										 |  |  | 	"github.com/coredns/coredns/plugin"
 | 
					
						
							|  |  |  | 	"github.com/coredns/coredns/plugin/pkg/dnsutil"
 | 
					
						
							|  |  |  | 	"github.com/coredns/coredns/plugin/pkg/healthcheck"
 | 
					
						
							|  |  |  | 	"github.com/coredns/coredns/plugin/pkg/tls"
 | 
					
						
							| 
									
										
										
										
											2016-08-19 17:14:17 -07:00
										 |  |  | 	"github.com/mholt/caddy/caddyfile"
 | 
					
						
							| 
									
										
										
										
											2016-03-19 16:11:30 +00:00
										 |  |  | 	"github.com/miekg/dns"
 | 
					
						
							| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | )
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type staticUpstream struct {
 | 
					
						
							| 
									
										
										
										
											2017-04-26 10:58:14 +01:00
										 |  |  | 	from string
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-09 09:21:33 -07:00
										 |  |  | 	healthcheck.HealthCheck
 | 
					
						
							| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-19 16:11:30 +00:00
										 |  |  | 	IgnoredSubDomains []string
 | 
					
						
							| 
									
										
										
										
											2017-02-06 19:32:48 +00:00
										 |  |  | 	ex                Exchanger
 | 
					
						
							| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewStaticUpstreams parses the configuration input and sets up
 | 
					
						
							| 
									
										
										
										
											2017-09-14 09:36:06 +01:00
										 |  |  | // static upstreams for the proxy plugin.
 | 
					
						
							| 
									
										
										
										
											2016-09-16 23:49:35 -07:00
										 |  |  | func NewStaticUpstreams(c *caddyfile.Dispenser) ([]Upstream, error) {
 | 
					
						
							| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | 	var upstreams []Upstream
 | 
					
						
							|  |  |  | 	for c.Next() {
 | 
					
						
							| 
									
										
										
										
											2018-01-27 17:25:39 -05:00
										 |  |  | 		u, err := NewStaticUpstream(c)
 | 
					
						
							|  |  |  | 		if err != nil {
 | 
					
						
							|  |  |  | 			return upstreams, err
 | 
					
						
							| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | 		}
 | 
					
						
							| 
									
										
										
										
											2018-01-27 17:25:39 -05:00
										 |  |  | 		upstreams = append(upstreams, u)
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 	return upstreams, nil
 | 
					
						
							|  |  |  | }
 | 
					
						
							| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-27 17:25:39 -05:00
										 |  |  | // 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(),
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2017-08-13 18:16:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-27 17:25:39 -05:00
										 |  |  | 	if !c.Args(&upstream.from) {
 | 
					
						
							|  |  |  | 		return upstream, c.ArgErr()
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 	upstream.from = plugin.Host(upstream.from).Normalize()
 | 
					
						
							| 
									
										
										
										
											2016-10-22 10:52:10 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-27 17:25:39 -05:00
										 |  |  | 	to := c.RemainingArgs()
 | 
					
						
							|  |  |  | 	if len(to) == 0 {
 | 
					
						
							|  |  |  | 		return upstream, c.ArgErr()
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-27 17:25:39 -05:00
										 |  |  | 	// process the host list, substituting in any nameservers in files
 | 
					
						
							|  |  |  | 	toHosts, err := dnsutil.ParseHostPortOrFile(to...)
 | 
					
						
							|  |  |  | 	if err != nil {
 | 
					
						
							|  |  |  | 		return upstream, err
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if len(toHosts) > max {
 | 
					
						
							|  |  |  | 		return upstream, fmt.Errorf("more than %d TOs configured: %d", max, len(toHosts))
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2018-01-08 15:03:42 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-27 17:25:39 -05:00
										 |  |  | 	for c.NextBlock() {
 | 
					
						
							|  |  |  | 		if err := parseBlock(c, upstream); err != nil {
 | 
					
						
							|  |  |  | 			return upstream, err
 | 
					
						
							| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | 		}
 | 
					
						
							| 
									
										
										
										
											2018-01-27 17:25:39 -05:00
										 |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-27 17:25:39 -05:00
										 |  |  | 	upstream.Hosts = make([]*healthcheck.UpstreamHost, len(toHosts))
 | 
					
						
							| 
									
										
										
										
											2017-10-15 19:38:39 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-27 17:25:39 -05:00
										 |  |  | 	for i, host := range toHosts {
 | 
					
						
							|  |  |  | 		uh := &healthcheck.UpstreamHost{
 | 
					
						
							|  |  |  | 			Name:        host,
 | 
					
						
							|  |  |  | 			FailTimeout: upstream.FailTimeout,
 | 
					
						
							|  |  |  | 			CheckDown:   checkDownFunc(upstream),
 | 
					
						
							| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | 		}
 | 
					
						
							| 
									
										
										
										
											2018-01-27 17:25:39 -05:00
										 |  |  | 		upstream.Hosts[i] = uh
 | 
					
						
							| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2018-01-27 17:25:39 -05:00
										 |  |  | 	upstream.Start()
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return upstream, nil
 | 
					
						
							| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-16 23:49:35 -07:00
										 |  |  | func parseBlock(c *caddyfile.Dispenser, u *staticUpstream) error {
 | 
					
						
							| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | 	switch c.Val() {
 | 
					
						
							|  |  |  | 	case "policy":
 | 
					
						
							|  |  |  | 		if !c.NextArg() {
 | 
					
						
							|  |  |  | 			return c.ArgErr()
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							| 
									
										
										
										
											2017-08-09 09:21:33 -07:00
										 |  |  | 		policyCreateFunc, ok := healthcheck.SupportedPolicies[c.Val()]
 | 
					
						
							| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | 		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()
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							| 
									
										
										
										
											2016-04-07 17:42:35 +01:00
										 |  |  | 		var err error
 | 
					
						
							|  |  |  | 		u.HealthCheck.Path, u.HealthCheck.Port, err = net.SplitHostPort(c.Val())
 | 
					
						
							|  |  |  | 		if err != nil {
 | 
					
						
							|  |  |  | 			return err
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							| 
									
										
											  
											
												plugin/proxy: decrease health timeouts (#1107)
Turn down the timeouts and numbers a bit:
FailTimeout 10s -> 5s
Future 60s -> 12s
TryDuration 60s -> 16s
The timeout for decrementing the fails in a host: 10s -> 2s
And the biggest change: don't set fails when the error is Timeout(),
meaning we loop for a bit and may try the same server again, but we
don't mark our upstream as bad, see comments in proxy.go. Testing this
with "ANY isc.org" and "MX miek.nl" we see:
~~~
::1 - [24/Sep/2017:08:06:17 +0100] "ANY IN isc.org. udp 37 false 4096" SERVFAIL qr,rd 37 10.001621221s
24/Sep/2017:08:06:17 +0100 [ERROR 0 isc.org. ANY] unreachable backend: read udp 192.168.1.148:37420->8.8.8.8:53: i/o timeout
::1 - [24/Sep/2017:08:06:17 +0100] "MX IN miek.nl. udp 37 false 4096" NOERROR qr,rd,ra,ad 170 35.957284ms
127.0.0.1 - [24/Sep/2017:08:06:18 +0100] "ANY IN isc.org. udp 37 false 4096" SERVFAIL qr,rd 37 10.002051726s
24/Sep/2017:08:06:18 +0100 [ERROR 0 isc.org. ANY] unreachable backend: read udp 192.168.1.148:54901->8.8.8.8:53: i/o timeout
::1 - [24/Sep/2017:08:06:19 +0100] "MX IN miek.nl. udp 37 false 4096" NOERROR qr,rd,ra,ad 170 56.848416ms
127.0.0.1 - [24/Sep/2017:08:06:21 +0100] "MX IN miek.nl. udp 37 false 4096" NOERROR qr,rd,ra,ad 170 48.118349ms
::1 - [24/Sep/2017:08:06:21 +0100] "MX IN miek.nl. udp 37 false 4096" NOERROR qr,rd,ra,ad 170 1.055172915s
~~~
So the ANY isc.org queries show up twice, because we retry internally -
this is I think WAI.
The `miek.nl MX` queries are just processed normally as no backend is
marked as unreachable.
May fix #1035 #486
											
										 
											2017-09-24 20:05:36 +01:00
										 |  |  | 		u.HealthCheck.Interval = 4 * time.Second
 | 
					
						
							| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | 		if c.NextArg() {
 | 
					
						
							|  |  |  | 			dur, err := time.ParseDuration(c.Val())
 | 
					
						
							|  |  |  | 			if err != nil {
 | 
					
						
							|  |  |  | 				return err
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							|  |  |  | 			u.HealthCheck.Interval = dur
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 	case "except":
 | 
					
						
							| 
									
										
										
										
											2016-03-19 16:11:30 +00:00
										 |  |  | 		ignoredDomains := c.RemainingArgs()
 | 
					
						
							|  |  |  | 		if len(ignoredDomains) == 0 {
 | 
					
						
							| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | 			return c.ArgErr()
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							| 
									
										
										
										
											2016-03-19 16:11:30 +00:00
										 |  |  | 		for i := 0; i < len(ignoredDomains); i++ {
 | 
					
						
							| 
									
										
										
										
											2017-09-14 09:36:06 +01:00
										 |  |  | 			ignoredDomains[i] = plugin.Host(ignoredDomains[i]).Normalize()
 | 
					
						
							| 
									
										
										
										
											2016-03-19 16:11:30 +00:00
										 |  |  | 		}
 | 
					
						
							|  |  |  | 		u.IgnoredSubDomains = ignoredDomains
 | 
					
						
							| 
									
										
										
										
											2016-07-04 21:13:28 +01:00
										 |  |  | 	case "spray":
 | 
					
						
							| 
									
										
										
										
											2017-08-09 09:21:33 -07:00
										 |  |  | 		u.Spray = &healthcheck.Spray{}
 | 
					
						
							| 
									
										
										
										
											2017-01-15 08:12:58 +00:00
										 |  |  | 	case "protocol":
 | 
					
						
							|  |  |  | 		encArgs := c.RemainingArgs()
 | 
					
						
							|  |  |  | 		if len(encArgs) == 0 {
 | 
					
						
							|  |  |  | 			return c.ArgErr()
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		switch encArgs[0] {
 | 
					
						
							|  |  |  | 		case "dns":
 | 
					
						
							| 
									
										
										
										
											2017-03-14 21:32:21 +00:00
										 |  |  | 			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()
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							| 
									
										
										
										
											2017-01-15 08:12:58 +00:00
										 |  |  | 		case "https_google":
 | 
					
						
							| 
									
										
										
										
											2017-02-06 19:32:48 +00:00
										 |  |  | 			boot := []string{"8.8.8.8:53", "8.8.4.4:53"}
 | 
					
						
							|  |  |  | 			if len(encArgs) > 2 && encArgs[1] == "bootstrap" {
 | 
					
						
							|  |  |  | 				boot = encArgs[2:]
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			u.ex = newGoogle("", boot) // "" for default in google.go
 | 
					
						
							| 
									
										
										
										
											2017-02-14 22:20:20 -05:00
										 |  |  | 		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)
 | 
					
						
							| 
									
										
										
										
											2017-01-15 08:12:58 +00:00
										 |  |  | 		default:
 | 
					
						
							|  |  |  | 			return fmt.Errorf("%s: %s", errInvalidProtocol, encArgs[0])
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							| 
									
										
										
										
											2016-04-30 15:54:41 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | 	default:
 | 
					
						
							|  |  |  | 		return c.Errf("unknown property '%s'", c.Val())
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 	return nil
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-07 18:01:16 +00:00
										 |  |  | func (u *staticUpstream) IsAllowedDomain(name string) bool {
 | 
					
						
							| 
									
										
										
										
											2017-02-07 21:03:17 +00:00
										 |  |  | 	if dns.Name(name) == dns.Name(u.From()) {
 | 
					
						
							|  |  |  | 		return true
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2017-02-07 18:01:16 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-07 21:03:17 +00:00
										 |  |  | 	for _, ignoredSubDomain := range u.IgnoredSubDomains {
 | 
					
						
							| 
									
										
										
										
											2017-09-14 09:36:06 +01:00
										 |  |  | 		if plugin.Name(ignoredSubDomain).Matches(name) {
 | 
					
						
							| 
									
										
										
										
											2016-03-19 16:11:30 +00:00
										 |  |  | 			return false
 | 
					
						
							| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | 		}
 | 
					
						
							| 
									
										
										
										
											2016-03-19 16:11:30 +00:00
										 |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2016-03-18 20:57:35 +00:00
										 |  |  | 	return true
 | 
					
						
							|  |  |  | }
 | 
					
						
							| 
									
										
										
										
											2017-02-06 19:32:48 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | func (u *staticUpstream) Exchanger() Exchanger { return u.ex }
 | 
					
						
							| 
									
										
										
										
											2017-10-15 19:38:39 +02:00
										 |  |  | func (u *staticUpstream) From() string         { return u.from }
 | 
					
						
							| 
									
										
										
										
											2018-01-08 15:03:42 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | const max = 15
 |