diff --git a/.circleci/config.yml b/.circleci/config.yml index 83403770c00..e1e0c0a4e05 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -18,7 +18,7 @@ initWorkingDir: &initWorkingDir integrationDefaults: &integrationDefaults machine: - image: ubuntu-2004:202201-02 + image: ubuntu-2004:2022.04.2 working_directory: ~/go/src/${CIRCLE_PROJECT_USERNAME}/coredns environment: - K8S_VERSION: v1.22.0 diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml index a556320f2c5..10e1ec916cf 100644 --- a/.github/workflows/cifuzz.yml +++ b/.github/workflows/cifuzz.yml @@ -20,7 +20,7 @@ jobs: fuzz-seconds: 600 dry-run: false - name: Upload Crash - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@3cea5372237819ed00197afe530f5a7ea3e805c8 if: failure() && steps.build.outcome == 'success' with: name: artifacts diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 921d38cef0b..e28bca4a76d 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -30,12 +30,12 @@ jobs: uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b - name: Initialize CodeQL - uses: github/codeql-action/init@2f58583a1b24a7d3c7034f6bf9fa506d23b1183b + uses: github/codeql-action/init@3e7e3b32d0fb8283594bb0a76cc60a00918b0969 with: languages: ${{ matrix.language }} - name: Autobuild - uses: github/codeql-action/autobuild@2f58583a1b24a7d3c7034f6bf9fa506d23b1183b + uses: github/codeql-action/autobuild@3e7e3b32d0fb8283594bb0a76cc60a00918b0969 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@2f58583a1b24a7d3c7034f6bf9fa506d23b1183b + uses: github/codeql-action/analyze@3e7e3b32d0fb8283594bb0a76cc60a00918b0969 diff --git a/.github/workflows/depsreview.yml b/.github/workflows/depsreview.yml index 3d73bb899a7..0707d68715a 100644 --- a/.github/workflows/depsreview.yml +++ b/.github/workflows/depsreview.yml @@ -11,4 +11,4 @@ jobs: - name: 'Checkout Repository' uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b - name: 'Dependency Review' - uses: actions/dependency-review-action@3f943b86c9a289f4e632c632695e2e0898d9d67d + uses: actions/dependency-review-action@94145f3150bfabdc97540cbd5f7e926306ea7744 diff --git a/.github/workflows/go.coverage.yml b/.github/workflows/go.coverage.yml index 0d29d0be267..ae38d84fcc1 100644 --- a/.github/workflows/go.coverage.yml +++ b/.github/workflows/go.coverage.yml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Install Go - uses: actions/setup-go@fcdc43634adb5f7ae75a9d7a9b9361790f7293e2 + uses: actions/setup-go@84cbf8094393cdc5fe1fe1671ff2647332956b1a with: go-version: '1.18.0' id: go diff --git a/.github/workflows/go.test.yml b/.github/workflows/go.test.yml index d416df44f53..8d0cbfe1733 100644 --- a/.github/workflows/go.test.yml +++ b/.github/workflows/go.test.yml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Install Go - uses: actions/setup-go@fcdc43634adb5f7ae75a9d7a9b9361790f7293e2 + uses: actions/setup-go@84cbf8094393cdc5fe1fe1671ff2647332956b1a with: go-version: '1.18.0' id: go @@ -31,7 +31,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Install Go - uses: actions/setup-go@fcdc43634adb5f7ae75a9d7a9b9361790f7293e2 + uses: actions/setup-go@84cbf8094393cdc5fe1fe1671ff2647332956b1a with: go-version: '1.18.0' id: go @@ -50,7 +50,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Install Go - uses: actions/setup-go@fcdc43634adb5f7ae75a9d7a9b9361790f7293e2 + uses: actions/setup-go@84cbf8094393cdc5fe1fe1671ff2647332956b1a with: go-version: '1.18.0' id: go diff --git a/.github/workflows/go.tidy.yml b/.github/workflows/go.tidy.yml index bdc84b04034..d6560e967dd 100644 --- a/.github/workflows/go.tidy.yml +++ b/.github/workflows/go.tidy.yml @@ -13,7 +13,7 @@ jobs: contents: write steps: - name: Install Go - uses: actions/setup-go@fcdc43634adb5f7ae75a9d7a9b9361790f7293e2 + uses: actions/setup-go@84cbf8094393cdc5fe1fe1671ff2647332956b1a with: go-version: '1.18.0' id: go diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml new file mode 100644 index 00000000000..1ac80981832 --- /dev/null +++ b/.github/workflows/golangci-lint.yml @@ -0,0 +1,16 @@ +name: golangci-lint +on: + pull_request: +jobs: + golangci: + name: lint + runs-on: ubuntu-latest + steps: + - uses: actions/setup-go@84cbf8094393cdc5fe1fe1671ff2647332956b1a + with: + go-version: '1.18' + - uses: actions/checkout@v3 + - name: golangci-lint + uses: golangci/golangci-lint-action@v3 + with: + version: v1.46.2 diff --git a/.github/workflows/make.doc.yml b/.github/workflows/make.doc.yml index e3bc41b0eea..ba2ab14ecc6 100644 --- a/.github/workflows/make.doc.yml +++ b/.github/workflows/make.doc.yml @@ -16,7 +16,7 @@ jobs: uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b - name: Setup Go - uses: actions/setup-go@fcdc43634adb5f7ae75a9d7a9b9361790f7293e2 + uses: actions/setup-go@84cbf8094393cdc5fe1fe1671ff2647332956b1a with: go-version: '1.18.0' diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index e6f4fe84343..7890837aff1 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -27,7 +27,7 @@ jobs: persist-credentials: false - name: "Run analysis" - uses: ossf/scorecard-action@c1aec4ac820532bab364f02a81873c555a0ba3a1 + uses: ossf/scorecard-action@ce330fde6b1a5c9c75b417e7efc510b822a35564 with: results_file: results.sarif results_format: sarif @@ -42,7 +42,7 @@ jobs: # Upload the results as artifacts (optional). - name: "Upload artifact" - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@3cea5372237819ed00197afe530f5a7ea3e805c8 with: name: SARIF file path: results.sarif @@ -50,6 +50,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@2f58583a1b24a7d3c7034f6bf9fa506d23b1183b + uses: github/codeql-action/upload-sarif@3e7e3b32d0fb8283594bb0a76cc60a00918b0969 with: sarif_file: results.sarif diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 793dad620b0..5150e336f0f 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -13,7 +13,7 @@ jobs: pull-requests: write # for actions/stale to close stale PRs runs-on: ubuntu-latest steps: - - uses: actions/stale@3cc123766321e9f15a6676375c154ccffb12a358 + - uses: actions/stale@532554b8a8498a0e006fbcde824b048728c4178f with: stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 7 days' stale-pr-message: 'This pull request is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 7 days' diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 00000000000..475ae82b9f9 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,13 @@ +run: + timeout: 5m +linters: + disable-all: true + enable: + - deadcode + - govet + - ineffassign + - staticcheck + - structcheck + - typecheck + - varcheck + - whitespace diff --git a/CODEOWNERS b/CODEOWNERS index a40529cd7a5..6b5525213fa 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1,6 +1,6 @@ # @miekg, miek@miek.nl, project lead: 11/11/2022 -* @bradbeam @chrisohaver @dilyevsky @jameshartig @greenpau @isolus @johnbelamaric @miekg @pmoroney @rajansandeep @stp-ip @superq @yongtang +* @bradbeam @chrisohaver @dilyevsky @jameshartig @greenpau @isolus @johnbelamaric @miekg @pmoroney @rajansandeep @stp-ip @superq @yongtang @Tantalor93 /.circleci/ @miekg @chrisohaver @rajansandeep /plugin/pkg/ @miekg @chrisohaver @johnbelamaric @yongtang @stp-ip @@ -12,13 +12,13 @@ go.sum @miekg @chrisohaver @johnbelamaric @yongtang @stp-ip go.mod @miekg @chrisohaver @johnbelamaric @yongtang @stp-ip /plugin/acl/ @miekg @ihac -/plugin/any/ @miekg +/plugin/any/ @miekg @yongtang /plugin/auto/ @miekg @stp-ip /plugin/autopath/ @chrisohaver @miekg /plugin/azure/ @miekg @yongtang @darshanime /plugin/bind/ @miekg /plugin/bufsize/ @ykhr53 -/plugin/cache/ @miekg +/plugin/cache/ @miekg @chrisohaver /plugin/cancel/ @miekg /plugin/chaos/ @miekg @zouyee /plugin/clouddns/ @miekg @yongtang @@ -26,31 +26,32 @@ go.mod @miekg @chrisohaver @johnbelamaric @yongtang @stp-ip /plugin/dnssec/ @isolus @miekg /plugin/dnstap/ @varyoo @yongtang /plugin/erratic/ @miekg -/plugin/errors/ @miekg +/plugin/errors/ @miekg @Tantalor93 /plugin/etcd/ @miekg @nitisht /plugin/file/ @miekg @yongtang @stp-ip -/plugin/forward/ @johnbelamaric @miekg @rdrozhdzh +/plugin/forward/ @johnbelamaric @miekg @rdrozhdzh @Tantalor93 @chrisohaver /plugin/geoip/ @miekg @snebel29 /plugin/grpc/ @inigohu @miekg @zouyee /plugin/health/ @jameshartig @miekg @zouyee /plugin/header/ @miekg @mqasimsarfraz /plugin/hosts/ @johnbelamaric @pmoroney -/plugin/k8s_external/ @miekg +/plugin/k8s_external/ @miekg @chrisohaver /plugin/kubernetes/ @bradbeam @chrisohaver @johnbelamaric @miekg @rajansandeep @yongtang @zouyee /plugin/loadbalance/ @miekg -/plugin/log/ @miekg @nchrisdk +/plugin/log/ @miekg @nchrisdk @Tantalor93 /plugin/loop/ @miekg @chrisohaver -/plugin/metadata/ @ekleiner @miekg -/plugin/metrics/ @jameshartig @miekg @superq @greenpau +/plugin/metadata/ @ekleiner @miekg @Tantalor93 +/plugin/metrics/ @jameshartig @miekg @superq @greenpau @Tantalor93 /plugin/nsid/ @yongtang /plugin/pprof/ @miekg @zouyee /plugin/reload/ @johnbelamaric /plugin/rewrite/ @greenpau @johnbelamaric -/plugin/root/ @miekg +/plugin/root/ @miekg @yongtang /plugin/route53/ @yongtang @dilyevsky /plugin/secondary/ @bradbeam @miekg /plugin/template/ @rtreffer /plugin/tls/ @johnbelamaric -/plugin/trace/ @johnbelamaric @zouyee +/plugin/trace/ @johnbelamaric @zouyee @Tantalor93 /plugin/transfer/ @miekg @chrisohaver -/plugin/whoami/ @miekg @chrisohaver +/plugin/tsig/ @chrisohaver +/plugin/whoami/ @miekg @chrisohaver @yongtang diff --git a/README.md b/README.md index f69b5d225cc..c8b37c3c293 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ CoreDNS requires Go to compile. However, if you already have docker installed an setup a Go environment, you could build CoreDNS easily: ``` -$ docker run --rm -i -t -v $PWD:/v -w /v golang:1.17 make +$ docker run --rm -i -t -v $PWD:/v -w /v golang:1.18 make ``` The above command alone will have `coredns` binary generated. diff --git a/core/dnsserver/address.go b/core/dnsserver/address.go index b8563c87e8e..547cd35ec54 100644 --- a/core/dnsserver/address.go +++ b/core/dnsserver/address.go @@ -49,7 +49,6 @@ func newOverlapZone() *zoneOverlap { // registerAndCheck adds a new zoneAddr for validation, it returns information about existing or overlapping with already registered // we consider that an unbound address is overlapping all bound addresses for same zone, same port func (zo *zoneOverlap) registerAndCheck(z zoneAddr) (existingZone *zoneAddr, overlappingZone *zoneAddr) { - if exist, ok := zo.registeredAddr[z]; ok { // exact same zone already registered return &exist, nil diff --git a/core/dnsserver/address_test.go b/core/dnsserver/address_test.go index 9f92a3fe6c6..05b601313b8 100644 --- a/core/dnsserver/address_test.go +++ b/core/dnsserver/address_test.go @@ -43,7 +43,6 @@ func TestSplitProtocolHostPort(t *testing.T) { if port != test.port { t.Errorf("Test %d: (address = %s) expected port with value %s but got %s", i, test.input, test.port, port) } - } } @@ -92,7 +91,6 @@ func TestOverlapAddressChecker(t *testing.T) { }, }, } { - checker := newOverlapZone() for _, call := range test.sequence { same, overlap := checker.registerAndCheck(call.zone) @@ -108,10 +106,8 @@ func TestOverlapAddressChecker(t *testing.T) { if overlap.String() != call.overlapKey { t.Errorf("Test %d: error, for zone %s, 'overlap Key' (%v) has not the expected value (%v)", i, sZone, overlap.String(), call.overlapKey) } - } } - } } } diff --git a/core/dnsserver/config.go b/core/dnsserver/config.go index e5200d67cdc..c34398b3980 100644 --- a/core/dnsserver/config.go +++ b/core/dnsserver/config.go @@ -28,6 +28,9 @@ type Config struct { // Debug controls the panic/recover mechanism that is enabled by default. Debug bool + // Stacktrace controls including stacktrace as part of log from recover mechanism, it is disabled by default. + Stacktrace bool + // The transport we implement, normally just "dns" over TCP/UDP, but could be // DNS-over-TLS or DNS-over-gRPC. Transport string @@ -40,6 +43,9 @@ type Config struct { // TLSConfig when listening for encrypted connections (gRPC, DNS-over-TLS). TLSConfig *tls.Config + // TSIG secrets, [name]key. + TsigSecret map[string]string + // Plugin stack. Plugin []plugin.Plugin diff --git a/core/dnsserver/onstartup.go b/core/dnsserver/onstartup.go index 113bf15e57a..b016f1ee508 100644 --- a/core/dnsserver/onstartup.go +++ b/core/dnsserver/onstartup.go @@ -2,9 +2,22 @@ package dnsserver import ( "fmt" + "regexp" "sort" + + "github.com/coredns/coredns/plugin/pkg/dnsutil" ) +// checkZoneSyntax() checks whether the given string match 1035 Preferred Syntax or not. +// The root zone, and all reverse zones always return true even though they technically don't meet 1035 Preferred Syntax +func checkZoneSyntax(zone string) bool { + if zone == "." || dnsutil.IsReverse(zone) != 0 { + return true + } + regex1035PreferredSyntax, _ := regexp.MatchString(`^(([A-Za-z]([A-Za-z0-9-]*[A-Za-z0-9])?)\.)+$`, zone) + return regex1035PreferredSyntax +} + // startUpZones creates the text that we show when starting up: // grpc://example.com.:1055 // example.com.:1053 on 127.0.0.1 @@ -13,6 +26,7 @@ func startUpZones(protocol, addr string, zones map[string]*Config) string { keys := make([]string, len(zones)) i := 0 + for k := range zones { keys[i] = k i++ @@ -20,6 +34,9 @@ func startUpZones(protocol, addr string, zones map[string]*Config) string { sort.Strings(keys) for _, zone := range keys { + if !checkZoneSyntax(zone) { + s += fmt.Sprintf("Warning: Domain %q does not follow RFC1035 preferred syntax\n", zone) + } // split addr into protocol, IP and Port _, ip, port, err := SplitProtocolHostPort(addr) diff --git a/core/dnsserver/onstartup_test.go b/core/dnsserver/onstartup_test.go new file mode 100644 index 00000000000..41d4d82f890 --- /dev/null +++ b/core/dnsserver/onstartup_test.go @@ -0,0 +1,39 @@ +package dnsserver + +import ( + "testing" +) + +func TestRegex1035PrefSyntax(t *testing.T) { + testCases := []struct { + zone string + expected bool + }{ + {zone: ".", expected: true}, + {zone: "example.com.", expected: true}, + {zone: "example.", expected: true}, + {zone: "example123.", expected: true}, + {zone: "example123.com.", expected: true}, + {zone: "abc-123.com.", expected: true}, + {zone: "an-example.com.", expected: true}, + {zone: "a.example.com.", expected: true}, + {zone: "1.0.0.2.ip6.arpa.", expected: true}, + {zone: "0.10.in-addr.arpa.", expected: true}, + {zone: "example", expected: false}, + {zone: "example:.", expected: false}, + {zone: "-example.com.", expected: false}, + {zone: ".example.com.", expected: false}, + {zone: "1.example.com", expected: false}, + {zone: "abc.123-xyz.", expected: false}, + {zone: "example-?&^%$.com.", expected: false}, + {zone: "abc-.example.com.", expected: false}, + {zone: "abc-%$.example.com.", expected: false}, + {zone: "123-abc.example.com.", expected: false}, + } + + for _, testCase := range testCases { + if checkZoneSyntax(testCase.zone) != testCase.expected { + t.Errorf("Expected %v for %q", testCase.expected, testCase.zone) + } + } +} diff --git a/core/dnsserver/register.go b/core/dnsserver/register.go index ec254085edc..d64a3e7d0e1 100644 --- a/core/dnsserver/register.go +++ b/core/dnsserver/register.go @@ -138,7 +138,6 @@ func (h *dnsContext) InspectServerBlocks(sourceFile string, serverBlocks []caddy // MakeServers uses the newly-created siteConfigs to create and return a list of server instances. func (h *dnsContext) MakeServers() ([]caddy.Server, error) { - // Now that all Keys and Directives are parsed and initialized // lets verify that there is no overlap on the zones and addresses to listen for errValid := h.validateZonesAndListeningAddresses() @@ -154,7 +153,9 @@ func (h *dnsContext) MakeServers() ([]caddy.Server, error) { c.Plugin = c.firstConfigInBlock.Plugin c.ListenHosts = c.firstConfigInBlock.ListenHosts c.Debug = c.firstConfigInBlock.Debug + c.Stacktrace = c.firstConfigInBlock.Stacktrace c.TLSConfig = c.firstConfigInBlock.TLSConfig + c.TsigSecret = c.firstConfigInBlock.TsigSecret } // we must map (group) each config to a bind address @@ -195,7 +196,6 @@ func (h *dnsContext) MakeServers() ([]caddy.Server, error) { } servers = append(servers, s) } - } return servers, nil @@ -260,11 +260,9 @@ func (h *dnsContext) validateZonesAndListeningAddresses() error { if overlapZone != nil { return fmt.Errorf("cannot serve %s - zone overlap listener capacity with %v", akey.String(), overlapZone.String()) } - } } return nil - } // groupSiteConfigsByListenAddr groups site configs by their listen diff --git a/core/dnsserver/register_test.go b/core/dnsserver/register_test.go index a2d24e1dd74..4d3ce11e2c6 100644 --- a/core/dnsserver/register_test.go +++ b/core/dnsserver/register_test.go @@ -114,7 +114,6 @@ func TestGroupingServers(t *testing.T) { for _, v := range test.expectedGroups { if _, ok := groups[v]; !ok { t.Errorf("Test %d : expected value %v to be in the group, was not", i, v) - } } } diff --git a/core/dnsserver/server.go b/core/dnsserver/server.go index fd498f196b4..15d1f90ceea 100644 --- a/core/dnsserver/server.go +++ b/core/dnsserver/server.go @@ -6,6 +6,7 @@ import ( "fmt" "net" "runtime" + "runtime/debug" "strings" "sync" "time" @@ -41,17 +42,20 @@ type Server struct { graceTimeout time.Duration // the maximum duration of a graceful shutdown trace trace.Trace // the trace plugin for the server debug bool // disable recover() + stacktrace bool // enable stacktrace in recover error log classChaos bool // allow non-INET class queries + + tsigSecret map[string]string } // NewServer returns a new CoreDNS server and compiles all plugins in to it. By default CH class // queries are blocked unless queries from enableChaos are loaded. func NewServer(addr string, group []*Config) (*Server, error) { - s := &Server{ Addr: addr, zones: make(map[string]*Config), graceTimeout: 5 * time.Second, + tsigSecret: make(map[string]string), } // We have to bound our wg with one increment @@ -67,9 +71,15 @@ func NewServer(addr string, group []*Config) (*Server, error) { s.debug = true log.D.Set() } + s.stacktrace = site.Stacktrace // set the config per zone s.zones[site.Zone] = site + // copy tsig secrets + for key, secret := range site.TsigSecret { + s.tsigSecret[key] = secret + } + // compile custom plugin for everything var stack plugin.Handler for i := len(site.Plugin) - 1; i >= 0; i-- { @@ -112,7 +122,7 @@ func (s *Server) Serve(l net.Listener) error { ctx := context.WithValue(context.Background(), Key{}, s) ctx = context.WithValue(ctx, LoopKey{}, 0) s.ServeDNS(ctx, w, r) - })} + }), TsigSecret: s.tsigSecret} s.m.Unlock() return s.server[tcp].ActivateAndServe() @@ -126,7 +136,7 @@ func (s *Server) ServePacket(p net.PacketConn) error { ctx := context.WithValue(context.Background(), Key{}, s) ctx = context.WithValue(ctx, LoopKey{}, 0) s.ServeDNS(ctx, w, r) - })} + }), TsigSecret: s.tsigSecret} s.m.Unlock() return s.server[udp].ActivateAndServe() @@ -163,7 +173,6 @@ func (s *Server) ListenPacket() (net.PacketConn, error) { // immediately. // This implements Caddy.Stopper interface. func (s *Server) Stop() (err error) { - if runtime.GOOS != "windows" { // force connections to close after timeout done := make(chan struct{}) @@ -213,7 +222,11 @@ func (s *Server) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) // In case the user doesn't enable error plugin, we still // need to make sure that we stay alive up here if rec := recover(); rec != nil { - log.Errorf("Recovered from panic in server: %q %v", s.Addr, rec) + if s.stacktrace { + log.Errorf("Recovered from panic in server: %q %v\n%s", s.Addr, rec, string(debug.Stack())) + } else { + log.Errorf("Recovered from panic in server: %q %v", s.Addr, rec) + } vars.Panic.Inc() errorAndMetricsFunc(s.Addr, w, r, dns.RcodeServerFailure) } diff --git a/core/dnsserver/server_grpc.go b/core/dnsserver/server_grpc.go index 766c27e367a..2ba4d6a4de3 100644 --- a/core/dnsserver/server_grpc.go +++ b/core/dnsserver/server_grpc.go @@ -82,7 +82,6 @@ func (s *ServergRPC) ServePacket(p net.PacketConn) error { return nil } // Listen implements caddy.TCPServer interface. func (s *ServergRPC) Listen() (net.Listener, error) { - l, err := reuseport.Listen("tcp", s.Addr[len(transport.GRPC+"://"):]) if err != nil { return nil, err diff --git a/core/dnsserver/server_https.go b/core/dnsserver/server_https.go index ba609721515..8904da57e76 100644 --- a/core/dnsserver/server_https.go +++ b/core/dnsserver/server_https.go @@ -4,6 +4,7 @@ import ( "context" "crypto/tls" "fmt" + stdlog "log" "net" "net/http" "strconv" @@ -13,6 +14,7 @@ import ( "github.com/coredns/coredns/plugin/metrics/vars" "github.com/coredns/coredns/plugin/pkg/dnsutil" "github.com/coredns/coredns/plugin/pkg/doh" + clog "github.com/coredns/coredns/plugin/pkg/log" "github.com/coredns/coredns/plugin/pkg/response" "github.com/coredns/coredns/plugin/pkg/reuseport" "github.com/coredns/coredns/plugin/pkg/transport" @@ -27,6 +29,18 @@ type ServerHTTPS struct { validRequest func(*http.Request) bool } +// loggerAdapter is a simple adapter around CoreDNS logger made to implement io.Writer in order to log errors from HTTP server +type loggerAdapter struct { +} + +func (l *loggerAdapter) Write(p []byte) (n int, err error) { + clog.Debug(string(p)) + return len(p), nil +} + +// HTTPRequestKey is the context key for the current processed HTTP request (if current processed request was done over DOH) +type HTTPRequestKey struct{} + // NewServerHTTPS returns a new CoreDNS HTTPS server and compiles all plugins in to it. func NewServerHTTPS(addr string, group []*Config) (*ServerHTTPS, error) { s, err := NewServer(addr, group) @@ -60,6 +74,7 @@ func NewServerHTTPS(addr string, group []*Config) (*ServerHTTPS, error) { ReadTimeout: 5 * time.Second, WriteTimeout: 10 * time.Second, IdleTimeout: 120 * time.Second, + ErrorLog: stdlog.New(&loggerAdapter{}, "", 0), } sh := &ServerHTTPS{ Server: s, tlsConfig: tlsConfig, httpsServer: srv, validRequest: validator, @@ -89,7 +104,6 @@ func (s *ServerHTTPS) ServePacket(p net.PacketConn) error { return nil } // Listen implements caddy.TCPServer interface. func (s *ServerHTTPS) Listen() (net.Listener, error) { - l, err := reuseport.Listen("tcp", s.Addr[len(transport.HTTPS+"://"):]) if err != nil { return nil, err @@ -126,7 +140,6 @@ func (s *ServerHTTPS) Stop() error { // ServeHTTP is the handler that gets the HTTP request and converts to the dns format, calls the plugin // chain, converts it back and write it to the client. func (s *ServerHTTPS) ServeHTTP(w http.ResponseWriter, r *http.Request) { - if !s.validRequest(r) { http.Error(w, "", http.StatusNotFound) s.countResponse(http.StatusNotFound) @@ -153,6 +166,7 @@ func (s *ServerHTTPS) ServeHTTP(w http.ResponseWriter, r *http.Request) { // We should expect a packet to be returned that we can send to the client. ctx := context.WithValue(context.Background(), Key{}, s.Server) ctx = context.WithValue(ctx, LoopKey{}, 0) + ctx = context.WithValue(ctx, HTTPRequestKey{}, r) s.ServeDNS(ctx, dw, msg) // See section 4.2.1 of RFC 8484. diff --git a/core/dnsserver/server_test.go b/core/dnsserver/server_test.go index d7289474b61..c52b6a21a26 100644 --- a/core/dnsserver/server_test.go +++ b/core/dnsserver/server_test.go @@ -26,6 +26,7 @@ func testConfig(transport string, p plugin.Handler) *Config { ListenHosts: []string{"127.0.0.1"}, Port: "53", Debug: false, + Stacktrace: false, } c.AddPlugin(func(next plugin.Handler) plugin.Handler { return p }) @@ -76,6 +77,27 @@ func TestDebug(t *testing.T) { } } +func TestStacktrace(t *testing.T) { + configNoStacktrace, configStacktrace := testConfig("dns", testPlugin{}), testConfig("dns", testPlugin{}) + configStacktrace.Stacktrace = true + + s1, err := NewServer("127.0.0.1:53", []*Config{configStacktrace, configStacktrace}) + if err != nil { + t.Errorf("Expected no error for NewServer, got %s", err) + } + if !s1.stacktrace { + t.Errorf("Expected stacktrace mode enabled for server s1") + } + + s2, err := NewServer("127.0.0.1:53", []*Config{configNoStacktrace}) + if err != nil { + t.Errorf("Expected no error for NewServer, got %s", err) + } + if s2.stacktrace { + t.Errorf("Expected stacktrace disabled for server s2") + } +} + func BenchmarkCoreServeDNS(b *testing.B) { s, err := NewServer("127.0.0.1:53", []*Config{testConfig("dns", testPlugin{})}) if err != nil { diff --git a/core/dnsserver/zdirectives.go b/core/dnsserver/zdirectives.go index bca217185f5..53168be869b 100644 --- a/core/dnsserver/zdirectives.go +++ b/core/dnsserver/zdirectives.go @@ -34,6 +34,7 @@ var Directives = []string{ "any", "chaos", "loadbalance", + "tsig", "cache", "rewrite", "header", diff --git a/core/plugin/zplugin.go b/core/plugin/zplugin.go index a9167eeaf6e..45bfb5415e0 100644 --- a/core/plugin/zplugin.go +++ b/core/plugin/zplugin.go @@ -52,5 +52,6 @@ import ( _ "github.com/coredns/coredns/plugin/tls" _ "github.com/coredns/coredns/plugin/trace" _ "github.com/coredns/coredns/plugin/transfer" + _ "github.com/coredns/coredns/plugin/tsig" _ "github.com/coredns/coredns/plugin/whoami" ) diff --git a/coremain/version.go b/coremain/version.go index 3740ff1ef48..89588ff5f0f 100644 --- a/coremain/version.go +++ b/coremain/version.go @@ -2,7 +2,7 @@ package coremain // Various CoreDNS constants. const ( - CoreVersion = "1.9.2" + CoreVersion = "1.9.3" coreName = "CoreDNS" serverType = "dns" ) diff --git a/go.mod b/go.mod index f87190f1a43..7e76dc89b8f 100644 --- a/go.mod +++ b/go.mod @@ -3,41 +3,42 @@ module github.com/coredns/coredns go 1.17 require ( - github.com/Azure/azure-sdk-for-go v64.1.0+incompatible + github.com/Azure/azure-sdk-for-go v66.0.0+incompatible github.com/Azure/go-autorest/autorest v0.11.27 github.com/Azure/go-autorest/autorest/azure/auth v0.5.11 github.com/apparentlymart/go-cidr v1.1.0 - github.com/aws/aws-sdk-go v1.44.14 + github.com/aws/aws-sdk-go v1.44.51 github.com/coredns/caddy v1.1.1 github.com/dnstap/golang-dnstap v0.4.0 github.com/farsightsec/golang-framestream v0.3.0 + github.com/go-logr/logr v1.2.3 github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 github.com/infobloxopen/go-trees v0.0.0-20200715205103-96a057b8dfb9 github.com/matttproud/golang_protobuf_extensions v1.0.1 - github.com/miekg/dns v1.1.49 + github.com/miekg/dns v1.1.50 github.com/opentracing/opentracing-go v1.2.0 github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5 github.com/openzipkin/zipkin-go v0.4.0 github.com/oschwald/geoip2-golang v1.7.0 - github.com/prometheus/client_golang v1.12.1 + github.com/prometheus/client_golang v1.12.2 github.com/prometheus/client_model v0.2.0 - github.com/prometheus/common v0.34.0 + github.com/prometheus/common v0.37.0 go.etcd.io/etcd/api/v3 v3.5.4 go.etcd.io/etcd/client/v3 v3.5.4 - golang.org/x/crypto v0.0.0-20220214200702-86341886e292 - golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 - google.golang.org/api v0.79.0 - google.golang.org/grpc v1.46.2 + golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e + golang.org/x/sys v0.0.0-20220624220833-87e55d714810 + google.golang.org/api v0.87.0 + google.golang.org/grpc v1.48.0 google.golang.org/protobuf v1.28.0 - gopkg.in/DataDog/dd-trace-go.v1 v1.38.1 - k8s.io/api v0.24.0 - k8s.io/apimachinery v0.24.0 - k8s.io/client-go v0.24.0 - k8s.io/klog/v2 v2.60.1 + gopkg.in/DataDog/dd-trace-go.v1 v1.40.1 + k8s.io/api v0.24.3 + k8s.io/apimachinery v0.24.3 + k8s.io/client-go v0.24.2 + k8s.io/klog/v2 v2.70.1 ) require ( - cloud.google.com/go/compute v1.6.1 // indirect + cloud.google.com/go/compute v1.7.0 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest/adal v0.9.18 // indirect github.com/Azure/go-autorest/autorest/azure/cli v0.4.5 // indirect @@ -48,7 +49,7 @@ require ( github.com/DataDog/datadog-agent/pkg/obfuscate v0.0.0-20211129110424-6491aa3bf583 // indirect github.com/DataDog/datadog-go v4.8.2+incompatible // indirect github.com/DataDog/datadog-go/v5 v5.0.2 // indirect - github.com/DataDog/sketches-go v1.0.0 // indirect + github.com/DataDog/sketches-go v1.2.1 // indirect github.com/Microsoft/go-winio v0.5.1 // indirect github.com/PuerkitoBio/purell v1.1.1 // indirect github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect @@ -63,7 +64,6 @@ require ( github.com/emicklei/go-restful v2.9.5+incompatible // indirect github.com/evanphx/json-patch v4.12.0+incompatible // indirect github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 // indirect - github.com/go-logr/logr v1.2.0 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonreference v0.19.5 // indirect github.com/go-openapi/swag v0.19.14 // indirect @@ -76,7 +76,8 @@ require ( github.com/google/go-cmp v0.5.8 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/uuid v1.3.0 // indirect - github.com/googleapis/gax-go/v2 v2.3.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.1.0 // indirect + github.com/googleapis/gax-go/v2 v2.4.0 // indirect github.com/imdario/mergo v0.3.12 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect @@ -99,18 +100,18 @@ require ( go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.17.0 // indirect golang.org/x/mod v0.4.2 // indirect - golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 // indirect - golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 // indirect + golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e // indirect + golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2 // indirect golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2 // indirect - golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f // indirect + golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3 // indirect + google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect + gopkg.in/yaml.v3 v3.0.0 // indirect k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42 // indirect k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 // indirect sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 // indirect diff --git a/go.sum b/go.sum index e7232335656..1b3ced0861f 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,6 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= @@ -26,8 +27,9 @@ cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+Y cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= -cloud.google.com/go v0.100.2 h1:t9Iw5QH5v4XtlEQaCtUY7x6sCABps8sW0acw7e2WQ6Y= cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= +cloud.google.com/go v0.102.0 h1:DAq3r8y4mDgyB/ZPJ9v/5VJNqjgJAxTn6ZYLlUywOu8= +cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= @@ -38,10 +40,12 @@ cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTB cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= -cloud.google.com/go/compute v1.6.1 h1:2sMmt8prCn7DPaG4Pmh0N3Inmc8cT8ae5k1M6VJ9Wqc= cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= +cloud.google.com/go/compute v1.7.0 h1:v/k9Eueb8aAJ0vZuxKMrgm6kPhCLZU9HxFU+AFDs9Uk= +cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -52,9 +56,10 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/Azure/azure-sdk-for-go v64.1.0+incompatible h1:FpsZmWR9FfEr9hP6K9S7RP0EkSFgGd6P1F2scHtbhnU= -github.com/Azure/azure-sdk-for-go v64.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go v66.0.0+incompatible h1:bmmC38SlE8/E81nNADlgmVGurPWMHDX2YNXVQMrBpEE= +github.com/Azure/azure-sdk-for-go v66.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= @@ -96,8 +101,8 @@ github.com/DataDog/datadog-go v4.8.2+incompatible/go.mod h1:LButxg5PwREeZtORoXG3 github.com/DataDog/datadog-go/v5 v5.0.2 h1:UFtEe7662/Qojxkw1d6SboAeA0CPI3naKhVASwFn+04= github.com/DataDog/datadog-go/v5 v5.0.2/go.mod h1:ZI9JFB4ewXbw1sBnF4sxsR2k1H3xjV+PUAOUsHvKpcU= github.com/DataDog/gostackparse v0.5.0/go.mod h1:lTfqcJKqS9KnXQGnyQMCugq3u1FP6UZMfWR0aitKFMM= -github.com/DataDog/sketches-go v1.0.0 h1:chm5KSXO7kO+ywGWJ0Zs6tdmWU8PBXSbywFVciL6BG4= -github.com/DataDog/sketches-go v1.0.0/go.mod h1:O+XkJHWk9w4hDwY2ZUDU31ZC9sNYlYo8DiFsxjYeo1k= +github.com/DataDog/sketches-go v1.2.1 h1:qTBzWLnZ3kM2kw39ymh6rMcnN+5VULwFs++lEYUUsro= +github.com/DataDog/sketches-go v1.2.1/go.mod h1:1xYmPLY1So10AwxV6MJV0J53XVH+WL9Ad1KetxVivVI= github.com/DataDog/zstd v1.3.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= @@ -122,7 +127,9 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= +github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apparentlymart/go-cidr v1.1.0 h1:2mAhrMoF+nhXqxTzSZMUzDHkLjmIHC+Zzn4tdgBZjnU= github.com/apparentlymart/go-cidr v1.1.0/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= @@ -133,8 +140,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkY github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48= -github.com/aws/aws-sdk-go v1.44.14 h1:qd7/muV1rElsbvkK9D1nHUzBoDlEw2etfeo4IE82eSQ= -github.com/aws/aws-sdk-go v1.44.14/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= +github.com/aws/aws-sdk-go v1.44.51 h1:jO9hoLynZOrMM4dj0KjeKIK+c6PA+HQbKoHOkAEye2Y= +github.com/aws/aws-sdk-go v1.44.51/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= github.com/aws/aws-sdk-go-v2 v1.0.0/go.mod h1:smfAbmpW+tcRVuNUjo3MOArSZmW72t62rkCzc2i0TWM= github.com/aws/aws-sdk-go-v2/config v1.0.0/go.mod h1:WysE/OpUgE37tjtmtJd8GXgT8s1euilE5XtUkRNUQ1w= github.com/aws/aws-sdk-go-v2/credentials v1.0.0/go.mod h1:/SvsiqBf509hG4Bddigr3NB12MIpfHhZapyBurJe8aY= @@ -191,6 +198,7 @@ github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM= github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/denisenkom/go-mssqldb v0.11.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/dgraph-io/ristretto v0.1.0 h1:Jv3CGQHp9OjuMBSne1485aDpUkTKEcUqF+jm/LuerPI= @@ -207,6 +215,7 @@ github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZ github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= @@ -253,7 +262,7 @@ github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSy github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.7.0/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= +github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U= github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-chi/chi v1.5.0/go.mod h1:REp24E+25iKvxgeTfHmdUoL5x15kBiDBlnIl5bCwe2k= @@ -272,8 +281,9 @@ github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v1.2.0 h1:QK40JKJyMdUDz+h+xvCsru/bJhvG0UxvePV0ufL/AcE= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= @@ -297,36 +307,13 @@ github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8w github.com/go-redis/redis/v7 v7.1.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg= github.com/go-redis/redis/v8 v8.0.0/go.mod h1:isLoQT/NFSP7V67lyvM9GmdvLdyZ7pEhsXvvyQtnQTo= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= -github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= -github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= -github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= -github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= -github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= -github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= -github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= -github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= -github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= -github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= -github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo= -github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk= -github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= -github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= -github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg= -github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE= -github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= -github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= -github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= -github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= -github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= -github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= -github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= -github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= github.com/gocql/gocql v0.0.0-20220224095938-0eacd3183625/go.mod h1:3gM2c4D3AnkISwBxGnMMsS8Oy4y2lhbPRsH4xnJrHG8= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofiber/fiber/v2 v2.11.0/go.mod h1:oZTLWqYnqpMMuF922SjGbsYZsdpE1MCfh416HNdweIM= @@ -336,7 +323,6 @@ github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFG github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= @@ -433,17 +419,21 @@ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= +github.com/googleapis/enterprise-certificate-proxy v0.1.0 h1:zO8WHNx/MYiAKJ3d5spxZXZE6KHmIQGQcAzwUzV7qQw= +github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= -github.com/googleapis/gax-go/v2 v2.3.0 h1:nRJtk3y8Fm770D42QV6T90ZnvFZyk7agSo3Q+Z9p3WI= github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= +github.com/googleapis/gax-go/v2 v2.4.0 h1:dS9eYAjhrE2RjmzYw2XAPvcXfmcQLtFEQWn0CR82awk= +github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= -github.com/gorilla/mux v1.5.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= @@ -507,7 +497,6 @@ github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1: github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/infobloxopen/go-trees v0.0.0-20200715205103-96a057b8dfb9 h1:w66aaP3c6SIQ0pi3QH1Tb4AMO3aWoEPxd1CNvLphbkA= github.com/infobloxopen/go-trees v0.0.0-20200715205103-96a057b8dfb9/go.mod h1:BaIJzjD2ZnHmx2acPF6XfGLPzNCMiBbMRqJr+8/8uRI= github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= @@ -571,8 +560,9 @@ github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/U github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg= github.com/jcmturner/gokrb5/v8 v8.4.2/go.mod h1:sb+Xq/fTY5yktf/VxLsE3wlfPqQjp0aWNYyvBVK62bc= github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= -github.com/jinzhu/gorm v1.9.1/go.mod h1:Vla75njaFJ8clLU1W44h34PjIkijhjHIYnZxMqCdxqo= +github.com/jinzhu/gorm v1.9.10/go.mod h1:Kh6hTsSGffh4ui079FHrR5Gg+5D0hgihqDcsDN2BBJY= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.3/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= @@ -581,7 +571,6 @@ github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHW github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= -github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= @@ -595,19 +584,15 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/julienschmidt/httprouter v1.1.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= -github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.12.2/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.14.2/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -628,6 +613,7 @@ github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3 github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= @@ -639,8 +625,6 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= -github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= @@ -658,14 +642,15 @@ github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOA github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.25/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.31/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= -github.com/miekg/dns v1.1.49 h1:qe0mQU3Z/XpFeE+AEBo2rqaS1IPBJ3anmqZ4XiZJVG8= -github.com/miekg/dns v1.1.49/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= +github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= +github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -727,6 +712,7 @@ github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+ github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5 h1:ZCnq+JUrvXcDVhX/xRolRBZifmabN1HcS1wrPSvxhrU= github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= +github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/openzipkin/zipkin-go v0.4.0 h1:CtfRrOVZtbDj8rt1WXjklw0kqqJQwICrCKmlfUuBUUw= github.com/openzipkin/zipkin-go v0.4.0/go.mod h1:4c3sLeE8xjNqehmF5RpAFLPLJxXscc0R4l6Zg0P1tTQ= @@ -736,7 +722,6 @@ github.com/oschwald/maxminddb-golang v1.9.0 h1:tIk4nv6VT9OiPyrnDAfJS1s1xKDQMZOsG github.com/oschwald/maxminddb-golang v1.9.0/go.mod h1:TK+s/Z2oZq0rSl4PSeAEoP0bgm82Cp5HyvYbt8K3zLY= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/philhofer/fwd v1.1.1 h1:GdGcTjf5RNAxwS4QLsiMzJYj5KEvPJD3Abr261yRQXQ= github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= @@ -757,26 +742,31 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= +github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk= github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= +github.com/prometheus/client_golang v1.12.2 h1:51L9cDoUHVrXx4zWYlcLQIZ+d+VXHgqnYKkIuq4g/34= +github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.34.0 h1:RBmGO9d/FVjqHT0yUGQwBJhkwKV+wPCn7KGpvfab0uE= -github.com/prometheus/common v0.34.0/go.mod h1:gB3sOl7P0TvJabZpLY5uQMpUqRCPPCyRLCZYc7JZTNE= +github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= +github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= @@ -786,8 +776,6 @@ github.com/rabbitmq/amqp091-go v1.1.0/go.mod h1:ogQDLSOACsLPsIq0NpbtiifNZi2YOz0V github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= @@ -804,7 +792,6 @@ github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhr github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= @@ -813,9 +800,7 @@ github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= @@ -860,7 +845,7 @@ github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/X github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.26.0/go.mod h1:cmWIqlu99AO/RKcp1HWaViTqc57FswJOfYYdPJBl8BA= -github.com/valyala/fasthttp v1.32.0/go.mod h1:2rsYD01CKFrjjsvFxx75KlEUNpWNBY9JWD3K/7o2Cus= +github.com/valyala/fasthttp v1.34.0/go.mod h1:epZA5N+7pY6ZaEKRmstzOuYJx9HI8DI1oaCGZpdH4h0= github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= @@ -890,7 +875,8 @@ go.etcd.io/etcd/client/pkg/v3 v3.5.4 h1:lrneYvz923dvC14R54XcA7FXoZ3mlGZAgmwhfm7H go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v3 v3.5.4 h1:p83BUL3tAYS0OT/r0qglgc3M1JjhM0diV8DSWAhVXv4= go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY= -go.mongodb.org/mongo-driver v1.5.1/go.mod h1:gRXCHX4Jo7J0IJ1oDQyUxF7jfy19UfxniMS4xxMmUqw= +go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng= +go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -919,6 +905,10 @@ go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +go4.org/intern v0.0.0-20211027215823-ae77deb06f29 h1:UXLjNohABv4S58tHmeuIZDO6e3mHpW2Dx33gaNt03LE= +go4.org/intern v0.0.0-20211027215823-ae77deb06f29/go.mod h1:cS2ma+47FKrLPdXFpr7CuxiTW3eyJbWew4qx0qtQWDA= +go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37 h1:Tx9kY6yUkLge/pFG7IEMwDZy6CS2ajFc9TvQdPCW0uA= +go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -928,7 +918,6 @@ golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= -golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190506204251-e1dfcc566284/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -949,8 +938,9 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210920023735-84f357641f63/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220214200702-86341886e292 h1:f+lwQ+GtmgoY+A2YaQxlSOnDjXcQ7ZRLWOHbC6HtRqE= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM= +golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -998,6 +988,7 @@ golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -1043,14 +1034,15 @@ golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210917221730-978cfadd31cf/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211020060615-d418f374d309/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA= golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e h1:TsQ7F31D3bUCLeqPT0u+yjp1guoArKaNKmCr22PYgTQ= +golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1069,21 +1061,23 @@ golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 h1:OSnWWcOd/CtWQC2cYSBgbTSJv3ciqd8r54ySIW2y3RE= golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= +golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2 h1:+jnHzr9VPj32ykQVai5DNahi9+NSp7yYuCsl5eAQtL0= +golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8= +golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1091,6 +1085,7 @@ golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1098,11 +1093,9 @@ golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1175,8 +1168,12 @@ golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220325203850-36772127a21f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 h1:nonptSpoQ4vQjyraW20DXPAglgQfVnM9ZC6MmNLMR60= +golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220624220833-87e55d714810 h1:rHZQSjJdAI4Xf5Qzeh2bBc5YJIkPFVM6oDtMFYmgws0= +golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= @@ -1200,6 +1197,7 @@ golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1208,9 +1206,6 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= @@ -1277,8 +1272,11 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f h1:GGU+dLjvlC3qDwqYgL6UgRmHXhOOgns0bZu2Ty5mm6U= golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f h1:uF6paiQQebLeSXkrTqHqz0MXhXXS1KgF41eUdBNvxK0= +golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -1316,8 +1314,11 @@ google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/S google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= -google.golang.org/api v0.79.0 h1:vaOcm0WdXvhGkci9a0+CcQVZqSRjN8ksSBlWv99f8Pg= -google.golang.org/api v0.79.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= +google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= +google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= +google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= +google.golang.org/api v0.87.0 h1:pUQVF/F+X7Tl1lo4LJoJf5BOpjtmINU80p9XpYTU2p4= +google.golang.org/api v0.87.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1328,6 +1329,7 @@ google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6 google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -1368,6 +1370,7 @@ google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= @@ -1400,9 +1403,16 @@ google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3 h1:q1kiSVscqoDeqTF27eQ2NnLLDmqF0I373qQNXYMy0fo= +google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f h1:hJ/Y5SqPXbarffmAsApliUlcvMU+wScNGfyop4bZm8o= +google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -1437,8 +1447,10 @@ google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzI google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.46.2 h1:u+MLGgVf7vRdjEYZ8wDFhAVNmhkbJ5hmrA1LMWK1CAQ= google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.48.0 h1:rQOsyJ/8+ufEDJd/Gdsz7HG220Mh9HAhFHRGnIjda0w= +google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -1455,8 +1467,8 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/DataDog/dd-trace-go.v1 v1.38.1 h1:nAKgcpJLXRHF56cKCP3bN8gTTQmmNAZFEblbyGKhKTo= -gopkg.in/DataDog/dd-trace-go.v1 v1.38.1/go.mod h1:GBhK4yaMJ1h329ivtKAqRNe1EZ944UnZwtz5lh7CnJc= +gopkg.in/DataDog/dd-trace-go.v1 v1.40.1 h1:ou2cMah30qvQEMTYPF0CVOhDd2ji2+WQk9/meYFuZ84= +gopkg.in/DataDog/dd-trace-go.v1 v1.40.1/go.mod h1:tlSNIf2aKOah7PmoEP4qQETNVKgonk5BWwNnblw8C8w= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -1486,14 +1498,16 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gorm.io/driver/mysql v1.0.1/go.mod h1:KtqSthtg55lFp3S5kUXqlGaelnWpKitn4k1xZTnoiPw= gorm.io/driver/postgres v1.0.0/go.mod h1:wtMFcOzmuA5QigNsgEIb7O5lhvH1tHAF1RbWmLWV4to= gorm.io/driver/sqlserver v1.0.4/go.mod h1:ciEo5btfITTBCj9BkoUVDvgQbUdLWQNqdFY5OGuGnRg= gorm.io/gorm v1.9.19/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= gorm.io/gorm v1.20.0/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= gorm.io/gorm v1.20.6/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1501,15 +1515,19 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +inet.af/netaddr v0.0.0-20211027220019-c74959edd3b6 h1:acCzuUSQ79tGsM/O50VRFySfMm19IoMKL+sZztZkCxw= +inet.af/netaddr v0.0.0-20211027220019-c74959edd3b6/go.mod h1:y3MGhcFMlh0KZPMuXXow8mpjxxAk3yoDNsp4cQz54i8= k8s.io/api v0.17.0/go.mod h1:npsyOePkeP0CPwyGfXDHxvypiYMJxBWAMpQxCaJ4ZxI= -k8s.io/api v0.24.0 h1:J0hann2hfxWr1hinZIDefw7Q96wmCBx6SSB8IY0MdDg= -k8s.io/api v0.24.0/go.mod h1:5Jl90IUrJHUJYEMANRURMiVvJ0g7Ax7r3R1bqO8zx8I= +k8s.io/api v0.24.2/go.mod h1:AHqbSkTm6YrQ0ObxjO3Pmp/ubFF/KuM7jU+3khoBsOg= +k8s.io/api v0.24.3 h1:tt55QEmKd6L2k5DP6G/ZzdMQKvG5ro4H4teClqm0sTY= +k8s.io/api v0.24.3/go.mod h1:elGR/XSZrS7z7cSZPzVWaycpJuGIw57j9b95/1PdJNI= k8s.io/apimachinery v0.17.0/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg= -k8s.io/apimachinery v0.24.0 h1:ydFCyC/DjCvFCHK5OPMKBlxayQytB8pxy8YQInd5UyQ= -k8s.io/apimachinery v0.24.0/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= +k8s.io/apimachinery v0.24.2/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= +k8s.io/apimachinery v0.24.3 h1:hrFiNSA2cBZqllakVYyH/VyEh4B581bQRmqATJSeQTg= +k8s.io/apimachinery v0.24.3/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= k8s.io/client-go v0.17.0/go.mod h1:TYgR6EUHs6k45hb6KWjVD6jFZvJV4gHDikv/It0xz+k= -k8s.io/client-go v0.24.0 h1:lbE4aB1gTHvYFSwm6eD3OF14NhFDKCejlnsGYlSJe5U= -k8s.io/client-go v0.24.0/go.mod h1:VFPQET+cAFpYxh6Bq6f4xyMY80G6jKKktU6G0m00VDw= +k8s.io/client-go v0.24.2 h1:CoXFSf8if+bLEbinDqN9ePIDGzcLtqhfd6jpfnwGOFA= +k8s.io/client-go v0.24.2/go.mod h1:zg4Xaoo+umDsfCWr4fCnmLEtQXyCNXCvJuSsglNcV30= k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= @@ -1518,8 +1536,9 @@ k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.60.1 h1:VW25q3bZx9uE3vvdL6M8ezOX79vA2Aq1nEWLqNQclHc= k8s.io/klog/v2 v2.60.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/klog/v2 v2.70.1 h1:7aaoSdahviPmR+XkS7FyxlkkXs6tHISSG03RxleQAVQ= +k8s.io/klog/v2 v2.70.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42 h1:Gii5eqf+GmIEwGNKQYQClCayuJCe2/4fZUvF7VG99sU= k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42/go.mod h1:Z/45zLw8lUo4wdiUkI+v/ImEGAvu3WatcZl3lPMR4Rk= diff --git a/man/coredns-tsig.7 b/man/coredns-tsig.7 new file mode 100644 index 00000000000..9716515facb --- /dev/null +++ b/man/coredns-tsig.7 @@ -0,0 +1,150 @@ +.\" Generated by Mmark Markdown Processer - mmark.miek.nl +.TH "COREDNS-TSIG" 7 "July 2022" "CoreDNS" "CoreDNS Plugins" + +.SH "NAME" +.PP +\fItsig\fP - validate TSIG requests and sign responses. + +.SH "DESCRIPTION" +.PP +With \fItsig\fP, you can define a set of TSIG secret keys for validating incoming TSIG requests and signing +responses. It can also require TSIG for certain query types, refusing requests that do not comply. + +.SH "SYNTAX" +.PP +.RS + +.nf +tsig [ZONE...] { + secret NAME KEY + secrets FILE + require [QTYPE...] +} + +.fi +.RE + +.IP \(bu 4 +\fBZONE\fP - the zones \fItsig\fP will TSIG. By default, the zones from the server block are used. +.IP \(bu 4 +\fB\fCsecret\fR \fBNAME\fP \fBKEY\fP - specifies a TSIG secret for \fBNAME\fP with \fBKEY\fP. Use this option more than once +to define multiple secrets. Secrets are global to the server instance, not just for the enclosing \fBZONE\fP. +.IP \(bu 4 +\fB\fCsecrets\fR \fBFILE\fP - same as \fB\fCsecret\fR, but load the secrets from a file. The file may define any number + of unique keys, each in the following \fB\fCnamed.conf\fR format: + +.PP +.RS + +.nf + key "example." { + secret "X28hl0BOfAL5G0jsmJWSacrwn7YRm2f6U5brnzwWEus="; + }; + +.fi +.RE + + +Each key may also specify an \fB\fCalgorithm\fR e.g. \fB\fCalgorithm hmac-sha256;\fR, but this is currently ignored by the plugin. + +.RS +.IP \(en 4 +\fB\fCrequire\fR \fBQTYPE...\fP - the query types that must be TSIG'd. Requests of the specified types +will be \fB\fCREFUSED\fR if they are not signed.\fB\fCrequire all\fR will require requests of all types to be +signed. \fB\fCrequire none\fR will not require requests any types to be signed. Default behavior is to not require. + +.RE + + +.SH "EXAMPLES" +.PP +Require TSIG signed transactions for transfer requests to \fB\fCexample.zone\fR. + +.PP +.RS + +.nf +example.zone { + tsig { + secret example.zone.key. NoTCJU+DMqFWywaPyxSijrDEA/eC3nK0xi3AMEZuPVk= + require AXFR IXFR + } + transfer { + to * + } +} + +.fi +.RE + +.PP +Require TSIG signed transactions for all requests to \fB\fCauth.zone\fR. + +.PP +.RS + +.nf +auth.zone { + tsig { + secret auth.zone.key. NoTCJU+DMqFWywaPyxSijrDEA/eC3nK0xi3AMEZuPVk= + require all + } + forward . 10.1.0.2 +} + +.fi +.RE + +.SH "BUGS" +.SS "ZONE TRANSFER NOTIFIES" +.PP +With the transfer plugin, zone transfer notifications from CoreDNS are not TSIG signed. + +.SS "SPECIAL CONSIDERATIONS FOR FORWARDING SERVERS (RFC 8945 5.5)" +.PP +https://datatracker.ietf.org/doc/html/rfc8945#section-5.5 +\[la]https://datatracker.ietf.org/doc/html/rfc8945#section-5.5\[ra] + +.PP +CoreDNS does not implement this section as follows ... + +.IP \(bu 4 +RFC requirement: +> If the name on the TSIG is not +of a secret that the server shares with the originator, the server +MUST forward the message unchanged including the TSIG. + + +.PP +CoreDNS behavior: +If ths zone of the request matches the \fItsig\fP plugin zones, then the TSIG record +is always stripped. But even when the \fItsig\fP plugin is not involved, the \fIforward\fP plugin +may alter the message with compression, which would cause validation failure +at the destination. + +.IP \(bu 4 +RFC requirement: +> If the TSIG passes all checks, the forwarding +server MUST, if possible, include a TSIG of its own to the +destination or the next forwarder. + + +.PP +CoreDNS behavior: +If ths zone of the request matches the \fItsig\fP plugin zones, \fIforward\fP plugin will +proxy the request upstream without TSIG. + +.IP \(bu 4 +RFC requirement: +> If no transaction security is +available to the destination and the message is a query, and if the +corresponding response has the AD flag (see RFC4035) set, the +forwarder MUST clear the AD flag before adding the TSIG to the +response and returning the result to the system from which it +received the query. + + +.PP +CoreDNS behavior: +The AD flag is not cleared. + diff --git a/notes/coredns-1.9.3.md b/notes/coredns-1.9.3.md new file mode 100644 index 00000000000..2601532765f --- /dev/null +++ b/notes/coredns-1.9.3.md @@ -0,0 +1,31 @@ ++++ +title = "CoreDNS-1.9.3 Release" +description = "CoreDNS-1.9.3 Release Notes." +tags = ["Release", "1.9.3", "Notes"] +release = "1.9.3" +date = "2022-05-27T00:00:00+00:00" +author = "coredns" ++++ + +This is a release with a focus on security (CVE-2022-27191 and CVE-2022-28948) fixes. Additionally, +several feature enhancements and bug fixes have been added. + +## Brought to You By + +Chris O'Haver, +lobshunter, +Naveen, +Radim Hatlapatka, +RetoHaslerMGB, +Tintin, +Yong Tang + + +## Noteworthy Changes + +* core: update gopkg.in/yaml.v3 to fix CVE-2022-28948 (https://github.com/coredns/coredns/pull/5408) +* core: update golang.org/x/crypto to fix CVE-2022-27191 (https://github.com/coredns/coredns/pull/5407) +* plugin/acl: adding a check to parse out zone info (https://github.com/coredns/coredns/pull/5387) +* plugin/dnstap: support FQDN TCP endpoint (https://github.com/coredns/coredns/pull/5377) +* plugin/errors: add `stacktrace` option to log a stacktrace during panic recovery (https://github.com/coredns/coredns/pull/5392) +* plugin/template: return SERVFAIL for zone-match regex-no-match case (https://github.com/coredns/coredns/pull/5180) diff --git a/plugin.cfg b/plugin.cfg index e7d84a107b8..eeecf047ff5 100644 --- a/plugin.cfg +++ b/plugin.cfg @@ -44,6 +44,7 @@ any:any aaaa:github.com/snapp-incubator/coredns-plugins/aaaa chaos:chaos loadbalance:loadbalance +tsig:tsig cache:cache rewrite:rewrite header:header diff --git a/plugin/acl/acl.go b/plugin/acl/acl.go index e684dc42c4e..95a5c65eb01 100644 --- a/plugin/acl/acl.go +++ b/plugin/acl/acl.go @@ -3,9 +3,11 @@ package acl import ( "context" "net" + "strings" "github.com/coredns/coredns/plugin" "github.com/coredns/coredns/plugin/metrics" + clog "github.com/coredns/coredns/plugin/pkg/log" "github.com/coredns/coredns/request" "github.com/infobloxopen/go-trees/iptree" @@ -49,6 +51,8 @@ const ( actionFilter ) +var log = clog.NewWithPlugin("acl") + // ServeDNS implements the plugin.Handler interface. func (a ACL) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { state := request.Request{W: w, Req: r} @@ -84,7 +88,6 @@ RulesCheckLoop: return dns.RcodeSuccess, nil } } - } RequestAllowCount.WithLabelValues(metrics.WithServer(ctx)).Inc() @@ -96,7 +99,19 @@ RulesCheckLoop: func matchWithPolicies(policies []policy, w dns.ResponseWriter, r *dns.Msg) action { state := request.Request{W: w, Req: r} - ip := net.ParseIP(state.IP()) + var ip net.IP + if idx := strings.IndexByte(state.IP(), '%'); idx >= 0 { + ip = net.ParseIP(state.IP()[:idx]) + } else { + ip = net.ParseIP(state.IP()) + } + + // if the parsing did not return a proper response then we simply return 'actionBlock' to + // block the query + if ip == nil { + log.Errorf("Blocking request. Unable to parse source address: %v", state.IP()) + return actionBlock + } qtype := state.QType() for _, policy := range policies { // dns.TypeNone matches all query types. diff --git a/plugin/acl/acl_test.go b/plugin/acl/acl_test.go index 0ab6c1d77bc..7b641cef5b8 100644 --- a/plugin/acl/acl_test.go +++ b/plugin/acl/acl_test.go @@ -19,6 +19,10 @@ func (t *testResponseWriter) setRemoteIP(ip string) { t.RemoteIP = ip } +func (t *testResponseWriter) setZone(zone string) { + t.Zone = zone +} + // WriteMsg implement dns.ResponseWriter interface. func (t *testResponseWriter) WriteMsg(m *dns.Msg) error { t.Rcode = m.Rcode @@ -392,6 +396,20 @@ func TestACLServeDNS(t *testing.T) { dns.RcodeSuccess, false, }, + { + "Blacklist Address%ifname", + `acl example.org { + block type AAAA net 2001:0db8:85a3:0000:0000:8a2e:0370:7334 + }`, + []string{"eth0"}, + args{ + "www.example.org.", + "2001:0db8:85a3:0000:0000:8a2e:0370:7334", + dns.TypeAAAA, + }, + dns.RcodeRefused, + false, + }, } ctx := context.Background() @@ -408,6 +426,9 @@ func TestACLServeDNS(t *testing.T) { w := &testResponseWriter{} m := new(dns.Msg) w.setRemoteIP(tt.args.sourceIP) + if len(tt.zones) > 0 { + w.setZone(tt.zones[0]) + } m.SetQuestion(tt.args.domain, tt.args.qtype) _, err = a.ServeDNS(ctx, w, m) if (err != nil) != tt.wantErr { diff --git a/plugin/auto/walk.go b/plugin/auto/walk.go index 993ded40828..a50c5d85c31 100644 --- a/plugin/auto/walk.go +++ b/plugin/auto/walk.go @@ -12,7 +12,6 @@ import ( // Walk will recursively walk of the file under l.directory and adds the one that match l.re. func (a Auto) Walk() error { - // TODO(miek): should add something so that we don't stomp on each other. toDelete := make(map[string]bool) diff --git a/plugin/autopath/autopath.go b/plugin/autopath/autopath.go index b8e153c2aca..e5675e87dd2 100644 --- a/plugin/autopath/autopath.go +++ b/plugin/autopath/autopath.go @@ -135,7 +135,6 @@ func (a *AutoPath) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Ms w.WriteMsg(msg) autoPathCount.WithLabelValues(metrics.WithServer(ctx)).Add(1) return rcode, err - } if plugin.ClientWrite(firstRcode) { w.WriteMsg(firstReply) diff --git a/plugin/azure/azure.go b/plugin/azure/azure.go index ed83a0af788..e236a08b259 100644 --- a/plugin/azure/azure.go +++ b/plugin/azure/azure.go @@ -134,7 +134,6 @@ func (h *Azure) updateZones(ctx context.Context) error { return fmt.Errorf("errors updating zones: %v", errs) } return nil - } func updateZoneFromPublicResourceSet(recordSet publicdns.RecordSetListResultPage, newZ *file.Zone) { @@ -297,7 +296,6 @@ func updateZoneFromPrivateResourceSet(recordSet privatedns.RecordSetListResultPa Target: dns.Fqdn(*CNAME)} newZ.Insert(cname) } - } } diff --git a/plugin/azure/setup.go b/plugin/azure/setup.go index 302555f11e8..6cabe059f46 100644 --- a/plugin/azure/setup.go +++ b/plugin/azure/setup.go @@ -29,20 +29,24 @@ func setup(c *caddy.Controller) error { publicDNSClient := publicAzureDNS.NewRecordSetsClient(env.Values[auth.SubscriptionID]) if publicDNSClient.Authorizer, err = env.GetAuthorizer(); err != nil { + cancel() return plugin.Error("azure", err) } privateDNSClient := privateAzureDNS.NewRecordSetsClient(env.Values[auth.SubscriptionID]) if privateDNSClient.Authorizer, err = env.GetAuthorizer(); err != nil { + cancel() return plugin.Error("azure", err) } h, err := New(ctx, publicDNSClient, privateDNSClient, keys, accessMap) if err != nil { + cancel() return plugin.Error("azure", err) } h.Fall = fall if err := h.Run(ctx); err != nil { + cancel() return plugin.Error("azure", err) } diff --git a/plugin/backend_lookup.go b/plugin/backend_lookup.go index d168ab1a107..0887bb4dc9c 100644 --- a/plugin/backend_lookup.go +++ b/plugin/backend_lookup.go @@ -23,7 +23,6 @@ func A(ctx context.Context, b ServiceBackend, zone string, state request.Request dup := make(map[string]struct{}) for _, serv := range services { - what, ip := serv.HostType() switch what { @@ -97,7 +96,6 @@ func AAAA(ctx context.Context, b ServiceBackend, zone string, state request.Requ dup := make(map[string]struct{}) for _, serv := range services { - what, ip := serv.HostType() switch what { @@ -344,7 +342,6 @@ func CNAME(ctx context.Context, b ServiceBackend, zone string, state request.Req // TXT returns TXT records from Backend or an error. func TXT(ctx context.Context, b ServiceBackend, zone string, state request.Request, previousRecords []dns.RR, opt Options) (records []dns.RR, truncated bool, err error) { - services, err := b.Services(ctx, state, false, opt) if err != nil { return nil, false, err @@ -353,7 +350,6 @@ func TXT(ctx context.Context, b ServiceBackend, zone string, state request.Reque dup := make(map[string]struct{}) for _, serv := range services { - what, _ := serv.HostType() switch what { @@ -405,7 +401,6 @@ func TXT(ctx context.Context, b ServiceBackend, zone string, state request.Reque dup[serv.Text] = struct{}{} records = append(records, serv.NewTXT(state.QName())) } - } } @@ -507,7 +502,6 @@ func BackendError(ctx context.Context, b ServiceBackend, zone string, rcode int, } func newAddress(s msg.Service, name string, ip net.IP, what uint16) dns.RR { - hdr := dns.RR_Header{Name: name, Rrtype: what, Class: dns.ClassINET, Ttl: s.TTL} if what == dns.TypeA { diff --git a/plugin/bind/setup.go b/plugin/bind/setup.go index 068d15dea5e..10fe4a955d4 100644 --- a/plugin/bind/setup.go +++ b/plugin/bind/setup.go @@ -12,7 +12,6 @@ import ( ) func setup(c *caddy.Controller) error { - config := dnsserver.GetConfig(c) // addresses will be consolidated over all BIND directives available in that BlocServer all := []string{} diff --git a/plugin/cache/README.md b/plugin/cache/README.md index 98363abbef4..9322af16ccf 100644 --- a/plugin/cache/README.md +++ b/plugin/cache/README.md @@ -38,6 +38,7 @@ cache [TTL] [ZONES...] { denial CAPACITY [TTL] [MINTTL] prefetch AMOUNT [[DURATION] [PERCENTAGE%]] serve_stale [DURATION] [REFRESH_MODE] + servfail DURATION } ~~~ @@ -54,15 +55,18 @@ cache [TTL] [ZONES...] { **DURATION** defaults to 1m. Prefetching will happen when the TTL drops below **PERCENTAGE**, which defaults to `10%`, or latest 1 second before TTL expiration. Values should be in the range `[10%, 90%]`. Note the percent sign is mandatory. **PERCENTAGE** is treated as an `int`. -* `serve_stale`, when serve\_stale is set, cache always will serve an expired entry to a client if there is one - available. When this happens, cache will attempt to refresh the cache entry after sending the expired cache - entry to the client. The responses have a TTL of 0. **DURATION** is how far back to consider - stale responses as fresh. The default duration is 1h. **REFRESH_MODE** controls when the attempt to refresh - the cache happens. `verified` will first verify that an entry is still unavailable from the source before sending - the stale response to the client. `immediate` will immediately send the expired response to the client before +* `serve_stale`, when serve\_stale is set, cache will always serve an expired entry to a client if there is one + available as long as it has not been expired for longer than **DURATION** (default 1 hour). By default, the _cache_ plugin will + attempt to refresh the cache entry after sending the expired cache entry to the client. The + responses have a TTL of 0. **REFRESH_MODE** controls the timing of the expired cache entry refresh. + `verify` will first verify that an entry is still unavailable from the source before sending the expired entry to the client. + `immediate` will immediately send the expired entry to the client before checking to see if the entry is available from the source. **REFRESH_MODE** defaults to `immediate`. Setting this - value to `verified` can lead to increased latency when serving stale responses, but will prevent stale entries + value to `verify` can lead to increased latency when serving stale responses, but will prevent stale entries from ever being served if an updated response can be retrieved from the source. +* `servfail` cache SERVFAIL responses for **DURATION**. Setting **DURATION** to 0 will disable caching of SERVFAIL + responses. If this option is not set, SERVFAIL responses will be cached for 5 seconds. **DURATION** may not be + greater than 5 minutes. ## Capacity and Eviction diff --git a/plugin/cache/cache.go b/plugin/cache/cache.go index 58a73e72ce7..d1989f35be7 100644 --- a/plugin/cache/cache.go +++ b/plugin/cache/cache.go @@ -32,6 +32,7 @@ type Cache struct { pcap int pttl time.Duration minpttl time.Duration + failttl time.Duration // TTL for caching SERVFAIL responses // Prefetch. prefetch int @@ -59,6 +60,7 @@ func New() *Cache { ncache: cache.New(defaultCap), nttl: maxNTTL, minnttl: minNTTL, + failttl: minNTTL, prefetch: 0, duration: 1 * time.Minute, percentage: 10, @@ -109,8 +111,12 @@ type ResponseWriter struct { server string // Server handling the request. do bool // When true the original request had the DO bit set. + ad bool // When true the original request had the AD bit set. prefetch bool // When true write nothing back to the client. remoteAddr net.Addr + + wildcardFunc func() string // function to retrieve wildcard name that synthesized the result. + } // newPrefetchResponseWriter returns a Cache ResponseWriter to be used in @@ -157,8 +163,7 @@ func (w *ResponseWriter) WriteMsg(res *dns.Msg) error { if mt == response.NameError || mt == response.NoData { duration = computeTTL(msgTTL, w.minnttl, w.nttl) } else if mt == response.ServerError { - // use default ttl which is 5s - duration = minTTL + duration = w.failttl } else { duration = computeTTL(msgTTL, w.minpttl, w.pttl) } @@ -185,8 +190,10 @@ func (w *ResponseWriter) WriteMsg(res *dns.Msg) error { res.Ns = filterRRSlice(res.Ns, ttl, w.do, false) res.Extra = filterRRSlice(res.Extra, ttl, w.do, false) - if !w.do { - res.AuthenticatedData = false // unset AD bit if client is not OK with DNSSEC + if !w.do && !w.ad { + // unset AD bit if requester is not OK with DNSSEC + // But retain AD bit if requester set the AD bit in the request, per RFC6840 5.7-5.8 + res.AuthenticatedData = false } return w.ResponseWriter.WriteMsg(res) @@ -198,6 +205,9 @@ func (w *ResponseWriter) set(m *dns.Msg, key uint64, mt response.Type, duration switch mt { case response.NoError, response.Delegation: i := newItem(m, w.now(), duration) + if w.wildcardFunc != nil { + i.wildcard = w.wildcardFunc() + } if w.pcache.Add(key, i) { evictions.WithLabelValues(w.server, Success, w.zonesMetricLabel).Inc() } @@ -208,6 +218,9 @@ func (w *ResponseWriter) set(m *dns.Msg, key uint64, mt response.Type, duration case response.NameError, response.NoData, response.ServerError: i := newItem(m, w.now(), duration) + if w.wildcardFunc != nil { + i.wildcard = w.wildcardFunc() + } if w.ncache.Add(key, i) { evictions.WithLabelValues(w.server, Denial, w.zonesMetricLabel).Inc() } diff --git a/plugin/cache/cache_test.go b/plugin/cache/cache_test.go index 7f8c28e3fc3..851507a4d79 100644 --- a/plugin/cache/cache_test.go +++ b/plugin/cache/cache_test.go @@ -7,6 +7,7 @@ import ( "time" "github.com/coredns/coredns/plugin" + "github.com/coredns/coredns/plugin/metadata" "github.com/coredns/coredns/plugin/pkg/dnstest" "github.com/coredns/coredns/plugin/pkg/response" "github.com/coredns/coredns/plugin/test" @@ -217,7 +218,7 @@ func TestCache(t *testing.T) { } if ok { - resp := i.toMsg(m, time.Now().UTC(), state.Do()) + resp := i.toMsg(m, time.Now().UTC(), state.Do(), m.AuthenticatedData) if err := test.Header(tc.Case, resp); err != nil { t.Logf("Cache %v", resp) @@ -258,6 +259,23 @@ func TestCacheZeroTTL(t *testing.T) { } } +func TestCacheServfailTTL0(t *testing.T) { + c := New() + c.minpttl = minTTL + c.minnttl = minNTTL + c.failttl = 0 + c.Next = servFailBackend(0) + + req := new(dns.Msg) + req.SetQuestion("example.org.", dns.TypeA) + ctx := context.TODO() + + c.ServeDNS(ctx, &test.ResponseWriter{}, req) + if c.ncache.Len() != 0 { + t.Errorf("SERVFAIL response should not have been cached") + } +} + func TestServeFromStaleCache(t *testing.T) { c := New() c.Next = ttlBackend(60) @@ -561,3 +579,59 @@ func TestComputeTTL(t *testing.T) { } } } + +func TestCacheWildcardMetadata(t *testing.T) { + c := New() + qname := "foo.bar.example.org." + wildcard := "*.bar.example.org." + c.Next = wildcardMetadataBackend(qname, wildcard) + + req := new(dns.Msg) + req.SetQuestion(qname, dns.TypeA) + + // 1. Test writing wildcard metadata retrieved from backend to the cache + + ctx := metadata.ContextWithMetadata(context.TODO()) + w := dnstest.NewRecorder(&test.ResponseWriter{}) + c.ServeDNS(ctx, w, req) + if c.pcache.Len() != 1 { + t.Errorf("Msg should have been cached") + } + _, k := key(qname, w.Msg, response.NoError) + i, _ := c.pcache.Get(k) + if i.(*item).wildcard != wildcard { + t.Errorf("expected wildcard reponse to enter cache with cache item's wildcard = %q, got %q", wildcard, i.(*item).wildcard) + } + + // 2. Test retrieving the cached item from cache and writing its wildcard value to metadata + + // reset context and response writer + ctx = metadata.ContextWithMetadata(context.TODO()) + w = dnstest.NewRecorder(&test.ResponseWriter{}) + + c.ServeDNS(ctx, w, req) + f := metadata.ValueFunc(ctx, "zone/wildcard") + if f == nil { + t.Fatal("expected metadata func for wildcard response retrieved from cache, got nil") + } + if f() != wildcard { + t.Errorf("after retrieving wildcard item from cache, expected \"zone/wildcard\" metadata value to be %q, got %q", wildcard, i.(*item).wildcard) + } +} + +// wildcardMetadataBackend mocks a backend that reponds with a response for qname synthesized by wildcard +// and sets the zone/wildcard metadata value +func wildcardMetadataBackend(qname, wildcard string) plugin.Handler { + return plugin.HandlerFunc(func(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { + m := new(dns.Msg) + m.SetReply(r) + m.Response, m.RecursionAvailable = true, true + m.Answer = []dns.RR{test.A(qname + " 300 IN A 127.0.0.1")} + metadata.SetValueFunc(ctx, "zone/wildcard", func() string { + return wildcard + }) + w.WriteMsg(m) + + return dns.RcodeSuccess, nil + }) +} diff --git a/plugin/cache/handler.go b/plugin/cache/handler.go index d5112fc6910..69ec4928d59 100644 --- a/plugin/cache/handler.go +++ b/plugin/cache/handler.go @@ -6,6 +6,7 @@ import ( "time" "github.com/coredns/coredns/plugin" + "github.com/coredns/coredns/plugin/metadata" "github.com/coredns/coredns/plugin/metrics" "github.com/coredns/coredns/request" @@ -17,6 +18,7 @@ func (c *Cache) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) rc := r.Copy() // We potentially modify r, to prevent other plugins from seeing this (r is a pointer), copy r into rc. state := request.Request{W: w, Req: rc} do := state.Do() + ad := r.AuthenticatedData zone := plugin.Zones(c.Zones).Matches(state.Name()) if zone == "" { @@ -36,7 +38,7 @@ func (c *Cache) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) ttl := 0 i := c.getIgnoreTTL(now, state, server) if i == nil { - crr := &ResponseWriter{ResponseWriter: w, Cache: c, state: state, server: server, do: do} + crr := &ResponseWriter{ResponseWriter: w, Cache: c, state: state, server: server, do: do, ad: ad, wildcardFunc: wildcardFunc(ctx)} return c.doRefresh(ctx, state, crr) } ttl = i.ttl(now) @@ -62,12 +64,29 @@ func (c *Cache) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) cw := newPrefetchResponseWriter(server, state, c) go c.doPrefetch(ctx, state, cw, i, now) } - resp := i.toMsg(r, now, do) - w.WriteMsg(resp) + if i.wildcard != "" { + // Set wildcard source record name to metadata + metadata.SetValueFunc(ctx, "zone/wildcard", func() string { + return i.wildcard + }) + } + + resp := i.toMsg(r, now, do, ad) + w.WriteMsg(resp) return dns.RcodeSuccess, nil } +func wildcardFunc(ctx context.Context) func() string { + return func() string { + // Get wildcard source record name from metadata + if f := metadata.ValueFunc(ctx, "zone/wildcard"); f != nil { + return f() + } + return "" + } +} + func (c *Cache) doPrefetch(ctx context.Context, state request.Request, cw *ResponseWriter, i *item, now time.Time) { cachePrefetches.WithLabelValues(cw.server, c.zonesMetricLabel).Inc() c.doRefresh(ctx, state, cw) diff --git a/plugin/cache/item.go b/plugin/cache/item.go index 56d188b36d2..6b51a5badfc 100644 --- a/plugin/cache/item.go +++ b/plugin/cache/item.go @@ -19,6 +19,7 @@ type item struct { Answer []dns.RR Ns []dns.RR Extra []dns.RR + wildcard string origTTL uint32 stored time.Time @@ -64,7 +65,7 @@ func newItem(m *dns.Msg, now time.Time, d time.Duration) *item { // So we're forced to always set this to 1; regardless if the answer came from the cache or not. // On newer systems(e.g. ubuntu 16.04 with glib version 2.23), this issue is resolved. // So we may set this bit back to 0 in the future ? -func (i *item) toMsg(m *dns.Msg, now time.Time, do bool) *dns.Msg { +func (i *item) toMsg(m *dns.Msg, now time.Time, do bool, ad bool) *dns.Msg { m1 := new(dns.Msg) m1.SetReply(m) @@ -73,8 +74,10 @@ func (i *item) toMsg(m *dns.Msg, now time.Time, do bool) *dns.Msg { // just set it to true. m1.Authoritative = true m1.AuthenticatedData = i.AuthenticatedData - if !do { - m1.AuthenticatedData = false // when DNSSEC was not wanted, it can't be authenticated data. + if !do && !ad { + // When DNSSEC was not wanted, it can't be authenticated data. + // However, retain the AD bit if the requester set the AD bit, per RFC6840 5.7-5.8 + m1.AuthenticatedData = false } m1.RecursionAvailable = i.RecursionAvailable m1.Rcode = i.Rcode diff --git a/plugin/cache/setup.go b/plugin/cache/setup.go index e5258dc06f4..aa487105c28 100644 --- a/plugin/cache/setup.go +++ b/plugin/cache/setup.go @@ -188,6 +188,23 @@ func cacheParse(c *caddy.Controller) (*Cache, error) { } ca.verifyStale = mode == "verify" } + case "servfail": + args := c.RemainingArgs() + if len(args) != 1 { + return nil, c.ArgErr() + } + d, err := time.ParseDuration(args[0]) + if err != nil { + return nil, err + } + if d < 0 { + return nil, errors.New("invalid negative ttl for servfail") + } + if d > 5*time.Minute { + // RFC 2308 prohibits caching SERVFAIL longer than 5 minutes + return nil, errors.New("caching SERVFAIL responses over 5 minutes is not permitted") + } + ca.failttl = d default: return nil, c.ArgErr() } diff --git a/plugin/cache/setup_test.go b/plugin/cache/setup_test.go index 675147d1bf9..5e684c510ef 100644 --- a/plugin/cache/setup_test.go +++ b/plugin/cache/setup_test.go @@ -155,3 +155,40 @@ func TestServeStale(t *testing.T) { } } } + +func TestServfail(t *testing.T) { + tests := []struct { + input string + shouldErr bool + failttl time.Duration + }{ + {"servfail 1s", false, 1 * time.Second}, + {"servfail 5m", false, 5 * time.Minute}, + {"servfail 0s", false, 0}, + {"servfail 0", false, 0}, + // fails + {"servfail", true, minNTTL}, + {"servfail 6m", true, minNTTL}, + {"servfail 20", true, minNTTL}, + {"servfail -1s", true, minNTTL}, + {"servfail aa", true, minNTTL}, + {"servfail 1m invalid", true, minNTTL}, + } + for i, test := range tests { + c := caddy.NewTestController("dns", fmt.Sprintf("cache {\n%s\n}", test.input)) + ca, err := cacheParse(c) + if test.shouldErr && err == nil { + t.Errorf("Test %v: Expected error but found nil", i) + continue + } else if !test.shouldErr && err != nil { + t.Errorf("Test %v: Expected no error but found error: %v", i, err) + continue + } + if test.shouldErr && err != nil { + continue + } + if ca.failttl != test.failttl { + t.Errorf("Test %v: Expected stale %v but found: %v", i, test.failttl, ca.staleUpTo) + } + } +} diff --git a/plugin/chaos/setup.go b/plugin/chaos/setup.go index de3622fe56e..ce0eb7a6869 100644 --- a/plugin/chaos/setup.go +++ b/plugin/chaos/setup.go @@ -30,7 +30,7 @@ func parse(c *caddy.Controller) (string, []string, error) { chaosVersion = caddy.AppName + "-" + caddy.AppVersion version := "" - for c.Next() { + if c.Next() { args := c.RemainingArgs() if len(args) == 0 { return trim(chaosVersion), Owners, nil diff --git a/plugin/chaos/zowners.go b/plugin/chaos/zowners.go index f38c51c700a..419ca3cf5a8 100644 --- a/plugin/chaos/zowners.go +++ b/plugin/chaos/zowners.go @@ -1,4 +1,4 @@ package chaos // Owners are all GitHub handlers of all maintainers. -var Owners = []string{"bradbeam", "chrisohaver", "darshanime", "dilyevsky", "ekleiner", "fastest963", "greenpau", "ihac", "inigohu", "isolus", "johnbelamaric", "miekg", "mqasimsarfraz", "nchrisdk", "nitisht", "pmoroney", "rajansandeep", "rdrozhdzh", "rtreffer", "snebel29", "stp-ip", "superq", "varyoo", "ykhr53", "yongtang", "zouyee"} +var Owners = []string{"Tantalor93", "bradbeam", "chrisohaver", "darshanime", "dilyevsky", "ekleiner", "greenpau", "ihac", "inigohu", "isolus", "jameshartig", "johnbelamaric", "miekg", "mqasimsarfraz", "nchrisdk", "nitisht", "pmoroney", "rajansandeep", "rdrozhdzh", "rtreffer", "snebel29", "stp-ip", "superq", "varyoo", "ykhr53", "yongtang", "zouyee"} diff --git a/plugin/clouddns/clouddns.go b/plugin/clouddns/clouddns.go index 4556ecac68f..e09c247be3b 100644 --- a/plugin/clouddns/clouddns.go +++ b/plugin/clouddns/clouddns.go @@ -204,7 +204,6 @@ func (h *CloudDNS) updateZones(ctx context.Context) error { (*z[i]).z = newZ h.zMu.Unlock() } - }(zName, z) } // Collect errors (if any). This will also sync on all zones updates diff --git a/plugin/clouddns/clouddns_test.go b/plugin/clouddns/clouddns_test.go index f394f0593cb..e052bf2563b 100644 --- a/plugin/clouddns/clouddns_test.go +++ b/plugin/clouddns/clouddns_test.go @@ -152,7 +152,6 @@ func TestCloudDNS(t *testing.T) { m.Authoritative = true rcode = dns.RcodeSuccess - } m.SetRcode(r, rcode) diff --git a/plugin/clouddns/setup.go b/plugin/clouddns/setup.go index 507ae278e17..cfd7eecd6f3 100644 --- a/plugin/clouddns/setup.go +++ b/plugin/clouddns/setup.go @@ -81,16 +81,19 @@ func setup(c *caddy.Controller) error { ctx, cancel := context.WithCancel(context.Background()) client, err := f(ctx, opt) if err != nil { + cancel() return err } h, err := New(ctx, client, keys, up) if err != nil { + cancel() return plugin.Error("clouddns", c.Errf("failed to create plugin: %v", err)) } h.Fall = fall if err := h.Run(ctx); err != nil { + cancel() return plugin.Error("clouddns", c.Errf("failed to initialize plugin: %v", err)) } diff --git a/plugin/debug/pcap_test.go b/plugin/debug/pcap_test.go index b2796e39962..6b263c88392 100644 --- a/plugin/debug/pcap_test.go +++ b/plugin/debug/pcap_test.go @@ -32,7 +32,7 @@ func TestNoDebug(t *testing.T) { } } -func ExampleLogHexdump() { +func ExampleHexdump() { buf, _ := msg().Pack() h := hexdump(buf) fmt.Println(string(h)) diff --git a/plugin/dns64/dns64_test.go b/plugin/dns64/dns64_test.go index 54d63439aa6..a294721dc63 100644 --- a/plugin/dns64/dns64_test.go +++ b/plugin/dns64/dns64_test.go @@ -74,7 +74,6 @@ func TestRequestShouldIntercept(t *testing.T) { } func TestTo6(t *testing.T) { - v6, err := To6("64:ff9b::/96", "64.64.64.64") if err != nil { t.Error(err) @@ -216,7 +215,7 @@ func TestDNS64(t *testing.T) { RecursionDesired: true, Opcode: dns.OpcodeQuery, }, - Question: []dns.Question{{"example.com.", dns.TypeAAAA, dns.ClassINET}}, + Question: []dns.Question{{Name: "example.com.", Qtype: dns.TypeAAAA, Qclass: dns.ClassINET}}, }, initResp: &dns.Msg{ //success, no answers MsgHdr: dns.MsgHdr{ @@ -226,7 +225,7 @@ func TestDNS64(t *testing.T) { Rcode: dns.RcodeSuccess, Response: true, }, - Question: []dns.Question{{"example.com.", dns.TypeAAAA, dns.ClassINET}}, + Question: []dns.Question{{Name: "example.com.", Qtype: dns.TypeAAAA, Qclass: dns.ClassINET}}, Ns: []dns.RR{test.SOA("example.com. 70 IN SOA foo bar 1 1 1 1 1")}, }, aResp: &dns.Msg{ @@ -237,7 +236,7 @@ func TestDNS64(t *testing.T) { Rcode: dns.RcodeSuccess, Response: true, }, - Question: []dns.Question{{"example.com.", dns.TypeA, dns.ClassINET}}, + Question: []dns.Question{{Name: "example.com.", Qtype: dns.TypeA, Qclass: dns.ClassINET}}, Answer: []dns.RR{ test.A("example.com. 60 IN A 192.0.2.42"), test.A("example.com. 5000 IN A 192.0.2.43"), @@ -252,7 +251,7 @@ func TestDNS64(t *testing.T) { Rcode: dns.RcodeSuccess, Response: true, }, - Question: []dns.Question{{"example.com.", dns.TypeAAAA, dns.ClassINET}}, + Question: []dns.Question{{Name: "example.com.", Qtype: dns.TypeAAAA, Qclass: dns.ClassINET}}, Answer: []dns.RR{ test.AAAA("example.com. 60 IN AAAA 64:ff9b::192.0.2.42"), // override RR ttl to SOA ttl, since it's lower @@ -269,7 +268,7 @@ func TestDNS64(t *testing.T) { RecursionDesired: true, Opcode: dns.OpcodeQuery, }, - Question: []dns.Question{{"example.com.", dns.TypeAAAA, dns.ClassINET}}, + Question: []dns.Question{{Name: "example.com.", Qtype: dns.TypeAAAA, Qclass: dns.ClassINET}}, }, initResp: &dns.Msg{ //success, no answers MsgHdr: dns.MsgHdr{ @@ -279,7 +278,7 @@ func TestDNS64(t *testing.T) { Rcode: dns.RcodeSuccess, Response: true, }, - Question: []dns.Question{{"example.com.", dns.TypeAAAA, dns.ClassINET}}, + Question: []dns.Question{{Name: "example.com.", Qtype: dns.TypeAAAA, Qclass: dns.ClassINET}}, Ns: []dns.RR{test.SOA("example.com. 3600 IN SOA foo bar 1 7200 900 1209600 86400")}, }, aResp: &dns.Msg{ @@ -290,7 +289,7 @@ func TestDNS64(t *testing.T) { Rcode: dns.RcodeSuccess, Response: true, }, - Question: []dns.Question{{"example.com.", dns.TypeA, dns.ClassINET}}, + Question: []dns.Question{{Name: "example.com.", Qtype: dns.TypeA, Qclass: dns.ClassINET}}, Ns: []dns.RR{test.SOA("example.com. 3600 IN SOA foo bar 1 7200 900 1209600 86400")}, }, @@ -302,7 +301,7 @@ func TestDNS64(t *testing.T) { Rcode: dns.RcodeSuccess, Response: true, }, - Question: []dns.Question{{"example.com.", dns.TypeAAAA, dns.ClassINET}}, + Question: []dns.Question{{Name: "example.com.", Qtype: dns.TypeAAAA, Qclass: dns.ClassINET}}, Ns: []dns.RR{test.SOA("example.com. 3600 IN SOA foo bar 1 7200 900 1209600 86400")}, Answer: []dns.RR{}, // just to make comparison happy }, @@ -316,7 +315,7 @@ func TestDNS64(t *testing.T) { RecursionDesired: true, Opcode: dns.OpcodeQuery, }, - Question: []dns.Question{{"example.com.", dns.TypeAAAA, dns.ClassINET}}, + Question: []dns.Question{{Name: "example.com.", Qtype: dns.TypeAAAA, Qclass: dns.ClassINET}}, }, initResp: &dns.Msg{ // failure MsgHdr: dns.MsgHdr{ @@ -326,7 +325,7 @@ func TestDNS64(t *testing.T) { Rcode: dns.RcodeRefused, Response: true, }, - Question: []dns.Question{{"example.com.", dns.TypeAAAA, dns.ClassINET}}, + Question: []dns.Question{{Name: "example.com.", Qtype: dns.TypeAAAA, Qclass: dns.ClassINET}}, }, aResp: &dns.Msg{ MsgHdr: dns.MsgHdr{ @@ -336,7 +335,7 @@ func TestDNS64(t *testing.T) { Rcode: dns.RcodeSuccess, Response: true, }, - Question: []dns.Question{{"example.com.", dns.TypeA, dns.ClassINET}}, + Question: []dns.Question{{Name: "example.com.", Qtype: dns.TypeA, Qclass: dns.ClassINET}}, Answer: []dns.RR{ test.A("example.com. 60 IN A 192.0.2.42"), test.A("example.com. 5000 IN A 192.0.2.43"), @@ -351,7 +350,7 @@ func TestDNS64(t *testing.T) { Rcode: dns.RcodeSuccess, Response: true, }, - Question: []dns.Question{{"example.com.", dns.TypeAAAA, dns.ClassINET}}, + Question: []dns.Question{{Name: "example.com.", Qtype: dns.TypeAAAA, Qclass: dns.ClassINET}}, Answer: []dns.RR{ test.AAAA("example.com. 60 IN AAAA 64:ff9b::192.0.2.42"), test.AAAA("example.com. 600 IN AAAA 64:ff9b::192.0.2.43"), @@ -367,7 +366,7 @@ func TestDNS64(t *testing.T) { RecursionDesired: true, Opcode: dns.OpcodeQuery, }, - Question: []dns.Question{{"example.com.", dns.TypeAAAA, dns.ClassINET}}, + Question: []dns.Question{{Name: "example.com.", Qtype: dns.TypeAAAA, Qclass: dns.ClassINET}}, }, initResp: &dns.Msg{ // failure MsgHdr: dns.MsgHdr{ @@ -377,7 +376,7 @@ func TestDNS64(t *testing.T) { Rcode: dns.RcodeNameError, Response: true, }, - Question: []dns.Question{{"example.com.", dns.TypeAAAA, dns.ClassINET}}, + Question: []dns.Question{{Name: "example.com.", Qtype: dns.TypeAAAA, Qclass: dns.ClassINET}}, Ns: []dns.RR{test.SOA("example.com. 3600 IN SOA foo bar 1 7200 900 1209600 86400")}, }, resp: &dns.Msg{ @@ -388,7 +387,7 @@ func TestDNS64(t *testing.T) { Rcode: dns.RcodeNameError, Response: true, }, - Question: []dns.Question{{"example.com.", dns.TypeAAAA, dns.ClassINET}}, + Question: []dns.Question{{Name: "example.com.", Qtype: dns.TypeAAAA, Qclass: dns.ClassINET}}, Ns: []dns.RR{test.SOA("example.com. 3600 IN SOA foo bar 1 7200 900 1209600 86400")}, }, }, @@ -401,7 +400,7 @@ func TestDNS64(t *testing.T) { RecursionDesired: true, Opcode: dns.OpcodeQuery, }, - Question: []dns.Question{{"example.com.", dns.TypeAAAA, dns.ClassINET}}, + Question: []dns.Question{{Name: "example.com.", Qtype: dns.TypeAAAA, Qclass: dns.ClassINET}}, }, initResp: &dns.Msg{ @@ -412,7 +411,7 @@ func TestDNS64(t *testing.T) { Rcode: dns.RcodeSuccess, Response: true, }, - Question: []dns.Question{{"example.com.", dns.TypeAAAA, dns.ClassINET}}, + Question: []dns.Question{{Name: "example.com.", Qtype: dns.TypeAAAA, Qclass: dns.ClassINET}}, Answer: []dns.RR{ test.AAAA("example.com. 60 IN AAAA ::1"), test.AAAA("example.com. 5000 IN AAAA ::2"), @@ -427,7 +426,7 @@ func TestDNS64(t *testing.T) { Rcode: dns.RcodeSuccess, Response: true, }, - Question: []dns.Question{{"example.com.", dns.TypeAAAA, dns.ClassINET}}, + Question: []dns.Question{{Name: "example.com.", Qtype: dns.TypeAAAA, Qclass: dns.ClassINET}}, Answer: []dns.RR{ test.AAAA("example.com. 60 IN AAAA ::1"), test.AAAA("example.com. 5000 IN AAAA ::2"), @@ -443,7 +442,7 @@ func TestDNS64(t *testing.T) { RecursionDesired: true, Opcode: dns.OpcodeQuery, }, - Question: []dns.Question{{"example.com.", dns.TypeAAAA, dns.ClassINET}}, + Question: []dns.Question{{Name: "example.com.", Qtype: dns.TypeAAAA, Qclass: dns.ClassINET}}, }, initResp: &dns.Msg{ //success, no answers MsgHdr: dns.MsgHdr{ @@ -453,7 +452,7 @@ func TestDNS64(t *testing.T) { Rcode: dns.RcodeSuccess, Response: true, }, - Question: []dns.Question{{"example.com.", dns.TypeAAAA, dns.ClassINET}}, + Question: []dns.Question{{Name: "example.com.", Qtype: dns.TypeAAAA, Qclass: dns.ClassINET}}, Ns: []dns.RR{test.SOA("example.com. 70 IN SOA foo bar 1 1 1 1 1")}, }, aResp: &dns.Msg{ @@ -465,7 +464,7 @@ func TestDNS64(t *testing.T) { Rcode: dns.RcodeSuccess, Response: true, }, - Question: []dns.Question{{"example.com.", dns.TypeA, dns.ClassINET}}, + Question: []dns.Question{{Name: "example.com.", Qtype: dns.TypeA, Qclass: dns.ClassINET}}, Answer: []dns.RR{ test.A("example.com. 60 IN A 192.0.2.42"), test.A("example.com. 5000 IN A 192.0.2.43"), @@ -481,7 +480,7 @@ func TestDNS64(t *testing.T) { Rcode: dns.RcodeSuccess, Response: true, }, - Question: []dns.Question{{"example.com.", dns.TypeAAAA, dns.ClassINET}}, + Question: []dns.Question{{Name: "example.com.", Qtype: dns.TypeAAAA, Qclass: dns.ClassINET}}, Answer: []dns.RR{ test.AAAA("example.com. 60 IN AAAA 64:ff9b::192.0.2.42"), // override RR ttl to SOA ttl, since it's lower diff --git a/plugin/dns64/setup.go b/plugin/dns64/setup.go index 134a12a6501..5e061875f38 100644 --- a/plugin/dns64/setup.go +++ b/plugin/dns64/setup.go @@ -6,14 +6,11 @@ import ( "github.com/coredns/caddy" "github.com/coredns/coredns/core/dnsserver" "github.com/coredns/coredns/plugin" - clog "github.com/coredns/coredns/plugin/pkg/log" "github.com/coredns/coredns/plugin/pkg/upstream" ) const pluginName = "dns64" -var log = clog.NewWithPlugin(pluginName) - func init() { plugin.Register(pluginName, setup) } func setup(c *caddy.Controller) error { diff --git a/plugin/dnssec/cache.go b/plugin/dnssec/cache.go index 2acfe72a5cd..d80f5c1bbc5 100644 --- a/plugin/dnssec/cache.go +++ b/plugin/dnssec/cache.go @@ -43,7 +43,6 @@ func periodicClean(c *cache.Cache, stop <-chan struct{}) { case <-stop: return - } } } diff --git a/plugin/dnssec/setup.go b/plugin/dnssec/setup.go index b82e6764819..7820e93a744 100644 --- a/plugin/dnssec/setup.go +++ b/plugin/dnssec/setup.go @@ -58,7 +58,6 @@ func dnssecParse(c *caddy.Controller) ([]string, []*DNSKEY, int, bool, error) { zones = plugin.OriginsFromArgsOrServerBlock(c.RemainingArgs(), c.ServerBlockKeys) for c.NextBlock() { - switch x := c.Val(); x { case "key": k, e := keyParse(c) @@ -79,7 +78,6 @@ func dnssecParse(c *caddy.Controller) ([]string, []*DNSKEY, int, bool, error) { default: return nil, nil, 0, false, c.Errf("unknown property '%s'", x) } - } } // Check if we have both KSKs and ZSKs. diff --git a/plugin/dnstap/handler.go b/plugin/dnstap/handler.go index 1ae0e3c62d9..04d29860ec7 100644 --- a/plugin/dnstap/handler.go +++ b/plugin/dnstap/handler.go @@ -23,7 +23,7 @@ type Dnstap struct { // TapMessage sends the message m to the dnstap interface. func (h Dnstap) TapMessage(m *tap.Message) { t := tap.Dnstap_MESSAGE - h.io.Dnstap(tap.Dnstap{Type: &t, Message: m}) + h.io.Dnstap(&tap.Dnstap{Type: &t, Message: m}) } func (h Dnstap) tapQuery(w dns.ResponseWriter, query *dns.Msg, queryTime time.Time) { diff --git a/plugin/dnstap/handler_test.go b/plugin/dnstap/handler_test.go index 74f72521d32..2c54f70e68e 100644 --- a/plugin/dnstap/handler_test.go +++ b/plugin/dnstap/handler_test.go @@ -18,7 +18,6 @@ func testCase(t *testing.T, tapq, tapr *tap.Message, q, r *dns.Msg) { h := Dnstap{ Next: test.HandlerFunc(func(_ context.Context, w dns.ResponseWriter, _ *dns.Msg) (int, error) { - return 0, w.WriteMsg(r) }), io: &w, @@ -34,7 +33,7 @@ type writer struct { queue []*tap.Message } -func (w *writer) Dnstap(e tap.Dnstap) { +func (w *writer) Dnstap(e *tap.Dnstap) { if len(w.queue) == 0 { w.t.Error("Message not expected") } diff --git a/plugin/dnstap/io.go b/plugin/dnstap/io.go index 857d860af10..d15d5669bc4 100644 --- a/plugin/dnstap/io.go +++ b/plugin/dnstap/io.go @@ -18,7 +18,7 @@ const ( // tapper interface is used in testing to mock the Dnstap method. type tapper interface { - Dnstap(tap.Dnstap) + Dnstap(*tap.Dnstap) } // dio implements the Tapper interface. @@ -26,7 +26,7 @@ type dio struct { endpoint string proto string enc *encoder - queue chan tap.Dnstap + queue chan *tap.Dnstap dropped uint32 quit chan struct{} flushTimeout time.Duration @@ -38,7 +38,7 @@ func newIO(proto, endpoint string) *dio { return &dio{ endpoint: endpoint, proto: proto, - queue: make(chan tap.Dnstap, queueSize), + queue: make(chan *tap.Dnstap, queueSize), quit: make(chan struct{}), flushTimeout: flushTimeout, tcpTimeout: tcpTimeout, @@ -67,7 +67,7 @@ func (d *dio) connect() error { } // Dnstap enqueues the payload for log. -func (d *dio) Dnstap(payload tap.Dnstap) { +func (d *dio) Dnstap(payload *tap.Dnstap) { select { case d.queue <- payload: default: @@ -104,7 +104,7 @@ func (d *dio) serve() { d.enc.close() return case payload := <-d.queue: - if err := d.write(&payload); err != nil { + if err := d.write(payload); err != nil { d.dial() } case <-timeout.C: diff --git a/plugin/dnstap/io_test.go b/plugin/dnstap/io_test.go index 30f0c75fbb2..3e94f05569f 100644 --- a/plugin/dnstap/io_test.go +++ b/plugin/dnstap/io_test.go @@ -65,7 +65,7 @@ func TestTransport(t *testing.T) { dio.flushTimeout = 30 * time.Millisecond dio.connect() - dio.Dnstap(tmsg) + dio.Dnstap(&tmsg) wg.Wait() l.Close() @@ -99,7 +99,7 @@ func TestRace(t *testing.T) { for i := 0; i < count; i++ { go func() { tmsg := tap.Dnstap_MESSAGE - dio.Dnstap(tap.Dnstap{Type: &tmsg}) + dio.Dnstap(&tap.Dnstap{Type: &tmsg}) wg.Done() }() } @@ -128,7 +128,7 @@ func TestReconnect(t *testing.T) { dio.connect() defer dio.close() - dio.Dnstap(tmsg) + dio.Dnstap(&tmsg) wg.Wait() @@ -149,7 +149,7 @@ func TestReconnect(t *testing.T) { for i := 0; i < count; i++ { time.Sleep(100 * time.Millisecond) - dio.Dnstap(tmsg) + dio.Dnstap(&tmsg) } wg.Wait() } diff --git a/plugin/errors/README.md b/plugin/errors/README.md index 0cb0b375d45..27ba1058a83 100644 --- a/plugin/errors/README.md +++ b/plugin/errors/README.md @@ -22,10 +22,13 @@ Extra knobs are available with an expanded syntax: ~~~ errors { + stacktrace consolidate DURATION REGEXP [LEVEL] } ~~~ +Option `stacktrace` will log a stacktrace during panic recovery. + Option `consolidate` allows collecting several error messages matching the regular expression **REGEXP** during **DURATION**. After the **DURATION** since receiving the first such message, the consolidated message will be printed to standard output with log level, which is configurable by optional option **LEVEL**. Supported options for **LEVEL** option are `warning`,`error`,`info` and `debug`. ~~~ diff --git a/plugin/errors/setup.go b/plugin/errors/setup.go index 67a9fe6534d..c040e102f55 100644 --- a/plugin/errors/setup.go +++ b/plugin/errors/setup.go @@ -52,38 +52,41 @@ func errorsParse(c *caddy.Controller) (*errorHandler, error) { } for c.NextBlock() { - if err := parseBlock(c, handler); err != nil { - return nil, err + switch c.Val() { + case "stacktrace": + dnsserver.GetConfig(c).Stacktrace = true + case "consolidate": + pattern, err := parseConsolidate(c) + if err != nil { + return nil, err + } + handler.patterns = append(handler.patterns, pattern) + default: + return handler, c.SyntaxErr("Unknown field " + c.Val()) } } } return handler, nil } -func parseBlock(c *caddy.Controller, h *errorHandler) error { - if c.Val() != "consolidate" { - return c.SyntaxErr("consolidate") - } - +func parseConsolidate(c *caddy.Controller) (*pattern, error) { args := c.RemainingArgs() if len(args) < 2 || len(args) > 3 { - return c.ArgErr() + return nil, c.ArgErr() } p, err := time.ParseDuration(args[0]) if err != nil { - return c.Err(err.Error()) + return nil, c.Err(err.Error()) } re, err := regexp.Compile(args[1]) if err != nil { - return c.Err(err.Error()) + return nil, c.Err(err.Error()) } lc, err := parseLogLevel(c, args) if err != nil { - return err + return nil, err } - h.patterns = append(h.patterns, &pattern{period: p, pattern: re, logCallback: lc}) - - return nil + return &pattern{period: p, pattern: re, logCallback: lc}, nil } func parseLogLevel(c *caddy.Controller, args []string) (func(format string, v ...interface{}), error) { diff --git a/plugin/errors/setup_test.go b/plugin/errors/setup_test.go index 72fdbcd0a69..5dbc9eca601 100644 --- a/plugin/errors/setup_test.go +++ b/plugin/errors/setup_test.go @@ -7,6 +7,7 @@ import ( "testing" "github.com/coredns/caddy" + "github.com/coredns/coredns/core/dnsserver" clog "github.com/coredns/coredns/plugin/pkg/log" ) @@ -15,47 +16,55 @@ func TestErrorsParse(t *testing.T) { inputErrorsRules string shouldErr bool optCount int + stacktrace bool }{ - {`errors`, false, 0}, - {`errors stdout`, false, 0}, - {`errors errors.txt`, true, 0}, - {`errors visible`, true, 0}, - {`errors { log visible }`, true, 0}, + {`errors`, false, 0, false}, + {`errors stdout`, false, 0, false}, + {`errors errors.txt`, true, 0, false}, + {`errors visible`, true, 0, false}, + {`errors { log visible }`, true, 0, false}, {`errors - errors `, true, 0}, - {`errors a b`, true, 0}, + errors `, true, 0, false}, + {`errors a b`, true, 0, false}, {`errors { consolidate - }`, true, 0}, + }`, true, 0, false}, {`errors { consolidate 1m - }`, true, 0}, + }`, true, 0, false}, {`errors { consolidate 1m .* extra - }`, true, 0}, + }`, true, 0, false}, {`errors { consolidate abc .* - }`, true, 0}, + }`, true, 0, false}, {`errors { consolidate 1 .* - }`, true, 0}, + }`, true, 0, false}, {`errors { consolidate 1m ()) - }`, true, 0}, + }`, true, 0, false}, + {`errors { + stacktrace + }`, false, 0, true}, + {`errors { + stacktrace + consolidate 1m ^exact$ + }`, false, 1, true}, {`errors { consolidate 1m ^exact$ - }`, false, 1}, + }`, false, 1, false}, {`errors { consolidate 1m error - }`, false, 1}, + }`, false, 1, false}, {`errors { consolidate 1m "format error" - }`, false, 1}, + }`, false, 1, false}, {`errors { consolidate 1m error1 consolidate 5s error2 - }`, false, 2}, + }`, false, 2, false}, } for i, test := range tests { c := caddy.NewTestController("dns", test.inputErrorsRules) @@ -69,6 +78,10 @@ func TestErrorsParse(t *testing.T) { t.Errorf("Test %d: pattern count mismatch, expected %d, got %d", i, test.optCount, len(h.patterns)) } + if dnsserver.GetConfig(c).Stacktrace != test.stacktrace { + t.Errorf("Test %d: stacktrace, expected %t, got %t", + i, test.stacktrace, dnsserver.GetConfig(c).Stacktrace) + } } } diff --git a/plugin/etcd/msg/service.go b/plugin/etcd/msg/service.go index 4e049e10a10..759a862131e 100644 --- a/plugin/etcd/msg/service.go +++ b/plugin/etcd/msg/service.go @@ -154,7 +154,6 @@ func split255(s string) []string { } else { sx = append(sx, s[p:]) break - } p, i = p+255, i+255 } diff --git a/plugin/etcd/msg/type.go b/plugin/etcd/msg/type.go index ad09e74fb96..a300eac760b 100644 --- a/plugin/etcd/msg/type.go +++ b/plugin/etcd/msg/type.go @@ -15,11 +15,9 @@ import ( // // Note that a service can double/triple as a TXT record or MX record. func (s *Service) HostType() (what uint16, normalized net.IP) { - ip := net.ParseIP(s.Host) switch { - case ip == nil: if len(s.Text) == 0 { return dns.TypeCNAME, nil diff --git a/plugin/etcd/setup.go b/plugin/etcd/setup.go index 751d741e14d..bd81af513f4 100644 --- a/plugin/etcd/setup.go +++ b/plugin/etcd/setup.go @@ -40,7 +40,7 @@ func etcdParse(c *caddy.Controller) (*Etcd, error) { etc.Upstream = upstream.New() - for c.Next() { + if c.Next() { etc.Zones = plugin.OriginsFromArgsOrServerBlock(c.RemainingArgs(), c.ServerBlockKeys) for c.NextBlock() { switch c.Val() { diff --git a/plugin/file/closest.go b/plugin/file/closest.go index 64652af83ae..5059194e63a 100644 --- a/plugin/file/closest.go +++ b/plugin/file/closest.go @@ -8,7 +8,6 @@ import ( // ClosestEncloser returns the closest encloser for qname. func (z *Zone) ClosestEncloser(qname string) (*tree.Elem, bool) { - offset, end := dns.NextLabel(qname, 0) for !end { elem, _ := z.Tree.Search(qname) diff --git a/plugin/file/include_test.go b/plugin/file/include_test.go index fad91df5c15..490f05a30df 100644 --- a/plugin/file/include_test.go +++ b/plugin/file/include_test.go @@ -10,7 +10,6 @@ import ( // Make sure the external miekg/dns dependency is up to date func TestInclude(t *testing.T) { - name, rm, err := test.TempFile(".", "foo\tIN\tA\t127.0.0.1\n") if err != nil { t.Fatalf("Unable to create tmpfile %q: %s", name, err) diff --git a/plugin/file/lookup.go b/plugin/file/lookup.go index 08eed7d6126..3f6929939c7 100644 --- a/plugin/file/lookup.go +++ b/plugin/file/lookup.go @@ -6,6 +6,7 @@ import ( "github.com/coredns/coredns/core/dnsserver" "github.com/coredns/coredns/plugin/file/rrutil" "github.com/coredns/coredns/plugin/file/tree" + "github.com/coredns/coredns/plugin/metadata" "github.com/coredns/coredns/request" "github.com/miekg/dns" @@ -150,7 +151,6 @@ func (z *Zone) Lookup(ctx context.Context, state request.Request, qname string) // If we see NS records, it means the name as been delegated, and we should return the delegation. if nsrrs := elem.Type(dns.TypeNS); nsrrs != nil { - // If the query is specifically for DS and the qname matches the delegated name, we should // return the DS in the answer section and leave the rest empty, i.e. just continue the loop // and continue searching. @@ -178,7 +178,6 @@ func (z *Zone) Lookup(ctx context.Context, state request.Request, qname string) // Found entire name. if found && shot { - if rrs := elem.Type(dns.TypeCNAME); len(rrs) > 0 && qtype != dns.TypeCNAME { ctx = context.WithValue(ctx, dnsserver.LoopKey{}, loop+1) return z.externalLookup(ctx, state, elem, rrs) @@ -207,14 +206,16 @@ func (z *Zone) Lookup(ctx context.Context, state request.Request, qname string) } return rrs, ap.ns(do), additional, Success - } // Haven't found the original name. // Found wildcard. if wildElem != nil { - auth := ap.ns(do) + // set metadata value for the wildcard record that synthesized the result + metadata.SetValueFunc(ctx, "zone/wildcard", func() string { + return wildElem.Name() + }) if rrs := wildElem.TypeForWildcard(dns.TypeCNAME, qname); len(rrs) > 0 && qtype != dns.TypeCNAME { ctx = context.WithValue(ctx, dnsserver.LoopKey{}, loop+1) @@ -233,6 +234,7 @@ func (z *Zone) Lookup(ctx context.Context, state request.Request, qname string) return nil, ret, nil, NoData } + auth := ap.ns(do) if do { // An NSEC is needed to say no longer name exists under this wildcard. if deny, found := tr.Prev(qname); found { @@ -243,7 +245,6 @@ func (z *Zone) Lookup(ctx context.Context, state request.Request, qname string) sigs := wildElem.TypeForWildcard(dns.TypeRRSIG, qname) sigs = rrutil.SubTypeSignature(sigs, qtype) rrs = append(rrs, sigs...) - } return rrs, auth, nil, Success } @@ -285,7 +286,6 @@ func (z *Zone) Lookup(ctx context.Context, state request.Request, qname string) } } } - } Out: return nil, ret, nil, rcode @@ -320,7 +320,6 @@ func (a Apex) ns(do bool) []dns.RR { // externalLookup adds signatures and tries to resolve CNAMEs that point to external names. func (z *Zone) externalLookup(ctx context.Context, state request.Request, elem *tree.Elem, rrs []dns.RR) ([]dns.RR, []dns.RR, []dns.RR, Result) { - qtype := state.QType() do := state.Do() diff --git a/plugin/file/secondary.go b/plugin/file/secondary.go index 160d8e0132a..932916bb394 100644 --- a/plugin/file/secondary.go +++ b/plugin/file/secondary.go @@ -183,7 +183,6 @@ Restart: retryTicker.Stop() expireTicker.Stop() goto Restart - } } } @@ -192,7 +191,6 @@ Restart: func jitter(n int) time.Duration { r := rand.Intn(n) return time.Duration(r) * time.Millisecond - } // MaxSerialIncrement is the maximum difference between two serial numbers. If the difference between diff --git a/plugin/file/setup.go b/plugin/file/setup.go index b8985f2d27a..95e5d729a10 100644 --- a/plugin/file/setup.go +++ b/plugin/file/setup.go @@ -139,7 +139,6 @@ func fileParse(c *caddy.Controller) (Zones, error) { return Zones{}, plugin.Error("file", openErr) } log.Warningf("Failed to open %q: trying again in %s", openErr, reload) - } return Zones{Z: z, Names: names}, nil } diff --git a/plugin/file/tree/less_test.go b/plugin/file/tree/less_test.go index dfd702c5557..f2559deb032 100644 --- a/plugin/file/tree/less_test.go +++ b/plugin/file/tree/less_test.go @@ -75,7 +75,6 @@ Tests: } continue Tests } - } } } diff --git a/plugin/forward/README.md b/plugin/forward/README.md index 1ea335bf2f3..5cc85b4093a 100644 --- a/plugin/forward/README.md +++ b/plugin/forward/README.md @@ -19,8 +19,6 @@ is taken as a healthy upstream. The health check uses the same protocol as speci When *all* upstreams are down it assumes health checking as a mechanism has failed and will try to connect to a random upstream (which may or may not work). -This plugin can only be used once per Server Block. - ## Syntax In its most basic form, a simple forwarder uses this syntax: @@ -103,7 +101,7 @@ Also note the TLS config is "global" for the whole forwarding proxy if you need On each endpoint, the timeouts for communication are set as follows: -* The dial timeout by default is 30s, and can decrease automatically down to 100ms based on early results. +* The dial timeout by default is 30s, and can decrease automatically down to 1s based on early results. * The read timeout is static at 2s. ## Metadata @@ -141,6 +139,40 @@ example.org { } ~~~ +Send all requests within `lab.example.local.` to `10.20.0.1`, all requests within `example.local.` (and not in +`lab.example.local.`) to `10.0.0.1`, all others requests to the servers defined in `/etc/resolv.conf`, and +caches results. Note that a CoreDNS server configured with multiple _forward_ plugins in a server block will evaluate those +forward plugins in the order they are listed when serving a request. Therefore, subdomains should be +placed before parent domains otherwise subdomain requests will be forwarded to the parent domain's upstream. +Accordingly, in this example `lab.example.local` is before `example.local`, and `example.local` is before `.`. + +~~~ corefile +. { + cache + forward lab.example.local 10.20.0.1 + forward example.local 10.0.0.1 + forward . /etc/resolv.conf +} +~~~ + +The example above is almost equivalent to the following example, except that example below defines three separate plugin +chains (and thus 3 separate instances of _cache_). + +~~~ corefile +lab.example.local { + cache + forward . 10.20.0.1 +} +example.local { + cache + forward . 10.0.0.1 +} +. { + cache + forward . /etc/resolv.conf +} +~~~ + Load balance all requests between three resolvers, one of which has a IPv6 address. ~~~ corefile diff --git a/plugin/forward/forward.go b/plugin/forward/forward.go index e7fcaaad661..90ae1aef885 100644 --- a/plugin/forward/forward.go +++ b/plugin/forward/forward.go @@ -74,7 +74,6 @@ func (f *Forward) Name() string { return "forward" } // ServeDNS implements plugin.Handler. func (f *Forward) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { - state := request.Request{W: w, Req: r} if !f.match(state) { return plugin.NextOrFailure(f.Name(), f.Next, ctx, w, r) diff --git a/plugin/forward/metrics.go b/plugin/forward/metrics.go index 9519c3c9b51..f1f0c48d67e 100644 --- a/plugin/forward/metrics.go +++ b/plugin/forward/metrics.go @@ -40,12 +40,6 @@ var ( Name: "healthcheck_broken_total", Help: "Counter of the number of complete failures of the healthchecks.", }) - SocketGauge = promauto.NewGaugeVec(prometheus.GaugeOpts{ - Namespace: plugin.Namespace, - Subsystem: "forward", - Name: "sockets_open", - Help: "Gauge of open sockets per upstream.", - }, []string{"to"}) MaxConcurrentRejectCount = promauto.NewCounter(prometheus.CounterOpts{ Namespace: plugin.Namespace, Subsystem: "forward", diff --git a/plugin/forward/proxy_test.go b/plugin/forward/proxy_test.go index dc1f5fb1843..74a0b5c4b6c 100644 --- a/plugin/forward/proxy_test.go +++ b/plugin/forward/proxy_test.go @@ -23,7 +23,8 @@ func TestProxy(t *testing.T) { defer s.Close() c := caddy.NewTestController("dns", "forward . "+s.Addr) - f, err := parseForward(c) + fs, err := parseForward(c) + f := fs[0] if err != nil { t.Errorf("Failed to create forwarder: %s", err) } @@ -53,7 +54,8 @@ func TestProxyTLSFail(t *testing.T) { defer s.Close() c := caddy.NewTestController("dns", "forward . tls://"+s.Addr) - f, err := parseForward(c) + fs, err := parseForward(c) + f := fs[0] if err != nil { t.Errorf("Failed to create forwarder: %s", err) } diff --git a/plugin/forward/setup.go b/plugin/forward/setup.go index af814189879..0e317bf9f2d 100644 --- a/plugin/forward/setup.go +++ b/plugin/forward/setup.go @@ -19,34 +19,47 @@ import ( func init() { plugin.Register("forward", setup) } func setup(c *caddy.Controller) error { - f, err := parseForward(c) + fs, err := parseForward(c) if err != nil { return plugin.Error("forward", err) } - if f.Len() > max { - return plugin.Error("forward", fmt.Errorf("more than %d TOs configured: %d", max, f.Len())) - } + for i := range fs { + f := fs[i] + if f.Len() > max { + return plugin.Error("forward", fmt.Errorf("more than %d TOs configured: %d", max, f.Len())) + } - dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler { - f.Next = next - return f - }) + if i == len(fs)-1 { + // last forward: point next to next plugin + dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler { + f.Next = next + return f + }) + } else { + // middle forward: point next to next forward + nextForward := fs[i+1] + dnsserver.GetConfig(c).AddPlugin(func(plugin.Handler) plugin.Handler { + f.Next = nextForward + return f + }) + } - c.OnStartup(func() error { - return f.OnStartup() - }) - c.OnStartup(func() error { - if taph := dnsserver.GetConfig(c).Handler("dnstap"); taph != nil { - if tapPlugin, ok := taph.(dnstap.Dnstap); ok { - f.tapPlugin = &tapPlugin + c.OnStartup(func() error { + return f.OnStartup() + }) + c.OnStartup(func() error { + if taph := dnsserver.GetConfig(c).Handler("dnstap"); taph != nil { + if tapPlugin, ok := taph.(dnstap.Dnstap); ok { + f.tapPlugin = &tapPlugin + } } - } - return nil - }) + return nil + }) - c.OnShutdown(func() error { - return f.OnShutdown() - }) + c.OnShutdown(func() error { + return f.OnShutdown() + }) + } return nil } @@ -67,23 +80,16 @@ func (f *Forward) OnShutdown() error { return nil } -func parseForward(c *caddy.Controller) (*Forward, error) { - var ( - f *Forward - err error - i int - ) +func parseForward(c *caddy.Controller) ([]*Forward, error) { + var fs = []*Forward{} for c.Next() { - if i > 0 { - return nil, plugin.ErrOnce - } - i++ - f, err = parseStanza(c) + f, err := parseStanza(c) if err != nil { return nil, err } + fs = append(fs, f) } - return f, nil + return fs, nil } func parseStanza(c *caddy.Controller) (*Forward, error) { diff --git a/plugin/forward/setup_policy_test.go b/plugin/forward/setup_policy_test.go index 2786f9a7a23..13466d7a34d 100644 --- a/plugin/forward/setup_policy_test.go +++ b/plugin/forward/setup_policy_test.go @@ -24,7 +24,7 @@ func TestSetupPolicy(t *testing.T) { for i, test := range tests { c := caddy.NewTestController("dns", test.input) - f, err := parseForward(c) + fs, err := parseForward(c) if test.shouldErr && err == nil { t.Errorf("Test %d: expected error but found %s for input %s", i, err, test.input) @@ -40,8 +40,8 @@ func TestSetupPolicy(t *testing.T) { } } - if !test.shouldErr && f.p.String() != test.expectedPolicy { - t.Errorf("Test %d: expected: %s, got: %s", i, test.expectedPolicy, f.p.String()) + if !test.shouldErr && (len(fs) == 0 || fs[0].p.String() != test.expectedPolicy) { + t.Errorf("Test %d: expected: %s, got: %s", i, test.expectedPolicy, fs[0].p.String()) } } } diff --git a/plugin/forward/setup_test.go b/plugin/forward/setup_test.go index 2a5257e98fa..829e1f745ec 100644 --- a/plugin/forward/setup_test.go +++ b/plugin/forward/setup_test.go @@ -7,6 +7,7 @@ import ( "testing" "github.com/coredns/caddy" + "github.com/coredns/coredns/core/dnsserver" ) func TestSetup(t *testing.T) { @@ -33,19 +34,19 @@ func TestSetup(t *testing.T) { {"forward . [2003::1]:53", false, ".", nil, 2, options{hcRecursionDesired: true, hcDomain: "."}, ""}, {"forward . 127.0.0.1 \n", false, ".", nil, 2, options{hcRecursionDesired: true, hcDomain: "."}, ""}, {"forward 10.9.3.0/18 127.0.0.1", false, "0.9.10.in-addr.arpa.", nil, 2, options{hcRecursionDesired: true, hcDomain: "."}, ""}, + {`forward . ::1 + forward com ::2`, false, ".", nil, 2, options{hcRecursionDesired: true, hcDomain: "."}, "plugin"}, // negative {"forward . a27.0.0.1", true, "", nil, 0, options{hcRecursionDesired: true, hcDomain: "."}, "not an IP"}, {"forward . 127.0.0.1 {\nblaatl\n}\n", true, "", nil, 0, options{hcRecursionDesired: true, hcDomain: "."}, "unknown property"}, {"forward . 127.0.0.1 {\nhealth_check 0.5s domain\n}\n", true, "", nil, 0, options{hcRecursionDesired: true, hcDomain: "."}, "Wrong argument count or unexpected line ending after 'domain'"}, - {`forward . ::1 - forward com ::2`, true, "", nil, 0, options{hcRecursionDesired: true, hcDomain: "."}, "plugin"}, {"forward . https://127.0.0.1 \n", true, ".", nil, 2, options{hcRecursionDesired: true, hcDomain: "."}, "'https' is not supported as a destination protocol in forward: https://127.0.0.1"}, {"forward xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 127.0.0.1 \n", true, ".", nil, 2, options{hcRecursionDesired: true, hcDomain: "."}, "unable to normalize 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'"}, } for i, test := range tests { c := caddy.NewTestController("dns", test.input) - f, err := parseForward(c) + fs, err := parseForward(c) if test.shouldErr && err == nil { t.Errorf("Test %d: expected error but found %s for input %s", i, err, test.input) @@ -61,19 +62,22 @@ func TestSetup(t *testing.T) { } } - if !test.shouldErr && f.from != test.expectedFrom { - t.Errorf("Test %d: expected: %s, got: %s", i, test.expectedFrom, f.from) - } - if !test.shouldErr && test.expectedIgnored != nil { - if !reflect.DeepEqual(f.ignored, test.expectedIgnored) { - t.Errorf("Test %d: expected: %q, actual: %q", i, test.expectedIgnored, f.ignored) + if !test.shouldErr { + f := fs[0] + if f.from != test.expectedFrom { + t.Errorf("Test %d: expected: %s, got: %s", i, test.expectedFrom, f.from) + } + if test.expectedIgnored != nil { + if !reflect.DeepEqual(f.ignored, test.expectedIgnored) { + t.Errorf("Test %d: expected: %q, actual: %q", i, test.expectedIgnored, f.ignored) + } + } + if f.maxfails != test.expectedFails { + t.Errorf("Test %d: expected: %d, got: %d", i, test.expectedFails, f.maxfails) + } + if f.opts != test.expectedOpts { + t.Errorf("Test %d: expected: %v, got: %v", i, test.expectedOpts, f.opts) } - } - if !test.shouldErr && f.maxfails != test.expectedFails { - t.Errorf("Test %d: expected: %d, got: %d", i, test.expectedFails, f.maxfails) - } - if !test.shouldErr && f.opts != test.expectedOpts { - t.Errorf("Test %d: expected: %v, got: %v", i, test.expectedOpts, f.opts) } } } @@ -100,7 +104,8 @@ func TestSetupTLS(t *testing.T) { for i, test := range tests { c := caddy.NewTestController("dns", test.input) - f, err := parseForward(c) + fs, err := parseForward(c) + f := fs[0] if test.shouldErr && err == nil { t.Errorf("Test %d: expected error but found %s for input %s", i, err, test.input) @@ -149,7 +154,7 @@ nameserver 10.10.255.253`), 0666); err != nil { for i, test := range tests { c := caddy.NewTestController("dns", test.input) - f, err := parseForward(c) + fs, err := parseForward(c) if test.shouldErr && err == nil { t.Errorf("Test %d: expected error but found %s for input %s", i, err, test.input) @@ -166,17 +171,18 @@ nameserver 10.10.255.253`), 0666); err != nil { } } - if !test.shouldErr { - for j, n := range test.expectedNames { - addr := f.proxies[j].addr - if n != addr { - t.Errorf("Test %d, expected %q, got %q", j, n, addr) - } - } - } if test.shouldErr { continue } + + f := fs[0] + for j, n := range test.expectedNames { + addr := f.proxies[j].addr + if n != addr { + t.Errorf("Test %d, expected %q, got %q", j, n, addr) + } + } + for _, p := range f.proxies { p.health.Check(p) // this should almost always err, we don't care it shouldn't crash } @@ -199,7 +205,7 @@ func TestSetupMaxConcurrent(t *testing.T) { for i, test := range tests { c := caddy.NewTestController("dns", test.input) - f, err := parseForward(c) + fs, err := parseForward(c) if test.shouldErr && err == nil { t.Errorf("Test %d: expected error but found %s for input %s", i, err, test.input) @@ -215,7 +221,11 @@ func TestSetupMaxConcurrent(t *testing.T) { } } - if !test.shouldErr && f.maxConcurrent != test.expectedVal { + if test.shouldErr { + continue + } + f := fs[0] + if f.maxConcurrent != test.expectedVal { t.Errorf("Test %d: expected: %d, got: %d", i, test.expectedVal, f.maxConcurrent) } } @@ -244,7 +254,7 @@ func TestSetupHealthCheck(t *testing.T) { for i, test := range tests { c := caddy.NewTestController("dns", test.input) - f, err := parseForward(c) + fs, err := parseForward(c) if test.shouldErr && err == nil { t.Errorf("Test %d: expected error but found %s for input %s", i, err, test.input) @@ -258,9 +268,62 @@ func TestSetupHealthCheck(t *testing.T) { t.Errorf("Test %d: expected error to contain: %v, found error: %v, input: %s", i, test.expectedErr, err, test.input) } } - if !test.shouldErr && (f.opts.hcRecursionDesired != test.expectedRecVal || f.proxies[0].health.GetRecursionDesired() != test.expectedRecVal || - f.opts.hcDomain != test.expectedDomain || f.proxies[0].health.GetDomain() != test.expectedDomain) { + + if test.shouldErr { + continue + } + + f := fs[0] + if f.opts.hcRecursionDesired != test.expectedRecVal || f.proxies[0].health.GetRecursionDesired() != test.expectedRecVal || + f.opts.hcDomain != test.expectedDomain || f.proxies[0].health.GetDomain() != test.expectedDomain { t.Errorf("Test %d: expectedRec: %v, got: %v. expectedDomain: %s, got: %s. ", i, test.expectedRecVal, f.opts.hcRecursionDesired, test.expectedDomain, f.opts.hcDomain) } } } + +func TestMultiForward(t *testing.T) { + input := ` + forward 1st.example.org 10.0.0.1 + forward 2nd.example.org 10.0.0.2 + forward 3rd.example.org 10.0.0.3 + ` + + c := caddy.NewTestController("dns", input) + setup(c) + dnsserver.NewServer("", []*dnsserver.Config{dnsserver.GetConfig(c)}) + + handlers := dnsserver.GetConfig(c).Handlers() + f1, ok := handlers[0].(*Forward) + if !ok { + t.Fatalf("expected first plugin to be Forward, got %v", reflect.TypeOf(f1.Next)) + } + + if f1.from != "1st.example.org." { + t.Errorf("expected first forward from \"1st.example.org.\", got %q", f1.from) + } + if f1.Next == nil { + t.Fatal("expected first forward to point to next forward instance, not nil") + } + + f2, ok := f1.Next.(*Forward) + if !ok { + t.Fatalf("expected second plugin to be Forward, got %v", reflect.TypeOf(f1.Next)) + } + if f2.from != "2nd.example.org." { + t.Errorf("expected second forward from \"2nd.example.org.\", got %q", f2.from) + } + if f2.Next == nil { + t.Fatal("expected second forward to point to third forward instance, got nil") + } + + f3, ok := f2.Next.(*Forward) + if !ok { + t.Fatalf("expected third plugin to be Forward, got %v", reflect.TypeOf(f2.Next)) + } + if f3.from != "3rd.example.org." { + t.Errorf("expected third forward from \"3rd.example.org.\", got %q", f3.from) + } + if f3.Next != nil { + t.Error("expected third plugin to be last, but Next is not nil") + } +} diff --git a/plugin/geoip/geoip_test.go b/plugin/geoip/geoip_test.go index eb5c04c2aae..b11fc8b574c 100644 --- a/plugin/geoip/geoip_test.go +++ b/plugin/geoip/geoip_test.go @@ -14,7 +14,6 @@ import ( ) func TestMetadata(t *testing.T) { - tests := []struct { label string expectedValue string @@ -37,7 +36,6 @@ func TestMetadata(t *testing.T) { knownIPAddr := "81.2.69.142" // This IP should be be part of the CDIR address range used to create the database fixtures. for _, tc := range tests { - t.Run(fmt.Sprintf("%s/%s", tc.label, "direct"), func(t *testing.T) { geoIP, err := newGeoIP(cityDBPath, false) if err != nil { @@ -89,5 +87,4 @@ func testMetadata(t *testing.T, state request.Request, geoIP *GeoIP, label, expe t.Errorf("expected value for label %q should be %q, got %q instead", label, expectedValue, value) } - } diff --git a/plugin/grpc/proxy.go b/plugin/grpc/proxy.go index f2bee95c0ea..9a96e9504ce 100644 --- a/plugin/grpc/proxy.go +++ b/plugin/grpc/proxy.go @@ -12,6 +12,7 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/credentials" + "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/status" ) @@ -33,7 +34,7 @@ func newProxy(addr string, tlsConfig *tls.Config) (*Proxy, error) { if tlsConfig != nil { p.dialOpts = append(p.dialOpts, grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig))) } else { - p.dialOpts = append(p.dialOpts, grpc.WithInsecure()) + p.dialOpts = append(p.dialOpts, grpc.WithTransportCredentials(insecure.NewCredentials())) } conn, err := grpc.Dial(p.addr, p.dialOpts...) diff --git a/plugin/grpc/setup.go b/plugin/grpc/setup.go index 1325438f6a1..48a3d2c3199 100644 --- a/plugin/grpc/setup.go +++ b/plugin/grpc/setup.go @@ -96,7 +96,6 @@ func parseStanza(c *caddy.Controller) (*GRPC, error) { } func parseBlock(c *caddy.Controller, g *GRPC) error { - switch c.Val() { case "except": ignore := c.RemainingArgs() diff --git a/plugin/header/header_test.go b/plugin/header/header_test.go index 396e26d07c2..bd80e86358f 100644 --- a/plugin/header/header_test.go +++ b/plugin/header/header_test.go @@ -78,7 +78,5 @@ func TestHeader(t *testing.T) { t.Errorf("Test %d: Expected flag state=%t, but got %t", i, test.expected, test.got(m)) continue } - } - } diff --git a/plugin/header/setup.go b/plugin/header/setup.go index dbd4f6ed5b0..f849095634f 100644 --- a/plugin/header/setup.go +++ b/plugin/header/setup.go @@ -46,5 +46,4 @@ func parse(c *caddy.Controller) ([]Rule, error) { } } return nil, c.ArgErr() - } diff --git a/plugin/health/README.md b/plugin/health/README.md index c8fda61a431..b18d2ec3cb4 100644 --- a/plugin/health/README.md +++ b/plugin/health/README.md @@ -48,13 +48,13 @@ Doing this is supported but both endpoints ":8080" and ":8081" will export the e ## Metrics -If monitoring is enabled (via the *prometheus* plugin) then the following metric is exported: +If monitoring is enabled (via the *prometheus* plugin) then the following metrics are exported: - * `coredns_health_request_duration_seconds{}` - duration to process a HTTP query to the local - `/health` endpoint. As this a local operation it should be fast. A (large) increase in this + * `coredns_health_request_duration_seconds{}` - The *health* plugin performs a self health check + once per second on the `/health` endpoint. This metric is the duration to process that request. + As this is a local operation it should be fast. A (large) increase in this duration indicates the CoreDNS process is having trouble keeping up with its query load. - * `coredns_health_request_failures_total{}` - The number of times the internal health check loop - failed to query `/health`. + * `coredns_health_request_failures_total{}` - The number of times the self health check failed. Note that these metrics *do not* have a `server` label, because being overloaded is a symptom of the running process, *not* a specific server. diff --git a/plugin/health/health.go b/plugin/health/health.go index a71c00c8cbd..c69b221ece2 100644 --- a/plugin/health/health.go +++ b/plugin/health/health.go @@ -70,3 +70,15 @@ func (h *health) OnFinalShutdown() error { h.nlSetup = false return nil } + +func (h *health) OnReload() error { + if !h.nlSetup { + return nil + } + + h.stop() + + h.ln.Close() + h.nlSetup = false + return nil +} diff --git a/plugin/health/overloaded.go b/plugin/health/overloaded.go index 482b8a28690..57b9ca2d00f 100644 --- a/plugin/health/overloaded.go +++ b/plugin/health/overloaded.go @@ -2,6 +2,7 @@ package health import ( "context" + "net" "net/http" "time" @@ -13,9 +14,22 @@ import ( // overloaded queries the health end point and updates a metrics showing how long it took. func (h *health) overloaded(ctx context.Context) { + bypassProxy := &http.Transport{ + Proxy: nil, + DialContext: (&net.Dialer{ + Timeout: 30 * time.Second, + KeepAlive: 30 * time.Second, + }).DialContext, + ForceAttemptHTTP2: true, + MaxIdleConns: 100, + IdleConnTimeout: 90 * time.Second, + TLSHandshakeTimeout: 10 * time.Second, + ExpectContinueTimeout: 1 * time.Second, + } timeout := 3 * time.Second client := http.Client{ - Timeout: timeout, + Timeout: timeout, + Transport: bypassProxy, } url := "http://" + h.Addr + "/health" diff --git a/plugin/health/setup.go b/plugin/health/setup.go index 624d88dd507..e9163ad4417 100644 --- a/plugin/health/setup.go +++ b/plugin/health/setup.go @@ -20,7 +20,7 @@ func setup(c *caddy.Controller) error { h := &health{Addr: addr, lameduck: lame} c.OnStartup(h.OnStartup) - c.OnRestart(h.OnFinalShutdown) + c.OnRestart(h.OnReload) c.OnFinalShutdown(h.OnFinalShutdown) c.OnRestartFailed(h.OnStartup) diff --git a/plugin/k8s_external/README.md b/plugin/k8s_external/README.md index c7df91abc5a..7d75c360d27 100644 --- a/plugin/k8s_external/README.md +++ b/plugin/k8s_external/README.md @@ -10,7 +10,7 @@ This plugin allows an additional zone to resolve the external IP address(es) of service. This plugin is only useful if the *kubernetes* plugin is also loaded. The plugin uses an external zone to resolve in-cluster IP addresses. It only handles queries for A, -AAAA and SRV records; all others result in NODATA responses. To make it a proper DNS zone, it handles +AAAA, SRV, and PTR records; all others result in NODATA responses. To make it a proper DNS zone, it handles SOA and NS queries for the apex of the zone. By default the apex of the zone will look like the following (assuming the zone used is `example.org`): @@ -101,6 +101,3 @@ zone transfers. Notifies are not supported. For some background see [resolve external IP address](https://github.com/kubernetes/dns/issues/242). And [A records for services with Load Balancer IP](https://github.com/coredns/coredns/issues/1851). -# Bugs - -PTR queries for the reverse zone is not supported. diff --git a/plugin/k8s_external/external.go b/plugin/k8s_external/external.go index 1096c2ea7d9..bbb844be7a6 100644 --- a/plugin/k8s_external/external.go +++ b/plugin/k8s_external/external.go @@ -105,6 +105,8 @@ func (e *External) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Ms m.Answer, m.Truncated = e.aaaa(ctx, svc, state) case dns.TypeSRV: m.Answer, m.Extra = e.srv(ctx, svc, state) + case dns.TypePTR: + m.Answer = e.ptr(svc, state) default: m.Ns = []dns.RR{e.soa(state)} } diff --git a/plugin/k8s_external/external_test.go b/plugin/k8s_external/external_test.go index 558c894f6aa..22ef08cc1e1 100644 --- a/plugin/k8s_external/external_test.go +++ b/plugin/k8s_external/external_test.go @@ -20,7 +20,7 @@ func TestExternal(t *testing.T) { k.APIConn = &external{} e := New() - e.Zones = []string{"example.com."} + e.Zones = []string{"example.com.", "in-addr.arpa."} e.Next = test.NextHandler(dns.RcodeSuccess, nil) e.externalFunc = k.External e.externalAddrFunc = externalAddress // internal test function @@ -49,12 +49,33 @@ func TestExternal(t *testing.T) { t.Error("Expected authoritative answer") } if err = test.SortAndCheck(resp, tc); err != nil { - t.Error(err) + t.Errorf("Test %d: %v", i, err) } } } var tests = []test.Case{ + // PTR reverse lookup + { + Qname: "4.3.2.1.in-addr.arpa.", Qtype: dns.TypePTR, Rcode: dns.RcodeSuccess, + Answer: []dns.RR{ + test.PTR("4.3.2.1.in-addr.arpa. 5 IN PTR svc1.testns.example.com."), + }, + }, + // Bad PTR reverse lookup using existing service name + { + Qname: "svc1.testns.example.com.", Qtype: dns.TypePTR, Rcode: dns.RcodeSuccess, + Ns: []dns.RR{ + test.SOA("example.com. 5 IN SOA ns1.dns.example.com. hostmaster.example.com. 1499347823 7200 1800 86400 5"), + }, + }, + // Bad PTR reverse lookup using non-existing service name + { + Qname: "not-existing.testns.example.com.", Qtype: dns.TypePTR, Rcode: dns.RcodeNameError, + Ns: []dns.RR{ + test.SOA("example.com. 5 IN SOA ns1.dns.example.com. hostmaster.example.com. 1499347823 7200 1800 86400 5"), + }, + }, // A Service { Qname: "svc1.testns.example.com.", Qtype: dns.TypeA, Rcode: dns.RcodeSuccess, @@ -155,7 +176,7 @@ var tests = []test.Case{ { Qname: "svc11.testns.example.com.", Qtype: dns.TypeA, Rcode: dns.RcodeSuccess, Answer: []dns.RR{ - test.A("svc11.testns.example.com. 5 IN A 1.2.3.4"), + test.A("svc11.testns.example.com. 5 IN A 2.3.4.5"), }, }, { @@ -164,7 +185,7 @@ var tests = []test.Case{ test.SRV("_http._tcp.svc11.testns.example.com. 5 IN SRV 0 100 80 svc11.testns.example.com."), }, Extra: []dns.RR{ - test.A("svc11.testns.example.com. 5 IN A 1.2.3.4"), + test.A("svc11.testns.example.com. 5 IN A 2.3.4.5"), }, }, { @@ -173,7 +194,7 @@ var tests = []test.Case{ test.SRV("svc11.testns.example.com. 5 IN SRV 0 100 80 svc11.testns.example.com."), }, Extra: []dns.RR{ - test.A("svc11.testns.example.com. 5 IN A 1.2.3.4"), + test.A("svc11.testns.example.com. 5 IN A 2.3.4.5"), }, }, // svc12 @@ -211,6 +232,20 @@ func (external) GetNodeByName(ctx context.Context, name string) (*api.Node, erro func (external) SvcIndex(s string) []*object.Service { return svcIndexExternal[s] } func (external) PodIndex(string) []*object.Pod { return nil } +func (external) SvcExtIndexReverse(ip string) (result []*object.Service) { + for _, svcs := range svcIndexExternal { + for _, svc := range svcs { + for _, exIp := range svc.ExternalIPs { + if exIp != ip { + continue + } + result = append(result, svc) + } + } + } + return result +} + func (external) GetNamespaceByName(name string) (*object.Namespace, error) { return &object.Namespace{ Name: name, @@ -243,7 +278,7 @@ var svcIndexExternal = map[string][]*object.Service{ Name: "svc11", Namespace: "testns", Type: api.ServiceTypeLoadBalancer, - ExternalIPs: []string{"1.2.3.4"}, + ExternalIPs: []string{"2.3.4.5"}, Ports: []api.ServicePort{{Name: "http", Protocol: "tcp", Port: 80}}, }, }, diff --git a/plugin/k8s_external/msg_to_dns.go b/plugin/k8s_external/msg_to_dns.go index 540c8f44234..6975718b9e7 100644 --- a/plugin/k8s_external/msg_to_dns.go +++ b/plugin/k8s_external/msg_to_dns.go @@ -5,6 +5,7 @@ import ( "math" "github.com/coredns/coredns/plugin/etcd/msg" + "github.com/coredns/coredns/plugin/pkg/dnsutil" "github.com/coredns/coredns/request" "github.com/miekg/dns" @@ -14,7 +15,6 @@ func (e *External) a(ctx context.Context, services []msg.Service, state request. dup := make(map[string]struct{}) for _, s := range services { - what, ip := s.HostType() switch what { @@ -47,7 +47,6 @@ func (e *External) aaaa(ctx context.Context, services []msg.Service, state reque dup := make(map[string]struct{}) for _, s := range services { - what, ip := s.HostType() switch what { @@ -76,6 +75,19 @@ func (e *External) aaaa(ctx context.Context, services []msg.Service, state reque return records, truncated } +func (e *External) ptr(services []msg.Service, state request.Request) (records []dns.RR) { + dup := make(map[string]struct{}) + for _, s := range services { + if _, ok := dup[s.Host]; !ok { + dup[s.Host] = struct{}{} + rr := s.NewPTR(state.QName(), dnsutil.Join(s.Host, e.Zones[0])) + rr.Hdr.Ttl = e.ttl + records = append(records, rr) + } + } + return records +} + func (e *External) srv(ctx context.Context, services []msg.Service, state request.Request) (records, extra []dns.RR) { dup := make(map[item]struct{}) @@ -113,7 +125,6 @@ func (e *External) srv(ctx context.Context, services []msg.Service, state reques what, ip := s.HostType() switch what { - case dns.TypeCNAME: addr := dns.Fqdn(s.Host) srv := s.NewSRV(state.QName(), weight) diff --git a/plugin/k8s_external/transfer_test.go b/plugin/k8s_external/transfer_test.go index c55f14c1faf..7e90803f39c 100644 --- a/plugin/k8s_external/transfer_test.go +++ b/plugin/k8s_external/transfer_test.go @@ -59,6 +59,11 @@ func TestTransferAXFR(t *testing.T) { if ans.Header().Rrtype == dns.TypeTXT { continue } + + // Exclude PTR records + if ans.Header().Rrtype == dns.TypePTR { + continue + } expect = append(expect, ans) } } @@ -78,7 +83,6 @@ func TestTransferAXFR(t *testing.T) { t.Errorf("%+v", rec) } } - } func TestTransferIXFR(t *testing.T) { @@ -122,7 +126,6 @@ func TestTransferIXFR(t *testing.T) { t.Errorf("%+v", rec) } } - } // difference shows what we're missing when comparing two RR slices diff --git a/plugin/kubernetes/controller.go b/plugin/kubernetes/controller.go index f2c349dba19..a34b641436e 100644 --- a/plugin/kubernetes/controller.go +++ b/plugin/kubernetes/controller.go @@ -25,6 +25,7 @@ const ( podIPIndex = "PodIP" svcNameNamespaceIndex = "ServiceNameNamespace" svcIPIndex = "ServiceIP" + svcExtIPIndex = "ServiceExternalIP" epNameNamespaceIndex = "EndpointNameNamespace" epIPIndex = "EndpointsIP" ) @@ -34,6 +35,7 @@ type dnsController interface { EndpointsList() []*object.Endpoints SvcIndex(string) []*object.Service SvcIndexReverse(string) []*object.Service + SvcExtIndexReverse(string) []*object.Service PodIndex(string) []*object.Pod EpIndex(string) []*object.Endpoints EpIndexReverse(string) []*object.Endpoints @@ -122,7 +124,7 @@ func newdnsController(ctx context.Context, kubeClient kubernetes.Interface, opts }, &api.Service{}, cache.ResourceEventHandlerFuncs{AddFunc: dns.Add, UpdateFunc: dns.Update, DeleteFunc: dns.Delete}, - cache.Indexers{svcNameNamespaceIndex: svcNameNamespaceIndexFunc, svcIPIndex: svcIPIndexFunc}, + cache.Indexers{svcNameNamespaceIndex: svcNameNamespaceIndexFunc, svcIPIndex: svcIPIndexFunc, svcExtIPIndex: svcExtIPIndexFunc}, object.DefaultProcessor(object.ToService, nil), ) @@ -232,12 +234,18 @@ func svcIPIndexFunc(obj interface{}) ([]string, error) { if !ok { return nil, errObj } - idx := make([]string, len(svc.ClusterIPs)+len(svc.ExternalIPs)) + idx := make([]string, len(svc.ClusterIPs)) copy(idx, svc.ClusterIPs) - if len(svc.ExternalIPs) == 0 { - return idx, nil + return idx, nil +} + +func svcExtIPIndexFunc(obj interface{}) ([]string, error) { + svc, ok := obj.(*object.Service) + if !ok { + return nil, errObj } - copy(idx[len(svc.ClusterIPs):], svc.ExternalIPs) + idx := make([]string, len(svc.ExternalIPs)) + copy(idx, svc.ExternalIPs) return idx, nil } @@ -502,6 +510,22 @@ func (dns *dnsControl) SvcIndexReverse(ip string) (svcs []*object.Service) { return svcs } +func (dns *dnsControl) SvcExtIndexReverse(ip string) (svcs []*object.Service) { + os, err := dns.svcLister.ByIndex(svcExtIPIndex, ip) + if err != nil { + return nil + } + + for _, o := range os { + s, ok := o.(*object.Service) + if !ok { + continue + } + svcs = append(svcs, s) + } + return svcs +} + func (dns *dnsControl) EpIndex(idx string) (ep []*object.Endpoints) { dns.epLock.RLock() defer dns.epLock.RUnlock() @@ -731,5 +755,3 @@ func (dns *dnsControl) updateExtModifed() { } var errObj = errors.New("obj was not of the correct type") - -const defaultResyncPeriod = 0 diff --git a/plugin/kubernetes/external.go b/plugin/kubernetes/external.go index 74e7151fcfe..702bdc30c13 100644 --- a/plugin/kubernetes/external.go +++ b/plugin/kubernetes/external.go @@ -14,6 +14,18 @@ import ( // External implements the ExternalFunc call from the external plugin. // It returns any services matching in the services' ExternalIPs. func (k *Kubernetes) External(state request.Request) ([]msg.Service, int) { + if state.QType() == dns.TypePTR { + ip := dnsutil.ExtractAddressFromReverse(state.Name()) + if ip != "" { + svcs, err := k.ExternalReverse(ip) + if err != nil { + return nil, dns.RcodeNameError + } + return svcs, dns.RcodeSuccess + } + // for invalid reverse names, fall through to determine proper nxdomain/nodata response + } + base, _ := dnsutil.TrimZone(state.Name(), state.Zone) segs := dns.SplitDomainName(base) @@ -76,6 +88,10 @@ func (k *Kubernetes) External(state request.Request) ([]msg.Service, int) { } } } + if state.QType() == dns.TypePTR { + // if this was a PTR request, return empty service list, but retain rcode for proper nxdomain/nodata response + return nil, rcode + } return services, rcode } @@ -111,3 +127,24 @@ func (k *Kubernetes) ExternalServices(zone string) (services []msg.Service) { func (k *Kubernetes) ExternalSerial(string) uint32 { return uint32(k.APIConn.Modified(true)) } + +// ExternalReverse does a reverse lookup for the external IPs +func (k *Kubernetes) ExternalReverse(ip string) ([]msg.Service, error) { + records := k.serviceRecordForExternalIP(ip) + if len(records) == 0 { + return records, errNoItems + } + return records, nil +} + +func (k *Kubernetes) serviceRecordForExternalIP(ip string) []msg.Service { + var svcs []msg.Service + for _, service := range k.APIConn.SvcExtIndexReverse(ip) { + if len(k.Namespaces) > 0 && !k.namespaceExposed(service.Namespace) { + continue + } + domain := strings.Join([]string{service.Name, service.Namespace}, ".") + svcs = append(svcs, msg.Service{Host: domain, TTL: k.ttl}) + } + return svcs +} diff --git a/plugin/kubernetes/external_test.go b/plugin/kubernetes/external_test.go index 28ccc608e6d..b0e89e00086 100644 --- a/plugin/kubernetes/external_test.go +++ b/plugin/kubernetes/external_test.go @@ -80,6 +80,7 @@ func (external) Run() func (external) Stop() error { return nil } func (external) EpIndexReverse(string) []*object.Endpoints { return nil } func (external) SvcIndexReverse(string) []*object.Service { return nil } +func (external) SvcExtIndexReverse(string) []*object.Service { return nil } func (external) Modified(bool) int64 { return 0 } func (external) EpIndex(s string) []*object.Endpoints { return nil } func (external) EndpointsList() []*object.Endpoints { return nil } diff --git a/plugin/kubernetes/handler_ignore_emptyservice_test.go b/plugin/kubernetes/handler_ignore_emptyservice_test.go index ee5a4e88c08..7af77fe1bea 100644 --- a/plugin/kubernetes/handler_ignore_emptyservice_test.go +++ b/plugin/kubernetes/handler_ignore_emptyservice_test.go @@ -30,7 +30,6 @@ var dnsEmptyServiceTestCases = []test.Case{ } func TestServeDNSEmptyService(t *testing.T) { - k := New([]string{"cluster.local."}) k.APIConn = &APIConnServeTest{} k.opts.ignoreEmptyService = true diff --git a/plugin/kubernetes/handler_pod_disabled_test.go b/plugin/kubernetes/handler_pod_disabled_test.go index 9a7f2745ad2..be7e7a37b43 100644 --- a/plugin/kubernetes/handler_pod_disabled_test.go +++ b/plugin/kubernetes/handler_pod_disabled_test.go @@ -28,7 +28,6 @@ var podModeDisabledCases = []test.Case{ } func TestServeDNSModeDisabled(t *testing.T) { - k := New([]string{"cluster.local."}) k.APIConn = &APIConnServeTest{} k.Next = test.NextHandler(dns.RcodeSuccess, nil) diff --git a/plugin/kubernetes/handler_pod_insecure_test.go b/plugin/kubernetes/handler_pod_insecure_test.go index 43c7b7940e9..b01d53f9e80 100644 --- a/plugin/kubernetes/handler_pod_insecure_test.go +++ b/plugin/kubernetes/handler_pod_insecure_test.go @@ -63,7 +63,6 @@ var podModeInsecureCases = []test.Case{ } func TestServeDNSModeInsecure(t *testing.T) { - k := New([]string{"cluster.local."}) k.APIConn = &APIConnServeTest{} k.Next = test.NextHandler(dns.RcodeSuccess, nil) diff --git a/plugin/kubernetes/handler_pod_verified_test.go b/plugin/kubernetes/handler_pod_verified_test.go index 668ce5260f1..c8b09c4f5f1 100644 --- a/plugin/kubernetes/handler_pod_verified_test.go +++ b/plugin/kubernetes/handler_pod_verified_test.go @@ -49,7 +49,6 @@ var podModeVerifiedCases = []test.Case{ } func TestServeDNSModeVerified(t *testing.T) { - k := New([]string{"cluster.local."}) k.APIConn = &APIConnServeTest{} k.Next = test.NextHandler(dns.RcodeSuccess, nil) diff --git a/plugin/kubernetes/handler_test.go b/plugin/kubernetes/handler_test.go index d867f727a18..ecf47887f0f 100644 --- a/plugin/kubernetes/handler_test.go +++ b/plugin/kubernetes/handler_test.go @@ -494,7 +494,6 @@ var notSyncedTestCases = []test.Case{ } func TestNotSyncedServeDNS(t *testing.T) { - k := New([]string{"cluster.local."}) k.APIConn = &APIConnServeTest{ notSynced: true, @@ -536,12 +535,13 @@ type APIConnServeTest struct { notSynced bool } -func (a APIConnServeTest) HasSynced() bool { return !a.notSynced } -func (APIConnServeTest) Run() {} -func (APIConnServeTest) Stop() error { return nil } -func (APIConnServeTest) EpIndexReverse(string) []*object.Endpoints { return nil } -func (APIConnServeTest) SvcIndexReverse(string) []*object.Service { return nil } -func (APIConnServeTest) Modified(bool) int64 { return int64(3) } +func (a APIConnServeTest) HasSynced() bool { return !a.notSynced } +func (APIConnServeTest) Run() {} +func (APIConnServeTest) Stop() error { return nil } +func (APIConnServeTest) EpIndexReverse(string) []*object.Endpoints { return nil } +func (APIConnServeTest) SvcIndexReverse(string) []*object.Service { return nil } +func (APIConnServeTest) SvcExtIndexReverse(string) []*object.Service { return nil } +func (APIConnServeTest) Modified(bool) int64 { return int64(3) } func (APIConnServeTest) PodIndex(ip string) []*object.Pod { if ip != "10.240.0.1" { diff --git a/plugin/kubernetes/kubernetes.go b/plugin/kubernetes/kubernetes.go index 3c80097e969..c6459fdaa4a 100644 --- a/plugin/kubernetes/kubernetes.go +++ b/plugin/kubernetes/kubernetes.go @@ -96,7 +96,6 @@ var ( func (k *Kubernetes) Services(ctx context.Context, state request.Request, exact bool, opt plugin.Options) (svcs []msg.Service, err error) { // We're looking again at types, which we've already done in ServeDNS, but there are some types k8s just can't answer. switch state.QType() { - case dns.TypeTXT: // 1 label + zone, label must be "dns-version". t, _ := dnsutil.TrimZone(state.Name(), state.Zone) @@ -216,7 +215,6 @@ func (k *Kubernetes) getClientConfig() (*rest.Config, error) { } cc.ContentType = "application/vnd.kubernetes.protobuf" return cc, err - } // InitKubeCache initializes a new Kubernetes cache. @@ -542,7 +540,6 @@ func (k *Kubernetes) findServices(r recordRequest, zone string) (services []msg. for _, eps := range ep.Subsets { for _, addr := range eps.Addresses { - // See comments in parse.go parseRequest about the endpoint handling. if r.endpoint != "" { if !match(r.endpoint, endpointHostname(addr, k.endpointNameMode)) { diff --git a/plugin/kubernetes/kubernetes_apex_test.go b/plugin/kubernetes/kubernetes_apex_test.go index 5d2f4079b1b..7531e21415a 100644 --- a/plugin/kubernetes/kubernetes_apex_test.go +++ b/plugin/kubernetes/kubernetes_apex_test.go @@ -60,7 +60,6 @@ var kubeApexCases = []test.Case{ } func TestServeDNSApex(t *testing.T) { - k := New([]string{"cluster.local."}) k.APIConn = &APIConnServeTest{} k.Next = test.NextHandler(dns.RcodeSuccess, nil) diff --git a/plugin/kubernetes/kubernetes_test.go b/plugin/kubernetes/kubernetes_test.go index 9832fbd9228..acdfd4c6413 100644 --- a/plugin/kubernetes/kubernetes_test.go +++ b/plugin/kubernetes/kubernetes_test.go @@ -39,13 +39,14 @@ func TestEndpointHostname(t *testing.T) { type APIConnServiceTest struct{} -func (APIConnServiceTest) HasSynced() bool { return true } -func (APIConnServiceTest) Run() {} -func (APIConnServiceTest) Stop() error { return nil } -func (APIConnServiceTest) PodIndex(string) []*object.Pod { return nil } -func (APIConnServiceTest) SvcIndexReverse(string) []*object.Service { return nil } -func (APIConnServiceTest) EpIndexReverse(string) []*object.Endpoints { return nil } -func (APIConnServiceTest) Modified(bool) int64 { return 0 } +func (APIConnServiceTest) HasSynced() bool { return true } +func (APIConnServiceTest) Run() {} +func (APIConnServiceTest) Stop() error { return nil } +func (APIConnServiceTest) PodIndex(string) []*object.Pod { return nil } +func (APIConnServiceTest) SvcIndexReverse(string) []*object.Service { return nil } +func (APIConnServiceTest) SvcExtIndexReverse(string) []*object.Service { return nil } +func (APIConnServiceTest) EpIndexReverse(string) []*object.Endpoints { return nil } +func (APIConnServiceTest) Modified(bool) int64 { return 0 } func (APIConnServiceTest) SvcIndex(string) []*object.Service { svcs := []*object.Service{ diff --git a/plugin/kubernetes/logger.go b/plugin/kubernetes/logger.go new file mode 100644 index 00000000000..ac9fe80711f --- /dev/null +++ b/plugin/kubernetes/logger.go @@ -0,0 +1,38 @@ +package kubernetes + +import ( + clog "github.com/coredns/coredns/plugin/pkg/log" + + "github.com/go-logr/logr" +) + +// loggerAdapter is a simple wrapper around CoreDNS plugin logger made to implement logr.LogSink interface, which is used +// as part of klog library for logging in Kubernetes client. By using this adapter CoreDNS is able to log messages/errors from +// kubernetes client in a CoreDNS logging format +type loggerAdapter struct { + clog.P +} + +func (l *loggerAdapter) Init(_ logr.RuntimeInfo) { +} + +func (l *loggerAdapter) Enabled(_ int) bool { + // verbosity is controlled inside klog library, we do not need to do anything here + return true +} + +func (l *loggerAdapter) Info(_ int, msg string, _ ...interface{}) { + l.P.Info(msg) +} + +func (l *loggerAdapter) Error(_ error, msg string, _ ...interface{}) { + l.P.Error(msg) +} + +func (l *loggerAdapter) WithValues(_ ...interface{}) logr.LogSink { + return l +} + +func (l *loggerAdapter) WithName(_ string) logr.LogSink { + return l +} diff --git a/plugin/kubernetes/ns_test.go b/plugin/kubernetes/ns_test.go index ca69c7c3b47..3ad12cb2989 100644 --- a/plugin/kubernetes/ns_test.go +++ b/plugin/kubernetes/ns_test.go @@ -14,14 +14,15 @@ import ( type APIConnTest struct{} -func (APIConnTest) HasSynced() bool { return true } -func (APIConnTest) Run() {} -func (APIConnTest) Stop() error { return nil } -func (APIConnTest) PodIndex(string) []*object.Pod { return nil } -func (APIConnTest) SvcIndexReverse(string) []*object.Service { return nil } -func (APIConnTest) EpIndex(string) []*object.Endpoints { return nil } -func (APIConnTest) EndpointsList() []*object.Endpoints { return nil } -func (APIConnTest) Modified(bool) int64 { return 0 } +func (APIConnTest) HasSynced() bool { return true } +func (APIConnTest) Run() {} +func (APIConnTest) Stop() error { return nil } +func (APIConnTest) PodIndex(string) []*object.Pod { return nil } +func (APIConnTest) SvcIndexReverse(string) []*object.Service { return nil } +func (APIConnTest) SvcExtIndexReverse(string) []*object.Service { return nil } +func (APIConnTest) EpIndex(string) []*object.Endpoints { return nil } +func (APIConnTest) EndpointsList() []*object.Endpoints { return nil } +func (APIConnTest) Modified(bool) int64 { return 0 } func (a APIConnTest) SvcIndex(s string) []*object.Service { switch s { @@ -98,7 +99,6 @@ func (APIConnTest) GetNamespaceByName(name string) (*object.Namespace, error) { } func TestNsAddrs(t *testing.T) { - k := New([]string{"inter.webs.test."}) k.APIConn = &APIConnTest{} k.localIPs = []net.IP{net.ParseIP("10.244.0.20")} @@ -107,7 +107,6 @@ func TestNsAddrs(t *testing.T) { if len(cdrs) != 3 { t.Fatalf("Expected 3 results, got %v", len(cdrs)) - } cdr := cdrs[0] expected := "10.0.0.111" @@ -139,7 +138,6 @@ func TestNsAddrs(t *testing.T) { } func TestNsAddrsExternal(t *testing.T) { - k := New([]string{"example.com."}) k.APIConn = &APIConnTest{} k.localIPs = []net.IP{net.ParseIP("10.244.0.20")} @@ -149,7 +147,6 @@ func TestNsAddrsExternal(t *testing.T) { if len(cdrs) != 0 { t.Fatalf("Expected 0 results, got %v", len(cdrs)) - } // Add an external IP to one of the services ... @@ -158,7 +155,6 @@ func TestNsAddrsExternal(t *testing.T) { if len(cdrs) != 1 { t.Fatalf("Expected 1 results, got %v", len(cdrs)) - } cdr := cdrs[0] expected := "1.2.3.4" @@ -169,5 +165,4 @@ func TestNsAddrsExternal(t *testing.T) { if cdr.Header().Name != expected { t.Errorf("Expected record name to be %q, got %q", expected, cdr.Header().Name) } - } diff --git a/plugin/kubernetes/object/service.go b/plugin/kubernetes/object/service.go index 812b272e26b..bd3e3d33501 100644 --- a/plugin/kubernetes/object/service.go +++ b/plugin/kubernetes/object/service.go @@ -68,7 +68,6 @@ func ToService(obj meta.Object) (meta.Object, error) { continue } s.ExternalIPs[li+i] = lb.Hostname - } *svc = api.Service{} diff --git a/plugin/kubernetes/parse.go b/plugin/kubernetes/parse.go index 1c072a64994..4690c814b11 100644 --- a/plugin/kubernetes/parse.go +++ b/plugin/kubernetes/parse.go @@ -66,7 +66,6 @@ func parseRequest(name, zone string) (r recordRequest, err error) { // Because of ambiguity we check the labels left: 1: an endpoint. 2: port and protocol. // Anything else is a query that is too long to answer and can safely be delegated to return an nxdomain. switch last { - case 0: // endpoint only r.endpoint = segs[last] case 1: // service and port diff --git a/plugin/kubernetes/reverse.go b/plugin/kubernetes/reverse.go index 2a5c5cdcec2..26fc3b4299b 100644 --- a/plugin/kubernetes/reverse.go +++ b/plugin/kubernetes/reverse.go @@ -12,7 +12,6 @@ import ( // Reverse implements the ServiceBackend interface. func (k *Kubernetes) Reverse(ctx context.Context, state request.Request, exact bool, opt plugin.Options) ([]msg.Service, error) { - ip := dnsutil.ExtractAddressFromReverse(state.Name()) if ip == "" { _, e := k.Records(ctx, state, exact) diff --git a/plugin/kubernetes/reverse_test.go b/plugin/kubernetes/reverse_test.go index aa21a9f8664..370b9f9b5fb 100644 --- a/plugin/kubernetes/reverse_test.go +++ b/plugin/kubernetes/reverse_test.go @@ -15,14 +15,15 @@ import ( type APIConnReverseTest struct{} -func (APIConnReverseTest) HasSynced() bool { return true } -func (APIConnReverseTest) Run() {} -func (APIConnReverseTest) Stop() error { return nil } -func (APIConnReverseTest) PodIndex(string) []*object.Pod { return nil } -func (APIConnReverseTest) EpIndex(string) []*object.Endpoints { return nil } -func (APIConnReverseTest) EndpointsList() []*object.Endpoints { return nil } -func (APIConnReverseTest) ServiceList() []*object.Service { return nil } -func (APIConnReverseTest) Modified(bool) int64 { return 0 } +func (APIConnReverseTest) HasSynced() bool { return true } +func (APIConnReverseTest) Run() {} +func (APIConnReverseTest) Stop() error { return nil } +func (APIConnReverseTest) PodIndex(string) []*object.Pod { return nil } +func (APIConnReverseTest) EpIndex(string) []*object.Endpoints { return nil } +func (APIConnReverseTest) EndpointsList() []*object.Endpoints { return nil } +func (APIConnReverseTest) ServiceList() []*object.Service { return nil } +func (APIConnReverseTest) SvcExtIndexReverse(string) []*object.Service { return nil } +func (APIConnReverseTest) Modified(bool) int64 { return 0 } func (APIConnReverseTest) SvcIndex(svc string) []*object.Service { if svc != "svc1.testns" { @@ -37,7 +38,6 @@ func (APIConnReverseTest) SvcIndex(svc string) []*object.Service { }, } return svcs - } func (APIConnReverseTest) SvcIndexReverse(ip string) []*object.Service { @@ -149,7 +149,6 @@ func (APIConnReverseTest) GetNamespaceByName(name string) (*object.Namespace, er } func TestReverse(t *testing.T) { - k := New([]string{"cluster.local.", "0.10.in-addr.arpa.", "168.192.in-addr.arpa.", "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.d.c.b.a.4.3.2.1.ip6.arpa.", "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.3.0.0.7.7.0.0.0.0.d.f.ip6.arpa."}) k.APIConn = &APIConnReverseTest{} diff --git a/plugin/kubernetes/setup.go b/plugin/kubernetes/setup.go index 5f5ab058255..d7f11e1a789 100644 --- a/plugin/kubernetes/setup.go +++ b/plugin/kubernetes/setup.go @@ -4,7 +4,6 @@ import ( "context" "errors" "fmt" - "os" "strconv" "strings" @@ -15,6 +14,7 @@ import ( clog "github.com/coredns/coredns/plugin/pkg/log" "github.com/coredns/coredns/plugin/pkg/upstream" + "github.com/go-logr/logr" "github.com/miekg/dns" meta "k8s.io/apimachinery/pkg/apis/meta/v1" _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" // pull this in here, because we want it excluded if plugin.cfg doesn't have k8s @@ -32,7 +32,7 @@ func init() { plugin.Register(pluginName, setup) } func setup(c *caddy.Controller) error { // Do not call klog.InitFlags(nil) here. It will cause reload to panic. - klog.SetOutput(os.Stdout) + klog.SetLogger(logr.New(&loggerAdapter{log})) k, err := kubernetesParse(c) if err != nil { @@ -87,7 +87,6 @@ func kubernetesParse(c *caddy.Controller) (*Kubernetes, error) { // ParseStanza parses a kubernetes stanza func ParseStanza(c *caddy.Controller) (*Kubernetes, error) { - k8s := New([]string{""}) k8s.autoPathSearch = searchFromResolvConf() diff --git a/plugin/kubernetes/xfr.go b/plugin/kubernetes/xfr.go index 38899acf0bc..e29a4a7a7af 100644 --- a/plugin/kubernetes/xfr.go +++ b/plugin/kubernetes/xfr.go @@ -63,7 +63,6 @@ func (k *Kubernetes) Transfer(zone string, serial uint32) (<-chan []dns.RR, erro } svcBase := []string{zonePath, Svc, svc.Namespace, svc.Name} switch svc.Type { - case api.ServiceTypeClusterIP, api.ServiceTypeNodePort, api.ServiceTypeLoadBalancer: clusterIP := net.ParseIP(svc.ClusterIPs[0]) if clusterIP != nil { diff --git a/plugin/loadbalance/loadbalance_test.go b/plugin/loadbalance/loadbalance_test.go index 44a5e4b0597..6f50b6e1a6c 100644 --- a/plugin/loadbalance/loadbalance_test.go +++ b/plugin/loadbalance/loadbalance_test.go @@ -91,7 +91,6 @@ func TestLoadBalance(t *testing.T) { if err != nil { t.Errorf("Test %d: Expected no error, but got %s", i, err) continue - } cname, address, mx, sorted := countRecords(rec.Msg.Answer) diff --git a/plugin/loadbalance/setup.go b/plugin/loadbalance/setup.go index f8f2b36837a..d8f273aaab4 100644 --- a/plugin/loadbalance/setup.go +++ b/plugin/loadbalance/setup.go @@ -35,7 +35,6 @@ func parse(c *caddy.Controller) error { case 1: if args[0] != "round_robin" { return fmt.Errorf("unknown policy: %s", args[0]) - } return nil } diff --git a/plugin/log/log.go b/plugin/log/log.go index 7e62011387a..8a3575fd321 100644 --- a/plugin/log/log.go +++ b/plugin/log/log.go @@ -46,11 +46,10 @@ func (l Logger) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) } if ok || ok1 { logstr := l.repl.Replace(ctx, state, rrw, rule.Format) - clog.Infof(logstr) + clog.Info(logstr) } return rc, err - } return plugin.NextOrFailure(l.Name(), l.Next, ctx, w, r) } diff --git a/plugin/log/log_test.go b/plugin/log/log_test.go index 1914e05f30b..e2f3acfdba7 100644 --- a/plugin/log/log_test.go +++ b/plugin/log/log_test.go @@ -202,6 +202,19 @@ func TestLogged(t *testing.T) { ShouldLog: true, ShouldString: "\"0\"", }, + { + Rules: []Rule{ + { + NameScope: ".", + Format: CombinedLogFormat, + Class: map[response.Class]struct{}{response.All: {}}, + }, + }, + Domain: "foo.%s.example.org.", + ShouldLog: true, + ShouldString: "foo.%s.example.org.", + ShouldNOTString: "%!s(MISSING)", + }, } for _, tc := range tests { diff --git a/plugin/log/setup_test.go b/plugin/log/setup_test.go index 8230710706d..2586aded979 100644 --- a/plugin/log/setup_test.go +++ b/plugin/log/setup_test.go @@ -165,7 +165,6 @@ func TestLogParse(t *testing.T) { i, len(test.expectedLogRules), len(actualLogRules)) } for j, actualLogRule := range actualLogRules { - if actualLogRule.NameScope != test.expectedLogRules[j].NameScope { t.Errorf("Test %d expected %dth LogRule NameScope for '%s' to be %s , but got %s", i, j, test.inputLogRules, test.expectedLogRules[j].NameScope, actualLogRule.NameScope) diff --git a/plugin/metadata/metadata.go b/plugin/metadata/metadata.go index b3f803eabc8..c2fe93cfb2a 100644 --- a/plugin/metadata/metadata.go +++ b/plugin/metadata/metadata.go @@ -27,7 +27,6 @@ func ContextWithMetadata(ctx context.Context) context.Context { // ServeDNS implements the plugin.Handler interface. func (m *Metadata) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { - ctx = ContextWithMetadata(ctx) state := request.Request{W: w, Req: r} diff --git a/plugin/metadata/provider.go b/plugin/metadata/provider.go index 06417cc6f1d..e1bd7059015 100644 --- a/plugin/metadata/provider.go +++ b/plugin/metadata/provider.go @@ -64,7 +64,6 @@ func IsLabel(label string) bool { return false } return true - } // Labels returns all metadata keys stored in the context. These label names should be named diff --git a/plugin/normalize.go b/plugin/normalize.go index 7543e42a0cc..4b92bb43e84 100644 --- a/plugin/normalize.go +++ b/plugin/normalize.go @@ -125,7 +125,6 @@ func (h Host) NormalizeExact() []string { } for i := range hosts { hosts[i] = Name(hosts[i]).Normalize() - } return hosts } diff --git a/plugin/normalize_test.go b/plugin/normalize_test.go index 7f1fb477379..cc32eaea4f3 100644 --- a/plugin/normalize_test.go +++ b/plugin/normalize_test.go @@ -52,7 +52,6 @@ func TestNameMatches(t *testing.T) { if actual != m.expected { t.Errorf("Expected %v for %s/%s, got %v", m.expected, m.parent, m.child, actual) } - } } diff --git a/plugin/pkg/cidr/cidr.go b/plugin/pkg/cidr/cidr.go index 15eac6d4847..91aead91a5a 100644 --- a/plugin/pkg/cidr/cidr.go +++ b/plugin/pkg/cidr/cidr.go @@ -38,7 +38,7 @@ func Split(n *net.IPNet) []string { func nets(network *net.IPNet, newPrefixLen int) []*net.IPNet { prefixLen, _ := network.Mask.Size() maxSubnets := int(math.Exp2(float64(newPrefixLen)) / math.Exp2(float64(prefixLen))) - nets := []*net.IPNet{{network.IP, net.CIDRMask(newPrefixLen, 8*len(network.IP))}} + nets := []*net.IPNet{{IP: network.IP, Mask: net.CIDRMask(newPrefixLen, 8*len(network.IP))}} for i := 1; i < maxSubnets; i++ { next, exceeds := cidr.NextSubnet(nets[len(nets)-1], newPrefixLen) diff --git a/plugin/pkg/dnstest/multirecorder_test.go b/plugin/pkg/dnstest/multirecorder_test.go index 756b635acbf..1299db58732 100644 --- a/plugin/pkg/dnstest/multirecorder_test.go +++ b/plugin/pkg/dnstest/multirecorder_test.go @@ -19,7 +19,6 @@ func TestMultiWriteMsg(t *testing.T) { if len(record.Msgs) != 2 { t.Fatalf("Expected 2 messages to be written, but instead found %d\n", len(record.Msgs)) - } if record.Len != responseTestMsg.Len()*2 { t.Fatalf("Expected the bytes written counter to be %d, but instead found %d\n", responseTestMsg.Len()*2, record.Len) diff --git a/plugin/pkg/dnsutil/reverse_test.go b/plugin/pkg/dnsutil/reverse_test.go index 3cf2f0c5112..6fb82795f70 100644 --- a/plugin/pkg/dnsutil/reverse_test.go +++ b/plugin/pkg/dnsutil/reverse_test.go @@ -66,6 +66,5 @@ func TestIsReverse(t *testing.T) { if got != tc.expected { t.Errorf("Test %d, got %d, expected %d for %s", i, got, tc.expected, tc.name) } - } } diff --git a/plugin/pkg/log/listener.go b/plugin/pkg/log/listener.go new file mode 100644 index 00000000000..2dfe8155c1c --- /dev/null +++ b/plugin/pkg/log/listener.go @@ -0,0 +1,141 @@ +package log + +import ( + "sync" +) + +// Listener listens for all log prints of plugin loggers aka loggers with plugin name. +// When a plugin logger gets called, it should first call the same method in the Listener object. +// A usage example is, the external plugin k8s_event will replicate log prints to Kubernetes events. +type Listener interface { + Name() string + Debug(plugin string, v ...interface{}) + Debugf(plugin string, format string, v ...interface{}) + Info(plugin string, v ...interface{}) + Infof(plugin string, format string, v ...interface{}) + Warning(plugin string, v ...interface{}) + Warningf(plugin string, format string, v ...interface{}) + Error(plugin string, v ...interface{}) + Errorf(plugin string, format string, v ...interface{}) + Fatal(plugin string, v ...interface{}) + Fatalf(plugin string, format string, v ...interface{}) +} + +type listeners struct { + listeners []Listener + sync.RWMutex +} + +var ls *listeners + +func init() { + ls = &listeners{} + ls.listeners = make([]Listener, 0) +} + +// RegisterListener register a listener object. +func RegisterListener(new Listener) error { + ls.Lock() + defer ls.Unlock() + for k, l := range ls.listeners { + if l.Name() == new.Name() { + ls.listeners[k] = new + return nil + } + } + ls.listeners = append(ls.listeners, new) + return nil +} + +// DeregisterListener deregister a listener object. +func DeregisterListener(old Listener) error { + ls.Lock() + defer ls.Unlock() + for k, l := range ls.listeners { + if l.Name() == old.Name() { + ls.listeners = append(ls.listeners[:k], ls.listeners[k+1:]...) + return nil + } + } + return nil +} + +func (ls *listeners) debug(plugin string, v ...interface{}) { + ls.RLock() + for _, l := range ls.listeners { + l.Debug(plugin, v...) + } + ls.RUnlock() +} + +func (ls *listeners) debugf(plugin string, format string, v ...interface{}) { + ls.RLock() + for _, l := range ls.listeners { + l.Debugf(plugin, format, v...) + } + ls.RUnlock() +} + +func (ls *listeners) info(plugin string, v ...interface{}) { + ls.RLock() + for _, l := range ls.listeners { + l.Info(plugin, v...) + } + ls.RUnlock() +} + +func (ls *listeners) infof(plugin string, format string, v ...interface{}) { + ls.RLock() + for _, l := range ls.listeners { + l.Infof(plugin, format, v...) + } + ls.RUnlock() +} + +func (ls *listeners) warning(plugin string, v ...interface{}) { + ls.RLock() + for _, l := range ls.listeners { + l.Warning(plugin, v...) + } + ls.RUnlock() +} + +func (ls *listeners) warningf(plugin string, format string, v ...interface{}) { + ls.RLock() + for _, l := range ls.listeners { + l.Warningf(plugin, format, v...) + } + ls.RUnlock() +} + +func (ls *listeners) error(plugin string, v ...interface{}) { + ls.RLock() + for _, l := range ls.listeners { + l.Error(plugin, v...) + } + ls.RUnlock() +} + +func (ls *listeners) errorf(plugin string, format string, v ...interface{}) { + ls.RLock() + for _, l := range ls.listeners { + l.Errorf(plugin, format, v...) + } + ls.RUnlock() +} + +func (ls *listeners) fatal(plugin string, v ...interface{}) { + ls.RLock() + for _, l := range ls.listeners { + l.Fatal(plugin, v...) + } + ls.RUnlock() +} + +func (ls *listeners) fatalf(plugin string, format string, v ...interface{}) { + ls.RLock() + for _, l := range ls.listeners { + l.Fatalf(plugin, format, v...) + } + ls.RUnlock() +} diff --git a/plugin/pkg/log/listener_test.go b/plugin/pkg/log/listener_test.go new file mode 100644 index 00000000000..0df03b446c7 --- /dev/null +++ b/plugin/pkg/log/listener_test.go @@ -0,0 +1,120 @@ +package log + +import ( + "bytes" + golog "log" + "strings" + "testing" +) + +func TestRegisterAndDeregisterListener(t *testing.T) { + for _, name := range []string{"listener1", "listener2", "listener1"} { + err := RegisterListener(NewMockListener(name)) + if err != nil { + t.Errorf("RegsiterListener Error %s", err) + } + } + if len(ls.listeners) != 2 { + t.Errorf("Expected number of listeners to be %d, got %d", 2, len(ls.listeners)) + } + for _, name := range []string{"listener1", "listener2"} { + err := DeregisterListener(NewMockListener(name)) + if err != nil { + t.Errorf("DeregsiterListener Error %s", err) + } + } + if len(ls.listeners) != 0 { + t.Errorf("Expected number of listeners to be %d, got %d", 0, len(ls.listeners)) + } +} + +func TestSingleListenerMock(t *testing.T) { + listener1Name := "listener1" + listener1Output := info + listener1Name + " mocked info" + testListenersCalled(t, []string{listener1Name}, []string{listener1Output}) +} + +func TestMultipleListenerMock(t *testing.T) { + listener1Name := "listener1" + listener1Output := info + listener1Name + " mocked info" + listener2Name := "listener2" + listener2Output := info + listener2Name + " mocked info" + testListenersCalled(t, []string{listener1Name, listener2Name}, []string{listener1Output, listener2Output}) +} + +func testListenersCalled(t *testing.T, listenerNames []string, outputs []string) { + for _, name := range listenerNames { + err := RegisterListener(NewMockListener(name)) + if err != nil { + t.Errorf("RegsiterListener Error %s", err) + } + } + var f bytes.Buffer + const ts = "test" + golog.SetOutput(&f) + lg := NewWithPlugin("testplugin") + lg.Info(ts) + for _, str := range outputs { + if x := f.String(); !strings.Contains(x, str) { + t.Errorf("Expected log to contain %s, got %s", str, x) + } + } + for _, name := range listenerNames { + err := DeregisterListener(NewMockListener(name)) + if err != nil { + t.Errorf("DeregsiterListener Error %s", err) + } + } +} + +type mockListener struct { + name string +} + +func NewMockListener(name string) *mockListener { + return &mockListener{name: name} +} + +func (l *mockListener) Name() string { + return l.name +} + +func (l *mockListener) Debug(plugin string, v ...interface{}) { + log(debug, l.name+" mocked debug") +} + +func (l *mockListener) Debugf(plugin string, format string, v ...interface{}) { + log(debug, l.name+" mocked debug") +} + +func (l *mockListener) Info(plugin string, v ...interface{}) { + log(info, l.name+" mocked info") +} + +func (l *mockListener) Infof(plugin string, format string, v ...interface{}) { + log(info, l.name+" mocked info") +} + +func (l *mockListener) Warning(plugin string, v ...interface{}) { + log(warning, l.name+" mocked warning") +} + +func (l *mockListener) Warningf(plugin string, format string, v ...interface{}) { + log(warning, l.name+" mocked warning") +} + +func (l *mockListener) Error(plugin string, v ...interface{}) { + log(err, l.name+" mocked error") +} + +func (l *mockListener) Errorf(plugin string, format string, v ...interface{}) { + log(err, l.name+" mocked error") +} + +func (l *mockListener) Fatal(plugin string, v ...interface{}) { + log(fatal, l.name+" mocked fatal") +} + +func (l *mockListener) Fatalf(plugin string, format string, v ...interface{}) { + log(fatal, l.name+" mocked fatal") +} diff --git a/plugin/pkg/log/plugin.go b/plugin/pkg/log/plugin.go index 8dbffa07e7e..1be79f1111d 100644 --- a/plugin/pkg/log/plugin.go +++ b/plugin/pkg/log/plugin.go @@ -27,6 +27,7 @@ func (p P) Debug(v ...interface{}) { if !D.Value() { return } + ls.debug(p.plugin, v...) p.log(debug, v...) } @@ -35,29 +36,56 @@ func (p P) Debugf(format string, v ...interface{}) { if !D.Value() { return } + ls.debugf(p.plugin, format, v...) p.logf(debug, format, v...) } // Info logs as log.Info. -func (p P) Info(v ...interface{}) { p.log(info, v...) } +func (p P) Info(v ...interface{}) { + ls.info(p.plugin, v...) + p.log(info, v...) +} // Infof logs as log.Infof. -func (p P) Infof(format string, v ...interface{}) { p.logf(info, format, v...) } +func (p P) Infof(format string, v ...interface{}) { + ls.infof(p.plugin, format, v...) + p.logf(info, format, v...) +} // Warning logs as log.Warning. -func (p P) Warning(v ...interface{}) { p.log(warning, v...) } +func (p P) Warning(v ...interface{}) { + ls.warning(p.plugin, v...) + p.log(warning, v...) +} // Warningf logs as log.Warningf. -func (p P) Warningf(format string, v ...interface{}) { p.logf(warning, format, v...) } +func (p P) Warningf(format string, v ...interface{}) { + ls.warningf(p.plugin, format, v...) + p.logf(warning, format, v...) +} // Error logs as log.Error. -func (p P) Error(v ...interface{}) { p.log(err, v...) } +func (p P) Error(v ...interface{}) { + ls.error(p.plugin, v...) + p.log(err, v...) +} // Errorf logs as log.Errorf. -func (p P) Errorf(format string, v ...interface{}) { p.logf(err, format, v...) } +func (p P) Errorf(format string, v ...interface{}) { + ls.errorf(p.plugin, format, v...) + p.logf(err, format, v...) +} // Fatal logs as log.Fatal and calls os.Exit(1). -func (p P) Fatal(v ...interface{}) { p.log(fatal, v...); os.Exit(1) } +func (p P) Fatal(v ...interface{}) { + ls.fatal(p.plugin, v...) + p.log(fatal, v...) + os.Exit(1) +} // Fatalf logs as log.Fatalf and calls os.Exit(1). -func (p P) Fatalf(format string, v ...interface{}) { p.logf(fatal, format, v...); os.Exit(1) } +func (p P) Fatalf(format string, v ...interface{}) { + ls.fatalf(p.plugin, format, v...) + p.logf(fatal, format, v...) + os.Exit(1) +} diff --git a/plugin/pkg/parse/host.go b/plugin/pkg/parse/host.go index 3bee4f96e35..9206a033d08 100644 --- a/plugin/pkg/parse/host.go +++ b/plugin/pkg/parse/host.go @@ -32,7 +32,6 @@ func stripZone(host string) string { func HostPortOrFile(s ...string) ([]string, error) { var servers []string for _, h := range s { - trans, host := Transport(h) addr, _, err := net.SplitHostPort(host) diff --git a/plugin/pkg/rand/rand.go b/plugin/pkg/rand/rand.go index d2b3960b42a..490f59b6dd0 100644 --- a/plugin/pkg/rand/rand.go +++ b/plugin/pkg/rand/rand.go @@ -32,5 +32,4 @@ func (r *Rand) Perm(n int) []int { v := r.r.Perm(n) r.m.Unlock() return v - } diff --git a/plugin/pkg/replacer/replacer.go b/plugin/pkg/replacer/replacer.go index 845f11bc3c1..f927305c241 100644 --- a/plugin/pkg/replacer/replacer.go +++ b/plugin/pkg/replacer/replacer.go @@ -271,6 +271,7 @@ func (r replacer) Replace(ctx context.Context, state request.Request, rr *dnstes } } s := string(b) + //nolint:staticcheck bufPool.Put(b[:0]) return s } diff --git a/plugin/pkg/replacer/replacer_test.go b/plugin/pkg/replacer/replacer_test.go index 25dd37a5b22..e428aad9abf 100644 --- a/plugin/pkg/replacer/replacer_test.go +++ b/plugin/pkg/replacer/replacer_test.go @@ -276,7 +276,6 @@ func BenchmarkReplacer(b *testing.B) { } func BenchmarkReplacer_CommonLogFormat(b *testing.B) { - w := dnstest.NewRecorder(&test.ResponseWriter{}) r := new(dns.Msg) r.SetQuestion("example.org.", dns.TypeHINFO) diff --git a/plugin/pprof/setup.go b/plugin/pprof/setup.go index 6eba18a2a40..3505b5d7231 100644 --- a/plugin/pprof/setup.go +++ b/plugin/pprof/setup.go @@ -57,7 +57,6 @@ func setup(c *caddy.Controller) error { return plugin.Error("pprof", c.Errf("unknown property '%s'", c.Val())) } } - } c.OnStartup(h.Startup) diff --git a/plugin/ready/list.go b/plugin/ready/list.go index e7d2584d878..c2462873069 100644 --- a/plugin/ready/list.go +++ b/plugin/ready/list.go @@ -13,6 +13,14 @@ type list struct { names []string } +// Reset resets l +func (l *list) Reset() { + l.Lock() + defer l.Unlock() + l.rs = nil + l.names = nil +} + // Append adds a new readiness to l. func (l *list) Append(r Readiness, name string) { l.Lock() diff --git a/plugin/ready/setup.go b/plugin/ready/setup.go index 80f09d516d7..e5657f62f2a 100644 --- a/plugin/ready/setup.go +++ b/plugin/ready/setup.go @@ -25,6 +25,7 @@ func setup(c *caddy.Controller) error { c.OnRestartFailed(func() error { return uniqAddr.ForEach() }) c.OnStartup(func() error { + plugins.Reset() for _, p := range dnsserver.GetConfig(c).Handlers() { if r, ok := p.(Readiness); ok { plugins.Append(r, p.Name()) diff --git a/plugin/rewrite/edns0.go b/plugin/rewrite/edns0.go index 5e3663ef6f0..85146c7ecdd 100644 --- a/plugin/rewrite/edns0.go +++ b/plugin/rewrite/edns0.go @@ -180,7 +180,6 @@ func newEdns0VariableRule(mode, action, code, variable string) (*edns0VariableRu // ruleData returns the data specified by the variable. func (rule *edns0VariableRule) ruleData(ctx context.Context, state request.Request) ([]byte, error) { - switch rule.variable { case queryName: return []byte(state.QName()), nil diff --git a/plugin/rewrite/name.go b/plugin/rewrite/name.go index 73aa171e6aa..95d2b7a0d63 100644 --- a/plugin/rewrite/name.go +++ b/plugin/rewrite/name.go @@ -367,7 +367,6 @@ func parseAnswerRules(name string, args []string) (auto bool, rules ResponseRule if last == "" && args[arg] != AnswerMatch { if last == "" { return false, nil, fmt.Errorf("exceeded the number of arguments for a non-answer rule argument for %s rule", name) - } return false, nil, fmt.Errorf("exceeded the number of arguments for %s answer rule for %s rule", last, name) } diff --git a/plugin/rewrite/name_test.go b/plugin/rewrite/name_test.go index bd0112e396a..2dbf1d1d269 100644 --- a/plugin/rewrite/name_test.go +++ b/plugin/rewrite/name_test.go @@ -33,7 +33,6 @@ func TestRewriteIllegalName(t *testing.T) { } func TestRewriteNamePrefixSuffix(t *testing.T) { - ctx, close := context.WithCancel(context.TODO()) defer close() @@ -76,7 +75,6 @@ func TestRewriteNamePrefixSuffix(t *testing.T) { } func TestRewriteNameNoRewrite(t *testing.T) { - ctx, close := context.WithCancel(context.TODO()) defer close() @@ -119,7 +117,6 @@ func TestRewriteNameNoRewrite(t *testing.T) { } func TestRewriteNamePrefixSuffixNoAutoAnswer(t *testing.T) { - ctx, close := context.WithCancel(context.TODO()) defer close() @@ -162,7 +159,6 @@ func TestRewriteNamePrefixSuffixNoAutoAnswer(t *testing.T) { } func TestRewriteNamePrefixSuffixAutoAnswer(t *testing.T) { - ctx, close := context.WithCancel(context.TODO()) defer close() @@ -211,7 +207,6 @@ func TestRewriteNamePrefixSuffixAutoAnswer(t *testing.T) { } func TestRewriteNameExactAnswer(t *testing.T) { - ctx, close := context.WithCancel(context.TODO()) defer close() @@ -260,7 +255,6 @@ func TestRewriteNameExactAnswer(t *testing.T) { } func TestRewriteNameRegexAnswer(t *testing.T) { - ctx, close := context.WithCancel(context.TODO()) defer close() diff --git a/plugin/rewrite/reverter_test.go b/plugin/rewrite/reverter_test.go index 2c90d9bb634..9156728b2dd 100644 --- a/plugin/rewrite/reverter_test.go +++ b/plugin/rewrite/reverter_test.go @@ -31,7 +31,6 @@ var tests = []struct { } func TestResponseReverter(t *testing.T) { - rules := []Rule{} r, _ := newNameRule("stop", "regex", `(core)\.(dns)\.(rocks)`, "{2}.{1}.{3}", "answer", "name", `(dns)\.(core)\.(rocks)`, "{2}.{1}.{3}") rules = append(rules, r) @@ -98,7 +97,6 @@ var valueTests = []struct { } func TestValueResponseReverter(t *testing.T) { - rules := []Rule{} r, err := newNameRule("stop", "regex", `(.*)\.domain\.uk`, "{1}.cluster.local", "answer", "name", `(.*)\.cluster\.local`, "{1}.domain.uk", "answer", "value", `(.*)\.cluster\.local`, "{1}.domain.uk") if err != nil { diff --git a/plugin/rewrite/rewrite.go b/plugin/rewrite/rewrite.go index c315fa73afb..b28352cbd44 100644 --- a/plugin/rewrite/rewrite.go +++ b/plugin/rewrite/rewrite.go @@ -100,10 +100,16 @@ func newRule(args ...string) (Rule, error) { switch arg0 { case Continue: mode = Continue + if len(args) < 2 { + return nil, fmt.Errorf("continue rule must begin with a rule type") + } ruleType = strings.ToLower(args[1]) expectNumArgs = len(args) - 1 startArg = 2 case Stop: + if len(args) < 2 { + return nil, fmt.Errorf("stop rule must begin with a rule type") + } ruleType = strings.ToLower(args[1]) expectNumArgs = len(args) - 1 startArg = 2 diff --git a/plugin/rewrite/setup_test.go b/plugin/rewrite/setup_test.go index 00808bd9c71..88d332fb4ba 100644 --- a/plugin/rewrite/setup_test.go +++ b/plugin/rewrite/setup_test.go @@ -8,60 +8,44 @@ import ( ) func TestParse(t *testing.T) { - c := caddy.NewTestController("dns", `rewrite`) - _, err := rewriteParse(c) - if err == nil { - t.Errorf("Expected error but found nil for `rewrite`") - } - c = caddy.NewTestController("dns", `rewrite name`) - _, err = rewriteParse(c) - if err == nil { - t.Errorf("Expected error but found nil for `rewrite name`") - } - c = caddy.NewTestController("dns", `rewrite name a.com b.com`) - _, err = rewriteParse(c) - if err != nil { - t.Errorf("Expected success but found %s for `rewrite name a.com b.com`", err) - } - - c = caddy.NewTestController("dns", - `rewrite stop { + tests := []struct { + inputFileRules string + shouldErr bool + errContains string + }{ + // parse errors + {`rewrite`, true, ""}, + {`rewrite name`, true, ""}, + {`rewrite name a.com b.com`, false, ""}, + {`rewrite stop { name regex foo bar answer name bar foo -}`) - _, err = rewriteParse(c) - if err != nil { - t.Errorf("Expected success but found %s for valid response rewrite", err) - } - - c = caddy.NewTestController("dns", `rewrite stop name regex foo bar answer name bar foo`) - _, err = rewriteParse(c) - if err != nil { - t.Errorf("Expected success but found %s for valid response rewrite", err) - } - - c = caddy.NewTestController("dns", - `rewrite stop { +}`, false, ""}, + {`rewrite stop name regex foo bar answer name bar foo`, false, ""}, + {`rewrite stop { name regex foo bar answer name bar foo name baz -}`) - _, err = rewriteParse(c) - if err == nil { - t.Errorf("Expected error but got success for invalid response rewrite") - } else if !strings.Contains(err.Error(), "2 arguments required") { - t.Errorf("Got wrong error for invalid response rewrite: %v", err.Error()) - } - - c = caddy.NewTestController("dns", - `rewrite stop { +}`, true, "2 arguments required"}, + {`rewrite stop { answer name bar foo name regex foo bar -}`) - _, err = rewriteParse(c) - if err == nil { - t.Errorf("Expected error but got success for invalid response rewrite") - } else if !strings.Contains(err.Error(), "must begin with a name rule") { - t.Errorf("Got wrong error for invalid response rewrite: %v", err.Error()) +}`, true, "must begin with a name rule"}, + {`rewrite stop`, true, ""}, + {`rewrite continue`, true, ""}, + } + + for i, test := range tests { + c := caddy.NewTestController("dns", test.inputFileRules) + _, err := rewriteParse(c) + if err == nil && test.shouldErr { + t.Fatalf("Test %d expected errors, but got no error\n---\n%s", i, test.inputFileRules) + } else if err != nil && !test.shouldErr { + t.Fatalf("Test %d expected no errors, but got '%v'\n---\n%s", i, err, test.inputFileRules) + } + + if err != nil && test.errContains != "" && !strings.Contains(err.Error(), test.errContains) { + t.Errorf("Test %d got wrong error for invalid response rewrite: '%v'\n---\n%s", i, err.Error(), test.inputFileRules) + } } } diff --git a/plugin/rewrite/ttl_test.go b/plugin/rewrite/ttl_test.go index 9ac0807acd5..a59fd460da0 100644 --- a/plugin/rewrite/ttl_test.go +++ b/plugin/rewrite/ttl_test.go @@ -83,7 +83,6 @@ func TestTtlRewrite(t *testing.T) { rule, err := newRule(r.args...) if err != nil { t.Fatalf("Rule %d: FAIL, %s: %s", i, r.args, err) - continue } if reflect.TypeOf(rule) != r.expectedType { t.Fatalf("Rule %d: FAIL, %s: rule type mismatch, expected %q, but got %q", i, r.args, r.expectedType, rule) diff --git a/plugin/rewrite/wire.go b/plugin/rewrite/wire.go index 116b7dcc149..11b4dac0525 100644 --- a/plugin/rewrite/wire.go +++ b/plugin/rewrite/wire.go @@ -9,7 +9,6 @@ import ( // ipToWire writes IP address to wire/binary format, 4 or 16 bytes depends on IPV4 or IPV6. func ipToWire(family int, ipAddr string) ([]byte, error) { - switch family { case 1: return net.ParseIP(ipAddr).To4(), nil @@ -28,7 +27,6 @@ func uint16ToWire(data uint16) []byte { // portToWire writes port to wire/binary format, 2 bytes func portToWire(portStr string) ([]byte, error) { - port, err := strconv.ParseUint(portStr, 10, 16) if err != nil { return nil, err diff --git a/plugin/route53/route53.go b/plugin/route53/route53.go index 72cb74e8fb5..abe5a7d756d 100644 --- a/plugin/route53/route53.go +++ b/plugin/route53/route53.go @@ -212,7 +212,6 @@ func maybeUnescape(s string) (string, error) { func updateZoneFromRRS(rrs *route53.ResourceRecordSet, z *file.Zone) error { for _, rr := range rrs.ResourceRecords { - n, err := maybeUnescape(aws.StringValue(rrs.Name)) if err != nil { return fmt.Errorf("failed to unescape `%s' name: %v", aws.StringValue(rrs.Name), err) @@ -273,7 +272,6 @@ func (h *Route53) updateZones(ctx context.Context) error { (*z[i]).z = newZ h.zMu.Unlock() } - }(zName, z) } // Collect errors (if any). This will also sync on all zones updates diff --git a/plugin/route53/route53_test.go b/plugin/route53/route53_test.go index e408b29f256..1e740361200 100644 --- a/plugin/route53/route53_test.go +++ b/plugin/route53/route53_test.go @@ -109,7 +109,6 @@ func TestRoute53(t *testing.T) { m.Authoritative = true rcode = dns.RcodeSuccess - } m.SetRcode(r, rcode) diff --git a/plugin/route53/setup.go b/plugin/route53/setup.go index e3117e7066e..3df6527c427 100644 --- a/plugin/route53/setup.go +++ b/plugin/route53/setup.go @@ -126,10 +126,12 @@ func setup(c *caddy.Controller) error { ctx, cancel := context.WithCancel(context.Background()) h, err := New(ctx, client, keys, refresh) if err != nil { + cancel() return plugin.Error("route53", c.Errf("failed to create route53 plugin: %v", err)) } h.Fall = fall if err := h.Run(ctx); err != nil { + cancel() return plugin.Error("route53", c.Errf("failed to initialize route53 plugin: %v", err)) } dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler { diff --git a/plugin/secondary/setup.go b/plugin/secondary/setup.go index 97d8cd9f5dc..22f0d32241d 100644 --- a/plugin/secondary/setup.go +++ b/plugin/secondary/setup.go @@ -64,7 +64,6 @@ func secondaryParse(c *caddy.Controller) (file.Zones, error) { z := make(map[string]*file.Zone) names := []string{} for c.Next() { - if c.Val() == "secondary" { // secondary [origin] origins := plugin.OriginsFromArgsOrServerBlock(c.RemainingArgs(), c.ServerBlockKeys) @@ -74,8 +73,7 @@ func secondaryParse(c *caddy.Controller) (file.Zones, error) { } for c.NextBlock() { - - f := []string{} + var f []string switch c.Val() { case "transfer": diff --git a/plugin/template/setup.go b/plugin/template/setup.go index 03a5322f840..44d774b6f78 100644 --- a/plugin/template/setup.go +++ b/plugin/template/setup.go @@ -32,7 +32,6 @@ func templateParse(c *caddy.Controller) (handler Handler, err error) { handler.Templates = make([]template, 0) for c.Next() { - if !c.NextArg() { return handler, c.ArgErr() } diff --git a/plugin/template/setup_test.go b/plugin/template/setup_test.go index 85261463ee0..39096cc8dcd 100644 --- a/plugin/template/setup_test.go +++ b/plugin/template/setup_test.go @@ -24,7 +24,6 @@ func TestSetup(t *testing.T) { } func TestSetupParse(t *testing.T) { - serverBlockKeys := []string{"domain.com.:8053", "dynamic.domain.com.:8053"} tests := []struct { diff --git a/plugin/test/helpers.go b/plugin/test/helpers.go index 99271df1e85..8145b605ae5 100644 --- a/plugin/test/helpers.go +++ b/plugin/test/helpers.go @@ -285,7 +285,6 @@ func SortAndCheck(resp *dns.Msg, tc Case) error { } if err := Section(tc, Ns, resp.Ns); err != nil { return err - } return Section(tc, Extra, resp.Extra) } diff --git a/plugin/test/responsewriter.go b/plugin/test/responsewriter.go index c66b0c96901..32167008ab8 100644 --- a/plugin/test/responsewriter.go +++ b/plugin/test/responsewriter.go @@ -12,6 +12,7 @@ import ( type ResponseWriter struct { TCP bool // if TCP is true we return an TCP connection instead of an UDP one. RemoteIP string + Zone string } // LocalAddr returns the local address, 127.0.0.1:53 (UDP, TCP if t.TCP is true). @@ -33,9 +34,9 @@ func (t *ResponseWriter) RemoteAddr() net.Addr { ip := net.ParseIP(remoteIP) port := 40212 if t.TCP { - return &net.TCPAddr{IP: ip, Port: port, Zone: ""} + return &net.TCPAddr{IP: ip, Port: port, Zone: t.Zone} } - return &net.UDPAddr{IP: ip, Port: port, Zone: ""} + return &net.UDPAddr{IP: ip, Port: port, Zone: t.Zone} } // WriteMsg implements dns.ResponseWriter interface. diff --git a/plugin/test/scrape.go b/plugin/test/scrape.go index 3fb6b26d5f7..7847e39d460 100644 --- a/plugin/test/scrape.go +++ b/plugin/test/scrape.go @@ -80,7 +80,6 @@ func Scrape(url string) []*MetricFamily { // ScrapeMetricAsInt provides a sum of all metrics collected for the name and label provided. // if the metric is not a numeric value, it will be counted a 0. func ScrapeMetricAsInt(addr string, name string, label string, nometricvalue int) int { - valueToInt := func(m metric) int { v := m.Value r, err := strconv.Atoi(v) @@ -141,7 +140,6 @@ func MetricValueLabel(name, label string, mfs []*MetricFamily) (string, map[stri return m.(metric).Value, m.(metric).Labels } } - } } } diff --git a/plugin/trace/README.md b/plugin/trace/README.md index 0a0d0e848ec..eac8a7ba764 100644 --- a/plugin/trace/README.md +++ b/plugin/trace/README.md @@ -32,6 +32,9 @@ trace [ENDPOINT-TYPE] [ENDPOINT] { service NAME client_server datadog_analytics_rate RATE + zipkin_max_backlog_size SIZE + zipkin_max_batch_size SIZE + zipkin_max_batch_interval DURATION } ~~~ @@ -43,6 +46,11 @@ trace [ENDPOINT-TYPE] [ENDPOINT] { * `datadog_analytics_rate` **RATE** will enable [trace analytics](https://docs.datadoghq.com/tracing/app_analytics) on the traces sent from *0* to *1*, *1* being every trace sent will be analyzed. This is a datadog only feature (**ENDPOINT-TYPE** needs to be `datadog`) +* `zipkin_max_backlog_size` configures the maximum backlog size for Zipkin HTTP reporter. When batch size reaches this threshold, + spans from the beginning of the batch will be disposed. Default is 1000 backlog size. +* `zipkin_max_batch_size` configures the maximum batch size for Zipkin HTTP reporter, after which a collect will be triggered. The default batch size is 100 traces. +* `zipkin_max_batch_interval` configures the maximum duration we will buffer traces before emitting them to the collector using Zipkin HTTP reporter. + The default batch interval is 1 second. ## Zipkin diff --git a/plugin/trace/logger.go b/plugin/trace/logger.go new file mode 100644 index 00000000000..649938715e2 --- /dev/null +++ b/plugin/trace/logger.go @@ -0,0 +1,20 @@ +package trace + +import ( + clog "github.com/coredns/coredns/plugin/pkg/log" +) + +// loggerAdapter is a simple adapter around plugin logger made to implement io.Writer and ddtrace.Logger interface +// in order to log errors from span reporters as warnings +type loggerAdapter struct { + clog.P +} + +func (l *loggerAdapter) Write(p []byte) (n int, err error) { + l.P.Warning(string(p)) + return len(p), nil +} + +func (l *loggerAdapter) Log(msg string) { + l.P.Warning(msg) +} diff --git a/plugin/trace/setup.go b/plugin/trace/setup.go index abe5db0f791..8672dcc535c 100644 --- a/plugin/trace/setup.go +++ b/plugin/trace/setup.go @@ -4,6 +4,7 @@ import ( "fmt" "strconv" "strings" + "time" "github.com/coredns/caddy" "github.com/coredns/coredns/core/dnsserver" @@ -100,6 +101,33 @@ func traceParse(c *caddy.Controller) (*trace, error) { if tr.datadogAnalyticsRate > 1 || tr.datadogAnalyticsRate < 0 { return nil, fmt.Errorf("datadog analytics rate must be between 0 and 1, '%f' is not supported", tr.datadogAnalyticsRate) } + case "zipkin_max_backlog_size": + args := c.RemainingArgs() + if len(args) != 1 { + return nil, c.ArgErr() + } + tr.zipkinMaxBacklogSize, err = strconv.Atoi(args[0]) + if err != nil { + return nil, err + } + case "zipkin_max_batch_size": + args := c.RemainingArgs() + if len(args) != 1 { + return nil, c.ArgErr() + } + tr.zipkinMaxBatchSize, err = strconv.Atoi(args[0]) + if err != nil { + return nil, err + } + case "zipkin_max_batch_interval": + args := c.RemainingArgs() + if len(args) != 1 { + return nil, c.ArgErr() + } + tr.zipkinMaxBatchInterval, err = time.ParseDuration(args[0]) + if err != nil { + return nil, err + } } } } diff --git a/plugin/trace/setup_test.go b/plugin/trace/setup_test.go index bbc5f987fa4..72de4ab16e9 100644 --- a/plugin/trace/setup_test.go +++ b/plugin/trace/setup_test.go @@ -2,35 +2,48 @@ package trace import ( "testing" + "time" "github.com/coredns/caddy" ) func TestTraceParse(t *testing.T) { tests := []struct { - input string - shouldErr bool - endpoint string - every uint64 - serviceName string - clientServer bool + input string + shouldErr bool + endpoint string + every uint64 + serviceName string + clientServer bool + zipkinMaxBacklogSize int + zipkinMaxBatchSize int + zipkinMaxBatchInterval time.Duration }{ // oks - {`trace`, false, "http://localhost:9411/api/v2/spans", 1, `coredns`, false}, - {`trace localhost:1234`, false, "http://localhost:1234/api/v2/spans", 1, `coredns`, false}, - {`trace http://localhost:1234/somewhere/else`, false, "http://localhost:1234/somewhere/else", 1, `coredns`, false}, - {`trace zipkin localhost:1234`, false, "http://localhost:1234/api/v2/spans", 1, `coredns`, false}, - {`trace datadog localhost`, false, "localhost", 1, `coredns`, false}, - {`trace datadog http://localhost:8127`, false, "http://localhost:8127", 1, `coredns`, false}, - {"trace datadog localhost {\n datadog_analytics_rate 0.1\n}", false, "localhost", 1, `coredns`, false}, - {"trace {\n every 100\n}", false, "http://localhost:9411/api/v2/spans", 100, `coredns`, false}, - {"trace {\n every 100\n service foobar\nclient_server\n}", false, "http://localhost:9411/api/v2/spans", 100, `foobar`, true}, - {"trace {\n every 2\n client_server true\n}", false, "http://localhost:9411/api/v2/spans", 2, `coredns`, true}, - {"trace {\n client_server false\n}", false, "http://localhost:9411/api/v2/spans", 1, `coredns`, false}, + {`trace`, false, "http://localhost:9411/api/v2/spans", 1, `coredns`, false, 0, 0, 0}, + {`trace localhost:1234`, false, "http://localhost:1234/api/v2/spans", 1, `coredns`, false, 0, 0, 0}, + {`trace http://localhost:1234/somewhere/else`, false, "http://localhost:1234/somewhere/else", 1, `coredns`, false, 0, 0, 0}, + {`trace zipkin localhost:1234`, false, "http://localhost:1234/api/v2/spans", 1, `coredns`, false, 0, 0, 0}, + {`trace datadog localhost`, false, "localhost", 1, `coredns`, false, 0, 0, 0}, + {`trace datadog http://localhost:8127`, false, "http://localhost:8127", 1, `coredns`, false, 0, 0, 0}, + {"trace datadog localhost {\n datadog_analytics_rate 0.1\n}", false, "localhost", 1, `coredns`, false, 0, 0, 0}, + {"trace {\n every 100\n}", false, "http://localhost:9411/api/v2/spans", 100, `coredns`, false, 0, 0, 0}, + {"trace {\n every 100\n service foobar\nclient_server\n}", false, "http://localhost:9411/api/v2/spans", 100, `foobar`, true, 0, 0, 0}, + {"trace {\n every 2\n client_server true\n}", false, "http://localhost:9411/api/v2/spans", 2, `coredns`, true, 0, 0, 0}, + {"trace {\n client_server false\n}", false, "http://localhost:9411/api/v2/spans", 1, `coredns`, false, 0, 0, 0}, + {"trace {\n zipkin_max_backlog_size 100\n zipkin_max_batch_size 200\n zipkin_max_batch_interval 10s\n}", false, + "http://localhost:9411/api/v2/spans", 1, `coredns`, false, 100, 200, 10 * time.Second}, + // fails - {`trace footype localhost:4321`, true, "", 1, "", false}, - {"trace {\n every 2\n client_server junk\n}", true, "", 1, "", false}, - {"trace datadog localhost {\n datadog_analytics_rate 2\n}", true, "", 1, "", false}, + {`trace footype localhost:4321`, true, "", 1, "", false, 0, 0, 0}, + {"trace {\n every 2\n client_server junk\n}", true, "", 1, "", false, 0, 0, 0}, + {"trace datadog localhost {\n datadog_analytics_rate 2\n}", true, "", 1, "", false, 0, 0, 0}, + {"trace {\n zipkin_max_backlog_size wrong\n}", true, "", 1, `coredns`, false, 0, 0, 0}, + {"trace {\n zipkin_max_batch_size wrong\n}", true, "", 1, `coredns`, false, 0, 0, 0}, + {"trace {\n zipkin_max_batch_interval wrong\n}", true, "", 1, `coredns`, false, 0, 0, 0}, + {"trace {\n zipkin_max_backlog_size\n}", true, "", 1, `coredns`, false, 0, 0, 0}, + {"trace {\n zipkin_max_batch_size\n}", true, "", 1, `coredns`, false, 0, 0, 0}, + {"trace {\n zipkin_max_batch_interval\n}", true, "", 1, `coredns`, false, 0, 0, 0}, } for i, test := range tests { c := caddy.NewTestController("dns", test.input) @@ -62,5 +75,14 @@ func TestTraceParse(t *testing.T) { if test.clientServer != m.clientServer { t.Errorf("Test %v: Expected client_server %t but found: %t", i, test.clientServer, m.clientServer) } + if test.zipkinMaxBacklogSize != m.zipkinMaxBacklogSize { + t.Errorf("Test %v: Expected zipkin_max_backlog_size %d but found: %d", i, test.zipkinMaxBacklogSize, m.zipkinMaxBacklogSize) + } + if test.zipkinMaxBatchSize != m.zipkinMaxBatchSize { + t.Errorf("Test %v: Expected zipkin_max_batch_size %d but found: %d", i, test.zipkinMaxBatchSize, m.zipkinMaxBatchSize) + } + if test.zipkinMaxBatchInterval != m.zipkinMaxBatchInterval { + t.Errorf("Test %v: Expected zipkin_max_batch_interval %v but found: %v", i, test.zipkinMaxBatchInterval, m.zipkinMaxBatchInterval) + } } } diff --git a/plugin/trace/trace.go b/plugin/trace/trace.go index 87cb65e6802..f7409679d5e 100644 --- a/plugin/trace/trace.go +++ b/plugin/trace/trace.go @@ -4,13 +4,17 @@ package trace import ( "context" "fmt" + stdlog "log" + "net/http" "sync" "sync/atomic" + "time" + "github.com/coredns/coredns/core/dnsserver" "github.com/coredns/coredns/plugin" "github.com/coredns/coredns/plugin/metadata" "github.com/coredns/coredns/plugin/pkg/dnstest" - "github.com/coredns/coredns/plugin/pkg/log" + clog "github.com/coredns/coredns/plugin/pkg/log" "github.com/coredns/coredns/plugin/pkg/rcode" _ "github.com/coredns/coredns/plugin/pkg/trace" // Plugin the trace package. "github.com/coredns/coredns/request" @@ -33,6 +37,8 @@ const ( metaTraceIdKey = "trace/traceid" ) +var log = clog.NewWithPlugin("trace") + type traceTags struct { Name string Type string @@ -61,17 +67,20 @@ var tagByProvider = map[string]traceTags{ type trace struct { count uint64 // as per Go spec, needs to be first element in a struct - Next plugin.Handler - Endpoint string - EndpointType string - tracer ot.Tracer - serviceEndpoint string - serviceName string - clientServer bool - every uint64 - datadogAnalyticsRate float64 - Once sync.Once - tagSet traceTags + Next plugin.Handler + Endpoint string + EndpointType string + tracer ot.Tracer + serviceEndpoint string + serviceName string + clientServer bool + every uint64 + datadogAnalyticsRate float64 + zipkinMaxBacklogSize int + zipkinMaxBatchSize int + zipkinMaxBatchInterval time.Duration + Once sync.Once + tagSet traceTags } func (t *trace) Tracer() ot.Tracer { @@ -88,10 +97,11 @@ func (t *trace) OnStartup() error { case "datadog": tracer := opentracer.New( tracer.WithAgentAddr(t.Endpoint), - tracer.WithDebugMode(log.D.Value()), + tracer.WithDebugMode(clog.D.Value()), tracer.WithGlobalTag(ext.SpanTypeDNS, true), tracer.WithServiceName(t.serviceName), tracer.WithAnalyticsRate(t.datadogAnalyticsRate), + tracer.WithLogger(&loggerAdapter{log}), ) t.tracer = tracer t.tagSet = tagByProvider["datadog"] @@ -103,7 +113,18 @@ func (t *trace) OnStartup() error { } func (t *trace) setupZipkin() error { - reporter := zipkinhttp.NewReporter(t.Endpoint) + var opts []zipkinhttp.ReporterOption + opts = append(opts, zipkinhttp.Logger(stdlog.New(&loggerAdapter{log}, "", 0))) + if t.zipkinMaxBacklogSize != 0 { + opts = append(opts, zipkinhttp.MaxBacklog(t.zipkinMaxBacklogSize)) + } + if t.zipkinMaxBatchSize != 0 { + opts = append(opts, zipkinhttp.BatchSize(t.zipkinMaxBatchSize)) + } + if t.zipkinMaxBatchInterval != 0 { + opts = append(opts, zipkinhttp.BatchInterval(t.zipkinMaxBatchInterval)) + } + reporter := zipkinhttp.NewReporter(t.Endpoint, opts...) recorder, err := zipkin.NewEndpoint(t.serviceName, t.serviceEndpoint) if err != nil { log.Warningf("build Zipkin endpoint found err: %v", err) @@ -140,8 +161,15 @@ func (t *trace) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) return plugin.NextOrFailure(t.Name(), t.Next, ctx, w, r) } + var spanCtx ot.SpanContext + if val := ctx.Value(dnsserver.HTTPRequestKey{}); val != nil { + if httpReq, ok := val.(*http.Request); ok { + spanCtx, _ = t.Tracer().Extract(ot.HTTPHeaders, ot.HTTPHeadersCarrier(httpReq.Header)) + } + } + req := request.Request{W: w, Req: r} - span = t.Tracer().StartSpan(defaultTopLevelSpanName) + span = t.Tracer().StartSpan(defaultTopLevelSpanName, otext.RPCServerOption(spanCtx)) defer span.Finish() switch spanCtx := span.Context().(type) { diff --git a/plugin/trace/trace_test.go b/plugin/trace/trace_test.go index dae546f8d51..940eb6b029a 100644 --- a/plugin/trace/trace_test.go +++ b/plugin/trace/trace_test.go @@ -3,9 +3,11 @@ package trace import ( "context" "errors" + "net/http/httptest" "testing" "github.com/coredns/caddy" + "github.com/coredns/coredns/core/dnsserver" "github.com/coredns/coredns/plugin" "github.com/coredns/coredns/plugin/pkg/dnstest" "github.com/coredns/coredns/plugin/pkg/rcode" @@ -13,6 +15,7 @@ import ( "github.com/coredns/coredns/request" "github.com/miekg/dns" + "github.com/opentracing/opentracing-go" "github.com/opentracing/opentracing-go/mocktracer" ) @@ -131,3 +134,40 @@ func TestTrace(t *testing.T) { }) } } + +func TestTrace_DOH_TraceHeaderExtraction(t *testing.T) { + w := dnstest.NewRecorder(&test.ResponseWriter{}) + m := mocktracer.New() + tr := &trace{ + Next: test.HandlerFunc(func(_ context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { + if plugin.ClientWrite(dns.RcodeSuccess) { + m := new(dns.Msg) + m.SetRcode(r, dns.RcodeSuccess) + w.WriteMsg(m) + } + return dns.RcodeSuccess, nil + }), + every: 1, + tracer: m, + } + q := new(dns.Msg).SetQuestion("example.net.", dns.TypeA) + + req := httptest.NewRequest("POST", "/dns-query", nil) + + outsideSpan := m.StartSpan("test-header-span") + outsideSpan.Tracer().Inject(outsideSpan.Context(), opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(req.Header)) + defer outsideSpan.Finish() + + ctx := context.TODO() + ctx = context.WithValue(ctx, dnsserver.HTTPRequestKey{}, req) + + tr.ServeDNS(ctx, w, q) + + fs := m.FinishedSpans() + rootCoreDNSspan := fs[1] + rootCoreDNSTraceID := rootCoreDNSspan.Context().(mocktracer.MockSpanContext).TraceID + outsideSpanTraceID := outsideSpan.Context().(mocktracer.MockSpanContext).TraceID + if rootCoreDNSTraceID != outsideSpanTraceID { + t.Errorf("Unexpected traceID: rootSpan.TraceID: want %v, got %v", rootCoreDNSTraceID, outsideSpanTraceID) + } +} diff --git a/plugin/transfer/failed_write_test.go b/plugin/transfer/failed_write_test.go index c1e2dc45b65..e60fd501bca 100644 --- a/plugin/transfer/failed_write_test.go +++ b/plugin/transfer/failed_write_test.go @@ -12,10 +12,9 @@ import ( type badwriter struct { dns.ResponseWriter - count int } -func (w *badwriter) WriteMsg(res *dns.Msg) error { return fmt.Errorf("failed to write msg") } +func (w *badwriter) WriteMsg(_ *dns.Msg) error { return fmt.Errorf("failed to write msg") } func TestWriteMessageFailed(t *testing.T) { transfer := newTestTransfer() diff --git a/plugin/transfer/setup.go b/plugin/transfer/setup.go index 604a269599a..cd7d2091fa9 100644 --- a/plugin/transfer/setup.go +++ b/plugin/transfer/setup.go @@ -28,8 +28,10 @@ func setup(c *caddy.Controller) error { }) c.OnStartup(func() error { + config := dnsserver.GetConfig(c) + t.tsigSecret = config.TsigSecret // find all plugins that implement Transferer and add them to Transferers - plugins := dnsserver.GetConfig(c).Handlers() + plugins := config.Handlers() for _, pl := range plugins { tr, ok := pl.(Transferer) if !ok { diff --git a/plugin/transfer/setup_test.go b/plugin/transfer/setup_test.go index d46eec17c87..ebfe99c06af 100644 --- a/plugin/transfer/setup_test.go +++ b/plugin/transfer/setup_test.go @@ -98,7 +98,6 @@ func TestParse(t *testing.T) { for k, zone := range x.Zones { if tc.exp.xfrs[j].Zones[k] != zone { t.Errorf("Test %d expected zone %v, got %v", i, tc.exp.xfrs[j].Zones[k], zone) - } } // Check to @@ -108,7 +107,6 @@ func TestParse(t *testing.T) { for k, to := range x.to { if tc.exp.xfrs[j].to[k] != to { t.Errorf("Test %d expected %v in 'to', got %v", i, tc.exp.xfrs[j].to[k], to) - } } } diff --git a/plugin/transfer/transfer.go b/plugin/transfer/transfer.go index a9ad211dfe2..71136e1dee3 100644 --- a/plugin/transfer/transfer.go +++ b/plugin/transfer/transfer.go @@ -18,6 +18,7 @@ var log = clog.NewWithPlugin("transfer") type Transfer struct { Transferers []Transferer // List of plugins that implement Transferer xfrs []*xfr + tsigSecret map[string]string Next plugin.Handler } @@ -110,6 +111,9 @@ func (t *Transfer) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Ms // Send response to client ch := make(chan *dns.Envelope) tr := new(dns.Transfer) + if r.IsTsig() != nil { + tr.TsigSecret = t.tsigSecret + } errCh := make(chan error) go func() { if err := tr.Out(w, r, ch); err != nil { @@ -163,7 +167,6 @@ func (t *Transfer) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Ms return dns.RcodeServerFailure, err } l += len(rrs) - } close(ch) // Even though we close the channel here, we still have diff --git a/plugin/tsig/README.md b/plugin/tsig/README.md new file mode 100644 index 00000000000..025c43ab1e9 --- /dev/null +++ b/plugin/tsig/README.md @@ -0,0 +1,111 @@ +# tsig + +## Name + +*tsig* - validate TSIG requests and sign responses. + +## Description + +With *tsig*, you can define a set of TSIG secret keys for validating incoming TSIG requests and signing +responses. It can also require TSIG for certain query types, refusing requests that do not comply. + +## Syntax + +~~~ +tsig [ZONE...] { + secret NAME KEY + secrets FILE + require [QTYPE...] +} +~~~ + + * **ZONE** - the zones *tsig* will TSIG. By default, the zones from the server block are used. + + * `secret` **NAME** **KEY** - specifies a TSIG secret for **NAME** with **KEY**. Use this option more than once + to define multiple secrets. Secrets are global to the server instance, not just for the enclosing **ZONE**. + + * `secrets` **FILE** - same as `secret`, but load the secrets from a file. The file may define any number + of unique keys, each in the following `named.conf` format: + ```cgo + key "example." { + secret "X28hl0BOfAL5G0jsmJWSacrwn7YRm2f6U5brnzwWEus="; + }; + ``` + Each key may also specify an `algorithm` e.g. `algorithm hmac-sha256;`, but this is currently ignored by the plugin. + + * `require` **QTYPE...** - the query types that must be TSIG'd. Requests of the specified types + will be `REFUSED` if they are not signed.`require all` will require requests of all types to be + signed. `require none` will not require requests any types to be signed. Default behavior is to not require. + +## Examples + +Require TSIG signed transactions for transfer requests to `example.zone`. + +``` +example.zone { + tsig { + secret example.zone.key. NoTCJU+DMqFWywaPyxSijrDEA/eC3nK0xi3AMEZuPVk= + require AXFR IXFR + } + transfer { + to * + } +} +``` + +Require TSIG signed transactions for all requests to `auth.zone`. + +``` +auth.zone { + tsig { + secret auth.zone.key. NoTCJU+DMqFWywaPyxSijrDEA/eC3nK0xi3AMEZuPVk= + require all + } + forward . 10.1.0.2 +} +``` + +## Bugs + +### Zone Transfer Notifies + +With the transfer plugin, zone transfer notifications from CoreDNS are not TSIG signed. + +### Special Considerations for Forwarding Servers (RFC 8945 5.5) + +https://datatracker.ietf.org/doc/html/rfc8945#section-5.5 + +CoreDNS does not implement this section as follows ... + +* RFC requirement: + > If the name on the TSIG is not +of a secret that the server shares with the originator, the server +MUST forward the message unchanged including the TSIG. + + CoreDNS behavior: +If ths zone of the request matches the _tsig_ plugin zones, then the TSIG record +is always stripped. But even when the _tsig_ plugin is not involved, the _forward_ plugin +may alter the message with compression, which would cause validation failure +at the destination. + + +* RFC requirement: + > If the TSIG passes all checks, the forwarding +server MUST, if possible, include a TSIG of its own to the +destination or the next forwarder. + + CoreDNS behavior: +If ths zone of the request matches the _tsig_ plugin zones, _forward_ plugin will +proxy the request upstream without TSIG. + + +* RFC requirement: + > If no transaction security is +available to the destination and the message is a query, and if the +corresponding response has the AD flag (see RFC4035) set, the +forwarder MUST clear the AD flag before adding the TSIG to the +response and returning the result to the system from which it +received the query. + + CoreDNS behavior: +The AD flag is not cleared. diff --git a/plugin/tsig/setup.go b/plugin/tsig/setup.go new file mode 100644 index 00000000000..a187a4b4ab0 --- /dev/null +++ b/plugin/tsig/setup.go @@ -0,0 +1,168 @@ +package tsig + +import ( + "bufio" + "fmt" + "io" + "os" + "strings" + + "github.com/coredns/caddy" + "github.com/coredns/coredns/core/dnsserver" + "github.com/coredns/coredns/plugin" + + "github.com/miekg/dns" +) + +func init() { + caddy.RegisterPlugin(pluginName, caddy.Plugin{ + ServerType: "dns", + Action: setup, + }) +} + +func setup(c *caddy.Controller) error { + t, err := parse(c) + if err != nil { + return plugin.Error(pluginName, c.ArgErr()) + } + + config := dnsserver.GetConfig(c) + + config.TsigSecret = t.secrets + + config.AddPlugin(func(next plugin.Handler) plugin.Handler { + t.Next = next + return t + }) + + return nil +} + +func parse(c *caddy.Controller) (*TSIGServer, error) { + t := &TSIGServer{ + secrets: make(map[string]string), + types: defaultQTypes, + } + + for i := 0; c.Next(); i++ { + if i > 0 { + return nil, plugin.ErrOnce + } + + t.Zones = plugin.OriginsFromArgsOrServerBlock(c.RemainingArgs(), c.ServerBlockKeys) + for c.NextBlock() { + switch c.Val() { + case "secret": + args := c.RemainingArgs() + if len(args) != 2 { + return nil, c.ArgErr() + } + k := plugin.Name(args[0]).Normalize() + if _, exists := t.secrets[k]; exists { + return nil, fmt.Errorf("key %q redefined", k) + } + t.secrets[k] = args[1] + case "secrets": + args := c.RemainingArgs() + if len(args) != 1 { + return nil, c.ArgErr() + } + f, err := os.Open(args[0]) + if err != nil { + return nil, err + } + secrets, err := parseKeyFile(f) + if err != nil { + return nil, err + } + for k, s := range secrets { + if _, exists := t.secrets[k]; exists { + return nil, fmt.Errorf("key %q redefined", k) + } + t.secrets[k] = s + } + case "require": + t.types = qTypes{} + args := c.RemainingArgs() + if len(args) == 0 { + return nil, c.ArgErr() + } + if args[0] == "all" { + t.all = true + continue + } + if args[0] == "none" { + continue + } + for _, str := range args { + qt, ok := dns.StringToType[str] + if !ok { + return nil, c.Errf("unknown query type '%s'", str) + } + t.types[qt] = struct{}{} + } + default: + return nil, c.Errf("unknown property '%s'", c.Val()) + } + } + } + return t, nil +} + +func parseKeyFile(f io.Reader) (map[string]string, error) { + secrets := make(map[string]string) + s := bufio.NewScanner(f) + for s.Scan() { + fields := strings.Fields(s.Text()) + if len(fields) == 0 { + continue + } + if fields[0] != "key" { + return nil, fmt.Errorf("unexpected token %q", fields[0]) + } + if len(fields) < 2 { + return nil, fmt.Errorf("expected key name %q", s.Text()) + } + key := strings.Trim(fields[1], "\"{") + if len(key) == 0 { + return nil, fmt.Errorf("expected key name %q", s.Text()) + } + key = plugin.Name(key).Normalize() + if _, ok := secrets[key]; ok { + return nil, fmt.Errorf("key %q redefined", key) + } + key: + for s.Scan() { + fields := strings.Fields(s.Text()) + if len(fields) == 0 { + continue + } + switch fields[0] { + case "algorithm": + continue + case "secret": + if len(fields) < 2 { + return nil, fmt.Errorf("expected secret key %q", s.Text()) + } + secret := strings.Trim(fields[1], "\";") + if len(secret) == 0 { + return nil, fmt.Errorf("expected secret key %q", s.Text()) + } + secrets[key] = secret + case "}": + fallthrough + case "};": + break key + default: + return nil, fmt.Errorf("unexpected token %q", fields[0]) + } + } + if _, ok := secrets[key]; !ok { + return nil, fmt.Errorf("expected secret for key %q", key) + } + } + return secrets, nil +} + +var defaultQTypes = qTypes{} diff --git a/plugin/tsig/setup_test.go b/plugin/tsig/setup_test.go new file mode 100644 index 00000000000..0d743399646 --- /dev/null +++ b/plugin/tsig/setup_test.go @@ -0,0 +1,245 @@ +package tsig + +import ( + "fmt" + "strings" + "testing" + + "github.com/coredns/caddy" + "github.com/coredns/coredns/plugin/test" + + "github.com/miekg/dns" +) + +func TestParse(t *testing.T) { + secrets := map[string]string{ + "name.key.": "test-key", + "name2.key.": "test-key-2", + } + secretConfig := "" + for k, s := range secrets { + secretConfig += fmt.Sprintf("secret %s %s\n", k, s) + } + secretsFile, cleanup, err := test.TempFile(".", `key "name.key." { + secret "test-key"; +}; +key "name2.key." { + secret "test-key2"; +};`) + if err != nil { + t.Fatalf("failed to create temp file: %v", err) + } + defer cleanup() + + tests := []struct { + input string + shouldErr bool + expectedZones []string + expectedQTypes qTypes + expectedSecrets map[string]string + expectedAll bool + }{ + { + input: "tsig {\n " + secretConfig + "}", + expectedZones: []string{"."}, + expectedQTypes: defaultQTypes, + expectedSecrets: secrets, + }, + { + input: "tsig {\n secrets " + secretsFile + "\n}", + expectedZones: []string{"."}, + expectedQTypes: defaultQTypes, + expectedSecrets: secrets, + }, + { + input: "tsig example.com {\n " + secretConfig + "}", + expectedZones: []string{"example.com."}, + expectedQTypes: defaultQTypes, + expectedSecrets: secrets, + }, + { + input: "tsig {\n " + secretConfig + " require all \n}", + expectedZones: []string{"."}, + expectedQTypes: qTypes{}, + expectedAll: true, + expectedSecrets: secrets, + }, + { + input: "tsig {\n " + secretConfig + " require none \n}", + expectedZones: []string{"."}, + expectedQTypes: qTypes{}, + expectedAll: false, + expectedSecrets: secrets, + }, + { + input: "tsig {\n " + secretConfig + " \n require A AAAA \n}", + expectedZones: []string{"."}, + expectedQTypes: qTypes{dns.TypeA: {}, dns.TypeAAAA: {}}, + expectedSecrets: secrets, + }, + { + input: "tsig {\n blah \n}", + shouldErr: true, + }, + { + input: "tsig {\n secret name. too many parameters \n}", + shouldErr: true, + }, + { + input: "tsig {\n require \n}", + shouldErr: true, + }, + { + input: "tsig {\n require invalid-qtype \n}", + shouldErr: true, + }, + } + + serverBlockKeys := []string{"."} + for i, test := range tests { + c := caddy.NewTestController("dns", test.input) + c.ServerBlockKeys = serverBlockKeys + ts, err := parse(c) + + if err == nil && test.shouldErr { + t.Fatalf("Test %d expected errors, but got no error.", i) + } else if err != nil && !test.shouldErr { + t.Fatalf("Test %d expected no errors, but got '%v'", i, err) + } + + if test.shouldErr { + continue + } + + if len(test.expectedZones) != len(ts.Zones) { + t.Fatalf("Test %d expected zones '%v', but got '%v'.", i, test.expectedZones, ts.Zones) + } + for j := range test.expectedZones { + if test.expectedZones[j] != ts.Zones[j] { + t.Errorf("Test %d expected zones '%v', but got '%v'.", i, test.expectedZones, ts.Zones) + break + } + } + + if test.expectedAll != ts.all { + t.Errorf("Test %d expected require all to be '%v', but got '%v'.", i, test.expectedAll, ts.all) + } + + if len(test.expectedQTypes) != len(ts.types) { + t.Fatalf("Test %d expected required types '%v', but got '%v'.", i, test.expectedQTypes, ts.types) + } + for qt := range test.expectedQTypes { + if _, ok := ts.types[qt]; !ok { + t.Errorf("Test %d required types '%v', but got '%v'.", i, test.expectedQTypes, ts.types) + break + } + } + + if len(test.expectedSecrets) != len(ts.secrets) { + t.Fatalf("Test %d expected secrets '%v', but got '%v'.", i, test.expectedSecrets, ts.secrets) + } + for qt := range test.expectedSecrets { + secret, ok := ts.secrets[qt] + if !ok { + t.Errorf("Test %d required secrets '%v', but got '%v'.", i, test.expectedSecrets, ts.secrets) + break + } + if secret != ts.secrets[qt] { + t.Errorf("Test %d required secrets '%v', but got '%v'.", i, test.expectedSecrets, ts.secrets) + break + } + } + } +} + +func TestParseKeyFile(t *testing.T) { + var reader = strings.NewReader(`key "foo" { + algorithm hmac-sha256; + secret "36eowrtmxceNA3T5AdE+JNUOWFCw3amtcyHACnrDVgQ="; +}; +key "bar" { + algorithm hmac-sha256; + secret "X28hl0BOfAL5G0jsmJWSacrwn7YRm2f6U5brnzwWEus="; +}; +key "baz" { + secret "BycDPXSx/5YCD44Q4g5Nd2QNxNRDKwWTXddrU/zpIQM="; +};`) + + secrets, err := parseKeyFile(reader) + if err != nil { + t.Fatalf("Unexpected error: %q", err) + } + expectedSecrets := map[string]string{ + "foo.": "36eowrtmxceNA3T5AdE+JNUOWFCw3amtcyHACnrDVgQ=", + "bar.": "X28hl0BOfAL5G0jsmJWSacrwn7YRm2f6U5brnzwWEus=", + "baz.": "BycDPXSx/5YCD44Q4g5Nd2QNxNRDKwWTXddrU/zpIQM=", + } + + if len(secrets) != len(expectedSecrets) { + t.Fatalf("result has %d keys. expected %d", len(secrets), len(expectedSecrets)) + } + + for k, sec := range secrets { + expectedSec, ok := expectedSecrets[k] + if !ok { + t.Errorf("unexpected key in result. %q", k) + continue + } + if sec != expectedSec { + t.Errorf("incorrect secret in result for key %q. expected %q got %q ", k, expectedSec, sec) + } + } +} + +func TestParseKeyFileErrors(t *testing.T) { + tests := []struct { + in string + err string + }{ + {in: `key {`, err: "expected key name \"key {\""}, + {in: `foo "key" {`, err: "unexpected token \"foo\""}, + { + in: `key "foo" { + secret "36eowrtmxceNA3T5AdE+JNUOWFCw3amtcyHACnrDVgQ="; + }; + key "foo" { + secret "X28hl0BOfAL5G0jsmJWSacrwn7YRm2f6U5brnzwWEus="; + }; `, + err: "key \"foo.\" redefined", + }, + {in: `key "foo" { + schmalgorithm hmac-sha256;`, + err: "unexpected token \"schmalgorithm\"", + }, + { + in: `key "foo" { + schmecret "36eowrtmxceNA3T5AdE+JNUOWFCw3amtcyHACnrDVgQ=";`, + err: "unexpected token \"schmecret\"", + }, + { + in: `key "foo" { + secret`, + err: "expected secret key \"\\tsecret\"", + }, + { + in: `key "foo" { + secret ;`, + err: "expected secret key \"\\tsecret ;\"", + }, + { + in: `key "foo" { + };`, + err: "expected secret for key \"foo.\"", + }, + } + for i, testcase := range tests { + _, err := parseKeyFile(strings.NewReader(testcase.in)) + if err == nil { + t.Errorf("Test %d: expected error, got no error", i) + continue + } + if err.Error() != testcase.err { + t.Errorf("Test %d: Expected error: %q, got %q", i, testcase.err, err.Error()) + } + } +} diff --git a/plugin/tsig/tsig.go b/plugin/tsig/tsig.go new file mode 100644 index 00000000000..6441c8a6b75 --- /dev/null +++ b/plugin/tsig/tsig.go @@ -0,0 +1,140 @@ +package tsig + +import ( + "context" + "encoding/binary" + "encoding/hex" + "time" + + "github.com/coredns/coredns/plugin" + "github.com/coredns/coredns/plugin/pkg/log" + "github.com/coredns/coredns/request" + + "github.com/miekg/dns" +) + +// TSIGServer verifies tsig status and adds tsig to responses +type TSIGServer struct { + Zones []string + secrets map[string]string // [key-name]secret + types qTypes + all bool + Next plugin.Handler +} + +type qTypes map[uint16]struct{} + +// Name implements plugin.Handler +func (t TSIGServer) Name() string { return pluginName } + +// ServeDNS implements plugin.Handler +func (t *TSIGServer) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { + var err error + state := request.Request{Req: r, W: w} + if z := plugin.Zones(t.Zones).Matches(state.Name()); z == "" { + return plugin.NextOrFailure(t.Name(), t.Next, ctx, w, r) + } + + var tsigRR = r.IsTsig() + rcode := dns.RcodeSuccess + if !t.tsigRequired(state.QType()) && tsigRR == nil { + return plugin.NextOrFailure(t.Name(), t.Next, ctx, w, r) + } + + if tsigRR == nil { + log.Debugf("rejecting '%s' request without TSIG\n", dns.TypeToString[state.QType()]) + rcode = dns.RcodeRefused + } + + // wrap the response writer so the response will be TSIG signed. + w = &restoreTsigWriter{w, r, tsigRR} + + tsigStatus := w.TsigStatus() + if tsigStatus != nil { + log.Debugf("TSIG validation failed: %v %v", dns.TypeToString[state.QType()], tsigStatus) + rcode = dns.RcodeNotAuth + switch tsigStatus { + case dns.ErrSecret: + tsigRR.Error = dns.RcodeBadKey + case dns.ErrTime: + tsigRR.Error = dns.RcodeBadTime + default: + tsigRR.Error = dns.RcodeBadSig + } + resp := new(dns.Msg).SetRcode(r, rcode) + w.WriteMsg(resp) + return dns.RcodeSuccess, nil + } + + // strip the TSIG RR. Next, and subsequent plugins will not see the TSIG RRs. + // This violates forwarding cases (RFC 8945 5.5). See README.md Bugs + if len(r.Extra) > 1 { + r.Extra = r.Extra[0 : len(r.Extra)-1] + } else { + r.Extra = []dns.RR{} + } + + if rcode == dns.RcodeSuccess { + rcode, err = plugin.NextOrFailure(t.Name(), t.Next, ctx, w, r) + if err != nil { + log.Errorf("request handler returned an error: %v\n", err) + } + } + // If the plugin chain result was not an error, restore the TSIG and write the response. + if !plugin.ClientWrite(rcode) { + resp := new(dns.Msg).SetRcode(r, rcode) + w.WriteMsg(resp) + } + return dns.RcodeSuccess, nil +} + +func (t *TSIGServer) tsigRequired(qtype uint16) bool { + if t.all { + return true + } + if _, ok := t.types[qtype]; ok { + return true + } + return false +} + +// restoreTsigWriter Implement Response Writer, and adds a TSIG RR to a response +type restoreTsigWriter struct { + dns.ResponseWriter + req *dns.Msg // original request excluding TSIG if it has one + reqTSIG *dns.TSIG // original TSIG +} + +// WriteMsg adds a TSIG RR to the response +func (r *restoreTsigWriter) WriteMsg(m *dns.Msg) error { + // Make sure the response has an EDNS OPT RR if the request had it. + // Otherwise ScrubWriter would append it *after* TSIG, making it a non-compliant DNS message. + state := request.Request{Req: r.req, W: r.ResponseWriter} + state.SizeAndDo(m) + + repTSIG := m.IsTsig() + if r.reqTSIG != nil && repTSIG == nil { + repTSIG = new(dns.TSIG) + repTSIG.Hdr = dns.RR_Header{Name: r.reqTSIG.Hdr.Name, Rrtype: dns.TypeTSIG, Class: dns.ClassANY} + repTSIG.Algorithm = r.reqTSIG.Algorithm + repTSIG.OrigId = m.MsgHdr.Id + repTSIG.Error = r.reqTSIG.Error + repTSIG.MAC = r.reqTSIG.MAC + repTSIG.MACSize = r.reqTSIG.MACSize + if repTSIG.Error == dns.RcodeBadTime { + // per RFC 8945 5.2.3. client time goes into TimeSigned, server time in OtherData, OtherLen = 6 ... + repTSIG.TimeSigned = r.reqTSIG.TimeSigned + b := make([]byte, 8) + // TimeSigned is network byte order. + binary.BigEndian.PutUint64(b, uint64(time.Now().Unix())) + // truncate to 48 least significant bits (network order 6 rightmost bytes) + repTSIG.OtherData = hex.EncodeToString(b[2:]) + repTSIG.OtherLen = 6 + } + m.Extra = append(m.Extra, repTSIG) + } + + return r.ResponseWriter.WriteMsg(m) +} + +const pluginName = "tsig" diff --git a/plugin/tsig/tsig_test.go b/plugin/tsig/tsig_test.go new file mode 100644 index 00000000000..f7ec1fdf1d8 --- /dev/null +++ b/plugin/tsig/tsig_test.go @@ -0,0 +1,255 @@ +package tsig + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/coredns/coredns/plugin/pkg/dnstest" + "github.com/coredns/coredns/plugin/test" + "github.com/coredns/coredns/request" + + "github.com/miekg/dns" +) + +func TestServeDNS(t *testing.T) { + cases := []struct { + zones []string + reqTypes qTypes + qType uint16 + qTsig, all bool + expectRcode int + expectTsig bool + statusError bool + }{ + { + zones: []string{"."}, + all: true, + qType: dns.TypeA, + qTsig: true, + expectRcode: dns.RcodeSuccess, + expectTsig: true, + }, + { + zones: []string{"."}, + all: true, + qType: dns.TypeA, + qTsig: false, + expectRcode: dns.RcodeRefused, + expectTsig: false, + }, + { + zones: []string{"another.domain."}, + all: true, + qType: dns.TypeA, + qTsig: false, + expectRcode: dns.RcodeSuccess, + expectTsig: false, + }, + { + zones: []string{"another.domain."}, + all: true, + qType: dns.TypeA, + qTsig: true, + expectRcode: dns.RcodeSuccess, + expectTsig: false, + }, + { + zones: []string{"."}, + reqTypes: qTypes{dns.TypeAXFR: {}}, + qType: dns.TypeAXFR, + qTsig: true, + expectRcode: dns.RcodeSuccess, + expectTsig: true, + }, + { + zones: []string{"."}, + reqTypes: qTypes{}, + qType: dns.TypeA, + qTsig: false, + expectRcode: dns.RcodeSuccess, + expectTsig: false, + }, + { + zones: []string{"."}, + reqTypes: qTypes{}, + qType: dns.TypeA, + qTsig: true, + expectRcode: dns.RcodeSuccess, + expectTsig: true, + }, + { + zones: []string{"."}, + all: true, + qType: dns.TypeA, + qTsig: true, + expectRcode: dns.RcodeNotAuth, + expectTsig: true, + statusError: true, + }, + } + + for i, tc := range cases { + t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { + tsig := TSIGServer{ + Zones: tc.zones, + all: tc.all, + types: tc.reqTypes, + Next: testHandler(), + } + + ctx := context.TODO() + + var w *dnstest.Recorder + if tc.statusError { + w = dnstest.NewRecorder(&ErrWriter{err: dns.ErrSig}) + } else { + w = dnstest.NewRecorder(&test.ResponseWriter{}) + } + r := new(dns.Msg) + r.SetQuestion("test.example.", tc.qType) + if tc.qTsig { + r.SetTsig("test.key.", dns.HmacSHA256, 300, time.Now().Unix()) + } + + _, err := tsig.ServeDNS(ctx, w, r) + if err != nil { + t.Fatal(err) + } + + if w.Msg.Rcode != tc.expectRcode { + t.Fatalf("expected rcode %v, got %v", tc.expectRcode, w.Msg.Rcode) + } + + if ts := w.Msg.IsTsig(); ts == nil && tc.expectTsig { + t.Fatal("expected TSIG in response") + } + if ts := w.Msg.IsTsig(); ts != nil && !tc.expectTsig { + t.Fatal("expected no TSIG in response") + } + }) + } +} + +func TestServeDNSTsigErrors(t *testing.T) { + clientNow := time.Now().Unix() + + cases := []struct { + desc string + tsigErr error + expectRcode int + expectError int + expectOtherLength int + expectTimeSigned int64 + }{ + { + desc: "Unknown Key", + tsigErr: dns.ErrSecret, + expectRcode: dns.RcodeNotAuth, + expectError: dns.RcodeBadKey, + expectOtherLength: 0, + expectTimeSigned: 0, + }, + { + desc: "Bad Signature", + tsigErr: dns.ErrSig, + expectRcode: dns.RcodeNotAuth, + expectError: dns.RcodeBadSig, + expectOtherLength: 0, + expectTimeSigned: 0, + }, + { + desc: "Bad Time", + tsigErr: dns.ErrTime, + expectRcode: dns.RcodeNotAuth, + expectError: dns.RcodeBadTime, + expectOtherLength: 6, + expectTimeSigned: clientNow, + }, + } + + tsig := TSIGServer{ + Zones: []string{"."}, + all: true, + Next: testHandler(), + } + + for _, tc := range cases { + t.Run(tc.desc, func(t *testing.T) { + ctx := context.TODO() + + var w *dnstest.Recorder + + w = dnstest.NewRecorder(&ErrWriter{err: tc.tsigErr}) + + r := new(dns.Msg) + r.SetQuestion("test.example.", dns.TypeA) + r.SetTsig("test.key.", dns.HmacSHA256, 300, clientNow) + + // set a fake MAC and Size in request + rtsig := r.IsTsig() + rtsig.MAC = "0123456789012345678901234567890101234567890123456789012345678901" + rtsig.MACSize = 32 + + _, err := tsig.ServeDNS(ctx, w, r) + if err != nil { + t.Fatal(err) + } + + if w.Msg.Rcode != tc.expectRcode { + t.Fatalf("expected rcode %v, got %v", tc.expectRcode, w.Msg.Rcode) + } + + ts := w.Msg.IsTsig() + + if ts == nil { + t.Fatal("expected TSIG in response") + } + + if int(ts.Error) != tc.expectError { + t.Errorf("expected TSIG error code %v, got %v", tc.expectError, ts.Error) + } + + if len(ts.OtherData)/2 != tc.expectOtherLength { + t.Errorf("expected Other of length %v, got %v", tc.expectOtherLength, len(ts.OtherData)) + } + + if int(ts.OtherLen) != tc.expectOtherLength { + t.Errorf("expected OtherLen %v, got %v", tc.expectOtherLength, ts.OtherLen) + } + + if ts.TimeSigned != uint64(tc.expectTimeSigned) { + t.Errorf("expected TimeSigned to be %v, got %v", tc.expectTimeSigned, ts.TimeSigned) + } + }) + } +} + +func testHandler() test.HandlerFunc { + return func(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { + state := request.Request{W: w, Req: r} + qname := state.Name() + m := new(dns.Msg) + rcode := dns.RcodeServerFailure + if qname == "test.example." { + m.SetReply(r) + rr := test.A("test.example. 300 IN A 1.2.3.48") + m.Answer = []dns.RR{rr} + m.Authoritative = true + rcode = dns.RcodeSuccess + } + m.SetRcode(r, rcode) + w.WriteMsg(m) + return rcode, nil + } +} + +// a test.ResponseWriter that always returns err as the TSIG status error +type ErrWriter struct { + err error + test.ResponseWriter +} + +// TsigStatus always returns an error. +func (t *ErrWriter) TsigStatus() error { return t.err } diff --git a/request/request.go b/request/request.go index 7731ff38b2f..9188888f507 100644 --- a/request/request.go +++ b/request/request.go @@ -313,7 +313,6 @@ func (r *Request) Class() string { } return dns.Class(r.Req.Question[0].Qclass).String() - } // QClass returns the class of the question in the request. @@ -327,7 +326,6 @@ func (r *Request) QClass() uint16 { } return r.Req.Question[0].Qclass - } // Clear clears all caching from Request s. diff --git a/test/file_loop_test.go b/test/file_loop_test.go index daebb70888f..2452cc0566b 100644 --- a/test/file_loop_test.go +++ b/test/file_loop_test.go @@ -44,6 +44,5 @@ func TestFileLoop(t *testing.T) { // For now: document current behavior in this test. if r.Rcode != dns.RcodeServerFailure { t.Errorf("Rcode should be dns.RcodeServerFailure: %d", r.Rcode) - } } diff --git a/test/grpc_test.go b/test/grpc_test.go index 55395758d8c..37504c930a5 100644 --- a/test/grpc_test.go +++ b/test/grpc_test.go @@ -9,6 +9,7 @@ import ( "github.com/miekg/dns" "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" ) func TestGrpc(t *testing.T) { @@ -24,7 +25,7 @@ func TestGrpc(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() - conn, err := grpc.DialContext(ctx, tcp, grpc.WithInsecure(), grpc.WithBlock()) + conn, err := grpc.DialContext(ctx, tcp, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock()) if err != nil { t.Fatalf("Expected no error but got: %s", err) } diff --git a/test/metric_naming_test.go b/test/metric_naming_test.go index e8aa585c1df..7fcfee52755 100644 --- a/test/metric_naming_test.go +++ b/test/metric_naming_test.go @@ -18,7 +18,6 @@ import ( ) func TestMetricNaming(t *testing.T) { - walker := validMetricWalker{} err := filepath.Walk("..", walker.walk) diff --git a/test/presubmit_test.go b/test/presubmit_test.go index b94beb31b04..79f612732eb 100644 --- a/test/presubmit_test.go +++ b/test/presubmit_test.go @@ -158,7 +158,6 @@ func (l logfmt) Visit(n ast.Node) ast.Visitor { for i, u := range bl.Value { // disregard " if i == 1 && !unicode.IsUpper(u) { - l.err = fmt.Errorf("test error message %s doesn't start with an uppercase", bl.Value) return nil } if i == 1 { diff --git a/test/readme_test.go b/test/readme_test.go index bc79f74655a..4012a2154ad 100644 --- a/test/readme_test.go +++ b/test/readme_test.go @@ -167,7 +167,6 @@ func sectionsFromReadme(readme string) error { return fmt.Errorf("Sections incomplete or ordered wrong: %q, want (at least): Name, Descripion, Syntax and Examples", readme) } return nil - } func create(c map[string]string) { diff --git a/test/reload_test.go b/test/reload_test.go index 4c67b671175..3c701635fd6 100644 --- a/test/reload_test.go +++ b/test/reload_test.go @@ -2,12 +2,17 @@ package test import ( "bytes" + "context" "fmt" "io" "net/http" "strings" "testing" + "github.com/coredns/caddy" + "github.com/coredns/coredns/core/dnsserver" + "github.com/coredns/coredns/plugin" + "github.com/miekg/dns" ) @@ -327,4 +332,61 @@ func TestMetricsAvailableAfterReloadAndFailedReload(t *testing.T) { // verify that metrics have not been pushed } +// TestReloadUnreadyPlugin tests that the ready plugin properly resets the list of readiness implementors during a reload. +// If it fails to do so, ready will respond with duplicate plugin names after a reload (e.g. in this test "unready,unready"). +func TestReloadUnreadyPlugin(t *testing.T) { + // Add/Register a perpetually unready plugin + dnsserver.Directives = append([]string{"unready"}, dnsserver.Directives...) + u := new(unready) + plugin.Register("unready", func(c *caddy.Controller) error { + dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler { + u.next = next + return u + }) + return nil + }) + + corefile := `.:0 { + unready + whoami + ready 127.0.0.1:53185 + }` + + coreInput := NewInput(corefile) + + c, err := CoreDNSServer(corefile) + if err != nil { + t.Fatalf("Could not get CoreDNS serving instance: %s", err) + } + + c1, err := c.Restart(coreInput) + if err != nil { + t.Fatal(err) + } + + resp, err := http.Get("http://127.0.0.1:53185/ready") + if err != nil { + t.Fatal(err) + } + bod, _ := io.ReadAll(resp.Body) + resp.Body.Close() + if string(bod) != u.Name() { + t.Errorf("Expected /ready endpoint response body %q, got %q", u.Name(), bod) + } + + c1.Stop() +} + +type unready struct { + next plugin.Handler +} + +func (u *unready) Ready() bool { return false } + +func (u *unready) Name() string { return "unready" } + +func (u *unready) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { + return u.next.ServeDNS(ctx, w, r) +} + const inUse = "address already in use" diff --git a/test/secondary_test.go b/test/secondary_test.go index c60aa03553e..8e99f0da0d4 100644 --- a/test/secondary_test.go +++ b/test/secondary_test.go @@ -214,5 +214,4 @@ www IN AAAA ::1 return } } - } diff --git a/test/tls_test.go b/test/tls_test.go new file mode 100644 index 00000000000..f302d51059c --- /dev/null +++ b/test/tls_test.go @@ -0,0 +1,46 @@ +package test + +import ( + "crypto/tls" + "testing" + + "github.com/miekg/dns" +) + +func TestDNSoverTLS(t *testing.T) { + corefile := `tls://.:1053 { + tls ../plugin/tls/test_cert.pem ../plugin/tls/test_key.pem + whoami + }` + qname := "example.com." + qtype := dns.TypeA + answerLength := 0 + + ex, _, tcp, err := CoreDNSServerAndPorts(corefile) + if err != nil { + t.Fatalf("Could not get CoreDNS serving instance: %s", err) + } + defer ex.Stop() + + m := new(dns.Msg) + m.SetQuestion(qname, qtype) + client := dns.Client{ + Net: "tcp-tls", + TLSConfig: &tls.Config{InsecureSkipVerify: true}, + } + r, _, err := client.Exchange(m, tcp) + + if err != nil { + t.Fatalf("Could not exchange msg: %s", err) + } + + if n := len(r.Answer); n != answerLength { + t.Fatalf("Expected %v answers, got %v", answerLength, n) + } + if n := len(r.Extra); n != 2 { + t.Errorf("Expected 2 RRs in additional section, but got %d", n) + } + if r.Rcode != dns.RcodeSuccess { + t.Errorf("Expected success but got %d", r.Rcode) + } +} diff --git a/test/tsig_test.go b/test/tsig_test.go new file mode 100644 index 00000000000..19d5d0379a3 --- /dev/null +++ b/test/tsig_test.go @@ -0,0 +1,166 @@ +package test + +import ( + "testing" + "time" + + "github.com/miekg/dns" +) + +var tsigKey = "tsig.key." +var tsigSecret = "i9M+00yrECfVZG2qCjr4mPpaGim/Bq+IWMiNrLjUO4Y=" + +var corefile = `.:0 { + tsig { + secret ` + tsigKey + ` ` + tsigSecret + ` + } + hosts { + 1.2.3.4 test + } + }` + +func TestTsig(t *testing.T) { + i, udp, _, err := CoreDNSServerAndPorts(corefile) + if err != nil { + t.Fatalf("Could not get CoreDNS serving instance: %s", err) + } + defer i.Stop() + + m := new(dns.Msg) + m.SetQuestion("test.", dns.TypeA) + m.SetTsig(tsigKey, dns.HmacSHA256, 300, time.Now().Unix()) + + client := dns.Client{Net: "udp", TsigSecret: map[string]string{tsigKey: tsigSecret}} + r, _, err := client.Exchange(m, udp) + if err != nil { + t.Fatalf("Could not send msg: %s", err) + } + if r.Rcode != dns.RcodeSuccess { + t.Fatalf("Rcode should be dns.RcodeSuccess") + } + tsig := r.IsTsig() + if tsig == nil { + t.Fatalf("Respose was not TSIG") + } + if tsig.Error != dns.RcodeSuccess { + t.Fatalf("TSIG Error code should be dns.RcodeSuccess") + } +} + +func TestTsigBadKey(t *testing.T) { + i, udp, _, err := CoreDNSServerAndPorts(corefile) + if err != nil { + t.Fatalf("Could not get CoreDNS serving instance: %s", err) + } + defer i.Stop() + + m := new(dns.Msg) + m.SetQuestion("test.", dns.TypeA) + m.SetTsig("bad.key.", dns.HmacSHA256, 300, time.Now().Unix()) + + // rename client key to a key name the server doesnt have + client := dns.Client{Net: "udp", TsigSecret: map[string]string{"bad.key.": tsigSecret}} + r, _, err := client.Exchange(m, udp) + + if err != dns.ErrAuth { + t.Fatalf("Expected \"dns: bad authentication\" error, got: %s", err) + } + if r.Rcode != dns.RcodeNotAuth { + t.Fatalf("Rcode should be dns.RcodeNotAuth") + } + tsig := r.IsTsig() + if tsig == nil { + t.Fatalf("Respose was not TSIG") + } + if tsig.Error != dns.RcodeBadKey { + t.Fatalf("TSIG Error code should be dns.RcodeBadKey") + } + if tsig.MAC != "" { + t.Fatalf("TSIG MAC should be empty") + } + if tsig.MACSize != 0 { + t.Fatalf("TSIG MACSize should be 0") + } + if tsig.TimeSigned != 0 { + t.Fatalf("TSIG TimeSigned should be 0") + } +} + +func TestTsigBadSig(t *testing.T) { + i, udp, _, err := CoreDNSServerAndPorts(corefile) + if err != nil { + t.Fatalf("Could not get CoreDNS serving instance: %s", err) + } + defer i.Stop() + + m := new(dns.Msg) + m.SetQuestion("test.", dns.TypeA) + m.SetTsig(tsigKey, dns.HmacSHA256, 300, time.Now().Unix()) + + // mangle the client secret so the sig wont match the server sig + client := dns.Client{Net: "udp", TsigSecret: map[string]string{tsigKey: "BADSIG00ECfVZG2qCjr4mPpaGim/Bq+IWMiNrLjUO4Y="}} + r, _, err := client.Exchange(m, udp) + + if err != dns.ErrAuth { + t.Fatalf("Expected \"dns: bad authentication\" error, got: %s", err) + } + if r.Rcode != dns.RcodeNotAuth { + t.Fatalf("Rcode should be dns.RcodeNotAuth") + } + tsig := r.IsTsig() + if tsig == nil { + t.Fatalf("Respose was not TSIG") + } + if tsig.Error != dns.RcodeBadSig { + t.Fatalf("TSIG Error code should be dns.RcodeBadSig") + } + if tsig.MAC != "" { + t.Fatalf("TSIG MAC should be empty") + } + if tsig.MACSize != 0 { + t.Fatalf("TSIG MACSize should be 0") + } + if tsig.TimeSigned != 0 { + t.Fatalf("TSIG TimeSigned should be 0") + } +} + +func TestTsigBadTime(t *testing.T) { + i, udp, _, err := CoreDNSServerAndPorts(corefile) + if err != nil { + t.Fatalf("Could not get CoreDNS serving instance: %s", err) + } + defer i.Stop() + + m := new(dns.Msg) + m.SetQuestion("test.", dns.TypeA) + + // set time to be older by > fudge seconds + m.SetTsig(tsigKey, dns.HmacSHA256, 300, time.Now().Unix()-600) + + client := dns.Client{Net: "udp", TsigSecret: map[string]string{tsigKey: tsigSecret}} + r, _, err := client.Exchange(m, udp) + + if err != dns.ErrAuth { + t.Fatalf("Expected \"dns: bad authentication\" error, got: %s", err) + } + if r.Rcode != dns.RcodeNotAuth { + t.Fatalf("Rcode should be dns.RcodeNotAuth") + } + tsig := r.IsTsig() + if tsig == nil { + t.Fatalf("Respose was not TSIG") + } + if tsig.Error != dns.RcodeBadTime { + t.Fatalf("TSIG Error code should be dns.RcodeBadTime") + } + if tsig.MAC == "" { + t.Fatalf("TSIG MAC should not be empty") + } + if tsig.MACSize != 32 { + t.Fatalf("TSIG MACSize should be 32") + } + if tsig.TimeSigned == 0 { + t.Fatalf("TSIG TimeSigned should not be 0") + } +}