middleware/proxy: multiple enhancements (#145)
Add port 53 in the proxy host if not specified. Check if the host is actually an IP address (v4 or v6) Remove the http headers and other TODOs
This commit is contained in:
parent
14b84ce02b
commit
e635b4e773
7 changed files with 86 additions and 62 deletions
|
@ -24,7 +24,7 @@ proxy from to... {
|
|||
max_fails integer
|
||||
health_check path:port [duration]
|
||||
except ignored_names...
|
||||
tcp
|
||||
ecs [v4 address/mask] [v6 address/mask] (TODO)
|
||||
}
|
||||
~~~
|
||||
|
||||
|
@ -35,8 +35,8 @@ proxy from to... {
|
|||
* `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 healthy. If it doesn't, the backend is marked as unhealthy for duration and no requests are routed to it. If this option is not provided then health checks are disabled. The default duration is 10 seconds ("10s").
|
||||
* `ignored_names...` is a space-separated list of paths to exclude from proxying. Requests that match any of these paths will be passed thru.
|
||||
* `tcp` use TCP for all upstream queries, otherwise it depends on the transport of the incoming
|
||||
query. TODO(miek): implement.
|
||||
* `ecs` add EDNS0 client submit metadata to the outgoing query. This can be optionally be followed
|
||||
by an IPv4 and/or IPv6 address. If none is specified the server's addresses are used.
|
||||
|
||||
## Policies
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@ package proxy
|
|||
// style as the proxy.
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
|
@ -18,7 +17,6 @@ func New(hosts []string) Proxy {
|
|||
|
||||
upstream := &staticUpstream{
|
||||
from: "",
|
||||
proxyHeaders: make(http.Header),
|
||||
Hosts: make([]*UpstreamHost, len(hosts)),
|
||||
Policy: &Random{},
|
||||
FailTimeout: 10 * time.Second,
|
||||
|
@ -32,7 +30,6 @@ func New(hosts []string) Proxy {
|
|||
Fails: 0,
|
||||
FailTimeout: upstream.FailTimeout,
|
||||
Unhealthy: false,
|
||||
ExtraHeaders: upstream.proxyHeaders, // TODO(miek): fixer the fix
|
||||
CheckDown: func(upstream *staticUpstream) UpstreamHostDownFunc {
|
||||
return func(uh *UpstreamHost) bool {
|
||||
if uh.Unhealthy {
|
||||
|
|
|
@ -3,14 +3,13 @@ package proxy
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/miekg/coredns/middleware"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
var errUnreachable = errors.New("unreachable backend")
|
||||
|
@ -36,6 +35,8 @@ type Upstream interface {
|
|||
Select() *UpstreamHost
|
||||
// Checks if subpdomain is not an ignored.
|
||||
IsAllowedPath(string) bool
|
||||
// Options returns the options set for this upstream
|
||||
Options() Options
|
||||
}
|
||||
|
||||
// UpstreamHostDownFunc can be used to customize how Down behaves.
|
||||
|
@ -48,7 +49,6 @@ type UpstreamHost struct {
|
|||
Fails int32
|
||||
FailTimeout time.Duration
|
||||
Unhealthy bool
|
||||
ExtraHeaders http.Header
|
||||
CheckDown UpstreamHostDownFunc
|
||||
WithoutPathPrefix string
|
||||
}
|
||||
|
@ -71,7 +71,6 @@ var tryDuration = 60 * time.Second
|
|||
// ServeDNS satisfies the middleware.Handler interface.
|
||||
func (p Proxy) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
|
||||
for _, upstream := range p.Upstreams {
|
||||
// allowed bla bla bla TODO(miek): fix full proxy spec from caddy
|
||||
start := time.Now()
|
||||
|
||||
// Since Select() should give us "up" hosts, keep retrying
|
||||
|
@ -81,8 +80,7 @@ func (p Proxy) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (
|
|||
if host == nil {
|
||||
return dns.RcodeServerFailure, errUnreachable
|
||||
}
|
||||
// TODO(miek): PORT!
|
||||
reverseproxy := ReverseProxy{Host: host.Name, Client: p.Client}
|
||||
reverseproxy := ReverseProxy{Host: host.Name, Client: p.Client, Options: upstream.Options()}
|
||||
|
||||
atomic.AddInt64(&host.Conns, 1)
|
||||
backendErr := reverseproxy.ServeDNS(w, r, nil)
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
package proxy
|
||||
|
||||
// Also test these inputs:
|
||||
//.:1053 {
|
||||
//proxy . ::1 2001:4860:4860::8844 8.8.8.8:54 [2001:4860:4860::8845]:53
|
||||
//}
|
||||
|
||||
/*
|
||||
func init() {
|
||||
tryDuration = 50 * time.Millisecond // prevent tests from hanging
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
type ReverseProxy struct {
|
||||
Host string
|
||||
Client Client
|
||||
Options Options
|
||||
}
|
||||
|
||||
func (p ReverseProxy) ServeDNS(w dns.ResponseWriter, r *dns.Msg, extra []dns.RR) error {
|
||||
|
@ -17,12 +18,11 @@ func (p ReverseProxy) ServeDNS(w dns.ResponseWriter, r *dns.Msg, extra []dns.RR)
|
|||
reply *dns.Msg
|
||||
err error
|
||||
)
|
||||
state := middleware.State{W: w, Req: r}
|
||||
|
||||
// We forward the original request, no need to fiddle with EDNS0 opt sizes.
|
||||
if state.Proto() == "tcp" {
|
||||
switch {
|
||||
case middleware.Proto(w) == "tcp":
|
||||
reply, err = middleware.Exchange(p.Client.TCP, r, p.Host)
|
||||
} else {
|
||||
default:
|
||||
reply, err = middleware.Exchange(p.Client.UDP, r, p.Host)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package proxy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
|
@ -20,8 +21,6 @@ var (
|
|||
|
||||
type staticUpstream struct {
|
||||
from string
|
||||
// TODO(miek): allows use to added headers
|
||||
proxyHeaders http.Header // TODO(miek): kill these
|
||||
Hosts HostPool
|
||||
Policy Policy
|
||||
|
||||
|
@ -34,6 +33,10 @@ type staticUpstream struct {
|
|||
}
|
||||
WithoutPathPrefix string
|
||||
IgnoredSubDomains []string
|
||||
options Options
|
||||
}
|
||||
type Options struct {
|
||||
Ecs []*net.IPNet // EDNS0 CLIENT SUBNET address (v4/v6) to add in CIDR notaton.
|
||||
}
|
||||
|
||||
// NewStaticUpstreams parses the configuration input and sets up
|
||||
|
@ -43,7 +46,6 @@ func NewStaticUpstreams(c parse.Dispenser) ([]Upstream, error) {
|
|||
for c.Next() {
|
||||
upstream := &staticUpstream{
|
||||
from: "",
|
||||
proxyHeaders: make(http.Header),
|
||||
Hosts: nil,
|
||||
Policy: &Random{},
|
||||
FailTimeout: 10 * time.Second,
|
||||
|
@ -57,6 +59,15 @@ func NewStaticUpstreams(c parse.Dispenser) ([]Upstream, error) {
|
|||
if len(to) == 0 {
|
||||
return upstreams, c.ArgErr()
|
||||
}
|
||||
for _, host := range to {
|
||||
h, _, err := net.SplitHostPort(host)
|
||||
if err != nil {
|
||||
h = host
|
||||
}
|
||||
if x := net.ParseIP(h); x == nil {
|
||||
return upstreams, fmt.Errorf("not an IP address: `%s'", h)
|
||||
}
|
||||
}
|
||||
|
||||
for c.NextBlock() {
|
||||
if err := parseBlock(&c, upstream); err != nil {
|
||||
|
@ -67,12 +78,11 @@ func NewStaticUpstreams(c parse.Dispenser) ([]Upstream, error) {
|
|||
upstream.Hosts = make([]*UpstreamHost, len(to))
|
||||
for i, host := range to {
|
||||
uh := &UpstreamHost{
|
||||
Name: host,
|
||||
Name: defaultHostPort(host),
|
||||
Conns: 0,
|
||||
Fails: 0,
|
||||
FailTimeout: upstream.FailTimeout,
|
||||
Unhealthy: false,
|
||||
ExtraHeaders: upstream.proxyHeaders,
|
||||
CheckDown: func(upstream *staticUpstream) UpstreamHostDownFunc {
|
||||
return func(uh *UpstreamHost) bool {
|
||||
if uh.Unhealthy {
|
||||
|
@ -107,6 +117,10 @@ func (u *staticUpstream) From() string {
|
|||
return u.from
|
||||
}
|
||||
|
||||
func (u *staticUpstream) Options() Options {
|
||||
return u.options
|
||||
}
|
||||
|
||||
func parseBlock(c *parse.Dispenser, u *staticUpstream) error {
|
||||
switch c.Val() {
|
||||
case "policy":
|
||||
|
@ -153,12 +167,6 @@ func parseBlock(c *parse.Dispenser, u *staticUpstream) error {
|
|||
}
|
||||
u.HealthCheck.Interval = dur
|
||||
}
|
||||
case "proxy_header":
|
||||
var header, value string
|
||||
if !c.Args(&header, &value) {
|
||||
return c.ArgErr()
|
||||
}
|
||||
u.proxyHeaders.Add(header, value)
|
||||
case "without":
|
||||
if !c.NextArg() {
|
||||
return c.ArgErr()
|
||||
|
@ -173,6 +181,12 @@ func parseBlock(c *parse.Dispenser, u *staticUpstream) error {
|
|||
ignoredDomains[i] = strings.ToLower(dns.Fqdn(ignoredDomains[i]))
|
||||
}
|
||||
u.IgnoredSubDomains = ignoredDomains
|
||||
case "ecs":
|
||||
ips := c.RemainingArgs()
|
||||
if len(ips) > 0 {
|
||||
|
||||
}
|
||||
|
||||
default:
|
||||
return c.Errf("unknown property '%s'", c.Val())
|
||||
}
|
||||
|
@ -247,3 +261,11 @@ func (u *staticUpstream) IsAllowedPath(name string) bool {
|
|||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func defaultHostPort(s string) string {
|
||||
_, _, e := net.SplitHostPort(s)
|
||||
if e == nil {
|
||||
return s
|
||||
}
|
||||
return net.JoinHostPort(s, "53")
|
||||
}
|
||||
|
|
|
@ -53,13 +53,15 @@ func (s *State) RemoteAddr() string {
|
|||
return s.W.RemoteAddr().String()
|
||||
}
|
||||
|
||||
// Proto gets the protocol used as the transport. This
|
||||
// will be udp or tcp.
|
||||
func (s *State) Proto() string {
|
||||
if _, ok := s.W.RemoteAddr().(*net.UDPAddr); ok {
|
||||
// Proto gets the protocol used as the transport. This will be udp or tcp.
|
||||
func (s *State) Proto() string { return Proto(s.W) }
|
||||
|
||||
// Proto gets the protocol used as the transport. This will be udp or tcp.
|
||||
func Proto(w dns.ResponseWriter) string {
|
||||
if _, ok := w.RemoteAddr().(*net.UDPAddr); ok {
|
||||
return "udp"
|
||||
}
|
||||
if _, ok := s.W.RemoteAddr().(*net.TCPAddr); ok {
|
||||
if _, ok := w.RemoteAddr().(*net.TCPAddr); ok {
|
||||
return "tcp"
|
||||
}
|
||||
return "udp"
|
||||
|
|
Loading…
Add table
Reference in a new issue