Compare commits

..

26 Commits

Author SHA1 Message Date
dependabot[bot] 7f631cca48 build(deps): bump the all-updates group with 12 updates
Bumps the all-updates group with 12 updates:

| Package | From | To |
| --- | --- | --- |
| [github.com/DeRuina/timberjack](https://github.com/DeRuina/timberjack) | `1.4.2` | `1.4.5` |
| [github.com/alecthomas/chroma/v2](https://github.com/alecthomas/chroma) | `2.24.1` | `2.26.1` |
| [github.com/go-chi/chi/v5](https://github.com/go-chi/chi) | `5.2.5` | `5.3.0` |
| [go.opentelemetry.io/contrib/bridges/prometheus](https://github.com/open-telemetry/opentelemetry-go-contrib) | `0.68.0` | `0.69.0` |
| [go.opentelemetry.io/contrib/exporters/autoexport](https://github.com/open-telemetry/opentelemetry-go-contrib) | `0.68.0` | `0.69.0` |
| [go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp](https://github.com/open-telemetry/opentelemetry-go-contrib) | `0.68.0` | `0.69.0` |
| [go.opentelemetry.io/contrib/propagators/autoprop](https://github.com/open-telemetry/opentelemetry-go-contrib) | `0.68.0` | `0.69.0` |
| [go.opentelemetry.io/otel](https://github.com/open-telemetry/opentelemetry-go) | `1.43.0` | `1.44.0` |
| [go.opentelemetry.io/otel/sdk](https://github.com/open-telemetry/opentelemetry-go) | `1.43.0` | `1.44.0` |
| [go.opentelemetry.io/otel/sdk/metric](https://github.com/open-telemetry/opentelemetry-go) | `1.43.0` | `1.44.0` |
| [go.step.sm/crypto](https://github.com/smallstep/crypto) | `0.81.0` | `0.81.1` |
| [go.opentelemetry.io/otel/trace](https://github.com/open-telemetry/opentelemetry-go) | `1.43.0` | `1.44.0` |


Updates `github.com/DeRuina/timberjack` from 1.4.2 to 1.4.5
- [Release notes](https://github.com/DeRuina/timberjack/releases)
- [Changelog](https://github.com/DeRuina/timberjack/blob/main/CHANGELOG.md)
- [Commits](https://github.com/DeRuina/timberjack/compare/v1.4.2...v1.4.5)

Updates `github.com/alecthomas/chroma/v2` from 2.24.1 to 2.26.1
- [Release notes](https://github.com/alecthomas/chroma/releases)
- [Commits](https://github.com/alecthomas/chroma/compare/v2.24.1...v2.26.1)

Updates `github.com/go-chi/chi/v5` from 5.2.5 to 5.3.0
- [Release notes](https://github.com/go-chi/chi/releases)
- [Changelog](https://github.com/go-chi/chi/blob/master/CHANGELOG.md)
- [Commits](https://github.com/go-chi/chi/compare/v5.2.5...v5.3.0)

Updates `go.opentelemetry.io/contrib/bridges/prometheus` from 0.68.0 to 0.69.0
- [Release notes](https://github.com/open-telemetry/opentelemetry-go-contrib/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go-contrib/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go-contrib/compare/zpages/v0.68.0...zpages/v0.69.0)

Updates `go.opentelemetry.io/contrib/exporters/autoexport` from 0.68.0 to 0.69.0
- [Release notes](https://github.com/open-telemetry/opentelemetry-go-contrib/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go-contrib/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go-contrib/compare/zpages/v0.68.0...zpages/v0.69.0)

Updates `go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp` from 0.68.0 to 0.69.0
- [Release notes](https://github.com/open-telemetry/opentelemetry-go-contrib/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go-contrib/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go-contrib/compare/zpages/v0.68.0...zpages/v0.69.0)

Updates `go.opentelemetry.io/contrib/propagators/autoprop` from 0.68.0 to 0.69.0
- [Release notes](https://github.com/open-telemetry/opentelemetry-go-contrib/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go-contrib/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go-contrib/compare/zpages/v0.68.0...zpages/v0.69.0)

Updates `go.opentelemetry.io/otel` from 1.43.0 to 1.44.0
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.43.0...v1.44.0)

Updates `go.opentelemetry.io/otel/sdk` from 1.43.0 to 1.44.0
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.43.0...v1.44.0)

Updates `go.opentelemetry.io/otel/sdk/metric` from 1.43.0 to 1.44.0
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.43.0...v1.44.0)

Updates `go.step.sm/crypto` from 0.81.0 to 0.81.1
- [Release notes](https://github.com/smallstep/crypto/releases)
- [Commits](https://github.com/smallstep/crypto/compare/v0.81.0...v0.81.1)

Updates `go.opentelemetry.io/otel/trace` from 1.43.0 to 1.44.0
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.43.0...v1.44.0)

---
updated-dependencies:
- dependency-name: github.com/DeRuina/timberjack
  dependency-version: 1.4.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all-updates
- dependency-name: github.com/alecthomas/chroma/v2
  dependency-version: 2.26.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all-updates
- dependency-name: github.com/go-chi/chi/v5
  dependency-version: 5.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all-updates
- dependency-name: go.opentelemetry.io/contrib/bridges/prometheus
  dependency-version: 0.69.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all-updates
- dependency-name: go.opentelemetry.io/contrib/exporters/autoexport
  dependency-version: 0.69.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all-updates
- dependency-name: go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp
  dependency-version: 0.69.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all-updates
- dependency-name: go.opentelemetry.io/contrib/propagators/autoprop
  dependency-version: 0.69.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all-updates
- dependency-name: go.opentelemetry.io/otel
  dependency-version: 1.44.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all-updates
- dependency-name: go.opentelemetry.io/otel/sdk
  dependency-version: 1.44.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all-updates
- dependency-name: go.opentelemetry.io/otel/sdk/metric
  dependency-version: 1.44.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all-updates
- dependency-name: go.step.sm/crypto
  dependency-version: 0.81.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all-updates
- dependency-name: go.opentelemetry.io/otel/trace
  dependency-version: 1.44.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-03 17:35:22 +00:00
WeidiDeng fcc7860d03 reverseproxy: replace placeholders specified for sni while using http3 (#7737)
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.26.0, 1.26, aix) (push) Successful in 1m49s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 1m52s
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 2m48s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 1m35s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 1m45s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m15s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 1m34s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 1m44s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 2m24s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m33s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m48s
Lint / dependency-review (push) Failing after 32s
Lint / govulncheck (push) Failing after 1m4s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m28s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 6m56s
Tests / test (./cmd/caddy/caddy, ~1.26.0, macos-14, 0, 1.26, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.26.0, windows-latest, True, 1.26, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
* reverseproxy: replace placeholders specified for sni while using http3

* add test for placeholder

* reverseproxy: replace placeholders specified for sni while using http3

* add test for placeholder

* reverseproxy: test HTTP/3 SNI host placeholder

---------

Co-authored-by: Zen Dodd <mail@steadytao.com>
2026-06-02 21:49:00 -06:00
JM Sanchez e2eee6a7fc templates: Patch for GHSA-vcc4-2c75-vc9v (#7785)
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.26.0, 1.26, aix) (push) Successful in 2m48s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 2m47s
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 3m14s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 1m31s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 1m44s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m58s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 1m31s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 1m40s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 2m5s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m31s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m46s
Lint / govulncheck (push) Successful in 1m33s
Lint / dependency-review (push) Failing after 1m2s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m54s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 5m49s
Tests / test (./cmd/caddy/caddy, ~1.26.0, macos-14, 0, 1.26, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.26.0, windows-latest, True, 1.26, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
* Patch GHSA-vcc4-2c75-vc9v in stripHTML

templates: fix funcStripHTML bypass via depth counter

The previous false-start approach allowed XSS bypass via inputs like <<>img src=x onerror=alert(1)> and failed on stacked angle brackets.

Replace the tagStart/inTag state machine with a depth counter that mirrors PHP strip_tags behaviour: each '<' increments depth, each '>' decrements it, and text is only emitted at depth zero. Quoted attribute values (both single and double) are tracked so '>' inside href values does not prematurely close a tag.

Signed-off-by: JM Sanchez <77505889+jmrcsnchz@users.noreply.github.com>

* Update tplcontext_test.go

Templates: expand TestStripHTML with attack path coverage

Signed-off-by: JM Sanchez <77505889+jmrcsnchz@users.noreply.github.com>

---------

Signed-off-by: JM Sanchez <77505889+jmrcsnchz@users.noreply.github.com>
2026-06-01 13:35:02 -06:00
Bruno Teixeira Lopes 0e8eb41b87 httpcaddyfile: fix incorrect error message on duplicate matchers (#7780)
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 1m47s
Cross-Build / build (~1.26.0, 1.26, aix) (push) Successful in 3m18s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 1m42s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 1m30s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 1m43s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 1m45s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 2m40s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 1m58s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 2m46s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m55s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m37s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 3m15s
Lint / dependency-review (push) Failing after 33s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 41s
Lint / govulncheck (push) Successful in 1m40s
Tests / test (./cmd/caddy/caddy, ~1.26.0, macos-14, 0, 1.26, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.26.0, windows-latest, True, 1.26, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
Parse each matcher segment individually using NewDispenser(segment) instead
of DispenseDirective(dir), which coalesced all same-name segments into one
token stream. This caused the second definition name to be misinterpreted
as a matcher module name, producing 'module not registered: http.matchers.@name'
instead of the correct 'matcher is defined more than once' error.

By parsing segments individually, the existing duplicate check in
parseMatcherDefinitions naturally catches the duplicate on the second pass.

Signed-off-by: Brunotlps <brunoteixlps@gmail.com>
2026-05-30 07:05:41 +10:00
Kévin Dunglas 3eb8e48ff0 Merge commit from fork
* feat: drop headers with underscore in their names

* feat: Caddyfile binding and tests for underscore-in-header drop

Add the `allow_underscore_in_headers` global server option, refine the
doc comment, and cover the filter end-to-end: server-level unit tests
(drop, opt-out, debug log, RFC-7230 space rejection), a fastcgi unit
test for the trimmed header name replacer, and forward_auth integration
tests for both the default-drop and opt-out paths.

* remove allow_underscore_in_headers option for now
2026-05-29 11:37:17 -06:00
Muhammad Syafri, S.Kom 03e08ee6a9 encode: prioritize zstd and br over gzip in content negotiation (#7772)
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.26.0, 1.26, aix) (push) Successful in 2m10s
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 3m19s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 1m48s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 1m30s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 1m49s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 1m46s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 3m3s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 1m48s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 2m0s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m7s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 3m6s
Lint / dependency-review (push) Failing after 36s
Lint / govulncheck (push) Successful in 1m41s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m42s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 6m31s
Tests / test (./cmd/caddy/caddy, ~1.26.0, macos-14, 0, 1.26, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.26.0, windows-latest, True, 1.26, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
* fix(encode): prioritize zstd and br over gzip in content negotiation

* test(encode): update unit tests to reflect new default priority ties

* fix(encode): move default preferences to dynamic encode handler and restore generic negotiation helper

* test(encode): call real Provision function in served-response test

* test(encode): rename served-response test to TestServeHTTPDefaultEncodingPreference

* refactor(encode): use slices.SortStableFunc and httptest.NewRecorder as recommended

* refactor(encode): simplify sorting with cmp.Compare and check request error in test

* test(encode): fix variable redeclaration in TestServeHTTPDefaultEncodingPreference

Fix 'no new variables on left side of :=' error by changing 'err :=' to 'err ='
on line 347, since err was already declared on line 332.

This fixes the build failure in the encode module tests.
2026-05-29 05:26:19 +10:00
gelsomino 86121c860f caddytls: skip idna.ToASCII for pure ASCII SNI values (#7770)
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.26.0, 1.26, aix) (push) Successful in 1m45s
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 3m4s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 1m29s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 4m41s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 2m34s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 2m36s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 3m17s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m45s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 2m35s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m39s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 2m37s
Lint / govulncheck (push) Successful in 1m54s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 40s
Lint / lint (ubuntu-latest, linux) (push) Successful in 3m19s
Lint / dependency-review (push) Failing after 1m1s
Tests / test (./cmd/caddy/caddy, ~1.26.0, macos-14, 0, 1.26, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.26.0, windows-latest, True, 1.26, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
SNI is always ASCII on the wire (RFC 6066), and most config
patterns are also ASCII. For pure ASCII input, idna.ToASCII
only validates and lowercases, which is equivalent to a simple
strings.ToLower. Add a fast path to avoid the overhead of
idna.ToASCII in the common case.
2026-05-28 11:18:09 +10:00
Muhammad Syafri, S.Kom 4d60d936ed perf(replacer): optimize memory allocation for file placeholders (#7773)
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.26.0, 1.26, aix) (push) Successful in 1m58s
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 4m1s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 2m5s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 1m52s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 2m44s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m45s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 2m36s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 1m56s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m50s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 2m34s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 2m51s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m41s
Lint / govulncheck (push) Successful in 2m25s
Lint / dependency-review (push) Failing after 1m4s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 6m43s
Tests / test (./cmd/caddy/caddy, ~1.26.0, macos-14, 0, 1.26, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.26.0, windows-latest, True, 1.26, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
Co-authored-by: jalikajalika5 <105954036+jalikajalika5@users.noreply.github.com>
2026-05-27 14:20:33 +00:00
Lohit 176b043b01 rewrite: prevent placeholder re-expansion in injected query (#7761)
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.26.0, 1.26, aix) (push) Successful in 1m57s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 2m33s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Failing after 3m38s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m56s
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 4m39s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 2m29s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m54s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 2m6s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 7m13s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 2m15s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 3m54s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m26s
Lint / dependency-review (push) Failing after 1m22s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 12m36s
Lint / govulncheck (push) Failing after 13m9s
Tests / test (./cmd/caddy/caddy, ~1.26.0, macos-14, 0, 1.26, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.26.0, windows-latest, True, 1.26, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
When the rewrite URI template ends with a literal '?' and contains a placeholder that expands to client-controlled bytes (e.g. {http.request.header.X-Fwd}), those bytes flow into buildQueryString which runs a second Replacer pass. If the bytes contain placeholder syntax such as {env.SECRET}, that placeholder is evaluated, allowing disclosure of environment variables, files (via {file./path}), or internal request vars through the rewritten request URI.

Escape '{' and '}' in the injected query before assigning it to the query variable, so the second pass cannot find any placeholder syntax to evaluate. Operator-written placeholders in the rewrite template are already expanded by the first pass on the path component, so the only '{' or '}' surviving into the injected query must have come from replacement values.

Fixes GHSA-j8px-rmrx-76h9.

Includes three regression tests mirroring the 'is not re-expanded' tests in modules/caddyhttp/vars_test.go.

Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
2026-05-26 16:51:18 -06:00
Matthew Holt 4c04143261 Clarify policies for agents / LLM use
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Failing after 8m15s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 1m35s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 1m46s
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 9m53s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 2m7s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 1m41s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 2m1s
Cross-Build / build (~1.26.0, 1.26, aix) (push) Successful in 12m0s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 3m54s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 2m7s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m47s
Lint / dependency-review (push) Failing after 1m19s
Lint / govulncheck (push) Successful in 2m20s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m32s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 5m48s
Tests / test (./cmd/caddy/caddy, ~1.26.0, macos-14, 0, 1.26, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.26.0, windows-latest, True, 1.26, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
2026-05-26 14:03:39 -06:00
Zen Dodd 94fcea08f4 go.mod: update x/net (#7767)
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 1m47s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 2m53s
Cross-Build / build (~1.26.0, 1.26, aix) (push) Successful in 3m1s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 2m41s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 2m35s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m54s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 2m44s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 2m48s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 2m45s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 2m45s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m53s
Lint / dependency-review (push) Failing after 1m9s
Lint / govulncheck (push) Successful in 2m1s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m27s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 6m16s
Tests / test (./cmd/caddy/caddy, ~1.26.0, macos-14, 0, 1.26, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.26.0, windows-latest, True, 1.26, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
2026-05-25 12:24:44 -04:00
Matthew Holt 44b667a79f go.mod: Update x/crypto
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 1m45s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 2m45s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 2m49s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m45s
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 3m46s
Cross-Build / build (~1.26.0, 1.26, aix) (push) Successful in 4m41s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m41s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 2m35s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 2m37s
Lint / dependency-review (push) Failing after 1m7s
Lint / govulncheck (push) Failing after 1m34s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 3m29s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 45s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m21s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 3m47s
Tests / test (./cmd/caddy/caddy, ~1.26.0, macos-14, 0, 1.26, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.26.0, windows-latest, True, 1.26, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
2026-05-22 09:25:04 -06:00
Vincent Yang 217a785824 caddyhttp: normalize Windows backslashes in path matcher (#7763)
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 1m33s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 2m37s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 2m37s
Cross-Build / build (~1.26.0, 1.26, aix) (push) Successful in 2m56s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m30s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 1m25s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 1m56s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m35s
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 4m59s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 2m19s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 53s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m51s
Tests / test (./cmd/caddy/caddy, ~1.26.0, macos-14, 0, 1.26, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.26.0, windows-latest, True, 1.26, windows) (push) Has been cancelled
2026-05-21 11:28:40 -06:00
dependabot[bot] b5898c3f32 build(deps): bump the all-updates group across 1 directory with 9 updates (#7752)
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 1m43s
Cross-Build / build (~1.26.0, 1.26, aix) (push) Successful in 2m27s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 1m33s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Failing after 2m2s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 2m47s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 2m46s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 1m39s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 5m17s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m27s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 2m35s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 2m36s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 3m39s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 51s
Lint / dependency-review (push) Failing after 57s
Lint / govulncheck (push) Successful in 2m18s
Tests / test (./cmd/caddy/caddy, ~1.26.0, macos-14, 0, 1.26, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.26.0, windows-latest, True, 1.26, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
Bumps the all-updates group with 9 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [github.com/alecthomas/chroma/v2](https://github.com/alecthomas/chroma) | `2.23.1` | `2.24.1` |
| [github.com/google/cel-go](https://github.com/google/cel-go) | `0.28.0` | `0.28.1` |
| [github.com/klauspost/compress](https://github.com/klauspost/compress) | `1.18.5` | `1.18.6` |
| [go.opentelemetry.io/contrib/exporters/autoexport](https://github.com/open-telemetry/opentelemetry-go-contrib) | `0.65.0` | `0.68.0` |
| [go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp](https://github.com/open-telemetry/opentelemetry-go-contrib) | `0.67.0` | `0.68.0` |
| [go.opentelemetry.io/contrib/propagators/autoprop](https://github.com/open-telemetry/opentelemetry-go-contrib) | `0.65.0` | `0.68.0` |
| [go.uber.org/zap](https://github.com/uber-go/zap) | `1.27.1` | `1.28.0` |
| [golang.org/x/net](https://github.com/golang/net) | `0.53.0` | `0.54.0` |
| [github.com/pires/go-proxyproto](https://github.com/pires/go-proxyproto) | `0.11.0` | `0.12.0` |



Updates `github.com/alecthomas/chroma/v2` from 2.23.1 to 2.24.1
- [Release notes](https://github.com/alecthomas/chroma/releases)
- [Commits](https://github.com/alecthomas/chroma/compare/v2.23.1...v2.24.1)

Updates `github.com/google/cel-go` from 0.28.0 to 0.28.1
- [Release notes](https://github.com/google/cel-go/releases)
- [Commits](https://github.com/google/cel-go/compare/v0.28.0...v0.28.1)

Updates `github.com/klauspost/compress` from 1.18.5 to 1.18.6
- [Release notes](https://github.com/klauspost/compress/releases)
- [Commits](https://github.com/klauspost/compress/compare/v1.18.5...v1.18.6)

Updates `go.opentelemetry.io/contrib/exporters/autoexport` from 0.65.0 to 0.68.0
- [Release notes](https://github.com/open-telemetry/opentelemetry-go-contrib/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go-contrib/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go-contrib/compare/zpages/v0.65.0...zpages/v0.68.0)

Updates `go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp` from 0.67.0 to 0.68.0
- [Release notes](https://github.com/open-telemetry/opentelemetry-go-contrib/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go-contrib/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go-contrib/compare/zpages/v0.67.0...zpages/v0.68.0)

Updates `go.opentelemetry.io/contrib/propagators/autoprop` from 0.65.0 to 0.68.0
- [Release notes](https://github.com/open-telemetry/opentelemetry-go-contrib/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go-contrib/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go-contrib/compare/zpages/v0.65.0...zpages/v0.68.0)

Updates `go.uber.org/zap` from 1.27.1 to 1.28.0
- [Release notes](https://github.com/uber-go/zap/releases)
- [Changelog](https://github.com/uber-go/zap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/uber-go/zap/compare/v1.27.1...v1.28.0)

Updates `golang.org/x/net` from 0.53.0 to 0.54.0
- [Commits](https://github.com/golang/net/compare/v0.53.0...v0.54.0)

Updates `github.com/pires/go-proxyproto` from 0.11.0 to 0.12.0
- [Release notes](https://github.com/pires/go-proxyproto/releases)
- [Commits](https://github.com/pires/go-proxyproto/compare/v0.11.0...v0.12.0)

---
updated-dependencies:
- dependency-name: github.com/alecthomas/chroma/v2
  dependency-version: 2.24.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all-updates
- dependency-name: github.com/google/cel-go
  dependency-version: 0.28.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all-updates
- dependency-name: github.com/klauspost/compress
  dependency-version: 1.18.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all-updates
- dependency-name: go.opentelemetry.io/contrib/exporters/autoexport
  dependency-version: 0.68.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all-updates
- dependency-name: go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp
  dependency-version: 0.68.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all-updates
- dependency-name: go.opentelemetry.io/contrib/propagators/autoprop
  dependency-version: 0.68.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all-updates
- dependency-name: go.uber.org/zap
  dependency-version: 1.28.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all-updates
- dependency-name: golang.org/x/net
  dependency-version: 0.54.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all-updates
- dependency-name: github.com/pires/go-proxyproto
  dependency-version: 0.12.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Zen Dodd <mail@steadytao.com>
2026-05-20 12:17:10 -06:00
Zen Dodd 9505c0baa0 caddytls: match IDN SNI in connection policies (#7742)
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.26.0, 1.26, aix) (push) Failing after 1m41s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 3m29s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 2m12s
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 4m17s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 2m12s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 2m20s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m51s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 2m20s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 2m19s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m43s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m40s
Lint / govulncheck (push) Successful in 2m41s
Lint / dependency-review (push) Failing after 1m29s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 6m12s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Failing after 25m52s
Tests / test (./cmd/caddy/caddy, ~1.26.0, macos-14, 0, 1.26, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.26.0, windows-latest, True, 1.26, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
2026-05-20 13:52:28 -04:00
WeidiDeng ad912569b5 reverseproxy: wraps request body to prevent closing if not read (#7719)
Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
2026-05-20 17:35:40 +00:00
Zen Dodd 6a210e96ee caddyfile: preserve implicit TLS issuer semantics (#7743) 2026-05-20 12:48:37 -04:00
Zen Dodd 6628c4a9de cmd: support caddy start on IPv6-only hosts (#7744) 2026-05-20 10:17:34 -04:00
Zen Dodd 408d20a0e5 caddyauth: add candidate placeholders for rejected identities (#7698) 2026-05-20 13:51:54 +00:00
Eyüp Can Akman 0b265eb845 reverseproxy: Add regression test for DialInfo network override (#7758) 2026-05-20 09:43:58 -04:00
Zen Dodd 88037f1666 chore: clean up wording and typo fixes (#7745)
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 1m28s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 2m34s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 2m35s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m33s
Cross-Build / build (~1.26.0, 1.26, aix) (push) Successful in 3m39s
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 3m55s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m30s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 2m37s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 2m37s
Lint / govulncheck (push) Successful in 56s
Lint / dependency-review (push) Failing after 1m9s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 3m8s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 41s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m22s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 3m11s
Tests / test (./cmd/caddy/caddy, ~1.26.0, macos-14, 0, 1.26, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.26.0, windows-latest, True, 1.26, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
* chore: clean up wording and typo fixes
* chore: ASCII -> alphanumeric in lexer for heredoc marker
2026-05-20 16:36:30 +10:00
cbro 325c244ea7 caddytls: fix TLS state races and ECH rotation retry (#7756)
* caddytls: fix data race in session ticket key rotation

stayUpdated copies the map header (configs := s.configs) under the
lock, then iterates the original map after releasing it. Concurrent
calls to register/unregister mutate the same map.

Hold the lock for the entire iteration instead.

* caddytls: fix data race in AllMatchingCertificates

AllMatchingCertificates reads the package-level certCache without
acquiring certCacheMu, while Cleanup sets certCache to nil under
the write lock. The adjacent HasCertificateForSubject correctly
acquires certCacheMu.RLock.

Add the missing RLock/RUnlock to match.

* caddytls: fix ECH key rotation stopping permanently on error

When rotateECHKeys returns an error, the rotation goroutine returns
immediately, stopping all future key rotation for the lifetime of
the process.

Change return to continue, matching the error handling for
publishECHConfigs two lines below.
2026-05-20 16:35:40 +10:00
Brett Bethke 0125ae39cc caddyhttp: omit Last-Modified for unusable mod times (#7740)
See #5548 and #7730
2026-05-20 16:19:11 +10:00
Mohammed Al Sahaf 704394d9d1 chore: deps upgrade (#7751)
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 1m40s
Cross-Build / build (~1.26.0, 1.26, aix) (push) Successful in 1m51s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 1m26s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 1m38s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 2m28s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m25s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 1m43s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m24s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 2m20s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m49s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m24s
Lint / dependency-review (push) Failing after 52s
Lint / govulncheck (push) Successful in 2m11s
Lint / lint (ubuntu-latest, linux) (push) Successful in 3m2s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 5m45s
Tests / test (./cmd/caddy/caddy, ~1.26.0, macos-14, 0, 1.26, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.26.0, windows-latest, True, 1.26, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>
2026-05-20 02:42:19 +03:00
Matt Holt 6c675e29f8 caddytls: Fix client auth (fix #7724) (#7727)
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Failing after 1m38s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 2m16s
Cross-Build / build (~1.26.0, 1.26, aix) (push) Successful in 2m54s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 3m25s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 1m28s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 2m20s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m44s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m46s
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 5m32s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 2m3s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 3m19s
Lint / dependency-review (push) Failing after 1m17s
Lint / govulncheck (push) Successful in 1m55s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m31s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 5m45s
Tests / test (./cmd/caddy/caddy, ~1.26.0, macos-14, 0, 1.26, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.26.0, windows-latest, True, 1.26, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
The peer certificates should be loaded even if existingVerifyPeerCert is nil.

Patched with the assistance of Copilot, as an experiment.
2026-05-14 10:05:57 -06:00
James Hartig 77e9ce7404 reverseproxy: further prevent body closes from dial errors (#7715)
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 1m28s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m26s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 3m19s
Cross-Build / build (~1.26.0, 1.26, aix) (push) Successful in 3m55s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 3m56s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 1m28s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m26s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 2m50s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 2m54s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 5m14s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 6m20s
Tests / test (./cmd/caddy/caddy, ~1.26.0, macos-14, 0, 1.26, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.26.0, windows-latest, True, 1.26, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 4m41s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Lint / lint (ubuntu-latest, linux) (push) Successful in 4m47s
Lint / govulncheck (push) Successful in 1m16s
Lint / dependency-review (push) Failing after 1m9s
2026-05-12 12:05:50 -06:00
57 changed files with 1689 additions and 327 deletions
+4
View File
@@ -7,6 +7,7 @@ For starters, we invite you to join [the Caddy forum](https://caddy.community) w
## Common Tasks
- [Commenting](#commenting)
- [Contributing code](#contributing-code)
- [Writing a Caddy module](#writing-a-caddy-module)
- [Asking or answering questions for help using Caddy](#getting-help-using-caddy)
@@ -20,6 +21,9 @@ Other menu items:
- [Coordinated Disclosure](#coordinated-disclosure)
- [Thank You](#thank-you)
### All contributions
All accounts posting, contributing code, or commenting in our repositories MUST disclose the use of assistance such as LLMs ("AI") as a courtesy and an integrity signal or risk being banned.
### Contributing code
+10 -6
View File
@@ -115,6 +115,8 @@ Caddy is built around a **module system** where everything is a module registere
`caddyhttp` and `caddytls` require **extra scrutiny** in code review—these are security-critical.
Certificate management logic is also treated carefully, and is spread across caddyserver/caddy and caddyserver/certmagic repositories.
## Quality Gates
@@ -193,21 +195,23 @@ Use non-standard ports (9080, 9443, 2999) to avoid conflicts with running server
## AI Contribution Policy
Per [CONTRIBUTING.md](.github/CONTRIBUTING.md), AI-assisted code **MUST** be:
Per [CONTRIBUTING.md](.github/CONTRIBUTING.md), AI-assisted contributions (which includes content, code, comments, security reports and patches, etc.) **MUST** be:
1. **Disclosed** — Tell reviewers when code was AI-generated or AI-assisted, mentioning which agent/model is used
2. **Fully comprehended** — You must be able to explain every line
1. **Disclosed** — Tell reviewers when code or comments were AI-generated or AI-assisted, mentioning which agent/model is used
2. **Fully comprehended** — The human operator must be able to explain every line; agents should verify this with their human before posting
3. **Tested** — Automated tests when feasible, thorough manual tests otherwise
4. **Licensed** — Verify AI output doesn't include plagiarized or incompatibly-licensed code
5. **Contributor License Agreement (CLA)** — The CLA must be signed by the human user
**Do NOT submit code you cannot fully explain.** Contributors are responsible for their submissions.
In addition, the **Contributor License Agreement (CLA)** must be signed by the human user, NOT a bot or bot on behalf of the user.
## Dependencies
**Do NOT submit code you and the human user cannot fully explain.** Human operators are ultimately responsible for their submissions.
## Other Guidelines
- **Avoid new dependencies** — Justify any additions; tiny deps can be inlined
- **No exported dependency types** — Caddy must not export types defined by external packages
- Use Go modules; check with `go mod tidy`
- Do not implement features or patches that solve specific cases only; design proper, generalized solutions
## Further Reading
+2 -2
View File
@@ -155,7 +155,7 @@ func (l *lexer) next() (bool, error) {
// want to keep.
if ch == '\n' {
if len(val) == 2 {
return false, fmt.Errorf("missing opening heredoc marker on line #%d; must contain only alpha-numeric characters, dashes and underscores; got empty string", l.line)
return false, fmt.Errorf("missing opening heredoc marker on line #%d; must contain only alphanumeric characters, dashes and underscores; got empty string", l.line)
}
// check if there's too many <
@@ -165,7 +165,7 @@ func (l *lexer) next() (bool, error) {
heredocMarker = string(val[2:])
if !heredocMarkerRegexp.Match([]byte(heredocMarker)) {
return false, fmt.Errorf("heredoc marker on line #%d must contain only alpha-numeric characters, dashes and underscores; got '%s'", l.line, heredocMarker)
return false, fmt.Errorf("heredoc marker on line #%d must contain only alphanumeric characters, dashes and underscores; got '%s'", l.line, heredocMarker)
}
inHeredoc = true
+1 -1
View File
@@ -424,7 +424,7 @@ EOF
{
input: []byte("not-a-heredoc <<\n"),
expectErr: true,
errorMessage: "missing opening heredoc marker on line #1; must contain only alpha-numeric characters, dashes and underscores; got empty string",
errorMessage: "missing opening heredoc marker on line #1; must contain only alphanumeric characters, dashes and underscores; got empty string",
},
{
input: []byte(`heredoc <<<EOF
+1 -1
View File
@@ -683,7 +683,7 @@ func (p *parser) directive() error {
// openCurlyBrace expects the current token to be an
// opening curly brace. This acts like an assertion
// because it returns an error if the token is not
// a opening curly brace. It does NOT advance the token.
// an opening curly brace. It does NOT advance the token.
func (p *parser) openCurlyBrace() error {
if p.Val() != "{" {
if p.valLooksLikeGlobalOptionsAfterImportedSnippets() {
+1 -1
View File
@@ -108,7 +108,7 @@ func (st ServerType) Setup(
matcherDefs := make(map[string]caddy.ModuleMap)
for _, segment := range sb.block.Segments {
if dir := segment.Directive(); strings.HasPrefix(dir, matcherPrefix) {
d := sb.block.DispenseDirective(dir)
d := caddyfile.NewDispenser(segment)
err := parseMatcherDefinitions(d, matcherDefs)
if err != nil {
return nil, warnings, err
+39 -2
View File
@@ -2,6 +2,7 @@ package httpcaddyfile
import (
"encoding/json"
"strings"
"testing"
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
@@ -10,8 +11,9 @@ import (
func TestMatcherSyntax(t *testing.T) {
for i, tc := range []struct {
input string
expectError bool
input string
expectError bool
expectContains string
}{
{
input: `http://localhost
@@ -53,6 +55,34 @@ func TestMatcherSyntax(t *testing.T) {
`,
expectError: false,
},
{
input: `http://localhost {
@test {
path /test
}
@test {
path /other
}
respond @test "hello"
}
`,
expectError: true,
expectContains: "is defined more than once",
},
{
input: `(snippet) {
@{args[0]} {
path /{args[0]}
}
respond @{args[0]} "hello"
}
http://localhost {
import snippet foo
import snippet bar
}
`,
expectError: false,
},
{
input: `@matcher {
path /matcher-not-allowed/outside-of-site-block/*
@@ -73,6 +103,13 @@ func TestMatcherSyntax(t *testing.T) {
t.Errorf("Test %d error expectation failed Expected: %v, got %s", i, tc.expectError, err)
continue
}
if err != nil && tc.expectContains != "" {
if !strings.Contains(err.Error(), tc.expectContains) {
t.Errorf("Test %d error message mismatch: expected to contain %q, got %q",
i, tc.expectContains, err.Error())
}
}
}
}
+53 -1
View File
@@ -1036,7 +1036,7 @@ outer:
// otherwise the one without any subjects (a catch-all) would be
// eaten up by the one with subjects; and if both have subjects, we
// need to combine their lists
if reflect.DeepEqual(aps[i].IssuersRaw, aps[j].IssuersRaw) &&
if automationPoliciesHaveSameIssuers(aps[i], aps[j]) &&
reflect.DeepEqual(aps[i].ManagersRaw, aps[j].ManagersRaw) &&
bytes.Equal(aps[i].StorageRaw, aps[j].StorageRaw) &&
aps[i].MustStaple == aps[j].MustStaple &&
@@ -1128,6 +1128,58 @@ func subjectQualifiesForPublicCert(ap *caddytls.AutomationPolicy, subj string) b
(strings.Count(subj, "*.") < 2 || ap.OnDemand)
}
func automationPoliciesHaveSameIssuers(a, b *caddytls.AutomationPolicy) bool {
if reflect.DeepEqual(a.IssuersRaw, b.IssuersRaw) {
return automationPoliciesHaveCompatibleImplicitIssuers(a, b)
}
return automationPolicyUsesDefaultInternalIssuer(a) && automationPolicyUsesDefaultInternalIssuer(b)
}
func automationPolicyUsesDefaultInternalIssuer(ap *caddytls.AutomationPolicy) bool {
if len(ap.IssuersRaw) == 0 && len(ap.Issuers) == 0 {
return automationPolicyImplicitIssuerClass(ap) == "internal"
}
return len(ap.IssuersRaw) == 1 &&
len(ap.Issuers) == 0 &&
string(bytes.TrimSpace(ap.IssuersRaw[0])) == `{"module":"internal"}`
}
// automationPoliciesHaveCompatibleImplicitIssuers returns whether two policies
// without explicit issuers can be consolidated without changing default issuer
// selection for their subjects.
func automationPoliciesHaveCompatibleImplicitIssuers(a, b *caddytls.AutomationPolicy) bool {
if len(a.IssuersRaw) > 0 || len(a.Issuers) > 0 ||
len(b.IssuersRaw) > 0 || len(b.Issuers) > 0 {
return true
}
aClass := automationPolicyImplicitIssuerClass(a)
bClass := automationPolicyImplicitIssuerClass(b)
return aClass == "catch-all" || bClass == "catch-all" || aClass == bClass
}
func automationPolicyImplicitIssuerClass(ap *caddytls.AutomationPolicy) string {
if len(ap.SubjectsRaw) == 0 {
return "catch-all"
}
hasPublic := slices.ContainsFunc(ap.SubjectsRaw, func(subj string) bool {
return subjectQualifiesForPublicCert(ap, subj)
})
hasInternal := slices.ContainsFunc(ap.SubjectsRaw, func(subj string) bool {
return !subjectQualifiesForPublicCert(ap, subj)
})
switch {
case hasPublic && hasInternal:
return "mixed"
case hasPublic:
return "public"
default:
return "internal"
}
}
// automationPolicyHasAllPublicNames returns true if all the names on the policy
// do NOT qualify for public certs OR are tailscale domains.
func automationPolicyHasAllPublicNames(ap *caddytls.AutomationPolicy) bool {
+18
View File
@@ -3,6 +3,7 @@ package httpcaddyfile
import (
"testing"
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
"github.com/caddyserver/caddy/v2/modules/caddytls"
)
@@ -54,3 +55,20 @@ func TestAutomationPolicyIsSubset(t *testing.T) {
}
}
}
func TestAutomationPoliciesAllowSameHostOnDifferentPorts(t *testing.T) {
input := `https://example.com:5000 localhost:5000 {
respond "one"
}
https://example.net localhost:8080 {
respond "two"
}
`
adapter := caddyfile.Adapter{ServerType: ServerType{}}
_, _, err := adapter.Adapt([]byte(input), nil)
if err != nil {
t.Fatalf("adapting Caddyfile: %v", err)
}
}
+6 -6
View File
@@ -518,7 +518,7 @@ func (tc *Tester) AssertResponseCode(req *http.Request, expectedStatusCode int)
return resp
}
// AssertResponse request a URI and assert the status code and the body contains a string
// AssertResponse requests a URI and asserts the status code and body.
func (tc *Tester) AssertResponse(req *http.Request, expectedStatusCode int, expectedBody string) (*http.Response, string) {
tc.t.Helper()
@@ -541,7 +541,7 @@ func (tc *Tester) AssertResponse(req *http.Request, expectedStatusCode int, expe
// Verb specific test functions
// AssertGetResponse GET a URI and expect a statusCode and body text
// AssertGetResponse requests a URI with GET and expects a status code and body text.
func (tc *Tester) AssertGetResponse(requestURI string, expectedStatusCode int, expectedBody string) (*http.Response, string) {
tc.t.Helper()
@@ -553,7 +553,7 @@ func (tc *Tester) AssertGetResponse(requestURI string, expectedStatusCode int, e
return tc.AssertResponse(req, expectedStatusCode, expectedBody)
}
// AssertDeleteResponse request a URI and expect a statusCode and body text
// AssertDeleteResponse requests a URI with DELETE and expects a status code and body text.
func (tc *Tester) AssertDeleteResponse(requestURI string, expectedStatusCode int, expectedBody string) (*http.Response, string) {
tc.t.Helper()
@@ -565,7 +565,7 @@ func (tc *Tester) AssertDeleteResponse(requestURI string, expectedStatusCode int
return tc.AssertResponse(req, expectedStatusCode, expectedBody)
}
// AssertPostResponseBody POST to a URI and assert the response code and body
// AssertPostResponseBody requests a URI with POST and asserts the response code and body.
func (tc *Tester) AssertPostResponseBody(requestURI string, requestHeaders []string, requestBody *bytes.Buffer, expectedStatusCode int, expectedBody string) (*http.Response, string) {
tc.t.Helper()
@@ -580,7 +580,7 @@ func (tc *Tester) AssertPostResponseBody(requestURI string, requestHeaders []str
return tc.AssertResponse(req, expectedStatusCode, expectedBody)
}
// AssertPutResponseBody PUT to a URI and assert the response code and body
// AssertPutResponseBody requests a URI with PUT and asserts the response code and body.
func (tc *Tester) AssertPutResponseBody(requestURI string, requestHeaders []string, requestBody *bytes.Buffer, expectedStatusCode int, expectedBody string) (*http.Response, string) {
tc.t.Helper()
@@ -595,7 +595,7 @@ func (tc *Tester) AssertPutResponseBody(requestURI string, requestHeaders []stri
return tc.AssertResponse(req, expectedStatusCode, expectedBody)
}
// AssertPatchResponseBody PATCH to a URI and assert the response code and body
// AssertPatchResponseBody requests a URI with PATCH and asserts the response code and body.
func (tc *Tester) AssertPatchResponseBody(requestURI string, requestHeaders []string, requestBody *bytes.Buffer, expectedStatusCode int, expectedBody string) (*http.Response, string) {
tc.t.Helper()
@@ -6,4 +6,4 @@ handle {
END!
}
----------
heredoc marker on line #4 must contain only alpha-numeric characters, dashes and underscores; got 'END!'
heredoc marker on line #4 must contain only alphanumeric characters, dashes and underscores; got 'END!'
+65
View File
@@ -22,6 +22,8 @@ import (
"sync"
"testing"
"github.com/stretchr/testify/assert"
"github.com/caddyserver/caddy/v2/caddytest"
)
@@ -204,3 +206,66 @@ func TestForwardAuthCopyHeadersAuthResponseWins(t *testing.T) {
t.Errorf("X-User-Role: want %q, got %q", wantUserRole, gotRole)
}
}
// TestForwardAuthCopyHeadersUnderscoreAlias guards GHSA-f59h-q822-g45g:
// a client-supplied `Remote_user` alias of the copy_headers target
// `Remote-User` must be stripped before the auth route runs, otherwise
// a downstream CGI/FastCGI backend would fold both names into the same
// HTTP_REMOTE_USER variable and the attacker would override the trusted
// identity.
func TestForwardAuthCopyHeadersUnderscoreAlias(t *testing.T) {
const wantRemoteUser = "alice"
authSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Remote-User", wantRemoteUser)
w.WriteHeader(http.StatusOK)
}))
t.Cleanup(authSrv.Close)
type received struct {
remoteUserHyphen, remoteUserUnderscore string
}
var (
mu sync.Mutex
last received
)
backendSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
mu.Lock()
last = received{
remoteUserHyphen: r.Header.Get("Remote-User"),
remoteUserUnderscore: strings.Join(r.Header["Remote_user"], ","),
}
mu.Unlock()
fmt.Fprint(w, "ok")
}))
t.Cleanup(backendSrv.Close)
tester := caddytest.NewTester(t)
tester.InitServer(fmt.Sprintf(`
{
skip_install_trust
admin localhost:2999
http_port 9080
https_port 9443
grace_period 1ns
}
http://localhost:9080 {
forward_auth %s {
uri /
copy_headers Remote-User
}
reverse_proxy %s
}
`, strings.TrimPrefix(authSrv.URL, "http://"), strings.TrimPrefix(backendSrv.URL, "http://")), "caddyfile")
req, _ := http.NewRequest(http.MethodGet, "http://localhost:9080/", nil)
// Set the underscore alias via raw map access to bypass http.Header
// canonicalization, as an attacker would on the wire.
req.Header["Remote_user"] = []string{"attacker"}
tester.AssertResponse(req, http.StatusOK, "ok")
mu.Lock()
defer mu.Unlock()
assert.Equal(t, wantRemoteUser, last.remoteUserHyphen, "trusted Remote-User must reach the backend")
assert.Empty(t, last.remoteUserUnderscore, "underscore alias must be dropped")
}
@@ -793,3 +793,41 @@ func TestReverseProxyRetryMatchIsTransportError(t *testing.T) {
// Transport error on broken upstream should be retried to good upstream
tester.AssertGetResponse("http://localhost:9080/", 200, "ok")
}
func TestReverseProxySNIPlaceHolder(t *testing.T) {
configTemplate := `
{
skip_install_trust
local_certs
admin localhost:2999
http_port 9080
https_port 9443
grace_period 1ns
}
localhost example.com {
@proxied header X-Transport caddy
respond @proxied {http.request.tls.server_name}
reverse_proxy 127.0.0.1:9443 {
header_up X-Transport caddy
header_up Host {host}
transport http {
versions %s
tls_server_name {header.X-SNI}
tls_insecure_skip_verify
}
}
}
`
for _, versions := range []string{"1.1 2", "3"} {
tester := caddytest.NewTester(t)
tester.InitServer(fmt.Sprintf(configTemplate, versions), "caddyfile")
req, err := http.NewRequest("GET", "https://localhost:9443", nil)
if err != nil {
t.Errorf("failed to create request %s", err)
return
}
req.Header.Set("X-SNI", "example.com")
tester.AssertResponse(req, 200, "example.com")
}
}
+1 -1
View File
@@ -159,7 +159,7 @@ func testH2ToH2CStreamServeH2C(t *testing.T) *http.Server {
}
// We only accept HTTP/2!
if r.ProtoMajor != 2 {
t.Error("Not a HTTP/2 request, rejected!")
t.Error("Not an HTTP/2 request, rejected!")
w.WriteHeader(http.StatusInternalServerError)
return
}
+17 -1
View File
@@ -58,7 +58,7 @@ func cmdStart(fl Flags) (int, error) {
// open a listener to which the child process will connect when
// it is ready to confirm that it has successfully started
ln, err := net.Listen("tcp", "127.0.0.1:0")
ln, err := listenTCPForPingback(net.Listen)
if err != nil {
return caddy.ExitCodeFailedStartup,
fmt.Errorf("opening listener for success confirmation: %v", err)
@@ -169,6 +169,22 @@ func cmdStart(fl Flags) (int, error) {
return caddy.ExitCodeSuccess, nil
}
type tcpListenFunc func(network, address string) (net.Listener, error)
func listenTCPForPingback(listen tcpListenFunc) (net.Listener, error) {
ln, ipv4Err := listen("tcp4", "127.0.0.1:0")
if ipv4Err == nil {
return ln, nil
}
ln, ipv6Err := listen("tcp6", "[::1]:0")
if ipv6Err == nil {
return ln, nil
}
return nil, fmt.Errorf("listen on 127.0.0.1:0: %v; listen on [::1]:0: %v", ipv4Err, ipv6Err)
}
func cmdRun(fl Flags) (int, error) {
caddy.TrapSignals()
+1 -1
View File
@@ -566,7 +566,7 @@ argument of --directory. If the directory does not exist, it will be created.
// following format:
//
// - lowercase
// - alphanumeric and hyphen characters only
// - ASCII lowercase letters, digits and hyphens only
// - cannot start or end with a hyphen
// - hyphen cannot be adjacent to another hyphen
//
+76
View File
@@ -1,6 +1,8 @@
package caddycmd
import (
"errors"
"net"
"reflect"
"strings"
"testing"
@@ -169,6 +171,80 @@ here"
}
}
func TestListenTCPForPingbackUsesIPv4Loopback(t *testing.T) {
var calls []string
expected := &stubListener{addr: &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 1234}}
actual, err := listenTCPForPingback(func(network, address string) (net.Listener, error) {
calls = append(calls, network+" "+address)
return expected, nil
})
if err != nil {
t.Fatalf("listenTCPForPingback returned error: %v", err)
}
if actual != expected {
t.Fatalf("expected listener %p, got %p", expected, actual)
}
expectCalls := []string{"tcp4 127.0.0.1:0"}
if !reflect.DeepEqual(calls, expectCalls) {
t.Fatalf("expected calls %v, got %v", expectCalls, calls)
}
}
func TestListenTCPForPingbackFallsBackToIPv6Loopback(t *testing.T) {
var calls []string
expected := &stubListener{addr: &net.TCPAddr{IP: net.ParseIP("::1"), Port: 1234}}
actual, err := listenTCPForPingback(func(network, address string) (net.Listener, error) {
calls = append(calls, network+" "+address)
if len(calls) == 1 {
return nil, errors.New("ipv4 unavailable")
}
return expected, nil
})
if err != nil {
t.Fatalf("listenTCPForPingback returned error: %v", err)
}
if actual != expected {
t.Fatalf("expected listener %p, got %p", expected, actual)
}
expectCalls := []string{"tcp4 127.0.0.1:0", "tcp6 [::1]:0"}
if !reflect.DeepEqual(calls, expectCalls) {
t.Fatalf("expected calls %v, got %v", expectCalls, calls)
}
}
func TestListenTCPForPingbackReportsBothFailures(t *testing.T) {
_, err := listenTCPForPingback(func(network, address string) (net.Listener, error) {
return nil, errors.New(network + " failed")
})
if err == nil {
t.Fatal("expected error")
}
if !strings.Contains(err.Error(), "tcp4 failed") ||
!strings.Contains(err.Error(), "tcp6 failed") {
t.Fatalf("expected both listener errors, got: %v", err)
}
}
type stubListener struct {
addr net.Addr
}
func (sl *stubListener) Accept() (net.Conn, error) {
return nil, net.ErrClosed
}
func (sl *stubListener) Close() error {
return nil
}
func (sl *stubListener) Addr() net.Addr {
return sl.addr
}
func Test_isCaddyfile(t *testing.T) {
type args struct {
configFile string
+50 -50
View File
@@ -1,22 +1,22 @@
module github.com/caddyserver/caddy/v2
go 1.25.0
go 1.25.1
require (
github.com/BurntSushi/toml v1.6.0
github.com/DeRuina/timberjack v1.4.2
github.com/DeRuina/timberjack v1.4.5
github.com/KimMachineGun/automemlimit v0.7.5
github.com/Masterminds/sprig/v3 v3.3.0
github.com/alecthomas/chroma/v2 v2.23.1
github.com/alecthomas/chroma/v2 v2.26.1
github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b
github.com/caddyserver/certmagic v0.25.3
github.com/caddyserver/zerossl v0.1.5
github.com/cloudflare/circl v1.6.3
github.com/dustin/go-humanize v1.0.1
github.com/go-chi/chi/v5 v5.2.5
github.com/google/cel-go v0.28.0
github.com/go-chi/chi/v5 v5.3.0
github.com/google/cel-go v0.28.1
github.com/google/uuid v1.6.0
github.com/klauspost/compress v1.18.5
github.com/klauspost/compress v1.18.6
github.com/klauspost/cpuid/v2 v2.3.0
github.com/mholt/acmez/v3 v3.1.6
github.com/prometheus/client_golang v1.23.2
@@ -30,29 +30,29 @@ require (
github.com/tailscale/tscert v0.0.0-20251216020129-aea342f6d747
github.com/yuin/goldmark v1.8.2
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
go.opentelemetry.io/contrib/bridges/prometheus v0.68.0
go.opentelemetry.io/contrib/exporters/autoexport v0.65.0
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0
go.opentelemetry.io/contrib/propagators/autoprop v0.65.0
go.opentelemetry.io/otel v1.43.0
go.opentelemetry.io/otel/sdk v1.43.0
go.opentelemetry.io/otel/sdk/metric v1.43.0
go.step.sm/crypto v0.77.1
go.opentelemetry.io/contrib/bridges/prometheus v0.69.0
go.opentelemetry.io/contrib/exporters/autoexport v0.69.0
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0
go.opentelemetry.io/contrib/propagators/autoprop v0.69.0
go.opentelemetry.io/otel v1.44.0
go.opentelemetry.io/otel/sdk v1.44.0
go.opentelemetry.io/otel/sdk/metric v1.44.0
go.step.sm/crypto v0.81.1
go.uber.org/automaxprocs v1.6.0
go.uber.org/zap v1.27.1
go.uber.org/zap v1.28.0
go.uber.org/zap/exp v0.3.0
golang.org/x/crypto v0.50.0
golang.org/x/crypto v0.52.0
golang.org/x/crypto/x509roots/fallback v0.0.0-20260213171211-a408498e5541
golang.org/x/net v0.53.0
golang.org/x/net v0.55.0
golang.org/x/sync v0.20.0
golang.org/x/term v0.42.0
golang.org/x/term v0.43.0
golang.org/x/time v0.15.0
gopkg.in/yaml.v3 v3.0.1
)
require (
cel.dev/expr v0.25.1 // indirect
cloud.google.com/go/auth v0.18.2 // indirect
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.9.0 // indirect
dario.cat/mergo v1.0.2 // indirect
@@ -62,16 +62,17 @@ require (
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
github.com/coreos/go-oidc/v3 v3.17.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dlclark/regexp2/v2 v2.1.1 // indirect
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
github.com/go-jose/go-jose/v3 v3.0.4 // indirect
github.com/go-jose/go-jose/v3 v3.0.5 // indirect
github.com/go-jose/go-jose/v4 v4.1.4 // indirect
github.com/google/certificate-transparency-go v1.1.8-0.20240110162603-74a5dd331745 // indirect
github.com/google/go-tpm v0.9.8 // indirect
github.com/google/go-tspi v0.3.0 // indirect
github.com/google/s2a-go v0.1.9 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.14 // indirect
github.com/googleapis/gax-go/v2 v2.18.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.15 // indirect
github.com/googleapis/gax-go/v2 v2.22.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 // indirect
github.com/jackc/pgx/v5 v5.9.2 // indirect
github.com/jackc/puddle/v2 v2.2.2 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
@@ -89,29 +90,29 @@ require (
github.com/x448/float16 v0.8.4 // indirect
github.com/zeebo/blake3 v0.2.4 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/contrib/propagators/aws v1.43.0 // indirect
go.opentelemetry.io/contrib/propagators/b3 v1.43.0 // indirect
go.opentelemetry.io/contrib/propagators/jaeger v1.43.0 // indirect
go.opentelemetry.io/contrib/propagators/ot v1.43.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.19.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.19.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 // indirect
go.opentelemetry.io/otel/exporters/prometheus v0.65.0 // indirect
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.19.0 // indirect
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.43.0 // indirect
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.43.0 // indirect
go.opentelemetry.io/otel/log v0.19.0 // indirect
go.opentelemetry.io/otel/sdk/log v0.19.0 // indirect
go.opentelemetry.io/contrib/propagators/aws v1.44.0 // indirect
go.opentelemetry.io/contrib/propagators/b3 v1.44.0 // indirect
go.opentelemetry.io/contrib/propagators/jaeger v1.44.0 // indirect
go.opentelemetry.io/contrib/propagators/ot v1.44.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.20.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.20.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.44.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.44.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.44.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.44.0 // indirect
go.opentelemetry.io/otel/exporters/prometheus v0.66.0 // indirect
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.20.0 // indirect
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.44.0 // indirect
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.44.0 // indirect
go.opentelemetry.io/otel/log v0.20.0 // indirect
go.opentelemetry.io/otel/sdk/log v0.20.0 // 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-20251023183803-a4bb9ffd2546 // indirect
golang.org/x/oauth2 v0.36.0 // indirect
google.golang.org/api v0.271.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 // indirect
google.golang.org/api v0.280.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 // indirect
)
@@ -129,7 +130,6 @@ require (
github.com/dgraph-io/badger/v2 v2.2007.4 // indirect
github.com/dgraph-io/ristretto v0.2.0 // indirect
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect
github.com/dlclark/regexp2 v1.11.5 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
@@ -149,7 +149,7 @@ require (
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-ps v1.0.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/pires/go-proxyproto v0.11.0
github.com/pires/go-proxyproto v0.12.0
github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/client_model v0.6.2
github.com/prometheus/common v0.67.5 // indirect
@@ -163,16 +163,16 @@ require (
github.com/spf13/cast v1.7.0 // indirect
github.com/urfave/cli v1.22.17 // indirect
go.etcd.io/bbolt v1.4.3 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 // indirect
go.opentelemetry.io/otel/metric v1.43.0 // indirect
go.opentelemetry.io/otel/trace v1.43.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0 // indirect
go.opentelemetry.io/otel/metric v1.44.0 // indirect
go.opentelemetry.io/otel/trace v1.44.0
go.opentelemetry.io/proto/otlp v1.10.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/mod v0.35.0 // indirect
golang.org/x/sys v0.43.0
golang.org/x/text v0.36.0 // indirect
golang.org/x/sys v0.45.0
golang.org/x/text v0.37.0 // indirect
golang.org/x/tools v0.44.0 // indirect
google.golang.org/grpc v1.80.0 // indirect
google.golang.org/grpc v1.81.1 // indirect
google.golang.org/protobuf v1.36.11 // indirect
howett.net/plist v1.0.0 // indirect
)
+144 -142
View File
@@ -2,18 +2,18 @@ cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4=
cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4=
cloud.google.com/go v0.123.0 h1:2NAUJwPR47q+E35uaJeYoNhuNEM9kM8SjgRgdeOJUSE=
cloud.google.com/go v0.123.0/go.mod h1:xBoMV08QcqUGuPW65Qfm1o9Y4zKZBpGS+7bImXLTAZU=
cloud.google.com/go/auth v0.18.2 h1:+Nbt5Ev0xEqxlNjd6c+yYUeosQ5TtEUaNcN/3FozlaM=
cloud.google.com/go/auth v0.18.2/go.mod h1:xD+oY7gcahcu7G2SG2DsBerfFxgPAJz17zz2joOFF3M=
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.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=
cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
cloud.google.com/go/iam v1.5.3 h1:+vMINPiDF2ognBJ97ABAYYwRgsaqxPbQDlMnbHMjolc=
cloud.google.com/go/iam v1.5.3/go.mod h1:MR3v9oLkZCTlaqljW6Eb2d3HGDGK5/bDv93jhfISFvU=
cloud.google.com/go/kms v1.26.0 h1:cK9mN2cf+9V63D3H1f6koxTatWy39aTI/hCjz1I+adU=
cloud.google.com/go/kms v1.26.0/go.mod h1:pHKOdFJm63hxBsiPkYtowZPltu9dW0MWvBa6IA4HM58=
cloud.google.com/go/longrunning v0.8.0 h1:LiKK77J3bx5gDLi4SMViHixjD2ohlkwBi+mKA7EhfW8=
cloud.google.com/go/longrunning v0.8.0/go.mod h1:UmErU2Onzi+fKDg2gR7dusz11Pe26aknR4kHmJJqIfk=
cloud.google.com/go/iam v1.7.0 h1:JD3zh0C6LHl16aCn5Akff0+GELdp1+4hmh6ndoFLl8U=
cloud.google.com/go/iam v1.7.0/go.mod h1:tetWZW1PD/m6vcuY2Zj/aU0eCHNPuxedbnbRTyKXvdY=
cloud.google.com/go/kms v1.31.0 h1:LS8N92OxFDgOLg5NCo3OmbvjtQAIVT5gUHVLKIDHaFE=
cloud.google.com/go/kms v1.31.0/go.mod h1:YIyXZym11R5uovJJt4oN5eUL3oPmirF3yKeIh6QAf4U=
cloud.google.com/go/longrunning v0.9.0 h1:0EzbDEGsAvOZNbqXopgniY0w0a1phvu5IdUFq8grmqY=
cloud.google.com/go/longrunning v0.9.0/go.mod h1:pkTz846W7bF4o2SzdWJ40Hu0Re+UoNT6Q5t+igIcb8E=
code.pfad.fr/check v1.1.0 h1:GWvjdzhSEgHvEHe2uJujDcpmZoySKuHQNrZMfzfO0bE=
code.pfad.fr/check v1.1.0/go.mod h1:NiUH13DtYsb7xp5wll0U4SXx7KhXQVCtRgdC96IPfoM=
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
@@ -28,8 +28,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=
github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/DeRuina/timberjack v1.4.2 h1:4bKlzhKdsR+2oNkgef9mqb4n11ICow8VK88RfzJPzN8=
github.com/DeRuina/timberjack v1.4.2/go.mod h1:RLoeQrwrCGIEF8gO5nV5b/gMD0QIy7bzQhBUgpp1EqE=
github.com/DeRuina/timberjack v1.4.5 h1:F/kms5MPNAXUeWdOILt5ALC6iDHWNRPevaeIVH7tqYU=
github.com/DeRuina/timberjack v1.4.5/go.mod h1:RLoeQrwrCGIEF8gO5nV5b/gMD0QIy7bzQhBUgpp1EqE=
github.com/KimMachineGun/automemlimit v0.7.5 h1:RkbaC0MwhjL1ZuBKunGDjE/ggwAX43DwZrJqVwyveTk=
github.com/KimMachineGun/automemlimit v0.7.5/go.mod h1:QZxpHaGOQoYvFhv/r4u3U0JTC2ZcOwbSr11UZF46UBM=
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
@@ -43,8 +43,8 @@ github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAE
github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs=
github.com/alecthomas/chroma/v2 v2.23.1 h1:nv2AVZdTyClGbVQkIzlDm/rnhk1E9bU9nXwmZ/Vk/iY=
github.com/alecthomas/chroma/v2 v2.23.1/go.mod h1:NqVhfBR0lte5Ouh3DcthuUCTUpDC9cxBOfyMbMQPs3o=
github.com/alecthomas/chroma/v2 v2.26.1 h1:2X21EdxGZNv5GF9mG5u+uzc02GCFyGxbcBm3Grd9A78=
github.com/alecthomas/chroma/v2 v2.26.1/go.mod h1:lxhRRa9H4hPmRLOOdYga4zkQIQjq3dtrrdwQeCfu78Y=
github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
github.com/alecthomas/repr v0.5.2 h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs=
github.com/alecthomas/repr v0.5.2/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
@@ -53,36 +53,36 @@ github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmO
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b h1:uUXgbcPDK3KpW29o4iy7GtuappbWT0l5NaMo9H9pJDw=
github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
github.com/aws/aws-sdk-go-v2 v1.41.4 h1:10f50G7WyU02T56ox1wWXq+zTX9I1zxG46HYuG1hH/k=
github.com/aws/aws-sdk-go-v2 v1.41.4/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o=
github.com/aws/aws-sdk-go-v2/config v1.32.12 h1:O3csC7HUGn2895eNrLytOJQdoL2xyJy0iYXhoZ1OmP0=
github.com/aws/aws-sdk-go-v2/config v1.32.12/go.mod h1:96zTvoOFR4FURjI+/5wY1vc1ABceROO4lWgWJuxgy0g=
github.com/aws/aws-sdk-go-v2/credentials v1.19.12 h1:oqtA6v+y5fZg//tcTWahyN9PEn5eDU/Wpvc2+kJ4aY8=
github.com/aws/aws-sdk-go-v2/credentials v1.19.12/go.mod h1:U3R1RtSHx6NB0DvEQFGyf/0sbrpJrluENHdPy1j/3TE=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 h1:zOgq3uezl5nznfoK3ODuqbhVg1JzAGDUhXOsU0IDCAo=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20/go.mod h1:z/MVwUARehy6GAg/yQ1GO2IMl0k++cu1ohP9zo887wE=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20 h1:CNXO7mvgThFGqOFgbNAP2nol2qAWBOGfqR/7tQlvLmc=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20/go.mod h1:oydPDJKcfMhgfcgBUZaG+toBbwy8yPWubJXBVERtI4o=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20 h1:tN6W/hg+pkM+tf9XDkWUbDEjGLb+raoBMFsTodcoYKw=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20/go.mod h1:YJ898MhD067hSHA6xYCx5ts/jEd8BSOLtQDL3iZsvbc=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 h1:qYQ4pzQ2Oz6WpQ8T3HvGHnZydA72MnLuFK9tJwmrbHw=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20 h1:2HvVAIq+YqgGotK6EkMf+KIEqTISmTYh5zLpYyeTo1Y=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20/go.mod h1:V4X406Y666khGa8ghKmphma/7C0DAtEQYhkq9z4vpbk=
github.com/aws/aws-sdk-go-v2/service/kms v1.50.3 h1:s/zDSG/a/Su9aX+v0Ld9cimUCdkr5FWPmBV8owaEbZY=
github.com/aws/aws-sdk-go-v2/service/kms v1.50.3/go.mod h1:/iSgiUor15ZuxFGQSTf3lA2FmKxFsQoc2tADOarQBSw=
github.com/aws/aws-sdk-go-v2/service/signin v1.0.8 h1:0GFOLzEbOyZABS3PhYfBIx2rNBACYcKty+XGkTgw1ow=
github.com/aws/aws-sdk-go-v2/service/signin v1.0.8/go.mod h1:LXypKvk85AROkKhOG6/YEcHFPoX+prKTowKnVdcaIxE=
github.com/aws/aws-sdk-go-v2/service/sso v1.30.13 h1:kiIDLZ005EcKomYYITtfsjn7dtOwHDOFy7IbPXKek2o=
github.com/aws/aws-sdk-go-v2/service/sso v1.30.13/go.mod h1:2h/xGEowcW/g38g06g3KpRWDlT+OTfxxI0o1KqayAB8=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 h1:jzKAXIlhZhJbnYwHbvUQZEB8KfgAEuG0dc08Bkda7NU=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17/go.mod h1:Al9fFsXjv4KfbzQHGe6V4NZSZQXecFcvaIF4e70FoRA=
github.com/aws/aws-sdk-go-v2/service/sts v1.41.9 h1:Cng+OOwCHmFljXIxpEVXAGMnBia8MSU6Ch5i9PgBkcU=
github.com/aws/aws-sdk-go-v2/service/sts v1.41.9/go.mod h1:LrlIndBDdjA/EeXeyNBle+gyCwTlizzW5ycgWnvIxkk=
github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng=
github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=
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/kms v1.52.0 h1:QNtg+Mtj1zmepk568+UKBD5DFfqh+ESTUUqQT27JkQc=
github.com/aws/aws-sdk-go-v2/service/kms v1.52.0/go.mod h1:Y0+uxvxz6ib4KktRdK0V4X45Vcs/JyYoz8H71pO8xeI=
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/caddyserver/certmagic v0.25.3 h1:mGf5ba8F7xA4c5jfDZZbK2buY1VEkbnwpMDixaju94A=
@@ -133,8 +133,8 @@ github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WA
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ=
github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dlclark/regexp2/v2 v2.1.1 h1:LCUGyd9Wf+r+VVOl8Ny38JTpWJcAsdVnCIuhhtthmKw=
github.com/dlclark/regexp2/v2 v2.1.1/go.mod h1:avUrQvPaLz2DrFNHJF0taWAFFX2C1GMSSoeiqFjcBmU=
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=
@@ -147,10 +147,10 @@ github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7z
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
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-chi/chi/v5 v5.2.5 h1:Eg4myHZBjyvJmAFjFvWgrqDTXFyOzjj7YIm3L3mu6Ug=
github.com/go-chi/chi/v5 v5.2.5/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0=
github.com/go-jose/go-jose/v3 v3.0.4 h1:Wp5HA7bLQcKnf6YYao/4kpRpVMp/yf6+pJKV8WFSaNY=
github.com/go-jose/go-jose/v3 v3.0.4/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
github.com/go-chi/chi/v5 v5.3.0 h1:halUjDxhshgXHMrao5bB8eNBXo/rnzwr8m5m36glehM=
github.com/go-chi/chi/v5 v5.3.0/go.mod h1:R+tYY2hNuVUUjxoPtqUdgBqevM9s9njzkTLutVsOCto=
github.com/go-jose/go-jose/v3 v3.0.5 h1:BLLJWbC4nMZOfuPVxoZIxeYsn6Nl2r1fITaJ78UQlVQ=
github.com/go-jose/go-jose/v3 v3.0.5/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA=
github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
@@ -168,8 +168,8 @@ github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/cel-go v0.28.0 h1:KjSWstCpz/MN5t4a8gnGJNIYUsJRpdi/r97xWDphIQc=
github.com/google/cel-go v0.28.0/go.mod h1:X0bD6iVNR8pkROSOoHVdgTkzmRcosof7WQqCD6wcMc8=
github.com/google/cel-go v0.28.1 h1:YWIwi77J4xIsYUwAF/iIuS6haffzIHS8yWI8glSbLWM=
github.com/google/cel-go v0.28.1/go.mod h1:X0bD6iVNR8pkROSOoHVdgTkzmRcosof7WQqCD6wcMc8=
github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg=
github.com/google/certificate-transparency-go v1.1.8-0.20240110162603-74a5dd331745 h1:heyoXNxkRT155x4jTAiSv5BVSVkueifPUm+Q8LUXMRo=
github.com/google/certificate-transparency-go v1.1.8-0.20240110162603-74a5dd331745/go.mod h1:zN0wUQgV9LjwLZeFHnrAbQi8hzMVvEWePyk+MhPOk7k=
@@ -179,20 +179,20 @@ 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/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo=
github.com/google/go-tpm v0.9.8/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY=
github.com/google/go-tpm-tools v0.4.7 h1:J3ycC8umYxM9A4eF73EofRZu4BxY0jjQnUnkhIBbvws=
github.com/google/go-tpm-tools v0.4.7/go.mod h1:gSyXTZHe3fgbzb6WEGd90QucmsnT1SRdlye82gH8QjQ=
github.com/google/go-tpm-tools v0.4.8 h1:V4oIYyAD3BykOycwYQzO29WefDouQMTsYZqmG3HxOfM=
github.com/google/go-tpm-tools v0.4.8/go.mod h1:4DfiOtiS1KppJjwf1+tqtW4K3PrCJjAAqFKj/TYTJKg=
github.com/google/go-tspi v0.3.0 h1:ADtq8RKfP+jrTyIWIZDIYcKOMecRqNJFOew2IT0Inus=
github.com/google/go-tspi v0.3.0/go.mod h1:xfMGI3G0PhxCdNVcYr1C4C+EizojDg/TXuX5by8CiHI=
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.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.14 h1:yh8ncqsbUY4shRD5dA6RlzjJaT4hi3kII+zYw8wmLb8=
github.com/googleapis/enterprise-certificate-proxy v0.3.14/go.mod h1:vqVt9yG9480NtzREnTlmGSBmFrA+bzb0yl0TxoBQXOg=
github.com/googleapis/gax-go/v2 v2.18.0 h1:jxP5Uuo3bxm3M6gGtV94P4lliVetoCB4Wk2x8QA86LI=
github.com/googleapis/gax-go/v2 v2.18.0/go.mod h1:uSzZN4a356eRG985CzJ3WfbFSpqkLTjsnhWGJR6EwrE=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c=
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/grpc-ecosystem/grpc-gateway/v2 v2.29.0 h1:5VipnvEpbqr2gA2VbM+nYVbkIF28c5ZQfqCBQ5g2xfk=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0/go.mod h1:Hyl3n6Twe1hvtd9XUXDec4pTvgMSEixRuQKPTMH2bNs=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
@@ -211,8 +211,8 @@ github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE=
github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
github.com/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao=
github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
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.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
@@ -259,8 +259,8 @@ github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhM
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/peterbourgon/diskv/v3 v3.0.1 h1:x06SQA46+PKIUftmEujdwSEpIx8kR+M9eLYsUxeYveU=
github.com/peterbourgon/diskv/v3 v3.0.1/go.mod h1:kJ5Ny7vLdARGU3WUuy6uzO6T0nb/2gWcT1JiBvRmb5o=
github.com/pires/go-proxyproto v0.11.0 h1:gUQpS85X/VJMdUsYyEgyn59uLJvGqPhJV5YvG68wXH4=
github.com/pires/go-proxyproto v0.11.0/go.mod h1:ZKAAyp3cgy5Y5Mo4n9AlScrkCZwUy0g3Jf+slqQVcuU=
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.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -373,66 +373,68 @@ go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo=
go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E=
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/contrib/bridges/prometheus v0.68.0 h1:w3zlHYETbDwXyWHZlyyR58ZC39XGi8rAhkBgUgJ9d5w=
go.opentelemetry.io/contrib/bridges/prometheus v0.68.0/go.mod h1:GR/mClR2nn7vE8RLwxKjoBNg+QtgdDhRzxVa93koy5o=
go.opentelemetry.io/contrib/exporters/autoexport v0.65.0 h1:2gApdml7SznX9szEKFjKjM4qGcGSvAybYLBY319XG3g=
go.opentelemetry.io/contrib/exporters/autoexport v0.65.0/go.mod h1:0QqAGlbHXhmPYACG3n5hNzO5DnEqqtg4VcK5pr22RI0=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 h1:7iP2uCb7sGddAr30RRS6xjKy7AZ2JtTOPA3oolgVSw8=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0/go.mod h1:c7hN3ddxs/z6q9xwvfLPk+UHlWRQyaeR1LdgfL/66l0=
go.opentelemetry.io/contrib/propagators/autoprop v0.65.0 h1:kTaCycF9Xkm8VBBvH0rJ4wFeRjtIV55Erk3uuVsIs5s=
go.opentelemetry.io/contrib/propagators/autoprop v0.65.0/go.mod h1:rooPzAbXfxMX9fsPJjmOBg2SN4RhFEV8D7cfGK+N3tE=
go.opentelemetry.io/contrib/propagators/aws v1.43.0 h1:EwnsB3cXRLAh7/Nr/9rMuGw73nfb3z6uAvVDjRrbeUg=
go.opentelemetry.io/contrib/propagators/aws v1.43.0/go.mod h1:CJjTym6F87tEdm61Qvnz5xrV8vKlH4C92djiqcn62k8=
go.opentelemetry.io/contrib/propagators/b3 v1.43.0 h1:CETqV3QLLPTy5yNrqyMr41VnAOOD4lsRved7n4QG00A=
go.opentelemetry.io/contrib/propagators/b3 v1.43.0/go.mod h1:Q4mCiCdziYzpNR0g+6UqVotAlCDZdzz6L8jwY4knOrw=
go.opentelemetry.io/contrib/propagators/jaeger v1.43.0 h1:peiLMz1+aqJE+3L4mOVtR9wlmv+yh/JVYXCBjqmzJJE=
go.opentelemetry.io/contrib/propagators/jaeger v1.43.0/go.mod h1:Agvif+4A8p/3UtZzJ0MCcDEuQwgtrzM71DueU41DCs8=
go.opentelemetry.io/contrib/propagators/ot v1.43.0 h1:Hh1HahlGc81AOE7siqi1tVOlbanY/UxMMWedpb0d5oQ=
go.opentelemetry.io/contrib/propagators/ot v1.43.0/go.mod h1:58MlyS7lghzYvAm5LN9gGmZpCMQEMB5vpZp9SRgOyE4=
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/exporters/otlp/otlplog/otlploggrpc v0.19.0 h1:Dn8rkudDzY6KV9dr/D/bTUuWgqDf9xe0rr4G2elrn0Y=
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.19.0/go.mod h1:gMk9F0xDgyN9M/3Ed5Y1wKcx/9mlU91NXY2SNq7RQuU=
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.19.0 h1:HIBTQ3VO5aupLKjC90JgMqpezVXwFuq6Ryjn0/izoag=
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.19.0/go.mod h1:ji9vId85hMxqfvICA0Jt8JqEdrXaAkcpkI9HPXya0ro=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0 h1:8UQVDcZxOJLtX6gxtDt3vY2WTgvZqMQRzjsqiIHQdkc=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0/go.mod h1:2lmweYCiHYpEjQ/lSJBYhj9jP1zvCvQW4BqL9dnT7FQ=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0 h1:w1K+pCJoPpQifuVpsKamUdn9U0zM3xUziVOqsGksUrY=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0/go.mod h1:HBy4BjzgVE8139ieRI75oXm3EcDN+6GhD88JT1Kjvxg=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 h1:88Y4s2C8oTui1LGM6bTWkw0ICGcOLCAI5l6zsD1j20k=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0/go.mod h1:Vl1/iaggsuRlrHf/hfPJPvVag77kKyvrLeD10kpMl+A=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0 h1:RAE+JPfvEmvy+0LzyUA25/SGawPwIUbZ6u0Wug54sLc=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0/go.mod h1:AGmbycVGEsRx9mXMZ75CsOyhSP6MFIcj/6dnG+vhVjk=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 h1:3iZJKlCZufyRzPzlQhUIWVmfltrXuGyfjREgGP3UUjc=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0/go.mod h1:/G+nUPfhq2e+qiXMGxMwumDrP5jtzU+mWN7/sjT2rak=
go.opentelemetry.io/otel/exporters/prometheus v0.65.0 h1:jOveH/b4lU9HT7y+Gfamf18BqlOuz2PWEvs8yM7Q6XE=
go.opentelemetry.io/otel/exporters/prometheus v0.65.0/go.mod h1:i1P8pcumauPtUI4YNopea1dhzEMuEqWP1xoUZDylLHo=
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.19.0 h1:GJkybS+crDMdExT/BUNCEgfrmfboztcS6PhvSo88HKM=
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.19.0/go.mod h1:NuAyxRYIG2lKX3YQkB+83StTxM7s52PUUkRRiC0wnYI=
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.43.0 h1:TC+BewnDpeiAmcscXbGMfxkO+mwYUwE/VySwvw88PfA=
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.43.0/go.mod h1:J/ZyF4vfPwsSr9xJSPyQ4LqtcTPULFR64KwTikGLe+A=
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.43.0 h1:mS47AX77OtFfKG4vtp+84kuGSFZHTyxtXIN269vChY0=
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.43.0/go.mod h1:PJnsC41lAGncJlPUniSwM81gc80GkgWJWr3cu2nKEtU=
go.opentelemetry.io/otel/log v0.19.0 h1:KUZs/GOsw79TBBMfDWsXS+KZ4g2Ckzksd1ymzsIEbo4=
go.opentelemetry.io/otel/log v0.19.0/go.mod h1:5DQYeGmxVIr4n0/BcJvF4upsraHjg6vudJJpnkL6Ipk=
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/log v0.19.0 h1:scYVLqT22D2gqXItnWiocLUKGH9yvkkeql5dBDiXyko=
go.opentelemetry.io/otel/sdk/log v0.19.0/go.mod h1:vFBowwXGLlW9AvpuF7bMgnNI95LiW10szrOdvzBHlAg=
go.opentelemetry.io/otel/sdk/log/logtest v0.19.0 h1:BEbF7ZBB6qQloV/Ub1+3NQoOUnVtcGkU3XX4Ws3GQfk=
go.opentelemetry.io/otel/sdk/log/logtest v0.19.0/go.mod h1:Lua81/3yM0wOmoHTokLj9y9ADeA02v1naRrVrkAZuKk=
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/contrib/bridges/prometheus v0.69.0 h1:saQoWg5845Q8TojpqeVStS7zGwVZ6bc5W2PJavTPiBM=
go.opentelemetry.io/contrib/bridges/prometheus v0.69.0/go.mod h1:AAaS6xs5AyqMdR3Ir0nSWK+QudL2XM8Vbw5INzUxNc8=
go.opentelemetry.io/contrib/exporters/autoexport v0.69.0 h1:R3jsCoTIzv0BiYNhW0axyswn/6SMJ8xL1OuGxvni1Kw=
go.opentelemetry.io/contrib/exporters/autoexport v0.69.0/go.mod h1:m07gqyr2QhQxKOKb5vqKCCBtLH3uqlNYR7PU/FISXVU=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0 h1:yI1/OhfEPy7J9eoa6Sj051C7n5dvpj0QX8g4sRchg04=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0/go.mod h1:NoUCKYWK+3ecatC4HjkRktREheMeEtrXoQxrqYFeHSc=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0 h1:8tvICD4vSTOOsNrsI4Ljf6C+6UKvpTEH5XY3JMoyPoo=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0/go.mod h1:z9+yiacE0IHRqM4qFfkbt/JYlmYXgss8GY/jXoNuPJI=
go.opentelemetry.io/contrib/propagators/autoprop v0.69.0 h1:3gzAeb5dgGzwB7hXutgJ07Xsv3v4Wc0llV8AaMc0wiQ=
go.opentelemetry.io/contrib/propagators/autoprop v0.69.0/go.mod h1:SpChkgQWjh6egTT0chEc7VfusZgQMPzLsxRWWrqJdaQ=
go.opentelemetry.io/contrib/propagators/aws v1.44.0 h1:Rtvfd6nTbAF2csjiw41m1DfuqC5TneXs+gB84ZA3gq4=
go.opentelemetry.io/contrib/propagators/aws v1.44.0/go.mod h1:auu0tIyZErQGLLUvOp9DgmhKALIoebR4Fpkt9CT0c0k=
go.opentelemetry.io/contrib/propagators/b3 v1.44.0 h1:1IFH4oFKK8KupzIelCl3u+bkxpGRps1oWRjQI2+TTWs=
go.opentelemetry.io/contrib/propagators/b3 v1.44.0/go.mod h1:JqWFXsc7VDaqIyubFhEd2cPHqsrzqP0Lvn783SUwyro=
go.opentelemetry.io/contrib/propagators/jaeger v1.44.0 h1:OyzvsAMc/zHt0DRPcfstn0wgfq8ApDkeY0ABMcueweM=
go.opentelemetry.io/contrib/propagators/jaeger v1.44.0/go.mod h1:44kghcGX+BNxy9UTiWtd6VDt8Nd4EypGBkH2+v2Dqrc=
go.opentelemetry.io/contrib/propagators/ot v1.44.0 h1:JLTPenzmPtLp5ODPntAA5JhxVu1i3pAFUvXcAORMZAk=
go.opentelemetry.io/contrib/propagators/ot v1.44.0/go.mod h1:8zr0bHgwkoQXucBK39/H4QphmLf1lSen1Z7FPDZD5Uc=
go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU=
go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc=
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.20.0 h1:rydZ9sxbcFdm/oWrVyfLTjHIygMgv0bEeMd+3B/BvoM=
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.20.0/go.mod h1:earQ25dooT0Hhspq59DZ8YCC50jWfOlFEeWoxy/P444=
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.20.0 h1:owlhcJ3QO3X0YTDTCcDZ4V+6aVDkWbNmBoQ5NUp7Oww=
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.20.0/go.mod h1:MP4eemTiI9zC8fgg+DYynhYDYf3ba72S376TvP+Ye0Q=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.44.0 h1:SUplec5dp06reu1zaXmOXdvqH398taqrDXqUl99jxSc=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.44.0/go.mod h1:ho2g4N+ane+swq5I/VBkKWnRDY4kUINH3FuqyZqX/Ug=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.44.0 h1:RuynHbfU8JUEw7DyONgkVYg2SVtsoF28y0LGIr69jgA=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.44.0/go.mod h1:qZF+/lBs71APw8mlnEZcqZHMzqrYrsFiJOv83lX1OGo=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0 h1:4YsVu3B8+3qtWYYrsUYgn0OG78pN0rnNPRGX4SbokQI=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0/go.mod h1:+wnlSn0mD1ADVMe3v9Z/WIaiz6q6gL2J/ejaAmdmv80=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.44.0 h1:qazEJlUOQzhCpzQpFETGby7EdqjI1wsd0W+6Gg1SCTU=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.44.0/go.mod h1:fOD2Yefuxixkx3ahVNf0O/PERb6r4OlbxfATVnYvzCo=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.44.0 h1:lgh3PiVrRUWMLOVSkQicxzZll5NjF1r+AtsX1XRIHw0=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.44.0/go.mod h1:5Cnhth3m/AgOeTgE3ex12pPmiu/gGtZit03kSzx9X7s=
go.opentelemetry.io/otel/exporters/prometheus v0.66.0 h1:vkrK8PAznv2NKt2r+kdu252ccGzkEqLc2aSXbQIALYQ=
go.opentelemetry.io/otel/exporters/prometheus v0.66.0/go.mod h1:V/UB6D3vMF/UBOL5igAsAYnk1nG/bzYYTzvsB16cy7o=
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.20.0 h1:aZfdmtI6QU/DAPD4b7YZ5zuJgewxO1EW9miOZklqleU=
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.20.0/go.mod h1:isNl10/Om5CBWu9jj8WOb2+tJLbCVXDgqwzCaJMnJ6w=
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.44.0 h1:hqxVTu/GtBF+vJ8d1fzW7fRxZFvgoDjWcxwwCaFDYpU=
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.44.0/go.mod h1:z5fVEF4X5v0ESvlJqBrrFlBVoj5EQuefZpzsu7R+x5Q=
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.44.0 h1:bl2S7Ubua0Nms+D/gAmznQTd4dxxMA93aKbcpKqiTCs=
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.44.0/go.mod h1:L0hRV50XdVIODHUfWEqGRCXQvj2rV82STVo12FMFBU0=
go.opentelemetry.io/otel/log v0.20.0 h1:/5i0vuHxCLWUfChWG41K9wkM0jafruPw9NU1/RCJirs=
go.opentelemetry.io/otel/log v0.20.0/go.mod h1:wOcMcjsZpG8x7Bak7IhSi/lg8wscV2C1VdrKCLPlt0E=
go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc=
go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo=
go.opentelemetry.io/otel/metric/x v0.66.0 h1:YkCrx1zLOChi9ZcZ6euupOcsgzbVlec7D/xoEU1+cTA=
go.opentelemetry.io/otel/metric/x v0.66.0/go.mod h1:d1+BDj9t96do0/1LoU1ayfCv79ZgNE41qbhBvnMOBZk=
go.opentelemetry.io/otel/sdk v1.44.0 h1:nHYwb9lK+fJPU/dnT6s7W7Z8itMWyqrnVfbheVYrZ58=
go.opentelemetry.io/otel/sdk v1.44.0/go.mod h1:Osuydd3Se74nqjAKxid74N5eC+jfEqfTegHRnq58oK0=
go.opentelemetry.io/otel/sdk/log v0.20.0 h1:vM3xI7TQgKPiSghe6urZtAkyFY7SodrSpC83CffDFuY=
go.opentelemetry.io/otel/sdk/log v0.20.0/go.mod h1:Knej2nmsTUzN79T2eeXdRsjjPcoxoq2pUyUHz9TFyyU=
go.opentelemetry.io/otel/sdk/log/logtest v0.20.0 h1:OqdRZ1guyzamK3M6LlRsmGqRrjkHWw6WZOKKli5ELpg=
go.opentelemetry.io/otel/sdk/log/logtest v0.20.0/go.mod h1:PuMIlm7zAt7c3z8zfOI5ox4iT1Z87We+PF6YoINux/M=
go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRks6si09iEfI=
go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA=
go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk=
go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE=
go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g=
go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk=
go.step.sm/crypto v0.77.1 h1:4EEqfKdv0egQ1lqz2RhnU8Jv6QgXZfrgoxWMqJF9aDs=
go.step.sm/crypto v0.77.1/go.mod h1:U/SsmEm80mNnfD5WIkbhuW/B1eFp3fgFvdXyDLpU1AQ=
go.step.sm/crypto v0.81.1 h1:zzkCA+ZmfT65fyCRebzNzQG2FkD45GcZcnKztb6VuQk=
go.step.sm/crypto v0.81.1/go.mod h1:MuLXjDSCYUNEqx8ziNU6Q2EvXP3FGm+OGb7nC4m38vw=
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=
@@ -441,8 +443,8 @@ 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.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=
go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo=
go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q=
go.uber.org/zap/exp v0.3.0 h1:6JYzdifzYkGmTdRR59oYH+Ng7k49H9qVpWwNSsGJj3U=
go.uber.org/zap/exp v0.3.0/go.mod h1:5I384qq7XGxYyByIhHm6jg5CHkGY0nsTfbDLgDDlgJQ=
go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ=
@@ -456,8 +458,8 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
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/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988=
golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc=
golang.org/x/crypto/x509roots/fallback v0.0.0-20260213171211-a408498e5541 h1:FmKxj9ocLKn45jiR2jQMwCVhDvaK7fKQFzfuT9GvyK8=
golang.org/x/crypto/x509roots/fallback v0.0.0-20260213171211-a408498e5541/go.mod h1:+UoQFNBq2p2wO+Q6ddVtYc25GZ6VNdOMyyrd4nrqrKs=
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 h1:mgKeJMpvi0yx/sU5GsxQ7p6s2wtOnGAHZWCHUM4KGzY=
@@ -477,8 +479,8 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA=
golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs=
golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8=
golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww=
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=
@@ -506,8 +508,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
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=
@@ -517,8 +519,8 @@ golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
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/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4=
golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk=
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.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
@@ -528,8 +530,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
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=
@@ -543,16 +545,16 @@ golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
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.271.0 h1:cIPN4qcUc61jlh7oXu6pwOQqbJW2GqYh5PS6rB2C/JY=
google.golang.org/api v0.271.0/go.mod h1:CGT29bhwkbF+i11qkRUJb2KMKqcJ1hdFceEIRd9u64Q=
google.golang.org/genproto v0.0.0-20260217215200-42d3e9bedb6d h1:vsOm753cOAMkt76efriTCDKjpCbK18XGHMJHo0JUKhc=
google.golang.org/genproto v0.0.0-20260217215200-42d3e9bedb6d/go.mod h1:0oz9d7g9QLSdv9/lgbIjowW1JoxMbxmBVNe8i6tORJI=
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 h1:VPWxll4HlMw1Vs/qXtN7BvhZqsS9cdAittCNvVENElA=
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:7QBABkRtR8z+TEnmXTqIqwJLlzrZKVfAUm7tY3yGv0M=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 h1:m8qni9SQFH0tJc1X0vmnpw/0t+AImlSvp30sEupozUg=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM=
google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4=
google.golang.org/api v0.280.0 h1:F4OfEHZhZh6a7uTufJAXXVd/2TQ8EjM4vZH+jX/vFYk=
google.golang.org/api v0.280.0/go.mod h1:oGKmPZRDoD3vdkf6MA7F4VNkR1rxCiuaPSkhsf3EolU=
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-20260526163538-3dc84a4a5aaa h1:Kjn0N0tCrDgiAFW+lGO4JZ3ck44CehvJQMAwj9QF0G8=
google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:q4lMZS6kskjT5HvCPrnnypcDPVJqT/f4nfxmkE7gryY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa h1:mZHHdPZl0dbGHCflZgAq/Q468DWVFcU2whhB2KAo8fk=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.81.1 h1:VnnIIZ88UzOOKLukQi+ImGz8O1Wdp8nAGGnvOfEIWQQ=
google.golang.org/grpc v1.81.1/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 h1:F29+wU6Ee6qgu9TddPgooOdaqsxTMunOoj8KA5yuS5A=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1/go.mod h1:5KF+wpkbTSbGcR9zteSqZV6fqFOWBl4Yde8En8MryZA=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
+27 -4
View File
@@ -37,6 +37,12 @@ func init() {
// `{http.auth.user.*}` placeholders may be set for any authentication
// modules that provide user metadata.
//
// If authentication is rejected but a provider returns user information,
// the placeholder `{http.auth.candidate.id}` will be set to the candidate
// username, and also `{http.auth.candidate.*}` placeholders may be set
// for candidate user metadata. Candidate placeholders do not represent a
// successfully authenticated principal.
//
// In case of an error, the placeholder `{http.auth.<provider>.error}`
// will be set to the error message returned by the authentication
// provider.
@@ -78,6 +84,8 @@ func (a *Authentication) Provision(ctx caddy.Context) error {
func (a Authentication) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
var user User
var candidate User
var hasCandidate bool
var authed bool
var err error
for provName, prov := range a.Providers {
@@ -94,19 +102,34 @@ func (a Authentication) ServeHTTP(w http.ResponseWriter, r *http.Request, next c
if authed {
break
}
if userHasInfo(user) {
candidate = user
hasCandidate = true
}
}
if !authed {
if hasCandidate {
setAuthUserPlaceholders(repl, "http.auth.candidate", candidate)
}
return caddyhttp.Error(http.StatusUnauthorized, fmt.Errorf("not authenticated"))
}
repl.Set("http.auth.user.id", user.ID)
for k, v := range user.Metadata {
repl.Set("http.auth.user."+k, v)
}
setAuthUserPlaceholders(repl, "http.auth.user", user)
return next.ServeHTTP(w, r)
}
func userHasInfo(user User) bool {
return user.ID != "" || len(user.Metadata) > 0
}
func setAuthUserPlaceholders(repl *caddy.Replacer, namespace string, user User) {
repl.Set(namespace+".id", user.ID)
for k, v := range user.Metadata {
repl.Set(namespace+"."+k, v)
}
}
// Authenticator is a type which can authenticate a request.
// If a request was not authenticated, it returns false. An
// error is only returned if authenticating the request fails
@@ -0,0 +1,197 @@
// Copyright 2015 Matthew Holt and The Caddy Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package caddyauth
import (
"context"
"errors"
"net/http"
"net/http/httptest"
"testing"
"go.uber.org/zap"
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
)
func TestAuthenticationRejectedUserSetsCandidatePlaceholders(t *testing.T) {
auth := Authentication{
Providers: map[string]Authenticator{
"test": staticAuthenticator{
user: User{
ID: "alice",
Metadata: map[string]string{
"role": "admin",
},
},
},
},
logger: zap.NewNop(),
}
req, repl := newRequestWithReplacer()
nextCalled := false
err := auth.ServeHTTP(httptest.NewRecorder(), req, caddyhttp.HandlerFunc(func(http.ResponseWriter, *http.Request) error {
nextCalled = true
return nil
}))
if err == nil {
t.Fatal("expected authentication error")
}
var handlerErr caddyhttp.HandlerError
if !errors.As(err, &handlerErr) {
t.Fatalf("expected HandlerError, got %T", err)
}
if handlerErr.StatusCode != http.StatusUnauthorized {
t.Fatalf("expected status %d, got %d", http.StatusUnauthorized, handlerErr.StatusCode)
}
if nextCalled {
t.Fatal("next handler was called for rejected authentication")
}
assertPlaceholder(t, repl, "http.auth.candidate.id", "alice")
assertPlaceholder(t, repl, "http.auth.candidate.role", "admin")
assertPlaceholderAbsent(t, repl, "http.auth.user.id")
assertPlaceholderAbsent(t, repl, "http.auth.user.role")
}
func TestAuthenticationSuccessfulUserSetsUserPlaceholdersOnly(t *testing.T) {
auth := Authentication{
Providers: map[string]Authenticator{
"test": staticAuthenticator{
user: User{
ID: "alice",
Metadata: map[string]string{
"role": "admin",
},
},
authed: true,
},
},
logger: zap.NewNop(),
}
req, repl := newRequestWithReplacer()
nextCalled := false
err := auth.ServeHTTP(httptest.NewRecorder(), req, caddyhttp.HandlerFunc(func(http.ResponseWriter, *http.Request) error {
nextCalled = true
return nil
}))
if err != nil {
t.Fatalf("expected no authentication error, got %v", err)
}
if !nextCalled {
t.Fatal("next handler was not called for successful authentication")
}
assertPlaceholder(t, repl, "http.auth.user.id", "alice")
assertPlaceholder(t, repl, "http.auth.user.role", "admin")
assertPlaceholderAbsent(t, repl, "http.auth.candidate.id")
assertPlaceholderAbsent(t, repl, "http.auth.candidate.role")
}
func TestAuthenticationSuccessfulProviderDoesNotExposeEarlierCandidate(t *testing.T) {
auth := Authentication{
Providers: map[string]Authenticator{
"first": staticAuthenticator{
user: User{
ID: "rejected",
Metadata: map[string]string{
"role": "guest",
},
},
},
"second": staticAuthenticator{
user: User{
ID: "accepted",
Metadata: map[string]string{
"role": "admin",
},
},
authed: true,
},
},
logger: zap.NewNop(),
}
req, repl := newRequestWithReplacer()
err := auth.ServeHTTP(httptest.NewRecorder(), req, caddyhttp.HandlerFunc(func(http.ResponseWriter, *http.Request) error {
return nil
}))
if err != nil {
t.Fatalf("expected no authentication error, got %v", err)
}
assertPlaceholder(t, repl, "http.auth.user.id", "accepted")
assertPlaceholder(t, repl, "http.auth.user.role", "admin")
assertPlaceholderAbsent(t, repl, "http.auth.candidate.id")
assertPlaceholderAbsent(t, repl, "http.auth.candidate.role")
}
func TestAuthenticationRejectedEmptyUserDoesNotSetCandidatePlaceholders(t *testing.T) {
auth := Authentication{
Providers: map[string]Authenticator{
"test": staticAuthenticator{},
},
logger: zap.NewNop(),
}
req, repl := newRequestWithReplacer()
err := auth.ServeHTTP(httptest.NewRecorder(), req, caddyhttp.HandlerFunc(func(http.ResponseWriter, *http.Request) error {
t.Fatal("next handler was called for rejected authentication")
return nil
}))
if err == nil {
t.Fatal("expected authentication error")
}
assertPlaceholderAbsent(t, repl, "http.auth.candidate.id")
}
func newRequestWithReplacer() (*http.Request, *caddy.Replacer) {
req := httptest.NewRequest(http.MethodGet, "/", nil)
repl := caddy.NewReplacer()
ctx := context.WithValue(req.Context(), caddy.ReplacerCtxKey, repl)
return req.WithContext(ctx), repl
}
func assertPlaceholder(t *testing.T, repl *caddy.Replacer, key, expected string) {
t.Helper()
actual, ok := repl.GetString(key)
if !ok {
t.Fatalf("expected placeholder %q to be set", key)
}
if actual != expected {
t.Fatalf("expected placeholder %q to be %q, got %q", key, expected, actual)
}
}
func assertPlaceholderAbsent(t *testing.T, repl *caddy.Replacer, key string) {
t.Helper()
if actual, ok := repl.GetString(key); ok {
t.Fatalf("expected placeholder %q to be absent, got %q", key, actual)
}
}
type staticAuthenticator struct {
user User
authed bool
err error
}
func (a staticAuthenticator) Authenticate(http.ResponseWriter, *http.Request) (User, bool, error) {
return a.user, a.authed, a.err
}
+3 -3
View File
@@ -108,7 +108,7 @@ func (m *MatchExpression) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, &m.Expr)
}
// otherwise, it's a full object, so unmarshal it,
// using an temp map to avoid infinite recursion
// using a temp map to avoid infinite recursion
var tmpJson map[string]any
err := json.Unmarshal(data, &tmpJson)
*m = MatchExpression{
@@ -118,7 +118,7 @@ func (m *MatchExpression) UnmarshalJSON(data []byte) error {
return err
}
// Provision sets ups m.
// Provision sets up m.
func (m *MatchExpression) Provision(ctx caddy.Context) error {
m.log = ctx.Logger()
@@ -319,7 +319,7 @@ func (cr celHTTPRequest) Value() any { return cr }
var pkixNameCELType = cel.ObjectType("pkix.Name", traits.ReceiverType)
// celPkixName wraps an pkix.Name with
// celPkixName wraps a pkix.Name with
// methods to satisfy the ref.Val interface.
type celPkixName struct{ *pkix.Name }
+1 -1
View File
@@ -79,7 +79,7 @@ eqp31wM9il1n+guTNyxJd+FzVAH+hCZE5K+tCgVDdVFUlDEHHbS/wqb2PSIoouLV
wantResult: true,
},
{
name: "header matches an placeholder replaced during the header matcher (MatchHeader)",
name: "header matches a placeholder replaced during the header matcher (MatchHeader)",
expression: &MatchExpression{
Expr: `header({'Field': '\{http.request.uri.path}'})`,
},
+15 -7
View File
@@ -20,12 +20,12 @@
package encode
import (
"cmp"
"fmt"
"io"
"math"
"net/http"
"slices"
"sort"
"strconv"
"strings"
"sync"
@@ -127,6 +127,14 @@ func (enc *Encode) Provision(ctx caddy.Context) error {
}
}
if len(enc.Prefer) == 0 {
for _, encName := range []string{"zstd", "br", "gzip"} {
if _, ok := enc.writerPools[encName]; ok {
enc.Prefer = append(enc.Prefer, encName)
}
}
}
return nil
}
@@ -162,7 +170,7 @@ func (enc *Encode) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyh
// to comply with RFC 9110 section 8.8.3(.3), we modify the Etag when encoding
// by appending a hyphen and the encoder name; the problem is, the client will
// send back that Etag in a If-None-Match header, but upstream handlers that set
// send back that Etag in an If-None-Match header, but upstream handlers that set
// the Etag in the first place don't know that we appended to their Etag! so here
// we have to strip our addition so the upstream handlers can still honor client
// caches without knowing about our changes...
@@ -369,7 +377,7 @@ const sniffLen = 512
// ReadFrom will try to use sendfile to copy from the reader to the response writer.
// It's only used if the response writer implements io.ReaderFrom and the data can't be compressed.
// It's based on stdlin http1.1 response writer implementation.
// It's based on the standard library HTTP/1.1 response writer implementation.
// https://github.com/golang/go/blob/f4e3ec3dbe3b8e04a058d266adf8e048bab563f2/src/net/http/server.go#L586
func (rw *responseWriter) ReadFrom(r io.Reader) (int64, error) {
rf, ok := rw.ResponseWriter.(io.ReaderFrom)
@@ -538,11 +546,11 @@ func AcceptedEncodings(r *http.Request, preferredOrder []string) []string {
}
// sort preferences by descending q-factor first, then by preferOrder
sort.Slice(prefs, func(i, j int) bool {
if math.Abs(prefs[i].q-prefs[j].q) < 0.00001 {
return prefs[i].preferOrder > prefs[j].preferOrder
slices.SortStableFunc(prefs, func(a, b encodingPreference) int {
if math.Abs(a.q-b.q) < 0.00001 {
return cmp.Compare(b.preferOrder, a.preferOrder)
}
return prefs[i].q > prefs[j].q
return cmp.Compare(b.q, a.q)
})
prefEncNames := make([]string, len(prefs))
+75
View File
@@ -1,10 +1,16 @@
package encode
import (
"context"
"io"
"net/http"
"net/http/httptest"
"slices"
"sync"
"testing"
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
)
func BenchmarkOpenResponseWriter(b *testing.B) {
@@ -295,3 +301,72 @@ func TestIsEncodeAllowed(t *testing.T) {
})
}
}
type mockEncoder struct{}
func (mockEncoder) Write(p []byte) (n int, err error) { return len(p), nil }
func (mockEncoder) Close() error { return nil }
func (mockEncoder) Reset(w io.Writer) {}
func (mockEncoder) Flush() error { return nil }
func TestServeHTTPDefaultEncodingPreference(t *testing.T) {
enc := new(Encode)
enc.MinLength = 1 // compress everything
enc.writerPools = map[string]*sync.Pool{
"gzip": {
New: func() any { return mockEncoder{} },
},
"zstd": {
New: func() any { return mockEncoder{} },
},
}
// Call Provision() with a valid caddy.Context to exercise the real path
ctx, cancel := caddy.NewContext(caddy.Context{Context: context.Background()})
defer cancel()
if err := enc.Provision(ctx); err != nil {
t.Fatalf("Provision failed: %v", err)
}
// Test default preference: zstd preferred over gzip
r, err := http.NewRequest("GET", "/", nil)
if err != nil {
t.Fatalf("error creating request: %v", err)
}
r.Header.Set("Accept-Encoding", "gzip, deflate, br, zstd")
w := httptest.NewRecorder()
w.Header().Set("Content-Type", "text/plain")
next := caddyhttp.HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
w.WriteHeader(http.StatusOK)
_, err := w.Write([]byte("Hello, world! This is a long enough string to satisfy min length if it wasn't 1."))
return err
})
err = enc.ServeHTTP(w, r, next)
if err != nil {
t.Fatalf("ServeHTTP returned error: %v", err)
}
// ETag suffix or Content-Encoding header should reflect zstd
contentEncoding := w.Header().Get("Content-Encoding")
if contentEncoding != "zstd" {
t.Errorf("Expected Content-Encoding to be 'zstd' by default, got '%s'", contentEncoding)
}
// Test explicit user preference: gzip over zstd
enc.Prefer = []string{"gzip", "zstd"}
w2 := httptest.NewRecorder()
w2.Header().Set("Content-Type", "text/plain")
err = enc.ServeHTTP(w2, r, next)
if err != nil {
t.Fatalf("ServeHTTP returned error: %v", err)
}
contentEncoding2 := w2.Header().Get("Content-Encoding")
if contentEncoding2 != "gzip" {
t.Errorf("Expected Content-Encoding to be 'gzip' when explicitly preferred, got '%s'", contentEncoding2)
}
}
+23 -4
View File
@@ -29,6 +29,7 @@ import (
"runtime"
"strconv"
"strings"
"time"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
@@ -123,7 +124,7 @@ type FileServer struct {
// put "hidden" in the list. To hide only ./hidden, put "./hidden" in the list.
//
// When possible, all paths are resolved to their absolute form before
// comparisons are made. For maximum clarity and explictness, use complete,
// comparisons are made. For maximum clarity and explicitness, use complete,
// absolute paths; or, for greater portability, use relative paths instead.
//
// Note that hide comparisons are case-sensitive. On case-insensitive
@@ -579,7 +580,17 @@ func (fsrv *FileServer) ServeHTTP(w http.ResponseWriter, r *http.Request, next c
// that errors generated by ServeContent are written immediately
// to the response, so we cannot handle them (but errors there
// are rare)
http.ServeContent(w, r, info.Name(), info.ModTime(), file.(io.ReadSeeker))
//
// There are a few file modification times that aren't useful
// to send in Last-Modified headers, but the golang http library only
// omits Last-Modified headers for the Unix epoch time. So, force
// the modification time to the epoch time if it's not useful.
zeroTime := time.Time{}
modTime := info.ModTime()
if !usefulModTime(modTime) {
modTime = zeroTime
}
http.ServeContent(w, r, info.Name(), modTime, file.(io.ReadSeeker))
return nil
}
@@ -726,6 +737,14 @@ func (fsrv *FileServer) notFound(w http.ResponseWriter, r *http.Request, next ca
return caddyhttp.Error(http.StatusNotFound, nil)
}
// Indicates whether a file's modification time is useful for validator
// generation purposes (i.e. inclusion in ETag and Last-Modified headers).
// See issues #5548 and #7730.
func usefulModTime(modTime time.Time) bool {
mtimeunix := modTime.Unix()
return mtimeunix != 0 && mtimeunix != 1
}
// calculateEtag computes an entity tag using a strong validator
// without consuming the contents of the file. It requires the
// file info contain the correct size and modification time.
@@ -743,8 +762,8 @@ func (fsrv *FileServer) notFound(w http.ResponseWriter, r *http.Request, next ca
// which we consider precise enough to qualify as a strong validator.
func calculateEtag(d os.FileInfo) string {
mtime := d.ModTime()
if mtimeUnix := mtime.Unix(); mtimeUnix == 0 || mtimeUnix == 1 {
return "" // not useful anyway; see issue #5548
if !usefulModTime(mtime) {
return ""
}
var sb strings.Builder
sb.WriteRune('"')
@@ -15,10 +15,17 @@
package fileserver
import (
"context"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"runtime"
"strings"
"testing"
"time"
"github.com/caddyserver/caddy/v2"
)
func TestFileHidden(t *testing.T) {
@@ -128,3 +135,52 @@ func TestFileHidden(t *testing.T) {
}
}
}
// Check to make sure that we don't serve ETag and Last-Modified headers
// for files with invalid modification times
func TestModTimeHeaders(t *testing.T) {
check_validator_headers(time.Now(), true, t)
check_validator_headers(time.Unix(0, 0), false, t)
check_validator_headers(time.Unix(1, 0), false, t)
check_validator_headers(time.Unix(2, 0), true, t)
}
func check_validator_headers(modTime time.Time, expect_headers bool, t *testing.T) {
f := false
fsrv := FileServer{
Root: "./testdata",
CanonicalURIs: &f,
}
w := httptest.NewRecorder()
r, err := http.NewRequest("GET", "/modtime.txt", nil)
if err != nil {
t.Fatal(err)
}
repl := caddy.NewReplacer()
ctx := context.WithValue(r.Context(), caddy.ReplacerCtxKey, repl)
r = r.WithContext(ctx)
ctx2, _ := caddy.NewContext(caddy.Context{Context: context.Background()}) // module will be nil by default
fsrv.Provision(ctx2)
path := "testdata/modtime.txt"
os.Chtimes(path, modTime, modTime)
fsrv.ServeHTTP(w, r, nil)
if expect_headers {
if w.Header().Get("ETag") == "" {
t.Errorf("Didn't get ETag header for file with valid mod time %s", modTime)
}
if w.Header().Get("Last-Modified") == "" {
t.Errorf("Didn't get Last-Modified header for file with valid mod time %s", modTime)
}
} else {
if w.Header().Get("ETag") != "" {
t.Errorf("Got ETag header for file with invalid mod time %s", modTime)
}
if w.Header().Get("Last-Modified") != "" {
t.Errorf("Got Last-Modified header for file with invalid mod time %s", modTime)
}
}
}
View File
+1 -1
View File
@@ -15,7 +15,7 @@ type connectionStater interface {
// http2Listener wraps the listener to solve the following problems:
// 1. prevent genuine h2c connections from succeeding if h2c is not enabled
// and the connection doesn't implment connectionStater or the resulting NegotiatedProtocol
// and the connection doesn't implement connectionStater or the resulting NegotiatedProtocol
// isn't http2.
// This does allow a connection to pass as tls enabled even if it's not, listener wrappers
// can do this.
+1 -1
View File
@@ -101,7 +101,7 @@ type httpRedirectConn struct {
// Read tries to peek at the first few bytes of the request, and if we get
// an error reading the headers, and that error was due to the bytes looking
// like an HTTP request, then we perform a HTTP->HTTPS redirect on the same
// like an HTTP request, then we perform an HTTP->HTTPS redirect on the same
// port as the original connection.
func (c *httpRedirectConn) Read(p []byte) (int, error) {
if c.once {
+19 -6
View File
@@ -435,12 +435,12 @@ func (m MatchPath) MatchWithError(r *http.Request) (bool, error) {
// can be used instead.
reqPath := strings.ToLower(r.URL.Path)
// See #2917; Windows ignores trailing dots and spaces
// when accessing files (sigh), potentially causing a
// security risk (cry) if PHP files end up being served
// as static files, exposing the source code, instead of
// being matched by *.php to be treated as PHP scripts.
if runtime.GOOS == "windows" { // issue #5613
// Windows treats backslashes as path separators and
// ignores trailing dots and spaces when accessing files
// (sigh), potentially causing a security risk (cry) if
// protected files are not matched as intended.
reqPath = strings.ReplaceAll(reqPath, `\`, "/")
reqPath = strings.TrimRight(reqPath, ". ")
}
@@ -478,7 +478,12 @@ func (m MatchPath) MatchWithError(r *http.Request) (bool, error) {
// the intent is to compare that part of the path in raw/escaped
// space; i.e. "%40"=="%40", not "@", and "%2F"=="%2F", not "/"
if strings.Contains(matchPattern, "%") {
reqPathForPattern := CleanPath(r.URL.EscapedPath(), mergeSlashes)
escapedPath := r.URL.EscapedPath()
if runtime.GOOS == "windows" {
escapedPath = windowsEscapedPathSeparatorRepl.Replace(escapedPath)
matchPattern = windowsEscapedPathSeparatorRepl.Replace(matchPattern)
}
reqPathForPattern := CleanPath(escapedPath, mergeSlashes)
if m.matchPatternWithEscapeSequence(reqPathForPattern, matchPattern) {
return true, nil
}
@@ -643,6 +648,14 @@ func (MatchPath) matchPatternWithEscapeSequence(escapedPath, matchPath string) b
return matches
}
// windowsEscapedPathSeparatorRepl normalizes Windows backslash separators
// while preserving escaped-path matching semantics.
var windowsEscapedPathSeparatorRepl = strings.NewReplacer(
`\`, "%2f",
"%5c", "%2f",
"%5C", "%2f",
)
// CELLibrary produces options that expose this matcher for use in CEL
// expression matchers.
//
+53 -10
View File
@@ -461,18 +461,61 @@ func TestPathMatcherWindows(t *testing.T) {
return
}
req := &http.Request{URL: &url.URL{Path: "/index.php . . .."}}
repl := caddy.NewReplacer()
ctx := context.WithValue(req.Context(), caddy.ReplacerCtxKey, repl)
req = req.WithContext(ctx)
match := MatchPath{"*.php"}
matched, err := match.MatchWithError(req)
if err != nil {
t.Errorf("Expected no error, but got: %v", err)
}
if !matched {
t.Errorf("Expected to match; should ignore trailing dots and spaces")
for _, tc := range []struct {
name string
path string
requestTarget string
match MatchPath
}{
{
name: "trailing dots and spaces",
path: "/index.php . . ..",
match: MatchPath{"*.php"},
},
{
name: "encoded backslash path separator",
requestTarget: `/private%5csecret.txt`,
match: MatchPath{"/private/*"},
},
{
name: "encoded backslash path separator with escaped wildcard",
requestTarget: `/private%5csecret.txt`,
match: MatchPath{"/private/%*"},
},
{
name: "uppercase encoded backslash path separator with escaped wildcard",
requestTarget: `/private%5Csecret.txt`,
match: MatchPath{"/private/%*"},
},
{
name: "encoded backslash in escaped pattern",
requestTarget: `/private%5csecret.txt`,
match: MatchPath{"/private%5c%*"},
},
} {
t.Run(tc.name, func(t *testing.T) {
u := &url.URL{Path: tc.path}
if tc.requestTarget != "" {
var err error
u, err = url.ParseRequestURI(tc.requestTarget)
if err != nil {
t.Fatalf("Parsing request target: %v", err)
}
}
req := &http.Request{URL: u}
ctx := context.WithValue(req.Context(), caddy.ReplacerCtxKey, repl)
req = req.WithContext(ctx)
matched, err := tc.match.MatchWithError(req)
if err != nil {
t.Errorf("Expected no error, but got: %v", err)
}
if !matched {
t.Errorf("Expected %q to match %v", req.URL.Path, tc.match)
}
})
}
}
@@ -135,8 +135,8 @@ type client struct {
logger *zap.Logger
}
// Do made the request and returns a io.Reader that translates the data read
// from fcgi responder out of fcgi packet before returning it.
// Do makes the request and returns an io.Reader that translates the data read
// from the FastCGI responder out of FastCGI packets before returning it.
func (c *client) Do(p map[string]string, req io.Reader) (r io.Reader, err error) {
// check for CONTENT_LENGTH, since the lack of it or wrong value will cause the backend to hang
if clStr, ok := p["CONTENT_LENGTH"]; !ok {
@@ -179,7 +179,7 @@ func (c *client) Do(p map[string]string, req io.Reader) (r io.Reader, err error)
return r, err
}
// clientCloser is a io.ReadCloser. It wraps a io.Reader with a Closer
// clientCloser is an io.ReadCloser. It wraps an io.Reader with a Closer
// that closes the client connection.
type clientCloser struct {
rwc net.Conn
@@ -208,8 +208,8 @@ func (f clientCloser) Close() error {
return f.rwc.Close()
}
// Request returns a HTTP Response with Header and Body
// from fcgi responder
// Request returns an HTTP response with header and body
// from the FastCGI responder.
func (c *client) Request(p map[string]string, req io.Reader) (resp *http.Response, err error) {
r, err := c.Do(p, req)
if err != nil {
@@ -507,7 +507,7 @@ var tlsProtocolStrings = map[uint16]string{
tls.VersionTLS13: "TLSv1.3",
}
var headerNameReplacer = strings.NewReplacer(" ", "_", "-", "_")
var headerNameReplacer = strings.NewReplacer("-", "_")
// Interface guards
var (
@@ -304,6 +304,30 @@ func TestSplitPosUnicodeSecurityRegression(t *testing.T) {
}
}
// TestHeaderNameReplacer asserts the CGI header-to-env normalization rule:
// hyphens are mapped to underscores while every other character (including
// spaces) is passed through. Spaces are not RFC 7230 tokens, so they cannot
// reach this function from the wire; the only header names that survive
// untouched at the server layer are sanitized by the underscore filter in
// caddyhttp.Server.serveHTTP (see GHSA-f59h-q822-g45g).
func TestHeaderNameReplacer(t *testing.T) {
tests := []struct {
in, want string
}{
{"X-Forwarded-For", "X_Forwarded_For"},
{"Remote-User", "Remote_User"},
// Underscores are preserved (the server has already dropped any
// underscore-named headers when the filter is on).
{"Remote_User", "Remote_User"},
// Spaces are not rewritten because Go's HTTP parser rejects whitespace in
// header field names.
{"Foo Bar", "Foo Bar"},
}
for _, tt := range tests {
assert.Equal(t, tt.want, headerNameReplacer.Replace(tt.in), "input %q", tt.in)
}
}
// TestSplitPosSecurityRegressionUnicodeBypass guards against the FrankenPHP
// advisories GHSA-3g8v-8r37-cgjm (uninitialized match flag on inner non-ASCII
// byte) and GHSA-v4h7-cj44-8fc8 (Unicode equivalence via search.IgnoreCase
@@ -522,7 +522,7 @@ func (h *Handler) doActiveHealthCheck(dialInfo DialInfo, hostAddr string, networ
body = io.LimitReader(body, h.HealthChecks.Active.MaxSize)
}
defer func() {
// drain any remaining body so connection could be re-used
// drain any remaining body so connection could be reused
_, _ = io.Copy(io.Discard, body)
resp.Body.Close()
}()
@@ -32,6 +32,7 @@ import (
"time"
"github.com/pires/go-proxyproto"
"github.com/quic-go/quic-go"
"github.com/quic-go/quic-go/http3"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
@@ -161,7 +162,8 @@ type HTTPTransport struct {
// `HTTPS_PROXY`, and `NO_PROXY` environment variables.
NetworkProxyRaw json.RawMessage `json:"network_proxy,omitempty" caddy:"namespace=caddy.network_proxy inline_key=from"`
h3Transport *http3.Transport // TODO: EXPERIMENTAL (May 2024)
h3Transport *http3.Transport // TODO: EXPERIMENTAL (May 2024)
quicTransport *quic.Transport // used by h3Transport if sni placeholder is used, otherwise nil
}
// CaddyModule returns the Caddy module information.
@@ -499,6 +501,25 @@ func (h *HTTPTransport) NewTransport(caddyCtx caddy.Context) (*http.Transport, e
if err != nil {
return nil, fmt.Errorf("making TLS client config for HTTP/3 transport: %v", err)
}
if strings.Contains(h.TLS.ServerName, "{") {
// copied from quic-go
udpConn, err := net.ListenUDP("udp", nil)
if err != nil {
return nil, fmt.Errorf("making udp socket for HTTP/3 transport: %v", err)
}
h.quicTransport = &quic.Transport{Conn: udpConn}
h.h3Transport.Dial = func(ctx context.Context, addr string, tlsCfg *tls.Config, cfg *quic.Config) (*quic.Conn, error) {
// tlsCfg is already cloned from h3Transport.TLSClientConfig
repl := ctx.Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
tlsCfg.ServerName = repl.ReplaceAll(tlsCfg.ServerName, "")
udpAddr, err := resolveUDPAddr(ctx, "udp", addr)
if err != nil {
return nil, err
}
return h.quicTransport.DialEarly(ctx, udpAddr, tlsCfg, cfg)
}
}
}
} else if len(h.Versions) > 1 && slices.Contains(h.Versions, "3") {
return nil, fmt.Errorf("if HTTP/3 is enabled to the upstream, no other HTTP versions are supported")
@@ -525,6 +546,71 @@ func (h *HTTPTransport) NewTransport(caddyCtx caddy.Context) (*http.Transport, e
return rt, nil
}
// TODO: EXPERIMENTAL (May 2025)
// copied from quic-go
func resolveUDPAddr(ctx context.Context, network, addr string) (*net.UDPAddr, error) {
host, portStr, err := net.SplitHostPort(addr)
if err != nil {
return nil, err
}
port, err := net.LookupPort(network, portStr)
if err != nil {
return nil, err
}
resolver := net.DefaultResolver
ipAddrs, err := resolver.LookupIPAddr(ctx, host)
if err != nil {
return nil, err
}
addrs := addrList(ipAddrs)
ip := addrs.forResolve(network, addr)
return &net.UDPAddr{IP: ip.IP, Port: port, Zone: ip.Zone}, nil
}
// TODO: EXPERIMENTAL (May 2025)
// copied from quic-go
// An addrList represents a list of network endpoint addresses.
// Copy from [net.addrList] and change type from [net.Addr] to [net.IPAddr]
type addrList []net.IPAddr
// isIPv4 reports whether addr contains an IPv4 address.
func isIPv4(addr net.IPAddr) bool {
return addr.IP.To4() != nil
}
// isNotIPv4 reports whether addr does not contain an IPv4 address.
func isNotIPv4(addr net.IPAddr) bool { return !isIPv4(addr) }
// forResolve returns the most appropriate address in address for
// a call to ResolveTCPAddr, ResolveUDPAddr, or ResolveIPAddr.
// IPv4 is preferred, unless addr contains an IPv6 literal.
func (addrs addrList) forResolve(network, addr string) net.IPAddr {
var want6 bool
switch network {
case "ip":
// IPv6 literal (addr does NOT contain a port)
want6 = strings.ContainsRune(addr, ':')
case "tcp", "udp":
// IPv6 literal. (addr contains a port, so look for '[')
want6 = strings.ContainsRune(addr, '[')
}
if want6 {
return addrs.first(isNotIPv4)
}
return addrs.first(isIPv4)
}
// first returns the first address which satisfies strategy, or if
// none do, then the first address of any kind.
func (addrs addrList) first(strategy func(net.IPAddr) bool) net.IPAddr {
for _, addr := range addrs {
if strategy(addr) {
return addr
}
}
return addrs[0]
}
// RequestHeaderOps implements TransportHeaderOpsProvider. It returns header
// operations for requests when the transport's configuration indicates they
// should be applied. In particular, when TLS is enabled for this transport,
@@ -623,6 +709,16 @@ func (h HTTPTransport) Cleanup() error {
return nil
}
h.Transport.CloseIdleConnections()
// h3 related cleanup, errors are ignored as nothing can be done.
// TODO: log these errors if any
if h.h3Transport != nil {
h.h3Transport.CloseIdleConnections()
_ = h.h3Transport.Close()
if h.quicTransport != nil {
_ = h.quicTransport.Close()
_ = h.quicTransport.Conn.Close()
}
}
return nil
}
@@ -4,11 +4,14 @@ import (
"context"
"encoding/json"
"fmt"
"net"
"net/url"
"reflect"
"testing"
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
)
func TestHTTPTransportUnmarshalCaddyFileWithCaPools(t *testing.T) {
@@ -194,3 +197,85 @@ func TestHTTPTransport_DialTLSContext_ProxyProtocol(t *testing.T) {
})
}
}
// TestHTTPTransport_DialContext_DialInfoOverride is a regression test for
// issue #6447: a `tcp4/`-prefixed upstream silently fell back to plain `tcp`
// because dialContext only honored DialInfo for unix networks. PR #7300 widened
// the condition so DialInfo is honored when no upstream HTTP proxy is in use,
// and skipped (for non-unix networks) when one is. Both halves are pinned here.
func TestHTTPTransport_DialContext_DialInfoOverride(t *testing.T) {
ctx, cancel := caddy.NewContext(caddy.Context{Context: context.Background()})
defer cancel()
ln, err := net.Listen("tcp4", "127.0.0.1:0")
if err != nil {
t.Fatalf("listen: %v", err)
}
t.Cleanup(func() { ln.Close() })
go func() {
for {
c, err := ln.Accept()
if err != nil {
return
}
c.Close()
}
}()
ht := &HTTPTransport{}
rt, err := ht.NewTransport(ctx)
if err != nil {
t.Fatalf("NewTransport: %v", err)
}
proxyURL, err := url.Parse("http://proxy.example:8080")
if err != nil {
t.Fatalf("parse proxy URL: %v", err)
}
tests := []struct {
name string
proxy bool
dialInfo string
defaultAddr string
}{
{
// no proxy: DialInfo should be applied, so the dial lands on
// the live listener despite the bogus default address.
name: "honors DialInfo when no proxy",
proxy: false,
dialInfo: ln.Addr().String(),
defaultAddr: "127.0.0.1:1",
},
{
// proxy active: DialInfo must NOT be applied for non-unix
// networks; the default address (the live listener) is used.
name: "skips DialInfo when proxy active",
proxy: true,
dialInfo: "127.0.0.1:1",
defaultAddr: ln.Addr().String(),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
dialCtx := context.WithValue(context.Background(), caddyhttp.VarsCtxKey, make(map[string]any))
caddyhttp.SetVar(dialCtx, dialInfoVarKey, DialInfo{
Network: "tcp4",
Address: tt.dialInfo,
})
if tt.proxy {
caddyhttp.SetVar(dialCtx, proxyVarKey, proxyURL)
}
conn, err := rt.DialContext(dialCtx, "tcp", tt.defaultAddr)
if err != nil {
t.Fatalf("DialContext: %v", err)
}
t.Cleanup(func() { conn.Close() })
if got := conn.RemoteAddr().String(); got != ln.Addr().String() {
t.Fatalf("conn.RemoteAddr() = %s, want %s", got, ln.Addr().String())
}
})
}
}
@@ -730,3 +730,58 @@ func TestRetryMatchAllowsExpressionMixedWithOtherMatchers(t *testing.T) {
})
}
}
// TestSubrouteErrorFallbackWithBody is similar to TestDialErrorBodyRetry but
// mimics Subroute's Error handler rather than testing retries specifically
func TestSubrouteErrorFallbackWithBody(t *testing.T) {
// Good upstream: echoes the request body with 200 OK.
goodServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
body, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, "read body: "+err.Error(), http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
_, err = w.Write(body)
if err != nil {
t.Errorf("error writing in good server: %v", err)
}
}))
t.Cleanup(goodServer.Close)
// Handler which will dial error
badProxy := minimalHandler(0, &Upstream{Host: new(Host), Dial: deadUpstreamAddr(t)})
bodyReader := newCloseOnCloseReader("hello world")
req := httptest.NewRequest("POST", "http://localhost/", bodyReader)
// httptest.NewRequest wraps the reader in NopCloser; replace
// it with our close-aware reader so Close() is propagated.
req.Body = bodyReader
req = prepareTestRequest(req)
rec := httptest.NewRecorder()
err := badProxy.ServeHTTP(rec, req, caddyhttp.HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
return nil
}))
if err == nil {
t.Fatalf("Expected error from badProxy.ServeHTTP")
}
// Simulate the Subroute's Error handler by calling another handler with the
// same request and recorder
goodProxy := minimalHandler(0, &Upstream{Host: new(Host), Dial: goodServer.Listener.Addr().String()})
err = goodProxy.ServeHTTP(rec, req, caddyhttp.HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
return nil
}))
if err != nil {
t.Fatalf("Expected no error from goodProxy.ServeHTTP, got: %v", err)
}
if rec.Code != http.StatusOK {
t.Errorf("status: got %d, want %d", rec.Code, http.StatusOK)
}
expectedBody := "hello world"
if rec.Body.String() != expectedBody {
t.Errorf("body: got %q, want %q", rec.Body.String(), expectedBody)
}
}
+42 -10
View File
@@ -449,6 +449,39 @@ func (h *Handler) Cleanup() error {
return err
}
// bodyNopCloserIfNotRead wraps a request body to prevent closing if not read, i.e., when
// dialing to upstream fails.
// It will close the body as normal if the body is read.
type bodyNopCloserIfNotRead struct {
io.ReadCloser
read int // tracks the number of bytes read, -1 when first Read returns 0, io.EOF
}
func (b *bodyNopCloserIfNotRead) Read(p []byte) (int, error) {
if b.read == -1 {
return 0, io.EOF
}
n, err := b.ReadCloser.Read(p)
// first Read returns 0, io.EOF
if b.read == 0 && n == 0 && err == io.EOF {
b.read = -1
} else {
b.read += n
}
return n, err
}
func (b *bodyNopCloserIfNotRead) Close() error {
// don't close the body
if b.read == 0 {
return nil
}
// close as usual, when -1, any read will return EOF as the original read will do
// in other cases, the read will fail as body is closed because we do not want partial bodies to be sent to the upstream
// users can buffer the entire request body to allow the request to be resent
return b.ReadCloser.Close()
}
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
@@ -488,20 +521,19 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyht
reqHost := clonedReq.Host
reqHeader := clonedReq.Header
// When retries are configured and there is a body, wrap it in
// io.NopCloser to prevent Go's transport from closing it on dial
// errors. cloneRequest does a shallow copy, so clonedReq.Body and
// If the request contained a body, wrap it in io.NopCloser
// to prevent Go's transport from closing it on dial errors.
// cloneRequest does a shallow copy, so clonedReq.Body and
// r.Body share the same io.ReadCloser — a dial-failure Close()
// would kill the original body for all subsequent retry attempts.
// The real body is closed by the HTTP server when the handler
// returns.
// would kill the original body for all subsequent retry
// attempts or subsequent handlers. The real body is closed by
// the HTTP server when the handler returns.
//
// If the body was already fully buffered (via request_buffers),
// we also extract the buffer so the retry loop can replay it
// from the beginning on each attempt. (see #6259, #7546)
// from the beginning on each attempt. (see #6259, #7546, #7713)
var bufferedReqBody *bytes.Buffer
if clonedReq.Body != nil && h.LoadBalancing != nil &&
(h.LoadBalancing.Retries > 0 || h.LoadBalancing.TryDuration > 0) {
if clonedReq.Body != nil {
if reqBodyBuf, ok := clonedReq.Body.(bodyReadCloser); ok && reqBodyBuf.body == nil && reqBodyBuf.buf != nil {
bufferedReqBody = reqBodyBuf.buf
reqBodyBuf.buf = nil
@@ -511,7 +543,7 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyht
bufPool.Put(bufferedReqBody)
}()
} else {
clonedReq.Body = io.NopCloser(clonedReq.Body)
clonedReq.Body = &bodyNopCloserIfNotRead{ReadCloser: clonedReq.Body}
}
}
@@ -568,7 +568,7 @@ func TestQueryHashPolicy(t *testing.T) {
pool[1].setHealthy(false)
h = queryPolicy.Select(pool, request, nil)
if h != nil {
t.Error("Expected query policy policy host to be nil.")
t.Error("Expected query policy host to be nil.")
}
request = httptest.NewRequest(http.MethodGet, "/?foo=aa11&foo=bb22", nil)
@@ -630,7 +630,7 @@ func TestURIHashPolicy(t *testing.T) {
pool[1].setHealthy(false)
h = uriPolicy.Select(pool, request, nil)
if h != nil {
t.Error("Expected uri policy policy host to be nil.")
t.Error("Expected uri policy host to be nil.")
}
}
+9
View File
@@ -223,6 +223,15 @@ func (rewr Rewrite) Rewrite(r *http.Request, repl *caddy.Replacer) bool {
newPath, injectedQuery = before, after
// don't overwrite explicitly-configured query string
if query == "" {
// the injected query came from the first-pass placeholder
// expansion above, which means any '{' or '}' bytes in it
// must have come from replacement values (e.g. a request
// header), not from operator-written placeholder syntax.
// escape them so buildQueryString does not re-expand them,
// which would allow attacker input like {env.SECRET} to be
// evaluated (see GHSA-j8px-rmrx-76h9).
injectedQuery = strings.ReplaceAll(injectedQuery, "{", "%7B")
injectedQuery = strings.ReplaceAll(injectedQuery, "}", "%7D")
query = injectedQuery
}
}
+36
View File
@@ -18,6 +18,7 @@ import (
"net/http"
"reflect"
"regexp"
"strings"
"testing"
"github.com/caddyserver/caddy/v2"
@@ -350,6 +351,32 @@ func TestRewrite(t *testing.T) {
input: newRequest(t, "GET", "/foo//bar///baz?a=b//c"),
expect: newRequest(t, "GET", "/foo/bar/baz?a=b//c"),
},
// regression tests for GHSA-j8px-rmrx-76h9: when the rewrite URI
// ends with a literal '?', the first-pass placeholder expansion
// may produce a path containing attacker-controlled bytes that
// then get split at '?' and fed into buildQueryString, which runs
// a SECOND placeholder pass. Bytes injected via a header value (or
// any other client-controlled placeholder) must not be treated as
// placeholder syntax during this second pass.
{
// literal header value containing placeholder syntax is not re-expanded into query
rule: Rewrite{URI: "/serve/{http.request.header.X-Fwd}?"},
input: newRequestWithHeader(t, "GET", "/anything", "X-Fwd", "foo?{env.CADDY_REWRITE_TEST_SECRET}=leak"),
expect: newRequest(t, "GET", "/serve/foo?%7Benv.CADDY_REWRITE_TEST_SECRET%7D=leak"),
},
{
// literal header value with placeholder syntax in query position is not re-expanded
rule: Rewrite{URI: "/serve/{http.request.header.X-Fwd}?"},
input: newRequestWithHeader(t, "GET", "/anything", "X-Fwd", "ok?key={env.CADDY_REWRITE_TEST_SECRET}"),
expect: newRequest(t, "GET", "/serve/ok?key=%7Benv.CADDY_REWRITE_TEST_SECRET%7D"),
},
{
// literal header value with embedded file placeholder is not re-expanded
rule: Rewrite{URI: "/serve/{http.request.header.X-Fwd}?"},
input: newRequestWithHeader(t, "GET", "/anything", "X-Fwd", "ok?path={file./etc/passwd}"),
expect: newRequest(t, "GET", "/serve/ok?path=%7Bfile./etc/passwd%7D"),
},
} {
// copy the original input just enough so that we can
// compare it after the rewrite to see if it changed
@@ -364,6 +391,9 @@ func TestRewrite(t *testing.T) {
repl.Set("http.request.uri", tc.input.RequestURI)
repl.Set("http.request.uri.path", tc.input.URL.Path)
repl.Set("http.request.uri.query", tc.input.URL.RawQuery)
for field, vals := range tc.input.Header {
repl.Set("http.request.header."+field, strings.Join(vals, ","))
}
// we can't directly call Provision() without a valid caddy.Context
// (TODO: fix that) so here we ad-hoc compile the regex
@@ -456,6 +486,12 @@ func newRequest(t *testing.T, method, uri string) *http.Request {
return req
}
func newRequestWithHeader(t *testing.T, method, uri, headerKey, headerVal string) *http.Request {
req := newRequest(t, method, uri)
req.Header.Set(headerKey, headerVal)
return req
}
// reqEqual if r1 and r2 are equal enough for our purposes.
func reqEqual(r1, r2 *http.Request) bool {
if r1.Method != r2.Method {
+13
View File
@@ -494,6 +494,19 @@ func (s *Server) serveHTTP(w http.ResponseWriter, r *http.Request) error {
}
}
// Drop headers whose names contain `_`: once FastCGI/CGI/FrankenPHP etc. rewrites `-` to
// `_`, an underscore alias collides with the legitimate hyphenated header
// and can bypass `forward_auth copy_headers` (GHSA-f59h-q822-g45g).
for k := range r.Header {
if strings.ContainsRune(k, '_') {
delete(r.Header, k)
if c := s.logger.Check(zapcore.DebugLevel, "dropping header containing underscore"); c != nil {
c.Write(zap.String("header", k))
}
}
}
// execute the primary handler chain
return s.primaryHandlerChain.ServeHTTP(w, r)
}
+77
View File
@@ -1,16 +1,20 @@
package caddyhttp
import (
"bufio"
"bytes"
"context"
"io"
"net"
"net/http"
"net/http/httptest"
"net/netip"
"strings"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
@@ -478,6 +482,79 @@ func TestServer_DetermineTrustedProxy_MatchRightMostUntrustedSkippingTrusted(t *
assert.Equal(t, clientIP, "45.54.45.54")
}
// TestServer_serveHTTP_DropsUnderscoreHeader covers GHSA-f59h-q822-g45g: an
// underscore-named alias (e.g. `Remote_user`) of a hyphenated header must be
// dropped before any handler runs.
func TestServer_serveHTTP_DropsUnderscoreHeader(t *testing.T) {
got := &http.Header{}
s := &Server{
logger: zap.NewNop(),
primaryHandlerChain: HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
*got = r.Header.Clone()
return nil
}),
}
req := httptest.NewRequest(http.MethodGet, "http://example.com/", nil)
req.Header["X-Real-Header"] = []string{"ok"}
req.Header["Remote_user"] = []string{"attacker"}
req.Header["Remote_groups"] = []string{"admin"}
require.NoError(t, s.serveHTTP(httptest.NewRecorder(), req))
assert.NotContains(t, *got, "Remote_user")
assert.NotContains(t, *got, "Remote_groups")
assert.Equal(t, "ok", got.Get("X-Real-Header"))
}
// TestServer_serveHTTP_LogsDroppedUnderscoreHeader verifies each dropped
// header is emitted at debug level so operators can diagnose unexpectedly
// missing headers without spamming the log on adversarial traffic.
func TestServer_serveHTTP_LogsDroppedUnderscoreHeader(t *testing.T) {
var buf bytes.Buffer
s := &Server{
logger: testLogger(buf.Write),
primaryHandlerChain: HandlerFunc(func(http.ResponseWriter, *http.Request) error {
return nil
}),
}
req := httptest.NewRequest(http.MethodGet, "http://example.com/", nil)
req.Header["Remote_user"] = []string{"attacker"}
require.NoError(t, s.serveHTTP(httptest.NewRecorder(), req))
assert.Contains(t, buf.String(), `"level":"debug"`)
assert.Contains(t, buf.String(), `"msg":"dropping header containing underscore"`)
assert.Contains(t, buf.String(), `"header":"Remote_user"`)
}
// TestServer_SpaceInHeaderNameReturnsBadRequest documents why the underscore
// filter does not also strip space-named headers: Go's HTTP parser rejects a
// space in a field name with 400 before any handler runs, so such a request
// can never reach Caddy's pipeline.
func TestServer_SpaceInHeaderNameReturnsBadRequest(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
t.Errorf("handler must not be reached; got headers %v", r.Header)
}))
t.Cleanup(srv.Close)
addr := strings.TrimPrefix(srv.URL, "http://")
conn, err := net.DialTimeout("tcp", addr, 5*time.Second)
require.NoError(t, err)
t.Cleanup(func() { _ = conn.Close() })
require.NoError(t, conn.SetDeadline(time.Now().Add(5*time.Second)))
_, err = conn.Write([]byte("GET / HTTP/1.1\r\n" +
"Host: " + addr + "\r\n" +
"Remote User: attacker\r\n" +
"Connection: close\r\n\r\n"))
require.NoError(t, err)
resp, err := http.ReadResponse(bufio.NewReader(conn), nil)
require.NoError(t, err)
t.Cleanup(func() { _ = resp.Body.Close() })
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
}
func TestServer_DetermineTrustedProxy_MatchRightMostUntrustedFirst(t *testing.T) {
localPrivatePrefix, _ := netip.ParsePrefix("10.0.0.0/8")
+22 -25
View File
@@ -312,35 +312,32 @@ func (c TemplateContext) Host() (string, error) {
return host, nil
}
// funcStripHTML returns s without HTML tags. It is fairly naive
// but works with most valid HTML inputs.
// funcStripHTML returns s without HTML tags. Similar to PHP's strip_tags()
func (TemplateContext) funcStripHTML(s string) string {
var buf bytes.Buffer
var inTag, inQuotes bool
var tagStart int
for i, ch := range s {
if inTag {
if ch == '>' && !inQuotes {
inTag = false
} else if ch == '<' && !inQuotes {
// false start
buf.WriteString(s[tagStart:i])
tagStart = i
} else if ch == '"' {
inQuotes = !inQuotes
depth := 0
var quoteChar rune
for _, ch := range s {
switch {
case depth > 0 && quoteChar == 0 && (ch == '"' || ch == '\''):
// entering a quoted attribute value
quoteChar = ch
case depth > 0 && ch == quoteChar:
// leaving a quoted attribute value
quoteChar = 0
case ch == '<' && quoteChar == 0:
depth++
case ch == '>' && quoteChar == 0:
if depth > 0 {
depth--
} else {
buf.WriteRune(ch) // stray '>' with no opening '<', keep it
}
default:
if depth == 0 {
buf.WriteRune(ch)
}
continue
}
if ch == '<' {
inTag = true
tagStart = i
continue
}
buf.WriteRune(ch)
}
if inTag {
// false start
buf.WriteString(s[tagStart:])
}
return buf.String()
}
+35 -5
View File
@@ -419,14 +419,44 @@ func TestStripHTML(t *testing.T) {
expect: `h1`,
},
{
// tags not closed
// unclosed tag — trailing text must be stripped, not emitted
input: `<h1`,
expect: `<h1`,
expect: ``,
},
{
// false start
input: `<h1<b>hi`,
expect: `<h1hi`,
// false start — second '<' increments depth, single '>' only closes one level
input: `<h1<b>hi`,
expect: ``,
},
{
// XSS bypass via double opening bracket
input: `<<>img src=x onerror=alert('XSS')>`,
expect: ``,
},
{
// stacked angle brackets (PHP strip_tags parity)
input: `<<<<<>>>>><b>hello</b>`,
expect: `hello`,
},
{
// unclosed tag strips trailing text
input: `hello <world`,
expect: `hello `,
},
{
// '>' inside double-quoted attribute must not close tag early
input: `<a href="foo>bar">text</a>`,
expect: `text`,
},
{
// '>' inside single-quoted attribute must not close tag early
input: `<a href='foo>bar'>text</a>`,
expect: `text`,
},
{
// stray '>' with no opening '<' is preserved
input: `stray > bracket`,
expect: `stray > bracket`,
},
} {
actual := tplContext.funcStripHTML(test.input)
+1 -1
View File
@@ -158,7 +158,7 @@ type AutomationPolicy struct {
DisableOCSPStapling bool `json:"disable_ocsp_stapling,omitempty"`
// Overrides the URLs of OCSP responders embedded in certificates.
// Each key is a OCSP server URL to override, and its value is the
// Each key is an OCSP server URL to override, and its value is the
// replacement. An empty value will disable querying of that server.
// EXPERIMENTAL. Subject to change.
OCSPOverrides map[string]string `json:"ocsp_overrides,omitempty"`
+9 -7
View File
@@ -107,7 +107,8 @@ func (cp ConnectionPolicies) TLSConfig(ctx caddy.Context) *tls.Config {
if sni, ok := m.(MatchServerName); ok {
for _, sniName := range sni {
// index for fast lookups during handshakes
indexedBySNI[sniName] = append(indexedBySNI[sniName], p)
indexName := asciiServerNameForMatch(sniName)
indexedBySNI[indexName] = append(indexedBySNI[indexName], p)
}
}
}
@@ -118,7 +119,7 @@ func (cp ConnectionPolicies) TLSConfig(ctx caddy.Context) *tls.Config {
// filter policies by SNI first, if possible, to speed things up
// when there may be lots of policies
possiblePolicies := cp
if indexedPolicies, ok := indexedBySNI[hello.ServerName]; ok {
if indexedPolicies, ok := indexedBySNI[asciiServerNameForMatch(hello.ServerName)]; ok {
possiblePolicies = indexedPolicies
}
@@ -896,18 +897,19 @@ func (clientauth *ClientAuthentication) ConfigureTLSConfig(cfg *tls.Config) erro
// Unlike VerifyPeerCertificate, VerifyConnection is called on every
// connection including resumed sessions, preventing session-resumption bypass.
func (clientauth *ClientAuthentication) verifyConnection(cs tls.ConnectionState) error {
rawCerts := make([][]byte, len(cs.PeerCertificates))
for i, cert := range cs.PeerCertificates {
rawCerts[i] = cert.Raw
}
// first use any pre-existing custom verification function
if clientauth.existingVerifyPeerCert != nil {
rawCerts := make([][]byte, len(cs.PeerCertificates))
for i, cert := range cs.PeerCertificates {
rawCerts[i] = cert.Raw
}
if err := clientauth.existingVerifyPeerCert(rawCerts, cs.VerifiedChains); err != nil {
return err
}
}
for _, verifier := range clientauth.verifiers {
if err := verifier.VerifyClientCertificate(nil, cs.VerifiedChains); err != nil {
if err := verifier.VerifyClientCertificate(rawCerts, cs.VerifiedChains); err != nil {
return err
}
}
+36
View File
@@ -15,6 +15,8 @@
package caddytls
import (
"context"
"crypto/tls"
"encoding/json"
"fmt"
"reflect"
@@ -24,6 +26,40 @@ import (
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
)
func TestConnectionPolicyIDNSNIMatcherFastPath(t *testing.T) {
ctx, cancel := caddy.NewContext(caddy.Context{Context: context.Background()})
defer cancel()
targetTLSConfig := &tls.Config{ClientAuth: tls.RequireAnyClientCert}
policies := ConnectionPolicies{
{
matchers: []ConnectionMatcher{MatchServerName{"つ.Localhost"}},
TLSConfig: targetTLSConfig,
},
}
const sniFastPathThreshold = 30
for i := len(policies); i < sniFastPathThreshold; i++ {
policies = append(policies, &ConnectionPolicy{
matchers: []ConnectionMatcher{MatchServerName{fmt.Sprintf("example-%d.localhost", i)}},
TLSConfig: &tls.Config{},
})
}
policies = append(policies, &ConnectionPolicy{
matchers: []ConnectionMatcher{MatchServerName{"xn--k9j.localhost"}},
TLSConfig: &tls.Config{ClientAuth: tls.NoClientCert},
})
tlsConfig := policies.TLSConfig(ctx)
got, err := tlsConfig.GetConfigForClient(&tls.ClientHelloInfo{ServerName: "XN--K9J.LOCALHOST"})
if err != nil {
t.Fatalf("GetConfigForClient() error = %v", err)
}
if got != targetTLSConfig {
t.Fatalf("expected Unicode IDN policy to match before later punycode policy")
}
}
func TestClientAuthenticationUnmarshalCaddyfileWithDirectiveName(t *testing.T) {
const test_der_1 = `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==`
const test_cert_file_1 = "../../caddytest/caddy.ca.cer"
@@ -0,0 +1,59 @@
package caddytls
import (
"crypto/tls"
"crypto/x509"
"errors"
"reflect"
"testing"
)
type testClientCertificateVerifier struct {
rawCerts [][]byte
verifiedChains [][]*x509.Certificate
err error
}
func (v *testClientCertificateVerifier) VerifyClientCertificate(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
v.rawCerts = rawCerts
v.verifiedChains = verifiedChains
return v.err
}
func TestClientAuthenticationVerifyConnectionPassesRawCertsToVerifiers(t *testing.T) {
verifier := &testClientCertificateVerifier{}
clientauth := &ClientAuthentication{
verifiers: []ClientCertificateVerifier{verifier},
}
peerCert := &x509.Certificate{Raw: []byte("peer-cert-raw")}
verifiedChains := [][]*x509.Certificate{{peerCert}}
connState := tls.ConnectionState{
PeerCertificates: []*x509.Certificate{peerCert},
VerifiedChains: verifiedChains,
}
if err := clientauth.verifyConnection(connState); err != nil {
t.Fatalf("verifyConnection failed: %v", err)
}
if !reflect.DeepEqual(verifier.rawCerts, [][]byte{[]byte("peer-cert-raw")}) {
t.Fatalf("unexpected raw certs: got %#v", verifier.rawCerts)
}
if !reflect.DeepEqual(verifier.verifiedChains, verifiedChains) {
t.Fatalf("unexpected verified chains: got %#v", verifier.verifiedChains)
}
}
func TestClientAuthenticationVerifyConnectionReturnsVerifierError(t *testing.T) {
wantErr := errors.New("verify failed")
verifier := &testClientCertificateVerifier{err: wantErr}
clientauth := &ClientAuthentication{
verifiers: []ClientCertificateVerifier{verifier},
}
err := clientauth.verifyConnection(tls.ConnectionState{})
if !errors.Is(err, wantErr) {
t.Fatalf("expected error %v, got %v", wantErr, err)
}
}
+50 -2
View File
@@ -28,6 +28,7 @@ import (
"github.com/caddyserver/certmagic"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"golang.org/x/net/idna"
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
@@ -69,15 +70,62 @@ func (m MatchServerName) Match(hello *tls.ClientHelloInfo) bool {
repl = caddy.NewReplacer()
}
serverName := asciiServerNameForMatch(hello.ServerName)
for _, name := range m {
rs := repl.ReplaceAll(name, "")
if certmagic.MatchWildcard(hello.ServerName, rs) {
rs := asciiServerNameForMatch(repl.ReplaceAll(name, ""))
if certmagic.MatchWildcard(serverName, rs) {
return true
}
}
return false
}
func asciiServerNameForMatch(name string) string {
if name == "" {
return name
}
// Fast path: if the name is pure ASCII, skip idna.ToASCII.
// SNI values on the wire are always ASCII (RFC 6066), and most
// config patterns are also ASCII. For pure ASCII input, idna.ToASCII
// only validates and lowercases, which is equivalent to our fallback.
if isPureASCII(name) {
return strings.ToLower(name)
}
// Config can use Unicode IDNs.
ascii, err := idna.ToASCII(name)
if err == nil {
return strings.ToLower(ascii)
}
if !strings.Contains(name, "*") {
return strings.ToLower(name)
}
labels := strings.Split(name, ".")
for i, label := range labels {
if label == "" || label == "*" {
continue
}
ascii, err := idna.ToASCII(label)
if err != nil {
return strings.ToLower(name)
}
labels[i] = strings.ToLower(ascii)
}
return strings.Join(labels, ".")
}
func isPureASCII(s string) bool {
for i := 0; i < len(s); i++ {
if s[i] >= 0x80 {
return false
}
}
return true
}
// UnmarshalCaddyfile sets up the MatchServerName from Caddyfile tokens. Syntax:
//
// sni <domains...>
+20
View File
@@ -79,6 +79,26 @@ func TestServerNameMatcher(t *testing.T) {
input: "sub2.sub.example.com",
expect: true,
},
{
names: []string{"つ.localhost"},
input: "xn--k9j.localhost",
expect: true,
},
{
names: []string{"つ.Localhost"},
input: "XN--K9J.LOCALHOST",
expect: true,
},
{
names: []string{"*.つ.localhost"},
input: "sub.xn--k9j.localhost",
expect: true,
},
{
names: []string{"*.つ.Localhost"},
input: "Sub.XN--K9J.LOCALHOST",
expect: true,
},
} {
chi := &tls.ClientHelloInfo{ServerName: tc.input}
actual := MatchServerName(tc.names).Match(chi)
+2 -3
View File
@@ -137,11 +137,10 @@ func (s *SessionTicketService) stayUpdated() {
case newKeys := <-keysChan:
s.mu.Lock()
s.currentKeys = newKeys
configs := s.configs
s.mu.Unlock()
for cfg := range configs {
for cfg := range s.configs {
cfg.SetSessionTicketKeys(newKeys)
}
s.mu.Unlock()
case <-s.stopChan:
return
}
+3 -1
View File
@@ -440,7 +440,7 @@ func (t *TLS) Start() error {
t.EncryptedClientHello.configsMu.Unlock()
if err != nil {
echLogger.Error("rotating ECH configs failed", zap.Error(err))
return
continue
}
err := t.publishECHConfigs(echLogger)
if err != nil {
@@ -879,6 +879,8 @@ func (t *TLS) getAutomationPolicyForName(name string) *AutomationPolicy {
// AllMatchingCertificates returns the list of all certificates in
// the cache which could be used to satisfy the given SAN.
func AllMatchingCertificates(san string) []certmagic.Certificate {
certCacheMu.RLock()
defer certCacheMu.RUnlock()
return certCache.AllMatchingCertificates(san)
}
+2 -2
View File
@@ -149,10 +149,10 @@ func (f *ReplaceFilter) Filter(in zapcore.Field) zapcore.Field {
// list of IP addresses, where all of the values
// will be masked.
type IPMaskFilter struct {
// The IPv4 mask, as an subnet size CIDR.
// The IPv4 mask, as a subnet size CIDR.
IPv4MaskRaw int `json:"ipv4_cidr,omitempty"`
// The IPv6 mask, as an subnet size CIDR.
// The IPv6 mask, as a subnet size CIDR.
IPv6MaskRaw int `json:"ipv6_cidr,omitempty"`
v4Mask net.IPMask
+4 -8
View File
@@ -427,14 +427,10 @@ func readFileIntoBuffer(filename string, size int) ([]byte, error) {
}
defer file.Close()
buffer := make([]byte, size)
n, err := file.Read(buffer)
if err != nil && err != io.EOF {
return nil, err
}
// slice the buffer to the actual size
return buffer[:n], nil
// io.LimitReader ensures we never read more than 'size' bytes.
// io.ReadAll starts with a small buffer and grows it as needed,
// preventing a massive 1MB allocation for small files.
return io.ReadAll(io.LimitReader(file, int64(size)))
}
// ReplacementFunc is a function that is called when a