dnsredir

package module
v0.0.0-...-e0d0113 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 14, 2020 License: Apache-2.0 Imports: 26 Imported by: 0

README

dnsredir

Name

dnsredir - yet another seems better forward/proxy plugin for CoreDNS, mainly focused on speed and reliable.

dnsredir plugin works just like the forward plugin which re-uses already opened sockets to the upstreams. Currently, it supports UDP, TCP, and DNS-over-TLS and uses in continuous health checking.

Like the proxy plugin, it also supports multiple backends, which each upstream also supports multiple TLS server names. Load balancing features including multiple policies, health checks and failovers.

The health check works by sending . IN NS to upstream host, somewhat like a ping packet in ICMP protocol. Any response that is not a network error(for example, REFUSED, SERVFAIL, etc.) is taken as a healthy upstream.

When all upstream hosts are down this plugin can opt to fallback to randomly selecting a upstream host and sending the requests to it as last resort.

Syntax

The phrase redirect and forward can be used interchangeably, unless explicitly stated otherwise.

In its most basic form, a simple DNS redirecter uses the following syntax:

dnsredir FILE... {
	to TO...
}
  • FILE... is the file list which contains base domain to match for the request to be redirected. .(i.e. root zone) can be used solely to match all incoming requests as a fallback.

    Currently, two kind of formats are supported:

    • DOMAIN, which the whole line is the domain name.

    • server=/DOMAIN/..., which is the format of dnsmasq config file, note that only the DOMAIN will be honored, other fields will be simply discarded.

    Text after # character will be treated as comment.

    Unparsable lines(including whitespace-only line) are therefore just ignored.

  • to TO... are the destination endpoints to redirected to. This is a mandatory option.

    The to syntax allows you to specify a protocol, a port, etc:

    [dns://]IP[:PORT] for plain DNS(without encryption).

    tls://IP[:PORT][@TLS_SERVER_NAME] for DNS over TLS, if you combine : and @, @ must comes last. Be aware of some DoT servers require TLS server name as a mandatory option.

An expanded syntax can be utilized to unleash of the power of dnsredir plugin:

dnsredir FILE... {
	reload DURATION
	[INLINE]
	except IGNORED_NAME...

	spray
	policy random|round_robin|sequential
	health_check DURATION
	max_fails INTEGER

	to TO...
	expire DURATION
	force_tcp
	prefer_udp
	tls CERT KEY CA
	tls_servername NAME
}

Some of the options take a DURATION as argument, second will be used as default time unit as if you don't specify it, zero time duration to disable corresponding feature unless it's explicitly stated otherwise.

  • FILE... and to TO... as above.

  • reload changes the interval between each FILE... reload. Default is 2s, minimal is 1s.

  • INLINE are the domain names embedded in Corefile, they serve as supplementaries. Note that domain names in FILE... will still be read. INLINE is forbidden if you specify .(i.e. root zone) as FILE....

    It usually not a good idea to embed too many INLINE domains in Corefile, in which case you should put them into a sole file, say, user_custom.conf.

  • except is a space-separated list of domains to exclude from redirecting. Requests that match none of these names will be passed through.

    It usually not a good idea to embed too many except domains in Corefile, in which case you should try to delete them directly in to files.

  • spray when all upstreams in to are marked as unhealthy, randomly pick one to send the traffic with. (Last resort, as a failsafe.)

  • policy specifies the policy to use for selecting upstream hosts. The default is random.

    • random will randomly select a healthy upstream host.

    • round_robin will select a healthy upstream host in round robin order.

    • sequential will select a healthy upstream host in sequential order.

  • health_check specifies upstream hosts health checking interval. Default is 2s, minimal is 1s.

  • max_fails is the maximum number of consecutive health checking failures that are needed before considering an upstream as down. 0 to disable this feature(which the upstream will never be marked as down). Default is 3.

  • expire will expire (cached) connections after this time interval. Default is 15s, minimal is 1s.

  • force_tcp uses TCP even if the request comes in over UDP.

  • prefer_udp try first using UDP even when the request comes in over TCP. If response is truncated(TC flag set in response) then do another attempt over TCP. If both force_tcp and prefer_udp are specified then force_tcp takes precedence.

    XXX: not yet implemented, this feature might be deprecated in future.

  • tls CERT KEY CA define the TLS properties for TLS connection. From 0 to 3 arguments can be specified:

    • tls - No client authentication is used, and the system CAs are used to verify the server certificate.

    • tls CA - No client authentication is used, and the CA file is used to verify the server certificate.

    • tls CERT KEY - Client authentication is used with the specified CERT/KEY pair. The server certificate is verified with the system CAs.

    • tls CERT KEY CA - Client authentication is used with the specified CERT/KEY pair. The server certificate is verified with the given CA file.

    Note that this TLS config is global for redirecting DNS requests.

  • tls_servername specifies the global TLS server name used in the TLS configuration.

    For example, cloudflare-dns.com can be used for 1.1.1.1(Cloudflare), and quad9.net can be used for 9.9.9.9(Quad9).

    Note that this is a global name, it doesn't affect the TLS server names specified in to TO....

Metrics

TODO

Examples

Redirect all requests to Cloudflare DNS:

dnsredir . {
	to tls://1.1.1.1 tls://1.0.0.1
	tls_servername one.one.one.one
}

Redirect all requests to with different upstreams:

dnsredir . {
	# 1.1.1.1 uses the global TLS server name
	# 8.8.8.8 and 9.9.9.9 uses its own TLS server name
	to tls://1.1.1.1 tls://8.8.8.8@dns.google tls://9.9.9.9@quad9.net
	tls_servername cloudflare-dns.com
}

Redirect domains listed in file and fallback to Google DNS:

dnsredir accelerated-domains.china.conf {
	reload 3s
	max_fails 0
	to 114.114.114.114 223.5.5.5 119.29.29.29
	policy round_robin
	prefer_udp

	# INLINE domain
	example.org
	example.gov
}

dnsredir google.china.conf apple.china.conf {
	reload 10s
	to tls://223.5.5.5 dns://101.6.6.6 tls://223.6.6.6
	# TLS upstreams use the global TLS server name
	tls_servername alidns.com
	except adservice.google.com doubleclick.net
}

dnsredir . {
	to tls://8.8.8.8@8888.google tls://2001:4860:4860::64@dns.google
	policy sequential
	spray
}

TODO: add more examples

BUGS

Sometimes you modified Corefile and yet Caddy server failed to reload the new config with the error "Error during parsing", dnsredir will do sanity check during parsing, if you misconfiged the Corefile, you're out of lock:

  • Argument count mismatch, out of range arguments, unrecognizable arguments, etc.

  • Missing mandatory property to TO....

  • Used unsupported DNS transport type in to TO....

  • except and INLINE share some same domain names(which yields a conflict).

  • .(i.e. root zone) is matched yet INLINE also embedded in Server Block(still a conflict).

Also note that some of the properties are cumulative: INLINE, except, to, in which case INLINE domains should be put one domain per line.

Rationale: Strict checking to ensure that user can detect errors ASAP, and make the Corefile less confusing.

Courtesy

Implementation and documentation of this plugin mainly inspired by forward plugin, proxy plugin, hosts plugin.

LICENSE

dnsredir uses the same LICENSE as with CoreDNS.

See also

CoreDNS github repository

forward plugin

proxy plugin

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	UnusedParam  = Unused
	UnusedResult = Unused
)
View Source
var SupportedPolicies = map[string]Policy{
	"random":      &Random{},
	"round_robin": &RoundRobin{},
	"sequential":  &Sequential{},
	"spray":       &Spray{},
}

