diff --git a/.github/SECURITY.md b/.github/SECURITY.md index 557b4bac1..6f13031b9 100644 --- a/.github/SECURITY.md +++ b/.github/SECURITY.md @@ -48,9 +48,9 @@ We consider publicly-registered domain names to be public information. This nece It will speed things up if you suggest a working patch, such as a code diff, and explain why and how it works. Reports that are not actionable, do not contain enough information, are too pushy/demanding, or are not able to convince us that it is a viable and practical attack on the web server itself may be deferred to a later time or possibly ignored, depending on available resources. Priority will be given to credible, responsible reports that are constructive, specific, and actionable. (We get a lot of invalid reports.) Thank you for understanding. -When you are ready, please email Matt Holt (the author) directly: matt at dyanim dot com. +When you are ready, please submit a [new private vulnerability report](https://github.com/caddyserver/caddy/security/advisories/new). -Please don't encrypt the email body. It only makes the process more complicated. +Please don't encrypt the message. It only makes the process more complicated. Please also understand that due to our nature as an open source project, we do not have a budget to award security bounties. We can only thank you. diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 64284b907..aeee0fd93 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,3 +5,8 @@ updates: directory: "/" schedule: interval: "monthly" + + - package-ecosystem: gomod + directory: / + schedule: + interval: weekly diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5f3e98db6..03e931830 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,6 +16,9 @@ env: # https://github.com/actions/setup-go/issues/491 GOTOOLCHAIN: local +permissions: + contents: read + jobs: test: strategy: @@ -55,13 +58,21 @@ jobs: SUCCESS: 'True' runs-on: ${{ matrix.OS_LABEL }} - + permissions: + contents: read + pull-requests: read + actions: write # to allow uploading artifacts and cache steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1 + with: + egress-policy: audit + - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Install Go - uses: actions/setup-go@v5 + uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 with: go-version: ${{ matrix.GO_SEMVER }} check-latest: true @@ -108,7 +119,7 @@ jobs: ./caddy stop - name: Publish Build Artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: caddy_${{ runner.os }}_go${{ matrix.go }}_${{ steps.vars.outputs.short_sha }} path: ${{ matrix.CADDY_BIN_PATH }} @@ -142,12 +153,21 @@ jobs: s390x-test: name: test (s390x on IBM Z) + permissions: + contents: read + pull-requests: read runs-on: ubuntu-latest if: github.event.pull_request.head.repo.full_name == 'caddyserver/caddy' && github.actor != 'dependabot[bot]' continue-on-error: true # August 2020: s390x VM is down due to weather and power issues steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1 + with: + egress-policy: audit + allowed-endpoints: ci-s390x.caddyserver.com:22 + - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Run Tests run: | set +e @@ -194,17 +214,25 @@ jobs: goreleaser-check: runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: read if: github.event.pull_request.head.repo.full_name == 'caddyserver/caddy' && github.actor != 'dependabot[bot]' steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1 + with: + egress-policy: audit + - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - uses: goreleaser/goreleaser-action@v6 + - uses: goreleaser/goreleaser-action@9c156ee8a17a598857849441385a2041ef570552 # v6.3.0 with: version: latest args: check - name: Install Go - uses: actions/setup-go@v5 + uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 with: go-version: "~1.24" check-latest: true @@ -212,7 +240,7 @@ jobs: run: | go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest xcaddy version - - uses: goreleaser/goreleaser-action@v6 + - uses: goreleaser/goreleaser-action@9c156ee8a17a598857849441385a2041ef570552 # v6.3.0 with: version: latest args: build --single-target --snapshot diff --git a/.github/workflows/cross-build.yml b/.github/workflows/cross-build.yml index 372cc7652..fe6b44c68 100644 --- a/.github/workflows/cross-build.yml +++ b/.github/workflows/cross-build.yml @@ -14,6 +14,9 @@ env: # https://github.com/actions/setup-go/issues/491 GOTOOLCHAIN: local +permissions: + contents: read + jobs: build: strategy: @@ -40,13 +43,21 @@ jobs: GO_SEMVER: '~1.24.1' runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: read continue-on-error: true steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1 + with: + egress-policy: audit + - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Install Go - uses: actions/setup-go@v5 + uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 with: go-version: ${{ matrix.GO_SEMVER }} check-latest: true diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index c5c89b502..7531d6a8e 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -44,14 +44,19 @@ jobs: runs-on: ${{ matrix.OS_LABEL }} steps: - - uses: actions/checkout@v4 - - uses: actions/setup-go@v5 + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1 + with: + egress-policy: audit + + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 with: go-version: '~1.24' check-latest: true - name: golangci-lint - uses: golangci/golangci-lint-action@v6 + uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 # v8.0.0 with: version: latest @@ -62,10 +67,39 @@ jobs: # only-new-issues: true govulncheck: + permissions: + contents: read + pull-requests: read runs-on: ubuntu-latest steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1 + with: + egress-policy: audit + - name: govulncheck - uses: golang/govulncheck-action@v1 + uses: golang/govulncheck-action@b625fbe08f3bccbe446d94fbf87fcc875a4f50ee # v1.0.4 with: go-version-input: '~1.24.1' check-latest: true + + dependency-review: + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1 + with: + egress-policy: audit + + - name: 'Checkout Repository' + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: 'Dependency Review' + uses: actions/dependency-review-action@da24556b548a50705dd671f47852072ea4c105d9 # v4.7.1 + with: + comment-summary-in-pr: on-failure + # https://github.com/actions/dependency-review-action/issues/430#issuecomment-1468975566 + base-ref: ${{ github.event.pull_request.base.sha || 'master' }} + head-ref: ${{ github.event.pull_request.head.sha || github.ref }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b508ba468..31c60df75 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,6 +9,9 @@ env: # https://github.com/actions/setup-go/issues/491 GOTOOLCHAIN: local +permissions: + contents: read + jobs: release: name: Release @@ -35,19 +38,24 @@ jobs: contents: write steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1 + with: + egress-policy: audit + - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: fetch-depth: 0 - name: Install Go - uses: actions/setup-go@v5 + uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 with: go-version: ${{ matrix.GO_SEMVER }} check-latest: true # Force fetch upstream tags -- because 65 minutes - # tl;dr: actions/checkout@v4 runs this line: + # tl;dr: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 runs this line: # git -c protocol.version=2 fetch --no-tags --prune --progress --no-recurse-submodules --depth=1 origin +ebc278ec98bb24f2852b61fde2a9bf2e3d83818b:refs/tags/ # which makes its own local lightweight tag, losing all the annotations in the process. Our earlier script ran: # git fetch --prune --unshallow @@ -101,11 +109,11 @@ jobs: git verify-tag "${{ steps.vars.outputs.version_tag }}" || exit 1 - name: Install Cosign - uses: sigstore/cosign-installer@main + uses: sigstore/cosign-installer@e9a05e6d32d7ed22b5656cd874ef31af58d05bfa # main - name: Cosign version run: cosign version - name: Install Syft - uses: anchore/sbom-action/download-syft@main + uses: anchore/sbom-action/download-syft@9246b90769f852b3a8921f330c59e0b3f439d6e9 # main - name: Syft version run: syft version - name: Install xcaddy @@ -114,7 +122,7 @@ jobs: xcaddy version # GoReleaser will take care of publishing those artifacts into the release - name: Run GoReleaser - uses: goreleaser/goreleaser-action@v6 + uses: goreleaser/goreleaser-action@9c156ee8a17a598857849441385a2041ef570552 # v6.3.0 with: version: latest args: release --clean --timeout 60m diff --git a/.github/workflows/release_published.yml b/.github/workflows/release_published.yml index 491dae75d..e33cd8a96 100644 --- a/.github/workflows/release_published.yml +++ b/.github/workflows/release_published.yml @@ -5,6 +5,9 @@ on: release: types: [published] +permissions: + contents: read + jobs: release: name: Release Published @@ -13,12 +16,20 @@ jobs: os: - ubuntu-latest runs-on: ${{ matrix.os }} - + permissions: + contents: read + pull-requests: read + actions: write steps: # See https://github.com/peter-evans/repository-dispatch + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1 + with: + egress-policy: audit + - name: Trigger event on caddyserver/dist - uses: peter-evans/repository-dispatch@v3 + uses: peter-evans/repository-dispatch@ff45666b9427631e3450c54a1bcbee4d9ff4d7c0 # v3.0.0 with: token: ${{ secrets.REPO_DISPATCH_TOKEN }} repository: caddyserver/dist @@ -26,7 +37,7 @@ jobs: client-payload: '{"tag": "${{ github.event.release.tag_name }}"}' - name: Trigger event on caddyserver/caddy-docker - uses: peter-evans/repository-dispatch@v3 + uses: peter-evans/repository-dispatch@ff45666b9427631e3450c54a1bcbee4d9ff4d7c0 # v3.0.0 with: token: ${{ secrets.REPO_DISPATCH_TOKEN }} repository: caddyserver/caddy-docker diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml new file mode 100644 index 000000000..fad8621d2 --- /dev/null +++ b/.github/workflows/scorecard.yml @@ -0,0 +1,86 @@ +# This workflow uses actions that are not certified by GitHub. They are provided +# by a third-party and are governed by separate terms of service, privacy +# policy, and support documentation. + +name: OpenSSF Scorecard supply-chain security +on: + # For Branch-Protection check. Only the default branch is supported. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection + branch_protection_rule: + # To guarantee Maintained check is occasionally updated. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained + schedule: + - cron: '20 2 * * 5' + push: + branches: [ "master", "2.*" ] + pull_request: + branches: [ "master", "2.*" ] + + +# Declare default permissions as read only. +permissions: read-all + +jobs: + analysis: + name: Scorecard analysis + runs-on: ubuntu-latest + # `publish_results: true` only works when run from the default branch. conditional can be removed if disabled. + if: github.event.repository.default_branch == github.ref_name || github.event_name == 'pull_request' + permissions: + # Needed to upload the results to code-scanning dashboard. + security-events: write + # Needed to publish results and get a badge (see publish_results below). + id-token: write + # Uncomment the permissions below if installing in a private repository. + # contents: read + # actions: read + + steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1 + with: + egress-policy: audit + + - name: "Checkout code" + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false + + - name: "Run analysis" + uses: ossf/scorecard-action@f49aabe0b5af0936a0987cfb85d86b75731b0186 # v2.4.1 + with: + results_file: results.sarif + results_format: sarif + # (Optional) "write" PAT token. Uncomment the `repo_token` line below if: + # - you want to enable the Branch-Protection check on a *public* repository, or + # - you are installing Scorecard on a *private* repository + # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action?tab=readme-ov-file#authentication-with-fine-grained-pat-optional. + # repo_token: ${{ secrets.SCORECARD_TOKEN }} + + # Public repositories: + # - Publish results to OpenSSF REST API for easy access by consumers + # - Allows the repository to include the Scorecard badge. + # - See https://github.com/ossf/scorecard-action#publishing-results. + # For private repositories: + # - `publish_results` will always be set to `false`, regardless + # of the value entered here. + publish_results: true + + # (Optional) Uncomment file_mode if you have a .gitattributes with files marked export-ignore + # file_mode: git + + # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF + # format to the repository Actions tab. + - name: "Upload artifact" + uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1 + with: + name: SARIF file + path: results.sarif + retention-days: 5 + + # Upload the results to GitHub's code scanning dashboard (optional). + # Commenting out will disable upload of results to your repo's Code Scanning dashboard + - name: "Upload to code-scanning" + uses: github/codeql-action/upload-sarif@ce28f5bb42b7a9f2c824e633a3f6ee835bab6858 # v3.29.0 + with: + sarif_file: results.sarif diff --git a/.golangci.yml b/.golangci.yml index aecff563e..0f1082e72 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,27 +1,15 @@ -linters-settings: - errcheck: - exclude-functions: - - fmt.* - - (go.uber.org/zap/zapcore.ObjectEncoder).AddObject - - (go.uber.org/zap/zapcore.ObjectEncoder).AddArray - gci: - sections: - - standard # Standard section: captures all standard packages. - - default # Default section: contains all imports that could not be matched to another section type. - - prefix(github.com/caddyserver/caddy/v2/cmd) # ensure that this is always at the top and always has a line break. - - prefix(github.com/caddyserver/caddy) # Custom section: groups all imports with the specified Prefix. - # Skip generated files. - # Default: true - skip-generated: true - # Enable custom order of sections. - # If `true`, make the section order the same as the order of `sections`. - # Default: false - custom-order: true - exhaustive: - ignore-enum-types: reflect.Kind|svc.Cmd - +version: "2" +run: + issues-exit-code: 1 + tests: false +output: + formats: + text: + path: stdout + print-linter-name: true + print-issued-lines: true linters: - disable-all: true + default: none enable: - asasalint - asciicheck @@ -35,148 +23,96 @@ linters: - errcheck - errname - exhaustive - - gci - - gofmt - - goimports - - gofumpt - gosec - - gosimple - govet - - ineffassign - importas + - ineffassign - misspell - prealloc - promlinter - sloglint - sqlclosecheck - staticcheck - - tenv - testableexamples - testifylint - tparallel - - typecheck - unconvert - unused - wastedassign - whitespace - zerologlint - # these are implicitly disabled: - # - containedctx - # - contextcheck - # - cyclop - # - depguard - # - errchkjson - # - errorlint - # - exhaustruct - # - execinquery - # - exhaustruct - # - forbidigo - # - forcetypeassert - # - funlen - # - ginkgolinter - # - gocheckcompilerdirectives - # - gochecknoglobals - # - gochecknoinits - # - gochecksumtype - # - gocognit - # - goconst - # - gocritic - # - gocyclo - # - godot - # - godox - # - goerr113 - # - goheader - # - gomnd - # - gomoddirectives - # - gomodguard - # - goprintffuncname - # - gosmopolitan - # - grouper - # - inamedparam - # - interfacebloat - # - ireturn - # - lll - # - loggercheck - # - maintidx - # - makezero - # - mirror - # - musttag - # - nakedret - # - nestif - # - nilerr - # - nilnil - # - nlreturn - # - noctx - # - nolintlint - # - nonamedreturns - # - nosprintfhostport - # - paralleltest - # - perfsprint - # - predeclared - # - protogetter - # - reassign - # - revive - # - rowserrcheck - # - stylecheck - # - tagalign - # - tagliatelle - # - testpackage - # - thelper - # - unparam - # - usestdlibvars - # - varnamelen - # - wrapcheck - # - wsl - -run: - # default concurrency is a available CPU number. - # concurrency: 4 # explicitly omit this value to fully utilize available resources. - timeout: 5m - issues-exit-code: 1 - tests: false - -# output configuration options -output: - formats: - - format: 'colored-line-number' - print-issued-lines: true - print-linter-name: true - -issues: - exclude-rules: - - text: 'G115' # TODO: Either we should fix the issues or nuke the linter if it's bad - linters: - - gosec - # we aren't calling unknown URL - - text: 'G107' # G107: Url provided to HTTP request as taint input - linters: - - gosec - # as a web server that's expected to handle any template, this is totally in the hands of the user. - - text: 'G203' # G203: Use of unescaped data in HTML templates - linters: - - gosec - # we're shelling out to known commands, not relying on user-defined input. - - text: 'G204' # G204: Audit use of command execution - linters: - - gosec - # the choice of weakrand is deliberate, hence the named import "weakrand" - - path: modules/caddyhttp/reverseproxy/selectionpolicies.go - text: 'G404' # G404: Insecure random number source (rand) - linters: - - gosec - - path: modules/caddyhttp/reverseproxy/streaming.go - text: 'G404' # G404: Insecure random number source (rand) - linters: - - gosec - - path: modules/logging/filters.go - linters: - - dupl - - path: modules/caddyhttp/matchers.go - linters: - - dupl - - path: modules/caddyhttp/vars.go - linters: - - dupl - - path: _test\.go - linters: - - errcheck + settings: + staticcheck: + checks: ["all", "-ST1000", "-ST1003", "-ST1016", "-ST1020", "-ST1021", "-ST1022", "-QF1006", "-QF1008"] # default, and exclude 1 more undesired check + errcheck: + exclude-functions: + - fmt.* + - (go.uber.org/zap/zapcore.ObjectEncoder).AddObject + - (go.uber.org/zap/zapcore.ObjectEncoder).AddArray + exhaustive: + ignore-enum-types: reflect.Kind|svc.Cmd + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling + rules: + - linters: + - gosec + text: G115 # TODO: Either we should fix the issues or nuke the linter if it's bad + - linters: + - gosec + text: G107 # we aren't calling unknown URL + - linters: + - gosec + text: G203 # as a web server that's expected to handle any template, this is totally in the hands of the user. + - linters: + - gosec + text: G204 # we're shelling out to known commands, not relying on user-defined input. + - linters: + - gosec + # the choice of weakrand is deliberate, hence the named import "weakrand" + path: modules/caddyhttp/reverseproxy/selectionpolicies.go + text: G404 + - linters: + - gosec + path: modules/caddyhttp/reverseproxy/streaming.go + text: G404 + - linters: + - dupl + path: modules/logging/filters.go + - linters: + - dupl + path: modules/caddyhttp/matchers.go + - linters: + - dupl + path: modules/caddyhttp/vars.go + - linters: + - errcheck + path: _test\.go + paths: + - third_party$ + - builtin$ + - examples$ +formatters: + enable: + - gci + - gofmt + - gofumpt + - goimports + settings: + gci: + sections: + - standard # Standard section: captures all standard packages. + - default # Default section: contains all imports that could not be matched to another section type. + - prefix(github.com/caddyserver/caddy/v2/cmd) # ensure that this is always at the top and always has a line break. + - prefix(github.com/caddyserver/caddy) # Custom section: groups all imports with the specified Prefix. + custom-order: true + exclusions: + generated: lax + paths: + - third_party$ + - builtin$ + - examples$ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000..a38a13da3 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,20 @@ +repos: +- repo: https://github.com/gitleaks/gitleaks + rev: v8.16.3 + hooks: + - id: gitleaks +- repo: https://github.com/golangci/golangci-lint + rev: v1.52.2 + hooks: + - id: golangci-lint-config-verify + - id: golangci-lint + - id: golangci-lint-fmt +- repo: https://github.com/jumanjihouse/pre-commit-hooks + rev: 3.0.0 + hooks: + - id: shellcheck +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.4.0 + hooks: + - id: end-of-file-fixer + - id: trailing-whitespace diff --git a/README.md b/README.md index 4bebaafdb..e5d5a83d6 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@

Caddy is an extensible server platform that uses TLS by default.

+
@caddyserver on Twitter diff --git a/caddy.go b/caddy.go index 1a6a11629..012480d11 100644 --- a/caddy.go +++ b/caddy.go @@ -408,11 +408,23 @@ func run(newCfg *Config, start bool) (Context, error) { return ctx, nil } + defer func() { + // if newCfg fails to start completely, clean up the already provisioned modules + // partially copied from provisionContext + if err != nil { + globalMetrics.configSuccess.Set(0) + ctx.cfg.cancelFunc() + + if currentCtx.cfg != nil { + certmagic.Default.Storage = currentCtx.cfg.storage + } + } + }() + // Provision any admin routers which may need to access // some of the other apps at runtime err = ctx.cfg.Admin.provisionAdminRouters(ctx) if err != nil { - globalMetrics.configSuccess.Set(0) return ctx, err } @@ -438,7 +450,6 @@ func run(newCfg *Config, start bool) (Context, error) { return nil }() if err != nil { - globalMetrics.configSuccess.Set(0) return ctx, err } globalMetrics.configSuccess.Set(1) @@ -449,7 +460,8 @@ func run(newCfg *Config, start bool) (Context, error) { // now that the user's config is running, finish setting up anything else, // such as remote admin endpoint, config loader, etc. - return ctx, finishSettingUp(ctx, ctx.cfg) + err = finishSettingUp(ctx, ctx.cfg) + return ctx, err } // provisionContext creates a new context from the given configuration and provisions @@ -1104,9 +1116,15 @@ func (e Event) Origin() Module { return e.origin } // Returns the module t // CloudEvents spec. func (e Event) CloudEvent() CloudEvent { dataJSON, _ := json.Marshal(e.Data) + var source string + if e.Origin() == nil { + source = "caddy" + } else { + source = string(e.Origin().CaddyModule().ID) + } return CloudEvent{ ID: e.id.String(), - Source: e.origin.CaddyModule().String(), + Source: source, SpecVersion: "1.0", Type: e.name, Time: e.ts, diff --git a/caddy_test.go b/caddy_test.go index adf14350e..08fa5c0d0 100644 --- a/caddy_test.go +++ b/caddy_test.go @@ -15,6 +15,7 @@ package caddy import ( + "context" "testing" "time" ) @@ -72,3 +73,21 @@ func TestParseDuration(t *testing.T) { } } } + +func TestEvent_CloudEvent_NilOrigin(t *testing.T) { + ctx, _ := NewContext(Context{Context: context.Background()}) // module will be nil by default + event, err := NewEvent(ctx, "started", nil) + if err != nil { + t.Fatalf("NewEvent() error = %v", err) + } + + // This should not panic + ce := event.CloudEvent() + + if ce.Source != "caddy" { + t.Errorf("Expected CloudEvent Source to be 'caddy', got '%s'", ce.Source) + } + if ce.Type != "started" { + t.Errorf("Expected CloudEvent Type to be 'started', got '%s'", ce.Type) + } +} diff --git a/caddyconfig/caddyfile/adapter.go b/caddyconfig/caddyfile/adapter.go index da4f98337..449370dc6 100644 --- a/caddyconfig/caddyfile/adapter.go +++ b/caddyconfig/caddyfile/adapter.go @@ -68,7 +68,7 @@ func (a Adapter) Adapt(body []byte, options map[string]any) ([]byte, []caddyconf // TODO: also perform this check on imported files func FormattingDifference(filename string, body []byte) (caddyconfig.Warning, bool) { // replace windows-style newlines to normalize comparison - normalizedBody := bytes.Replace(body, []byte("\r\n"), []byte("\n"), -1) + normalizedBody := bytes.ReplaceAll(body, []byte("\r\n"), []byte("\n")) formatted := Format(normalizedBody) if bytes.Equal(formatted, normalizedBody) { diff --git a/caddyconfig/caddyfile/formatter.go b/caddyconfig/caddyfile/formatter.go index f1d12fa51..0476a9b93 100644 --- a/caddyconfig/caddyfile/formatter.go +++ b/caddyconfig/caddyfile/formatter.go @@ -94,7 +94,7 @@ func Format(input []byte) []byte { } // detect whether we have the start of a heredoc - if !quoted && !(heredoc != heredocClosed || heredocEscaped) && + if !quoted && (heredoc == heredocClosed && !heredocEscaped) && space && last == '<' && ch == '<' { write(ch) heredoc = heredocOpening diff --git a/caddyconfig/caddyfile/lexer.go b/caddyconfig/caddyfile/lexer.go index 9b523f397..a3b1fc7db 100644 --- a/caddyconfig/caddyfile/lexer.go +++ b/caddyconfig/caddyfile/lexer.go @@ -137,7 +137,7 @@ func (l *lexer) next() (bool, error) { } // detect whether we have the start of a heredoc - if !(quoted || btQuoted) && !(inHeredoc || heredocEscaped) && + if (!quoted && !btQuoted) && (!inHeredoc && !heredocEscaped) && len(val) > 1 && string(val[:2]) == "<<" { // a space means it's just a regular token and not a heredoc if ch == ' ' { diff --git a/caddyconfig/httpcaddyfile/builtins.go b/caddyconfig/httpcaddyfile/builtins.go index 3fc08b2c8..fb05d29f1 100644 --- a/caddyconfig/httpcaddyfile/builtins.go +++ b/caddyconfig/httpcaddyfile/builtins.go @@ -15,6 +15,7 @@ package httpcaddyfile import ( + "encoding/json" "fmt" "html" "net/http" @@ -843,13 +844,18 @@ func parseHandleErrors(h Helper) ([]ConfigValue, error) { return nil, h.Errf("segment was not parsed as a subroute") } + // wrap the subroutes + wrappingRoute := caddyhttp.Route{ + HandlersRaw: []json.RawMessage{caddyconfig.JSONModuleObject(subroute, "handler", "subroute", nil)}, + } + subroute = &caddyhttp.Subroute{ + Routes: []caddyhttp.Route{wrappingRoute}, + } if expression != "" { statusMatcher := caddy.ModuleMap{ "expression": h.JSON(caddyhttp.MatchExpression{Expr: expression}), } - for i := range subroute.Routes { - subroute.Routes[i].MatcherSetsRaw = []caddy.ModuleMap{statusMatcher} - } + subroute.Routes[0].MatcherSetsRaw = []caddy.ModuleMap{statusMatcher} } return []ConfigValue{ { @@ -1160,6 +1166,11 @@ func parseLogSkip(h Helper) (caddyhttp.MiddlewareHandler, error) { if h.NextArg() { return nil, h.ArgErr() } + + if h.NextBlock(0) { + return nil, h.Err("log_skip directive does not accept blocks") + } + return caddyhttp.VarsMiddleware{"log_skip": true}, nil } diff --git a/caddyconfig/httpcaddyfile/directives.go b/caddyconfig/httpcaddyfile/directives.go index 3fe27bfff..eac7f5dc2 100644 --- a/caddyconfig/httpcaddyfile/directives.go +++ b/caddyconfig/httpcaddyfile/directives.go @@ -174,10 +174,12 @@ func RegisterDirectiveOrder(dir string, position Positional, standardDir string) if d != standardDir { continue } - if position == Before { + switch position { + case Before: newOrder = append(newOrder[:i], append([]string{dir}, newOrder[i:]...)...) - } else if position == After { + case After: newOrder = append(newOrder[:i+1], append([]string{dir}, newOrder[i+1:]...)...) + case First, Last: } break } diff --git a/caddytest/caddytest.go b/caddytest/caddytest.go index 623c45e5e..7b56bb281 100644 --- a/caddytest/caddytest.go +++ b/caddytest/caddytest.go @@ -281,7 +281,7 @@ func validateTestPrerequisites(tc *Tester) error { tc.t.Cleanup(func() { os.Remove(f.Name()) }) - if _, err := f.WriteString(fmt.Sprintf(initConfig, tc.config.AdminPort)); err != nil { + if _, err := fmt.Fprintf(f, initConfig, tc.config.AdminPort); err != nil { return err } diff --git a/caddytest/integration/acme_test.go b/caddytest/integration/acme_test.go index d7e4c296d..f10aef6a8 100644 --- a/caddytest/integration/acme_test.go +++ b/caddytest/integration/acme_test.go @@ -12,13 +12,14 @@ import ( "strings" "testing" - "github.com/caddyserver/caddy/v2" - "github.com/caddyserver/caddy/v2/caddytest" "github.com/mholt/acmez/v3" "github.com/mholt/acmez/v3/acme" smallstepacme "github.com/smallstep/certificates/acme" "go.uber.org/zap" "go.uber.org/zap/exp/zapslog" + + "github.com/caddyserver/caddy/v2" + "github.com/caddyserver/caddy/v2/caddytest" ) const acmeChallengePort = 9081 diff --git a/caddytest/integration/acmeserver_test.go b/caddytest/integration/acmeserver_test.go index ca5845f87..d6a9ba005 100644 --- a/caddytest/integration/acmeserver_test.go +++ b/caddytest/integration/acmeserver_test.go @@ -9,11 +9,12 @@ import ( "strings" "testing" - "github.com/caddyserver/caddy/v2/caddytest" "github.com/mholt/acmez/v3" "github.com/mholt/acmez/v3/acme" "go.uber.org/zap" "go.uber.org/zap/exp/zapslog" + + "github.com/caddyserver/caddy/v2/caddytest" ) func TestACMEServerDirectory(t *testing.T) { diff --git a/caddytest/integration/caddyfile_adapt/error_example.caddyfiletest b/caddytest/integration/caddyfile_adapt/error_example.caddyfiletest index bd42aee55..6f3059ab2 100644 --- a/caddytest/integration/caddyfile_adapt/error_example.caddyfiletest +++ b/caddytest/integration/caddyfile_adapt/error_example.caddyfiletest @@ -106,20 +106,29 @@ example.com { "handler": "subroute", "routes": [ { - "group": "group0", "handle": [ { - "handler": "rewrite", - "uri": "/{http.error.status_code}.html" - } - ] - }, - { - "handle": [ - { - "handler": "file_server", - "hide": [ - "./Caddyfile" + "handler": "subroute", + "routes": [ + { + "group": "group0", + "handle": [ + { + "handler": "rewrite", + "uri": "/{http.error.status_code}.html" + } + ] + }, + { + "handle": [ + { + "handler": "file_server", + "hide": [ + "./Caddyfile" + ] + } + ] + } ] } ] diff --git a/caddytest/integration/caddyfile_adapt/error_multi_site_blocks.caddyfiletest b/caddytest/integration/caddyfile_adapt/error_multi_site_blocks.caddyfiletest index 0e84a13c2..1bec4b3e8 100644 --- a/caddytest/integration/caddyfile_adapt/error_multi_site_blocks.caddyfiletest +++ b/caddytest/integration/caddyfile_adapt/error_multi_site_blocks.caddyfiletest @@ -165,8 +165,17 @@ bar.localhost { { "handle": [ { - "body": "404 or 410 error", - "handler": "static_response" + "handler": "subroute", + "routes": [ + { + "handle": [ + { + "body": "404 or 410 error", + "handler": "static_response" + } + ] + } + ] } ], "match": [ @@ -178,8 +187,17 @@ bar.localhost { { "handle": [ { - "body": "Error In range [500 .. 599]", - "handler": "static_response" + "handler": "subroute", + "routes": [ + { + "handle": [ + { + "body": "Error In range [500 .. 599]", + "handler": "static_response" + } + ] + } + ] } ], "match": [ @@ -208,8 +226,17 @@ bar.localhost { { "handle": [ { - "body": "404 or 410 error from second site", - "handler": "static_response" + "handler": "subroute", + "routes": [ + { + "handle": [ + { + "body": "404 or 410 error from second site", + "handler": "static_response" + } + ] + } + ] } ], "match": [ @@ -221,8 +248,17 @@ bar.localhost { { "handle": [ { - "body": "Error In range [500 .. 599] from second site", - "handler": "static_response" + "handler": "subroute", + "routes": [ + { + "handle": [ + { + "body": "Error In range [500 .. 599] from second site", + "handler": "static_response" + } + ] + } + ] } ], "match": [ diff --git a/caddytest/integration/caddyfile_adapt/error_range_codes.caddyfiletest b/caddytest/integration/caddyfile_adapt/error_range_codes.caddyfiletest index 46b70c8e3..299abc5f1 100644 --- a/caddytest/integration/caddyfile_adapt/error_range_codes.caddyfiletest +++ b/caddytest/integration/caddyfile_adapt/error_range_codes.caddyfiletest @@ -96,8 +96,17 @@ localhost:3010 { { "handle": [ { - "body": "Error in the [400 .. 499] range", - "handler": "static_response" + "handler": "subroute", + "routes": [ + { + "handle": [ + { + "body": "Error in the [400 .. 499] range", + "handler": "static_response" + } + ] + } + ] } ], "match": [ diff --git a/caddytest/integration/caddyfile_adapt/error_range_simple_codes.caddyfiletest b/caddytest/integration/caddyfile_adapt/error_range_simple_codes.caddyfiletest index 70158830c..9d4d2645c 100644 --- a/caddytest/integration/caddyfile_adapt/error_range_simple_codes.caddyfiletest +++ b/caddytest/integration/caddyfile_adapt/error_range_simple_codes.caddyfiletest @@ -116,8 +116,17 @@ localhost:2099 { { "handle": [ { - "body": "Error in the [400 .. 499] range", - "handler": "static_response" + "handler": "subroute", + "routes": [ + { + "handle": [ + { + "body": "Error in the [400 .. 499] range", + "handler": "static_response" + } + ] + } + ] } ], "match": [ @@ -129,8 +138,17 @@ localhost:2099 { { "handle": [ { - "body": "Error code is equal to 500 or in the [300..399] range", - "handler": "static_response" + "handler": "subroute", + "routes": [ + { + "handle": [ + { + "body": "Error code is equal to 500 or in the [300..399] range", + "handler": "static_response" + } + ] + } + ] } ], "match": [ diff --git a/caddytest/integration/caddyfile_adapt/error_simple_codes.caddyfiletest b/caddytest/integration/caddyfile_adapt/error_simple_codes.caddyfiletest index 5ac5863e3..29a51ffad 100644 --- a/caddytest/integration/caddyfile_adapt/error_simple_codes.caddyfiletest +++ b/caddytest/integration/caddyfile_adapt/error_simple_codes.caddyfiletest @@ -96,8 +96,17 @@ localhost:3010 { { "handle": [ { - "body": "404 or 410 error", - "handler": "static_response" + "handler": "subroute", + "routes": [ + { + "handle": [ + { + "body": "404 or 410 error", + "handler": "static_response" + } + ] + } + ] } ], "match": [ diff --git a/caddytest/integration/caddyfile_adapt/error_sort.caddyfiletest b/caddytest/integration/caddyfile_adapt/error_sort.caddyfiletest index 63701cccb..1faf9b3bd 100644 --- a/caddytest/integration/caddyfile_adapt/error_sort.caddyfiletest +++ b/caddytest/integration/caddyfile_adapt/error_sort.caddyfiletest @@ -116,8 +116,17 @@ localhost:2099 { { "handle": [ { - "body": "Error in the [400 .. 499] range", - "handler": "static_response" + "handler": "subroute", + "routes": [ + { + "handle": [ + { + "body": "Error in the [400 .. 499] range", + "handler": "static_response" + } + ] + } + ] } ], "match": [ @@ -129,8 +138,17 @@ localhost:2099 { { "handle": [ { - "body": "Fallback route: code outside the [400..499] range", - "handler": "static_response" + "handler": "subroute", + "routes": [ + { + "handle": [ + { + "body": "Fallback route: code outside the [400..499] range", + "handler": "static_response" + } + ] + } + ] } ] } diff --git a/caddytest/integration/caddyfile_adapt/error_subhandlers.caddyfiletest b/caddytest/integration/caddyfile_adapt/error_subhandlers.caddyfiletest new file mode 100644 index 000000000..54429a73e --- /dev/null +++ b/caddytest/integration/caddyfile_adapt/error_subhandlers.caddyfiletest @@ -0,0 +1,260 @@ +{ + http_port 2099 +} +localhost:2099 { + root * /var/www/ + file_server + + handle_errors 404 { + handle /en/* { + respond "not found" 404 + } + handle /es/* { + respond "no encontrado" + } + handle { + respond "default not found" + } + } + handle_errors { + handle /en/* { + respond "English error" + } + handle /es/* { + respond "Spanish error" + } + handle { + respond "Default error" + } + } +} +---------- +{ + "apps": { + "http": { + "http_port": 2099, + "servers": { + "srv0": { + "listen": [ + ":2099" + ], + "routes": [ + { + "match": [ + { + "host": [ + "localhost" + ] + } + ], + "handle": [ + { + "handler": "subroute", + "routes": [ + { + "handle": [ + { + "handler": "vars", + "root": "/var/www/" + }, + { + "handler": "file_server", + "hide": [ + "./Caddyfile" + ] + } + ] + } + ] + } + ], + "terminal": true + } + ], + "errors": { + "routes": [ + { + "match": [ + { + "host": [ + "localhost" + ] + } + ], + "handle": [ + { + "handler": "subroute", + "routes": [ + { + "handle": [ + { + "handler": "subroute", + "routes": [ + { + "group": "group3", + "handle": [ + { + "handler": "subroute", + "routes": [ + { + "handle": [ + { + "body": "not found", + "handler": "static_response", + "status_code": 404 + } + ] + } + ] + } + ], + "match": [ + { + "path": [ + "/en/*" + ] + } + ] + }, + { + "group": "group3", + "handle": [ + { + "handler": "subroute", + "routes": [ + { + "handle": [ + { + "body": "no encontrado", + "handler": "static_response" + } + ] + } + ] + } + ], + "match": [ + { + "path": [ + "/es/*" + ] + } + ] + }, + { + "group": "group3", + "handle": [ + { + "handler": "subroute", + "routes": [ + { + "handle": [ + { + "body": "default not found", + "handler": "static_response" + } + ] + } + ] + } + ] + } + ] + } + ], + "match": [ + { + "expression": "{http.error.status_code} in [404]" + } + ] + }, + { + "handle": [ + { + "handler": "subroute", + "routes": [ + { + "group": "group8", + "handle": [ + { + "handler": "subroute", + "routes": [ + { + "handle": [ + { + "body": "English error", + "handler": "static_response" + } + ] + } + ] + } + ], + "match": [ + { + "path": [ + "/en/*" + ] + } + ] + }, + { + "group": "group8", + "handle": [ + { + "handler": "subroute", + "routes": [ + { + "handle": [ + { + "body": "Spanish error", + "handler": "static_response" + } + ] + } + ] + } + ], + "match": [ + { + "path": [ + "/es/*" + ] + } + ] + }, + { + "group": "group8", + "handle": [ + { + "handler": "subroute", + "routes": [ + { + "handle": [ + { + "body": "Default error", + "handler": "static_response" + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ], + "terminal": true + } + ] + } + } + } + } + } +} + diff --git a/caddytest/integration/caddyfile_adapt/tls_client_auth_leaf_verifier_file_loader_block.caddyfiletest b/caddytest/integration/caddyfile_adapt/tls_client_auth_leaf_verifier_file_loader_block.caddyfiletest new file mode 100644 index 000000000..bc71456b8 --- /dev/null +++ b/caddytest/integration/caddyfile_adapt/tls_client_auth_leaf_verifier_file_loader_block.caddyfiletest @@ -0,0 +1,87 @@ +localhost + +respond "hello from localhost" +tls { + client_auth { + mode request + trust_pool inline { + trust_der MIIDSzCCAjOgAwIBAgIUfIRObjWNUA4jxQ/0x8BOCvE2Vw4wDQYJKoZIhvcNAQELBQAwFjEUMBIGA1UEAwwLRWFzeS1SU0EgQ0EwHhcNMTkwODI4MTYyNTU5WhcNMjkwODI1MTYyNTU5WjAWMRQwEgYDVQQDDAtFYXN5LVJTQSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK5m5elxhQfMp/3aVJ4JnpN9PUSz6LlP6LePAPFU7gqohVVFVtDkChJAG3FNkNQNlieVTja/bgH9IcC6oKbROwdY1h0MvNV8AHHigvl03WuJD8g2ReVFXXwsnrPmKXCFzQyMI6TYk3m2gYrXsZOU1GLnfMRC3KAMRgE2F45twOs9hqG169YJ6mM2eQjzjCHWI6S2/iUYvYxRkCOlYUbLsMD/AhgAf1plzg6LPqNxtdlwxZnA0ytgkmhK67HtzJu0+ovUCsMv0RwcMhsEo9T8nyFAGt9XLZ63X5WpBCTUApaAUhnG0XnerjmUWb6eUWw4zev54sEfY5F3x002iQaW6cECAwEAAaOBkDCBjTAdBgNVHQ4EFgQU4CBUbZsS2GaNIkGRz/cBsD5ivjswUQYDVR0jBEowSIAU4CBUbZsS2GaNIkGRz/cBsD5ivjuhGqQYMBYxFDASBgNVBAMMC0Vhc3ktUlNBIENBghR8hE5uNY1QDiPFD/THwE4K8TZXDjAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAKB3V4HIzoiO/Ch6WMj9bLJ2FGbpkMrcb/Eq01hT5zcfKD66lVS1MlK+cRL446Z2b2KDP1oFyVs+qmrmtdwrWgD+nfe2sBmmIHo9m9KygMkEOfG3MghGTEcS+0cTKEcoHYWYyOqQh6jnedXY8Cdm4GM1hAc9MiL3/sqV8YCVSLNnkoNysmr06/rZ0MCUZPGUtRmfd0heWhrfzAKw2HLgX+RAmpOE2MZqWcjvqKGyaRiaZks4nJkP6521aC2Lgp0HhCz1j8/uQ5ldoDszCnu/iro0NAsNtudTMD+YoLQxLqdleIh6CW+illc2VdXwj7mn6J04yns9jfE2jRjW/yTLFuQ== + } + verifier leaf { + file ../caddy.ca.cer + } + } +} +---------- +{ + "apps": { + "http": { + "servers": { + "srv0": { + "listen": [ + ":443" + ], + "routes": [ + { + "match": [ + { + "host": [ + "localhost" + ] + } + ], + "handle": [ + { + "handler": "subroute", + "routes": [ + { + "handle": [ + { + "body": "hello from localhost", + "handler": "static_response" + } + ] + } + ] + } + ], + "terminal": true + } + ], + "tls_connection_policies": [ + { + "match": { + "sni": [ + "localhost" + ] + }, + "client_authentication": { + "ca": { + "provider": "inline", + "trusted_ca_certs": [ + "MIIDSzCCAjOgAwIBAgIUfIRObjWNUA4jxQ/0x8BOCvE2Vw4wDQYJKoZIhvcNAQELBQAwFjEUMBIGA1UEAwwLRWFzeS1SU0EgQ0EwHhcNMTkwODI4MTYyNTU5WhcNMjkwODI1MTYyNTU5WjAWMRQwEgYDVQQDDAtFYXN5LVJTQSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK5m5elxhQfMp/3aVJ4JnpN9PUSz6LlP6LePAPFU7gqohVVFVtDkChJAG3FNkNQNlieVTja/bgH9IcC6oKbROwdY1h0MvNV8AHHigvl03WuJD8g2ReVFXXwsnrPmKXCFzQyMI6TYk3m2gYrXsZOU1GLnfMRC3KAMRgE2F45twOs9hqG169YJ6mM2eQjzjCHWI6S2/iUYvYxRkCOlYUbLsMD/AhgAf1plzg6LPqNxtdlwxZnA0ytgkmhK67HtzJu0+ovUCsMv0RwcMhsEo9T8nyFAGt9XLZ63X5WpBCTUApaAUhnG0XnerjmUWb6eUWw4zev54sEfY5F3x002iQaW6cECAwEAAaOBkDCBjTAdBgNVHQ4EFgQU4CBUbZsS2GaNIkGRz/cBsD5ivjswUQYDVR0jBEowSIAU4CBUbZsS2GaNIkGRz/cBsD5ivjuhGqQYMBYxFDASBgNVBAMMC0Vhc3ktUlNBIENBghR8hE5uNY1QDiPFD/THwE4K8TZXDjAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAKB3V4HIzoiO/Ch6WMj9bLJ2FGbpkMrcb/Eq01hT5zcfKD66lVS1MlK+cRL446Z2b2KDP1oFyVs+qmrmtdwrWgD+nfe2sBmmIHo9m9KygMkEOfG3MghGTEcS+0cTKEcoHYWYyOqQh6jnedXY8Cdm4GM1hAc9MiL3/sqV8YCVSLNnkoNysmr06/rZ0MCUZPGUtRmfd0heWhrfzAKw2HLgX+RAmpOE2MZqWcjvqKGyaRiaZks4nJkP6521aC2Lgp0HhCz1j8/uQ5ldoDszCnu/iro0NAsNtudTMD+YoLQxLqdleIh6CW+illc2VdXwj7mn6J04yns9jfE2jRjW/yTLFuQ==" + ] + }, + "verifiers": [ + { + "leaf_certs_loaders": [ + { + "files": [ + "../caddy.ca.cer" + ], + "loader": "file" + } + ], + "verifier": "leaf" + } + ], + "mode": "request" + } + }, + {} + ] + } + } + } + } +} \ No newline at end of file diff --git a/caddytest/integration/caddyfile_adapt/tls_client_auth_leaf_verifier_file_loader_inline.caddyfiletest b/caddytest/integration/caddyfile_adapt/tls_client_auth_leaf_verifier_file_loader_inline.caddyfiletest new file mode 100644 index 000000000..baca2433a --- /dev/null +++ b/caddytest/integration/caddyfile_adapt/tls_client_auth_leaf_verifier_file_loader_inline.caddyfiletest @@ -0,0 +1,85 @@ +localhost + +respond "hello from localhost" +tls { + client_auth { + mode request + trust_pool inline { + trust_der MIIDSzCCAjOgAwIBAgIUfIRObjWNUA4jxQ/0x8BOCvE2Vw4wDQYJKoZIhvcNAQELBQAwFjEUMBIGA1UEAwwLRWFzeS1SU0EgQ0EwHhcNMTkwODI4MTYyNTU5WhcNMjkwODI1MTYyNTU5WjAWMRQwEgYDVQQDDAtFYXN5LVJTQSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK5m5elxhQfMp/3aVJ4JnpN9PUSz6LlP6LePAPFU7gqohVVFVtDkChJAG3FNkNQNlieVTja/bgH9IcC6oKbROwdY1h0MvNV8AHHigvl03WuJD8g2ReVFXXwsnrPmKXCFzQyMI6TYk3m2gYrXsZOU1GLnfMRC3KAMRgE2F45twOs9hqG169YJ6mM2eQjzjCHWI6S2/iUYvYxRkCOlYUbLsMD/AhgAf1plzg6LPqNxtdlwxZnA0ytgkmhK67HtzJu0+ovUCsMv0RwcMhsEo9T8nyFAGt9XLZ63X5WpBCTUApaAUhnG0XnerjmUWb6eUWw4zev54sEfY5F3x002iQaW6cECAwEAAaOBkDCBjTAdBgNVHQ4EFgQU4CBUbZsS2GaNIkGRz/cBsD5ivjswUQYDVR0jBEowSIAU4CBUbZsS2GaNIkGRz/cBsD5ivjuhGqQYMBYxFDASBgNVBAMMC0Vhc3ktUlNBIENBghR8hE5uNY1QDiPFD/THwE4K8TZXDjAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAKB3V4HIzoiO/Ch6WMj9bLJ2FGbpkMrcb/Eq01hT5zcfKD66lVS1MlK+cRL446Z2b2KDP1oFyVs+qmrmtdwrWgD+nfe2sBmmIHo9m9KygMkEOfG3MghGTEcS+0cTKEcoHYWYyOqQh6jnedXY8Cdm4GM1hAc9MiL3/sqV8YCVSLNnkoNysmr06/rZ0MCUZPGUtRmfd0heWhrfzAKw2HLgX+RAmpOE2MZqWcjvqKGyaRiaZks4nJkP6521aC2Lgp0HhCz1j8/uQ5ldoDszCnu/iro0NAsNtudTMD+YoLQxLqdleIh6CW+illc2VdXwj7mn6J04yns9jfE2jRjW/yTLFuQ== + } + verifier leaf file ../caddy.ca.cer + } +} +---------- +{ + "apps": { + "http": { + "servers": { + "srv0": { + "listen": [ + ":443" + ], + "routes": [ + { + "match": [ + { + "host": [ + "localhost" + ] + } + ], + "handle": [ + { + "handler": "subroute", + "routes": [ + { + "handle": [ + { + "body": "hello from localhost", + "handler": "static_response" + } + ] + } + ] + } + ], + "terminal": true + } + ], + "tls_connection_policies": [ + { + "match": { + "sni": [ + "localhost" + ] + }, + "client_authentication": { + "ca": { + "provider": "inline", + "trusted_ca_certs": [ + "MIIDSzCCAjOgAwIBAgIUfIRObjWNUA4jxQ/0x8BOCvE2Vw4wDQYJKoZIhvcNAQELBQAwFjEUMBIGA1UEAwwLRWFzeS1SU0EgQ0EwHhcNMTkwODI4MTYyNTU5WhcNMjkwODI1MTYyNTU5WjAWMRQwEgYDVQQDDAtFYXN5LVJTQSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK5m5elxhQfMp/3aVJ4JnpN9PUSz6LlP6LePAPFU7gqohVVFVtDkChJAG3FNkNQNlieVTja/bgH9IcC6oKbROwdY1h0MvNV8AHHigvl03WuJD8g2ReVFXXwsnrPmKXCFzQyMI6TYk3m2gYrXsZOU1GLnfMRC3KAMRgE2F45twOs9hqG169YJ6mM2eQjzjCHWI6S2/iUYvYxRkCOlYUbLsMD/AhgAf1plzg6LPqNxtdlwxZnA0ytgkmhK67HtzJu0+ovUCsMv0RwcMhsEo9T8nyFAGt9XLZ63X5WpBCTUApaAUhnG0XnerjmUWb6eUWw4zev54sEfY5F3x002iQaW6cECAwEAAaOBkDCBjTAdBgNVHQ4EFgQU4CBUbZsS2GaNIkGRz/cBsD5ivjswUQYDVR0jBEowSIAU4CBUbZsS2GaNIkGRz/cBsD5ivjuhGqQYMBYxFDASBgNVBAMMC0Vhc3ktUlNBIENBghR8hE5uNY1QDiPFD/THwE4K8TZXDjAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAKB3V4HIzoiO/Ch6WMj9bLJ2FGbpkMrcb/Eq01hT5zcfKD66lVS1MlK+cRL446Z2b2KDP1oFyVs+qmrmtdwrWgD+nfe2sBmmIHo9m9KygMkEOfG3MghGTEcS+0cTKEcoHYWYyOqQh6jnedXY8Cdm4GM1hAc9MiL3/sqV8YCVSLNnkoNysmr06/rZ0MCUZPGUtRmfd0heWhrfzAKw2HLgX+RAmpOE2MZqWcjvqKGyaRiaZks4nJkP6521aC2Lgp0HhCz1j8/uQ5ldoDszCnu/iro0NAsNtudTMD+YoLQxLqdleIh6CW+illc2VdXwj7mn6J04yns9jfE2jRjW/yTLFuQ==" + ] + }, + "verifiers": [ + { + "leaf_certs_loaders": [ + { + "files": [ + "../caddy.ca.cer" + ], + "loader": "file" + } + ], + "verifier": "leaf" + } + ], + "mode": "request" + } + }, + {} + ] + } + } + } + } +} \ No newline at end of file diff --git a/caddytest/integration/caddyfile_adapt/tls_client_auth_leaf_verifier_file_loader_multi-in-block.caddyfiletest b/caddytest/integration/caddyfile_adapt/tls_client_auth_leaf_verifier_file_loader_multi-in-block.caddyfiletest new file mode 100644 index 000000000..1fe22a317 --- /dev/null +++ b/caddytest/integration/caddyfile_adapt/tls_client_auth_leaf_verifier_file_loader_multi-in-block.caddyfiletest @@ -0,0 +1,94 @@ +localhost + +respond "hello from localhost" +tls { + client_auth { + mode request + trust_pool inline { + trust_der MIIDSzCCAjOgAwIBAgIUfIRObjWNUA4jxQ/0x8BOCvE2Vw4wDQYJKoZIhvcNAQELBQAwFjEUMBIGA1UEAwwLRWFzeS1SU0EgQ0EwHhcNMTkwODI4MTYyNTU5WhcNMjkwODI1MTYyNTU5WjAWMRQwEgYDVQQDDAtFYXN5LVJTQSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK5m5elxhQfMp/3aVJ4JnpN9PUSz6LlP6LePAPFU7gqohVVFVtDkChJAG3FNkNQNlieVTja/bgH9IcC6oKbROwdY1h0MvNV8AHHigvl03WuJD8g2ReVFXXwsnrPmKXCFzQyMI6TYk3m2gYrXsZOU1GLnfMRC3KAMRgE2F45twOs9hqG169YJ6mM2eQjzjCHWI6S2/iUYvYxRkCOlYUbLsMD/AhgAf1plzg6LPqNxtdlwxZnA0ytgkmhK67HtzJu0+ovUCsMv0RwcMhsEo9T8nyFAGt9XLZ63X5WpBCTUApaAUhnG0XnerjmUWb6eUWw4zev54sEfY5F3x002iQaW6cECAwEAAaOBkDCBjTAdBgNVHQ4EFgQU4CBUbZsS2GaNIkGRz/cBsD5ivjswUQYDVR0jBEowSIAU4CBUbZsS2GaNIkGRz/cBsD5ivjuhGqQYMBYxFDASBgNVBAMMC0Vhc3ktUlNBIENBghR8hE5uNY1QDiPFD/THwE4K8TZXDjAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAKB3V4HIzoiO/Ch6WMj9bLJ2FGbpkMrcb/Eq01hT5zcfKD66lVS1MlK+cRL446Z2b2KDP1oFyVs+qmrmtdwrWgD+nfe2sBmmIHo9m9KygMkEOfG3MghGTEcS+0cTKEcoHYWYyOqQh6jnedXY8Cdm4GM1hAc9MiL3/sqV8YCVSLNnkoNysmr06/rZ0MCUZPGUtRmfd0heWhrfzAKw2HLgX+RAmpOE2MZqWcjvqKGyaRiaZks4nJkP6521aC2Lgp0HhCz1j8/uQ5ldoDszCnu/iro0NAsNtudTMD+YoLQxLqdleIh6CW+illc2VdXwj7mn6J04yns9jfE2jRjW/yTLFuQ== + } + verifier leaf { + file ../caddy.ca.cer + file ../caddy.ca.cer + } + } +} +---------- +{ + "apps": { + "http": { + "servers": { + "srv0": { + "listen": [ + ":443" + ], + "routes": [ + { + "match": [ + { + "host": [ + "localhost" + ] + } + ], + "handle": [ + { + "handler": "subroute", + "routes": [ + { + "handle": [ + { + "body": "hello from localhost", + "handler": "static_response" + } + ] + } + ] + } + ], + "terminal": true + } + ], + "tls_connection_policies": [ + { + "match": { + "sni": [ + "localhost" + ] + }, + "client_authentication": { + "ca": { + "provider": "inline", + "trusted_ca_certs": [ + "MIIDSzCCAjOgAwIBAgIUfIRObjWNUA4jxQ/0x8BOCvE2Vw4wDQYJKoZIhvcNAQELBQAwFjEUMBIGA1UEAwwLRWFzeS1SU0EgQ0EwHhcNMTkwODI4MTYyNTU5WhcNMjkwODI1MTYyNTU5WjAWMRQwEgYDVQQDDAtFYXN5LVJTQSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK5m5elxhQfMp/3aVJ4JnpN9PUSz6LlP6LePAPFU7gqohVVFVtDkChJAG3FNkNQNlieVTja/bgH9IcC6oKbROwdY1h0MvNV8AHHigvl03WuJD8g2ReVFXXwsnrPmKXCFzQyMI6TYk3m2gYrXsZOU1GLnfMRC3KAMRgE2F45twOs9hqG169YJ6mM2eQjzjCHWI6S2/iUYvYxRkCOlYUbLsMD/AhgAf1plzg6LPqNxtdlwxZnA0ytgkmhK67HtzJu0+ovUCsMv0RwcMhsEo9T8nyFAGt9XLZ63X5WpBCTUApaAUhnG0XnerjmUWb6eUWw4zev54sEfY5F3x002iQaW6cECAwEAAaOBkDCBjTAdBgNVHQ4EFgQU4CBUbZsS2GaNIkGRz/cBsD5ivjswUQYDVR0jBEowSIAU4CBUbZsS2GaNIkGRz/cBsD5ivjuhGqQYMBYxFDASBgNVBAMMC0Vhc3ktUlNBIENBghR8hE5uNY1QDiPFD/THwE4K8TZXDjAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAKB3V4HIzoiO/Ch6WMj9bLJ2FGbpkMrcb/Eq01hT5zcfKD66lVS1MlK+cRL446Z2b2KDP1oFyVs+qmrmtdwrWgD+nfe2sBmmIHo9m9KygMkEOfG3MghGTEcS+0cTKEcoHYWYyOqQh6jnedXY8Cdm4GM1hAc9MiL3/sqV8YCVSLNnkoNysmr06/rZ0MCUZPGUtRmfd0heWhrfzAKw2HLgX+RAmpOE2MZqWcjvqKGyaRiaZks4nJkP6521aC2Lgp0HhCz1j8/uQ5ldoDszCnu/iro0NAsNtudTMD+YoLQxLqdleIh6CW+illc2VdXwj7mn6J04yns9jfE2jRjW/yTLFuQ==" + ] + }, + "verifiers": [ + { + "leaf_certs_loaders": [ + { + "files": [ + "../caddy.ca.cer" + ], + "loader": "file" + }, + { + "files": [ + "../caddy.ca.cer" + ], + "loader": "file" + } + ], + "verifier": "leaf" + } + ], + "mode": "request" + } + }, + {} + ] + } + } + } + } +} \ No newline at end of file diff --git a/caddytest/integration/caddyfile_adapt/tls_client_auth_leaf_verifier_folder_loader_block.caddyfiletest b/caddytest/integration/caddyfile_adapt/tls_client_auth_leaf_verifier_folder_loader_block.caddyfiletest new file mode 100644 index 000000000..ee9be71aa --- /dev/null +++ b/caddytest/integration/caddyfile_adapt/tls_client_auth_leaf_verifier_folder_loader_block.caddyfiletest @@ -0,0 +1,87 @@ +localhost + +respond "hello from localhost" +tls { + client_auth { + mode request + trust_pool inline { + trust_der MIIDSzCCAjOgAwIBAgIUfIRObjWNUA4jxQ/0x8BOCvE2Vw4wDQYJKoZIhvcNAQELBQAwFjEUMBIGA1UEAwwLRWFzeS1SU0EgQ0EwHhcNMTkwODI4MTYyNTU5WhcNMjkwODI1MTYyNTU5WjAWMRQwEgYDVQQDDAtFYXN5LVJTQSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK5m5elxhQfMp/3aVJ4JnpN9PUSz6LlP6LePAPFU7gqohVVFVtDkChJAG3FNkNQNlieVTja/bgH9IcC6oKbROwdY1h0MvNV8AHHigvl03WuJD8g2ReVFXXwsnrPmKXCFzQyMI6TYk3m2gYrXsZOU1GLnfMRC3KAMRgE2F45twOs9hqG169YJ6mM2eQjzjCHWI6S2/iUYvYxRkCOlYUbLsMD/AhgAf1plzg6LPqNxtdlwxZnA0ytgkmhK67HtzJu0+ovUCsMv0RwcMhsEo9T8nyFAGt9XLZ63X5WpBCTUApaAUhnG0XnerjmUWb6eUWw4zev54sEfY5F3x002iQaW6cECAwEAAaOBkDCBjTAdBgNVHQ4EFgQU4CBUbZsS2GaNIkGRz/cBsD5ivjswUQYDVR0jBEowSIAU4CBUbZsS2GaNIkGRz/cBsD5ivjuhGqQYMBYxFDASBgNVBAMMC0Vhc3ktUlNBIENBghR8hE5uNY1QDiPFD/THwE4K8TZXDjAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAKB3V4HIzoiO/Ch6WMj9bLJ2FGbpkMrcb/Eq01hT5zcfKD66lVS1MlK+cRL446Z2b2KDP1oFyVs+qmrmtdwrWgD+nfe2sBmmIHo9m9KygMkEOfG3MghGTEcS+0cTKEcoHYWYyOqQh6jnedXY8Cdm4GM1hAc9MiL3/sqV8YCVSLNnkoNysmr06/rZ0MCUZPGUtRmfd0heWhrfzAKw2HLgX+RAmpOE2MZqWcjvqKGyaRiaZks4nJkP6521aC2Lgp0HhCz1j8/uQ5ldoDszCnu/iro0NAsNtudTMD+YoLQxLqdleIh6CW+illc2VdXwj7mn6J04yns9jfE2jRjW/yTLFuQ== + } + verifier leaf { + folder ../ + } + } +} +---------- +{ + "apps": { + "http": { + "servers": { + "srv0": { + "listen": [ + ":443" + ], + "routes": [ + { + "match": [ + { + "host": [ + "localhost" + ] + } + ], + "handle": [ + { + "handler": "subroute", + "routes": [ + { + "handle": [ + { + "body": "hello from localhost", + "handler": "static_response" + } + ] + } + ] + } + ], + "terminal": true + } + ], + "tls_connection_policies": [ + { + "match": { + "sni": [ + "localhost" + ] + }, + "client_authentication": { + "ca": { + "provider": "inline", + "trusted_ca_certs": [ + "MIIDSzCCAjOgAwIBAgIUfIRObjWNUA4jxQ/0x8BOCvE2Vw4wDQYJKoZIhvcNAQELBQAwFjEUMBIGA1UEAwwLRWFzeS1SU0EgQ0EwHhcNMTkwODI4MTYyNTU5WhcNMjkwODI1MTYyNTU5WjAWMRQwEgYDVQQDDAtFYXN5LVJTQSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK5m5elxhQfMp/3aVJ4JnpN9PUSz6LlP6LePAPFU7gqohVVFVtDkChJAG3FNkNQNlieVTja/bgH9IcC6oKbROwdY1h0MvNV8AHHigvl03WuJD8g2ReVFXXwsnrPmKXCFzQyMI6TYk3m2gYrXsZOU1GLnfMRC3KAMRgE2F45twOs9hqG169YJ6mM2eQjzjCHWI6S2/iUYvYxRkCOlYUbLsMD/AhgAf1plzg6LPqNxtdlwxZnA0ytgkmhK67HtzJu0+ovUCsMv0RwcMhsEo9T8nyFAGt9XLZ63X5WpBCTUApaAUhnG0XnerjmUWb6eUWw4zev54sEfY5F3x002iQaW6cECAwEAAaOBkDCBjTAdBgNVHQ4EFgQU4CBUbZsS2GaNIkGRz/cBsD5ivjswUQYDVR0jBEowSIAU4CBUbZsS2GaNIkGRz/cBsD5ivjuhGqQYMBYxFDASBgNVBAMMC0Vhc3ktUlNBIENBghR8hE5uNY1QDiPFD/THwE4K8TZXDjAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAKB3V4HIzoiO/Ch6WMj9bLJ2FGbpkMrcb/Eq01hT5zcfKD66lVS1MlK+cRL446Z2b2KDP1oFyVs+qmrmtdwrWgD+nfe2sBmmIHo9m9KygMkEOfG3MghGTEcS+0cTKEcoHYWYyOqQh6jnedXY8Cdm4GM1hAc9MiL3/sqV8YCVSLNnkoNysmr06/rZ0MCUZPGUtRmfd0heWhrfzAKw2HLgX+RAmpOE2MZqWcjvqKGyaRiaZks4nJkP6521aC2Lgp0HhCz1j8/uQ5ldoDszCnu/iro0NAsNtudTMD+YoLQxLqdleIh6CW+illc2VdXwj7mn6J04yns9jfE2jRjW/yTLFuQ==" + ] + }, + "verifiers": [ + { + "leaf_certs_loaders": [ + { + "folders": [ + "../" + ], + "loader": "folder" + } + ], + "verifier": "leaf" + } + ], + "mode": "request" + } + }, + {} + ] + } + } + } + } +} \ No newline at end of file diff --git a/caddytest/integration/caddyfile_adapt/tls_client_auth_leaf_verifier_folder_loader_inline.caddyfiletest b/caddytest/integration/caddyfile_adapt/tls_client_auth_leaf_verifier_folder_loader_inline.caddyfiletest new file mode 100644 index 000000000..b6c4b8727 --- /dev/null +++ b/caddytest/integration/caddyfile_adapt/tls_client_auth_leaf_verifier_folder_loader_inline.caddyfiletest @@ -0,0 +1,85 @@ +localhost + +respond "hello from localhost" +tls { + client_auth { + mode request + trust_pool inline { + trust_der MIIDSzCCAjOgAwIBAgIUfIRObjWNUA4jxQ/0x8BOCvE2Vw4wDQYJKoZIhvcNAQELBQAwFjEUMBIGA1UEAwwLRWFzeS1SU0EgQ0EwHhcNMTkwODI4MTYyNTU5WhcNMjkwODI1MTYyNTU5WjAWMRQwEgYDVQQDDAtFYXN5LVJTQSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK5m5elxhQfMp/3aVJ4JnpN9PUSz6LlP6LePAPFU7gqohVVFVtDkChJAG3FNkNQNlieVTja/bgH9IcC6oKbROwdY1h0MvNV8AHHigvl03WuJD8g2ReVFXXwsnrPmKXCFzQyMI6TYk3m2gYrXsZOU1GLnfMRC3KAMRgE2F45twOs9hqG169YJ6mM2eQjzjCHWI6S2/iUYvYxRkCOlYUbLsMD/AhgAf1plzg6LPqNxtdlwxZnA0ytgkmhK67HtzJu0+ovUCsMv0RwcMhsEo9T8nyFAGt9XLZ63X5WpBCTUApaAUhnG0XnerjmUWb6eUWw4zev54sEfY5F3x002iQaW6cECAwEAAaOBkDCBjTAdBgNVHQ4EFgQU4CBUbZsS2GaNIkGRz/cBsD5ivjswUQYDVR0jBEowSIAU4CBUbZsS2GaNIkGRz/cBsD5ivjuhGqQYMBYxFDASBgNVBAMMC0Vhc3ktUlNBIENBghR8hE5uNY1QDiPFD/THwE4K8TZXDjAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAKB3V4HIzoiO/Ch6WMj9bLJ2FGbpkMrcb/Eq01hT5zcfKD66lVS1MlK+cRL446Z2b2KDP1oFyVs+qmrmtdwrWgD+nfe2sBmmIHo9m9KygMkEOfG3MghGTEcS+0cTKEcoHYWYyOqQh6jnedXY8Cdm4GM1hAc9MiL3/sqV8YCVSLNnkoNysmr06/rZ0MCUZPGUtRmfd0heWhrfzAKw2HLgX+RAmpOE2MZqWcjvqKGyaRiaZks4nJkP6521aC2Lgp0HhCz1j8/uQ5ldoDszCnu/iro0NAsNtudTMD+YoLQxLqdleIh6CW+illc2VdXwj7mn6J04yns9jfE2jRjW/yTLFuQ== + } + verifier leaf folder ../ + } +} +---------- +{ + "apps": { + "http": { + "servers": { + "srv0": { + "listen": [ + ":443" + ], + "routes": [ + { + "match": [ + { + "host": [ + "localhost" + ] + } + ], + "handle": [ + { + "handler": "subroute", + "routes": [ + { + "handle": [ + { + "body": "hello from localhost", + "handler": "static_response" + } + ] + } + ] + } + ], + "terminal": true + } + ], + "tls_connection_policies": [ + { + "match": { + "sni": [ + "localhost" + ] + }, + "client_authentication": { + "ca": { + "provider": "inline", + "trusted_ca_certs": [ + "MIIDSzCCAjOgAwIBAgIUfIRObjWNUA4jxQ/0x8BOCvE2Vw4wDQYJKoZIhvcNAQELBQAwFjEUMBIGA1UEAwwLRWFzeS1SU0EgQ0EwHhcNMTkwODI4MTYyNTU5WhcNMjkwODI1MTYyNTU5WjAWMRQwEgYDVQQDDAtFYXN5LVJTQSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK5m5elxhQfMp/3aVJ4JnpN9PUSz6LlP6LePAPFU7gqohVVFVtDkChJAG3FNkNQNlieVTja/bgH9IcC6oKbROwdY1h0MvNV8AHHigvl03WuJD8g2ReVFXXwsnrPmKXCFzQyMI6TYk3m2gYrXsZOU1GLnfMRC3KAMRgE2F45twOs9hqG169YJ6mM2eQjzjCHWI6S2/iUYvYxRkCOlYUbLsMD/AhgAf1plzg6LPqNxtdlwxZnA0ytgkmhK67HtzJu0+ovUCsMv0RwcMhsEo9T8nyFAGt9XLZ63X5WpBCTUApaAUhnG0XnerjmUWb6eUWw4zev54sEfY5F3x002iQaW6cECAwEAAaOBkDCBjTAdBgNVHQ4EFgQU4CBUbZsS2GaNIkGRz/cBsD5ivjswUQYDVR0jBEowSIAU4CBUbZsS2GaNIkGRz/cBsD5ivjuhGqQYMBYxFDASBgNVBAMMC0Vhc3ktUlNBIENBghR8hE5uNY1QDiPFD/THwE4K8TZXDjAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAKB3V4HIzoiO/Ch6WMj9bLJ2FGbpkMrcb/Eq01hT5zcfKD66lVS1MlK+cRL446Z2b2KDP1oFyVs+qmrmtdwrWgD+nfe2sBmmIHo9m9KygMkEOfG3MghGTEcS+0cTKEcoHYWYyOqQh6jnedXY8Cdm4GM1hAc9MiL3/sqV8YCVSLNnkoNysmr06/rZ0MCUZPGUtRmfd0heWhrfzAKw2HLgX+RAmpOE2MZqWcjvqKGyaRiaZks4nJkP6521aC2Lgp0HhCz1j8/uQ5ldoDszCnu/iro0NAsNtudTMD+YoLQxLqdleIh6CW+illc2VdXwj7mn6J04yns9jfE2jRjW/yTLFuQ==" + ] + }, + "verifiers": [ + { + "leaf_certs_loaders": [ + { + "folders": [ + "../" + ], + "loader": "folder" + } + ], + "verifier": "leaf" + } + ], + "mode": "request" + } + }, + {} + ] + } + } + } + } +} \ No newline at end of file diff --git a/caddytest/integration/caddyfile_adapt/tls_client_auth_leaf_verifier_folder_loader_multi-in-block.caddyfiletest b/caddytest/integration/caddyfile_adapt/tls_client_auth_leaf_verifier_folder_loader_multi-in-block.caddyfiletest new file mode 100644 index 000000000..dd5663d8d --- /dev/null +++ b/caddytest/integration/caddyfile_adapt/tls_client_auth_leaf_verifier_folder_loader_multi-in-block.caddyfiletest @@ -0,0 +1,94 @@ +localhost + +respond "hello from localhost" +tls { + client_auth { + mode request + trust_pool inline { + trust_der MIIDSzCCAjOgAwIBAgIUfIRObjWNUA4jxQ/0x8BOCvE2Vw4wDQYJKoZIhvcNAQELBQAwFjEUMBIGA1UEAwwLRWFzeS1SU0EgQ0EwHhcNMTkwODI4MTYyNTU5WhcNMjkwODI1MTYyNTU5WjAWMRQwEgYDVQQDDAtFYXN5LVJTQSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK5m5elxhQfMp/3aVJ4JnpN9PUSz6LlP6LePAPFU7gqohVVFVtDkChJAG3FNkNQNlieVTja/bgH9IcC6oKbROwdY1h0MvNV8AHHigvl03WuJD8g2ReVFXXwsnrPmKXCFzQyMI6TYk3m2gYrXsZOU1GLnfMRC3KAMRgE2F45twOs9hqG169YJ6mM2eQjzjCHWI6S2/iUYvYxRkCOlYUbLsMD/AhgAf1plzg6LPqNxtdlwxZnA0ytgkmhK67HtzJu0+ovUCsMv0RwcMhsEo9T8nyFAGt9XLZ63X5WpBCTUApaAUhnG0XnerjmUWb6eUWw4zev54sEfY5F3x002iQaW6cECAwEAAaOBkDCBjTAdBgNVHQ4EFgQU4CBUbZsS2GaNIkGRz/cBsD5ivjswUQYDVR0jBEowSIAU4CBUbZsS2GaNIkGRz/cBsD5ivjuhGqQYMBYxFDASBgNVBAMMC0Vhc3ktUlNBIENBghR8hE5uNY1QDiPFD/THwE4K8TZXDjAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAKB3V4HIzoiO/Ch6WMj9bLJ2FGbpkMrcb/Eq01hT5zcfKD66lVS1MlK+cRL446Z2b2KDP1oFyVs+qmrmtdwrWgD+nfe2sBmmIHo9m9KygMkEOfG3MghGTEcS+0cTKEcoHYWYyOqQh6jnedXY8Cdm4GM1hAc9MiL3/sqV8YCVSLNnkoNysmr06/rZ0MCUZPGUtRmfd0heWhrfzAKw2HLgX+RAmpOE2MZqWcjvqKGyaRiaZks4nJkP6521aC2Lgp0HhCz1j8/uQ5ldoDszCnu/iro0NAsNtudTMD+YoLQxLqdleIh6CW+illc2VdXwj7mn6J04yns9jfE2jRjW/yTLFuQ== + } + verifier leaf { + folder ../ + folder ../ + } + } +} +---------- +{ + "apps": { + "http": { + "servers": { + "srv0": { + "listen": [ + ":443" + ], + "routes": [ + { + "match": [ + { + "host": [ + "localhost" + ] + } + ], + "handle": [ + { + "handler": "subroute", + "routes": [ + { + "handle": [ + { + "body": "hello from localhost", + "handler": "static_response" + } + ] + } + ] + } + ], + "terminal": true + } + ], + "tls_connection_policies": [ + { + "match": { + "sni": [ + "localhost" + ] + }, + "client_authentication": { + "ca": { + "provider": "inline", + "trusted_ca_certs": [ + "MIIDSzCCAjOgAwIBAgIUfIRObjWNUA4jxQ/0x8BOCvE2Vw4wDQYJKoZIhvcNAQELBQAwFjEUMBIGA1UEAwwLRWFzeS1SU0EgQ0EwHhcNMTkwODI4MTYyNTU5WhcNMjkwODI1MTYyNTU5WjAWMRQwEgYDVQQDDAtFYXN5LVJTQSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK5m5elxhQfMp/3aVJ4JnpN9PUSz6LlP6LePAPFU7gqohVVFVtDkChJAG3FNkNQNlieVTja/bgH9IcC6oKbROwdY1h0MvNV8AHHigvl03WuJD8g2ReVFXXwsnrPmKXCFzQyMI6TYk3m2gYrXsZOU1GLnfMRC3KAMRgE2F45twOs9hqG169YJ6mM2eQjzjCHWI6S2/iUYvYxRkCOlYUbLsMD/AhgAf1plzg6LPqNxtdlwxZnA0ytgkmhK67HtzJu0+ovUCsMv0RwcMhsEo9T8nyFAGt9XLZ63X5WpBCTUApaAUhnG0XnerjmUWb6eUWw4zev54sEfY5F3x002iQaW6cECAwEAAaOBkDCBjTAdBgNVHQ4EFgQU4CBUbZsS2GaNIkGRz/cBsD5ivjswUQYDVR0jBEowSIAU4CBUbZsS2GaNIkGRz/cBsD5ivjuhGqQYMBYxFDASBgNVBAMMC0Vhc3ktUlNBIENBghR8hE5uNY1QDiPFD/THwE4K8TZXDjAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAKB3V4HIzoiO/Ch6WMj9bLJ2FGbpkMrcb/Eq01hT5zcfKD66lVS1MlK+cRL446Z2b2KDP1oFyVs+qmrmtdwrWgD+nfe2sBmmIHo9m9KygMkEOfG3MghGTEcS+0cTKEcoHYWYyOqQh6jnedXY8Cdm4GM1hAc9MiL3/sqV8YCVSLNnkoNysmr06/rZ0MCUZPGUtRmfd0heWhrfzAKw2HLgX+RAmpOE2MZqWcjvqKGyaRiaZks4nJkP6521aC2Lgp0HhCz1j8/uQ5ldoDszCnu/iro0NAsNtudTMD+YoLQxLqdleIh6CW+illc2VdXwj7mn6J04yns9jfE2jRjW/yTLFuQ==" + ] + }, + "verifiers": [ + { + "leaf_certs_loaders": [ + { + "folders": [ + "../" + ], + "loader": "folder" + }, + { + "folders": [ + "../" + ], + "loader": "folder" + } + ], + "verifier": "leaf" + } + ], + "mode": "request" + } + }, + {} + ] + } + } + } + } +} \ No newline at end of file diff --git a/caddytest/integration/caddyfile_adapt_test.go b/caddytest/integration/caddyfile_adapt_test.go index 0d9f0fa47..674036e17 100644 --- a/caddytest/integration/caddyfile_adapt_test.go +++ b/caddytest/integration/caddyfile_adapt_test.go @@ -10,7 +10,6 @@ import ( "testing" "github.com/caddyserver/caddy/v2/caddytest" - _ "github.com/caddyserver/caddy/v2/internal/testmocks" ) diff --git a/caddytest/integration/caddyfile_test.go b/caddytest/integration/caddyfile_test.go index 11ffc08ae..d45d5a5e9 100644 --- a/caddytest/integration/caddyfile_test.go +++ b/caddytest/integration/caddyfile_test.go @@ -615,7 +615,6 @@ func TestReplaceWithReplacementPlaceholder(t *testing.T) { respond "{query}"`, "caddyfile") tester.AssertGetResponse("http://localhost:9080/endpoint?placeholder=baz&foo=bar", 200, "foo=baz&placeholder=baz") - } func TestReplaceWithKeyPlaceholder(t *testing.T) { @@ -783,6 +782,46 @@ func TestHandleErrorRangeAndCodes(t *testing.T) { tester.AssertGetResponse("http://localhost:9080/private", 410, "Error in the [400 .. 499] range") } +func TestHandleErrorSubHandlers(t *testing.T) { + tester := caddytest.NewTester(t) + tester.InitServer(`{ + admin localhost:2999 + http_port 9080 + } + localhost:9080 { + root * /srv + file_server + error /*/internalerr* "Internal Server Error" 500 + + handle_errors 404 { + handle /en/* { + respond "not found" 404 + } + handle /es/* { + respond "no encontrado" 404 + } + handle { + respond "default not found" + } + } + handle_errors { + handle { + respond "Default error" + } + handle /en/* { + respond "English error" + } + } + } + `, "caddyfile") + // act and assert + tester.AssertGetResponse("http://localhost:9080/en/notfound", 404, "not found") + tester.AssertGetResponse("http://localhost:9080/es/notfound", 404, "no encontrado") + tester.AssertGetResponse("http://localhost:9080/notfound", 404, "default not found") + tester.AssertGetResponse("http://localhost:9080/es/internalerr", 500, "Default error") + tester.AssertGetResponse("http://localhost:9080/en/internalerr", 500, "English error") +} + func TestInvalidSiteAddressesAsDirectives(t *testing.T) { type testCase struct { config, expectedError string diff --git a/caddytest/integration/mockdns_test.go b/caddytest/integration/mockdns_test.go index 615116a3a..31dc4be7b 100644 --- a/caddytest/integration/mockdns_test.go +++ b/caddytest/integration/mockdns_test.go @@ -3,10 +3,11 @@ package integration import ( "context" - "github.com/caddyserver/caddy/v2" - "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" "github.com/caddyserver/certmagic" "github.com/libdns/libdns" + + "github.com/caddyserver/caddy/v2" + "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" ) func init() { @@ -55,7 +56,9 @@ func (MockDNSProvider) SetRecords(ctx context.Context, zone string, recs []libdn } // Interface guard -var _ caddyfile.Unmarshaler = (*MockDNSProvider)(nil) -var _ certmagic.DNSProvider = (*MockDNSProvider)(nil) -var _ caddy.Provisioner = (*MockDNSProvider)(nil) -var _ caddy.Module = (*MockDNSProvider)(nil) +var ( + _ caddyfile.Unmarshaler = (*MockDNSProvider)(nil) + _ certmagic.DNSProvider = (*MockDNSProvider)(nil) + _ caddy.Provisioner = (*MockDNSProvider)(nil) + _ caddy.Module = (*MockDNSProvider)(nil) +) diff --git a/caddytest/integration/stream_test.go b/caddytest/integration/stream_test.go index d2f2fd79b..57231a527 100644 --- a/caddytest/integration/stream_test.go +++ b/caddytest/integration/stream_test.go @@ -13,9 +13,10 @@ import ( "testing" "time" - "github.com/caddyserver/caddy/v2/caddytest" "golang.org/x/net/http2" "golang.org/x/net/http2/h2c" + + "github.com/caddyserver/caddy/v2/caddytest" ) // (see https://github.com/caddyserver/caddy/issues/3556 for use case) diff --git a/cmd/commands.go b/cmd/commands.go index 259dd358f..0dae876fb 100644 --- a/cmd/commands.go +++ b/cmd/commands.go @@ -20,6 +20,7 @@ import ( "os" "regexp" "strings" + "sync" "github.com/spf13/cobra" "github.com/spf13/cobra/doc" @@ -80,10 +81,16 @@ type CommandFunc func(Flags) (int, error) // Commands returns a list of commands initialised by // RegisterCommand func Commands() map[string]Command { + commandsMu.RLock() + defer commandsMu.RUnlock() + return commands } -var commands = make(map[string]Command) +var ( + commandsMu sync.RWMutex + commands = make(map[string]Command) +) func init() { RegisterCommand(Command{ @@ -441,7 +448,7 @@ EXPERIMENTAL: May be changed or removed. }) defaultFactory.Use(func(rootCmd *cobra.Command) { - rootCmd.AddCommand(caddyCmdToCobra(Command{ + manpageCommand := Command{ Name: "manpage", Usage: "--directory ", Short: "Generates the manual pages for Caddy commands", @@ -471,11 +478,12 @@ argument of --directory. If the directory does not exist, it will be created. return caddy.ExitCodeSuccess, nil }) }, - })) + } // source: https://github.com/spf13/cobra/blob/main/shell_completions.md - rootCmd.AddCommand(&cobra.Command{ - Use: "completion [bash|zsh|fish|powershell]", + completionCommand := Command{ + Name: "completion", + Usage: "[bash|zsh|fish|powershell]", Short: "Generate completion script", Long: fmt.Sprintf(`To load completions: @@ -516,24 +524,37 @@ argument of --directory. If the directory does not exist, it will be created. PS> %[1]s completion powershell > %[1]s.ps1 # and source this file from your PowerShell profile. `, rootCmd.Root().Name()), - DisableFlagsInUseLine: true, - ValidArgs: []string{"bash", "zsh", "fish", "powershell"}, - Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs), - RunE: func(cmd *cobra.Command, args []string) error { - switch args[0] { - case "bash": - return cmd.Root().GenBashCompletion(os.Stdout) - case "zsh": - return cmd.Root().GenZshCompletion(os.Stdout) - case "fish": - return cmd.Root().GenFishCompletion(os.Stdout, true) - case "powershell": - return cmd.Root().GenPowerShellCompletionWithDesc(os.Stdout) - default: - return fmt.Errorf("unrecognized shell: %s", args[0]) + CobraFunc: func(cmd *cobra.Command) { + cmd.DisableFlagsInUseLine = true + cmd.ValidArgs = []string{"bash", "zsh", "fish", "powershell"} + cmd.Args = cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs) + cmd.RunE = func(cmd *cobra.Command, args []string) error { + switch args[0] { + case "bash": + return cmd.Root().GenBashCompletion(os.Stdout) + case "zsh": + return cmd.Root().GenZshCompletion(os.Stdout) + case "fish": + return cmd.Root().GenFishCompletion(os.Stdout, true) + case "powershell": + return cmd.Root().GenPowerShellCompletionWithDesc(os.Stdout) + default: + return fmt.Errorf("unrecognized shell: %s", args[0]) + } } }, - }) + } + + rootCmd.AddCommand(caddyCmdToCobra(manpageCommand)) + rootCmd.AddCommand(caddyCmdToCobra(completionCommand)) + + // add manpage and completion commands to the map of + // available commands, because they're not registered + // through RegisterCommand. + commandsMu.Lock() + commands[manpageCommand.Name] = manpageCommand + commands[completionCommand.Name] = completionCommand + commandsMu.Unlock() }) } @@ -552,6 +573,9 @@ argument of --directory. If the directory does not exist, it will be created. // // This function should be used in init(). func RegisterCommand(cmd Command) { + commandsMu.Lock() + defer commandsMu.Unlock() + if cmd.Name == "" { panic("command name is required") } @@ -570,6 +594,7 @@ func RegisterCommand(cmd Command) { defaultFactory.Use(func(rootCmd *cobra.Command) { rootCmd.AddCommand(caddyCmdToCobra(cmd)) }) + commands[cmd.Name] = cmd } var commandNameRegex = regexp.MustCompile(`^[a-z0-9]$|^([a-z0-9]+-?[a-z0-9]*)+[a-z0-9]$`) diff --git a/cmd/commands_test.go b/cmd/commands_test.go new file mode 100644 index 000000000..085a9d789 --- /dev/null +++ b/cmd/commands_test.go @@ -0,0 +1,39 @@ +package caddycmd + +import ( + "maps" + "reflect" + "slices" + "testing" +) + +func TestCommandsAreAvailable(t *testing.T) { + // trigger init, and build the default factory, so that + // all commands from this package are available + cmd := defaultFactory.Build() + if cmd == nil { + t.Fatal("default factory failed to build") + } + + // check that the default factory has 17 commands; it doesn't + // include the commands registered through calls to init in + // other packages + cmds := Commands() + if len(cmds) != 17 { + t.Errorf("expected 17 commands, got %d", len(cmds)) + } + + commandNames := slices.Collect(maps.Keys(cmds)) + slices.Sort(commandNames) + + expectedCommandNames := []string{ + "adapt", "add-package", "build-info", "completion", + "environ", "fmt", "list-modules", "manpage", + "reload", "remove-package", "run", "start", + "stop", "storage", "upgrade", "validate", "version", + } + + if !reflect.DeepEqual(expectedCommandNames, commandNames) { + t.Errorf("expected %v, got %v", expectedCommandNames, commandNames) + } +} diff --git a/cmd/main.go b/cmd/main.go index 87fa9fb95..47d702ca7 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -418,7 +418,7 @@ func parseEnvFile(envInput io.Reader) (map[string]string, error) { // quoted value: support newlines if strings.HasPrefix(val, `"`) || strings.HasPrefix(val, "'") { quote := string(val[0]) - for !(strings.HasSuffix(line, quote) && !strings.HasSuffix(line, `\`+quote)) { + for !strings.HasSuffix(line, quote) || strings.HasSuffix(line, `\`+quote) { val = strings.ReplaceAll(val, `\`+quote, quote) if !scanner.Scan() { break diff --git a/cmd/main_test.go b/cmd/main_test.go index 3b2412c57..bff34f443 100644 --- a/cmd/main_test.go +++ b/cmd/main_test.go @@ -235,7 +235,6 @@ func Test_isCaddyfile(t *testing.T) { wantErr: false, }, { - name: "json is not caddyfile but not error", args: args{ configFile: "./Caddyfile.json", @@ -245,7 +244,6 @@ func Test_isCaddyfile(t *testing.T) { wantErr: false, }, { - name: "prefix of Caddyfile and ./ with any extension is Caddyfile", args: args{ configFile: "./Caddyfile.prd", @@ -255,7 +253,6 @@ func Test_isCaddyfile(t *testing.T) { wantErr: false, }, { - name: "prefix of Caddyfile without ./ with any extension is Caddyfile", args: args{ configFile: "Caddyfile.prd", diff --git a/cmd/packagesfuncs.go b/cmd/packagesfuncs.go index 695232001..cda6f31f6 100644 --- a/cmd/packagesfuncs.go +++ b/cmd/packagesfuncs.go @@ -84,7 +84,7 @@ func cmdAddPackage(fl Flags) (int, error) { return caddy.ExitCodeFailedStartup, fmt.Errorf("invalid module name: %v", err) } // only allow a version to be specified if it's different from the existing version - if _, ok := pluginPkgs[module]; ok && !(version != "" && pluginPkgs[module].Version != version) { + if _, ok := pluginPkgs[module]; ok && (version == "" || pluginPkgs[module].Version == version) { return caddy.ExitCodeFailedStartup, fmt.Errorf("package is already added") } pluginPkgs[module] = pluginPackage{Version: version, Path: module} diff --git a/go.mod b/go.mod index e160b0626..db8133d45 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b github.com/caddyserver/certmagic v0.23.0 github.com/caddyserver/zerossl v0.1.3 - github.com/cloudflare/circl v1.6.0 + github.com/cloudflare/circl v1.6.1 github.com/dustin/go-humanize v1.0.1 github.com/go-chi/chi/v5 v5.2.1 github.com/google/cel-go v0.24.1 diff --git a/go.sum b/go.sum index 03ca8ca10..9db9a77c7 100644 --- a/go.sum +++ b/go.sum @@ -113,8 +113,8 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04= github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/circl v1.6.0 h1:cr5JKic4HI+LkINy2lg3W2jF8sHCVTBncJr5gIIq7qk= -github.com/cloudflare/circl v1.6.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= +github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= +github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= diff --git a/listeners_test.go b/listeners_test.go index 03945308e..a4cadd3aa 100644 --- a/listeners_test.go +++ b/listeners_test.go @@ -30,7 +30,7 @@ func TestSplitNetworkAddress(t *testing.T) { expectErr bool }{ { - input: "", + input: "", expectHost: "", }, { @@ -41,7 +41,7 @@ func TestSplitNetworkAddress(t *testing.T) { input: ":", // empty host & empty port }, { - input: "::", + input: "::", expectHost: "::", }, { @@ -184,9 +184,8 @@ func TestParseNetworkAddress(t *testing.T) { expectErr bool }{ { - input: "", - expectAddr: NetworkAddress{ - }, + input: "", + expectAddr: NetworkAddress{}, }, { input: ":", @@ -311,9 +310,8 @@ func TestParseNetworkAddressWithDefaults(t *testing.T) { expectErr bool }{ { - input: "", - expectAddr: NetworkAddress{ - }, + input: "", + expectAddr: NetworkAddress{}, }, { input: ":", diff --git a/modules/caddyhttp/autohttps.go b/modules/caddyhttp/autohttps.go index ce8e6f8df..c34954f92 100644 --- a/modules/caddyhttp/autohttps.go +++ b/modules/caddyhttp/autohttps.go @@ -343,7 +343,7 @@ uniqueDomainsLoop: // match on known domain names, unless it's our special case of a // catch-all which is an empty string (common among catch-all sites // that enable on-demand TLS for yet-unknown domain names) - if !(len(domains) == 1 && domains[0] == "") { + if len(domains) != 1 || domains[0] != "" { matcherSet = append(matcherSet, MatchHost(domains)) } diff --git a/modules/caddyhttp/caddyauth/basicauth.go b/modules/caddyhttp/caddyauth/basicauth.go index 52a5a08c1..5a9e167e1 100644 --- a/modules/caddyhttp/caddyauth/basicauth.go +++ b/modules/caddyhttp/caddyauth/basicauth.go @@ -236,10 +236,7 @@ func (c *Cache) makeRoom() { // the cache is on a long tail, we can save a lot of CPU // time by doing a whole bunch of deletions now and then // we won't have to do them again for a while - numToDelete := len(c.cache) / 10 - if numToDelete < 1 { - numToDelete = 1 - } + numToDelete := max(len(c.cache)/10, 1) for deleted := 0; deleted <= numToDelete; deleted++ { // Go maps are "nondeterministic" not actually random, // so although we could just chop off the "front" of the diff --git a/modules/caddyhttp/fileserver/browse.html b/modules/caddyhttp/fileserver/browse.html index 704661f21..fe92dabe6 100644 --- a/modules/caddyhttp/fileserver/browse.html +++ b/modules/caddyhttp/fileserver/browse.html @@ -828,6 +828,96 @@ footer { Grid + {{- if and (eq .Layout "grid") (eq .Sort "name") (ne .Order "asc")}} + + + Z + A + + + + + {{- else if and (eq .Layout "grid") (eq .Sort "name") (ne .Order "desc")}} + + + A + Z + + + + + {{- else if and (eq .Layout "grid")}} + + + A + Z + + + + + {{- end}} + {{- if and (eq .Layout "grid") (eq .Sort "size") (ne .Order "asc")}} + + + + + + + + + + {{- else if and (eq .Layout "grid") (eq .Sort "size") (ne .Order "desc")}} + + + + + + + + + + {{- else if and (eq .Layout "grid")}} + + + + + + + + + + {{- end}} + {{- if and (eq .Layout "grid") (eq .Sort "time") (ne .Order "asc")}} + + + + + + + + + + {{- else if and (eq .Layout "grid") (eq .Sort "time") (ne .Order "desc")}} + + + + + + + + + + {{- else if and (eq .Layout "grid")}} + + + + + + + + + + {{- end}}

{{- if eq .Layout "grid"}} diff --git a/modules/caddyhttp/fileserver/matcher.go b/modules/caddyhttp/fileserver/matcher.go index b5b4c9f0f..fbcd36e0a 100644 --- a/modules/caddyhttp/fileserver/matcher.go +++ b/modules/caddyhttp/fileserver/matcher.go @@ -252,7 +252,7 @@ func celFileMatcherMacroExpander() parser.MacroExpander { } for _, arg := range args { - if !(isCELStringLiteral(arg) || isCELCaddyPlaceholderCall(arg)) { + if !isCELStringLiteral(arg) && !isCELCaddyPlaceholderCall(arg) { return nil, &common.Error{ Location: eh.OffsetLocation(arg.ID()), Message: "matcher only supports repeated string literal arguments", @@ -616,15 +616,16 @@ func isCELTryFilesLiteral(e ast.Expr) bool { return false } mapKeyStr := mapKey.AsLiteral().ConvertToType(types.StringType).Value() - if mapKeyStr == "try_files" || mapKeyStr == "split_path" { + switch mapKeyStr { + case "try_files", "split_path": if !isCELStringListLiteral(mapVal) { return false } - } else if mapKeyStr == "try_policy" || mapKeyStr == "root" { + case "try_policy", "root": if !(isCELStringExpr(mapVal)) { return false } - } else { + default: return false } } diff --git a/modules/caddyhttp/matchers.go b/modules/caddyhttp/matchers.go index 01bd7b8b4..48c92f174 100644 --- a/modules/caddyhttp/matchers.go +++ b/modules/caddyhttp/matchers.go @@ -552,7 +552,6 @@ func (MatchPath) matchPatternWithEscapeSequence(escapedPath, matchPath string) b if iPattern >= len(matchPath) || iPath >= len(escapedPath) { break } - // get the next character from the request path pathCh := string(escapedPath[iPath]) diff --git a/modules/caddyhttp/metrics_test.go b/modules/caddyhttp/metrics_test.go index 4a0519b87..4e1aa8b30 100644 --- a/modules/caddyhttp/metrics_test.go +++ b/modules/caddyhttp/metrics_test.go @@ -9,8 +9,9 @@ import ( "sync" "testing" - "github.com/caddyserver/caddy/v2" "github.com/prometheus/client_golang/prometheus/testutil" + + "github.com/caddyserver/caddy/v2" ) func TestServerNameFromContext(t *testing.T) { diff --git a/modules/caddyhttp/replacer.go b/modules/caddyhttp/replacer.go index 776aa6294..69779e6ed 100644 --- a/modules/caddyhttp/replacer.go +++ b/modules/caddyhttp/replacer.go @@ -363,13 +363,13 @@ func addHTTPVarsToReplacer(repl *caddy.Replacer, req *http.Request, w http.Respo } } - switch { - case key == "http.shutting_down": + switch key { + case "http.shutting_down": server := req.Context().Value(ServerCtxKey).(*Server) server.shutdownAtMu.RLock() defer server.shutdownAtMu.RUnlock() return !server.shutdownAt.IsZero(), true - case key == "http.time_until_shutdown": + case "http.time_until_shutdown": server := req.Context().Value(ServerCtxKey).(*Server) server.shutdownAtMu.RLock() defer server.shutdownAtMu.RUnlock() diff --git a/modules/caddyhttp/reverseproxy/caddyfile.go b/modules/caddyhttp/reverseproxy/caddyfile.go index d0947197a..8439d1d51 100644 --- a/modules/caddyhttp/reverseproxy/caddyfile.go +++ b/modules/caddyhttp/reverseproxy/caddyfile.go @@ -665,9 +665,10 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { if d.NextArg() { return d.ArgErr() } - if subdir == "request_buffers" { + switch subdir { + case "request_buffers": h.RequestBuffers = size - } else if subdir == "response_buffers" { + case "response_buffers": h.ResponseBuffers = size } diff --git a/modules/caddyhttp/reverseproxy/command.go b/modules/caddyhttp/reverseproxy/command.go index f9304efa2..54955bcf7 100644 --- a/modules/caddyhttp/reverseproxy/command.go +++ b/modules/caddyhttp/reverseproxy/command.go @@ -122,9 +122,10 @@ func cmdReverseProxy(fs caddycmd.Flags) (int, error) { } } if fromAddr.Port == "" { - if fromAddr.Scheme == "http" { + switch fromAddr.Scheme { + case "http": fromAddr.Port = httpPort - } else if fromAddr.Scheme == "https" { + case "https": fromAddr.Port = httpsPort } } diff --git a/modules/caddyhttp/reverseproxy/healthchecks.go b/modules/caddyhttp/reverseproxy/healthchecks.go index f018f40ca..ac42570b2 100644 --- a/modules/caddyhttp/reverseproxy/healthchecks.go +++ b/modules/caddyhttp/reverseproxy/healthchecks.go @@ -484,7 +484,7 @@ func (h *Handler) doActiveHealthCheck(dialInfo DialInfo, hostAddr string, networ markHealthy := func() { // increment passes and then check if it has reached the threshold to be healthy - err := upstream.Host.countHealthPass(1) + err := upstream.countHealthPass(1) if err != nil { if c := h.HealthChecks.Active.logger.Check(zapcore.ErrorLevel, "could not count active health pass"); c != nil { c.Write( diff --git a/modules/caddyhttp/reverseproxy/reverseproxy.go b/modules/caddyhttp/reverseproxy/reverseproxy.go index f07d3daeb..88fba55a1 100644 --- a/modules/caddyhttp/reverseproxy/reverseproxy.go +++ b/modules/caddyhttp/reverseproxy/reverseproxy.go @@ -1150,7 +1150,7 @@ func (lb LoadBalancing) tryAgain(ctx caddy.Context, start time.Time, retries int // we have to assume the upstream received the request, and // retries need to be carefully decided, because some requests // are not idempotent - if !isDialError && !(isHandlerError && errors.Is(herr, errNoUpstream)) { + if !isDialError && (!isHandlerError || !errors.Is(herr, errNoUpstream)) { if lb.RetryMatch == nil && req.Method != "GET" { // by default, don't retry requests if they aren't GET return false diff --git a/modules/caddyhttp/reverseproxy/selectionpolicies.go b/modules/caddyhttp/reverseproxy/selectionpolicies.go index fcf7f90f6..585fc3400 100644 --- a/modules/caddyhttp/reverseproxy/selectionpolicies.go +++ b/modules/caddyhttp/reverseproxy/selectionpolicies.go @@ -219,10 +219,7 @@ func (r RandomChoiceSelection) Validate() error { // Select returns an available host, if any. func (r RandomChoiceSelection) Select(pool UpstreamPool, _ *http.Request, _ http.ResponseWriter) *Upstream { - k := r.Choose - if k > len(pool) { - k = len(pool) - } + k := min(r.Choose, len(pool)) choices := make([]*Upstream, k) for i, upstream := range pool { if !upstream.Available() { @@ -808,7 +805,7 @@ func leastRequests(upstreams []*Upstream) *Upstream { return nil } var best []*Upstream - var bestReqs int = -1 + bestReqs := -1 for _, upstream := range upstreams { if upstream == nil { continue diff --git a/modules/caddyhttp/reverseproxy/upstreams_test.go b/modules/caddyhttp/reverseproxy/upstreams_test.go index 48e2d2a63..8caf8696a 100644 --- a/modules/caddyhttp/reverseproxy/upstreams_test.go +++ b/modules/caddyhttp/reverseproxy/upstreams_test.go @@ -52,5 +52,4 @@ func TestResolveIpVersion(t *testing.T) { t.Errorf("resolveIpVersion(): Expected %s got %s", test.expectedIpVersion, ipVersion) } } - } diff --git a/modules/caddyhttp/rewrite/rewrite.go b/modules/caddyhttp/rewrite/rewrite.go index 31ebfb430..2b18744db 100644 --- a/modules/caddyhttp/rewrite/rewrite.go +++ b/modules/caddyhttp/rewrite/rewrite.go @@ -377,11 +377,7 @@ func buildQueryString(qs string, repl *caddy.Replacer) string { // performed in normalized/unescaped space. func trimPathPrefix(escapedPath, prefix string) string { var iPath, iPrefix int - for { - if iPath >= len(escapedPath) || iPrefix >= len(prefix) { - break - } - + for iPath < len(escapedPath) && iPrefix < len(prefix) { prefixCh := prefix[iPrefix] ch := string(escapedPath[iPath]) diff --git a/modules/caddyhttp/server_test.go b/modules/caddyhttp/server_test.go index 53f35368f..6ce09974b 100644 --- a/modules/caddyhttp/server_test.go +++ b/modules/caddyhttp/server_test.go @@ -171,6 +171,7 @@ func BenchmarkServer_LogRequest_WithTrace(b *testing.B) { s.logRequest(accLog, req, wrec, &duration, repl, bodyReader, false) } } + func TestServer_TrustedRealClientIP_NoTrustedHeaders(t *testing.T) { req := httptest.NewRequest("GET", "/", nil) req.RemoteAddr = "192.0.2.1:12345" diff --git a/modules/caddytls/automation.go b/modules/caddytls/automation.go index 6f3b98a3e..c9274878a 100644 --- a/modules/caddytls/automation.go +++ b/modules/caddytls/automation.go @@ -388,10 +388,8 @@ func (ap *AutomationPolicy) onlyInternalIssuer() bool { // isWildcardOrDefault determines if the subjects include any wildcard domains, // or is the "default" policy (i.e. no subjects) which is unbounded. func (ap *AutomationPolicy) isWildcardOrDefault() bool { - isWildcardOrDefault := false - if len(ap.subjects) == 0 { - isWildcardOrDefault = true - } + isWildcardOrDefault := len(ap.subjects) == 0 + for _, sub := range ap.subjects { if strings.HasPrefix(sub, "*") { isWildcardOrDefault = true diff --git a/modules/caddytls/connpolicy.go b/modules/caddytls/connpolicy.go index 2f4dcf3c5..018ef243f 100644 --- a/modules/caddytls/connpolicy.go +++ b/modules/caddytls/connpolicy.go @@ -989,6 +989,48 @@ func (l *LeafCertClientAuth) Provision(ctx caddy.Context) error { return nil } +// UnmarshalCaddyfile implements caddyfile.Unmarshaler. +func (l *LeafCertClientAuth) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { + d.NextArg() + + // accommodate the use of one-liners + if d.CountRemainingArgs() > 1 { + d.NextArg() + modName := d.Val() + mod, err := caddyfile.UnmarshalModule(d, "tls.leaf_cert_loader."+modName) + if err != nil { + return d.WrapErr(err) + } + vMod, ok := mod.(LeafCertificateLoader) + if !ok { + return fmt.Errorf("leaf module '%s' is not a leaf certificate loader", vMod) + } + l.LeafCertificateLoadersRaw = append( + l.LeafCertificateLoadersRaw, + caddyconfig.JSONModuleObject(vMod, "loader", modName, nil), + ) + return nil + } + + // accommodate the use of nested blocks + for nesting := d.Nesting(); d.NextBlock(nesting); { + modName := d.Val() + mod, err := caddyfile.UnmarshalModule(d, "tls.leaf_cert_loader."+modName) + if err != nil { + return d.WrapErr(err) + } + vMod, ok := mod.(LeafCertificateLoader) + if !ok { + return fmt.Errorf("leaf module '%s' is not a leaf certificate loader", vMod) + } + l.LeafCertificateLoadersRaw = append( + l.LeafCertificateLoadersRaw, + caddyconfig.JSONModuleObject(vMod, "loader", modName, nil), + ) + } + return nil +} + func (l LeafCertClientAuth) VerifyClientCertificate(rawCerts [][]byte, _ [][]*x509.Certificate) error { if len(rawCerts) == 0 { return fmt.Errorf("no client certificate provided") @@ -1050,6 +1092,7 @@ var secretsLogPool = caddy.NewUsagePool() var ( _ caddyfile.Unmarshaler = (*ClientAuthentication)(nil) _ caddyfile.Unmarshaler = (*ConnectionPolicy)(nil) + _ caddyfile.Unmarshaler = (*LeafCertClientAuth)(nil) ) // ParseCaddyfileNestedMatcherSet parses the Caddyfile tokens for a nested diff --git a/modules/caddytls/leaffileloader.go b/modules/caddytls/leaffileloader.go index 1d3f3a3e5..c2177ab55 100644 --- a/modules/caddytls/leaffileloader.go +++ b/modules/caddytls/leaffileloader.go @@ -21,6 +21,7 @@ import ( "os" "github.com/caddyserver/caddy/v2" + "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" ) func init() { @@ -32,6 +33,14 @@ type LeafFileLoader struct { Files []string `json:"files,omitempty"` } +// CaddyModule returns the Caddy module information. +func (LeafFileLoader) CaddyModule() caddy.ModuleInfo { + return caddy.ModuleInfo{ + ID: "tls.leaf_cert_loader.file", + New: func() caddy.Module { return new(LeafFileLoader) }, + } +} + // Provision implements caddy.Provisioner. func (fl *LeafFileLoader) Provision(ctx caddy.Context) error { repl, ok := ctx.Value(caddy.ReplacerCtxKey).(*caddy.Replacer) @@ -44,12 +53,11 @@ func (fl *LeafFileLoader) Provision(ctx caddy.Context) error { return nil } -// CaddyModule returns the Caddy module information. -func (LeafFileLoader) CaddyModule() caddy.ModuleInfo { - return caddy.ModuleInfo{ - ID: "tls.leaf_cert_loader.file", - New: func() caddy.Module { return new(LeafFileLoader) }, - } +// UnmarshalCaddyfile implements caddyfile.Unmarshaler. +func (fl *LeafFileLoader) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { + d.NextArg() + fl.Files = append(fl.Files, d.RemainingArgs()...) + return nil } // LoadLeafCertificates returns the certificates to be loaded by fl. @@ -96,4 +104,5 @@ func convertPEMFilesToDERBytes(filename string) ([]byte, error) { var ( _ LeafCertificateLoader = (*LeafFileLoader)(nil) _ caddy.Provisioner = (*LeafFileLoader)(nil) + _ caddyfile.Unmarshaler = (*LeafFileLoader)(nil) ) diff --git a/modules/caddytls/leaffolderloader.go b/modules/caddytls/leaffolderloader.go index 5c7b06e76..20f5aa82c 100644 --- a/modules/caddytls/leaffolderloader.go +++ b/modules/caddytls/leaffolderloader.go @@ -22,6 +22,7 @@ import ( "strings" "github.com/caddyserver/caddy/v2" + "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" ) func init() { @@ -55,6 +56,13 @@ func (fl *LeafFolderLoader) Provision(ctx caddy.Context) error { return nil } +// UnmarshalCaddyfile implements caddyfile.Unmarshaler. +func (fl *LeafFolderLoader) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { + d.NextArg() + fl.Folders = append(fl.Folders, d.RemainingArgs()...) + return nil +} + // LoadLeafCertificates loads all the leaf certificates in the directories // listed in fl from all files ending with .pem. func (fl LeafFolderLoader) LoadLeafCertificates() ([]*x509.Certificate, error) { @@ -94,4 +102,5 @@ func (fl LeafFolderLoader) LoadLeafCertificates() ([]*x509.Certificate, error) { var ( _ LeafCertificateLoader = (*LeafFolderLoader)(nil) _ caddy.Provisioner = (*LeafFolderLoader)(nil) + _ caddyfile.Unmarshaler = (*LeafFolderLoader)(nil) ) diff --git a/modules/caddytls/leafpemloader.go b/modules/caddytls/leafpemloader.go index 28467ccf2..de24bcc9f 100644 --- a/modules/caddytls/leafpemloader.go +++ b/modules/caddytls/leafpemloader.go @@ -19,6 +19,7 @@ import ( "fmt" "github.com/caddyserver/caddy/v2" + "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" ) func init() { @@ -52,6 +53,13 @@ func (LeafPEMLoader) CaddyModule() caddy.ModuleInfo { } } +// UnmarshalCaddyfile implements caddyfile.Unmarshaler. +func (fl *LeafPEMLoader) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { + d.NextArg() + fl.Certificates = append(fl.Certificates, d.RemainingArgs()...) + return nil +} + // LoadLeafCertificates returns the certificates contained in pl. func (pl LeafPEMLoader) LoadLeafCertificates() ([]*x509.Certificate, error) { certs := make([]*x509.Certificate, 0, len(pl.Certificates)) diff --git a/modules/logging/filewriter_test.go b/modules/logging/filewriter_test.go index f9072f98a..2a246156c 100644 --- a/modules/logging/filewriter_test.go +++ b/modules/logging/filewriter_test.go @@ -317,7 +317,7 @@ func TestFileModeToJSON(t *testing.T) { }{ { name: "none zero", - mode: 0644, + mode: 0o644, want: `"0644"`, wantErr: false, }, @@ -358,7 +358,7 @@ func TestFileModeModification(t *testing.T) { defer os.RemoveAll(dir) fpath := path.Join(dir, "test.log") - f_tmp, err := os.OpenFile(fpath, os.O_WRONLY|os.O_APPEND|os.O_CREATE, os.FileMode(0600)) + f_tmp, err := os.OpenFile(fpath, os.O_WRONLY|os.O_APPEND|os.O_CREATE, os.FileMode(0o600)) if err != nil { t.Fatalf("failed to create test file: %v", err) } diff --git a/modules/logging/filters_test.go b/modules/logging/filters_test.go index 8f7ba0d70..a929617d7 100644 --- a/modules/logging/filters_test.go +++ b/modules/logging/filters_test.go @@ -3,9 +3,10 @@ package logging import ( "testing" + "go.uber.org/zap/zapcore" + "github.com/caddyserver/caddy/v2" "github.com/caddyserver/caddy/v2/modules/caddyhttp" - "go.uber.org/zap/zapcore" ) func TestIPMaskSingleValue(t *testing.T) {