aboutsummaryrefslogtreecommitdiff
path: root/plugin/forward/forward.go
diff options
context:
space:
mode:
authorGravatar Christian Ang <christian.ang@outlook.com> 2021-11-12 08:22:34 -0800
committerGravatar GitHub <noreply@github.com> 2021-11-12 11:22:34 -0500
commit2e6953c7dbd1d6b359911e1ce92e2567df07ca8c (patch)
treed91514ca867bb5b000bec3ea219e6a2ab0a0c244 /plugin/forward/forward.go
parent6953ab2b4f23f916a08b68ba51b9a26e41e9a748 (diff)
downloadcoredns-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.go118
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)