SupportedPolicies is the collection of policies registered

Functions

func PluginError

func PluginError(err error) error

func SplitByByte

func SplitByByte(s string, c byte) (string, string)

Return two strings delimited by the `c' If `c' not found in `s', `s' and an empty string will be returned

func Unused

func Unused(arg0 interface{}, args ...interface{})

Used for generic expressions and statements

Types

type Dnsredir

type Dnsredir struct {
	Next plugin.Handler

	Upstreams *[]Upstream
}

func (*Dnsredir) Name

func (r *Dnsredir) Name() string

func (*Dnsredir) OnShutdown

func (r *Dnsredir) OnShutdown() error

func (*Dnsredir) OnStartup

func (r *Dnsredir) OnStartup() error

func (*Dnsredir) ServeDNS

func (r *Dnsredir) ServeDNS(ctx context.Context, w dns.ResponseWriter, req *dns.Msg) (int, error)

type HealthCheck

type HealthCheck struct {
	// contains filtered or unexported fields
}

func (*HealthCheck) Select

func (hc *HealthCheck) Select() *UpstreamHost

Select an upstream host based on the policy and the health check result Taken from proxy/healthcheck/healthcheck.go with modification

func (*HealthCheck) Start

func (hc *HealthCheck) Start()

func (*HealthCheck) Stop

func (hc *HealthCheck) Stop()

type Nameitem

type Nameitem struct {
	sync.RWMutex
	// contains filtered or unexported fields
}

func NewNameitemsWithPaths

func NewNameitemsWithPaths(paths []string) []*Nameitem

type Namelist

type Namelist struct {
	// contains filtered or unexported fields
}

func (*Namelist) Match

func (n *Namelist) Match(child string) bool

Assume `child' is lower cased and without trailing dot

