diff options
author | 2021-11-12 08:22:34 -0800 | |
---|---|---|
committer | 2021-11-12 11:22:34 -0500 | |
commit | 2e6953c7dbd1d6b359911e1ce92e2567df07ca8c (patch) | |
tree | d91514ca867bb5b000bec3ea219e6a2ab0a0c244 /plugin/forward/forward.go | |
parent | 6953ab2b4f23f916a08b68ba51b9a26e41e9a748 (diff) | |
download | coredns-2e6953c7dbd1d6b359911e1ce92e2567df07ca8c.tar.gz coredns-2e6953c7dbd1d6b359911e1ce92e2567df07ca8c.tar.zst coredns-2e6953c7dbd1d6b359911e1ce92e2567df07ca8c.zip |
Initial implementation of ForwardCRD plugin (#4512)
* Add forwardcrd plugin README.md
Co-authored-by: Aidan Obley <aobley@vmware.com>
Signed-off-by: Christian Ang <angc@vmware.com>
* Create forwardcrd plugin
- Place forwardcrd before forward plugin in plugin list. This will avoid
forward from preventing the forwardcrd plugin from handling any queries
in the case of having a default upstream forwarder in a server block (as
is the case in the default kubernetes Corefile).
Co-authored-by: Aidan Obley <aobley@vmware.com>
Signed-off-by: Christian Ang <angc@vmware.com>
* Add Forward CRD
Signed-off-by: Christian Ang <angc@vmware.com>
* Add NewWithConfig to forward plugin
- allows external packages to instanciate forward plugins
Co-authored-by: Aidan Obley <aobley@vmware.com>
Signed-off-by: Christian Ang <angc@vmware.com>
* ForwardCRD plugin handles requests for Forward CRs
- add a Kubernetes controller that can read Forward CRs
- instances of the forward plugin are created based on Forward CRs from
the Kubernetes controller
- DNS requests are handled by calling matching Forward plugin instances
based on zone name
- Defaults to the kube-system namespace to align with Corefile RBAC
Signed-off-by: Christian Ang <angc@vmware.com>
Use klog v2 in forwardcrd plugin
* Refactor forward setup to use NewWithConfig
Co-authored-by: Christian Ang <angc@vmware.com>
Signed-off-by: Edwin Xie <exie@vmware.com>
* Use ParseInt instead of Atoi
- to ensure that the bitsize is 32 for later casting to uint32
Signed-off-by: Christian Ang <angc@vmware.com>
* Add @christianang to CODEOWNERS for forwardcrd
Signed-off-by: Christian Ang <angc@vmware.com>
Co-authored-by: Edwin Xie <exie@vmware.com>
Diffstat (limited to 'plugin/forward/forward.go')
-rw-r--r-- | plugin/forward/forward.go | 118 |
1 files changed, 118 insertions, 0 deletions
diff --git a/plugin/forward/forward.go b/plugin/forward/forward.go index 19a469c72..707ad31e1 100644 --- a/plugin/forward/forward.go +++ b/plugin/forward/forward.go @@ -8,6 +8,7 @@ import ( "context" "crypto/tls" "errors" + "fmt" "sync/atomic" "time" @@ -16,6 +17,8 @@ import ( "github.com/coredns/coredns/plugin/dnstap" "github.com/coredns/coredns/plugin/metadata" clog "github.com/coredns/coredns/plugin/pkg/log" + "github.com/coredns/coredns/plugin/pkg/parse" + "github.com/coredns/coredns/plugin/pkg/transport" "github.com/coredns/coredns/request" "github.com/miekg/dns" @@ -54,12 +57,127 @@ type Forward struct { Next plugin.Handler } +// ForwardConfig represents the configuration of the Forward Plugin. This can +// be used with NewWithConfig to create a new configured instance of the +// Forward Plugin. +type ForwardConfig struct { + From string + To []string + Except []string + MaxFails *uint32 + HealthCheck *time.Duration + HealthCheckNoRec bool + ForceTCP bool + PreferUDP bool + TLSConfig *tls.Config + TLSServerName string + Expire *time.Duration + MaxConcurrent *int64 + Policy string + TapPlugin *dnstap.Dnstap +} + // New returns a new Forward. func New() *Forward { f := &Forward{maxfails: 2, tlsConfig: new(tls.Config), expire: defaultExpire, p: new(random), from: ".", hcInterval: hcInterval, opts: options{forceTCP: false, preferUDP: false, hcRecursionDesired: true}} return f } +// NewWithConfig returns a new Forward configured by the provided +// ForwardConfig. +func NewWithConfig(config ForwardConfig) (*Forward, error) { + f := New() + if config.From != "" { + zones := plugin.Host(config.From).NormalizeExact() + f.from = zones[0] // there can only be one here, won't work with non-octet reverse + + if len(zones) > 1 { + log.Warningf("Unsupported CIDR notation: '%s' expands to multiple zones. Using only '%s'.", config.From, f.from) + } + } + for i := 0; i < len(config.Except); i++ { + f.ignored = append(f.ignored, plugin.Host(config.Except[i]).NormalizeExact()...) + } + if config.MaxFails != nil { + f.maxfails = *config.MaxFails + } + if config.HealthCheck != nil { + if *config.HealthCheck < 0 { + return nil, fmt.Errorf("health_check can't be negative: %s", *config.HealthCheck) + } + f.hcInterval = *config.HealthCheck + } + f.opts.hcRecursionDesired = !config.HealthCheckNoRec + f.opts.forceTCP = config.ForceTCP + f.opts.preferUDP = config.PreferUDP + if config.TLSConfig != nil { + f.tlsConfig = config.TLSConfig + } + f.tlsServerName = config.TLSServerName + if f.tlsServerName != "" { + f.tlsConfig.ServerName = f.tlsServerName + } + if config.Expire != nil { + f.expire = *config.Expire + if *config.Expire < 0 { + return nil, fmt.Errorf("expire can't be negative: %s", *config.Expire) + } + } + if config.MaxConcurrent != nil { + if *config.MaxConcurrent < 0 { + return f, fmt.Errorf("max_concurrent can't be negative: %d", *config.MaxConcurrent) + } + f.ErrLimitExceeded = fmt.Errorf("concurrent queries exceeded maximum %d", *config.MaxConcurrent) + f.maxConcurrent = *config.MaxConcurrent + } + if config.Policy != "" { + switch config.Policy { + case "random": + f.p = &random{} + case "round_robin": + f.p = &roundRobin{} + case "sequential": + f.p = &sequential{} + default: + return f, fmt.Errorf("unknown policy '%s'", config.Policy) + } + } + f.tapPlugin = config.TapPlugin + + toHosts, err := parse.HostPortOrFile(config.To...) + if err != nil { + return f, err + } + + transports := make([]string, len(toHosts)) + allowedTrans := map[string]bool{"dns": true, "tls": true} + for i, host := range toHosts { + trans, h := parse.Transport(host) + + if !allowedTrans[trans] { + return f, fmt.Errorf("'%s' is not supported as a destination protocol in forward: %s", trans, host) + } + p := NewProxy(h, trans) + f.proxies = append(f.proxies, p) + transports[i] = trans + } + + // Initialize ClientSessionCache in tls.Config. This may speed up a TLS handshake + // in upcoming connections to the same TLS server. + f.tlsConfig.ClientSessionCache = tls.NewLRUClientSessionCache(len(f.proxies)) + + for i := range f.proxies { + // Only set this for proxies that need it. + if transports[i] == transport.TLS { + f.proxies[i].SetTLSConfig(f.tlsConfig) + } + f.proxies[i].SetExpire(f.expire) + f.proxies[i].health.SetRecursionDesired(f.opts.hcRecursionDesired) + + } + return f, nil +} + // SetProxy appends p to the proxy list and starts healthchecking. func (f *Forward) SetProxy(p *Proxy) { f.proxies = append(f.proxies, p) |