diff --git a/.circleci/config.yml b/.circleci/config.yml index 117ac3f412..8e098fbbd6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -21,8 +21,8 @@ integrationDefaults: &integrationDefaults image: default working_directory: ~/go/src/${CIRCLE_PROJECT_USERNAME}/coredns environment: - - K8S_VERSION: v1.29.4 - - KIND_VERSION: v0.25.0 + - K8S_VERSION: v1.34.0 + - KIND_VERSION: v0.30.0 - KUBECONFIG: /home/circleci/.kube/kind-config-kind setupKubernetes: &setupKubernetes diff --git a/.codecov.yml b/.codecov.yml deleted file mode 100644 index 167f5636c6..0000000000 --- a/.codecov.yml +++ /dev/null @@ -1,8 +0,0 @@ -coverage: - status: - project: - default: - target: 50% - threshold: null - patch: false - changes: false diff --git a/.github/dependabot.yml b/.github/dependabot.yml index a13a068d51..cb8106955d 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,6 +5,8 @@ updates: directory: "/" schedule: interval: "weekly" + cooldown: + default-days: 7 groups: k8s.io: patterns: @@ -18,4 +20,6 @@ updates: directory: "/" schedule: interval: "weekly" + cooldown: + default-days: 7 open-pull-requests-limit: 20 diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml index c9397fc8ae..5b487e773b 100644 --- a/.github/workflows/cifuzz.yml +++ b/.github/workflows/cifuzz.yml @@ -25,7 +25,7 @@ jobs: fuzz-seconds: 600 dry-run: false - name: Upload Crash - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 if: failure() && steps.build.outcome == 'success' with: name: artifacts diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 30cd01710f..6d71b8b501 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -27,15 +27,17 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Initialize CodeQL - uses: github/codeql-action/init@3c3833e0f8c1c83d449a7478aa59c036a9165498 # v3.29.11 + uses: github/codeql-action/init@e46ed2cbd01164d986452f91f178727624ae40d7 # v4.35.3 with: languages: ${{ matrix.language }} - name: Autobuild - uses: github/codeql-action/autobuild@3c3833e0f8c1c83d449a7478aa59c036a9165498 # v3.29.11 + uses: github/codeql-action/autobuild@e46ed2cbd01164d986452f91f178727624ae40d7 # v4.35.3 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@3c3833e0f8c1c83d449a7478aa59c036a9165498 # v3.29.11 + uses: github/codeql-action/analyze@e46ed2cbd01164d986452f91f178727624ae40d7 # v4.35.3 diff --git a/.github/workflows/depsreview.yml b/.github/workflows/depsreview.yml index 9fc948280e..ba694c208b 100644 --- a/.github/workflows/depsreview.yml +++ b/.github/workflows/depsreview.yml @@ -9,6 +9,8 @@ jobs: runs-on: ubuntu-latest steps: - name: 'Checkout Repository' - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: 'Dependency Review' - uses: actions/dependency-review-action@bc41886e18ea39df68b1b1245f4184881938e050 + uses: actions/dependency-review-action@2031cfc080254a8a887f58cffee85186f0e49e48 # v4.9.0 diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 8037b5a4b3..e0137fb955 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -16,19 +16,22 @@ jobs: docker-release: runs-on: ubuntu-latest env: - DOCKER_LOGIN: ${{ secrets.DOCKERHUB_USERNAME }} - DOCKER_PASSWORD: ${{ secrets.DOCKERHUB_PASSWORD }} RELEASE: ${{ github.event.inputs.release || github.event.release.tag_name }} steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Build Docker Images run: make VERSION=${RELEASE:1} DOCKER=coredns -f Makefile.docker release - name: Show Docker Images run: docker images - name: Docker login - uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0 + uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_PASSWORD }} - name: Publish Docker Images run: make VERSION=${RELEASE:1} DOCKER=coredns -f Makefile.docker docker-push + env: + DOCKER_LOGIN: ${{ secrets.DOCKERHUB_USERNAME }} + DOCKER_PASSWORD: ${{ secrets.DOCKERHUB_PASSWORD }} diff --git a/.github/workflows/go.coverage.yml b/.github/workflows/go.coverage.yml deleted file mode 100644 index 2c249463da..0000000000 --- a/.github/workflows/go.coverage.yml +++ /dev/null @@ -1,34 +0,0 @@ -name: Go Coverage -on: [pull_request] -permissions: - contents: read - -jobs: - test: - name: Coverage - runs-on: ubuntu-latest - steps: - - name: Check out code - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - - name: Setup Go Version - run: echo "GO_VERSION=$(cat .go-version)" >> $GITHUB_ENV - - - name: Install Go - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 - with: - go-version: ${{ env.GO_VERSION }} - id: go - - - name: Build - run: go build -v ./... - - - name: Test With Coverage - run: | - go install github.com/fatih/faillint@c56e3ec6dbfc933bbeb884fd31f2bcd41f712657 # v1.15.0 - for d in request core coremain plugin test; do \ - ( cd $d; go test -coverprofile=cover.out -covermode=atomic -race ./...; [ -f cover.out ] && cat cover.out >> ../coverage.txt ); \ - done - - - name: Upload coverage to Codecov - uses: codecov/codecov-action@fdcc8476540edceab3de004e990f80d881c6cc00 # v5.5.0 diff --git a/.github/workflows/go.test.yml b/.github/workflows/go.test.yml index 06b9656952..6c92813df1 100644 --- a/.github/workflows/go.test.yml +++ b/.github/workflows/go.test.yml @@ -1,5 +1,9 @@ name: Go Tests -on: [push, pull_request] +on: + push: + branches: + - master + pull_request: permissions: contents: read @@ -9,21 +13,45 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out code - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: Install Go + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 + with: + go-version-file: .go-version + id: go + + - name: Build + run: go build -v ./... + + - name: Test + run: | + ( cd request; go test -race ./... ) + ( cd core; go test -race ./... ) + ( cd coremain; go test -race ./... ) - - name: Setup Go Version - run: echo "GO_VERSION=$(cat .go-version)" >> $GITHUB_ENV + test-windows: + name: Test Windows + runs-on: windows-latest + steps: + - name: Check out code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Install Go - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: - go-version: ${{ env.GO_VERSION }} + go-version-file: .go-version id: go - name: Build run: go build -v ./... - name: Test + shell: bash run: | ( cd request; go test -race ./... ) ( cd core; go test -race ./... ) @@ -34,15 +62,14 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out code - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - - name: Setup Go Version - run: echo "GO_VERSION=$(cat .go-version)" >> $GITHUB_ENV + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Install Go - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: - go-version: ${{ env.GO_VERSION }} + go-version-file: .go-version id: go - name: Build @@ -56,15 +83,14 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out code - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - - name: Setup Go Version - run: echo "GO_VERSION=$(cat .go-version)" >> $GITHUB_ENV + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Install Go - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: - go-version: ${{ env.GO_VERSION }} + go-version-file: .go-version id: go - name: Build @@ -80,7 +106,9 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out code - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Install dependencies run: sudo apt-get install make curl diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index 1ad2763ea3..fd60be0886 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -11,13 +11,13 @@ jobs: name: lint runs-on: ubuntu-latest steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - name: Setup Go Version - run: echo "GO_VERSION=$(cat .go-version)" >> $GITHUB_ENV - - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - go-version: ${{ env.GO_VERSION }} + persist-credentials: false + - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 + with: + go-version-file: .go-version - name: golangci-lint - uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 # v8.0.0 + uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9.2.0 with: - version: v2.4.0 + version: v2.11.4 diff --git a/.github/workflows/make.doc.yml b/.github/workflows/make.doc.yml index 471ca31bd7..dc359db7ed 100644 --- a/.github/workflows/make.doc.yml +++ b/.github/workflows/make.doc.yml @@ -12,35 +12,28 @@ jobs: runs-on: ubuntu-latest permissions: contents: write + pull-requests: write steps: - name: Checkout - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - - name: Setup Go Version - run: echo "GO_VERSION=$(cat .go-version)" >> $GITHUB_ENV + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Setup Go - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: - go-version: ${{ env.GO_VERSION }} + go-version-file: .go-version - name: Update Docs run: | bash -x -e ./.github/fixup_file_mtime.sh make -f Makefile.doc - - name: Set up Git - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - git config user.name "coredns[bot]" - git config user.email "bot@bot.coredns.io" - git remote set-url origin https://x-access-token:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git - - - name: Commit and push changes - run: | - git add . - if output=$(git status --porcelain) && [ ! -z "$output" ]; then - git commit -s -m 'auto make -f Makefile.doc' - git push - fi + - name: Create Pull Request + uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1 + with: + commit-message: 'auto make -f Makefile.doc' + title: 'Update generated documentation' + body: 'Automated doc generation via `make -f Makefile.doc`' + branch: bot/make-doc + committer: 'coredns[bot] ' + author: 'coredns[bot] ' + signoff: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d1b1b4bad4..ee19eddd2d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,14 +18,17 @@ jobs: contents: write steps: - name: Check out code - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: ${{ github.event.inputs.commit }} + persist-credentials: false - name: Set up info + env: + INPUT_COMMIT: ${{ github.event.inputs.commit }} run: | set -x -e VERSION=$(make -f Makefile.release version) - COMMIT=$(git rev-parse --quiet --verify ${{ github.event.inputs.commit }}) + COMMIT=$(git rev-parse --quiet --verify "${INPUT_COMMIT}") if [[ "$(git tag -l v${VERSION})" == "v${VERSION}" ]]; then echo "v${VERSION} already released" exit 1 @@ -36,19 +39,24 @@ jobs: - name: Build release binary run: make -f Makefile.release release - name: Build release binary sha256 - run: (cd release; for asset in `ls -A *tgz`; do sha256sum $asset > $asset.sha256; done) + run: (cd release; for asset in `ls -A *.tgz *.zip`; do sha256sum $asset > $asset.sha256; done) - name: Remove hidden section - run: sed '/+++/,//d' notes/coredns-${{ steps.info.outputs.version}}.md > release.md + env: + VERSION: ${{ steps.info.outputs.version }} + run: sed '/+++/,//d' "notes/coredns-${VERSION}.md" > release.md - name: Log release info + env: + COMMIT: ${{ steps.info.outputs.commit }} + VERSION: ${{ steps.info.outputs.version }} run: | set -x -e git log -1 - echo ${{ steps.info.outputs.commit }} - echo ${{ steps.info.outputs.version }} + echo "${COMMIT}" + echo "${VERSION}" cat release.md - sha256sum release/*.tgz + sha256sum release/*.tgz release/*.zip - name: Draft release - uses: softprops/action-gh-release@72f2c25fcb47643c292f7107632f7a47c1df5cd8 # v2.3.2 + uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0 with: body_path: release.md name: v${{ steps.info.outputs.version }} @@ -57,4 +65,6 @@ jobs: files: | release/*.tgz release/*.tgz.sha256 + release/*.zip + release/*.zip.sha256 draft: true diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index 68269a125e..907a19222a 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -23,12 +23,12 @@ jobs: steps: - name: "Checkout code" - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: "Run analysis" - uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde # v2.4.2 + uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3 with: results_file: results.sarif results_format: sarif @@ -43,7 +43,7 @@ jobs: # Upload the results as artifacts (optional). - name: "Upload artifact" - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: SARIF file path: results.sarif @@ -51,6 +51,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@3c3833e0f8c1c83d449a7478aa59c036a9165498 # v3.29.11 + uses: github/codeql-action/upload-sarif@e46ed2cbd01164d986452f91f178727624ae40d7 # v4.35.3 with: sarif_file: results.sarif diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index f2c3fa4f5f..ffcd7791f0 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@5bef64f19d7facfb25b37b414482c7164d639639 # v9.1.0 + - uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0 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/.github/workflows/trivy-scan.yaml b/.github/workflows/trivy-scan.yaml deleted file mode 100644 index 7a0bca8a32..0000000000 --- a/.github/workflows/trivy-scan.yaml +++ /dev/null @@ -1,33 +0,0 @@ -name: Trivy Nightly Scan -on: - schedule: - - cron: '0 2 * * 5' # Run at 2AM UTC on every Friday - -permissions: read-all -jobs: - nightly-scan: - name: Trivy Scan nightly - strategy: - fail-fast: false - matrix: - # It will test for only the latest version as older version is not maintained - versions: [latest] - permissions: - security-events: write # for github/codeql-action/upload-sarif to upload SARIF results - - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - name: Run Trivy vulnerability scanner - uses: aquasecurity/trivy-action@dc5a429b52fcf669ce959baa2c2dd26090d2a6c4 # master - with: - image-ref: 'docker.io/coredns/coredns:${{ matrix.versions }}' - severity: 'CRITICAL,HIGH' - format: 'sarif' - output: 'trivy-results.sarif' - - - name: Upload Trivy scan results to GitHub Security tab - uses: github/codeql-action/upload-sarif@3c3833e0f8c1c83d449a7478aa59c036a9165498 # v3.29.11 - with: - sarif_file: 'trivy-results.sarif' diff --git a/.github/workflows/verify-make-gen.yml b/.github/workflows/verify-make-gen.yml new file mode 100644 index 0000000000..0a2fee556f --- /dev/null +++ b/.github/workflows/verify-make-gen.yml @@ -0,0 +1,38 @@ +name: Verify Generated Files +on: + push: + branches: + - master + pull_request: +permissions: + contents: read + +jobs: + verify-generate: + name: Verify make gen + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 + with: + go-version-file: .go-version + - name: Run make gen + run: make gen + - name: Check for uncommitted changes + run: | + UNTRACKED=$(git ls-files --others --exclude-standard) + if [ -n "$UNTRACKED" ]; then + echo "ERROR: make gen produced untracked files:" + echo "$UNTRACKED" + echo "" + echo "Run 'make gen' and commit the result." + exit 1 + fi + if ! git diff --exit-code; then + echo "" + echo "ERROR: Generated files are out of date." + echo "Run 'make gen' and commit the result." + exit 1 + fi diff --git a/.github/workflows/yamllint.yml b/.github/workflows/yamllint.yml index 7f05d86ee3..0eb8bd9391 100644 --- a/.github/workflows/yamllint.yml +++ b/.github/workflows/yamllint.yml @@ -8,12 +8,12 @@ jobs: runs-on: ubuntu-latest steps: - name: 'Checkout' - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - name: 'Yamllint' - uses: karancode/yamllint-github-action@4052d365f09b8d34eb552c363d1141fd60e2aeb2 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - yamllint_file_or_dir: '.' - yamllint_strict: false - yamllint_comment: true - env: - GITHUB_ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }} + persist-credentials: false + - name: 'Setup uv' + uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 + - name: 'Yamllint' + run: | + uvx --from 'yamllint==1.38.0' yamllint . + echo "yamllint passed" diff --git a/.go-version b/.go-version index ad2191947f..f8f7381409 100644 --- a/.go-version +++ b/.go-version @@ -1 +1 @@ -1.25.0 +1.26.3 diff --git a/.golangci.yml b/.golangci.yml index 8a81385338..29eec3fe09 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -9,13 +9,21 @@ linters: enable: - canonicalheader - copyloopvar + - durationcheck + - godoclint + - gosec - govet - ineffassign - intrange + - modernize + - nakedret - nolintlint + - perfsprint + - prealloc - protogetter - staticcheck - thelper + - revive - unconvert - unused - usetesting @@ -28,7 +36,18 @@ linters: - common-false-positives - legacy - std-error-handling + rules: + # Exclude some linters from running on tests files. + - path: _test\.go + linters: + - perfsprint + - gosec settings: + govet: + enable: + - nilness + perfsprint: + error-format: false revive: rules: - name: blank-imports diff --git a/ADOPTERS.md b/ADOPTERS.md index 8e5eaa8bfc..639fd5f727 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -1,3 +1,5 @@ +> Note: Adopters list is kept in chronological order. + * [Qwilt](https://www.qwilt.com) uses CoreDNS in production, implementing a global DNS service allowing access to our ISP edge and regional locations. * [Infoblox](https://www.infoblox.com) uses CoreDNS in its Active Trust Cloud SaaS service, as well as for Kubernetes cluster DNS. * [Sky Betting & Gaming](https://engineering.skybettingandgaming.com) uses CoreDNS for Kubernetes cluster DNS. diff --git a/Dockerfile b/Dockerfile index 5202e8b7b4..140a30340e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,21 +1,14 @@ ARG DEBIAN_IMAGE=debian:stable-slim ARG BASE=gcr.io/distroless/static-debian12:nonroot -FROM --platform=$BUILDPLATFORM ${DEBIAN_IMAGE} AS build -SHELL [ "/bin/sh", "-ec" ] -RUN export DEBCONF_NONINTERACTIVE_SEEN=true \ - DEBIAN_FRONTEND=noninteractive \ - DEBIAN_PRIORITY=critical \ - TERM=linux ; \ - apt-get -qq update ; \ - apt-get -qq upgrade ; \ - apt-get -qq --no-install-recommends install ca-certificates libcap2-bin; \ - apt-get clean +FROM --platform=$BUILDPLATFORM ${DEBIAN_IMAGE} AS build +ARG DEBIAN_FRONTEND=noninteractive +RUN apt-get -qq update \ + && apt-get -qq --no-install-recommends install libcap2-bin COPY coredns /coredns RUN setcap cap_net_bind_service=+ep /coredns -FROM --platform=$TARGETPLATFORM ${BASE} -COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ +FROM ${BASE} COPY --from=build /coredns /coredns USER nonroot:nonroot # Reset the working directory inherited from the base image back to the expected default: diff --git a/Makefile b/Makefile index 8ee633c363..f591cdc438 100644 --- a/Makefile +++ b/Makefile @@ -4,10 +4,13 @@ BINARY:=coredns SYSTEM:= CHECKS:=check BUILDOPTS?=-v +GOTAGS?=grpcnotrace GOPATH?=$(HOME)/go MAKEPWD:=$(dir $(realpath $(firstword $(MAKEFILE_LIST)))) CGO_ENABLED?=0 GOLANG_VERSION ?= $(shell cat .go-version) +STRIP_FLAGS?=-s -w +LDFLAGS?=-ldflags="$(STRIP_FLAGS) -X github.com/coredns/coredns/coremain.GitCommit=$(GITCOMMIT)" export GOSUMDB = sum.golang.org export GOTOOLCHAIN = go$(GOLANG_VERSION) @@ -17,7 +20,7 @@ all: coredns .PHONY: coredns coredns: $(CHECKS) - CGO_ENABLED=$(CGO_ENABLED) $(SYSTEM) go build $(BUILDOPTS) -ldflags="-s -w -X github.com/coredns/coredns/coremain.GitCommit=$(GITCOMMIT)" -o $(BINARY) + CGO_ENABLED=$(CGO_ENABLED) $(SYSTEM) go build $(BUILDOPTS) -tags="$(GOTAGS)" $(LDFLAGS) -o $(BINARY) .PHONY: check check: core/plugin/zplugin.go core/dnsserver/zdirectives.go diff --git a/Makefile.doc b/Makefile.doc index 2adc143a34..8f5748d39e 100644 --- a/Makefile.doc +++ b/Makefile.doc @@ -3,7 +3,7 @@ # updated before doing a release. The Debian package, for instance, looks at these pages # and will install them on your system. -MMARK_VERSION:=2.2.4 +MMARK_VERSION:=2.2.47 PLUGINS:=$(wildcard plugin/*/README.md) READMES:=$(subst plugin/,,$(PLUGINS)) READMES:=$(subst /README.md,,$(READMES)) @@ -23,26 +23,39 @@ MMARK := $(FIRST_GOPATH)/bin/mmark -man MMARK_URL := https://github.com/mmarkdown/mmark/releases/download/v$(MMARK_VERSION)/mmark_$(MMARK_VERSION)_$(GO_BUILD_PLATFORM).tgz +# SHA-256 checksums for mmark v2.2.47 release tarballs. +# See: https://github.com/mmarkdown/mmark/releases/tag/v2.2.47 +MMARK_SHA256_darwin_amd64 := 941e963489e328c9da188629688438f9f3e93b6a4895c6ef943d55fbbfa84a3c +MMARK_SHA256_darwin_arm64 := 641cbc566edec29dd989ac15432b7f007be54ebc163cf6d98cd1d3df295ef9c4 +MMARK_SHA256_linux_amd64 := e0585cc9628c562e0f1be36e45221e84c6fc84957c999168ce4ba35594264ff2 +MMARK_SHA256_linux_arm := f6e1059c032b9ec0a74da40d8e901968d0bb41d92a160ef4782febbbf298d22d +MMARK_SHA256_linux_arm64 := 9a3c9b2e525b5addf97f968ec28829947520f628fdf4850134be784e220149ca +MMARK_SHA256_windows_amd64 := 9c73231e4427651d1f7911d4a9014aa2d87e735aac9c93e4e400061bc1de6714 +MMARK_SHA256 := $(MMARK_SHA256_$(GO_BUILD_PLATFORM)) + .PHONY: mmark mmark: $(MMARK_BIN) $(MMARK_BIN): $(eval MMARK_TMP := $(shell mktemp -d)) - curl -s -L $(MMARK_URL) | tar -xvzf - -C $(MMARK_TMP) + curl -s -L -o $(MMARK_TMP)/mmark.tgz $(MMARK_URL) + @if [ -z "$(MMARK_SHA256)" ]; then \ + echo "error: no known checksum for platform $(GO_BUILD_PLATFORM)" >&2; exit 1; \ + fi + @echo "$(MMARK_SHA256) $(MMARK_TMP)/mmark.tgz" | shasum -a 256 -c - + tar -xzf $(MMARK_TMP)/mmark.tgz -C $(MMARK_TMP) mkdir -p $(FIRST_GOPATH)/bin cp $(MMARK_TMP)/mmark $(FIRST_GOPATH)/bin/mmark rm -r $(MMARK_TMP) man/coredns.1: coredns.1.md - @/bin/echo -e '%%%\n title = "coredns 1"\n' \ - 'area = "CoreDNS"\n workgroup = "CoreDNS"\n%%%\n\n' > $@.header + @printf '%%%%%%\n title = "coredns 1"\n area = "CoreDNS"\n workgroup = "CoreDNS"\n%%%%%%\n\n' > $@.header @cat $@.header $< > $@.md && rm $@.header - @sed -i -e "s/@@PLUGINS@@/$(PLUGINS)/" $@.md + @sed -e "s/@@PLUGINS@@/$(PLUGINS)/" $@.md > $@.md.tmp && mv $@.md.tmp $@.md $(MMARK) $@.md > $@ && rm $@.md man/corefile.5: corefile.5.md - @/bin/echo -e '%%%\n title = "corefile 5"\n' \ - 'area = "CoreDNS"\n workgroup = "CoreDNS"\n%%%\n\n' > $@.header + @printf '%%%%%%\n title = "corefile 5"\n area = "CoreDNS"\n workgroup = "CoreDNS"\n%%%%%%\n\n' > $@.header @cat $@.header $< > $@.md && rm $@.header $(MMARK) $@.md > $@ && rm $@.md @@ -53,10 +66,9 @@ plugins: done man/coredns-%.7: plugin/%/README.md - @/bin/echo -e "%%%\n title = \"`basename $@ | sed s\/\.7\/\/` 7\"\n" \ - 'area = "CoreDNS"\n workgroup = "CoreDNS Plugins"\n%%%\n\n' > $@.header + @printf '%%%%%%\n title = "%s 7"\n area = "CoreDNS"\n workgroup = "CoreDNS Plugins"\n%%%%%%\n\n' "`basename $@ | sed s/\.7//`" > $@.header @cat $@.header $< > $@.md && rm $@.header - @sed -i '/^# .*/d' $@.md + @sed -e '/^# .*/d' $@.md > $@.md.tmp && mv $@.md.tmp $@.md $(MMARK) $@.md > $@ && rm $@.md PHONY: clean diff --git a/Makefile.release b/Makefile.release index bc4108fd50..94c8d41a55 100644 --- a/Makefile.release +++ b/Makefile.release @@ -85,6 +85,7 @@ tar: tar -zcf release/$(NAME)_$(VERSION)_darwin_amd64.tgz -C build/darwin/amd64 $(NAME) tar -zcf release/$(NAME)_$(VERSION)_darwin_arm64.tgz -C build/darwin/arm64 $(NAME) tar -zcf release/$(NAME)_$(VERSION)_windows_amd64.tgz -C build/windows/amd64 $(NAME).exe + zip -j release/$(NAME)_$(VERSION)_windows_amd64.zip build/windows/amd64/$(NAME).exe for arch in $(LINUX_ARCH); do \ tar -zcf release/$(NAME)_$(VERSION)_linux_$$arch.tgz -C build/linux/$$arch $(NAME) ;\ done @@ -97,15 +98,15 @@ else @echo Releasing: $(VERSION) @$(eval RELEASE:=$(shell curl -s -d '{"tag_name": "v$(VERSION)", "name": "v$(VERSION)"}' -H "Authorization: token ${GITHUB_ACCESS_TOKEN}" "https://api.github.com/repos/$(GITHUB)/$(NAME)/releases" | grep -m 1 '"id"' | tr -cd '[[:digit:]]')) @echo ReleaseID: $(RELEASE) - @( cd release; for asset in `ls -A *tgz`; do \ + @( cd release; for asset in `ls -A *.tgz *.zip`; do \ echo $$asset; \ curl -o /dev/null -X POST \ - -H "Content-Type: application/gzip" \ + -H "Content-Type: application/octet-stream" \ -H "Authorization: token ${GITHUB_ACCESS_TOKEN}" \ --data-binary "@$$asset" \ "https://uploads.github.com/repos/$(GITHUB)/$(NAME)/releases/$(RELEASE)/assets?name=$${asset}" ; \ done ) - @( cd release; for asset in `ls -A *tgz`; do \ + @( cd release; for asset in `ls -A *.tgz *.zip`; do \ sha256sum $$asset > $$asset.sha256; \ done ) @( cd release; for asset in `ls -A *sha256`; do \ diff --git a/README.md b/README.md index 9d60143042..00f3819be7 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,6 @@ ![CodeQL](https://github.com/coredns/coredns/actions/workflows/codeql-analysis.yml/badge.svg) ![Go Tests](https://github.com/coredns/coredns/actions/workflows/go.test.yml/badge.svg) [![CircleCI](https://circleci.com/gh/coredns/coredns.svg?style=shield)](https://circleci.com/gh/coredns/coredns) -[![Code Coverage](https://img.shields.io/codecov/c/github/coredns/coredns/master.svg)](https://codecov.io/github/coredns/coredns?branch=master) [![Docker Pulls](https://img.shields.io/docker/pulls/coredns/coredns.svg)](https://hub.docker.com/r/coredns/coredns) [![Go Report Card](https://goreportcard.com/badge/github.com/coredns/coredns)](https://goreportcard.com/report/coredns/coredns) [![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/1250/badge)](https://bestpractices.coreinfrastructure.org/projects/1250) @@ -23,6 +22,7 @@ CoreDNS can listen for DNS requests coming in over: * UDP/TCP (go'old DNS). * TLS - DoT ([RFC 7858](https://tools.ietf.org/html/rfc7858)). * DNS over HTTP/2 - DoH ([RFC 8484](https://tools.ietf.org/html/rfc8484)). +* DNS over HTTP/3 - DoH3 * DNS over QUIC - DoQ ([RFC 9250](https://tools.ietf.org/html/rfc9250)). * [gRPC](https://grpc.io) (not a standard). @@ -57,7 +57,7 @@ out-of-tree plugins. To compile CoreDNS, we assume you have a working Go setup. See various tutorials if you don’t have that already configured. -First, make sure your golang version is 1.24.0 or higher as `go mod` support and other api is needed. +First, make sure your golang version is 1.25.0 or higher as `go mod` support and other api is needed. See [here](https://github.com/golang/go/wiki/Modules) for `go mod` details. Then, check out the project and run `make` to compile the binary: @@ -79,11 +79,33 @@ setup a Go environment, you could build CoreDNS easily: ``` docker run --rm -i -t \ -v $PWD:/go/src/github.com/coredns/coredns -w /go/src/github.com/coredns/coredns \ - golang:1.24 sh -c 'GOFLAGS="-buildvcs=false" make gen && GOFLAGS="-buildvcs=false" make' + golang:1.25 sh -c 'GOFLAGS="-buildvcs=false" make gen && GOFLAGS="-buildvcs=false" make' ``` The above command alone will have `coredns` binary generated. +## Quick Start + +Create a minimal Corefile: + +```bash +cat > Corefile < 0 { - // This config has filters. Check for overlap with other (unfiltered) configs. - existZone, overlapZone = checker.check(akey) + // This config has filters (e.g. view plugin). It is allowed to + // share a zone/port with an unfiltered server block, so we only + // check without registering and skip the "already defined" error. + _, overlapZone = checker.check(akey) } else { - // This config has no filters. Check for overlap with other (unfiltered) configs, - // and register the zone to prevent subsequent zones from overlapping with it. + // This config has no filters. Check for overlap with other + // unfiltered configs and register the zone. existZone, overlapZone = checker.registerAndCheck(akey) } if existZone != nil { @@ -347,6 +351,13 @@ func makeServersForGroup(addr string, group []*Config) ([]caddy.Server, error) { return nil, err } servers = append(servers, s) + + case transport.HTTPS3: + s, err := NewServerHTTPS3(addr, group) + if err != nil { + return nil, err + } + servers = append(servers, s) } } return servers, nil diff --git a/core/dnsserver/server.go b/core/dnsserver/server.go index 2107e8d01b..942520ace2 100644 --- a/core/dnsserver/server.go +++ b/core/dnsserver/server.go @@ -4,8 +4,8 @@ package dnsserver import ( "context" "fmt" + "maps" "net" - "runtime" "runtime/debug" "strings" "sync" @@ -16,6 +16,7 @@ import ( "github.com/coredns/coredns/plugin/metrics/vars" "github.com/coredns/coredns/plugin/pkg/edns" "github.com/coredns/coredns/plugin/pkg/log" + cproxyproto "github.com/coredns/coredns/plugin/pkg/proxyproto" "github.com/coredns/coredns/plugin/pkg/rcode" "github.com/coredns/coredns/plugin/pkg/reuseport" "github.com/coredns/coredns/plugin/pkg/trace" @@ -24,6 +25,7 @@ import ( "github.com/miekg/dns" ot "github.com/opentracing/opentracing-go" + "github.com/pires/go-proxyproto" ) // Server represents an instance of a server, which serves @@ -32,23 +34,30 @@ import ( // the same address and the listener may be stopped for // graceful termination (POSIX only). type Server struct { - Addr string // Address we listen on + Addr string // Address we listen on + IdleTimeout time.Duration // Idle timeout for TCP + ReadTimeout time.Duration // Read timeout for TCP + WriteTimeout time.Duration // Write timeout for TCP + + connPolicy proxyproto.ConnPolicyFunc // Proxy Protocol connection policy function + udpSessionTrackingTTL time.Duration // TTL for UDP PPv2 session tracking (0 = disabled) + udpSessionTrackingMaxSessions int // LRU cap for UDP session tracking (0 = default) server [2]*dns.Server // 0 is a net.Listener, 1 is a net.PacketConn (a *UDPConn) in our case. m sync.Mutex // protects the servers zones map[string][]*Config // zones keyed by their address - dnsWg sync.WaitGroup // used to wait on outstanding connections 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 - idleTimeout time.Duration // Idle timeout for TCP - readTimeout time.Duration // Read timeout for TCP - writeTimeout time.Duration // Write timeout for TCP tsigSecret map[string]string + + // Ensure Stop is idempotent when invoked concurrently (e.g., during reload and SIGTERM). + stopOnce sync.Once + stopErr error } // MetadataCollector is a plugin that can retrieve metadata functions from all metadata providing plugins @@ -63,20 +72,12 @@ func NewServer(addr string, group []*Config) (*Server, error) { Addr: addr, zones: make(map[string][]*Config), graceTimeout: 5 * time.Second, - idleTimeout: 10 * time.Second, - readTimeout: 3 * time.Second, - writeTimeout: 5 * time.Second, + IdleTimeout: 10 * time.Second, + ReadTimeout: 3 * time.Second, + WriteTimeout: 5 * time.Second, tsigSecret: make(map[string]string), } - // We have to bound our wg with one increment - // to prevent a "race condition" that is hard-coded - // into sync.WaitGroup.Wait() - basically, an add - // with a positive delta must be guaranteed to - // occur before Wait() is called on the wg. - // In a way, this kind of acts as a safety barrier. - s.dnsWg.Add(1) - for _, site := range group { if site.Debug { s.debug = true @@ -89,19 +90,17 @@ func NewServer(addr string, group []*Config) (*Server, error) { // set timeouts if site.ReadTimeout != 0 { - s.readTimeout = site.ReadTimeout + s.ReadTimeout = site.ReadTimeout } if site.WriteTimeout != 0 { - s.writeTimeout = site.WriteTimeout + s.WriteTimeout = site.WriteTimeout } if site.IdleTimeout != 0 { - s.idleTimeout = site.IdleTimeout + s.IdleTimeout = site.IdleTimeout } // copy tsig secrets - for key, secret := range site.TsigSecret { - s.tsigSecret[key] = secret - } + maps.Copy(s.tsigSecret, site.TsigSecret) // compile custom plugin for everything var stack plugin.Handler @@ -130,6 +129,15 @@ func NewServer(addr string, group []*Config) (*Server, error) { } } site.pluginChain = stack + if site.ProxyProtoConnPolicy != nil { + s.connPolicy = site.ProxyProtoConnPolicy + } + if site.ProxyProtoUDPSessionTrackingTTL > 0 { + s.udpSessionTrackingTTL = site.ProxyProtoUDPSessionTrackingTTL + } + if site.ProxyProtoUDPSessionTrackingMaxSessions > 0 { + s.udpSessionTrackingMaxSessions = site.ProxyProtoUDPSessionTrackingMaxSessions + } } if !s.debug { @@ -152,10 +160,10 @@ func (s *Server) Serve(l net.Listener) error { Net: "tcp", TsigSecret: s.tsigSecret, MaxTCPQueries: tcpMaxQueries, - ReadTimeout: s.readTimeout, - WriteTimeout: s.writeTimeout, + ReadTimeout: s.ReadTimeout, + WriteTimeout: s.WriteTimeout, IdleTimeout: func() time.Duration { - return s.idleTimeout + return s.IdleTimeout }, Handler: dns.HandlerFunc(func(w dns.ResponseWriter, r *dns.Msg) { ctx := context.WithValue(context.Background(), Key{}, s) @@ -188,6 +196,9 @@ func (s *Server) Listen() (net.Listener, error) { if err != nil { return nil, err } + if s.connPolicy != nil { + l = &proxyproto.Listener{Listener: l, ConnPolicy: s.connPolicy} + } return l, nil } @@ -202,44 +213,40 @@ func (s *Server) ListenPacket() (net.PacketConn, error) { if err != nil { return nil, err } - + if s.connPolicy != nil { + p = &cproxyproto.PacketConn{PacketConn: p, ConnPolicy: s.connPolicy, UDPSessionTrackingTTL: s.udpSessionTrackingTTL, UDPSessionTrackingMaxSessions: s.udpSessionTrackingMaxSessions} + } return p, nil } -// Stop stops the server. It blocks until the server is -// totally stopped. On POSIX systems, it will wait for -// connections to close (up to a max timeout of a few -// seconds); on Windows it will close the listener -// immediately. +// Stop attempts to gracefully stop the server. +// It waits until the server is stopped and its connections are closed, +// up to a max timeout of a few seconds. If unsuccessful, an error is returned. +// // 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{}) - go func() { - s.dnsWg.Done() // decrement our initial increment used as a barrier - s.dnsWg.Wait() - close(done) - }() +func (s *Server) Stop() error { + s.stopOnce.Do(func() { + ctx, cancelCtx := context.WithTimeout(context.Background(), s.graceTimeout) + defer cancelCtx() + + var wg sync.WaitGroup + s.m.Lock() + for _, s1 := range s.server { + // We might not have started and initialized the full set of servers + if s1 == nil { + continue + } - // Wait for remaining connections to finish or - // force them all to close after timeout - select { - case <-time.After(s.graceTimeout): - case <-done: + wg.Go(func() { + s1.ShutdownContext(ctx) + }) } - } + s.m.Unlock() + wg.Wait() - // Close the listener now; this stops the server without delay - s.m.Lock() - for _, s1 := range s.server { - // We might not have started and initialized the full set of servers - if s1 != nil { - err = s1.Shutdown() - } - } - s.m.Unlock() - return + s.stopErr = ctx.Err() + }) + return s.stopErr } // Address together with Stop() implement caddy.GracefulServer. @@ -407,7 +414,7 @@ func (s *Server) Tracer() ot.Tracer { } // errorFunc responds to an DNS request with an error. -func errorFunc(server string, w dns.ResponseWriter, r *dns.Msg, rc int) { +func errorFunc(_server string, w dns.ResponseWriter, r *dns.Msg, rc int) { state := request.Request{W: w, Req: r} answer := new(dns.Msg) diff --git a/core/dnsserver/server_grpc.go b/core/dnsserver/server_grpc.go index cbff8c28a8..42f54eaff6 100644 --- a/core/dnsserver/server_grpc.go +++ b/core/dnsserver/server_grpc.go @@ -15,17 +15,36 @@ import ( "github.com/grpc-ecosystem/grpc-opentracing/go/otgrpc" "github.com/miekg/dns" "github.com/opentracing/opentracing-go" + "github.com/pires/go-proxyproto" + "golang.org/x/net/netutil" "google.golang.org/grpc" "google.golang.org/grpc/peer" ) +const ( + // maxDNSMessageBytes is the maximum size of a DNS message on the wire. + maxDNSMessageBytes = dns.MaxMsgSize + + // maxProtobufPayloadBytes accounts for protobuf overhead. + // Field tag=1 (1 byte) + length varint for 65535 (3 bytes) = 4 bytes total + maxProtobufPayloadBytes = maxDNSMessageBytes + 4 + + // DefaultGRPCMaxStreams is the default maximum number of concurrent streams per connection. + DefaultGRPCMaxStreams = 256 + + // DefaultGRPCMaxConnections is the default maximum number of concurrent connections. + DefaultGRPCMaxConnections = 200 +) + // ServergRPC represents an instance of a DNS-over-gRPC server. type ServergRPC struct { *Server *pb.UnimplementedDnsServiceServer - grpcServer *grpc.Server - listenAddr net.Addr - tlsConfig *tls.Config + grpcServer *grpc.Server + listenAddr net.Addr + tlsConfig *tls.Config + maxStreams int + maxConnections int } // NewServergRPC returns a new CoreDNS GRPC server and compiles all plugin in to it. @@ -49,7 +68,22 @@ func NewServergRPC(addr string, group []*Config) (*ServergRPC, error) { tlsConfig.NextProtos = []string{"h2"} } - return &ServergRPC{Server: s, tlsConfig: tlsConfig}, nil + maxStreams := DefaultGRPCMaxStreams + if len(group) > 0 && group[0] != nil && group[0].MaxGRPCStreams != nil { + maxStreams = *group[0].MaxGRPCStreams + } + + maxConnections := DefaultGRPCMaxConnections + if len(group) > 0 && group[0] != nil && group[0].MaxGRPCConnections != nil { + maxConnections = *group[0].MaxGRPCConnections + } + + return &ServergRPC{ + Server: s, + tlsConfig: tlsConfig, + maxStreams: maxStreams, + maxConnections: maxConnections, + }, nil } // Compile-time check to ensure ServergRPC implements the caddy.GracefulServer interface @@ -61,26 +95,41 @@ func (s *ServergRPC) Serve(l net.Listener) error { s.listenAddr = l.Addr() s.m.Unlock() + serverOpts := []grpc.ServerOption{ + grpc.MaxRecvMsgSize(maxProtobufPayloadBytes), + grpc.MaxSendMsgSize(maxProtobufPayloadBytes), + } + + // Only set MaxConcurrentStreams if not unbounded (0) + if s.maxStreams > 0 { + serverOpts = append(serverOpts, grpc.MaxConcurrentStreams(uint32(s.maxStreams))) // #nosec G115 -- maxStreams is bounded + } + if s.Tracer() != nil { - onlyIfParent := func(parentSpanCtx opentracing.SpanContext, method string, req, resp interface{}) bool { + onlyIfParent := func(parentSpanCtx opentracing.SpanContext, _method string, _req, _resp any) bool { return parentSpanCtx != nil } - intercept := otgrpc.OpenTracingServerInterceptor(s.Tracer(), otgrpc.IncludingSpans(onlyIfParent)) - s.grpcServer = grpc.NewServer(grpc.UnaryInterceptor(intercept)) - } else { - s.grpcServer = grpc.NewServer() + serverOpts = append(serverOpts, grpc.UnaryInterceptor(otgrpc.OpenTracingServerInterceptor(s.Tracer(), otgrpc.IncludingSpans(onlyIfParent)))) } + s.grpcServer = grpc.NewServer(serverOpts...) + pb.RegisterDnsServiceServer(s.grpcServer, s) if s.tlsConfig != nil { l = tls.NewListener(l, s.tlsConfig) } + + // Wrap listener to limit concurrent connections + if s.maxConnections > 0 { + l = netutil.LimitListener(l, s.maxConnections) + } + return s.grpcServer.Serve(l) } // ServePacket implements caddy.UDPServer interface. -func (s *ServergRPC) ServePacket(p net.PacketConn) error { return nil } +func (s *ServergRPC) ServePacket(_p net.PacketConn) error { return nil } // Listen implements caddy.TCPServer interface. func (s *ServergRPC) Listen() (net.Listener, error) { @@ -88,6 +137,9 @@ func (s *ServergRPC) Listen() (net.Listener, error) { if err != nil { return nil, err } + if s.connPolicy != nil { + l = &proxyproto.Listener{Listener: l, ConnPolicy: s.connPolicy} + } return l, nil } @@ -122,6 +174,9 @@ func (s *ServergRPC) Stop() (err error) { // any normal server. We use a custom responseWriter to pick up the bytes we need to write // back to the client as a protobuf. func (s *ServergRPC) Query(ctx context.Context, in *pb.DnsPacket) (*pb.DnsPacket, error) { + if len(in.GetMsg()) > dns.MaxMsgSize { + return nil, fmt.Errorf("dns message exceeds size limit: %d", len(in.GetMsg())) + } msg := new(dns.Msg) err := msg.Unpack(in.GetMsg()) if err != nil { @@ -140,6 +195,16 @@ func (s *ServergRPC) Query(ctx context.Context, in *pb.DnsPacket) (*pb.DnsPacket w := &gRPCresponse{localAddr: s.listenAddr, remoteAddr: a, Msg: msg} + if tsig := msg.IsTsig(); tsig != nil { + if s.tsigSecret == nil { + w.tsigStatus = dns.ErrSecret + } else if secret, ok := s.tsigSecret[tsig.Hdr.Name]; !ok { + w.tsigStatus = dns.ErrSecret + } else { + w.tsigStatus = dns.TsigVerify(in.GetMsg(), secret, "", false) + } + } + dnsCtx := context.WithValue(ctx, Key{}, s.Server) dnsCtx = context.WithValue(dnsCtx, LoopKey{}, 0) s.ServeDNS(dnsCtx, w, msg) @@ -164,6 +229,7 @@ type gRPCresponse struct { localAddr net.Addr remoteAddr net.Addr Msg *dns.Msg + tsigStatus error } // Write is the hack that makes this work. It does not actually write the message @@ -175,9 +241,10 @@ func (r *gRPCresponse) Write(b []byte) (int, error) { } // These methods implement the dns.ResponseWriter interface from Go DNS. + func (r *gRPCresponse) Close() error { return nil } -func (r *gRPCresponse) TsigStatus() error { return nil } -func (r *gRPCresponse) TsigTimersOnly(b bool) {} +func (r *gRPCresponse) TsigStatus() error { return r.tsigStatus } +func (r *gRPCresponse) TsigTimersOnly(_b bool) {} func (r *gRPCresponse) Hijack() {} func (r *gRPCresponse) LocalAddr() net.Addr { return r.localAddr } func (r *gRPCresponse) RemoteAddr() net.Addr { return r.remoteAddr } diff --git a/core/dnsserver/server_grpc_test.go b/core/dnsserver/server_grpc_test.go index 5dc72b55bd..3a74b1677a 100644 --- a/core/dnsserver/server_grpc_test.go +++ b/core/dnsserver/server_grpc_test.go @@ -3,8 +3,10 @@ package dnsserver import ( "context" "crypto/tls" + "errors" "net" "testing" + "time" "github.com/coredns/coredns/pb" "github.com/coredns/coredns/plugin/pkg/transport" @@ -69,6 +71,61 @@ func TestNewServergRPCWithTLS(t *testing.T) { } } +func TestNewServergRPCWithCustomLimits(t *testing.T) { + config := testConfig("grpc", testPlugin{}) + maxStreams := 50 + maxConnections := 100 + config.MaxGRPCStreams = &maxStreams + config.MaxGRPCConnections = &maxConnections + + server, err := NewServergRPC("127.0.0.1:0", []*Config{config}) + if err != nil { + t.Fatalf("NewServergRPC() with custom limits failed: %v", err) + } + + if server.maxStreams != maxStreams { + t.Errorf("Expected maxStreams = %d, got %d", maxStreams, server.maxStreams) + } + + if server.maxConnections != maxConnections { + t.Errorf("Expected maxConnections = %d, got %d", maxConnections, server.maxConnections) + } +} + +func TestNewServergRPCDefaults(t *testing.T) { + server, err := NewServergRPC("127.0.0.1:0", []*Config{testConfig("grpc", testPlugin{})}) + if err != nil { + t.Fatalf("NewServergRPC() failed: %v", err) + } + + if server.maxStreams != DefaultGRPCMaxStreams { + t.Errorf("Expected default maxStreams = %d, got %d", DefaultGRPCMaxStreams, server.maxStreams) + } + + if server.maxConnections != DefaultGRPCMaxConnections { + t.Errorf("Expected default maxConnections = %d, got %d", DefaultGRPCMaxConnections, server.maxConnections) + } +} + +func TestNewServergRPCZeroLimits(t *testing.T) { + config := testConfig("grpc", testPlugin{}) + zero := 0 + config.MaxGRPCStreams = &zero + config.MaxGRPCConnections = &zero + + server, err := NewServergRPC("127.0.0.1:0", []*Config{config}) + if err != nil { + t.Fatalf("NewServergRPC() with zero limits failed: %v", err) + } + + if server.maxStreams != 0 { + t.Errorf("Expected maxStreams = 0, got %d", server.maxStreams) + } + if server.maxConnections != 0 { + t.Errorf("Expected maxConnections = 0, got %d", server.maxConnections) + } +} + func TestServergRPC_Listen(t *testing.T) { server, err := NewServergRPC(transport.GRPC+"://127.0.0.1:0", []*Config{testConfig("grpc", testPlugin{})}) if err != nil { @@ -328,3 +385,236 @@ func TestGRPCResponse_WriteInvalidMessage(t *testing.T) { t.Error("Write() should return error for invalid DNS message") } } + +func TestServergRPC_Query_LargeMessage(t *testing.T) { + server, err := NewServergRPC("127.0.0.1:0", []*Config{testConfig("grpc", testPlugin{})}) + if err != nil { + t.Fatalf("NewServergRPC failed: %v", err) + } + + // Create oversized message (> dns.MaxMsgSize = 65535) + oversizedMsg := make([]byte, dns.MaxMsgSize+1) + dnsPacket := &pb.DnsPacket{Msg: oversizedMsg} + + tcpAddr, _ := net.ResolveTCPAddr("tcp", "127.0.0.1:12345") + p := &peer.Peer{Addr: tcpAddr} + ctx := peer.NewContext(context.Background(), p) + + server.listenAddr = tcpAddr + + _, err = server.Query(ctx, dnsPacket) + if err == nil { + t.Error("Expected error for oversized message") + } + + expectedError := "dns message exceeds size limit: 65536" + if err.Error() != expectedError { + t.Errorf("Expected error '%s', got '%s'", expectedError, err.Error()) + } +} + +func TestServergRPC_Query_MaxSizeMessage(t *testing.T) { + server, err := NewServergRPC("127.0.0.1:0", []*Config{testConfig("grpc", testPlugin{})}) + if err != nil { + t.Fatalf("NewServergRPC failed: %v", err) + } + + // Create message exactly at the size limit (dns.MaxMsgSize = 65535) + msg := new(dns.Msg) + msg.SetQuestion("example.com.", dns.TypeA) + packed, err := msg.Pack() + if err != nil { + t.Fatalf("Failed to pack DNS message: %v", err) + } + + // Pad the message to exactly max size + if len(packed) > dns.MaxMsgSize { + t.Fatalf("Packed message is already larger than max size: %d", len(packed)) + } + + maxSizeMsg := make([]byte, dns.MaxMsgSize) + copy(maxSizeMsg, packed) + + dnsPacket := &pb.DnsPacket{Msg: maxSizeMsg} + + tcpAddr, _ := net.ResolveTCPAddr("tcp", "127.0.0.1:12345") + p := &peer.Peer{Addr: tcpAddr} + ctx := peer.NewContext(context.Background(), p) + + server.listenAddr = tcpAddr + + // Should not return an error for exactly max size message + _, err = server.Query(ctx, dnsPacket) + if err != nil { + t.Errorf("Expected no error for max size message, got: %v", err) + } +} + +func TestGRPCResponseTsigStatusReturnsStoredStatus(t *testing.T) { + want := errors.New("bad tsig") + + r := &gRPCresponse{ + tsigStatus: want, + } + + if got := r.TsigStatus(); got != want { + t.Fatalf("TsigStatus() = %v, want %v", got, want) + } +} + +func TestServergRPC_Query_TSIGBadSigSetsTsigStatus(t *testing.T) { + const keyName = "tsig-key." + const clientSecret = "MTIzNDU2Nzg5MDEyMzQ1Ng==" + const serverSecret = "QUJDREVGR0hJSktMTU5PUA==" + + called := make(chan struct{}, 1) + + server, err := NewServergRPC("127.0.0.1:0", []*Config{ + testConfig("grpc", tsigStatusCheckPlugin{ + t: t, + called: called, + check: func(t *testing.T, got error) { + t.Helper() + if got == nil { + t.Fatal("TsigStatus() = nil, want non-nil for bad TSIG MAC") + } + if errors.Is(got, dns.ErrSecret) { + t.Fatalf("TsigStatus() = %v, want signature verification error, not ErrSecret", got) + } + if errors.Is(got, dns.ErrTime) { + t.Fatalf("TsigStatus() = %v, want signature verification error, not ErrTime", got) + } + }, + }), + }) + if err != nil { + t.Fatalf("NewServergRPC() failed: %v", err) + } + + server.tsigSecret = map[string]string{ + keyName: serverSecret, + } + + tcpAddr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:12345") + if err != nil { + t.Fatalf("ResolveTCPAddr() failed: %v", err) + } + ctx := peer.NewContext(context.Background(), &peer.Peer{Addr: tcpAddr}) + server.listenAddr = tcpAddr + + wire := mustPackSignedTSIGQuery(t, keyName, clientSecret, time.Now().Unix()) + + _, err = server.Query(ctx, &pb.DnsPacket{Msg: wire}) + if err != nil { + t.Fatalf("Query() failed: %v", err) + } + + select { + case <-called: + default: + t.Fatal("ServeDNS() was not called") + } +} + +func TestServergRPC_Query_TSIGBadTimeSetsTsigStatus(t *testing.T) { + const keyName = "tsig-key." + const secret = "MTIzNDU2Nzg5MDEyMzQ1Ng==" + + called := make(chan struct{}, 1) + + server, err := NewServergRPC("127.0.0.1:0", []*Config{ + testConfig("grpc", tsigStatusCheckPlugin{ + t: t, + called: called, + check: func(t *testing.T, got error) { + t.Helper() + if !errors.Is(got, dns.ErrTime) { + t.Fatalf("TsigStatus() = %v, want %v", got, dns.ErrTime) + } + }, + }), + }) + if err != nil { + t.Fatalf("NewServergRPC() failed: %v", err) + } + + server.tsigSecret = map[string]string{ + keyName: secret, + } + + tcpAddr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:12345") + if err != nil { + t.Fatalf("ResolveTCPAddr() failed: %v", err) + } + ctx := peer.NewContext(context.Background(), &peer.Peer{Addr: tcpAddr}) + server.listenAddr = tcpAddr + + wire := mustPackSignedTSIGQuery(t, keyName, secret, time.Now().Add(-10*time.Minute).Unix()) + + _, err = server.Query(ctx, &pb.DnsPacket{Msg: wire}) + if err != nil { + t.Fatalf("Query() failed: %v", err) + } + + select { + case <-called: + default: + t.Fatal("ServeDNS() was not called") + } +} + +func TestServergRPC_Query_TSIGValidLeavesTsigStatusNil(t *testing.T) { + const keyName = "tsig-key." + const secret = "MTIzNDU2Nzg5MDEyMzQ1Ng==" + + called := make(chan struct{}, 1) + + server, err := NewServergRPC("127.0.0.1:0", []*Config{ + testConfig("grpc", tsigStatusCheckPlugin{ + t: t, + called: called, + check: func(t *testing.T, got error) { + t.Helper() + if got != nil { + t.Fatalf("TsigStatus() = %v, want nil", got) + } + }, + }), + }) + if err != nil { + t.Fatalf("NewServergRPC() failed: %v", err) + } + + server.tsigSecret = map[string]string{ + keyName: secret, + } + + tcpAddr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:12345") + if err != nil { + t.Fatalf("ResolveTCPAddr() failed: %v", err) + } + ctx := peer.NewContext(context.Background(), &peer.Peer{Addr: tcpAddr}) + server.listenAddr = tcpAddr + + wire := mustPackSignedTSIGQuery(t, keyName, secret, time.Now().Unix()) + + resp, err := server.Query(ctx, &pb.DnsPacket{Msg: wire}) + if err != nil { + t.Fatalf("Query() failed: %v", err) + } + + select { + case <-called: + default: + t.Fatal("ServeDNS() was not called") + } + + if len(resp.GetMsg()) == 0 { + t.Fatal("Query() returned empty message") + } + + respMsg := new(dns.Msg) + if err := respMsg.Unpack(resp.GetMsg()); err != nil { + t.Fatalf("Failed to unpack response message: %v", err) + } +} diff --git a/core/dnsserver/server_https.go b/core/dnsserver/server_https.go index d4cb64be9c..2f0746b0d6 100644 --- a/core/dnsserver/server_https.go +++ b/core/dnsserver/server_https.go @@ -18,15 +18,25 @@ import ( "github.com/coredns/coredns/plugin/pkg/response" "github.com/coredns/coredns/plugin/pkg/reuseport" "github.com/coredns/coredns/plugin/pkg/transport" + + "github.com/miekg/dns" + "github.com/pires/go-proxyproto" + "golang.org/x/net/netutil" +) + +const ( + // DefaultHTTPSMaxConnections is the default maximum number of concurrent connections. + DefaultHTTPSMaxConnections = 200 ) // ServerHTTPS represents an instance of a DNS-over-HTTPS server. type ServerHTTPS struct { *Server - httpsServer *http.Server - listenAddr net.Addr - tlsConfig *tls.Config - validRequest func(*http.Request) bool + httpsServer *http.Server + listenAddr net.Addr + tlsConfig *tls.Config + validRequest func(*http.Request) bool + maxConnections int } // loggerAdapter is a simple adapter around CoreDNS logger made to implement io.Writer in order to log errors from HTTP server @@ -38,9 +48,13 @@ func (l *loggerAdapter) Write(p []byte) (n int, err error) { return len(p), nil } -// HTTPRequestKey is the context key for the current processed HTTP request (if current processed request was done over DOH) +// HTTPRequestKey is the context key for the HTTP request when processing DNS-over-HTTPS. +// Plugins can access the original HTTP request to retrieve headers, client IP, and metadata. type HTTPRequestKey struct{} +// connAddrKey is the context key for the per-connection local address set by ConnContext. +type connAddrKey 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) @@ -75,13 +89,25 @@ func NewServerHTTPS(addr string, group []*Config) (*ServerHTTPS, error) { } srv := &http.Server{ - ReadTimeout: s.readTimeout, - WriteTimeout: s.writeTimeout, - IdleTimeout: s.idleTimeout, + ReadTimeout: s.ReadTimeout, + WriteTimeout: s.WriteTimeout, + IdleTimeout: s.IdleTimeout, ErrorLog: stdlog.New(&loggerAdapter{}, "", 0), + ConnContext: func(ctx context.Context, c net.Conn) context.Context { + return context.WithValue(ctx, connAddrKey{}, c.LocalAddr()) + }, + } + maxConnections := DefaultHTTPSMaxConnections + if len(group) > 0 && group[0] != nil && group[0].MaxHTTPSConnections != nil { + maxConnections = *group[0].MaxHTTPSConnections } + sh := &ServerHTTPS{ - Server: s, tlsConfig: tlsConfig, httpsServer: srv, validRequest: validator, + Server: s, + tlsConfig: tlsConfig, + httpsServer: srv, + validRequest: validator, + maxConnections: maxConnections, } sh.httpsServer.Handler = sh @@ -97,14 +123,20 @@ func (s *ServerHTTPS) Serve(l net.Listener) error { s.listenAddr = l.Addr() s.m.Unlock() + // Wrap listener to limit concurrent connections (before TLS) + if s.maxConnections > 0 { + l = netutil.LimitListener(l, s.maxConnections) + } + if s.tlsConfig != nil { l = tls.NewListener(l, s.tlsConfig) } + return s.httpsServer.Serve(l) } // ServePacket implements caddy.UDPServer interface. -func (s *ServerHTTPS) ServePacket(p net.PacketConn) error { return nil } +func (s *ServerHTTPS) ServePacket(_p net.PacketConn) error { return nil } // Listen implements caddy.TCPServer interface. func (s *ServerHTTPS) Listen() (net.Listener, error) { @@ -112,6 +144,9 @@ func (s *ServerHTTPS) Listen() (net.Listener, error) { if err != nil { return nil, err } + if s.connPolicy != nil { + l = &proxyproto.Listener{Listener: l, ConnPolicy: s.connPolicy} + } return l, nil } @@ -141,6 +176,14 @@ func (s *ServerHTTPS) Stop() error { return nil } +// localAddr returns the per-connection local address from context, or s.listenAddr as fallback. +func (s *ServerHTTPS) localAddr(r *http.Request) net.Addr { + if addr, ok := r.Context().Value(connAddrKey{}).(net.Addr); ok { + return addr + } + return s.listenAddr +} + // 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) { @@ -150,7 +193,7 @@ func (s *ServerHTTPS) ServeHTTP(w http.ResponseWriter, r *http.Request) { return } - msg, err := doh.RequestToMsg(r) + msg, raw, err := doh.RequestToMsgWire(r) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) s.countResponse(http.StatusBadRequest) @@ -161,14 +204,28 @@ func (s *ServerHTTPS) ServeHTTP(w http.ResponseWriter, r *http.Request) { h, p, _ := net.SplitHostPort(r.RemoteAddr) port, _ := strconv.Atoi(p) dw := &DoHWriter{ - laddr: s.listenAddr, + laddr: s.localAddr(r), raddr: &net.TCPAddr{IP: net.ParseIP(h), Port: port}, request: r, } + if tsig := msg.IsTsig(); tsig != nil { + if s.tsigSecret == nil { + dw.tsigStatus = dns.ErrSecret + } else if secret, ok := s.tsigSecret[tsig.Hdr.Name]; !ok { + dw.tsigStatus = dns.ErrSecret + } else { + dw.tsigStatus = dns.TsigVerify(raw, secret, "", false) + } + } + // We just call the normal chain handler - all error handling is done there. // We should expect a packet to be returned that we can send to the client. - ctx := context.WithValue(context.Background(), Key{}, s.Server) + + // Propagate HTTP request context to DNS processing chain. This ensures that + // HTTP request timeouts, cancellations, and other context values are properly + // inherited by the DNS processing pipeline. + ctx := context.WithValue(r.Context(), Key{}, s.Server) ctx = context.WithValue(ctx, LoopKey{}, 0) ctx = context.WithValue(ctx, HTTPRequestKey{}, r) s.ServeDNS(ctx, dw, msg) diff --git a/core/dnsserver/server_https3.go b/core/dnsserver/server_https3.go new file mode 100644 index 0000000000..597f0e529c --- /dev/null +++ b/core/dnsserver/server_https3.go @@ -0,0 +1,229 @@ +package dnsserver + +import ( + "context" + "crypto/tls" + "fmt" + "net" + "net/http" + "strconv" + "time" + + "github.com/coredns/caddy" + "github.com/coredns/coredns/plugin/metrics/vars" + "github.com/coredns/coredns/plugin/pkg/dnsutil" + "github.com/coredns/coredns/plugin/pkg/doh" + cproxyproto "github.com/coredns/coredns/plugin/pkg/proxyproto" + "github.com/coredns/coredns/plugin/pkg/response" + "github.com/coredns/coredns/plugin/pkg/reuseport" + "github.com/coredns/coredns/plugin/pkg/transport" + + "github.com/miekg/dns" + "github.com/quic-go/quic-go" + "github.com/quic-go/quic-go/http3" +) + +const ( + // DefaultHTTPS3MaxStreams is the default maximum number of concurrent QUIC streams per connection. + DefaultHTTPS3MaxStreams = 256 +) + +// ServerHTTPS3 represents a DNS-over-HTTP/3 server. +type ServerHTTPS3 struct { + *Server + httpsServer *http3.Server + listenAddr net.Addr + tlsConfig *tls.Config + quicConfig *quic.Config + validRequest func(*http.Request) bool + maxStreams int +} + +// NewServerHTTPS3 builds the HTTP/3 (DoH3) server. +func NewServerHTTPS3(addr string, group []*Config) (*ServerHTTPS3, error) { + s, err := NewServer(addr, group) + if err != nil { + return nil, err + } + + // Extract TLS config (CoreDNS guarantees it is consistent) + var tlsConfig *tls.Config + for _, z := range s.zones { + for _, conf := range z { + tlsConfig = conf.TLSConfig + } + } + if tlsConfig == nil { + return nil, fmt.Errorf("DoH3 requires TLS, no TLS config found") + } + + // HTTP/3 requires ALPN "h3" + tlsConfig.NextProtos = []string{"h3"} + + // Request validator + var validator func(*http.Request) bool + for _, z := range s.zones { + for _, conf := range z { + validator = conf.HTTPRequestValidateFunc + } + } + if validator == nil { + validator = func(r *http.Request) bool { return r.URL.Path == doh.Path } + } + + maxStreams := DefaultHTTPS3MaxStreams + if len(group) > 0 && group[0] != nil && group[0].MaxHTTPS3Streams != nil { + maxStreams = *group[0].MaxHTTPS3Streams + } + + // QUIC transport config with stream limits (0 means use QUIC default) + qconf := &quic.Config{ + MaxIdleTimeout: s.IdleTimeout, + Allow0RTT: true, + } + if maxStreams > 0 { + qconf.MaxIncomingStreams = int64(maxStreams) + qconf.MaxIncomingUniStreams = int64(maxStreams) + } + + h3srv := &http3.Server{ + Handler: nil, // set after constructing ServerHTTPS3 + TLSConfig: tlsConfig, + EnableDatagrams: true, + QUICConfig: qconf, + // Logger: stdlog.New(&loggerAdapter{}, "", 0), TODO: Fix it + } + + sh := &ServerHTTPS3{ + Server: s, + tlsConfig: tlsConfig, + httpsServer: h3srv, + quicConfig: qconf, + validRequest: validator, + maxStreams: maxStreams, + } + + h3srv.Handler = sh + + return sh, nil +} + +var _ caddy.GracefulServer = &ServerHTTPS3{} + +// ListenPacket opens the UDP socket for QUIC. +func (s *ServerHTTPS3) ListenPacket() (net.PacketConn, error) { + p, err := reuseport.ListenPacket("udp", s.Addr[len(transport.HTTPS3+"://"):]) + if err != nil { + return nil, err + } + if s.connPolicy != nil { + p = &cproxyproto.PacketConn{PacketConn: p, ConnPolicy: s.connPolicy} + } + return p, nil +} + +// ServePacket starts serving QUIC+HTTP/3 on an existing UDP socket. +func (s *ServerHTTPS3) ServePacket(pc net.PacketConn) error { + s.m.Lock() + s.listenAddr = pc.LocalAddr() + s.m.Unlock() + // Serve HTTP/3 over QUIC + return s.httpsServer.Serve(pc) +} + +// Listen function not used in HTTP/3, but defined for compatibility +func (s *ServerHTTPS3) Listen() (net.Listener, error) { return nil, nil } +func (s *ServerHTTPS3) Serve(_l net.Listener) error { return nil } + +// OnStartupComplete lists the sites served by this server +// and any relevant information, assuming Quiet is false. +func (s *ServerHTTPS3) OnStartupComplete() { + if Quiet { + return + } + out := startUpZones(transport.HTTPS3+"://", s.Addr, s.zones) + if out != "" { + fmt.Print(out) + } +} + +// Stop graceful shutdown. It blocks until the server is totally stopped. +func (s *ServerHTTPS3) Stop() error { + s.m.Lock() + defer s.m.Unlock() + if s.httpsServer != nil { + return s.httpsServer.Shutdown(context.Background()) + } + return nil +} + +// Shutdown stops the server (non gracefully). +func (s *ServerHTTPS3) Shutdown() error { + if s.httpsServer != nil { + s.httpsServer.Shutdown(context.Background()) + } + return nil +} + +// ServeHTTP is the handler for the DoH3 requests +func (s *ServerHTTPS3) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if !s.validRequest(r) { + http.Error(w, "", http.StatusNotFound) + s.countResponse(http.StatusNotFound) + return + } + + msg, raw, err := doh.RequestToMsgWire(r) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + s.countResponse(http.StatusBadRequest) + return + } + + // from HTTP request → DNS writer + h, p, _ := net.SplitHostPort(r.RemoteAddr) + port, _ := strconv.Atoi(p) + dw := &DoHWriter{ + laddr: s.listenAddr, + raddr: &net.UDPAddr{IP: net.ParseIP(h), Port: port}, + request: r, + } + + if tsig := msg.IsTsig(); tsig != nil { + if s.tsigSecret == nil { + dw.tsigStatus = dns.ErrSecret + } else if secret, ok := s.tsigSecret[tsig.Hdr.Name]; !ok { + dw.tsigStatus = dns.ErrSecret + } else { + dw.tsigStatus = dns.TsigVerify(raw, secret, "", false) + } + } + + ctx := context.WithValue(r.Context(), Key{}, s.Server) + ctx = context.WithValue(ctx, LoopKey{}, 0) + ctx = context.WithValue(ctx, HTTPRequestKey{}, r) + + s.ServeDNS(ctx, dw, msg) + + if dw.Msg == nil { + http.Error(w, "No response", http.StatusInternalServerError) + s.countResponse(http.StatusInternalServerError) + return + } + + buf, _ := dw.Msg.Pack() + mt, _ := response.Typify(dw.Msg, time.Now().UTC()) + age := dnsutil.MinimalTTL(dw.Msg, mt) + + w.Header().Set("Content-Type", doh.MimeType) + w.Header().Set("Cache-Control", fmt.Sprintf("max-age=%d", uint32(age.Seconds()))) + w.Header().Set("Content-Length", strconv.Itoa(len(buf))) + w.WriteHeader(http.StatusOK) + + s.countResponse(http.StatusOK) + w.Write(buf) +} + +func (s *ServerHTTPS3) countResponse(status int) { + vars.HTTPS3ResponsesCount.WithLabelValues(s.Addr, strconv.Itoa(status)).Inc() +} diff --git a/core/dnsserver/server_https3_test.go b/core/dnsserver/server_https3_test.go new file mode 100644 index 0000000000..faf879a5c2 --- /dev/null +++ b/core/dnsserver/server_https3_test.go @@ -0,0 +1,328 @@ +package dnsserver + +import ( + "bytes" + "crypto/tls" + "errors" + "io" + "net/http" + "net/http/httptest" + "strings" + "testing" + "time" + + "github.com/coredns/coredns/plugin" + + "github.com/miekg/dns" +) + +const ( + testTSIGKeyNameHTTPS3 = "tsig-key." + testTSIGSecretHTTPS3 = "MTIzNA==" + testTSIGWrongSecretHTTPS3 = "NTY3OA==" +) + +func testServerHTTPS3(t *testing.T, path string, validator func(*http.Request) bool) *http.Response { + t.Helper() + c := Config{ + Zone: "example.com.", + Transport: "https", + TLSConfig: &tls.Config{}, + ListenHosts: []string{"127.0.0.1"}, + Port: "443", + HTTPRequestValidateFunc: validator, + } + s, err := NewServerHTTPS3("127.0.0.1:443", []*Config{&c}) + if err != nil { + t.Log(err) + t.Fatal("could not create HTTPS3 server") + } + m := new(dns.Msg) + m.SetQuestion("example.org.", dns.TypeDNSKEY) + buf, err := m.Pack() + if err != nil { + t.Fatal(err) + } + + r := httptest.NewRequest(http.MethodPost, path, bytes.NewReader(buf)) + w := httptest.NewRecorder() + s.ServeHTTP(w, r) + + return w.Result() +} + +func TestCustomHTTP3RequestValidator(t *testing.T) { + testCases := map[string]struct { + path string + expected int + validator func(*http.Request) bool + }{ + "default": {"/dns-query", http.StatusOK, nil}, + "custom validator": {"/b10cada", http.StatusOK, validator}, + "no validator set": {"/adb10c", http.StatusNotFound, nil}, + "invalid path with validator": {"/helloworld", http.StatusNotFound, validator}, + } + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + res := testServerHTTPS3(t, tc.path, tc.validator) + if res.StatusCode != tc.expected { + t.Error("unexpected HTTP code", res.StatusCode) + } + res.Body.Close() + }) + } +} + +func TestNewServerHTTPS3WithCustomLimits(t *testing.T) { + maxStreams := 50 + c := Config{ + Zone: "example.com.", + Transport: "https3", + TLSConfig: &tls.Config{}, + ListenHosts: []string{"127.0.0.1"}, + Port: "443", + MaxHTTPS3Streams: &maxStreams, + } + + server, err := NewServerHTTPS3("127.0.0.1:443", []*Config{&c}) + if err != nil { + t.Fatalf("NewServerHTTPS3() with custom limits failed: %v", err) + } + + if server.maxStreams != maxStreams { + t.Errorf("Expected maxStreams = %d, got %d", maxStreams, server.maxStreams) + } + + expectedMaxStreams := int64(maxStreams) + if server.quicConfig.MaxIncomingStreams != expectedMaxStreams { + t.Errorf("Expected quicConfig.MaxIncomingStreams = %d, got %d", expectedMaxStreams, server.quicConfig.MaxIncomingStreams) + } + + if server.quicConfig.MaxIncomingUniStreams != expectedMaxStreams { + t.Errorf("Expected quicConfig.MaxIncomingUniStreams = %d, got %d", expectedMaxStreams, server.quicConfig.MaxIncomingUniStreams) + } +} + +func TestNewServerHTTPS3Defaults(t *testing.T) { + c := Config{ + Zone: "example.com.", + Transport: "https3", + TLSConfig: &tls.Config{}, + ListenHosts: []string{"127.0.0.1"}, + Port: "443", + } + + server, err := NewServerHTTPS3("127.0.0.1:443", []*Config{&c}) + if err != nil { + t.Fatalf("NewServerHTTPS3() failed: %v", err) + } + + if server.maxStreams != DefaultHTTPS3MaxStreams { + t.Errorf("Expected default maxStreams = %d, got %d", DefaultHTTPS3MaxStreams, server.maxStreams) + } + + expectedMaxStreams := int64(DefaultHTTPS3MaxStreams) + if server.quicConfig.MaxIncomingStreams != expectedMaxStreams { + t.Errorf("Expected default quicConfig.MaxIncomingStreams = %d, got %d", expectedMaxStreams, server.quicConfig.MaxIncomingStreams) + } +} + +func TestNewServerHTTPS3ZeroLimits(t *testing.T) { + zero := 0 + c := Config{ + Zone: "example.com.", + Transport: "https3", + TLSConfig: &tls.Config{}, + ListenHosts: []string{"127.0.0.1"}, + Port: "443", + MaxHTTPS3Streams: &zero, + } + + server, err := NewServerHTTPS3("127.0.0.1:443", []*Config{&c}) + if err != nil { + t.Fatalf("NewServerHTTPS3() with zero limits failed: %v", err) + } + + if server.maxStreams != 0 { + t.Errorf("Expected maxStreams = 0, got %d", server.maxStreams) + } + // When maxStreams is 0, quicConfig should not set MaxIncomingStreams (uses QUIC default) + if server.quicConfig.MaxIncomingStreams != 0 { + t.Errorf("Expected quicConfig.MaxIncomingStreams = 0 (QUIC default), got %d", server.quicConfig.MaxIncomingStreams) + } +} + +func testConfigWithTSIGCheckPluginHTTPS3(t *testing.T, check func(*testing.T, error)) *Config { + t.Helper() + + c := &Config{ + Zone: "example.com.", + Transport: "https3", + TLSConfig: &tls.Config{}, + ListenHosts: []string{"127.0.0.1"}, + Port: "443", + TsigSecret: map[string]string{ + testTSIGKeyNameHTTPS3: testTSIGSecretHTTPS3, + }, + } + c.AddPlugin(func(_next plugin.Handler) plugin.Handler { + return tsigStatusCheckPlugin{t: t, check: check} + }) + return c +} + +func testServerHTTPS3Msg(t *testing.T, cfg *Config, req *dns.Msg) *dns.Msg { + t.Helper() + + s, err := NewServerHTTPS3("127.0.0.1:443", []*Config{cfg}) + if err != nil { + t.Fatal("could not create HTTPS3 server:", err) + } + + buf, err := req.Pack() + if err != nil { + t.Fatal(err) + } + + r := httptest.NewRequest(http.MethodPost, "/dns-query", bytes.NewReader(buf)) + r.RemoteAddr = "127.0.0.1:12345" + w := httptest.NewRecorder() + + s.ServeHTTP(w, r) + + res := w.Result() + defer res.Body.Close() + + if res.StatusCode != http.StatusOK { + t.Fatalf("unexpected HTTP status: got %d", res.StatusCode) + } + + body, err := io.ReadAll(res.Body) + if err != nil { + t.Fatal(err) + } + + m := new(dns.Msg) + if err := m.Unpack(body); err != nil { + t.Fatal(err) + } + return m +} + +func testServerHTTPS3Raw(t *testing.T, cfg *Config, buf []byte) *dns.Msg { + t.Helper() + + s, err := NewServerHTTPS3("127.0.0.1:443", []*Config{cfg}) + if err != nil { + t.Fatal("could not create HTTPS3 server:", err) + } + + r := httptest.NewRequest(http.MethodPost, "/dns-query", bytes.NewReader(buf)) + r.RemoteAddr = "127.0.0.1:12345" + w := httptest.NewRecorder() + + s.ServeHTTP(w, r) + + res := w.Result() + defer res.Body.Close() + + if res.StatusCode != http.StatusOK { + t.Fatalf("unexpected HTTP status: got %d", res.StatusCode) + } + + body, err := io.ReadAll(res.Body) + if err != nil { + t.Fatal(err) + } + + m := new(dns.Msg) + if err := m.Unpack(body); err != nil { + t.Fatal(err) + } + return m +} + +func forgedTSIGMsgHTTPS3() *dns.Msg { + m := new(dns.Msg) + m.SetQuestion("example.com.", dns.TypeA) + + m.Extra = append(m.Extra, &dns.TSIG{ + Hdr: dns.RR_Header{ + Name: "bogus-key.", + Rrtype: dns.TypeTSIG, + Class: dns.ClassANY, + Ttl: 0, + }, + Algorithm: dns.HmacSHA256, + TimeSigned: uint64(time.Now().Unix()), + Fudge: 300, + MACSize: 32, + MAC: strings.Repeat("00", 32), + OrigId: m.Id, + Error: dns.RcodeSuccess, + }) + return m +} + +func TestServeHTTP3RejectsUnsignedTSIGRequiredRequest(t *testing.T) { + cfg := testConfigWithTSIGCheckPluginHTTPS3(t, func(t *testing.T, err error) { + t.Helper() + if err != nil { + t.Fatalf("expected nil TsigStatus for unsigned request, got %v", err) + } + }) + + m := new(dns.Msg) + m.SetQuestion("example.com.", dns.TypeA) + resp := testServerHTTPS3Msg(t, cfg, m) + + if resp.Rcode != dns.RcodeSuccess { + t.Fatalf("expected NOERROR response from plugin, got %s", dns.RcodeToString[resp.Rcode]) + } +} + +func TestServeHTTP3RejectsTSIGWithUnknownKey(t *testing.T) { + cfg := testConfigWithTSIGCheckPluginHTTPS3(t, func(t *testing.T, err error) { + t.Helper() + if !errors.Is(err, dns.ErrSecret) { + t.Fatalf("expected dns.ErrSecret for unknown TSIG key, got %v", err) + } + }) + + resp := testServerHTTPS3Msg(t, cfg, forgedTSIGMsgHTTPS3()) + if resp.Rcode != dns.RcodeSuccess { + t.Fatalf("expected NOERROR response from plugin, got %s", dns.RcodeToString[resp.Rcode]) + } +} + +func TestServeHTTP3RejectsTSIGWithBadMAC(t *testing.T) { + cfg := testConfigWithTSIGCheckPluginHTTPS3(t, func(t *testing.T, err error) { + t.Helper() + if err == nil { + t.Fatal("expected non-nil TsigStatus for bad TSIG MAC") + } + }) + + buf := mustPackSignedTSIGQuery(t, testTSIGKeyNameHTTPS3, testTSIGWrongSecretHTTPS3, time.Now().Unix()) + resp := testServerHTTPS3Raw(t, cfg, buf) + + if resp.Rcode != dns.RcodeSuccess { + t.Fatalf("expected NOERROR response from plugin, got %s", dns.RcodeToString[resp.Rcode]) + } +} + +func TestServeHTTP3AcceptsValidTSIG(t *testing.T) { + cfg := testConfigWithTSIGCheckPluginHTTPS3(t, func(t *testing.T, err error) { + t.Helper() + if err != nil { + t.Fatalf("expected nil TsigStatus for valid TSIG, got %v", err) + } + }) + + buf := mustPackSignedTSIGQuery(t, testTSIGKeyNameHTTPS3, testTSIGSecretHTTPS3, time.Now().Unix()) + resp := testServerHTTPS3Raw(t, cfg, buf) + + if resp.Rcode != dns.RcodeSuccess { + t.Fatalf("expected NOERROR response from plugin, got %s", dns.RcodeToString[resp.Rcode]) + } +} diff --git a/core/dnsserver/server_https_test.go b/core/dnsserver/server_https_test.go index 031cbf14ad..e36c3d1d15 100644 --- a/core/dnsserver/server_https_test.go +++ b/core/dnsserver/server_https_test.go @@ -2,11 +2,18 @@ package dnsserver import ( "bytes" + "context" "crypto/tls" + "io" + "net" "net/http" "net/http/httptest" "regexp" + "strings" "testing" + "time" + + "github.com/coredns/coredns/plugin" "github.com/miekg/dns" ) @@ -16,6 +23,12 @@ var ( validator = func(r *http.Request) bool { return validPath.MatchString(r.URL.Path) } ) +const ( + testTSIGKeyName = "tsig-key." + testTSIGSecret = "MTIzNA==" + testTSIGWrongSecret = "NTY3OA==" +) + func testServerHTTPS(t *testing.T, path string, validator func(*http.Request) bool) *http.Response { t.Helper() c := Config{ @@ -66,3 +79,507 @@ func TestCustomHTTPRequestValidator(t *testing.T) { }) } } + +func TestNewServerHTTPSWithCustomLimits(t *testing.T) { + maxConnections := 100 + c := Config{ + Zone: "example.com.", + Transport: "https", + TLSConfig: &tls.Config{}, + ListenHosts: []string{"127.0.0.1"}, + Port: "443", + MaxHTTPSConnections: &maxConnections, + } + + server, err := NewServerHTTPS("127.0.0.1:443", []*Config{&c}) + if err != nil { + t.Fatalf("NewServerHTTPS() with custom limits failed: %v", err) + } + + if server.maxConnections != maxConnections { + t.Errorf("Expected maxConnections = %d, got %d", maxConnections, server.maxConnections) + } +} + +func TestNewServerHTTPSDefaults(t *testing.T) { + c := Config{ + Zone: "example.com.", + Transport: "https", + TLSConfig: &tls.Config{}, + ListenHosts: []string{"127.0.0.1"}, + Port: "443", + } + + server, err := NewServerHTTPS("127.0.0.1:443", []*Config{&c}) + if err != nil { + t.Fatalf("NewServerHTTPS() failed: %v", err) + } + + if server.maxConnections != DefaultHTTPSMaxConnections { + t.Errorf("Expected default maxConnections = %d, got %d", DefaultHTTPSMaxConnections, server.maxConnections) + } +} + +func TestNewServerHTTPSZeroLimits(t *testing.T) { + zero := 0 + c := Config{ + Zone: "example.com.", + Transport: "https", + TLSConfig: &tls.Config{}, + ListenHosts: []string{"127.0.0.1"}, + Port: "443", + MaxHTTPSConnections: &zero, + } + + server, err := NewServerHTTPS("127.0.0.1:443", []*Config{&c}) + if err != nil { + t.Fatalf("NewServerHTTPS() with zero limits failed: %v", err) + } + + if server.maxConnections != 0 { + t.Errorf("Expected maxConnections = 0, got %d", server.maxConnections) + } +} + +type contextCapturingPlugin struct { + capturedContext context.Context + contextCancelled bool +} + +func (p *contextCapturingPlugin) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { + p.capturedContext = ctx + select { + case <-ctx.Done(): + p.contextCancelled = true + default: + } + + m := new(dns.Msg) + m.SetReply(r) + m.Authoritative = true + w.WriteMsg(m) + return dns.RcodeSuccess, nil +} + +func (p *contextCapturingPlugin) Name() string { return "context_capturing" } + +func testConfigWithPlugin(p *contextCapturingPlugin) *Config { + c := &Config{ + Zone: "example.com.", + Transport: "https", + TLSConfig: &tls.Config{}, + ListenHosts: []string{"127.0.0.1"}, + Port: "443", + } + c.AddPlugin(func(_next plugin.Handler) plugin.Handler { return p }) + return c +} + +func TestDoHWriterLaddrFromConnContext(t *testing.T) { + capturer := &addrCapturingPlugin{} + cfg := testConfigWithHandler(capturer) + + s, err := NewServerHTTPS("127.0.0.1:443", []*Config{cfg}) + if err != nil { + t.Fatal("could not create HTTPS server:", err) + } + s.listenAddr = &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 443} + + m := new(dns.Msg) + m.SetQuestion("example.com.", dns.TypeA) + buf, err := m.Pack() + if err != nil { + t.Fatal(err) + } + + // Simulate a PROXY protocol destination that differs from listenAddr. + ppDst := &net.TCPAddr{IP: net.ParseIP("10.0.0.1"), Port: 443} + + r := httptest.NewRequest(http.MethodPost, "/dns-query", io.NopCloser(bytes.NewReader(buf))) + ctx := context.WithValue(r.Context(), connAddrKey{}, ppDst) + r = r.WithContext(ctx) + w := httptest.NewRecorder() + + s.ServeHTTP(w, r) + + if !capturer.called { + t.Fatal("plugin was not called") + } + if capturer.localAddr == nil { + t.Fatal("DoHWriter.laddr is nil") + } + if capturer.localAddr.String() != ppDst.String() { + t.Errorf("expected laddr %s (PP destination), got %s", ppDst, capturer.localAddr) + } +} + +func TestDoHWriterLaddrFallback(t *testing.T) { + capturer := &addrCapturingPlugin{} + cfg := testConfigWithHandler(capturer) + + s, err := NewServerHTTPS("127.0.0.1:443", []*Config{cfg}) + if err != nil { + t.Fatal("could not create HTTPS server:", err) + } + s.listenAddr = &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 443} + + m := new(dns.Msg) + m.SetQuestion("example.com.", dns.TypeA) + buf, err := m.Pack() + if err != nil { + t.Fatal(err) + } + + // No connAddrKey in context; should fall back to s.listenAddr. + r := httptest.NewRequest(http.MethodPost, "/dns-query", io.NopCloser(bytes.NewReader(buf))) + w := httptest.NewRecorder() + + s.ServeHTTP(w, r) + + if !capturer.called { + t.Fatal("plugin was not called") + } + if capturer.localAddr == nil { + t.Fatal("DoHWriter.laddr is nil") + } + if capturer.localAddr.String() != s.listenAddr.String() { + t.Errorf("expected fallback laddr %s, got %s", s.listenAddr, capturer.localAddr) + } +} + +type addrCapturingPlugin struct { + called bool + localAddr net.Addr + remoteAddr net.Addr +} + +func (p *addrCapturingPlugin) ServeDNS(_ context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { + p.called = true + p.localAddr = w.LocalAddr() + p.remoteAddr = w.RemoteAddr() + m := new(dns.Msg) + m.SetReply(r) + m.Authoritative = true + w.WriteMsg(m) + return dns.RcodeSuccess, nil +} + +func (p *addrCapturingPlugin) Name() string { return "addr_capturing" } + +func testConfigWithHandler(h plugin.Handler) *Config { + c := &Config{ + Zone: "example.com.", + Transport: "https", + TLSConfig: &tls.Config{}, + ListenHosts: []string{"127.0.0.1"}, + Port: "443", + } + c.AddPlugin(func(_next plugin.Handler) plugin.Handler { return h }) + return c +} + +func TestHTTPRequestContextPropagation(t *testing.T) { + plugin := &contextCapturingPlugin{} + + s, err := NewServerHTTPS("127.0.0.1:443", []*Config{testConfigWithPlugin(plugin)}) + if err != nil { + t.Fatal("could not create HTTPS server:", err) + } + + m := new(dns.Msg) + m.SetQuestion("example.com.", dns.TypeA) + buf, err := m.Pack() + if err != nil { + t.Fatal(err) + } + t.Run("context values propagation", func(t *testing.T) { + contextValue := "test-request-id" + + r := httptest.NewRequest(http.MethodPost, "/dns-query", io.NopCloser(bytes.NewReader(buf))) + ctx := context.WithValue(r.Context(), Key{}, contextValue) + r = r.WithContext(ctx) + w := httptest.NewRecorder() + + s.ServeHTTP(w, r) + + if plugin.capturedContext == nil { + t.Fatal("No context received in plugin") + } + + if val := plugin.capturedContext.Value(Key{}); val != s.Server { + t.Error("Server key not properly set in context") + } + + if httpReq, ok := plugin.capturedContext.Value(HTTPRequestKey{}).(*http.Request); !ok { + t.Error("HTTPRequestKey not found in context") + } else if httpReq != r { + t.Error("HTTPRequestKey contains different request than expected") + } + }) + + t.Run("plugins can access HTTP request details", func(t *testing.T) { + r := httptest.NewRequest(http.MethodPost, "/dns-query", io.NopCloser(bytes.NewReader(buf))) + r.Header.Set("User-Agent", "my-doh-client/2.1") + r.Header.Set("X-Forwarded-For", "10.10.10.10") + r.Header.Set("Accept", "application/dns-message") + r.RemoteAddr = "10.10.10.100:45678" + w := httptest.NewRecorder() + + s.ServeHTTP(w, r) + + if plugin.capturedContext == nil { + t.Fatal("No context received in plugin") + } + + httpReq, ok := plugin.capturedContext.Value(HTTPRequestKey{}).(*http.Request) + if !ok { + t.Fatal("HTTPRequestKey not found in context") + } + + if httpReq.Method != "POST" { + t.Errorf("Plugin expected POST method, got %s", httpReq.Method) + } + + if ua := httpReq.Header.Get("User-Agent"); ua != "my-doh-client/2.1" { + t.Errorf("Plugin expected User-Agent 'my-doh-client/2.1', got %s", ua) + } + + if xff := httpReq.Header.Get("X-Forwarded-For"); xff != "10.10.10.10" { + t.Errorf("Plugin expected X-Forwarded-For '10.10.10.10', got %s", xff) + } + + if accept := httpReq.Header.Get("Accept"); accept != "application/dns-message" { + t.Errorf("Plugin expected Accept 'application/dns-message', got %s", accept) + } + + if httpReq.RemoteAddr != "10.10.10.100:45678" { + t.Errorf("Plugin expected RemoteAddr '10.10.10.100:45678', got %s", httpReq.RemoteAddr) + } + + if loopValue := plugin.capturedContext.Value(LoopKey{}); loopValue != 0 { + t.Errorf("Expected LoopKey value 0, got %v", loopValue) + } + }) + + t.Run("context cancellation propagation", func(t *testing.T) { + r := httptest.NewRequest(http.MethodPost, "/dns-query", io.NopCloser(bytes.NewReader(buf))) + ctx, cancel := context.WithCancel(r.Context()) + r = r.WithContext(ctx) + w := httptest.NewRecorder() + + cancel() + s.ServeHTTP(w, r) + + if plugin.capturedContext == nil { + t.Fatal("No context received in plugin") + } + + if !plugin.contextCancelled { + t.Error("Context cancellation was not detected in plugin") + } + + if err := plugin.capturedContext.Err(); err == nil { + t.Error("Expected context to be cancelled, but it wasn't") + } + }) + + t.Run("context timeout propagation", func(t *testing.T) { + r := httptest.NewRequest(http.MethodPost, "/dns-query", io.NopCloser(bytes.NewReader(buf))) + ctx, cancel := context.WithTimeout(r.Context(), time.Millisecond) + defer cancel() + r = r.WithContext(ctx) + w := httptest.NewRecorder() + + s.ServeHTTP(w, r) + + if plugin.capturedContext == nil { + t.Fatal("No context received in plugin") + } + + if deadline, ok := plugin.capturedContext.Deadline(); !ok { + t.Error("Expected context to have a deadline") + } else if deadline.IsZero() { + t.Error("Context deadline is zero") + } + }) +} + +type tsigStatusPlugin struct{} + +func (p *tsigStatusPlugin) ServeDNS(_ context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { + m := new(dns.Msg) + m.SetReply(r) + m.Authoritative = true + + switch { + case r.IsTsig() == nil: + m.Rcode = dns.RcodeRefused + case w.TsigStatus() != nil: + m.Rcode = dns.RcodeNotAuth + default: + m.Rcode = dns.RcodeSuccess + } + + if err := w.WriteMsg(m); err != nil { + return dns.RcodeServerFailure, err + } + return dns.RcodeSuccess, nil +} + +func (p *tsigStatusPlugin) Name() string { return "tsig_status" } + +func testConfigWithTSIGStatusPlugin() *Config { + c := &Config{ + Zone: "example.com.", + Transport: "https", + TLSConfig: &tls.Config{}, + ListenHosts: []string{"127.0.0.1"}, + Port: "443", + TsigSecret: map[string]string{ + testTSIGKeyName: testTSIGSecret, + }, + } + c.AddPlugin(func(_next plugin.Handler) plugin.Handler { return &tsigStatusPlugin{} }) + return c +} + +func testServerHTTPSMsg(t *testing.T, cfg *Config, req *dns.Msg) *dns.Msg { + t.Helper() + + s, err := NewServerHTTPS("127.0.0.1:443", []*Config{cfg}) + if err != nil { + t.Fatal("could not create HTTPS server:", err) + } + + buf, err := req.Pack() + if err != nil { + t.Fatal(err) + } + + r := httptest.NewRequest(http.MethodPost, "/dns-query", bytes.NewReader(buf)) + r.RemoteAddr = "127.0.0.1:12345" + w := httptest.NewRecorder() + + s.ServeHTTP(w, r) + + res := w.Result() + defer res.Body.Close() + + if res.StatusCode != http.StatusOK { + t.Fatalf("unexpected HTTP status: got %d", res.StatusCode) + } + + body, err := io.ReadAll(res.Body) + if err != nil { + t.Fatal(err) + } + + m := new(dns.Msg) + if err := m.Unpack(body); err != nil { + t.Fatal(err) + } + return m +} + +func testServerHTTPSRaw(t *testing.T, cfg *Config, buf []byte) *dns.Msg { + t.Helper() + + s, err := NewServerHTTPS("127.0.0.1:443", []*Config{cfg}) + if err != nil { + t.Fatal("could not create HTTPS server:", err) + } + + r := httptest.NewRequest(http.MethodPost, "/dns-query", bytes.NewReader(buf)) + r.RemoteAddr = "127.0.0.1:12345" + w := httptest.NewRecorder() + + s.ServeHTTP(w, r) + + res := w.Result() + defer res.Body.Close() + + if res.StatusCode != http.StatusOK { + t.Fatalf("unexpected HTTP status: got %d", res.StatusCode) + } + + body, err := io.ReadAll(res.Body) + if err != nil { + t.Fatal(err) + } + + m := new(dns.Msg) + if err := m.Unpack(body); err != nil { + t.Fatal(err) + } + return m +} + +func forgedTSIGMsg() *dns.Msg { + m := new(dns.Msg) + m.SetQuestion("example.com.", dns.TypeA) + + m.Extra = append(m.Extra, &dns.TSIG{ + Hdr: dns.RR_Header{ + Name: "bogus-key.", + Rrtype: dns.TypeTSIG, + Class: dns.ClassANY, + Ttl: 0, + }, + Algorithm: dns.HmacSHA256, + TimeSigned: uint64(time.Now().Unix()), + Fudge: 300, + MACSize: 32, + MAC: strings.Repeat("00", 32), + OrigId: m.Id, + Error: dns.RcodeSuccess, + }) + return m +} + +func mustSignedTSIGQueryBytes(t *testing.T, keyName, secret string) []byte { + t.Helper() + return mustPackSignedTSIGQuery(t, keyName, secret, time.Now().Unix()) +} + +func TestServeHTTPRejectsUnsignedTSIGRequiredRequest(t *testing.T) { + m := new(dns.Msg) + m.SetQuestion("example.com.", dns.TypeA) + + resp := testServerHTTPSMsg(t, testConfigWithTSIGStatusPlugin(), m) + if resp.Rcode != dns.RcodeRefused { + t.Fatalf("expected REFUSED for unsigned request, got %s", dns.RcodeToString[resp.Rcode]) + } +} + +func TestServeHTTPRejectsTSIGWithUnknownKey(t *testing.T) { + resp := testServerHTTPSMsg(t, testConfigWithTSIGStatusPlugin(), forgedTSIGMsg()) + + if resp.Rcode != dns.RcodeNotAuth { + t.Fatalf("expected NOTAUTH for unknown TSIG key, got %s", dns.RcodeToString[resp.Rcode]) + } +} + +func TestServeHTTPRejectsTSIGWithBadMAC(t *testing.T) { + buf := mustSignedTSIGQueryBytes(t, testTSIGKeyName, testTSIGWrongSecret) + + resp := testServerHTTPSRaw(t, testConfigWithTSIGStatusPlugin(), buf) + if resp.Rcode != dns.RcodeNotAuth { + t.Fatalf("expected NOTAUTH for bad TSIG MAC, got %s", dns.RcodeToString[resp.Rcode]) + } +} + +func TestServeHTTPAcceptsValidTSIG(t *testing.T) { + buf := mustSignedTSIGQueryBytes(t, testTSIGKeyName, testTSIGSecret) + + resp := testServerHTTPSRaw(t, testConfigWithTSIGStatusPlugin(), buf) + if resp.Rcode != dns.RcodeSuccess { + t.Fatalf("expected NOERROR for valid TSIG, got %s", dns.RcodeToString[resp.Rcode]) + } +} + +func TestDoHWriterTsigStatusReturnsStoredStatus(t *testing.T) { + dw := &DoHWriter{tsigStatus: dns.ErrSecret} + if dw.TsigStatus() != dns.ErrSecret { + t.Fatal("expected TsigStatus to return stored tsigStatus") + } +} diff --git a/core/dnsserver/server_quic.go b/core/dnsserver/server_quic.go index 2e42155a96..0deb80c375 100644 --- a/core/dnsserver/server_quic.go +++ b/core/dnsserver/server_quic.go @@ -11,6 +11,7 @@ import ( "github.com/coredns/coredns/plugin/metrics/vars" clog "github.com/coredns/coredns/plugin/pkg/log" + cproxyproto "github.com/coredns/coredns/plugin/pkg/proxyproto" "github.com/coredns/coredns/plugin/pkg/reuseport" "github.com/coredns/coredns/plugin/pkg/transport" @@ -84,7 +85,7 @@ func NewServerQUIC(addr string, group []*Config) (*ServerQUIC, error) { } var quicConfig = &quic.Config{ - MaxIdleTimeout: s.idleTimeout, + MaxIdleTimeout: s.IdleTimeout, MaxIncomingStreams: int64(maxStreams), MaxIncomingUniStreams: int64(maxStreams), // Enable 0-RTT by default for all connections on the server-side. @@ -103,6 +104,14 @@ func NewServerQUIC(addr string, group []*Config) (*ServerQUIC, error) { // ServePacket implements caddy.UDPServer interface. func (s *ServerQUIC) ServePacket(p net.PacketConn) error { s.m.Lock() + if s.quicListener == nil { + listener, err := quic.Listen(p, s.tlsConfig, s.quicConfig) + if err != nil { + s.m.Unlock() + return err + } + s.quicListener = listener + } s.listenAddr = s.quicListener.Addr() s.m.Unlock() @@ -127,6 +136,15 @@ func (s *ServerQUIC) ServeQUIC() error { } } +func acquireQUICWorker(ctx context.Context, pool chan struct{}) bool { + select { + case pool <- struct{}{}: + return true + case <-ctx.Done(): + return false + } +} + // serveQUICConnection handles a new QUIC connection. It waits for new streams // and passes them to serveQUICStream. func (s *ServerQUIC) serveQUICConnection(conn *quic.Conn) { @@ -148,10 +166,13 @@ func (s *ServerQUIC) serveQUICConnection(conn *quic.Conn) { return } - // Use a bounded worker pool - s.streamProcessPool <- struct{}{} // Acquire a worker slot, may block + if !acquireQUICWorker(conn.Context(), s.streamProcessPool) { + _ = stream.Close() + return + } + go func(st *quic.Stream, cn *quic.Conn) { - defer func() { <-s.streamProcessPool }() // Release worker slot + defer func() { <-s.streamProcessPool }() s.serveQUICStream(st, cn) }(stream, conn) } @@ -203,6 +224,16 @@ func (s *ServerQUIC) serveQUICStream(stream *quic.Stream, conn *quic.Conn) { Msg: req, } + if tsig := req.IsTsig(); tsig != nil { + if s.tsigSecret == nil { + w.tsigStatus = dns.ErrSecret + } else if secret, ok := s.tsigSecret[tsig.Hdr.Name]; !ok { + w.tsigStatus = dns.ErrSecret + } else { + w.tsigStatus = dns.TsigVerify(buf, secret, "", false) + } + } + dnsCtx := context.WithValue(stream.Context(), Key{}, s.Server) dnsCtx = context.WithValue(dnsCtx, LoopKey{}, 0) s.ServeDNS(dnsCtx, w, req) @@ -216,6 +247,10 @@ func (s *ServerQUIC) ListenPacket() (net.PacketConn, error) { return nil, err } + if s.connPolicy != nil { + p = &cproxyproto.PacketConn{PacketConn: p, ConnPolicy: s.connPolicy} + } + s.m.Lock() defer s.m.Unlock() @@ -253,7 +288,7 @@ func (s *ServerQUIC) Stop() error { } // Serve implements caddy.TCPServer interface. -func (s *ServerQUIC) Serve(l net.Listener) error { return nil } +func (s *ServerQUIC) Serve(_l net.Listener) error { return nil } // Listen implements caddy.TCPServer interface. func (s *ServerQUIC) Listen() (net.Listener, error) { return nil, nil } @@ -338,7 +373,7 @@ func readDOQMessage(r io.Reader) ([]byte, error) { // A client or server receives a STREAM FIN before receiving all the bytes // for a message indicated in the 2-octet length field. // See https://www.rfc-editor.org/rfc/rfc9250#section-4.3.3-2.2 - if size != uint16(len(buf)) { + if size != uint16(len(buf)) { // #nosec G115 -- buf length fits in uint16 return nil, fmt.Errorf("message size does not match 2-byte prefix") } diff --git a/core/dnsserver/server_quic_test.go b/core/dnsserver/server_quic_test.go index 8deb11c7c2..28d8931bea 100644 --- a/core/dnsserver/server_quic_test.go +++ b/core/dnsserver/server_quic_test.go @@ -2,9 +2,18 @@ package dnsserver import ( "bytes" + "context" + "crypto/rand" + "crypto/rsa" "crypto/tls" + "crypto/x509" + "crypto/x509/pkix" "errors" + "math/big" "testing" + "time" + + "github.com/coredns/coredns/plugin/pkg/transport" "github.com/miekg/dns" "github.com/quic-go/quic-go" @@ -400,3 +409,277 @@ func TestAddPrefix(t *testing.T) { }) } } + +func TestAcquireQUICWorkerWaitsForSlot(t *testing.T) { + pool := make(chan struct{}, 1) + pool <- struct{}{} + + ctx, cancel := context.WithCancel(t.Context()) + defer cancel() + + done := make(chan bool, 1) + go func() { + done <- acquireQUICWorker(ctx, pool) + }() + + select { + case <-done: + t.Fatal("acquireQUICWorker returned before a slot was released") + default: + } + + <-pool + + got := <-done + if !got { + t.Fatal("expected acquireQUICWorker to succeed after slot release") + } +} + +func TestAcquireQUICWorkerReturnsFalseOnCancelledContext(t *testing.T) { + pool := make(chan struct{}, 1) + pool <- struct{}{} + + ctx, cancel := context.WithCancel(context.Background()) + cancel() + + if got := acquireQUICWorker(ctx, pool); got { + t.Fatal("expected acquireQUICWorker to return false when context is cancelled") + } +} + +func TestDoQWriterTsigStatusReturnsStoredStatus(t *testing.T) { + want := errors.New("bad tsig") + + w := &DoQWriter{ + tsigStatus: want, + } + + if got := w.TsigStatus(); got != want { + t.Fatalf("TsigStatus() = %v, want %v", got, want) + } +} + +func TestServerQUIC_ServeQUIC_TSIGBadSigSetsTsigStatus(t *testing.T) { + const keyName = "tsig-key." + const clientSecret = "MTIzNDU2Nzg5MDEyMzQ1Ng==" + const serverSecret = "QUJDREVGR0hJSktMTU5PUA==" + + called := make(chan struct{}, 1) + + config := testConfig("quic", tsigStatusCheckPlugin{ + t: t, + called: called, + check: func(t *testing.T, got error) { + t.Helper() + if got == nil { + t.Fatal("TsigStatus() = nil, want non-nil for bad TSIG MAC") + } + if errors.Is(got, dns.ErrSecret) { + t.Fatalf("TsigStatus() = %v, want signature verification error, not ErrSecret", got) + } + if errors.Is(got, dns.ErrTime) { + t.Fatalf("TsigStatus() = %v, want signature verification error, not ErrTime", got) + } + }, + }) + config.TLSConfig = mustMakeQUICServerTLSConfig(t) + + server, err := NewServerQUIC(transport.QUIC+"://127.0.0.1:0", []*Config{config}) + if err != nil { + t.Fatalf("NewServerQUIC() failed: %v", err) + } + + server.tsigSecret = map[string]string{ + keyName: serverSecret, + } + + pc, err := server.ListenPacket() + if err != nil { + t.Fatalf("ListenPacket() failed: %v", err) + } + defer pc.Close() + + serveErrCh := make(chan error, 1) + go func() { + serveErrCh <- server.ServeQUIC() + }() + + defer func() { + _ = server.Stop() + select { + case <-serveErrCh: + case <-time.After(2 * time.Second): + } + }() + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + conn, err := quic.DialAddr(ctx, pc.LocalAddr().String(), mustMakeQUICClientTLSConfig(), &quic.Config{}) + if err != nil { + t.Fatalf("quic.DialAddr() failed: %v", err) + } + defer conn.CloseWithError(DoQCodeNoError, "") + + stream, err := conn.OpenStreamSync(ctx) + if err != nil { + t.Fatalf("OpenStreamSync() failed: %v", err) + } + + wire := mustPackSignedTSIGQuery(t, keyName, clientSecret, time.Now().Unix()) + + if _, err := stream.Write(AddPrefix(wire)); err != nil { + t.Fatalf("stream.Write() failed: %v", err) + } + if err := stream.Close(); err != nil { + t.Fatalf("stream.Close() failed: %v", err) + } + + respWire, err := readDOQMessage(stream) + if err != nil { + t.Fatalf("readDOQMessage() failed: %v", err) + } + + resp := new(dns.Msg) + if err := resp.Unpack(respWire); err != nil { + t.Fatalf("response unpack failed: %v", err) + } + + select { + case <-called: + case <-time.After(5 * time.Second): + t.Fatal("ServeDNS() was not called") + } +} + +func mustMakeQUICServerTLSConfig(t *testing.T) *tls.Config { + t.Helper() + + priv, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + t.Fatalf("rsa.GenerateKey() failed: %v", err) + } + + tmpl := &x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{ + CommonName: "127.0.0.1", + }, + NotBefore: time.Now().Add(-time.Hour), + NotAfter: time.Now().Add(time.Hour), + KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + DNSNames: []string{"localhost"}, + IPAddresses: nil, + } + + der, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, &priv.PublicKey, priv) + if err != nil { + t.Fatalf("x509.CreateCertificate() failed: %v", err) + } + + cert := tls.Certificate{ + Certificate: [][]byte{der}, + PrivateKey: priv, + } + + return &tls.Config{ + Certificates: []tls.Certificate{cert}, + NextProtos: []string{"doq"}, + } +} + +func mustMakeQUICClientTLSConfig() *tls.Config { + return &tls.Config{ + InsecureSkipVerify: true, + NextProtos: []string{"doq"}, + } +} + +func TestServerQUIC_ServeQUIC_TSIGValidSigLeavesTsigStatusNil(t *testing.T) { + const keyName = "tsig-key." + const secret = "MTIzNDU2Nzg5MDEyMzQ1Ng==" + + called := make(chan struct{}, 1) + + config := testConfig("quic", tsigStatusCheckPlugin{ + t: t, + called: called, + check: func(t *testing.T, got error) { + t.Helper() + if got != nil { + t.Fatalf("TsigStatus() = %v, want nil for valid TSIG MAC", got) + } + }, + }) + config.TLSConfig = mustMakeQUICServerTLSConfig(t) + + server, err := NewServerQUIC(transport.QUIC+"://127.0.0.1:0", []*Config{config}) + if err != nil { + t.Fatalf("NewServerQUIC() failed: %v", err) + } + + server.tsigSecret = map[string]string{ + keyName: secret, + } + + pc, err := server.ListenPacket() + if err != nil { + t.Fatalf("ListenPacket() failed: %v", err) + } + defer pc.Close() + + serveErrCh := make(chan error, 1) + go func() { + serveErrCh <- server.ServeQUIC() + }() + + defer func() { + _ = server.Stop() + select { + case <-serveErrCh: + case <-time.After(2 * time.Second): + } + }() + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + conn, err := quic.DialAddr(ctx, pc.LocalAddr().String(), mustMakeQUICClientTLSConfig(), &quic.Config{}) + if err != nil { + t.Fatalf("quic.DialAddr() failed: %v", err) + } + defer conn.CloseWithError(DoQCodeNoError, "") + + stream, err := conn.OpenStreamSync(ctx) + if err != nil { + t.Fatalf("OpenStreamSync() failed: %v", err) + } + + wire := mustPackSignedTSIGQuery(t, keyName, secret, time.Now().Unix()) + + if _, err := stream.Write(AddPrefix(wire)); err != nil { + t.Fatalf("stream.Write() failed: %v", err) + } + if err := stream.Close(); err != nil { + t.Fatalf("stream.Close() failed: %v", err) + } + + respWire, err := readDOQMessage(stream) + if err != nil { + t.Fatalf("readDOQMessage() failed: %v", err) + } + + resp := new(dns.Msg) + if err := resp.Unpack(respWire); err != nil { + t.Fatalf("response unpack failed: %v", err) + } + + select { + case <-called: + case <-time.After(5 * time.Second): + t.Fatal("ServeDNS() was not called") + } +} diff --git a/core/dnsserver/server_test.go b/core/dnsserver/server_test.go index 76e7a61136..36395fcacf 100644 --- a/core/dnsserver/server_test.go +++ b/core/dnsserver/server_test.go @@ -2,7 +2,11 @@ package dnsserver import ( "context" + "errors" + "net" + "sync" "testing" + "time" "github.com/coredns/coredns/plugin" "github.com/coredns/coredns/plugin/pkg/log" @@ -13,12 +17,30 @@ import ( type testPlugin struct{} -func (tp testPlugin) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { +func (tp testPlugin) ServeDNS(_ctx context.Context, _w dns.ResponseWriter, _r *dns.Msg) (int, error) { return 0, nil } func (tp testPlugin) Name() string { return "local" } +// blockingPlugin uses sync.Mutex to simulate extended processing. +type blockingPlugin struct { + sync.Mutex +} + +func (b *blockingPlugin) Name() string { return "blocking" } + +func (b *blockingPlugin) ServeDNS(_ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { + // Respond immediately to avoid waiting in dns.Exchange + m := new(dns.Msg) + m.SetRcodeFormatError(r) + w.WriteMsg(m) + + b.Lock() + defer b.Unlock() + return dns.RcodeSuccess, nil +} + func testConfig(transport string, p plugin.Handler) *Config { c := &Config{ Zone: "example.com.", @@ -29,7 +51,7 @@ func testConfig(transport string, p plugin.Handler) *Config { Stacktrace: false, } - c.AddPlugin(func(next plugin.Handler) plugin.Handler { return p }) + c.AddPlugin(func(_next plugin.Handler) plugin.Handler { return p }) return c } @@ -103,6 +125,44 @@ func TestStacktrace(t *testing.T) { } } +func TestGracefulStopTimeout_Internal(t *testing.T) { + p := new(blockingPlugin) + cfg := testConfig("dns", p) + + s, err := NewServer("127.0.0.1:0", []*Config{cfg}) + if err != nil { + t.Fatalf("NewServer failed: %v", err) + } + + // Shorten the graceful timeout + s.graceTimeout = 500 * time.Millisecond + + pc, err := net.ListenPacket("udp", "127.0.0.1:0") + if err != nil { + t.Fatalf("ListenPacket failed: %v", err) + } + defer pc.Close() + + go s.ServePacket(pc) + udp := pc.LocalAddr().String() + + // Block the handler + p.Lock() + defer p.Unlock() + + m := new(dns.Msg) + m.SetQuestion("example.com.", dns.TypeA) + _, err = dns.Exchange(m, udp) + if err != nil { + t.Fatalf("dns.Exchange failed: %v", err) + } + + err = s.Stop() + if !errors.Is(err, context.DeadlineExceeded) { + t.Fatalf("expected context.DeadlineExceeded, got %v", err) + } +} + func BenchmarkCoreServeDNS(b *testing.B) { s, err := NewServer("127.0.0.1:53", []*Config{testConfig("dns", testPlugin{})}) if err != nil { @@ -115,8 +175,8 @@ func BenchmarkCoreServeDNS(b *testing.B) { m.SetQuestion("aaa.example.com.", dns.TypeTXT) b.ReportAllocs() - b.ResetTimer() - for range b.N { + + for b.Loop() { s.ServeDNS(ctx, w, m) } } diff --git a/core/dnsserver/server_tls.go b/core/dnsserver/server_tls.go index 4c6faa14cf..0c2e390606 100644 --- a/core/dnsserver/server_tls.go +++ b/core/dnsserver/server_tls.go @@ -12,6 +12,7 @@ import ( "github.com/coredns/coredns/plugin/pkg/transport" "github.com/miekg/dns" + "github.com/pires/go-proxyproto" ) // ServerTLS represents an instance of a TLS-over-DNS-server. @@ -53,11 +54,12 @@ func (s *ServerTLS) Serve(l net.Listener) error { // Only fill out the TCP server for this one. s.server[tcp] = &dns.Server{Listener: l, Net: "tcp-tls", + TsigSecret: s.tsigSecret, MaxTCPQueries: tlsMaxQueries, - ReadTimeout: s.readTimeout, - WriteTimeout: s.writeTimeout, + ReadTimeout: s.ReadTimeout, + WriteTimeout: s.WriteTimeout, IdleTimeout: func() time.Duration { - return s.idleTimeout + return s.IdleTimeout }, Handler: dns.HandlerFunc(func(w dns.ResponseWriter, r *dns.Msg) { ctx := context.WithValue(context.Background(), Key{}, s.Server) @@ -71,7 +73,7 @@ func (s *ServerTLS) Serve(l net.Listener) error { } // ServePacket implements caddy.UDPServer interface. -func (s *ServerTLS) ServePacket(p net.PacketConn) error { return nil } +func (s *ServerTLS) ServePacket(_p net.PacketConn) error { return nil } // Listen implements caddy.TCPServer interface. func (s *ServerTLS) Listen() (net.Listener, error) { @@ -79,6 +81,9 @@ func (s *ServerTLS) Listen() (net.Listener, error) { if err != nil { return nil, err } + if s.connPolicy != nil { + l = &proxyproto.Listener{Listener: l, ConnPolicy: s.connPolicy} + } return l, nil } diff --git a/core/dnsserver/server_tls_test.go b/core/dnsserver/server_tls_test.go new file mode 100644 index 0000000000..01774b26ed --- /dev/null +++ b/core/dnsserver/server_tls_test.go @@ -0,0 +1,56 @@ +package dnsserver + +import ( + "errors" + "net" + "testing" +) + +type stubListener struct { + addr net.Addr +} + +func (l *stubListener) Accept() (net.Conn, error) { + return nil, errors.New("test listener closed") +} + +func (l *stubListener) Close() error { + return nil +} + +func (l *stubListener) Addr() net.Addr { + if l.addr != nil { + return l.addr + } + return &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 0} +} + +func TestServerTLSSetsTsigSecret(t *testing.T) { + server, err := NewServerTLS("tls://127.0.0.1:0", []*Config{testConfig("tls", testPlugin{})}) + if err != nil { + t.Fatalf("NewServerTLS() failed: %v", err) + } + + server.tsigSecret = map[string]string{ + "test.": "abcd", + } + + l := &stubListener{} + + err = server.Serve(l) + if err == nil { + t.Fatal("expected Serve() to return from stub listener") + } + + if server.server[tcp] == nil { + t.Fatal("expected tcp server to be initialized") + } + + if server.server[tcp].TsigSecret == nil { + t.Fatal("expected TsigSecret to be propagated") + } + + if got := server.server[tcp].TsigSecret["test."]; got != "abcd" { + t.Fatalf("expected tsig secret %q, got %q", "abcd", got) + } +} diff --git a/core/dnsserver/tsig_test.go b/core/dnsserver/tsig_test.go new file mode 100644 index 0000000000..61cef3e80a --- /dev/null +++ b/core/dnsserver/tsig_test.go @@ -0,0 +1,46 @@ +package dnsserver + +import ( + "context" + "testing" + + "github.com/miekg/dns" +) + +type tsigStatusCheckPlugin struct { + t *testing.T + check func(*testing.T, error) + called chan struct{} +} + +func (p tsigStatusCheckPlugin) Name() string { return "tsig-status-check" } + +func (p tsigStatusCheckPlugin) ServeDNS(_ context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { + p.t.Helper() + if p.called != nil { + p.called <- struct{}{} + } + p.check(p.t, w.TsigStatus()) + + m := new(dns.Msg) + m.SetReply(r) + if err := w.WriteMsg(m); err != nil { + p.t.Fatalf("WriteMsg() failed: %v", err) + } + return dns.RcodeSuccess, nil +} + +func mustPackSignedTSIGQuery(t *testing.T, keyName, secret string, tsigTime int64) []byte { + t.Helper() + + m := new(dns.Msg) + m.SetQuestion("example.com.", dns.TypeA) + m.Id = 0 + m.SetTsig(keyName, dns.HmacSHA256, 300, tsigTime) + + wire, _, err := dns.TsigGenerate(m, secret, "", false) + if err != nil { + t.Fatalf("dns.TsigGenerate() failed: %v", err) + } + return wire +} diff --git a/core/dnsserver/zdirectives.go b/core/dnsserver/zdirectives.go index 9bfcb1f315..cf39df3554 100644 --- a/core/dnsserver/zdirectives.go +++ b/core/dnsserver/zdirectives.go @@ -15,7 +15,11 @@ var Directives = []string{ "geoip", "cancel", "tls", + "proxyproto", "quic", + "grpc_server", + "https", + "https3", "timeouts", "multisocket", "reload", @@ -33,13 +37,13 @@ var Directives = []string{ "dnstap", "local", "dns64", - "acl", "any", "chaos", "loadbalance", "tsig", "cache", "rewrite", + "acl", "header", "dnssec", "autopath", @@ -64,4 +68,5 @@ var Directives = []string{ "on", "sign", "view", + "nomad", } diff --git a/core/plugin/zplugin.go b/core/plugin/zplugin.go index 3aa6b10b5c..476d8c1dca 100644 --- a/core/plugin/zplugin.go +++ b/core/plugin/zplugin.go @@ -27,9 +27,12 @@ import ( _ "github.com/coredns/coredns/plugin/forward" _ "github.com/coredns/coredns/plugin/geoip" _ "github.com/coredns/coredns/plugin/grpc" + _ "github.com/coredns/coredns/plugin/grpc_server" _ "github.com/coredns/coredns/plugin/header" _ "github.com/coredns/coredns/plugin/health" _ "github.com/coredns/coredns/plugin/hosts" + _ "github.com/coredns/coredns/plugin/https" + _ "github.com/coredns/coredns/plugin/https3" _ "github.com/coredns/coredns/plugin/k8s_external" _ "github.com/coredns/coredns/plugin/kubernetes" _ "github.com/coredns/coredns/plugin/loadbalance" @@ -40,8 +43,10 @@ import ( _ "github.com/coredns/coredns/plugin/metrics" _ "github.com/coredns/coredns/plugin/minimal" _ "github.com/coredns/coredns/plugin/multisocket" + _ "github.com/coredns/coredns/plugin/nomad" _ "github.com/coredns/coredns/plugin/nsid" _ "github.com/coredns/coredns/plugin/pprof" + _ "github.com/coredns/coredns/plugin/proxyproto" _ "github.com/coredns/coredns/plugin/quic" _ "github.com/coredns/coredns/plugin/ready" _ "github.com/coredns/coredns/plugin/reload" diff --git a/coredns.1.md b/coredns.1.md index 64daaca484..6418772769 100644 --- a/coredns.1.md +++ b/coredns.1.md @@ -39,6 +39,9 @@ Available options: **-version** : show version and quit. +**-windows-service** +: run as a Windows service (only available on Windows builds, default false). + ## Authors CoreDNS Authors. diff --git a/coremain/run.go b/coremain/run.go index 56d179879a..93050c07f7 100644 --- a/coremain/run.go +++ b/coremain/run.go @@ -80,7 +80,7 @@ func Run() { } // Twiddle your thumbs - instance.Wait() + runService(instance) } // mustLogFatal wraps log.Fatal() in a way that ensures the @@ -89,7 +89,7 @@ func Run() { // enabled. If this process is an upgrade, however, and the user // might not be there anymore, this just logs to the process // log and exits. -func mustLogFatal(args ...interface{}) { +func mustLogFatal(args ...any) { if !caddy.IsUpgrade() { log.SetOutput(os.Stderr) } @@ -192,6 +192,6 @@ var ( gitShortStat string // git diff-index --shortstat gitFilesModified string // git diff-index --name-only HEAD - // Gitcommit contains the commit where we built CoreDNS from. + // GitCommit contains the commit where we built CoreDNS from. GitCommit string ) diff --git a/coremain/run_test.go b/coremain/run_test.go index 84ca7dfa5a..4df352d2de 100644 --- a/coremain/run_test.go +++ b/coremain/run_test.go @@ -149,21 +149,23 @@ func TestDefaultLoader(t *testing.T) { } } - // Create a file but make it unreadable - tmpFile := filepath.Join(tmpDir, "Corefile") - if err := os.WriteFile(tmpFile, []byte("test"), 0644); err != nil { - t.Fatalf("Failed to create test file: %v", err) - } - if err := os.Chmod(tmpFile, 0000); err != nil { - t.Fatalf("Failed to change permissions: %v", err) - } + if runtime.GOOS != "windows" { + // Create a file but make it unreadable + tmpFile := filepath.Join(tmpDir, "Corefile") + if err := os.WriteFile(tmpFile, []byte("test"), 0644); err != nil { + t.Fatalf("Failed to create test file: %v", err) + } + if err := os.Chmod(tmpFile, 0000); err != nil { + t.Fatalf("Failed to change permissions: %v", err) + } - input, err = defaultLoader("dns") - if err == nil { - t.Error("Expected error for unreadable Corefile but got none") - } - if input != nil { - t.Error("Expected nil input for unreadable Corefile") + input, err = defaultLoader("dns") + if err == nil { + t.Error("Expected error for unreadable Corefile but got none") + } + if input != nil { + t.Error("Expected nil input for unreadable Corefile") + } } } diff --git a/coremain/service_other.go b/coremain/service_other.go new file mode 100644 index 0000000000..023c7e701f --- /dev/null +++ b/coremain/service_other.go @@ -0,0 +1,9 @@ +//go:build !windows + +package coremain + +import "github.com/coredns/caddy" + +func runService(instance *caddy.Instance) { + instance.Wait() +} diff --git a/coremain/service_windows.go b/coremain/service_windows.go new file mode 100644 index 0000000000..2a4450caa6 --- /dev/null +++ b/coremain/service_windows.go @@ -0,0 +1,63 @@ +//go:build windows + +package coremain + +import ( + "flag" + "log" + + "github.com/coredns/caddy" + + "golang.org/x/sys/windows/svc" +) + +var windowsService bool + +func init() { + flag.BoolVar(&windowsService, "windows-service", false, "Run as a Windows service") +} + +type corednsService struct { + instance *caddy.Instance +} + +func (s *corednsService) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (svcSpecificEC bool, exitCode uint32) { + changes <- svc.Status{State: svc.StartPending} + changes <- svc.Status{State: svc.Running, Accepts: svc.AcceptStop | svc.AcceptShutdown} + + for req := range r { + switch req.Cmd { + case svc.Interrogate: + changes <- req.CurrentStatus + case svc.Stop, svc.Shutdown: + changes <- svc.Status{State: svc.StopPending} + if s.instance != nil { + s.instance.Stop() + } + return false, 0 + default: + log.Printf("unexpected control request #%d", req.Cmd) + } + } + + return false, 0 +} + +func runService(instance *caddy.Instance) { + if windowsService { + isService, err := svc.IsWindowsService() + if err != nil { + log.Fatalf("failed to determine if running as service: %v", err) + } + if isService { + err = svc.Run("CoreDNS", &corednsService{instance: instance}) + if err != nil { + log.Fatalf("failed to start service: %v", err) + } + return + } else { + log.Printf("Windows service flag provided, but not running as a Windows service.") + } + } + instance.Wait() +} diff --git a/coremain/version.go b/coremain/version.go index 71e9641794..1a3d4ae70f 100644 --- a/coremain/version.go +++ b/coremain/version.go @@ -2,7 +2,7 @@ package coremain // Various CoreDNS constants. const ( - CoreVersion = "1.12.3" + CoreVersion = "1.14.3" CoreName = "CoreDNS" serverType = "dns" ) diff --git a/go.mod b/go.mod index 072a8b342b..41547be888 100644 --- a/go.mod +++ b/go.mod @@ -2,56 +2,64 @@ module github.com/coredns/coredns // Note this minimum version requirement. CoreDNS supports the last two // Go versions. This follows the upstream Go project support. -go 1.24.0 +go 1.25.0 require ( github.com/Azure/azure-sdk-for-go v68.0.0+incompatible github.com/Azure/go-autorest/autorest v0.11.30 github.com/Azure/go-autorest/autorest/azure/auth v0.5.13 - github.com/DataDog/dd-trace-go/v2 v2.2.2 - github.com/apparentlymart/go-cidr v1.1.0 - github.com/aws/aws-sdk-go-v2 v1.38.1 - github.com/aws/aws-sdk-go-v2/config v1.31.2 - github.com/aws/aws-sdk-go-v2/credentials v1.18.6 - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.4 - github.com/aws/aws-sdk-go-v2/service/route53 v1.56.2 - github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.38.2 - github.com/coredns/caddy v1.1.2-0.20241029205200-8de985351a98 + github.com/DataDog/dd-trace-go/v2 v2.8.1 + github.com/apparentlymart/go-cidr v1.1.1 + github.com/aws/aws-sdk-go-v2 v1.41.7 + github.com/aws/aws-sdk-go-v2/config v1.32.17 + github.com/aws/aws-sdk-go-v2/credentials v1.19.16 + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.23 + github.com/aws/aws-sdk-go-v2/service/route53 v1.62.7 + github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.41.7 + github.com/coredns/caddy v1.1.4-0.20250930002214-15135a999495 github.com/dnstap/golang-dnstap v0.4.0 - github.com/expr-lang/expr v1.17.6 + github.com/expr-lang/expr v1.17.8 github.com/farsightsec/golang-framestream v0.3.0 github.com/go-logr/logr v1.4.3 github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 + github.com/hashicorp/nomad/api v0.0.0-20250909143645-a3b86c697f38 // v1.10.5 github.com/infobloxopen/go-trees v0.0.0-20200715205103-96a057b8dfb9 - github.com/matttproud/golang_protobuf_extensions v1.0.4 - github.com/miekg/dns v1.1.68 + github.com/miekg/dns v1.1.72 github.com/opentracing/opentracing-go v1.2.0 github.com/openzipkin-contrib/zipkin-go-opentracing v0.5.0 github.com/openzipkin/zipkin-go v0.4.3 - github.com/oschwald/geoip2-golang v1.13.0 - github.com/prometheus/client_golang v1.23.0 + github.com/oschwald/geoip2-golang/v2 v2.1.0 + github.com/prometheus/client_golang v1.23.2 github.com/prometheus/client_model v0.6.2 - github.com/prometheus/common v0.65.0 - github.com/quic-go/quic-go v0.54.0 - go.etcd.io/etcd/api/v3 v3.6.4 - go.etcd.io/etcd/client/v3 v3.6.4 + github.com/prometheus/common v0.67.5 + github.com/quic-go/quic-go v0.59.0 + github.com/stretchr/testify v1.11.1 + go.etcd.io/etcd/api/v3 v3.6.11 + go.etcd.io/etcd/client/v3 v3.6.11 go.uber.org/automaxprocs v1.6.0 - golang.org/x/crypto v0.41.0 - golang.org/x/sys v0.35.0 - google.golang.org/api v0.248.0 - google.golang.org/grpc v1.75.0 - google.golang.org/protobuf v1.36.8 - k8s.io/api v0.33.4 - k8s.io/apimachinery v0.33.4 - k8s.io/client-go v0.33.4 - k8s.io/klog/v2 v2.130.1 - sigs.k8s.io/mcs-api v0.2.0 + golang.org/x/crypto v0.50.0 + golang.org/x/sys v0.44.0 + google.golang.org/api v0.278.0 + google.golang.org/grpc v1.81.0 + google.golang.org/protobuf v1.36.11 + k8s.io/api v0.35.4 + k8s.io/apimachinery v0.35.4 + k8s.io/client-go v0.35.4 + k8s.io/klog/v2 v2.140.0 + sigs.k8s.io/mcs-api v0.5.0 ) require ( - cloud.google.com/go/auth v0.16.5 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 + github.com/pires/go-proxyproto v0.12.0 + github.com/prometheus/exporter-toolkit v0.16.0 + golang.org/x/net v0.53.0 +) + +require ( + cloud.google.com/go/auth v0.20.0 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect - cloud.google.com/go/compute/metadata v0.8.0 // indirect + cloud.google.com/go/compute/metadata v0.9.0 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest/adal v0.9.22 // indirect github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 // indirect @@ -59,132 +67,148 @@ require ( github.com/Azure/go-autorest/autorest/to v0.2.0 // indirect github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect - github.com/DataDog/appsec-internal-go v1.13.0 // indirect - github.com/DataDog/datadog-agent/comp/core/tagger/origindetection v0.67.0 // indirect - github.com/DataDog/datadog-agent/pkg/obfuscate v0.67.0 // indirect - github.com/DataDog/datadog-agent/pkg/proto v0.67.0 // indirect - github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.67.0 // indirect - github.com/DataDog/datadog-agent/pkg/trace v0.67.0 // indirect - github.com/DataDog/datadog-agent/pkg/util/log v0.67.0 // indirect - github.com/DataDog/datadog-agent/pkg/util/scrubber v0.67.0 // indirect - github.com/DataDog/datadog-agent/pkg/version v0.67.0 // indirect - github.com/DataDog/datadog-go/v5 v5.6.0 // indirect - github.com/DataDog/go-libddwaf/v4 v4.3.2 // indirect - github.com/DataDog/go-runtime-metrics-internal v0.0.4-0.20250721125240-fdf1ef85b633 // indirect - github.com/DataDog/go-sqllexer v0.1.6 // indirect - github.com/DataDog/go-tuf v1.1.0-0.5.2 // indirect - github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes v0.27.0 // indirect - github.com/DataDog/sketches-go v1.4.7 // indirect - github.com/Masterminds/semver/v3 v3.3.1 // indirect + github.com/DataDog/datadog-agent/comp/core/tagger/origindetection v0.77.0 // indirect + github.com/DataDog/datadog-agent/pkg/obfuscate v0.77.0 // indirect + github.com/DataDog/datadog-agent/pkg/opentelemetry-mapping-go/otlp/attributes v0.77.0 // indirect + github.com/DataDog/datadog-agent/pkg/proto v0.77.0 // indirect + github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.77.0 // indirect + github.com/DataDog/datadog-agent/pkg/template v0.77.0 // indirect + github.com/DataDog/datadog-agent/pkg/trace v0.77.0 // indirect + github.com/DataDog/datadog-agent/pkg/trace/log v0.77.0 // indirect + github.com/DataDog/datadog-agent/pkg/trace/otel v0.77.0 // indirect + github.com/DataDog/datadog-agent/pkg/trace/stats v0.77.0 // indirect + github.com/DataDog/datadog-agent/pkg/trace/traceutil v0.77.0 // indirect + github.com/DataDog/datadog-agent/pkg/util/log v0.77.0 // indirect + github.com/DataDog/datadog-agent/pkg/util/scrubber v0.77.0 // indirect + github.com/DataDog/datadog-agent/pkg/version v0.77.0 // indirect + github.com/DataDog/datadog-go/v5 v5.8.3 // indirect + github.com/DataDog/go-libddwaf/v4 v4.9.0 // indirect + github.com/DataDog/go-runtime-metrics-internal v0.0.4-0.20260217080614-b0f4edc38a6d // indirect + github.com/DataDog/go-sqllexer v0.1.13 // indirect + github.com/DataDog/go-tuf v1.1.1-0.5.2 // indirect + github.com/DataDog/sketches-go v1.4.8 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.4 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.4 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.0 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.4 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.28.2 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.33.2 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.38.0 // indirect - github.com/aws/smithy-go v1.22.5 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.23 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.23 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.24 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.9 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.23 // indirect + github.com/aws/aws-sdk-go-v2/service/signin v1.0.11 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.30.17 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.21 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.42.1 // indirect + github.com/aws/smithy-go v1.25.1 // indirect github.com/beorn7/perks v1.0.1 // indirect + github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 // indirect github.com/coreos/go-semver v0.3.1 // indirect - github.com/coreos/go-systemd/v22 v22.5.0 // indirect + github.com/coreos/go-systemd/v22 v22.7.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dimchansky/utfbom v1.1.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect - github.com/eapache/queue/v2 v2.0.0-20230407133247-75960ed334e4 // indirect - github.com/ebitengine/purego v0.8.3 // indirect - github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/ebitengine/purego v0.10.0 // indirect + github.com/emicklei/go-restful/v3 v3.12.2 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 // indirect - github.com/fxamacker/cbor/v2 v2.7.0 // indirect + github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/swag v0.23.0 // indirect - github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.5.2 // indirect + github.com/golang-jwt/jwt/v5 v5.3.0 // indirect github.com/golang/protobuf v1.5.4 // indirect - github.com/google/gnostic-models v0.6.9 // indirect + github.com/google/gnostic-models v0.7.0 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/google/s2a-go v0.1.9 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect - github.com/googleapis/gax-go/v2 v2.15.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect - github.com/hashicorp/go-version v1.7.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.15 // indirect + github.com/googleapis/gax-go/v2 v2.22.0 // indirect + github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.4 // indirect + github.com/hashicorp/cronexpr v1.1.3 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/go-rootcerts v1.0.2 // indirect + github.com/hashicorp/go-version v1.8.0 // indirect github.com/josharian/intern v1.0.0 // indirect + github.com/jpillora/backoff v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/compress v1.18.4 // indirect + github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/kylelemons/godebug v1.1.0 // indirect - github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 // indirect + github.com/linkdata/deadlock v0.5.5 // indirect + github.com/lufia/plan9stats v0.0.0-20260216142805-b3301c5f2a88 // indirect github.com/mailru/easyjson v0.7.7 // indirect + github.com/mdlayher/socket v0.5.1 // indirect + github.com/mdlayher/vsock v1.2.1 // indirect + github.com/minio/simdjson-go v0.4.5 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/onsi/ginkgo/v2 v2.22.1 // indirect - github.com/onsi/gomega v1.36.2 // indirect + github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492 // indirect - github.com/oschwald/maxminddb-golang v1.13.0 // indirect + github.com/oschwald/maxminddb-golang/v2 v2.1.1 // indirect github.com/outcaste-io/ristretto v0.2.3 // indirect - github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c // indirect + github.com/petermattis/goid v0.0.0-20260226131333-17d1149c6ac6 // indirect + github.com/philhofer/fwd v1.2.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect - github.com/prometheus/procfs v0.16.1 // indirect + github.com/prometheus/procfs v0.19.2 // indirect github.com/puzpuzpuz/xsync/v3 v3.5.1 // indirect - github.com/secure-systems-lab/go-securesystemslib v0.9.0 // indirect - github.com/shirou/gopsutil/v4 v4.25.3 // indirect - github.com/spf13/pflag v1.0.5 // indirect - github.com/stretchr/testify v1.10.0 // indirect - github.com/tinylib/msgp v1.2.5 // indirect - github.com/tklauser/go-sysconf v0.3.15 // indirect - github.com/tklauser/numcpus v0.10.0 // indirect + github.com/quic-go/qpack v0.6.0 // indirect + github.com/secure-systems-lab/go-securesystemslib v0.10.0 // indirect + github.com/shirou/gopsutil/v4 v4.26.2 // indirect + github.com/spf13/pflag v1.0.10 // indirect + github.com/tinylib/msgp v1.6.3 // indirect + github.com/tklauser/go-sysconf v0.3.16 // indirect + github.com/tklauser/numcpus v0.11.0 // indirect + github.com/trailofbits/go-mutexasserts v0.0.0-20250514102930-c1f3d2e37561 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect - go.etcd.io/etcd/client/pkg/v3 v3.6.4 // indirect - go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.opentelemetry.io/collector/component v1.31.0 // indirect - go.opentelemetry.io/collector/featuregate v1.31.0 // indirect - go.opentelemetry.io/collector/internal/telemetry v0.125.0 // indirect - go.opentelemetry.io/collector/pdata v1.31.0 // indirect - go.opentelemetry.io/collector/semconv v0.125.0 // indirect - go.opentelemetry.io/contrib/bridges/otelzap v0.10.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect - go.opentelemetry.io/otel v1.37.0 // indirect - go.opentelemetry.io/otel/log v0.11.0 // indirect - go.opentelemetry.io/otel/metric v1.37.0 // indirect - go.opentelemetry.io/otel/sdk v1.37.0 // indirect - go.opentelemetry.io/otel/trace v1.37.0 // indirect + go.etcd.io/etcd/client/pkg/v3 v3.6.11 // indirect + go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/collector/component v1.51.1-0.20260205185216-81bc641f26c0 // indirect + go.opentelemetry.io/collector/featuregate v1.51.1-0.20260205185216-81bc641f26c0 // indirect + go.opentelemetry.io/collector/pdata v1.51.1-0.20260205185216-81bc641f26c0 // indirect + go.opentelemetry.io/collector/pdata/pprofile v0.145.1-0.20260205185216-81bc641f26c0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 // indirect + go.opentelemetry.io/otel v1.43.0 // indirect + go.opentelemetry.io/otel/metric v1.43.0 // indirect + go.opentelemetry.io/otel/trace v1.43.0 // indirect + go.opentelemetry.io/proto/otlp v1.9.0 // indirect go.uber.org/atomic v1.11.0 // indirect - go.uber.org/mock v0.5.0 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.27.0 // indirect - golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect - golang.org/x/mod v0.26.0 // indirect - golang.org/x/net v0.43.0 // indirect - golang.org/x/oauth2 v0.30.0 // indirect - golang.org/x/sync v0.16.0 // indirect - golang.org/x/term v0.34.0 // indirect - golang.org/x/text v0.28.0 // indirect - golang.org/x/time v0.12.0 // indirect - golang.org/x/tools v0.35.0 // indirect - golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c // indirect - gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect + go.uber.org/zap v1.27.1 // indirect + go.yaml.in/yaml/v2 v2.4.4 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect + golang.org/x/exp v0.0.0-20260209203927-2842357ff358 // indirect + golang.org/x/mod v0.34.0 // indirect + golang.org/x/oauth2 v0.36.0 // indirect + golang.org/x/sync v0.20.0 // indirect + golang.org/x/term v0.42.0 // indirect + golang.org/x/text v0.36.0 // indirect + golang.org/x/time v0.15.0 // indirect + golang.org/x/tools v0.43.0 // indirect + golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260319201613-d00831a3d3e7 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260427160629-7cedc36a6bc4 // indirect + gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/ini.v1 v1.67.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect - k8s.io/utils v0.0.0-20241210054802-24370beab758 // indirect - sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect + k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 // indirect + k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 // indirect + sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect sigs.k8s.io/randfill v1.0.0 // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect - sigs.k8s.io/yaml v1.4.0 // indirect + sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect + sigs.k8s.io/yaml v1.6.0 // indirect ) diff --git a/go.sum b/go.sum index 7d63921783..d800e90185 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,9 @@ -cloud.google.com/go/auth v0.16.5 h1:mFWNQ2FEVWAliEQWpAdH80omXFokmrnbDhUS9cBywsI= -cloud.google.com/go/auth v0.16.5/go.mod h1:utzRfHMP+Vv0mpOkTRQoWD2q3BatTOoWbA7gCc2dUhQ= +cloud.google.com/go/auth v0.20.0 h1:kXTssoVb4azsVDoUiF8KvxAqrsQcQtB53DcSgta74CA= +cloud.google.com/go/auth v0.20.0/go.mod h1:942/yi/itH1SsmpyrbnTMDgGfdy2BUqIKyd0cyYLc5Q= cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= -cloud.google.com/go/compute/metadata v0.8.0 h1:HxMRIbao8w17ZX6wBnjhcDkW6lTFpgcaobyVfZWqRLA= -cloud.google.com/go/compute/metadata v0.8.0/go.mod h1:sYOGTp851OV9bOFJ9CH7elVvyzopvWQFNNghtDQ/Biw= +cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= +cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU= github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= @@ -29,92 +29,102 @@ github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+Z github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= -github.com/DataDog/appsec-internal-go v1.13.0 h1:aO6DmHYsAU8BNFuvYJByhMKGgcQT3WAbj9J/sgAJxtA= -github.com/DataDog/appsec-internal-go v1.13.0/go.mod h1:9YppRCpElfGX+emXOKruShFYsdPq7WEPq/Fen4tYYpk= -github.com/DataDog/datadog-agent/comp/core/tagger/origindetection v0.67.0 h1:2mEwRWvhIPHMPK4CMD8iKbsrYBxeMBSuuCXumQAwShU= -github.com/DataDog/datadog-agent/comp/core/tagger/origindetection v0.67.0/go.mod h1:ejJHsyJTG7NU6c6TDbF7dmckD3g+AUGSdiSXy+ZyaCE= -github.com/DataDog/datadog-agent/pkg/obfuscate v0.67.0 h1:NcvyDVIUA0NbBDbp7QJnsYhoBv548g8bXq886795mCQ= -github.com/DataDog/datadog-agent/pkg/obfuscate v0.67.0/go.mod h1:1oPcs3BUTQhiTkmk789rb7ob105MxNV6OuBa28BdukQ= -github.com/DataDog/datadog-agent/pkg/proto v0.67.0 h1:7dO6mKYRb7qSiXEu7Q2mfeKbhp4hykCAULy4BfMPmsQ= -github.com/DataDog/datadog-agent/pkg/proto v0.67.0/go.mod h1:bKVXB7pxBg0wqXF6YSJ+KU6PeCWKDyJj83kUH1ab+7o= -github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.67.0 h1:tB+H/TFlFl97ON6v+r9PXPrM+X5qUTc+UPAWF9pA0Fc= -github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.67.0/go.mod h1:HuNrai9MbPj2ZciBLSfj5wQl8CJOOkrH5xzEPezRNT4= -github.com/DataDog/datadog-agent/pkg/trace v0.67.0 h1:dqt+/nObo0JKyaEqIMZgfqGZbx9TfEHpCkrjQ/zzH7k= -github.com/DataDog/datadog-agent/pkg/trace v0.67.0/go.mod h1:zmZoEtKvOnaKHbJGBKH3a4xuyPrSfBaF0ZE3Q3rCoDw= -github.com/DataDog/datadog-agent/pkg/util/log v0.67.0 h1:xrH15QNqeJZkYoXYi44VCIvGvTwlQ3z2iT2QVTGiT7s= -github.com/DataDog/datadog-agent/pkg/util/log v0.67.0/go.mod h1:dfVLR+euzEyg1CeiExgJQq1c1dod42S6IeiRPj8H7Yk= -github.com/DataDog/datadog-agent/pkg/util/scrubber v0.67.0 h1:aIWF85OKxXGo7rVyqJ7jm7lm2qCQrgyXzYyFuw0T2EQ= -github.com/DataDog/datadog-agent/pkg/util/scrubber v0.67.0/go.mod h1:Lfap5FuM4b/Pw9IrTuAvWBWZEmXOvZhCya3dYv4G8O0= -github.com/DataDog/datadog-agent/pkg/version v0.67.0 h1:TB8H8r+laB1Qdttvvc6XJVyLGxp8E6j2f2Mh5IPbYmQ= -github.com/DataDog/datadog-agent/pkg/version v0.67.0/go.mod h1:kvAw/WbI7qLAsDI2wHabZfM7Cv2zraD3JA3323GEB+8= -github.com/DataDog/datadog-go/v5 v5.6.0 h1:2oCLxjF/4htd55piM75baflj/KoE6VYS7alEUqFvRDw= -github.com/DataDog/datadog-go/v5 v5.6.0/go.mod h1:K9kcYBlxkcPP8tvvjZZKs/m1edNAUFzBbdpTUKfCsuw= -github.com/DataDog/dd-trace-go/v2 v2.2.2 h1:t7RCS6et5z+xrvM9dqUPtCGoNOWTj0pcApzbktMZi2k= -github.com/DataDog/dd-trace-go/v2 v2.2.2/go.mod h1:xvdSukALA8/NnVfTAnxqkZfLLO/6Vi4y5ooxfVy6dfk= -github.com/DataDog/go-libddwaf/v4 v4.3.2 h1:YGvW2Of1C4e1yU+p7iibmhN2zEOgi9XEchbhQjBxb/A= -github.com/DataDog/go-libddwaf/v4 v4.3.2/go.mod h1:/AZqP6zw3qGJK5mLrA0PkfK3UQDk1zCI2fUNCt4xftE= -github.com/DataDog/go-runtime-metrics-internal v0.0.4-0.20250721125240-fdf1ef85b633 h1:ZRLR9Lbym748e8RznWzmSoK+OfV+8qW6SdNYA4/IqdA= -github.com/DataDog/go-runtime-metrics-internal v0.0.4-0.20250721125240-fdf1ef85b633/go.mod h1:YFoTl1xsMzdSRFIu33oCSPS/3+HZAPGpO3oOM96wXCM= -github.com/DataDog/go-sqllexer v0.1.6 h1:skEXpWEVCpeZFIiydoIa2f2rf+ymNpjiIMqpW4w3YAk= -github.com/DataDog/go-sqllexer v0.1.6/go.mod h1:GGpo1h9/BVSN+6NJKaEcJ9Jn44Hqc63Rakeb+24Mjgo= -github.com/DataDog/go-tuf v1.1.0-0.5.2 h1:4CagiIekonLSfL8GMHRHcHudo1fQnxELS9g4tiAupQ4= -github.com/DataDog/go-tuf v1.1.0-0.5.2/go.mod h1:zBcq6f654iVqmkk8n2Cx81E1JnNTMOAx1UEO/wZR+P0= -github.com/DataDog/gostackparse v0.7.0 h1:i7dLkXHvYzHV308hnkvVGDL3BR4FWl7IsXNPz/IGQh4= -github.com/DataDog/gostackparse v0.7.0/go.mod h1:lTfqcJKqS9KnXQGnyQMCugq3u1FP6UZMfWR0aitKFMM= -github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes v0.27.0 h1:5US5SqqhfkZkg/E64uvn7YmeTwnudJHtlPEH/LOT99w= -github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes v0.27.0/go.mod h1:VRo4D6rj92AExpVBlq3Gcuol9Nm1bber12KyxRjKGWw= -github.com/DataDog/sketches-go v1.4.7 h1:eHs5/0i2Sdf20Zkj0udVFWuCrXGRFig2Dcfm5rtcTxc= -github.com/DataDog/sketches-go v1.4.7/go.mod h1:eAmQ/EBmtSO+nQp7IZMZVRPT4BQTmIc5RZQ+deGlTPM= -github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4= -github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/DataDog/datadog-agent/comp/core/tagger/origindetection v0.77.0 h1:Lu/HEo5svx/UwE7XWh8vOrEHCrVRsein9X1N0jGK5bo= +github.com/DataDog/datadog-agent/comp/core/tagger/origindetection v0.77.0/go.mod h1:+Ty3r23MjcmMSkr8JbFeqA3utgtc1wxsZ0KaQ9CzoWA= +github.com/DataDog/datadog-agent/pkg/obfuscate v0.77.0 h1:mrHaNnDAIOFAVYhCqDpkenUtbadswHN68ZlG5krv40o= +github.com/DataDog/datadog-agent/pkg/obfuscate v0.77.0/go.mod h1:E6RGAcEOr/d8wsV5/khYHvaHkijWex6dfNEvsBIgR7A= +github.com/DataDog/datadog-agent/pkg/opentelemetry-mapping-go/otlp/attributes v0.77.0 h1:g1d9d1CfG54WjXgvkysTFL9yjXexWeDbYssQaf1PG6c= +github.com/DataDog/datadog-agent/pkg/opentelemetry-mapping-go/otlp/attributes v0.77.0/go.mod h1:N/AB9VGpVwHxCcyX+7GLNYMfnTZvn65vp5cHb5Ed0ow= +github.com/DataDog/datadog-agent/pkg/proto v0.77.0 h1:21nDAKD+LdxZz2pMsLAQTZ+w9Z4JqecjKpt8xY1b7Ig= +github.com/DataDog/datadog-agent/pkg/proto v0.77.0/go.mod h1:g2QYJe1CheZdssiDQpSYWra9hORkh+S3WO8aOqDNLkg= +github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.77.0 h1:SxFGFN/Dd/uREaUTxuVTi0R7fRABzvUtu32YOXcjf6c= +github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.77.0/go.mod h1:TpW5ZwsQTrlRBPjtZH5/OFwpmOqxV/v2i9BiF4Xfcac= +github.com/DataDog/datadog-agent/pkg/template v0.77.0 h1:sUbTCoQyU9kXMc6/aDd4YTP2pe9PlNkgySM11ydMEbE= +github.com/DataDog/datadog-agent/pkg/template v0.77.0/go.mod h1:ZUjICHSlN0of0cmWrYk9Pof0DV0eqHSpTUK1NTnN26Y= +github.com/DataDog/datadog-agent/pkg/trace v0.77.0 h1:B7M6IW0sd60XnLfeP6HEneeR4lRnaNtI9bboU4R1cV0= +github.com/DataDog/datadog-agent/pkg/trace v0.77.0/go.mod h1:+7zMNPjHTDidiphECirrpq5jLK09S9kmLtGRv7di29Q= +github.com/DataDog/datadog-agent/pkg/trace/log v0.77.0 h1:2VY1byEA2XnYVg7+eLQSTgX2f76ZCf/AwpCBJDXCiDc= +github.com/DataDog/datadog-agent/pkg/trace/log v0.77.0/go.mod h1:thnxBOGfMU9uRlFUClXud6J7DdI8qWtElELSds5jqts= +github.com/DataDog/datadog-agent/pkg/trace/otel v0.77.0 h1:9zSto72E+wSETaKs47Yiq5D9du9H71IqWgbDJzGpzSs= +github.com/DataDog/datadog-agent/pkg/trace/otel v0.77.0/go.mod h1:IxBidgqUt8aBrKYq4VKynBHWYZYNoflk+0+m7w+lfbI= +github.com/DataDog/datadog-agent/pkg/trace/stats v0.77.0 h1:InA6JO5R8TFAUcKRxsdmIF1hJpVZqVO5Aqux+Iw/2V0= +github.com/DataDog/datadog-agent/pkg/trace/stats v0.77.0/go.mod h1:iZVotmInV8qaU6Q5h+tsKk4CBYupDcOgTzonzLlMi0k= +github.com/DataDog/datadog-agent/pkg/trace/traceutil v0.77.0 h1:b/2+uA/cG2xEV0LzgwnxloMFWe5sdJa3xtaQhSGN0+s= +github.com/DataDog/datadog-agent/pkg/trace/traceutil v0.77.0/go.mod h1:csT+8o3GOUjhKPs/GqWMb5Zh4iQpuZ/HZQ4Z5ls8Sak= +github.com/DataDog/datadog-agent/pkg/util/log v0.77.0 h1:YFa+8kIg2qQZca9zvowtwCPdHDhGMcTIF+PMIQsLSRs= +github.com/DataDog/datadog-agent/pkg/util/log v0.77.0/go.mod h1:DFK2U5RcB8/BcObgmVEZ4VxqXUi2t7y2svLTtJwQqeo= +github.com/DataDog/datadog-agent/pkg/util/scrubber v0.77.0 h1:dd0W9e39rv0R3DSgnaurVnQ43/jX/juqQPwLpGAJgFs= +github.com/DataDog/datadog-agent/pkg/util/scrubber v0.77.0/go.mod h1:nkhevws2pJvoXSGhjc8wuTbptNQ9ECRBjwVr4hSvoq0= +github.com/DataDog/datadog-agent/pkg/version v0.77.0 h1:fxpMWuoaRHS5vHzCNHftvJ6wdQrGhEmuozjjl8wZG5k= +github.com/DataDog/datadog-agent/pkg/version v0.77.0/go.mod h1:h9eJjfeTHlYYv+kzq6n3rQ07qXGirdCCacn1Ryu4TFQ= +github.com/DataDog/datadog-go/v5 v5.8.3 h1:s58CUJ9s8lezjhTNJO/SxkPBv2qZjS3ktpRSqGF5n0s= +github.com/DataDog/datadog-go/v5 v5.8.3/go.mod h1:K9kcYBlxkcPP8tvvjZZKs/m1edNAUFzBbdpTUKfCsuw= +github.com/DataDog/dd-trace-go/v2 v2.8.1 h1:O/lPXXcJof4hqfcBGsL6p/PiVa5xTfvYzv5iv/4S66U= +github.com/DataDog/dd-trace-go/v2 v2.8.1/go.mod h1:IVkBpsq66Cw/YIRM/Te3pl2F0M9n4zguAB2ReGczWeo= +github.com/DataDog/go-libddwaf/v4 v4.9.0 h1:a788e37iuH7sR9uIYHkulvTnp2FkXTiZ3yY/kuaHgZE= +github.com/DataDog/go-libddwaf/v4 v4.9.0/go.mod h1:/AZqP6zw3qGJK5mLrA0PkfK3UQDk1zCI2fUNCt4xftE= +github.com/DataDog/go-runtime-metrics-internal v0.0.4-0.20260217080614-b0f4edc38a6d h1:cH9Bm0tJ8FEQbA4FRi0iRm7Zr/5Lata/Or31c+Dth0E= +github.com/DataDog/go-runtime-metrics-internal v0.0.4-0.20260217080614-b0f4edc38a6d/go.mod h1:yDuvU+Ak1TKwgd4K8DNcpJmUrrK8ONLkBMGNAppmBRk= +github.com/DataDog/go-sqllexer v0.1.13 h1:HhT2G21y7SDZYQx9i1b+3Sy/CHhESHet/YKMSm06XcE= +github.com/DataDog/go-sqllexer v0.1.13/go.mod h1:vOw7Ia7z+z6nl3zGZlLIZe0vQlPtCPR906WIPBJadxc= +github.com/DataDog/go-tuf v1.1.1-0.5.2 h1:YWvghV4ZvrQsPcUw8IOUMSDpqc3W5ruOIC+KJxPknv0= +github.com/DataDog/go-tuf v1.1.1-0.5.2/go.mod h1:zBcq6f654iVqmkk8n2Cx81E1JnNTMOAx1UEO/wZR+P0= +github.com/DataDog/sketches-go v1.4.8 h1:pFk9BNn+Rzv8IMIoPUttoOpOr3bJOqU3P6EP5wK+Lv8= +github.com/DataDog/sketches-go v1.4.8/go.mod h1:a/wjRUqzqtGS8qRHRPDCs4EAQfmvPDZGDlMIF5mxXOE= +github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= +github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -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/aws/aws-sdk-go-v2 v1.38.1 h1:j7sc33amE74Rz0M/PoCpsZQ6OunLqys/m5antM0J+Z8= -github.com/aws/aws-sdk-go-v2 v1.38.1/go.mod h1:9Q0OoGQoboYIAJyslFyF1f5K1Ryddop8gqMhWx/n4Wg= -github.com/aws/aws-sdk-go-v2/config v1.31.2 h1:NOaSZpVGEH2Np/c1toSeW0jooNl+9ALmsUTZ8YvkJR0= -github.com/aws/aws-sdk-go-v2/config v1.31.2/go.mod h1:17ft42Yb2lF6OigqSYiDAiUcX4RIkEMY6XxEMJsrAes= -github.com/aws/aws-sdk-go-v2/credentials v1.18.6 h1:AmmvNEYrru7sYNJnp3pf57lGbiarX4T9qU/6AZ9SucU= -github.com/aws/aws-sdk-go-v2/credentials v1.18.6/go.mod h1:/jdQkh1iVPa01xndfECInp1v1Wnp70v3K4MvtlLGVEc= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.4 h1:lpdMwTzmuDLkgW7086jE94HweHCqG+uOJwHf3LZs7T0= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.4/go.mod h1:9xzb8/SV62W6gHQGC/8rrvgNXU6ZoYM3sAIJCIrXJxY= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.4 h1:IdCLsiiIj5YJ3AFevsewURCPV+YWUlOW8JiPhoAy8vg= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.4/go.mod h1:l4bdfCD7XyyZA9BolKBo1eLqgaJxl0/x91PL4Yqe0ao= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.4 h1:j7vjtr1YIssWQOMeOWRbh3z8g2oY/xPjnZH2gLY4sGw= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.4/go.mod h1:yDmJgqOiH4EA8Hndnv4KwAo8jCGTSnM5ASG1nBI+toA= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.0 h1:6+lZi2JeGKtCraAj1rpoZfKqnQ9SptseRZioejfUOLM= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.0/go.mod h1:eb3gfbVIxIoGgJsi9pGne19dhCBpK6opTYpQqAmdy44= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.4 h1:ueB2Te0NacDMnaC+68za9jLwkjzxGWm0KB5HTUHjLTI= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.4/go.mod h1:nLEfLnVMmLvyIG58/6gsSA03F1voKGaCfHV7+lR8S7s= -github.com/aws/aws-sdk-go-v2/service/route53 v1.56.2 h1:6QKyfbweIsjt1kvE8rw+LeDxmCt1uvI8ywRe2cYOpQo= -github.com/aws/aws-sdk-go-v2/service/route53 v1.56.2/go.mod h1:Ro0zSeA7hRAhX04QgnUAc8MvvQO74wg/S15wzA/mxgo= -github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.38.2 h1:BvsTLbavBCIWhGav8Rm/vPPyyhDwkOMSi0pkGaohCag= -github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.38.2/go.mod h1:KwGTe+BJ29tKBIkVuZgDzlw70aS4BZxLJVqAjwnhfRQ= -github.com/aws/aws-sdk-go-v2/service/sso v1.28.2 h1:ve9dYBB8CfJGTFqcQ3ZLAAb/KXWgYlgu/2R2TZL2Ko0= -github.com/aws/aws-sdk-go-v2/service/sso v1.28.2/go.mod h1:n9bTZFZcBa9hGGqVz3i/a6+NG0zmZgtkB9qVVFDqPA8= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.33.2 h1:pd9G9HQaM6UZAZh19pYOkpKSQkyQQ9ftnl/LttQOcGI= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.33.2/go.mod h1:eknndR9rU8UpE/OmFpqU78V1EcXPKFTTm5l/buZYgvM= -github.com/aws/aws-sdk-go-v2/service/sts v1.38.0 h1:iV1Ko4Em/lkJIsoKyGfc0nQySi+v0Udxr6Igq+y9JZc= -github.com/aws/aws-sdk-go-v2/service/sts v1.38.0/go.mod h1:bEPcjW7IbolPfK67G1nilqWyoxYMSPrDiIQ3RdIdKgo= -github.com/aws/smithy-go v1.22.5 h1:P9ATCXPMb2mPjYBgueqJNCA5S9UfktsW0tTxi+a7eqw= -github.com/aws/smithy-go v1.22.5/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= +github.com/apparentlymart/go-cidr v1.1.1 h1:oEEk8CE0HP0YpHxsegk/TaOtR2FLHdWv4p3eM4ceUwg= +github.com/apparentlymart/go-cidr v1.1.1/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc= +github.com/aws/aws-sdk-go-v2 v1.41.7 h1:DWpAJt66FmnnaRIOT/8ASTucrvuDPZASqhhLey6tLY8= +github.com/aws/aws-sdk-go-v2 v1.41.7/go.mod h1:4LAfZOPHNVNQEckOACQx60Y8pSRjIkNZQz1w92xpMJc= +github.com/aws/aws-sdk-go-v2/config v1.32.17 h1:FpL4/758/diKwqbytU0prpuiu60fgXKUWCpDJtApclU= +github.com/aws/aws-sdk-go-v2/config v1.32.17/go.mod h1:OXqUMzgXytfoF9JaKkhrOYsyh72t9G+MJH8mMRaexOE= +github.com/aws/aws-sdk-go-v2/credentials v1.19.16 h1:r3RJBuU7X9ibt8RHbMjWE6y60QbKBiII6wSrXnapxSU= +github.com/aws/aws-sdk-go-v2/credentials v1.19.16/go.mod h1:6cx7zqDENJDbBIIWX6P8s0h6hqHC8Avbjh9Dseo27ug= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.23 h1:UuSfcORqNSz/ey3VPRS8TcVH2Ikf0/sC+Hdj400QI6U= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.23/go.mod h1:+G/OSGiOFnSOkYloKj/9M35s74LgVAdJBSD5lsFfqKg= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.23 h1:GpT/TrnBYuE5gan2cZbTtvP+JlHsutdmlV2YfEyNde0= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.23/go.mod h1:xYWD6BS9ywC5bS3sz9Xh04whO/hzK2plt2Zkyrp4JuA= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.23 h1:bpd8vxhlQi2r1hiueOw02f/duEPTMK59Q4QMAoTTtTo= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.23/go.mod h1:15DfR2nw+CRHIk0tqNyifu3G1YdAOy68RftkhMDDwYk= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.24 h1:OQqn11BtaYv1WLUowvcA30MpzIu8Ti4pcLPIIyoKZrA= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.24/go.mod h1:X5ZJyfwVrWA96GzPmUCWFQaEARPR7gCrpq2E92PJwAE= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.9 h1:FLudkZLt5ci0ozzgkVo8BJGwvqNaZbTWb3UcucAateA= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.9/go.mod h1:w7wZ/s9qK7c8g4al+UyoF1Sp/Z45UwMGcqIzLWVQHWk= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.23 h1:pbrxO/kuIwgEsOPLkaHu0O+m4fNgLU8B3vxQ+72jTPw= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.23/go.mod h1:/CMNUqoj46HpS3MNRDEDIwcgEnrtZlKRaHNaHxIFpNA= +github.com/aws/aws-sdk-go-v2/service/route53 v1.62.7 h1:twRRMmtSITnt/rrp+D7UDLzE5pKMZe759aalkUdN+OY= +github.com/aws/aws-sdk-go-v2/service/route53 v1.62.7/go.mod h1:ztM1lr+sRoCAI8336ZUvlRPbToue0d3gE/wd6jomSJ8= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.41.7 h1:JUGKqUnJHbXpS8uyuICP/zpQ+vXUIXW2zTEqjMLCqrY= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.41.7/go.mod h1:l/cqI7ujYqBuTR6Ll13d9/gG/uUdlVzJ1UDltEEBTOo= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.11 h1:TdJ+HdzOBhU8+iVAOGUTU63VXopcumCOF1paFulHWZc= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.11/go.mod h1:R82ZRExE/nheo0N+T8zHPcLRTcH8MGsnR3BiVGX0TwI= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.17 h1:7byT8HUWrgoRp6sXjxtZwgOKfhss5fW6SkLBtqzgRoE= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.17/go.mod h1:xNWknVi4Ezm1vg1QsB/5EWpAJURq22uqd38U8qKvOJc= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.21 h1:+1Kl1zx6bWi4X7cKi3VYh29h8BvsCoHQEQ6ST9X8w7w= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.21/go.mod h1:4vIRDq+CJB2xFAXZ+YgGUTiEft7oAQlhIs71xcSeuVg= +github.com/aws/aws-sdk-go-v2/service/sts v1.42.1 h1:F/M5Y9I3nwr2IEpshZgh1GeHpOItExNM9L1euNuh/fk= +github.com/aws/aws-sdk-go-v2/service/sts v1.42.1/go.mod h1:mTNxImtovCOEEuD65mKW7DCsL+2gjEH+RPEAexAzAio= +github.com/aws/smithy-go v1.25.1 h1:J8ERsGSU7d+aCmdQur5Txg6bVoYelvQJgtZehD12GkI= +github.com/aws/smithy-go v1.25.1/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= +github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 h1:kHaBemcxl8o/pQ5VM1c8PVE1PubbNx3mjUr09OqWGCs= github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575/go.mod h1:9d6lWj8KzO/fd/NrVaLscBKmPigpZpn5YawRPw+e3Yo= -github.com/coredns/caddy v1.1.2-0.20241029205200-8de985351a98 h1:c+Epklw9xk6BZ1OFBPWLA2PcL8QalKvl3if8CP9x8uw= -github.com/coredns/caddy v1.1.2-0.20241029205200-8de985351a98/go.mod h1:A6ntJQlAWuQfFlsd9hvigKbo2WS0VUs2l1e2F+BawD4= +github.com/coredns/caddy v1.1.4-0.20250930002214-15135a999495 h1:JFeOmbjLnVRhvmLHyuO3M1pfXWlPWpwkdM8UqXZRtBg= +github.com/coredns/caddy v1.1.4-0.20250930002214-15135a999495/go.mod h1:A6ntJQlAWuQfFlsd9hvigKbo2WS0VUs2l1e2F+BawD4= github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4= github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec= -github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= -github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/go-systemd/v22 v22.7.0 h1:LAEzFkke61DFROc7zNLX/WA2i5J8gYqe0rSj9KI28KA= +github.com/coreos/go-systemd/v22 v22.7.0/go.mod h1:xNUYtjHu2EDXbsxz1i41wouACIwT7Ybq9o0BQhMwD0w= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -127,25 +137,25 @@ github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= github.com/dnstap/golang-dnstap v0.4.0 h1:KRHBoURygdGtBjDI2w4HifJfMAhhOqDuktAokaSa234= github.com/dnstap/golang-dnstap v0.4.0/go.mod h1:FqsSdH58NAmkAvKcpyxht7i4FoBjKu8E4JUPt8ipSUs= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/eapache/queue/v2 v2.0.0-20230407133247-75960ed334e4 h1:8EXxF+tCLqaVk8AOC29zl2mnhQjwyLxxOTuhUazWRsg= -github.com/eapache/queue/v2 v2.0.0-20230407133247-75960ed334e4/go.mod h1:I5sHm0Y0T1u5YjlyqC5GVArM7aNZRUYtTjmJ8mPJFds= -github.com/ebitengine/purego v0.8.3 h1:K+0AjQp63JEZTEMZiwsI9g0+hAMNohwUOtY0RPGexmc= -github.com/ebitengine/purego v0.8.3/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= -github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= -github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/expr-lang/expr v1.17.6 h1:1h6i8ONk9cexhDmowO/A64VPxHScu7qfSl2k8OlINec= -github.com/expr-lang/expr v1.17.6/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4= +github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= +github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU= +github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/expr-lang/expr v1.17.8 h1:W1loDTT+0PQf5YteHSTpju2qfUfNoBt4yw9+wOEU9VM= +github.com/expr-lang/expr v1.17.8/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4= github.com/farsightsec/golang-framestream v0.3.0 h1:/spFQHucTle/ZIPkYqrfshQqPe2VQEzesH243TjIwqA= github.com/farsightsec/golang-framestream v0.3.0/go.mod h1:eNde4IQyEiA5br02AouhEHCu3p3UzrCdFR4LuQHklMI= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= -github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= -github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= +github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -165,9 +175,6 @@ github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= -github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= -github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= 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= @@ -175,10 +182,11 @@ github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzw github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= +github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/mock v1.7.0-rc.1 h1:YojYx61/OLFsiv6Rw1Z96LpldJIy31o+UHmwAUMJ6/U= github.com/golang/mock v1.7.0-rc.1/go.mod h1:s42URUywIqd+OcERslBJvOjepvNymP31m3q8d/GkuRs= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= @@ -186,44 +194,64 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw= -github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw= +github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo= +github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg= -github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4= -github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= -github.com/googleapis/gax-go/v2 v2.15.0 h1:SyjDc1mGgZU5LncH8gimWo9lW1DtIfPibOG81vgd/bo= -github.com/googleapis/gax-go/v2 v2.15.0/go.mod h1:zVVkkxAQHa1RQpg9z2AUCMnKhi0Qld9rcmyfL1OZhoc= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI= +github.com/googleapis/enterprise-certificate-proxy v0.3.15 h1:xolVQTEXusUcAA5UgtyRLjelpFFHWlPQ4XfWGc7MBas= +github.com/googleapis/enterprise-certificate-proxy v0.3.15/go.mod h1:vqVt9yG9480NtzREnTlmGSBmFrA+bzb0yl0TxoBQXOg= +github.com/googleapis/gax-go/v2 v2.22.0 h1:PjIWBpgGIVKGoCXuiCoP64altEJCj3/Ei+kSU5vlZD4= +github.com/googleapis/gax-go/v2 v2.22.0/go.mod h1:irWBbALSr0Sk3qlqb9SyJ1h68WjgeFuiOzI4Rqw5+aY= +github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo= +github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.4 h1:kEISI/Gx67NzH3nJxAmY/dGac80kKZgZt134u7Y/k1s= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.4/go.mod h1:6Nz966r3vQYCqIzWsuEl9d7cf7mRhtDmm++sOxlnfxI= github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 h1:MJG/KsmcqMwFAkh8mTnAwhyKoB+sTAnY4CACC110tbU= github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw= -github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= -github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/cronexpr v1.1.3 h1:rl5IkxXN2m681EfivTlccqIryzYJSXRGRNa0xeG7NA4= +github.com/hashicorp/cronexpr v1.1.3/go.mod h1:P4wA0KBl9C5q2hABiMO7cp6jcIg96CDh1Efb3g1PWA4= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= +github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-version v1.8.0 h1:KAkNb1HAiZd1ukkxDFGmokVZe1Xy9HG6NUp+bPle2i4= +github.com/hashicorp/go-version v1.8.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/hashicorp/nomad/api v0.0.0-20250909143645-a3b86c697f38 h1:1LTbcTpGdSdbj0ee7YZHNe4R2XqxfyWwIkSGWRhgkfM= +github.com/hashicorp/nomad/api v0.0.0-20250909143645-a3b86c697f38/go.mod h1:0Tdp+9HbvwrxprXv/LfYZ8P21bOl4oA8Afyet1kUvhI= 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/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 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 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.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= -github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/klauspost/compress v1.18.4 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE9a2c= +github.com/klauspost/compress v1.18.4/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= +github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= +github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -233,17 +261,25 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 h1:PpXWgLPs+Fqr325bN2FD2ISlRRztXibcX6e8f5FR5Dc= -github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= +github.com/linkdata/deadlock v0.5.5 h1:d6O+rzEqasSfamGDA8u7bjtaq7hOX8Ha4Zn36Wxrkvo= +github.com/linkdata/deadlock v0.5.5/go.mod h1:tXb28stzAD3trzEEK0UJWC+rZKuobCoPktPYzebb1u0= +github.com/lufia/plan9stats v0.0.0-20260216142805-b3301c5f2a88 h1:PTw+yKnXcOFCR6+8hHTyWBeQ/P4Nb7dd4/0ohEcWQuM= +github.com/lufia/plan9stats v0.0.0-20260216142805-b3301c5f2a88/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos= +github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ= +github.com/mdlayher/vsock v1.2.1 h1:pC1mTJTvjo1r9n9fbm7S1j04rCgCzhCOS5DY0zqHlnQ= +github.com/mdlayher/vsock v1.2.1/go.mod h1:NRfCibel++DgeMD8z/hP+PPTjlNJsdPOmxcnENvE+SE= github.com/miekg/dns v1.1.31/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= -github.com/miekg/dns v1.1.68 h1:jsSRkNozw7G/mnmXULynzMNIsgY2dHC8LO6U6Ij2JEA= -github.com/miekg/dns v1.1.68/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps= +github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI= +github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs= +github.com/minio/simdjson-go v0.4.5 h1:r4IQwjRGmWCQ2VeMc7fGiilu1z5du0gJ/I/FsKwgo5A= +github.com/minio/simdjson-go v0.4.5/go.mod h1:eoNz0DcLQRyEDeaPr4Ru6JpjlZPzbA0IodxVJk8lO8E= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c h1:cqn374mizHuIWj+OSJCajGr/phAmuMug9qIX3l9CflE= +github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -252,14 +288,16 @@ github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFd github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/onsi/ginkgo/v2 v2.22.1 h1:QW7tbJAUDyVDVOM5dFa7qaybo+CRfR7bemlQUN6Z8aM= -github.com/onsi/ginkgo/v2 v2.22.1/go.mod h1:S6aTpoRsSq2cZOd+pssHAlKW/Q/jZt6cPrPlnj4a1xM= -github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8= -github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY= -github.com/open-telemetry/opentelemetry-collector-contrib/pkg/sampling v0.125.0 h1:0dOJCEtabevxxDQmxed69oMzSw+gb3ErCnFwFYZFu0M= -github.com/open-telemetry/opentelemetry-collector-contrib/pkg/sampling v0.125.0/go.mod h1:QwzQhtxPThXMUDW1XRXNQ+l0GrI2BRsvNhX6ZuKyAds= -github.com/open-telemetry/opentelemetry-collector-contrib/processor/probabilisticsamplerprocessor v0.125.0 h1:F68/Nbpcvo3JZpaWlRUDJtG7xs8FHBZ7A8GOMauDkyc= -github.com/open-telemetry/opentelemetry-collector-contrib/processor/probabilisticsamplerprocessor v0.125.0/go.mod h1:haO4cJtAk05Y0p7NO9ME660xxtSh54ifCIIT7+PO9C0= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/onsi/ginkgo/v2 v2.27.2 h1:LzwLj0b89qtIy6SSASkzlNvX6WktqurSHwkk2ipF/Ns= +github.com/onsi/ginkgo/v2 v2.27.2/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo= +github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= +github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/sampling v0.145.0 h1:7rdLY2Ewa1WVnjMfJTEKwQ5uPDHYeA1tqNPNROi957U= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/sampling v0.145.0/go.mod h1:jYlQAaJO4ZyJAW2jcKAbjN+nt5BRCyu49mlZv4Rui7U= +github.com/open-telemetry/opentelemetry-collector-contrib/processor/probabilisticsamplerprocessor v0.145.0 h1:12mxn+8YLeAjMZ1kLGulBcvHrdhRNUmxLVIDnaLkJbQ= +github.com/open-telemetry/opentelemetry-collector-contrib/processor/probabilisticsamplerprocessor v0.145.0/go.mod h1:V87HYJpfmvCeQ6Cjy3Q4xylxfCn2wVSS80wvv5ECc0s= github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492 h1:lM6RxxfUMrYL/f8bWEUqdXrANWtrL7Nndbm9iFN0DlU= github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= @@ -268,14 +306,19 @@ github.com/openzipkin-contrib/zipkin-go-opentracing v0.5.0 h1:uhcF5Jd7rP9DVEL10S github.com/openzipkin-contrib/zipkin-go-opentracing v0.5.0/go.mod h1:+oCZ5GXXr7KPI/DNOQORPTq5AWHfALJj9c72b0+YsEY= github.com/openzipkin/zipkin-go v0.4.3 h1:9EGwpqkgnwdEIJ+Od7QVSEIH+ocmm5nPat0G7sjsSdg= github.com/openzipkin/zipkin-go v0.4.3/go.mod h1:M9wCJZFWCo2RiY+o1eBCEMe0Dp2S5LDHcMZmk3RmK7c= -github.com/oschwald/geoip2-golang v1.13.0 h1:Q44/Ldc703pasJeP5V9+aFSZFmBN7DKHbNsSFzQATJI= -github.com/oschwald/geoip2-golang v1.13.0/go.mod h1:P9zG+54KPEFOliZ29i7SeYZ/GM6tfEL+rgSn03hYuUo= -github.com/oschwald/maxminddb-golang v1.13.0 h1:R8xBorY71s84yO06NgTmQvqvTvlS/bnYZrrWX1MElnU= -github.com/oschwald/maxminddb-golang v1.13.0/go.mod h1:BU0z8BfFVhi1LQaonTwwGQlsHUEu9pWNdMfmq4ztm0o= +github.com/oschwald/geoip2-golang/v2 v2.1.0 h1:DjnLhNJu9WHwTrmoiQFvgmyJoczhdnm7LB23UBI2Amo= +github.com/oschwald/geoip2-golang/v2 v2.1.0/go.mod h1:qdVmcPgrTJ4q2eP9tHq/yldMTdp2VMr33uVdFbHBiBc= +github.com/oschwald/maxminddb-golang/v2 v2.1.1 h1:lA8FH0oOrM4u7mLvowq8IT6a3Q/qEnqRzLQn9eH5ojc= +github.com/oschwald/maxminddb-golang/v2 v2.1.1/go.mod h1:PLdx6PR+siSIoXqqy7C7r3SB3KZnhxWr1Dp6g0Hacl8= github.com/outcaste-io/ristretto v0.2.3 h1:AK4zt/fJ76kjlYObOeNwh4T3asEuaCmp26pOvUOL9w0= github.com/outcaste-io/ristretto v0.2.3/go.mod h1:W8HywhmtlopSB1jeMg3JtdIhf+DYkLAr0VN/s4+MHac= -github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c h1:dAMKvw0MlJT1GshSTtih8C2gDs04w8dReiOGXrGLNoY= -github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM= +github.com/petermattis/goid v0.0.0-20250813065127-a731cc31b4fe/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= +github.com/petermattis/goid v0.0.0-20260226131333-17d1149c6ac6 h1:rh2lKw/P/EqHa724vYH2+VVQ1YnW4u6EOXl0PMAovZE= +github.com/petermattis/goid v0.0.0-20260226131333-17d1149c6ac6/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= +github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM= +github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM= +github.com/pires/go-proxyproto v0.12.0 h1:TTCxD66dU898tahivkqc3hoceZp7P44FnorWyo9d5vM= +github.com/pires/go-proxyproto v0.12.0/go.mod h1:qUvfqUMEoX7T8g0q7TQLDnhMjdTrxnG0hvpMn+7ePNI= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= @@ -287,31 +330,37 @@ github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= -github.com/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc= -github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE= +github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= +github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE= -github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= -github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= -github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= +github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4= +github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw= +github.com/prometheus/exporter-toolkit v0.16.0 h1:xT/j7L2XKF+VJd6B4fpUw6xWabHrSmsUf6mYmFqyu0s= +github.com/prometheus/exporter-toolkit v0.16.0/go.mod h1:d1EL8Z9674xQe/iWhwP2wDyCEoBPbXVeqDbqAUsgJWY= +github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws= +github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw= github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg= github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA= -github.com/quic-go/quic-go v0.54.0 h1:6s1YB9QotYI6Ospeiguknbp2Znb/jZYjZLRXn9kMQBg= -github.com/quic-go/quic-go v0.54.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY= +github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= +github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII= +github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw= +github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU= github.com/richardartoul/molecule v1.0.1-0.20240531184615-7ca0df43c0b3 h1:4+LEVOB87y175cLJC/mbsgKmoDOjrBldtXvioEy96WY= github.com/richardartoul/molecule v1.0.1-0.20240531184615-7ca0df43c0b3/go.mod h1:vl5+MqJ1nBINuSsUI2mGgH79UweUT/B5Fy8857PqyyI= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/secure-systems-lab/go-securesystemslib v0.9.0 h1:rf1HIbL64nUpEIZnjLZ3mcNEL9NBPB0iuVjyxvq3LZc= -github.com/secure-systems-lab/go-securesystemslib v0.9.0/go.mod h1:DVHKMcZ+V4/woA/peqr+L0joiRXbPpQ042GgJckkFgw= -github.com/shirou/gopsutil/v4 v4.25.3 h1:SeA68lsu8gLggyMbmCn8cmp97V1TI9ld9sVzAUcKcKE= -github.com/shirou/gopsutil/v4 v4.25.3/go.mod h1:xbuxyoZj+UsgnZrENu3lQivsngRR5BdjbJwf2fv4szA= +github.com/secure-systems-lab/go-securesystemslib v0.10.0 h1:l+H5ErcW0PAehBNrBxoGv1jjNpGYdZ9RcheFkB2WI14= +github.com/secure-systems-lab/go-securesystemslib v0.10.0/go.mod h1:MRKONWmRoFzPNQ9USRF9i1mc7MvAVvF1LlW8X5VWDvk= +github.com/shirou/gopsutil/v4 v4.26.2 h1:X8i6sicvUFih4BmYIGT1m2wwgw2VG9YgrDTi7cIRGUI= +github.com/shirou/gopsutil/v4 v4.26.2/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= +github.com/shoenig/test v1.12.1 h1:mLHfnMv7gmhhP44WrvT+nKSxKkPDiNkIuHGdIGI9RLU= +github.com/shoenig/test v1.12.1/go.mod h1:UxJ6u/x2v/TNs/LoLxBNJRV9DiwBBKYxXSyczsBHFoI= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -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/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -323,14 +372,17 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/tinylib/msgp v1.2.5 h1:WeQg1whrXRFiZusidTQqzETkRpGjFjcIhW6uqWH09po= -github.com/tinylib/msgp v1.2.5/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0= -github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4= -github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4= -github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso= -github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/tinylib/msgp v1.6.3 h1:bCSxiTz386UTgyT1i0MSCvdbWjVW+8sG3PjkGsZQt4s= +github.com/tinylib/msgp v1.6.3/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA= +github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= +github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= +github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= +github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ= +github.com/trailofbits/go-mutexasserts v0.0.0-20250514102930-c1f3d2e37561 h1:qqa3P9AtNn6RMe90l/lxd3eJWnIRxjI4eb5Rx8xqCLA= +github.com/trailofbits/go-mutexasserts v0.0.0-20250514102930-c1f3d2e37561/go.mod h1:GA3+Mq3kt3tYAfM0WZCu7ofy+GW9PuGysHfhr+6JX7s= github.com/vmihailenco/msgpack/v4 v4.3.13 h1:A2wsiTbvp63ilDaWmsk2wjx6xZdxQOvpiNlKBGKKXKI= github.com/vmihailenco/msgpack/v4 v4.3.13/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= github.com/vmihailenco/tagparser v0.1.2 h1:gnjoVuB/kljJ5wICEEOpx98oXMWPLj22G67Vbd1qPqc= @@ -343,64 +395,68 @@ github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= -go.etcd.io/etcd/api/v3 v3.6.4 h1:7F6N7toCKcV72QmoUKa23yYLiiljMrT4xCeBL9BmXdo= -go.etcd.io/etcd/api/v3 v3.6.4/go.mod h1:eFhhvfR8Px1P6SEuLT600v+vrhdDTdcfMzmnxVXXSbk= -go.etcd.io/etcd/client/pkg/v3 v3.6.4 h1:9HBYrjppeOfFjBjaMTRxT3R7xT0GLK8EJMVC4xg6ok0= -go.etcd.io/etcd/client/pkg/v3 v3.6.4/go.mod h1:sbdzr2cl3HzVmxNw//PH7aLGVtY4QySjQFuaCgcRFAI= -go.etcd.io/etcd/client/v3 v3.6.4 h1:YOMrCfMhRzY8NgtzUsHl8hC2EBSnuqbR3dh84Uryl7A= -go.etcd.io/etcd/client/v3 v3.6.4/go.mod h1:jaNNHCyg2FdALyKWnd7hxZXZxZANb0+KGY+YQaEMISo= -go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= -go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/collector/component v1.31.0 h1:9LzU8X1RhV3h8/QsAoTX23aFUfoJ3EUc9O/vK+hFpSI= -go.opentelemetry.io/collector/component v1.31.0/go.mod h1:JbZl/KywXJxpUXPbt96qlEXJSym1zQ2hauMxYMuvlxM= -go.opentelemetry.io/collector/component/componentstatus v0.125.0 h1:zlxGQZYd9kknRZSjRpOYW5SBjl0a5zYFYRPbreobXoU= -go.opentelemetry.io/collector/component/componentstatus v0.125.0/go.mod h1:bHXc2W8bqqo9adOvCgvhcO7pYzJOSpyV4cuQ1wiIl04= -go.opentelemetry.io/collector/component/componenttest v0.125.0 h1:E2mpnMQbkMpYoZ3Q8pHx4kod7kedjwRs1xqDpzCe/84= -go.opentelemetry.io/collector/component/componenttest v0.125.0/go.mod h1:pQtsE1u/SPZdTphP5BZP64XbjXSq6wc+mDut5Ws/JDI= -go.opentelemetry.io/collector/consumer v1.31.0 h1:L+y66ywxLHnAxnUxv0JDwUf5bFj53kMxCCyEfRKlM7s= -go.opentelemetry.io/collector/consumer v1.31.0/go.mod h1:rPsqy5ni+c6xNMUkOChleZYO/nInVY6eaBNZ1FmWJVk= -go.opentelemetry.io/collector/consumer/consumertest v0.125.0 h1:TUkxomGS4DAtjBvcWQd2UY4FDLLEKMQD6iOIDUr/5dM= -go.opentelemetry.io/collector/consumer/consumertest v0.125.0/go.mod h1:vkHf3y85cFLDHARO/cTREVjLjOPAV+cQg7lkC44DWOY= -go.opentelemetry.io/collector/consumer/xconsumer v0.125.0 h1:oTreUlk1KpMSWwuHFnstW+orrjGTyvs2xd3o/Dpy+hI= -go.opentelemetry.io/collector/consumer/xconsumer v0.125.0/go.mod h1:FX0G37r0W+wXRgxxFtwEJ4rlsCB+p0cIaxtU3C4hskw= -go.opentelemetry.io/collector/featuregate v1.31.0 h1:20q7plPQZwmAiaYAa6l1m/i2qDITZuWlhjr4EkmeQls= -go.opentelemetry.io/collector/featuregate v1.31.0/go.mod h1:Y/KsHbvREENKvvN9RlpiWk/IGBK+CATBYzIIpU7nccc= -go.opentelemetry.io/collector/internal/telemetry v0.125.0 h1:6lcGOxw3dAg7LfXTKdN8ZjR+l7KvzLdEiPMhhLwG4r4= -go.opentelemetry.io/collector/internal/telemetry v0.125.0/go.mod h1:5GyFslLqjZgq1DZTtFiluxYhhXrCofHgOOOybodDPGE= -go.opentelemetry.io/collector/pdata v1.31.0 h1:P5WuLr1l2JcIvr6Dw2hl01ltp2ZafPnC4Isv+BLTBqU= -go.opentelemetry.io/collector/pdata v1.31.0/go.mod h1:m41io9nWpy7aCm/uD1L9QcKiZwOP0ldj83JEA34dmlk= -go.opentelemetry.io/collector/pdata/pprofile v0.125.0 h1:Qqlx8w1HpiYZ9RQqjmMQIysI0cHNO1nh3E/fCTeFysA= -go.opentelemetry.io/collector/pdata/pprofile v0.125.0/go.mod h1:p/yK023VxAp8hm27/1G5DPTcMIpnJy3cHGAFUQZGyaQ= -go.opentelemetry.io/collector/pdata/testdata v0.125.0 h1:due1Hl0EEVRVwfCkiamRy5E8lS6yalv0lo8Zl/SJtGw= -go.opentelemetry.io/collector/pdata/testdata v0.125.0/go.mod h1:1GpEWlgdMrd+fWsBk37ZC2YmOP5YU3gFQ4rWuCu9g24= -go.opentelemetry.io/collector/pipeline v0.125.0 h1:oitBgcAFqntDB4ihQJUHJSQ8IHqKFpPkaTVbTYdIUzM= -go.opentelemetry.io/collector/pipeline v0.125.0/go.mod h1:TO02zju/K6E+oFIOdi372Wk0MXd+Szy72zcTsFQwXl4= -go.opentelemetry.io/collector/processor v1.31.0 h1:+u7sBUpnCBsHYoALp4hfr9VEjLHHYa4uKENGITe0K9Q= -go.opentelemetry.io/collector/processor v1.31.0/go.mod h1:5hDYJ7/hTdfd2tF2Rj5Hs6+mfyFz2O7CaPzVvW1qHQc= -go.opentelemetry.io/collector/processor/processorhelper v0.125.0 h1:QRpX7oFW88DAZhy+Q93npklRoaQr8ue0GKpeup7C/Fk= -go.opentelemetry.io/collector/processor/processorhelper v0.125.0/go.mod h1:oXRvslUuN62wErcoJrcEJYoTXu5wHyNyJsE+/a9Cc9s= -go.opentelemetry.io/collector/processor/processortest v0.125.0 h1:ZVAN4iZPDcWhpzKqnuok2NIuS5hwGVVQUOWkJFR12tA= -go.opentelemetry.io/collector/processor/processortest v0.125.0/go.mod h1:VAw0IRG35cWTBjBtreXeXJEgqkRegfjrH/EuLhNX2+I= -go.opentelemetry.io/collector/processor/xprocessor v0.125.0 h1:VWYPMW1VmDq6xB7M5SYjBpQCCIq3MhQ3W++wU47QpZM= -go.opentelemetry.io/collector/processor/xprocessor v0.125.0/go.mod h1:bCxUyFVlksANg8wjYZqWVsRB33lkLQ294rTrju/IZiM= -go.opentelemetry.io/collector/semconv v0.125.0 h1:SyRP617YGvNSWRSKMy7Lbk9RaJSR+qFAAfyxJOeZe4s= -go.opentelemetry.io/collector/semconv v0.125.0/go.mod h1:te6VQ4zZJO5Lp8dM2XIhDxDiL45mwX0YAQQWRQ0Qr9U= -go.opentelemetry.io/contrib/bridges/otelzap v0.10.0 h1:ojdSRDvjrnm30beHOmwsSvLpoRF40MlwNCA+Oo93kXU= -go.opentelemetry.io/contrib/bridges/otelzap v0.10.0/go.mod h1:oTTm4g7NEtHSV2i/0FeVdPaPgUIZPfQkFbq0vbzqnv0= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q= -go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= -go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I= -go.opentelemetry.io/otel/log v0.11.0 h1:c24Hrlk5WJ8JWcwbQxdBqxZdOK7PcP/LFtOtwpDTe3Y= -go.opentelemetry.io/otel/log v0.11.0/go.mod h1:U/sxQ83FPmT29trrifhQg+Zj2lo1/IPN1PF6RTFqdwc= -go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE= -go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E= -go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI= -go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg= -go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc= -go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps= -go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= -go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= +go.etcd.io/etcd/api/v3 v3.6.11 h1:XFGTgrJ8nak3kB4NgMG8t7NT+lEeuuvKQAqUHKVgkWQ= +go.etcd.io/etcd/api/v3 v3.6.11/go.mod h1:HYfTh0jyh+uFgp6gMbxJteIDYY97yMuYz85Rnw6Gy9o= +go.etcd.io/etcd/client/pkg/v3 v3.6.11 h1:e41mp315Yn3QMGPmEzCyLsMINgJXTY/dX8kM++1csxU= +go.etcd.io/etcd/client/pkg/v3 v3.6.11/go.mod h1:DysuMe/inqRyC/1tjRR6hReH/VV9Lufs27YKSKBWWJg= +go.etcd.io/etcd/client/v3 v3.6.11 h1:LAByD96VmmeuairkvdAcE0RZnrmGz/q3ceeWePo9bwc= +go.etcd.io/etcd/client/v3 v3.6.11/go.mod h1:vOTDMCo+fGPEClJqcFEFSqZ+8e7WKV7AyqJjX//HR2w= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/collector/component v1.51.1-0.20260205185216-81bc641f26c0 h1:ZSlXxE90IY0Cl53RTqzyEgRgRPLTeTNBdGhaTmvj9eY= +go.opentelemetry.io/collector/component v1.51.1-0.20260205185216-81bc641f26c0/go.mod h1:944C7vEIdk13Pn1fBbyaU8C1qKf2XC0jRBlc69NAsRY= +go.opentelemetry.io/collector/component/componentstatus v0.145.0 h1:EwUZfSaagdpRXnlrb0TqReJXXW2p9HWBU5YiIeXPCAE= +go.opentelemetry.io/collector/component/componentstatus v0.145.0/go.mod h1:OiYb8rT4FtSJPFSGCKYvOaajdueDUTJZncixGrmy5aM= +go.opentelemetry.io/collector/component/componenttest v0.145.1-0.20260205185216-81bc641f26c0 h1:+VCK6wX/WN170dcaWJweRAkxpmAEyVucfrUV13NwUlY= +go.opentelemetry.io/collector/component/componenttest v0.145.1-0.20260205185216-81bc641f26c0/go.mod h1:U2wUjKMGwgqM49/q8ORkzzYzSWY2m6zpG/e606eK1wc= +go.opentelemetry.io/collector/consumer v1.51.1-0.20260205185216-81bc641f26c0 h1:WNkJ1bKnRVAEJtBm1bwEkoLG2x7GyANc3/OnErZJ338= +go.opentelemetry.io/collector/consumer v1.51.1-0.20260205185216-81bc641f26c0/go.mod h1:Erk6qdfVj+24QTrGCpurcrF+qdUlHkb4dgMy5wJxLvY= +go.opentelemetry.io/collector/consumer/consumertest v0.145.1-0.20260205185216-81bc641f26c0 h1:FHyDIlTbqt0Y6tDI9EbI3hr9uWthwkeLY7uGF1jZYqQ= +go.opentelemetry.io/collector/consumer/consumertest v0.145.1-0.20260205185216-81bc641f26c0/go.mod h1:IFc/FeaIHQClb8KK0aVn0tFDNMc+/MmfQ+aBT1cJNeo= +go.opentelemetry.io/collector/consumer/xconsumer v0.145.1-0.20260205185216-81bc641f26c0 h1:zg2Jqfy7n7o/LEmLsXB4sFhxWtOEMFCKRyQUFLFUS9M= +go.opentelemetry.io/collector/consumer/xconsumer v0.145.1-0.20260205185216-81bc641f26c0/go.mod h1:SryDCLP2ZaFeZJtA2CSksJ0XvjH8k3LmlfXvy/kC7Wc= +go.opentelemetry.io/collector/featuregate v1.51.1-0.20260205185216-81bc641f26c0 h1:fOXhfT2xKqNhfalTXaT/Wic9EBRK8+9ZH0y8phReQS4= +go.opentelemetry.io/collector/featuregate v1.51.1-0.20260205185216-81bc641f26c0/go.mod h1:/1bclXgP91pISaEeNulRxzzmzMTm4I5Xih2SnI4HRSo= +go.opentelemetry.io/collector/internal/componentalias v0.145.1-0.20260205185216-81bc641f26c0 h1:s4/vCxeIxgQpuWmX1AK1DRbZmEdmNBq925EKES8ebiI= +go.opentelemetry.io/collector/internal/componentalias v0.145.1-0.20260205185216-81bc641f26c0/go.mod h1:Z0TtMbzaMp2qhj1dw4toya8toyQzqoTF46/WhJXplVw= +go.opentelemetry.io/collector/internal/testutil v0.145.0 h1:H/KL0GH3kGqSMKxZvnQ0B0CulfO9xdTg4DZf28uV7fY= +go.opentelemetry.io/collector/internal/testutil v0.145.0/go.mod h1:YAD9EAkwh/l5asZNbEBEUCqEjoL1OKMjAMoPjPqH76c= +go.opentelemetry.io/collector/pdata v1.51.1-0.20260205185216-81bc641f26c0 h1:8tgf9W3aW3vFabyVxPNHKsaoyUytudfVOQbqZI9xBHQ= +go.opentelemetry.io/collector/pdata v1.51.1-0.20260205185216-81bc641f26c0/go.mod h1:GoX1bjKDR++mgFKdT7Hynv9+mdgQ1DDXbjs7/Ww209Q= +go.opentelemetry.io/collector/pdata/pprofile v0.145.1-0.20260205185216-81bc641f26c0 h1:MJcnK8txYZlqHZyfZ1rVf66kt5/kEveIdR6KX//BY1Y= +go.opentelemetry.io/collector/pdata/pprofile v0.145.1-0.20260205185216-81bc641f26c0/go.mod h1:a60GC7wQPhLAixWzKbbP51QLwwc+J0Cmp4SurOlhGUk= +go.opentelemetry.io/collector/pdata/testdata v0.145.0 h1:iFsxsCMtE3lnAc/5kZbhZHpRv1OMmM+O5ry46xdQHbg= +go.opentelemetry.io/collector/pdata/testdata v0.145.0/go.mod h1:0y2ERArdzqmYdJHdKLKue+AUubSEGlwK49F+23+Mbic= +go.opentelemetry.io/collector/pipeline v1.51.1-0.20260205185216-81bc641f26c0 h1:1KP5gXGF9qN1mEzJupZDUQVIND35qe/0Hy6Cptvdk0s= +go.opentelemetry.io/collector/pipeline v1.51.1-0.20260205185216-81bc641f26c0/go.mod h1:xUrAqiebzYbrgxyoXSkk6/Y3oi5Sy3im2iCA51LwUAI= +go.opentelemetry.io/collector/processor v1.51.0 h1:PKpCzkLQmqaW08TOVh/zM0qx07Ihq+DR5J/OBkPiL9o= +go.opentelemetry.io/collector/processor v1.51.0/go.mod h1:rtIPFS+EFRAkG+CSwtjxs2IsIkuZStObvALeueD02XI= +go.opentelemetry.io/collector/processor/processorhelper v0.145.0 h1:vXdv6lHz20Tm3ZEsg0i6jPZJBQgy9kzk/PuqWhHWiiM= +go.opentelemetry.io/collector/processor/processorhelper v0.145.0/go.mod h1:3Ecpe5jHRHGf24EvJHeJ/ekK/a1DLByyq0CSUxjjURg= +go.opentelemetry.io/collector/processor/processortest v0.145.0 h1:RDGBmyZnHk7XVK/EdLt/8iPWj+QLStbbVi1nFTNR01s= +go.opentelemetry.io/collector/processor/processortest v0.145.0/go.mod h1:WAvxAzSojkdoZB915Z1lsVHCPDJBb2fepjJBjenrzjg= +go.opentelemetry.io/collector/processor/xprocessor v0.145.0 h1:DaIE7MxRlg0OL1o2P0GQZtmZeExAmVso3qWv8S0RLps= +go.opentelemetry.io/collector/processor/xprocessor v0.145.0/go.mod h1:kUwRyKBU/kjCmXodd+0z7CpvcP0A9G9/QL+MaJt4U2o= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 h1:OyrsyzuttWTSur2qN/Lm0m2a8yqyIjUVBZcxFPuXq2o= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0/go.mod h1:C2NGBr+kAB4bk3xtMXfZ94gqFDtg/GkI7e9zqGh5Beg= +go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= +go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= +go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= +go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= +go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= +go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg= +go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw= +go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A= +go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= +go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= +go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A= +go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4= +go.opentelemetry.io/proto/slim/otlp v1.9.0 h1:fPVMv8tP3TrsqlkH1HWYUpbCY9cAIemx184VGkS6vlE= +go.opentelemetry.io/proto/slim/otlp v1.9.0/go.mod h1:xXdeJJ90Gqyll+orzUkY4bOd2HECo5JofeoLpymVqdI= +go.opentelemetry.io/proto/slim/otlp/collector/profiles/v1development v0.2.0 h1:o13nadWDNkH/quoDomDUClnQBpdQQ2Qqv0lQBjIXjE8= +go.opentelemetry.io/proto/slim/otlp/collector/profiles/v1development v0.2.0/go.mod h1:Gyb6Xe7FTi/6xBHwMmngGoHqL0w29Y4eW8TGFzpefGA= +go.opentelemetry.io/proto/slim/otlp/profiles/v1development v0.2.0 h1:EiUYvtwu6PMrMHVjcPfnsG3v+ajPkbUeH+IL93+QYyk= +go.opentelemetry.io/proto/slim/otlp/profiles/v1development v0.2.0/go.mod h1:mUUHKFiN2SST3AhJ8XhJxEoeVW12oqfXog0Bo8W3Ec4= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= @@ -408,30 +464,34 @@ go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= -go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= +go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= +go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= -go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= +go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= +go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= -golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= -golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= -golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM= -golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8= +golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI= +golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q= +golang.org/x/exp v0.0.0-20260209203927-2842357ff358 h1:kpfSV7uLwKJbFSEgNhWzGSL47NDSF/5pYYQw1V0ub6c= +golang.org/x/exp v0.0.0-20260209203927-2842357ff358/go.mod h1:R3t0oliuryB5eenPWl3rrQxwnNM3WTwnsRZZiXLAAW8= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg= -golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ= +golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI= +golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -443,19 +503,18 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= -golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= -golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= -golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA= +golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs= +golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= +golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= 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-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= -golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -476,15 +535,15 @@ golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= -golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ= +golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= -golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= -golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= +golang.org/x/term v0.42.0 h1:UiKe+zDFmJobeJ5ggPwOshJIVt6/Ft0rcfrXZDLWAWY= +golang.org/x/term v0.42.0/go.mod h1:Dq/D+snpsbazcBG5+F9Q1n2rXV8Ma+71xEjTRufARgY= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -492,10 +551,10 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= -golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= -golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= -golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= +golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg= +golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164= +golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= +golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -504,69 +563,68 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0= -golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw= +golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s= +golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= -golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= -gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= -gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/api v0.248.0 h1:hUotakSkcwGdYUqzCRc5yGYsg4wXxpkKlW5ryVqvC1Y= -google.golang.org/api v0.248.0/go.mod h1:yAFUAF56Li7IuIQbTFoLwXTCI6XCFKueOlS7S9e4F9k= +golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY= +golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= +gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= +gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= +google.golang.org/api v0.278.0 h1:W7jiRvRi53VYFfZ/HoZjQBtJk7gOFbHD8ot1RzVZU6E= +google.golang.org/api v0.278.0/go.mod h1:B9TqLBwJqVjp1mtt7WeoQwWRwvu/400y5lETOql+giQ= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= -google.golang.org/genproto v0.0.0-20250603155806-513f23925822 h1:rHWScKit0gvAPuOnu87KpaYtjK5zBMLcULh7gxkCXu4= -google.golang.org/genproto v0.0.0-20250603155806-513f23925822/go.mod h1:HubltRL7rMh0LfnQPkMH4NPDFEWp0jw3vixw7jEM53s= -google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 h1:FiusG7LWj+4byqhbvmB+Q93B/mOxJLN2DTozDuZm4EU= -google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:kXqgZtrWaf6qS3jZOCnCH7WYfrvFjkC51bM8fz3RsCA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c h1:qXWI/sQtv5UKboZ/zUk7h+mrf/lXORyI+n9DKDAusdg= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c/go.mod h1:gw1tLEfykwDz2ET4a12jcXt4couGAm7IwsVaTy0Sflo= -google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4= -google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= +google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7 h1:XzmzkmB14QhVhgnawEVsOn6OFsnpyxNPRY9QV01dNB0= +google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:L43LFes82YgSonw6iTXTxXUX1OlULt4AQtkik4ULL/I= +google.golang.org/genproto/googleapis/api v0.0.0-20260319201613-d00831a3d3e7 h1:41r6JMbpzBMen0R/4TZeeAmGXSJC7DftGINUodzTkPI= +google.golang.org/genproto/googleapis/api v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:EIQZ5bFCfRQDV4MhRle7+OgjNtZ6P1PiZBgAKuxXu/Y= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260427160629-7cedc36a6bc4 h1:tEkOQcXgF6dH1G+MVKZrfpYvozGrzb91k6ha7jireSM= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260427160629-7cedc36a6bc4/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +google.golang.org/grpc v1.81.0 h1:W3G9N3KQf3BU+YuCtGKJk0CmxQNbAISICD/9AORxLIw= +google.golang.org/grpc v1.81.0/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I= 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= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= -google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= -gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= +gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo= +gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= -gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.67.1 h1:tVBILHy0R6e4wkYOn3XmiITt/hEVH4TFMYvAX2Ytz6k= +gopkg.in/ini.v1 v1.67.1/go.mod h1:x/cyOwCgZqOkJoDIJ3c1KNHMo10+nLGAhh+kn3Zizss= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.33.4 h1:oTzrFVNPXBjMu0IlpA2eDDIU49jsuEorGHB4cvKupkk= -k8s.io/api v0.33.4/go.mod h1:VHQZ4cuxQ9sCUMESJV5+Fe8bGnqAARZ08tSTdHWfeAc= -k8s.io/apimachinery v0.33.4 h1:SOf/JW33TP0eppJMkIgQ+L6atlDiP/090oaX0y9pd9s= -k8s.io/apimachinery v0.33.4/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= -k8s.io/client-go v0.33.4 h1:TNH+CSu8EmXfitntjUPwaKVPN0AYMbc9F1bBS8/ABpw= -k8s.io/client-go v0.33.4/go.mod h1:LsA0+hBG2DPwovjd931L/AoaezMPX9CmBgyVyBZmbCY= -k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= -k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4= -k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8= -k8s.io/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJJI8IUa1AmH/qa0= -k8s.io/utils v0.0.0-20241210054802-24370beab758/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= -sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo= -sigs.k8s.io/mcs-api v0.2.0 h1:F8o/nIpQmog494Qwe94srDWjS3ltEu4y5IL9i3dB938= -sigs.k8s.io/mcs-api v0.2.0/go.mod h1:zZ5CK8uS6HaLkxY4HqsmcBHfzHuNMrY2uJy8T7jffK4= -sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +k8s.io/api v0.35.4 h1:P7nFYKl5vo9AGUp1Z+Pmd3p2tA7bX2wbFWCvDeRv988= +k8s.io/api v0.35.4/go.mod h1:yl4lqySWOgYJJf9RERXKUwE9g2y+CkuwG+xmcOK8wXU= +k8s.io/apimachinery v0.35.4 h1:xtdom9RG7e+yDp71uoXoJDWEE2eOiHgeO4GdBzwWpds= +k8s.io/apimachinery v0.35.4/go.mod h1:NNi1taPOpep0jOj+oRha3mBJPqvi0hGdaV8TCqGQ+cc= +k8s.io/client-go v0.35.4 h1:DN6fyaGuzK64UvnKO5fOA6ymSjvfGAnCAHAR0C66kD8= +k8s.io/client-go v0.35.4/go.mod h1:2Pg9WpsS4NeOpoYTfHHfMxBG8zFMSAUi4O/qoiJC3nY= +k8s.io/klog/v2 v2.140.0 h1:Tf+J3AH7xnUzZyVVXhTgGhEKnFqye14aadWv7bzXdzc= +k8s.io/klog/v2 v2.140.0/go.mod h1:o+/RWfJ6PwpnFn7OyAG3QnO47BFsymfEfrz6XyYSSp0= +k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 h1:Y3gxNAuB0OBLImH611+UDZcmKS3g6CthxToOb37KgwE= +k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ= +k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 h1:SjGebBtkBqHFOli+05xYbK8YF1Dzkbzn+gDM4X9T4Ck= +k8s.io/utils v0.0.0-20251002143259-bc988d571ff4/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= +sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= +sigs.k8s.io/mcs-api v0.5.0 h1:UMgKuFFIRVEy4LR2LQovPnBkV9rgmZMT+sJO7MkaVj4= +sigs.k8s.io/mcs-api v0.5.0/go.mod h1:zZ5CK8uS6HaLkxY4HqsmcBHfzHuNMrY2uJy8T7jffK4= sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= -sigs.k8s.io/structured-merge-diff/v4 v4.6.0 h1:IUA9nvMmnKWcj5jl84xn+T5MnlZKThmUW1TdblaLVAc= -sigs.k8s.io/structured-merge-diff/v4 v4.6.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= -sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= -sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= +sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco= +sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= +sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= +sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= diff --git a/man/coredns-acl.7 b/man/coredns-acl.7 index f03731cc9c..7dcbf30ecd 100644 --- a/man/coredns-acl.7 +++ b/man/coredns-acl.7 @@ -1,5 +1,5 @@ .\" Generated by Mmark Markdown Processer - mmark.miek.nl -.TH "COREDNS-ACL" 7 "March 2021" "CoreDNS" "CoreDNS Plugins" +.TH "COREDNS-ACL" 7 "March 2026" "CoreDNS" "CoreDNS Plugins" .SH "NAME" .PP @@ -7,7 +7,13 @@ .SH "DESCRIPTION" .PP -With \fB\fCacl\fR enabled, users are able to block or filter suspicious DNS queries by configuring IP filter rule sets, i.e. allowing authorized queries to recurse or blocking unauthorized queries. +With \fB\fCacl\fR enabled, users are able to block or filter suspicious DNS queries by configuring IP filter rule sets, i.e. allowing authorized queries or blocking unauthorized queries. + +.PP +When evaluating the rule sets, \fIacl\fP uses the source IP of the TCP/UDP headers of the DNS query received by CoreDNS. +This source IP will be different than the IP of the client originating the request in cases where the source IP of the request is changed in transit. For example: +* if the request passes though an intermediate forwarding DNS server or recursive DNS server before reaching CoreDNS +* if the request traverses a Source NAT before reaching CoreDNS .PP This plugin can be used multiple times per Server Block. @@ -27,7 +33,7 @@ acl [ZONES...] { .IP \(bu 4 \fBZONES\fP zones it should be authoritative for. If empty, the zones from the configuration block are used. .IP \(bu 4 -\fBACTION\fP (\fIallow\fP, \fIblock\fP, or \fIfilter\fP) defines the way to deal with DNS queries matched by this rule. The default action is \fIallow\fP, which means a DNS query not matched by any rules will be allowed to recurse. The difference between \fIblock\fP and \fIfilter\fP is that block returns status code of \fIREFUSED\fP while filter returns an empty set \fINOERROR\fP +\fBACTION\fP (\fIallow\fP, \fIblock\fP, \fIfilter\fP, or \fIdrop\fP) defines the way to deal with DNS queries matched by this rule. The default action is \fIallow\fP, which means a DNS query not matched by any rules will be allowed to recurse. The difference between \fIblock\fP and \fIfilter\fP is that block returns status code of \fIREFUSED\fP while filter returns an empty set \fINOERROR\fP. \fIdrop\fP however returns no response to the client. .IP \(bu 4 \fBQTYPE\fP is the query type to match for the requests to be allowed or blocked. Common resource record types are supported. \fB\fC*\fR stands for all record types. The default behavior for an omitted \fB\fCtype QTYPE...\fR is to match all kinds of DNS queries (same as \fB\fCtype *\fR). .IP \(bu 4 @@ -120,14 +126,34 @@ example.org { .fi .RE +.PP +Drop all DNS queries from 192.0.2.0/24: + +.PP +.RS + +.nf +\&. { + acl { + drop net 192.0.2.0/24 + } +} + +.fi +.RE + .SH "METRICS" .PP If monitoring is enabled (via the \fIprometheus\fP plugin) then the following metrics are exported: .IP \(bu 4 -\fB\fCcoredns_acl_blocked_requests_total{server, zone}\fR - counter of DNS requests being blocked. +\fB\fCcoredns_acl_blocked_requests_total{server, zone, view}\fR - counter of DNS requests being blocked. +.IP \(bu 4 +\fB\fCcoredns_acl_filtered_requests_total{server, zone, view}\fR - counter of DNS requests being filtered. +.IP \(bu 4 +\fB\fCcoredns_acl_allowed_requests_total{server, view}\fR - counter of DNS requests being allowed. .IP \(bu 4 -\fB\fCcoredns_acl_allowed_requests_total{server}\fR - counter of DNS requests being allowed. +\fB\fCcoredns_acl_dropped_requests_total{server, zone, view}\fR - counter of DNS requests being dropped. .PP diff --git a/man/coredns-any.7 b/man/coredns-any.7 index c17b34ca57..a578a8bf44 100644 --- a/man/coredns-any.7 +++ b/man/coredns-any.7 @@ -1,5 +1,5 @@ .\" Generated by Mmark Markdown Processer - mmark.miek.nl -.TH "COREDNS-ANY" 7 "March 2021" "CoreDNS" "CoreDNS Plugins" +.TH "COREDNS-ANY" 7 "March 2026" "CoreDNS" "CoreDNS Plugins" .SH "NAME" .PP diff --git a/man/coredns-auto.7 b/man/coredns-auto.7 index 2362129d50..6253b90725 100644 --- a/man/coredns-auto.7 +++ b/man/coredns-auto.7 @@ -1,5 +1,5 @@ .\" Generated by Mmark Markdown Processer - mmark.miek.nl -.TH "COREDNS-AUTO" 7 "March 2021" "CoreDNS" "CoreDNS Plugins" +.TH "COREDNS-AUTO" 7 "March 2026" "CoreDNS" "CoreDNS Plugins" .SH "NAME" .PP @@ -34,7 +34,8 @@ are used. used to extract the origin. \fBORIGIN_TEMPLATE\fP will be used as a template for the origin. Strings like \fB\fC{}\fR are replaced with the respective matches in the file name, e.g. \fB\fC{1}\fR is the first match, \fB\fC{2}\fR is the second. The default is: \fB\fCdb\.(.*) {1}\fR i.e. from a file with the -name \fB\fCdb.example.com\fR, the extracted origin will be \fB\fCexample.com\fR. +name \fB\fCdb.example.com\fR, the extracted origin will be \fB\fCexample.com\fR. \fBREGEXP\fP must not be longer +than 10000 characters. .IP \(bu 4 \fB\fCreload\fR interval to perform reloads of zones if SOA version changes and zonefiles. It specifies how often CoreDNS should scan the directory to watch for file removal and addition. Default is one minute. Value of \fB\fC0\fR means to not scan for changes and reload. eg. \fB\fC30s\fR checks zonefile every 30 seconds diff --git a/man/coredns-autopath.7 b/man/coredns-autopath.7 index 4840b8ab80..66bb63d06e 100644 --- a/man/coredns-autopath.7 +++ b/man/coredns-autopath.7 @@ -1,5 +1,5 @@ .\" Generated by Mmark Markdown Processer - mmark.miek.nl -.TH "COREDNS-AUTOPATH" 7 "March 2021" "CoreDNS" "CoreDNS Plugins" +.TH "COREDNS-AUTOPATH" 7 "March 2026" "CoreDNS" "CoreDNS Plugins" .SH "NAME" .PP @@ -81,7 +81,7 @@ path) in the following case. To properly build the search path of a client \fIau the namespace of the a Pod making a DNS request. To do this, it relies on the \fIkubernetes\fP plugin's Pod cache to resolve the client's IP address to a Pod. The Pod cache is maintained by an API watch on Pods. When Pod IP assignments change, the Kubernetes API notifies CoreDNS via the API watch. -However, that notification is not instantaneous. In the case that a Pod is deleted, and it's IP is +However, that notification is not instantaneous. In the case that a Pod is deleted, and its IP is immediately provisioned to a Pod in another namespace, and that new Pod make a DNS lookup \fIbefore\fP the API watch can notify CoreDNS of the change, \fIautopath\fP will resolve the IP to the previous Pod's namespace. diff --git a/man/coredns-azure.7 b/man/coredns-azure.7 index dc92154fa6..07286280dc 100644 --- a/man/coredns-azure.7 +++ b/man/coredns-azure.7 @@ -1,5 +1,5 @@ .\" Generated by Mmark Markdown Processer - mmark.miek.nl -.TH "COREDNS-AZURE" 7 "March 2021" "CoreDNS" "CoreDNS Plugins" +.TH "COREDNS-AZURE" 7 "March 2026" "CoreDNS" "CoreDNS Plugins" .SH "NAME" .PP diff --git a/man/coredns-bind.7 b/man/coredns-bind.7 index 2306bca15e..7f19afc9b7 100644 --- a/man/coredns-bind.7 +++ b/man/coredns-bind.7 @@ -1,5 +1,5 @@ .\" Generated by Mmark Markdown Processer - mmark.miek.nl -.TH "COREDNS-BIND" 7 "March 2021" "CoreDNS" "CoreDNS Plugins" +.TH "COREDNS-BIND" 7 "March 2026" "CoreDNS" "CoreDNS Plugins" .SH "NAME" .PP @@ -17,23 +17,43 @@ If several addresses are provided, a listener will be open on each of the IP pro Each address has to be an IP or name of one of the interfaces of the host. Bind by interface name, binds to the IPs on that interface at the time of startup or reload (reload will happen with a SIGHUP or if the config file changes). .PP -If the given argument is an interface name, and that interface has serveral IP addresses, CoreDNS will listen on all of the interface IP addresses (including IPv4 and IPv6). +If the given argument is an interface name, and that interface has several IP addresses, CoreDNS will listen on all of the interface IP addresses (including IPv4 and IPv6). .SH "SYNTAX" +.PP +In its basic form, a simple bind uses this syntax: + .PP .RS .nf -bind ADDRESS ... +bind ADDRESS|IFACE ... .fi .RE .PP -\fBADDRESS\fP is an IP address to bind to. -When several addresses are provided a listener will be opened on each of the addresses. +You can also exclude some addresses with their IP address or interface name in expanded syntax: + +.PP +.RS + +.nf +bind ADDRESS|IFACE ... { + except ADDRESS|IFACE ... +} + +.fi +.RE + +.IP \(bu 4 +\fBADDRESS|IFACE\fP is an IP address or interface name to bind to. +When several addresses are provided a listener will be opened on each of the addresses. Please read the \fIDescription\fP for more details. +.IP \(bu 4 +\fB\fCexcept\fR, excludes interfaces or IP addresses to bind to. \fB\fCexcept\fR option only excludes addresses for the current \fB\fCbind\fR directive if multiple \fB\fCbind\fR directives are used in the same server block. .SH "EXAMPLES" + .PP To make your socket accessible only to that machine, bind to IP 127.0.0.1 (localhost): @@ -92,12 +112,35 @@ The following server block, binds on localhost with its interface name (both "12 .fi .RE +.PP +You can exclude some addresses by their IP or interface name (The following will only listen on \fB\fC::1\fR or whatever addresses have been assigned to the \fB\fClo\fR interface): + +.PP +.RS + +.nf +\&. { + bind lo { + except 127.0.0.1 + } +} + +.fi +.RE + .SH "BUGS" +.SS "AVOIDING LISTENER CONTENTION" +.PP +TL;DR, When adding the \fIbind\fP plugin to a server block, it must also be added to all other server blocks that listen on the same port. + .PP -When defining more than one server block, take care not to bind more than one server to the same -address and port. Doing so will result in unpredictable behavior (requests may be randomly -served by either server). Keep in mind that \fIwithout\fP the \fIbind\fP plugin, a server will bind to all -interfaces, and this will collide with another server if it's using \fIbind\fP to listen to an interface +When more than one server block is configured to listen to a common port, those server blocks must either +all use the \fIbind\fP plugin, or all use default binding (no \fIbind\fP plugin). Note that "port" here refers the TCP/UDP port that +a server block is configured to serve (default 53) - not a network interface. For two server blocks listening on the same port, +if one uses the bind plugin and the other does not, two separate listeners will be created that will contend for serving +packets destined to the same address. Doing so will result in unpredictable behavior (requests may be randomly +served by either server). This happens because \fIwithout\fP the \fIbind\fP plugin, a server will bind to all +interfaces, and this will collide with another server if it's using \fIbind\fP to listen to an address on the same port. For example, the following creates two servers that both listen on 127.0.0.1:53, which would result in unpredictable behavior for queries in \fB\fCa.bad.example.com\fR: diff --git a/man/coredns-bufsize.7 b/man/coredns-bufsize.7 index e5303e3636..e21a92618e 100644 --- a/man/coredns-bufsize.7 +++ b/man/coredns-bufsize.7 @@ -1,14 +1,20 @@ .\" Generated by Mmark Markdown Processer - mmark.miek.nl -.TH "COREDNS-BUFSIZE" 7 "March 2021" "CoreDNS" "CoreDNS Plugins" +.TH "COREDNS-BUFSIZE" 7 "March 2026" "CoreDNS" "CoreDNS Plugins" .SH "NAME" .PP -\fIbufsize\fP - sizes EDNS0 buffer size to prevent IP fragmentation. +\fIbufsize\fP - limits EDNS0 buffer size to prevent IP fragmentation. .SH "DESCRIPTION" .PP -\fIbufsize\fP limits a requester's UDP payload size. +\fIbufsize\fP limits a requester's UDP payload size to within a maximum value. +If a request with an OPT RR has a bufsize greater than the limit, the bufsize +of the request will be reduced. Otherwise the request is unaffected. It prevents IP fragmentation, mitigating certain DNS vulnerabilities. +It cannot increase UDP size requested by the client, it can be reduced only. +This will only affect queries that have +an OPT RR (EDNS(0) +\[la]https://www.rfc-editor.org/rfc/rfc6891\[ra]). .SH "SYNTAX" .PP @@ -22,7 +28,7 @@ bufsize [SIZE] .PP \fB[SIZE]\fP is an int value for setting the buffer size. -The default value is 512, and the value must be within 512 - 4096. +The default value is 1232, and the value must be within 512 - 4096. Only one argument is acceptable, and it covers both IPv4 and IPv6. .SH "EXAMPLES" @@ -34,7 +40,7 @@ Enable limiting the buffer size of outgoing query to the resolver (172.31.0.10): .nf \&. { - bufsize 512 + bufsize 1100 forward . 172.31.0.10 log } @@ -50,7 +56,7 @@ Enable limiting the buffer size as an authoritative nameserver: .nf \&. { - bufsize 512 + bufsize 1220 file db.example.org log } @@ -61,7 +67,5 @@ Enable limiting the buffer size as an authoritative nameserver: .SH "CONSIDERATIONS" .IP \(bu 4 Setting 1232 bytes to bufsize may avoid fragmentation on the majority of networks in use today, but it depends on the MTU of the physical network links. -.IP \(bu 4 -For now, if a client does not use EDNS, this plugin adds OPT RR. diff --git a/man/coredns-cache.7 b/man/coredns-cache.7 index b05eeae3f5..2e1a1e9fe3 100644 --- a/man/coredns-cache.7 +++ b/man/coredns-cache.7 @@ -1,5 +1,5 @@ .\" Generated by Mmark Markdown Processer - mmark.miek.nl -.TH "COREDNS-CACHE" 7 "March 2021" "CoreDNS" "CoreDNS Plugins" +.TH "COREDNS-CACHE" 7 "March 2026" "CoreDNS" "CoreDNS Plugins" .SH "NAME" .PP @@ -12,8 +12,7 @@ With \fIcache\fP enabled, all records except zone transfers and metadata records database, etc.) is expensive. .PP -\fICache\fP will change the query to enable DNSSEC (DNSSEC OK; DO) if it passes through the plugin. If -the client didn't request any DNSSEC (records), these are filtered out when replying. +\fICache\fP will pass DNSSEC (DNSSEC OK; DO) options through the plugin for upstream queries. .PP This plugin can only be used once per Server Block. @@ -38,6 +37,11 @@ Setting a TTL of 300: \fB\fCcache 300\fR would cache records up to 300 seconds. .PP Each element in the cache is cached according to its TTL (with \fBTTL\fP as the max). +Note that \fBTTL\fP only caps the cache duration and does not extend it. A record with a 30s TTL +will still be cached for 30s even with \fB\fCcache 600\fR. The minimum cache duration defaults to 5 +seconds and can be adjusted per cache type using \fBMINTTL\fP in the \fB\fCsuccess\fR or \fB\fCdenial\fR directives. + +.PP A cache is divided into 256 shards, each holding up to 39 items by default - for a total size of 256 * 39 = 9984 items. @@ -52,7 +56,10 @@ cache [TTL] [ZONES...] { success CAPACITY [TTL] [MINTTL] denial CAPACITY [TTL] [MINTTL] prefetch AMOUNT [[DURATION] [PERCENTAGE%]] - serve\_stale [DURATION] + serve\_stale [DURATION] [REFRESH\_MODE] + servfail DURATION + disable success|denial [ZONES...] + keepttl } .fi @@ -76,10 +83,28 @@ Popular means \fBAMOUNT\fP queries have been seen with no gaps of \fBDURATION\fP which defaults to \fB\fC10%\fR, or latest 1 second before TTL expiration. Values should be in the range \fB\fC[10%, 90%]\fR. Note the percent sign is mandatory. \fBPERCENTAGE\fP is treated as an \fB\fCint\fR. .IP \(bu 4 -\fB\fCserve_stale\fR, 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. \fBDURATION\fP is how far back to consider -stale responses as fresh. The default duration is 1h. +\fB\fCserve_stale\fR, 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 \fBDURATION\fP (default 1 hour). By default, the \fIcache\fP plugin will +attempt to refresh the cache entry after sending the expired cache entry to the client. The +responses have a TTL of 0. \fBREFRESH_MODE\fP controls the timing of the expired cache entry refresh. +\fB\fCverify\fR will first verify that an entry is still unavailable from the source before sending the expired entry to the client. +\fB\fCimmediate\fR will immediately send the expired entry to the client before +checking to see if the entry is available from the source. \fBREFRESH_MODE\fP defaults to \fB\fCimmediate\fR. Setting this +value to \fB\fCverify\fR 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. +.IP \(bu 4 +\fB\fCservfail\fR cache SERVFAIL responses for \fBDURATION\fP. Setting \fBDURATION\fP to 0 will disable caching of SERVFAIL +responses. If this option is not set, SERVFAIL responses will be cached for 5 seconds. \fBDURATION\fP may not be +greater than 5 minutes. +.IP \(bu 4 +\fB\fCdisable\fR disable the success or denial cache for the listed \fBZONES\fP. If no \fBZONES\fP are given, the specified +cache will be disabled for all zones. +.IP \(bu 4 +\fB\fCkeepttl\fR do not age TTL when serving responses from cache. The entry will still be removed from cache +when the TTL expires as normal, but until it expires responses will include the original TTL instead +of the remaining TTL. This can be useful if CoreDNS is used as an authoritative server and you want +to serve a consistent TTL to downstream clients. This is \fBNOT\fP recommended when CoreDNS is caching +records it is not authoritative for because it could result in downstream clients using stale answers. .SH "CAPACITY AND EVICTION" @@ -98,17 +123,21 @@ Entries with 0 TTL will remain in the cache until randomly evicted when the shar If monitoring is enabled (via the \fIprometheus\fP plugin) then the following metrics are exported: .IP \(bu 4 -\fB\fCcoredns_cache_entries{server, type}\fR - Total elements in the cache by cache type. +\fB\fCcoredns_cache_entries{server, type, zones, view}\fR - Total elements in the cache by cache type. +.IP \(bu 4 +\fB\fCcoredns_cache_hits_total{server, type, zones, view}\fR - Counter of cache hits by cache type. .IP \(bu 4 -\fB\fCcoredns_cache_hits_total{server, type}\fR - Counter of cache hits by cache type. +\fB\fCcoredns_cache_misses_total{server, zones, view}\fR - Counter of cache misses. - Deprecated, derive misses from cache hits/requests counters. .IP \(bu 4 -\fB\fCcoredns_cache_misses_total{server}\fR - Counter of cache misses. +\fB\fCcoredns_cache_requests_total{server, zones, view}\fR - Counter of cache requests. .IP \(bu 4 -\fB\fCcoredns_cache_prefetch_total{server}\fR - Counter of times the cache has prefetched a cached item. +\fB\fCcoredns_cache_prefetch_total{server, zones, view}\fR - Counter of times the cache has prefetched a cached item. .IP \(bu 4 -\fB\fCcoredns_cache_drops_total{server}\fR - Counter of responses excluded from the cache due to request/response question name mismatch. +\fB\fCcoredns_cache_drops_total{server, zones, view}\fR - Counter of responses excluded from the cache due to request/response question name mismatch. .IP \(bu 4 -\fB\fCcoredns_cache_served_stale_total{server}\fR - Counter of requests served from stale cache entries. +\fB\fCcoredns_cache_served_stale_total{server, zones, view}\fR - Counter of requests served from stale cache entries. +.IP \(bu 4 +\fB\fCcoredns_cache_evictions_total{server, type, zones, view}\fR - Counter of cache evictions. .PP @@ -163,3 +192,19 @@ example.org { .fi .RE +.PP +Enable caching for \fB\fCexample.org\fR, but do not cache denials in \fB\fCsub.example.org\fR: + +.PP +.RS + +.nf +example.org { + cache { + disable denial sub.example.org + } +} + +.fi +.RE + diff --git a/man/coredns-cancel.7 b/man/coredns-cancel.7 index a0c83865b4..a1037b03bf 100644 --- a/man/coredns-cancel.7 +++ b/man/coredns-cancel.7 @@ -1,5 +1,5 @@ .\" Generated by Mmark Markdown Processer - mmark.miek.nl -.TH "COREDNS-CANCEL" 7 "March 2021" "CoreDNS" "CoreDNS Plugins" +.TH "COREDNS-CANCEL" 7 "March 2026" "CoreDNS" "CoreDNS Plugins" .SH "NAME" .PP diff --git a/man/coredns-chaos.7 b/man/coredns-chaos.7 index a873d694b9..23e815ac22 100644 --- a/man/coredns-chaos.7 +++ b/man/coredns-chaos.7 @@ -1,5 +1,5 @@ .\" Generated by Mmark Markdown Processer - mmark.miek.nl -.TH "COREDNS-CHAOS" 7 "March 2021" "CoreDNS" "CoreDNS Plugins" +.TH "COREDNS-CHAOS" 7 "March 2026" "CoreDNS" "CoreDNS Plugins" .SH "NAME" .PP diff --git a/man/coredns-clouddns.7 b/man/coredns-clouddns.7 index 3b83a3a59a..51ac55dd10 100644 --- a/man/coredns-clouddns.7 +++ b/man/coredns-clouddns.7 @@ -1,5 +1,5 @@ .\" Generated by Mmark Markdown Processer - mmark.miek.nl -.TH "COREDNS-CLOUDDNS" 7 "March 2021" "CoreDNS" "CoreDNS Plugins" +.TH "COREDNS-CLOUDDNS" 7 "March 2026" "CoreDNS" "CoreDNS Plugins" .SH "NAME" .PP @@ -41,6 +41,13 @@ Therefore, for a non-existing resource record, SOA response will be from the rig accessed. .IP \(bu 4 \fB\fCcredentials\fR is used for reading the credential file from \fBFILENAME\fP (normally a .json file). +This field is optional. If this field is not provided then authentication will be done automatically, +e.g., through environmental variable \fB\fCGOOGLE_APPLICATION_CREDENTIALS\fR. Note that CoreDNS validates that the given +file has a valid credentials type, but does not validate the credentials file for malicious input. Please see +Google Cloud's authentication method +\[la]https://cloud.google.com/docs/authentication\[ra] and +validating credential configurations from external sources +\[la]https://docs.cloud.google.com/docs/authentication/client-libraries#external-credentials\[ra] for more details. .IP \(bu 4 \fB\fCfallthrough\fR If zone matches and no record can be generated, pass request to the next plugin. If \fB[ZONES...]\fP is omitted, then fallthrough happens for all zones for which the plugin is diff --git a/man/coredns-debug.7 b/man/coredns-debug.7 index 90ee34961e..ab034baece 100644 --- a/man/coredns-debug.7 +++ b/man/coredns-debug.7 @@ -1,5 +1,5 @@ .\" Generated by Mmark Markdown Processer - mmark.miek.nl -.TH "COREDNS-DEBUG" 7 "March 2021" "CoreDNS" "CoreDNS Plugins" +.TH "COREDNS-DEBUG" 7 "March 2026" "CoreDNS" "CoreDNS Plugins" .SH "NAME" .PP diff --git a/man/coredns-dns64.7 b/man/coredns-dns64.7 index 7a5274cb24..7491460382 100644 --- a/man/coredns-dns64.7 +++ b/man/coredns-dns64.7 @@ -1,5 +1,5 @@ .\" Generated by Mmark Markdown Processer - mmark.miek.nl -.TH "COREDNS-DNS64" 7 "March 2021" "CoreDNS" "CoreDNS Plugins" +.TH "COREDNS-DNS64" 7 "March 2026" "CoreDNS" "CoreDNS Plugins" .SH "NAME" .PP @@ -41,6 +41,7 @@ Or use this slightly longer form with more options: dns64 [PREFIX] { [translate\_all] prefix PREFIX + [allow\_ipv4] } .fi @@ -50,6 +51,8 @@ dns64 [PREFIX] { \fB\fCprefix\fR specifies any local IPv6 prefix to use, instead of the well known prefix (64:ff9b::/96) .IP \(bu 4 \fB\fCtranslate_all\fR translates all queries, including responses that have AAAA results. +.IP \(bu 4 +\fB\fCallow_ipv4\fR Allow translating queries if they come in over IPv4, default is IPv6 only translation. .SH "EXAMPLES" @@ -113,6 +116,25 @@ Enable translation even if an existing AAAA record is present. .fi .RE +.PP +Apply translation even to the requests which arrived over IPv4 network. Warning, the \fB\fCallow_ipv4\fR feature will apply +translations to requests coming from dual-stack clients. This means that a request for a client that sends an \fB\fCAAAA\fR +that would normal result in an \fB\fCNXDOMAIN\fR would get a translated result. +This may cause unwanted IPv6 dns64 traffic when a dualstack client would normally use the result of an \fB\fCA\fR record request. + +.PP +.RS + +.nf +\&. { + dns64 { + allow\_ipv4 + } +} + +.fi +.RE + .SH "METRICS" .PP If monitoring is enabled (via the \fIprometheus\fP plugin) then the following metrics are exported: diff --git a/man/coredns-dnssec.7 b/man/coredns-dnssec.7 index 7376e6b03b..d4c7249737 100644 --- a/man/coredns-dnssec.7 +++ b/man/coredns-dnssec.7 @@ -1,5 +1,5 @@ .\" Generated by Mmark Markdown Processer - mmark.miek.nl -.TH "COREDNS-DNSSEC" 7 "March 2021" "CoreDNS" "CoreDNS Plugins" +.TH "COREDNS-DNSSEC" 7 "March 2026" "CoreDNS" "CoreDNS Plugins" .SH "NAME" .PP @@ -20,7 +20,7 @@ This plugin can only be used once per Server Block. .nf dnssec [ZONES... ] { - key file KEY... + key file|aws\_secretsmanager KEY... cache\_capacity CAPACITY } @@ -39,10 +39,13 @@ ZSK/KSK split. All signing operations are done online. Authenticated denial of existence is implemented with NSEC black lies. Using ECDSA as an algorithm is preferred as this leads to smaller signatures (compared to RSA). NSEC3 is \fInot\fP supported. +.PP +As the \fIdnssec\fP plugin can't see the original TTL of the RRSets it signs, it will always use 3600s +as the value. + .PP If multiple \fIdnssec\fP plugins are specified in the same zone, the last one specified will be -used (See bugs -\[la]#bugs\[ra]). +used. .IP \(bu 4 \fBZONES\fP zones that should be signed. If empty, the zones from the configuration block @@ -62,6 +65,36 @@ generated public key \fB\fCKexample.org+013+45330.key\fR generated private key \fB\fCKexample.org+013+45330.private\fR .RE +.IP \(bu 4 +\fB\fCkey aws_secretsmanager\fR indicates that \fBKEY\fP secret(s) should be read from AWS Secrets Manager. Secret +names or ARNs may be used. After generating the keys as described in the \fB\fCkey file\fR section, you can +store them in AWS Secrets Manager using the following AWS CLI v2 command: + + +.PP +.RS + +.nf + aws secretsmanager create\-secret \-\-name "Kexample.org.+013+45330" \\ + \-\-description "DNSSEC keys for example.org" \\ + \-\-secret\-string "$(jq \-n \-\-arg key "$(cat Kexample.org.+013+45330.key)" \\ + \-\-arg private "$(cat Kexample.org.+013+45330.private)" \\ + '{key: $key, private: $private}')" + +.fi +.RE + +.PP +This command reads the contents of the \fB\fC.key\fR and \fB\fC.private\fR files, constructs a JSON object, and stores it + as a new secret in AWS Secrets Manager with the specified name and description. CoreDNS will then fetch + the key data from AWS Secrets Manager when using the \fB\fCkey aws_secretsmanager\fR directive. + +.PP +AWS SDK for Go V2 +\[la]https://aws.github.io/aws-sdk-go-v2/docs/configuring-sdk/#specifying-credentials\[ra] is used + for authentication with AWS Secrets Manager. Make sure the provided AWS credentials have the necessary + permissions (e.g., \fB\fCsecretsmanager:GetSecretValue\fR) to access the specified secrets in AWS Secrets Manager. + .IP \(bu 4 \fB\fCcache_capacity\fR indicates the capacity of the cache. The dnssec plugin uses a cache to store RRSIGs. The default for \fBCAPACITY\fP is 10000. @@ -100,6 +133,24 @@ example.org { .fi .RE +.PP +Sign responses for \fB\fCexample.org\fR with the key stored in AWS Secrets Manager under the secret name +"Kexample.org.+013+45330". + +.PP +.RS + +.nf +example.org { + dnssec { + key aws\_secretsmanager Kexample.org.+013+45330 + } + whoami +} + +.fi +.RE + .PP Sign responses for a kubernetes zone with the key "Kcluster.local+013+45129.key". diff --git a/man/coredns-dnstap.7 b/man/coredns-dnstap.7 index 6d39902505..a94c5c421f 100644 --- a/man/coredns-dnstap.7 +++ b/man/coredns-dnstap.7 @@ -1,5 +1,5 @@ .\" Generated by Mmark Markdown Processer - mmark.miek.nl -.TH "COREDNS-DNSTAP" 7 "March 2021" "CoreDNS" "CoreDNS Plugins" +.TH "COREDNS-DNSTAP" 7 "March 2026" "CoreDNS" "CoreDNS Plugins" .SH "NAME" .PP @@ -20,7 +20,12 @@ Every message is sent to the socket as soon as it comes in, the \fIdnstap\fP plu .RS .nf -dnstap SOCKET [full] +dnstap SOCKET [full] [writebuffer] [queue] { + [identity IDENTITY] + [version VERSION] + [extra EXTRA] + [skipverify] +} .fi .RE @@ -29,6 +34,19 @@ dnstap SOCKET [full] \fBSOCKET\fP is the socket (path) supplied to the dnstap command line tool. .IP \(bu 4 \fB\fCfull\fR to include the wire-format DNS message. +.IP \(bu 4 +\fBwritebuffer\fP sets the TCP write buffer multiplier in MiB. Valid range: [1, 1024]. +.IP \(bu 4 +\fBqueue\fP sets the queue multiplier, applied to 10,000 messages. Valid range: [1, 4096]. +.IP \(bu 4 +\fBIDENTITY\fP to override the identity of the server. Defaults to the hostname. +.IP \(bu 4 +\fBVERSION\fP to override the version field. Defaults to the CoreDNS version. +.IP \(bu 4 +\fBEXTRA\fP to define "extra" field in dnstap payload, metadata +\[la]../metadata/\[ra] replacement available here. +.IP \(bu 4 +\fB\fCskipverify\fR to skip tls verification during connection. Default to be secure .SH "EXAMPLES" @@ -44,6 +62,18 @@ dnstap /tmp/dnstap.sock .fi .RE +.PP +Log information about client requests and responses with a custom TCP write buffer (1024 MiB) and queue capacity (2048 x 10000). + +.PP +.RS + +.nf +dnstap /tmp/dnstap.sock full 1024 2048 + +.fi +.RE + .PP Log information including the wire-format DNS message about client requests and responses to \fI/tmp/dnstap.sock\fP. @@ -68,6 +98,78 @@ dnstap tcp://127.0.0.1:6000 full .fi .RE +.PP +Log to a remote endpoint by FQDN. + +.PP +.RS + +.nf +dnstap tcp://example.com:6000 full + +.fi +.RE + +.PP +Log to a socket, overriding the default identity and version. + +.PP +.RS + +.nf +dnstap /tmp/dnstap.sock { + identity my\-dns\-server1 + version MyDNSServer\-1.2.3 +} + +.fi +.RE + +.PP +Log to a socket, customize the "extra" field in dnstap payload. You may use metadata provided by other plugins in the extra field. + +.PP +.RS + +.nf +forward . 8.8.8.8 +metadata +dnstap /tmp/dnstap.sock { + extra "upstream: {/forward/upstream}" +} + +.fi +.RE + +.PP +Log to a remote TLS endpoint. + +.PP +.RS + +.nf +dnstap tls://127.0.0.1:6000 full { + skipverify +} + +.fi +.RE + +.PP +You can use \fIdnstap\fP more than once to define multiple taps. The following logs information including the +wire-format DNS message about client requests and responses to \fI/tmp/dnstap.sock\fP, +and also sends client requests and responses without wire-format DNS messages to a remote FQDN. + +.PP +.RS + +.nf +dnstap /tmp/dnstap.sock full +dnstap tcp://example.com:6000 + +.fi +.RE + .SH "COMMAND LINE TOOL" .PP Dnstap has a command line tool that can be used to inspect the logging. The tool can be found @@ -112,16 +214,18 @@ $ dnstap \-l 127.0.0.1:6000 .SH "USING DNSTAP IN YOUR PLUGIN" .PP -In your setup function, check to see if the \fIdnstap\fP plugin is loaded: +In your setup function, collect and store a list of all \fIdnstap\fP plugins loaded in the config: .PP .RS .nf +x := \&ExamplePlugin{} + c.OnStartup(func() error { if taph := dnsserver.GetConfig(c).Handler("dnstap"); taph != nil { - if tapPlugin, ok := taph.(dnstap.Dnstap); ok { - f.tapPlugin = \&tapPlugin + for tapPlugin, ok := taph.(*dnstap.Dnstap); ok; tapPlugin, ok = tapPlugin.Next.(*dnstap.Dnstap) { + x.tapPlugins = append(x.tapPlugins, tapPlugin) } } return nil @@ -137,8 +241,15 @@ And then in your plugin: .RS .nf -func (x RandomPlugin) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { - if tapPlugin != nil { +import ( + "github.com/coredns/coredns/plugin/dnstap/msg" + "github.com/coredns/coredns/request" + + tap "github.com/dnstap/golang\-dnstap" +) + +func (x ExamplePlugin) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { + for \_, tapPlugin := range x.tapPlugins { q := new(msg.Msg) msg.SetQueryTime(q, time.Now()) msg.SetQueryAddress(q, w.RemoteAddr()) @@ -147,7 +258,12 @@ func (x RandomPlugin) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns q.QueryMessage = buf } msg.SetType(q, tap.Message\_CLIENT\_QUERY) + + // if no metadata interpretation is needed, just send the message tapPlugin.TapMessage(q) + + // OR: to interpret the metadata in "extra" field, give more context info + tapPlugin.TapMessageWithMetadata(ctx, q, request.Request{W: w, Req: query}) } // ... } diff --git a/man/coredns-erratic.7 b/man/coredns-erratic.7 index c851536581..71d88c4d5a 100644 --- a/man/coredns-erratic.7 +++ b/man/coredns-erratic.7 @@ -1,5 +1,5 @@ .\" Generated by Mmark Markdown Processer - mmark.miek.nl -.TH "COREDNS-ERRATIC" 7 "March 2021" "CoreDNS" "CoreDNS Plugins" +.TH "COREDNS-ERRATIC" 7 "March 2026" "CoreDNS" "CoreDNS Plugins" .SH "NAME" .PP diff --git a/man/coredns-errors.7 b/man/coredns-errors.7 index b296fa3247..7357a5956f 100644 --- a/man/coredns-errors.7 +++ b/man/coredns-errors.7 @@ -1,5 +1,5 @@ .\" Generated by Mmark Markdown Processer - mmark.miek.nl -.TH "COREDNS-ERRORS" 7 "March 2021" "CoreDNS" "CoreDNS Plugins" +.TH "COREDNS-ERRORS" 7 "March 2026" "CoreDNS" "CoreDNS Plugins" .SH "NAME" .PP @@ -33,14 +33,19 @@ Extra knobs are available with an expanded syntax: .nf errors { - consolidate DURATION REGEXP + stacktrace + consolidate DURATION REGEXP [LEVEL] [show\_first] } .fi .RE .PP -Option \fB\fCconsolidate\fR allows collecting several error messages matching the regular expression \fBREGEXP\fP during \fBDURATION\fP. After the \fBDURATION\fP since receiving the first such message, the consolidated message will be printed to standard output, e.g. +Option \fB\fCstacktrace\fR will log a stacktrace during panic recovery. + +.PP +Option \fB\fCconsolidate\fR allows collecting several error messages matching the regular expression \fBREGEXP\fP during \fBDURATION\fP. \fBREGEXP\fP must not exceed 10000 characters. After the \fBDURATION\fP since receiving the first such message, the consolidated message will be printed to standard output with +log level, which is configurable by optional option \fBLEVEL\fP. Supported options for \fBLEVEL\fP option are \fB\fCwarning\fR,\fB\fCerror\fR,\fB\fCinfo\fR and \fB\fCdebug\fR. .PP .RS @@ -51,6 +56,36 @@ Option \fB\fCconsolidate\fR allows collecting several error messages matching th .fi .RE +.PP +If the optional \fB\fCshow_first\fR flag is specified, the first error will be logged immediately when it occurs, and then subsequent matching errors will be consolidated. When the consolidation period ends: +- If only one error occurred, no summary is printed (since it was already logged) +- If multiple errors occurred, a summary is printed showing the total count + +.PP +Example with 3 errors: + +.PP +.RS + +.nf +[WARNING] 2 example.org. A: read udp 10.0.0.1:53\->8.8.8.8:53: i/o timeout +[WARNING] 3 errors like '^read udp .* i/o timeout$' occurred in last 30s + +.fi +.RE + +.PP +Example with 1 error: + +.PP +.RS + +.nf +[WARNING] 2 example.org. A: read udp 10.0.0.1:53\->8.8.8.8:53: i/o timeout + +.fi +.RE + .PP Multiple \fB\fCconsolidate\fR options with different \fBDURATION\fP and \fBREGEXP\fP are allowed. In case if some error message corresponds to several defined regular expressions the message will be associated with the first appropriate \fBREGEXP\fP. @@ -74,7 +109,9 @@ example.org { .RE .PP -Use the \fIforward\fP to resolve queries via 8.8.8.8 and print consolidated error messages for errors with suffix " i/o timeout" or with prefix "Failed to ". +Use the \fIforward\fP plugin to resolve queries via 8.8.8.8 and print consolidated messages +for errors with suffix " i/o timeout" as warnings, +and errors with prefix "Failed to " as errors. .PP .RS @@ -83,7 +120,7 @@ Use the \fIforward\fP to resolve queries via 8.8.8.8 and print consolidated erro \&. { forward . 8.8.8.8 errors { - consolidate 5m ".* i/o timeout$" + consolidate 5m ".* i/o timeout$" warning consolidate 30s "^Failed to .+" } } @@ -91,3 +128,22 @@ Use the \fIforward\fP to resolve queries via 8.8.8.8 and print consolidated erro .fi .RE +.PP +Use the \fIforward\fP plugin and consolidate timeout errors with \fB\fCshow_first\fR option to see both +the summary and the first occurrence of the error: + +.PP +.RS + +.nf +\&. { + forward . 8.8.8.8 + errors { + consolidate 5m ".* i/o timeout$" warning show\_first + consolidate 30s "^Failed to .+" error show\_first + } +} + +.fi +.RE + diff --git a/man/coredns-etcd.7 b/man/coredns-etcd.7 index 371f81f2bd..0a384f7272 100644 --- a/man/coredns-etcd.7 +++ b/man/coredns-etcd.7 @@ -1,5 +1,5 @@ .\" Generated by Mmark Markdown Processer - mmark.miek.nl -.TH "COREDNS-ETCD" 7 "March 2021" "CoreDNS" "CoreDNS Plugins" +.TH "COREDNS-ETCD" 7 "March 2026" "CoreDNS" "CoreDNS Plugins" .SH "NAME" .PP @@ -85,6 +85,10 @@ file - if the server certificate is not signed by a system-installed CA and clie is needed. .RE +.IP \(bu 4 +\fB\fCmin-lease-ttl\fR the minimum TTL for DNS records based on etcd lease duration. Accepts flexible time formats like '30', '30s', '5m', '1h', '2h30m'. Default: 30 seconds. +.IP \(bu 4 +\fB\fCmax-lease-ttl\fR the maximum TTL for DNS records based on etcd lease duration. Accepts flexible time formats like '30', '30s', '5m', '1h', '2h30m'. Default: 24 hours. .SH "SPECIAL BEHAVIOUR" @@ -93,7 +97,7 @@ The \fIetcd\fP plugin leverages directory structure to look for related entries. an entry \fB\fC/skydns/test/skydns/mx\fR would have entries like \fB\fC/skydns/test/skydns/mx/a\fR, \fB\fC/skydns/test/skydns/mx/b\fR and so on. Similarly a directory \fB\fC/skydns/test/skydns/mx1\fR will have all \fB\fCmx1\fR entries. Note this plugin will search through the entire (sub)tree for records. In case of the -first example, a query for \fB\fCmx.skydns.text\fR will return both the contents of the \fB\fCa\fR and \fB\fCb\fR records. +first example, a query for \fB\fCmx.skydns.test\fR will return both the contents of the \fB\fCa\fR and \fB\fCb\fR records. If the directory extends deeper those records are returned as well. .PP @@ -120,6 +124,8 @@ skydns.local { etcd { path /skydns endpoint http://localhost:2379 + min\-lease\-ttl 60 # minimum 1 minute for lease\-based records + max\-lease\-ttl 1h # maximum 1 hour for lease\-based records } prometheus cache @@ -349,6 +355,7 @@ If you would like to use \fB\fCTXT\fR records, you can set the following: .nf % etcdctl put /skydns/local/skydns/x6 '{"ttl":60,"text":"this is a random text message."}' +% etcdctl put /skydns/local/skydns/x7 '{"ttl":60,"text":"this is a another random text message."}' .fi .RE @@ -362,6 +369,7 @@ If you query the zone name for \fB\fCTXT\fR now, you will get the following resp .nf % dig +short skydns.local TXT @localhost "this is a random text message." +"this is a another random text message." .fi .RE diff --git a/man/coredns-file.7 b/man/coredns-file.7 index 972ac3d476..14f3958259 100644 --- a/man/coredns-file.7 +++ b/man/coredns-file.7 @@ -1,5 +1,5 @@ .\" Generated by Mmark Markdown Processer - mmark.miek.nl -.TH "COREDNS-FILE" 7 "May 2025" "CoreDNS" "CoreDNS Plugins" +.TH "COREDNS-FILE" 7 "March 2026" "CoreDNS" "CoreDNS Plugins" .SH "NAME" .PP diff --git a/man/coredns-forward.7 b/man/coredns-forward.7 index c6f608d248..4d88c558d6 100644 --- a/man/coredns-forward.7 +++ b/man/coredns-forward.7 @@ -1,5 +1,5 @@ .\" Generated by Mmark Markdown Processer - mmark.miek.nl -.TH "COREDNS-FORWARD" 7 "March 2021" "CoreDNS" "CoreDNS Plugins" +.TH "COREDNS-FORWARD" 7 "March 2026" "CoreDNS" "CoreDNS Plugins" .SH "NAME" .PP @@ -22,9 +22,6 @@ is taken as a healthy upstream. The health check uses the same protocol as speci When \fIall\fP 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). -.PP -This plugin can only be used once per Server Block. - .SH "SYNTAX" .PP In its most basic form, a simple forwarder uses this syntax: @@ -39,7 +36,8 @@ forward FROM TO... .RE .IP \(bu 4 -\fBFROM\fP is the base domain to match for the request to be forwarded. +\fBFROM\fP is the base domain to match for the request to be forwarded. Domains using CIDR notation +that expand to multiple reverse zones are not fully supported; only the first expanded zone is used. .IP \(bu 4 \fBTO...\fP are the destination endpoints to forward to. The \fBTO\fP syntax allows you to specify a protocol, \fB\fCtls://9.9.9.9\fR or \fB\fCdns://\fR (or no protocol) for plain DNS. The number of upstreams is @@ -62,12 +60,17 @@ forward FROM TO... { force\_tcp prefer\_udp expire DURATION + max\_idle\_conns INTEGER max\_fails INTEGER + max\_connect\_attempts INTEGER tls CERT KEY CA tls\_servername NAME policy random|round\_robin|sequential - health\_check DURATION [no\_rec] + health\_check DURATION [no\_rec] [domain FQDN] max\_concurrent MAX + next RCODE\_1 [RCODE\_2] [RCODE\_3...] + failfast\_all\_unhealthy\_upstreams + failover RCODE\_1 [RCODE\_2] [RCODE\_3...] } .fi @@ -89,8 +92,15 @@ Requests that match none of these names will be passed through. an upstream to be down. If 0, the upstream will never be marked as down (nor health checked). Default is 2. .IP \(bu 4 +\fB\fCmax_connect_attempts\fR caps the total number of upstream connect attempts +performed for a single incoming DNS request. Default value of 0 means no per-request +cap. +.IP \(bu 4 \fB\fCexpire\fR \fBDURATION\fP, expire (cached) connections after this time, the default is 10s. .IP \(bu 4 +\fB\fCmax_idle_conns\fR \fBINTEGER\fP, maximum number of idle connections to cache per upstream for reuse. +Default is 0, which means unlimited. +.IP \(bu 4 \fB\fCtls\fR \fBCERT\fP \fBKEY\fP \fBCA\fP define the TLS properties for TLS connection. From 0 to 3 arguments can be provided with the meaning as described below @@ -109,9 +119,18 @@ The server certificate is verified using the specified CA file .RE .IP \(bu 4 \fB\fCtls_servername\fR \fBNAME\fP allows you to set a server name in the TLS configuration; for instance 9.9.9.9 -needs this to be set to \fB\fCdns.quad9.net\fR. Multiple upstreams are still allowed in this scenario, -but they have to use the same \fB\fCtls_servername\fR. E.g. mixing 9.9.9.9 (QuadDNS) with 1.1.1.1 -(Cloudflare) will not work. +needs this to be set to \fB\fCdns.quad9.net\fR. Using TLS forwarding but not setting \fB\fCtls_servername\fR results in anyone +being able to man-in-the-middle your connection to the DNS server you are forwarding to. Because of this, +it is strongly recommended to set this value when using TLS forwarding. + + +.PP +Per destination endpoint TLS server name indication is possible in the form of \fB\fCtls://9.9.9.9%dns.quad9.net\fR. + \fB\fCtls_servername\fR must not be specified when using per destination endpoint TLS server name indication + as it would introduce clash between the server name indication spectifications. If destination endpoint + is to be reached via a port other than 853 then the port must be appended to the end of the destination + endpoint specifier. In case of port 10853, the above string would be: \fB\fCtls://9.9.9.9%dns.quad9.net:10853\fR. + .IP \(bu 4 \fB\fCpolicy\fR specifies the policy to use for selecting upstream servers. The default is \fB\fCrandom\fR. @@ -133,6 +152,9 @@ but they have to use the same \fB\fCtls_servername\fR. E.g. mixing 9.9.9.9 (Quad .IP \(en 4 \fB\fCno_rec\fR - optional argument that sets the RecursionDesired-flag of the dns-query used in health checking to \fB\fCfalse\fR. The flag is default \fB\fCtrue\fR. +.IP \(en 4 +\fB\fCdomain FQDN\fR - set the domain name used for health checks to \fBFQDN\fP. +If not configured, the domain name used for health checks is \fB\fC.\fR. .RE .IP \(bu 4 @@ -141,17 +163,23 @@ raise the number of concurrent queries above the \fBMAX\fP will result in a REFU response does not count as a health failure. When choosing a value for \fBMAX\fP, pick a number at least greater than the expected \fIupstream query rate\fP * \fIlatency\fP of the upstream servers. As an upper bound for \fBMAX\fP, consider that each concurrent query will use about 2kb of memory. +.IP \(bu 4 +\fB\fCnext\fR If the \fB\fCRCODE\fR (i.e. \fB\fCNXDOMAIN\fR) is returned by the remote then execute the next plugin. If no next plugin is defined, or the next plugin is not a \fB\fCforward\fR plugin, this setting is ignored +.IP \(bu 4 +\fB\fCfailfast_all_unhealthy_upstreams\fR - determines the handling of requests when all upstream servers are unhealthy and unresponsive to health checks. Enabling this option will immediately return SERVFAIL responses for all requests. By default, requests are sent to a random upstream. +.IP \(bu 4 +\fB\fCfailover\fR - By default when a DNS lookup fails to return a DNS response (e.g. timeout), \fIforward\fP will attempt a lookup on the next upstream server. The \fB\fCfailover\fR option will make \fIforward\fP do the same for any response with a response code matching an \fB\fCRCODE\fR ( e.g. \fB\fCSERVFAIL\fR、\fB\fCREFUSED\fR). \fB\fCNOERROR\fR cannot be used. If all upstreams have been tried, the response from the last attempt is returned. .PP Also note the TLS config is "global" for the whole forwarding proxy if you need a different -\fB\fCtls-name\fR for different upstreams you're out of luck. +\fB\fCtls_servername\fR for different upstreams you're out of luck. .PP On each endpoint, the timeouts for communication are set as follows: .IP \(bu 4 -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. .IP \(bu 4 The read timeout is static at 2s. @@ -170,28 +198,35 @@ plugin is also enabled: If monitoring is enabled (via the \fIprometheus\fP plugin) then the following metric are exported: .IP \(bu 4 -\fB\fCcoredns_forward_requests_total{to}\fR - query count per upstream. -.IP \(bu 4 -\fB\fCcoredns_forward_responses_total{to}\fR - Counter of responses received per upstream. -.IP \(bu 4 -\fB\fCcoredns_forward_request_duration_seconds{to, rcode, type}\fR - duration per upstream, RCODE, type -.IP \(bu 4 -\fB\fCcoredns_forward_responses_total{to, rcode}\fR - count of RCODEs per upstream. -.IP \(bu 4 -\fB\fCcoredns_forward_healthcheck_failures_total{to}\fR - number of failed health checks per upstream. -.IP \(bu 4 -\fB\fCcoredns_forward_healthcheck_broken_total{}\fR - counter of when all upstreams are unhealthy, +\fB\fCcoredns_forward_healthcheck_broken_total{}\fR - count of when all upstreams are unhealthy, and we are randomly (this always uses the \fB\fCrandom\fR policy) spraying to an upstream. .IP \(bu 4 -\fB\fCcoredns_forward_max_concurrent_rejects_total{}\fR - counter of the number of queries rejected because the +\fB\fCcoredns_forward_max_concurrent_rejects_total{}\fR - count of queries rejected because the number of concurrent queries were at maximum. .IP \(bu 4 -\fB\fCcoredns_forward_conn_cache_hits_total{to, proto}\fR - counter of connection cache hits per upstream and protocol. +\fB\fCcoredns_proxy_request_duration_seconds{proxy_name="forward", to, rcode}\fR - histogram per upstream, RCODE +.IP \(bu 4 +\fB\fCcoredns_proxy_healthcheck_failures_total{proxy_name="forward", to, rcode}\fR- count of failed health checks per upstream. .IP \(bu 4 -\fB\fCcoredns_forward_conn_cache_misses_total{to, proto}\fR - counter of connection cache misses per upstream and protocol. +\fB\fCcoredns_proxy_conn_cache_hits_total{proxy_name="forward", to, proto}\fR- count of connection cache hits per upstream and protocol. +.IP \(bu 4 +\fB\fCcoredns_proxy_conn_cache_misses_total{proxy_name="forward", to, proto}\fR - count of connection cache misses per upstream and protocol. + + +.PP Where \fB\fCto\fR is one of the upstream servers (\fBTO\fP from the config), \fB\fCrcode\fR is the returned RCODE from the upstream, \fB\fCproto\fR is the transport protocol like \fB\fCudp\fR, \fB\fCtcp\fR, \fB\fCtcp-tls\fR. +.PP +The following metrics have recently been deprecated: +* \fB\fCcoredns_forward_healthcheck_failures_total{to, rcode}\fR + * Can be replaced with \fB\fCcoredns_proxy_healthcheck_failures_total{proxy_name="forward", to, rcode}\fR +* \fB\fCcoredns_forward_requests_total{to}\fR + * Can be replaced with \fB\fCsum(coredns_proxy_request_duration_seconds_count{proxy_name="forward", to})\fR +* \fB\fCcoredns_forward_responses_total{to, rcode}\fR + * Can be replaced with \fB\fCcoredns_proxy_request_duration_seconds_count{proxy_name="forward", to, rcode}\fR +* \fB\fCcoredns_forward_request_duration_seconds{to, rcode}\fR + * Can be replaced with \fB\fCcoredns_proxy_request_duration_seconds{proxy_name="forward", to, rcode}\fR .SH "EXAMPLES" .PP @@ -208,6 +243,52 @@ example.org { .fi .RE +.PP +Send all requests within \fB\fClab.example.local.\fR to \fB\fC10.20.0.1\fR, all requests within \fB\fCexample.local.\fR (and not in +\fB\fClab.example.local.\fR) to \fB\fC10.0.0.1\fR, all others requests to the servers defined in \fB\fC/etc/resolv.conf\fR, and +caches results. Note that a CoreDNS server configured with multiple \fIforward\fP 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 \fB\fClab.example.local\fR is before \fB\fCexample.local\fR, and \fB\fCexample.local\fR is before \fB\fC.\fR. + +.PP +.RS + +.nf +\&. { + cache + forward lab.example.local 10.20.0.1 + forward example.local 10.0.0.1 + forward . /etc/resolv.conf +} + +.fi +.RE + +.PP +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 \fIcache\fP). + +.PP +.RS + +.nf +lab.example.local { + cache + forward . 10.20.0.1 +} +example.local { + cache + forward . 10.0.0.1 +} +\&. { + cache + forward . /etc/resolv.conf +} + +.fi +.RE + .PP Load balance all requests between three resolvers, one of which has a IPv6 address. @@ -275,6 +356,24 @@ service with health checks. .fi .RE +.PP +Or configure other domain name for health check requests + +.PP +.RS + +.nf +\&. { + forward . tls://9.9.9.9 { + tls\_servername dns.quad9.net + health\_check 5s domain example.org + } + cache 30 +} + +.fi +.RE + .PP Or with multiple upstreams from the same provider @@ -305,13 +404,13 @@ Or when you have multiple DoT upstreams with different \fB\fCtls_servername\fRs, } \&.:5301 { - forward . 8.8.8.8 8.8.4.4 { + forward . tls://8.8.8.8 tls://8.8.4.4 { tls\_servername dns.google } } \&.:5302 { - forward . 1.1.1.1 1.0.0.1 { + forward . tls://1.1.1.1 tls://1.0.0.1 { tls\_servername cloudflare\-dns.com } } @@ -319,6 +418,44 @@ Or when you have multiple DoT upstreams with different \fB\fCtls_servername\fRs, .fi .RE +.PP +The following would try 1.2.3.4 first. If the response is \fB\fCNXDOMAIN\fR, try 5.6.7.8. If the response from 5.6.7.8 is \fB\fCNXDOMAIN\fR, try 9.0.1.2. + +.PP +.RS + +.nf +\&. { + forward . 1.2.3.4 { + next NXDOMAIN + } + forward . 5.6.7.8 { + next NXDOMAIN + } + forward . 9.0.1.2 { + } +} + +.fi +.RE + +.PP +In the following example, if the response from \fB\fC1.2.3.4\fR is \fB\fCSERVFAIL\fR or \fB\fCREFUSED\fR, it will try \fB\fC5.6.7.8\fR. If the response from \fB\fC5.6.7.8\fR is \fB\fCSERVFAIL\fR or \fB\fCREFUSED\fR, it will try \fB\fC9.0.1.2\fR. + +.PP +.RS + +.nf +\&. { + forward . 1.2.3.4 5.6.7.8 9.0.1.2 { + policy sequential + failover SERVFAIL REFUSED + } +} + +.fi +.RE + .SH "SEE ALSO" .PP RFC 7858 diff --git a/man/coredns-geoip.7 b/man/coredns-geoip.7 index 28104259d0..7d2b54db13 100644 --- a/man/coredns-geoip.7 +++ b/man/coredns-geoip.7 @@ -1,17 +1,20 @@ .\" Generated by Mmark Markdown Processer - mmark.miek.nl -.TH "COREDNS-GEOIP" 7 "July 2021" "CoreDNS" "CoreDNS Plugins" +.TH "COREDNS-GEOIP" 7 "March 2026" "CoreDNS" "CoreDNS Plugins" .SH "NAME" .PP -\fIgeoip\fP - Lookup maxmind geoip2 databases using the client IP, then add associated geoip data to the context request. +\fIgeoip\fP - Lookup \fB\fC.mmdb\fR (MaxMind db file format +\[la]https://maxmind.github.io/MaxMind-DB/\[ra]) databases using the client IP, then add associated geoip data to the context request. .SH "DESCRIPTION" .PP -The \fIgeoip\fP plugin add geo location data associated with the client IP, it allows you to configure a geoIP2 maxmind database -\[la]https://dev.maxmind.com/geoip/docs/databases\[ra] to add the geo location data associated with the IP address. +The \fIgeoip\fP plugin allows you to enrich the data associated with Client IP addresses, e.g. geoip information like City, Country, and Network ASN. GeoIP data is commonly available in the \fB\fC.mmdb\fR format, a database format that maps IPv4 and IPv6 addresses to data records using a binary search tree. .PP -The data is added leveraging the \fImetadata\fP plugin, values can then be retrieved using it as well, for example: +The data is added leveraging the \fImetadata\fP plugin, values can then be retrieved using it as well. + +.PP +\fBLongitude example:\fP .PP .RS @@ -34,13 +37,64 @@ if getLongitude := metadata.ValueFunc(ctx, "geoip/longitude"); getLongitude != n .fi .RE +.PP +\fBCity example:\fP + +.PP +.RS + +.nf +import ( + "github.com/coredns/coredns/plugin/metadata" +) +// ... +if getCity := metadata.ValueFunc(ctx, "geoip/city/name"); getCity != nil { + city := getCity() + // Do something useful with city. +} else { + // The metadata label geoip/city/name for some reason, was not set. +} +// ... + +.fi +.RE + +.PP +\fBASN example:\fP + +.PP +.RS + +.nf +import ( + "strconv" + "github.com/coredns/coredns/plugin/metadata" +) +// ... +if getASN := metadata.ValueFunc(ctx, "geoip/asn/number"); getASN != nil { + if asn, err := strconv.ParseUint(getASN(), 10, 32); err == nil { + // Do something useful with asn. + } +} +if getASNOrg := metadata.ValueFunc(ctx, "geoip/asn/org"); getASNOrg != nil { + asnOrg := getASNOrg() + // Do something useful with asnOrg. +} +// ... + +.fi +.RE + .SH "DATABASES" .PP -The supported databases use city schema such as \fB\fCCity\fR and \fB\fCEnterprise\fR. Other databases types with different schemas are not supported yet. +The supported databases use city schema such as \fB\fCASN\fR, \fB\fCCity\fR, and \fB\fCEnterprise\fR. \fB\fC.mmdb\fR files are generally supported, as long as their field names correctly map to the Metadata Labels below. Other database types with different schemas are not supported yet. .PP -You can download a free and public City database -\[la]https://dev.maxmind.com/geoip/geolite2-free-geolocation-data\[ra]. +Free and commercial GeoIP \fB\fC.mmdb\fR files are commonly available from vendors like MaxMind +\[la]https://dev.maxmind.com/geoip/docs/databases\[ra], IPinfo +\[la]https://ipinfo.io/developers/database-download\[ra], and IPtoASN +\[la]https://iptoasn.com/\[ra] which is Public Domain-licensed +\[la]https://opendatacommons.org/licenses/pddl/1-0/\[ra]. .SH "SYNTAX" .PP @@ -52,34 +106,88 @@ geoip [DBFILE] .fi .RE +.PP +or + +.PP +.RS + +.nf +geoip [DBFILE] { + [edns\-subnet] +} + +.fi +.RE + +.IP \(bu 4 +\fBDBFILE\fP the \fB\fCmmdb\fR database file path. We recommend updating your \fB\fCmmdb\fR database periodically for more accurate results. .IP \(bu 4 -\fBDBFILE\fP the mmdb database file path. +\fB\fCedns-subnet\fR: Optional. Use EDNS0 subnet +\[la]https://en.wikipedia.org/wiki/EDNS_Client_Subnet\[ra] (if present) for Geo IP instead of the source IP of the DNS request. This helps identifying the closest source IP address through intermediary DNS resolvers, and it also makes GeoIP testing easy: \fB\fCdig +subnet=1.2.3.4 @dns-server.example.com www.geo-aware.com\fR. +.PP +\fBNOTE:\fP due to security reasons, recursive DNS resolvers may mask a few bits off of the clients' IP address, which can cause inaccuracies in GeoIP resolution. + +.PP +There is no defined mask size in the standards, but there are examples: RFC 7871's example +\[la]https://datatracker.ietf.org/doc/html/rfc7871#section-13\[ra] conceals the last 72 bits of an IPv6 source address, and NS1 Help Center mentions +\[la]https://help.ns1.com/hc/en-us/articles/360020256573-About-the-EDNS-Client-Subnet-ECS-DNS-extension\[ra] that ECS-enabled DNS resolvers send only the first three octets (eg. /24) of the source IPv4 address. + .SH "EXAMPLES" .PP -The following configuration configures the \fB\fCCity\fR database. +The following configuration configures the \fB\fCCity\fR database, and looks up geolocation based on EDNS0 subnet if present. .PP .RS .nf \&. { - geoip /opt/geoip2/db/GeoLite2\-City.mmdb + geoip /opt/geoip2/db/GeoLite2\-City.mmdb { + edns\-subnet + } metadata # Note that metadata plugin must be enabled as well. } .fi .RE -.SH "METADATADA LABELS" +.PP +The \fIview\fP plugin can use \fIgeoip\fP metadata as selection criteria to provide GSLB functionality. +In this example, clients from the city "Exampleshire" will receive answers for \fB\fCexample.com\fR from the zone defined in +\fB\fCexample.com.exampleshire-db\fR. All other clients will receive answers from the zone defined in \fB\fCexample.com.db\fR. +Note that the order of the two \fB\fCexample.com\fR server blocks below is important; the default viewless server block +must be last. + +.PP +.RS + +.nf +example.com { + view exampleshire { + expr metadata('geoip/city/name') == 'Exampleshire' + } + geoip /opt/geoip2/db/GeoLite2\-City.mmdb + metadata + file example.com.exampleshire\-db +} + +example.com { + file example.com.db +} + +.fi +.RE + +.SH "METADATA LABELS" .PP A limited set of fields will be exported as labels, all values are stored using strings \fBregardless of their underlying value type\fP, and therefore you may have to convert it back to its original type, note that numeric values are always represented in base 10. .RS .TS allbox; -l l l l +l l l l l l l l . \fBLabel\fP\fB Type\fP\fB Example\fP\fB Description\fP \fB\fCgeoip/city/name\fR \fB\fCstring\fR \fB\fCCambridge\fR Then city name in English language. @@ -94,6 +202,10 @@ l l l l . \fB\fCgeoip/longitude\fR \fB\fCfloat64\fR \fB\fC0.1315\fR Base 10, max available precision. \fB\fCgeoip/timezone\fR \fB\fCstring\fR \fB\fCEurope/London\fR The timezone. \fB\fCgeoip/postalcode\fR \fB\fCstring\fR \fB\fCCB4\fR The postal code. +\fB\fCgeoip/subdivisions/code\fR \fB\fCstring\fR \fB\fCENG,TWH\fR Comma separated ISO 3166-2 +\[la]https://en.wikipedia.org/wiki/ISO_3166-2\[ra] subdivision(region) codes, e.g. first level (province), second level (state). +\fB\fCgeoip/asn/number\fR \fB\fCuint\fR \fB\fC396982\fR The autonomous system number. +\fB\fCgeoip/asn/org\fR \fB\fCstring\fR \fB\fCGOOGLE-CLOUD-PLATFORM\fR The autonomous system organization. .TE .RE @@ -102,7 +214,7 @@ l l l l . .RS .TS allbox; -l l +l l l l . \fBValue\fP\fB Continent (EN)\fP AF Africa @@ -116,3 +228,11 @@ SA South America .RE +.SH "NOTABLE CHANGES" +.IP \(bu 4 +In CoreDNS v1.13.2, the \fB\fCgeoip\fR plugin was upgraded to use \fB\fCoschwald/geoip2-golang/v2\fR +\[la]https://github.com/oschwald/geoip2-golang/blob/main/MIGRATION.md\[ra], the Go library that reads and parses \fB\fC.mmdb\fR +\[la]https://maxmind.github.io/MaxMind-DB/\[ra] databases. It has a small, but possibly-breaking change, where the \fB\fCLocation.Latitude\fR and \fB\fCLocation.Longitude\fR structs changed from value types to pointers (\fB\fCfloat64\fR → \fB\fC*float64\fR). In \fB\fCoschwald/geoip2-golang\fR v1, missing coordinates returned "0" (which is a valid location +\[la]https://en.wikipedia.org/wiki/Null_Island\[ra]), and in v2 they now return an empty string "". + + diff --git a/man/coredns-grpc.7 b/man/coredns-grpc.7 index d286cda0eb..f2aefd908f 100644 --- a/man/coredns-grpc.7 +++ b/man/coredns-grpc.7 @@ -1,5 +1,5 @@ .\" Generated by Mmark Markdown Processer - mmark.miek.nl -.TH "COREDNS-GRPC" 7 "March 2021" "CoreDNS" "CoreDNS Plugins" +.TH "COREDNS-GRPC" 7 "March 2026" "CoreDNS" "CoreDNS Plugins" .SH "NAME" .PP @@ -48,6 +48,7 @@ grpc FROM TO... { tls CERT KEY CA tls\_servername NAME policy random|round\_robin|sequential + fallthrough [ZONES...] } .fi @@ -82,6 +83,13 @@ but they have to use the same \fB\fCtls_servername\fR. E.g. mixing 9.9.9.9 (Quad (Cloudflare) will not work. .IP \(bu 4 \fB\fCpolicy\fR specifies the policy to use for selecting upstream servers. The default is \fB\fCrandom\fR. +.IP \(bu 4 +\fB\fCfallthrough\fR \fB[ZONES...]\fP If a query results in NXDOMAIN from the gRPC backend, pass the request +to the next plugin instead of returning the NXDOMAIN response. This is useful when the gRPC backend +is authoritative for a zone but should not return authoritative NXDOMAIN responses for queries that +don't actually belong to that zone (e.g., search path queries). If \fB[ZONES...]\fP is omitted, then +fallthrough happens for all zones. If specific zones are listed, then only queries for those zones +will be subject to fallthrough. .PP @@ -198,6 +206,37 @@ Or with multiple upstreams from the same provider .fi .RE +.PP +Forward requests to a local upstream listening on a Unix domain socket. + +.PP +.RS + +.nf +\&. { + grpc . unix:///path/to/grpc.sock +} + +.fi +.RE + +.PP +Proxy requests for \fB\fCexample.org.\fR to a gRPC backend, but fallthrough to the next plugin for NXDOMAIN responses to handle search path queries correctly. + +.PP +.RS + +.nf +example.org { + grpc . 127.0.0.1:9005 { + fallthrough + } + forward . 8.8.8.8 +} + +.fi +.RE + .SH "BUGS" .PP The TLS config is global for the whole grpc proxy if you need a different \fB\fCtls_servername\fR for diff --git a/man/coredns-grpc_server.7 b/man/coredns-grpc_server.7 new file mode 100644 index 0000000000..3dfa8b25f7 --- /dev/null +++ b/man/coredns-grpc_server.7 @@ -0,0 +1,72 @@ +.\" Generated by Mmark Markdown Processer - mmark.miek.nl +.TH "COREDNS-GRPC_SERVER" 7 "March 2026" "CoreDNS" "CoreDNS Plugins" + +.SH "NAME" +.PP +\fIgrpc_server\fP - configures DNS-over-gRPC server options. + +.SH "DESCRIPTION" +.PP +The \fIgrpc_server\fP plugin allows you to configure parameters for the DNS-over-gRPC server to fine-tune the security posture and performance of the server. + +.PP +This plugin can only be used once per gRPC listener block. + +.SH "SYNTAX" +.PP +.RS + +.nf +grpc\_server { + max\_streams POSITIVE\_INTEGER + max\_connections POSITIVE\_INTEGER +} + +.fi +.RE + +.IP \(bu 4 +\fB\fCmax_streams\fR limits the number of concurrent gRPC streams per connection. This helps prevent unbounded streams on a single connection, exhausting server resources. The default value is 256 if not specified. Set to 0 for unbounded. +.IP \(bu 4 +\fB\fCmax_connections\fR limits the number of concurrent TCP connections to the gRPC server. The default value is 200 if not specified. Set to 0 for unbounded. + + +.SH "EXAMPLES" +.PP +Set custom limits for maximum streams and connections: + +.PP +.RS + +.nf +grpc://.:8053 { + tls cert.pem key.pem + grpc\_server { + max\_streams 50 + max\_connections 100 + } + whoami +} + +.fi +.RE + +.PP +Set values to 0 for unbounded, matching CoreDNS behaviour before v1.14.0: + +.PP +.RS + +.nf +grpc://.:8053 { + tls cert.pem key.pem + grpc\_server { + max\_streams 0 + max\_connections 0 + } + whoami +} + +.fi +.RE + diff --git a/man/coredns-header.7 b/man/coredns-header.7 index c0e76e46df..0dbd89718b 100644 --- a/man/coredns-header.7 +++ b/man/coredns-header.7 @@ -1,14 +1,14 @@ .\" Generated by Mmark Markdown Processer - mmark.miek.nl -.TH "COREDNS-HEADER" 7 "July 2021" "CoreDNS" "CoreDNS Plugins" +.TH "COREDNS-HEADER" 7 "March 2026" "CoreDNS" "CoreDNS Plugins" .SH "NAME" .PP -\fIheader\fP - modifies the header for responses. +\fIheader\fP - modifies the header for queries and responses. .SH "DESCRIPTION" .PP -\fIheader\fP ensures that the flags are in the desired state for responses. The modifications are made transparently for -the client. +\fIheader\fP ensures that the flags are in the desired state for queries and responses. +The modifications are made transparently for the client and subsequent plugins. .SH "SYNTAX" .PP @@ -16,13 +16,15 @@ the client. .nf header { - ACTION FLAGS... - ACTION FLAGS... + SELECTOR ACTION FLAGS... + SELECTOR ACTION FLAGS... } .fi .RE +.IP \(bu 4 +\fBSELECTOR\fP defines if the action should be applied on \fB\fCquery\fR or \fB\fCresponse\fR. .IP \(bu 4 \fBACTION\fP defines the state for DNS message header flags. Actions are evaluated in the order they are defined so last one has the most precedence. Allowed values are: @@ -58,7 +60,7 @@ Make sure recursive available \fB\fCra\fR flag is set in all the responses: .nf \&. { header { - set ra + response set ra } } @@ -74,8 +76,24 @@ Make sure "recursion available" \fB\fCra\fR and "authoritative answer" \fB\fCaa\ .nf \&. { header { - set ra aa - clear rd + response set ra aa + response clear rd + } +} + +.fi +.RE + +.PP +Make sure "recursion desired" \fB\fCrd\fR is set for all subsequent plugins:: + +.PP +.RS + +.nf +\&. { + header { + query set rd } } diff --git a/man/coredns-health.7 b/man/coredns-health.7 index 388d4e96ce..0db899642d 100644 --- a/man/coredns-health.7 +++ b/man/coredns-health.7 @@ -1,5 +1,5 @@ .\" Generated by Mmark Markdown Processer - mmark.miek.nl -.TH "COREDNS-HEALTH" 7 "March 2021" "CoreDNS" "CoreDNS Plugins" +.TH "COREDNS-HEALTH" 7 "March 2026" "CoreDNS" "CoreDNS Plugins" .SH "NAME" .PP @@ -40,7 +40,7 @@ health [ADDRESS] { .IP \(bu 4 Where \fB\fClameduck\fR will delay shutdown for \fBDURATION\fP. /health will still answer 200 OK. -Note: The \fIready\fP plugin will not answer OK while CoreDNS is in lameduck mode prior to shutdown. +Note: The \fIready\fP plugin will not answer OK while CoreDNS is in lame duck mode prior to shutdown. .PP @@ -69,16 +69,19 @@ Doing this is supported but both endpoints ":8080" and ":8081" will export the e .SH "METRICS" .PP -If monitoring is enabled (via the \fIprometheus\fP plugin) then the following metric is exported: +If monitoring is enabled (via the \fIprometheus\fP plugin) then the following metrics are exported: .IP \(bu 4 -\fB\fCcoredns_health_request_duration_seconds{}\fR - duration to process a HTTP query to the local -\fB\fC/health\fR endpoint. As this a local operation it should be fast. A (large) increase in this +\fB\fCcoredns_health_request_duration_seconds{}\fR - The \fIhealth\fP plugin performs a self health check +once per second on the \fB\fC/health\fR 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. +.IP \(bu 4 +\fB\fCcoredns_health_request_failures_total{}\fR - The number of times the self health check failed. .PP -Note that this metric \fIdoes not\fP have a \fB\fCserver\fR label, because being overloaded is a symptom of +Note that these metrics \fIdo not\fP have a \fB\fCserver\fR label, because being overloaded is a symptom of the running process, \fInot\fP a specific server. .SH "EXAMPLES" @@ -98,7 +101,7 @@ Run another health endpoint on http://localhost:8091 .RE .PP -Set a lameduck duration of 1 second: +Set a lame duck duration of 1 second: .PP .RS diff --git a/man/coredns-hosts.7 b/man/coredns-hosts.7 index 72bf9a9081..826dd0e68a 100644 --- a/man/coredns-hosts.7 +++ b/man/coredns-hosts.7 @@ -1,5 +1,5 @@ .\" Generated by Mmark Markdown Processer - mmark.miek.nl -.TH "COREDNS-HOSTS" 7 "March 2021" "CoreDNS" "CoreDNS Plugins" +.TH "COREDNS-HOSTS" 7 "March 2026" "CoreDNS" "CoreDNS Plugins" .SH "NAME" .PP diff --git a/man/coredns-https.7 b/man/coredns-https.7 new file mode 100644 index 0000000000..d164e30719 --- /dev/null +++ b/man/coredns-https.7 @@ -0,0 +1,67 @@ +.\" Generated by Mmark Markdown Processer - mmark.miek.nl +.TH "COREDNS-HTTPS" 7 "March 2026" "CoreDNS" "CoreDNS Plugins" + +.SH "NAME" +.PP +\fIhttps\fP - configures DNS-over-HTTPS (DoH) server options. + +.SH "DESCRIPTION" +.PP +The \fIhttps\fP plugin allows you to configure parameters for the DNS-over-HTTPS (DoH) server to fine-tune the security posture and performance of the server. + +.PP +This plugin can only be used once per HTTPS listener block. + +.SH "SYNTAX" +.PP +.RS + +.nf +https { + max\_connections POSITIVE\_INTEGER +} + +.fi +.RE + +.IP \(bu 4 +\fB\fCmax_connections\fR limits the number of concurrent TCP connections to the HTTPS server. The default value is 200 if not specified. Set to 0 for unbounded. + + +.SH "EXAMPLES" +.PP +Set custom limits for maximum connections: + +.PP +.RS + +.nf +https://.:443 { + tls cert.pem key.pem + https { + max\_connections 100 + } + whoami +} + +.fi +.RE + +.PP +Set values to 0 for unbounded, matching CoreDNS behaviour before v1.14.0: + +.PP +.RS + +.nf +https://.:443 { + tls cert.pem key.pem + https { + max\_connections 0 + } + whoami +} + +.fi +.RE + diff --git a/man/coredns-https3.7 b/man/coredns-https3.7 new file mode 100644 index 0000000000..fbcc2c3f20 --- /dev/null +++ b/man/coredns-https3.7 @@ -0,0 +1,67 @@ +.\" Generated by Mmark Markdown Processer - mmark.miek.nl +.TH "COREDNS-HTTPS3" 7 "March 2026" "CoreDNS" "CoreDNS Plugins" + +.SH "NAME" +.PP +\fIhttps3\fP - configures DNS-over-HTTPS/3 (DoH3) server options. + +.SH "DESCRIPTION" +.PP +The \fIhttps3\fP plugin allows you to configure parameters for the DNS-over-HTTPS/3 (DoH3) server to fine-tune the security posture and performance of the server. HTTPS/3 uses QUIC as the underlying transport. + +.PP +This plugin can only be used once per HTTPS3 listener block. + +.SH "SYNTAX" +.PP +.RS + +.nf +https3 { + max\_streams POSITIVE\_INTEGER +} + +.fi +.RE + +.IP \(bu 4 +\fB\fCmax_streams\fR limits the number of concurrent QUIC streams per connection. This helps prevent unbounded streams on a single connection, exhausting server resources. The default value is 256 if not specified. Set to 0 to use underlying QUIC transport default. + + +.SH "EXAMPLES" +.PP +Set custom limits for maximum streams: + +.PP +.RS + +.nf +https3://.:443 { + tls cert.pem key.pem + https3 { + max\_streams 50 + } + whoami +} + +.fi +.RE + +.PP +Set values to 0 for QUIC transport default, matching CoreDNS behaviour before v1.14.0: + +.PP +.RS + +.nf +https3://.:443 { + tls cert.pem key.pem + https3 { + max\_streams 0 + } + whoami +} + +.fi +.RE + diff --git a/man/coredns-import.7 b/man/coredns-import.7 index dda17a022a..57dd1e1a35 100644 --- a/man/coredns-import.7 +++ b/man/coredns-import.7 @@ -1,5 +1,5 @@ .\" Generated by Mmark Markdown Processer - mmark.miek.nl -.TH "COREDNS-IMPORT" 7 "March 2021" "CoreDNS" "CoreDNS Plugins" +.TH "COREDNS-IMPORT" 7 "March 2026" "CoreDNS" "CoreDNS Plugins" .SH "NAME" .PP @@ -29,6 +29,9 @@ import PATTERN this line, as if that file's contents appeared here to begin with. +.PP +Corefile may contain at most 10000 import statements. A glob pattern counts as a single import. The limit protects the configuration from recursive imports. + .SH "FILES" .PP You can use \fIimport\fP to include a file or files. This file's location is relative to the diff --git a/man/coredns-k8s_external.7 b/man/coredns-k8s_external.7 index f0b902e499..657620c736 100644 --- a/man/coredns-k8s_external.7 +++ b/man/coredns-k8s_external.7 @@ -1,19 +1,18 @@ .\" Generated by Mmark Markdown Processer - mmark.miek.nl -.TH "COREDNS-K8S_EXTERNAL" 7 "March 2021" "CoreDNS" "CoreDNS Plugins" +.TH "COREDNS-K8S_EXTERNAL" 7 "March 2026" "CoreDNS" "CoreDNS Plugins" .SH "NAME" .PP -\fIk8s_external\fP - resolves load balancer and external IPs from outside Kubernetes clusters. +\fIk8s_external\fP - resolves load balancer, external IPs from outside Kubernetes clusters and if enabled headless services. .SH "DESCRIPTION" .PP This plugin allows an additional zone to resolve the external IP address(es) of a Kubernetes -service. This plugin is only useful if the \fIkubernetes\fP plugin is also loaded. +service and headless services. This plugin is only useful if the \fIkubernetes\fP plugin is also loaded. .PP 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 -SOA and NS queries for the apex of the zone. +AAAA, SRV, and PTR records; To make it a proper DNS zone, it handles SOA and NS queries for the apex of the zone. .PP By default the apex of the zone will look like the following (assuming the zone used is \fB\fCexample.org\fR): @@ -82,6 +81,38 @@ k8s\_external [ZONE...] { \fB\fCttl\fR allows you to set a custom \fBTTL\fP for responses. The default is 5 (seconds). +.PP +If you want to enable headless service resolution, you can do so by adding \fB\fCheadless\fR option. + +.PP +.RS + +.nf +k8s\_external [ZONE...] { + headless +} + +.fi +.RE + +.IP \(bu 4 +if there is a headless service with external IPs set, external IPs will be resolved + + +.PP +If the queried domain does not exist, you can fall through to next plugin by adding the \fB\fCfallthrough\fR option. + +.PP +.RS + +.nf +k8s\_external [ZONE...] { + fallthrough [ZONE...] +} + +.fi +.RE + .SH "EXAMPLES" .PP Enable names under \fB\fCexample.org\fR to be resolved to in-cluster DNS addresses. @@ -119,12 +150,46 @@ spec: .fi .RE +.PP +The \fIk8s_external\fP plugin can be used in conjunction with the \fItransfer\fP plugin to enable +zone transfers. Notifies are not supported. + +.PP +.RS + +.nf + . { + transfer example.org { + to * + } + kubernetes cluster.local + k8s\_external example.org + } + +.fi +.RE + +.PP +With the \fB\fCfallthrough\fR option, if the queried domain does not exist, it will be passed to the next plugin that matches the zone. + +.PP +.RS + +.nf +\&. { + kubernetes cluster.local + k8s\_external example.org { + fallthrough + } + forward . 8.8.8.8 +} + +.fi +.RE + .PP For some background see resolve external IP address \[la]https://github.com/kubernetes/dns/issues/242\[ra]. And A records for services with Load Balancer IP \[la]https://github.com/coredns/coredns/issues/1851\[ra]. -.PP -PTR queries for the reverse zone is not supported. - diff --git a/man/coredns-kubernetes.7 b/man/coredns-kubernetes.7 index 078c73d604..06ce806a19 100644 --- a/man/coredns-kubernetes.7 +++ b/man/coredns-kubernetes.7 @@ -1,5 +1,5 @@ .\" Generated by Mmark Markdown Processer - mmark.miek.nl -.TH "COREDNS-KUBERNETES" 7 "March 2021" "CoreDNS" "CoreDNS Plugins" +.TH "COREDNS-KUBERNETES" 7 "March 2026" "CoreDNS" "CoreDNS Plugins" .SH "NAME" .PP @@ -50,6 +50,9 @@ kubernetes [ZONES...] { endpoint URL tls CERT KEY CACERT kubeconfig KUBECONFIG [CONTEXT] + apiserver\_qps QPS + apiserver\_burst BURST + apiserver\_max\_inflight MAX namespaces NAMESPACE... labels EXPRESSION pods POD\-MODE @@ -58,6 +61,8 @@ kubernetes [ZONES...] { noendpoints fallthrough [ZONES...] ignore empty\_service + multicluster [ZONES...] + startup\_timeout DURATION } .fi @@ -65,7 +70,8 @@ kubernetes [ZONES...] { .IP \(bu 4 \fB\fCendpoint\fR specifies the \fBURL\fP for a remote k8s API endpoint. -If omitted, it will connect to k8s in-cluster using the cluster service account. +If omitted, it will connect to k8s in-cluster using the cluster service account. Needs \fB\fCtls\fR for clusters with authentication. +This option is ignored if \fB\fCkubeconfig\fR is set. .IP \(bu 4 \fB\fCtls\fR \fBCERT\fP \fBKEY\fP \fBCACERT\fP are the TLS cert, key and the CA cert file names for remote k8s connection. This option is ignored if connecting in-cluster (i.e. endpoint is not specified). @@ -73,7 +79,16 @@ This option is ignored if connecting in-cluster (i.e. endpoint is not specified) \fB\fCkubeconfig\fR \fBKUBECONFIG [CONTEXT]\fP authenticates the connection to a remote k8s cluster using a kubeconfig file. \fB[CONTEXT]\fP is optional, if not set, then the current context specified in kubeconfig will be used. It supports TLS, username and password, or token-based authentication. -This option is ignored if connecting in-cluster (i.e., the endpoint is not specified). +This option is ignored if omitted. The cluster address in the \fB\fCkubeconfig\fR is given preference. +.IP \(bu 4 +\fB\fCapiserver_qps\fR \fBQPS\fP sets the maximum queries per second (QPS) rate limit for requests. +This allows you to control the rate at which the plugin sends requests to the API server to prevent overwhelming it. +.IP \(bu 4 +\fB\fCapiserver_burst\fR \fBBURST\fP sets the maximum burst size for requests. +This allows temporary spikes in request rate up to this value, even if it exceeds the QPS limit. +.IP \(bu 4 +\fB\fCapiserver_max_inflight\fR \fBMAX\fP sets the maximum number of concurrent in-flight requests. +This caps the total number of simultaneous requests the plugin can make to the API server. .IP \(bu 4 \fB\fCnamespaces\fR \fBNAMESPACE [NAMESPACE...]\fP only exposes the k8s namespaces listed. If this option is omitted all namespaces are exposed @@ -120,7 +135,7 @@ IP address (e.g., \fB\fC1-2-3-4.my-service.namespace.svc.cluster.local.\fR) If this directive is included, then name selection for endpoints changes as follows: Use the hostname of the endpoint, or if hostname is not set, use the pod name of the pod targeted by the endpoint. If there is no pod targeted by -the endpoint, use the dashed IP address form. +the endpoint or pod name is longer than 63, use the dashed IP address form. .IP \(bu 4 \fB\fCttl\fR allows you to set a custom TTL for responses. The default is 5 seconds. The minimum TTL allowed is 0 seconds, and the maximum is capped at 3600 seconds. Setting TTL to 0 will prevent records from being cached. @@ -138,16 +153,41 @@ queries for those zones will be subject to fallthrough. \fB\fCignore empty_service\fR returns NXDOMAIN for services without any ready endpoint addresses (e.g., ready pods). This allows the querying pod to continue searching for the service in the search path. The search path could, for example, include another Kubernetes cluster. +.IP \(bu 4 +\fB\fCmulticluster\fR defines the multicluster zones as defined by Multi-Cluster +Services API (MCS-API). Specifying this option is generally paired with the +installation of an MCS-API implementation and the ServiceImport and ServiceExport +CRDs. The plugin MUST be authoritative for the zones listed here. +.IP \(bu 4 +\fB\fCstartup_timeout\fR specifies the \fBDURATION\fP value that limits the time to wait for informer cache synced +when the kubernetes plugin starts. If not specified, the default timeout will be 5s. .PP Enabling zone transfer is done by using the \fItransfer\fP plugin. +.SH "STARTUP" +.PP +When CoreDNS starts with the \fIkubernetes\fP plugin enabled, it will delay serving DNS for up to 5 seconds +until it can connect to the Kubernetes API and synchronize all object watches. If this cannot happen within +5 seconds, then CoreDNS will start serving DNS while the \fIkubernetes\fP plugin continues to try to connect +and synchronize all object watches. CoreDNS will answer SERVFAIL to any request made for a Kubernetes record +that has not yet been synchronized. You can also determine how long to wait by specifying \fB\fCstartup_timeout\fR. + +.SH "MONITORING KUBERNETES ENDPOINTS" +.PP +The \fIkubernetes\fP plugin watches Endpoints via the \fB\fCdiscovery.EndpointSlices\fR API. + .SH "READY" .PP This plugin reports readiness to the ready plugin. This will happen after it has synced to the Kubernetes API. +.SH "PTR RECORDS" +.PP +This plugin creates PTR records for every Pod selected by a Service. If a given Pod is selected by more than +one Service a separate PTR record will exist for each Service selecting it. + .SH "EXAMPLES" .PP Handle all queries in the \fB\fCcluster.local\fR zone. Connect to Kubernetes in-cluster. Also handle all @@ -196,6 +236,20 @@ kubernetes cluster.local { .fi .RE +.PP +Configure multicluster + +.PP +.RS + +.nf +kubernetes cluster.local clusterset.local { + multicluster clusterset.local +} + +.fi +.RE + .SH "STUBDOMAINS AND UPSTREAMNAMESERVERS" .PP Here we use the \fIforward\fP plugin to implement a stubDomain that forwards \fB\fCexample.local\fR to the nameserver \fB\fC10.100.0.10:53\fR. @@ -256,37 +310,6 @@ cluster.local { .fi .RE -.SH "WILDCARDS" -.PP -Some query labels accept a wildcard value to match any value. If a label is a valid wildcard (*, -or the word "any"), then that label will match all values. The labels that accept wildcards are: - -.IP \(bu 4 -\fIendpoint\fP in an \fB\fCA\fR record request: \fIendpoint\fP.service.namespace.svc.zone, e.g., \fB\fC*.nginx.ns.svc.cluster.local\fR -.IP \(bu 4 -\fIservice\fP in an \fB\fCA\fR record request: \fIservice\fP.namespace.svc.zone, e.g., \fB\fC*.ns.svc.cluster.local\fR -.IP \(bu 4 -\fInamespace\fP in an \fB\fCA\fR record request: service.\fInamespace\fP.svc.zone, e.g., \fB\fCnginx.*.svc.cluster.local\fR -.IP \(bu 4 -\fIport and/or protocol\fP in an \fB\fCSRV\fR request: \fBport_.\fPprotocol_.service.namespace.svc.zone., -e.g., \fB\fC_http.*.service.ns.svc.cluster.local\fR -.IP \(bu 4 -multiple wildcards are allowed in a single query, e.g., \fB\fCA\fR Request \fB\fC*.*.svc.zone.\fR or \fB\fCSRV\fR request \fB\fC*.*.*.*.svc.zone.\fR - - -.PP -For example, wildcards can be used to resolve all Endpoints for a Service as \fB\fCA\fR records. e.g.: \fB\fC*.service.ns.svc.myzone.local\fR will return the Endpoint IPs in the Service \fB\fCservice\fR in namespace \fB\fCdefault\fR: - -.PP -.RS - -.nf -*.service.default.svc.cluster.local. 5 IN A 192.168.10.10 -*.service.default.svc.cluster.local. 5 IN A 192.168.25.15 - -.fi -.RE - .SH "METADATA" .PP The kubernetes plugin will publish the following metadata, if the \fImetadata\fP @@ -308,11 +331,14 @@ plugin is also enabled: \fB\fCkubernetes/client-namespace\fR: the client pod's namespace (see requirements below) .IP \(bu 4 \fB\fCkubernetes/client-pod-name\fR: the client pod's name (see requirements below) +.IP \(bu 4 +\fB\fCkubernetes/client-label/