type Policy

type Policy interface {
	// nil will be selected if all hosts are down
	// NOTE: Spray policy will always return a nonnull host
	Select(pool UpstreamHostPool) *UpstreamHost
}

Policy decides how a host will be selected from a pool. When all hosts are unhealthy, it is assumed the health checking failed. In this case each policy will *randomly* return a host from the pool

to prevent no traffic to go through at all.

type Random

type Random struct{}

Random is a policy that selects up hosts from a pool at random.

func (*Random) Select

func (r *Random) Select(pool UpstreamHostPool) *UpstreamHost

Select selects an up host at random from the specified pool.

func (*Random) String

func (r *Random) String() string

type RoundRobin

type RoundRobin struct {
	// contains filtered or unexported fields
}

RoundRobin is a policy that selects hosts based on round robin ordering.

func (*RoundRobin) Select

func (r *RoundRobin) Select(pool UpstreamHostPool) *UpstreamHost

Select selects an up host from the pool using a round robin ordering scheme.

func (*RoundRobin) String

func (r *RoundRobin) String() string

type Sequential

type Sequential struct{}

Sequential is a policy that selects always the first healthy host in the list order.

func (*Sequential) Select

func (s *Sequential) Select(pool UpstreamHostPool) *UpstreamHost

Select always the first that is not Down, nil if all hosts are down

func (*Sequential) String

func (s *Sequential) String() string

type Spray

type Spray struct{}

Spray is a policy that selects a host from a pool at random. This should be used as a last ditch attempt to get

a host when all hosts are reporting unhealthy.

func (*Spray) Select

func (s *Spray) Select(pool UpstreamHostPool) *UpstreamHost

Select selects an up host at random from the specified pool.

func (*Spray) String

func (s *Spray) String() string

type Transport

type Transport struct {
	// contains filtered or unexported fields
}

Transport settings Inspired from coredns/plugin/forward/persistent.go addr isn't sealed into this struct since it's a high-level item

func (*Transport) Start

func (t *Transport) Start()

Start starts the transport's connection manager.

func (*Transport) Stop

func (t *Transport) Stop()

Stop stops the transport's connection manager.

func (*Transport) Yield

func (t *Transport) Yield(pc *persistConn)

Yield return the connection to transport for reuse.

type Upstream

type Upstream interface {
	// Check if given domain name should be routed to this upstream zone
	Match(name string) bool
	// Select an upstream host to be routed to, nil if no available host
	Select() *UpstreamHost

	Start() error
	Stop() error
}

Upstream manages a pool of proxy upstream hosts see: github.com/coredns/proxy#proxy.go

func NewReloadableUpstreams

func NewReloadableUpstreams(c *caddy.Controller) ([]Upstream, error)

Parses Caddy config input and return a list of reloadable upstream for this plugin

type UpstreamHost

type UpstreamHost struct {
	// contains filtered or unexported fields
}

UpstreamHost represents a single upstream DNS server

func (*UpstreamHost) Check

func (uh *UpstreamHost) Check() error

For health check we send to . IN NS +norec message to the upstream. Dial timeouts and empty replies are considered fails

basically anything else constitutes a healthy upstream.

func (*UpstreamHost) Dial

func (uh *UpstreamHost) Dial(proto string) (*persistConn, bool, error)

see: upstream.go/transToProto() Return:

#0	Persistent connection
#1	true if it's a cached connection
#2	error(if any)

func (*UpstreamHost) Down

func (uh *UpstreamHost) Down() bool

Down checks whether the upstream host is down or not Down will try to use uh.downFunc first, and will fallback

to some default criteria if necessary.

func (*UpstreamHost) Exchange

func (uh *UpstreamHost) Exchange(ctx context.Context, state request.Request) (*dns.Msg, error)

type UpstreamHostDownFunc

type UpstreamHostDownFunc func(*UpstreamHost) bool

UpstreamHostDownFunc can be used to customize how Down behaves see: proxy/healthcheck/healthcheck.go

type UpstreamHostPool

type UpstreamHostPool []*UpstreamHost

UpstreamHostPool is an array of upstream DNS servers

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL