Compare commits

..

259 Commits

Author SHA1 Message Date
Matt Holt d0a3cf0a0a Merge branch 'master' into proxy-stream-detached 2026-05-12 12:18:27 -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
Matthew Holt cc58caa109 go.mod: Upgrade quic-go to v0.59.1
Tests / goreleaser-check (push) Has been skipped
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 1m32s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 2m47s
Cross-Build / build (~1.26.0, 1.26, aix) (push) Successful in 2m48s
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 3m24s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 3m25s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m23s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 2m50s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 2m53s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m21s
Lint / dependency-review (push) Failing after 1m14s
Lint / govulncheck (push) Successful in 1m44s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 4m26s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 4m27s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 7m5s
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-11 17:33:42 -06:00
Br1an d80774cb3f metrics: Add nil check for metricsHandler in AdminMetrics.serveHTTP (#7553)
* metrics: Add nil check for metricsHandler in AdminMetrics.serveHTTP

Prevents panic when the admin metrics endpoint is accessed before
the module is fully provisioned. Returns a proper API error instead
of crashing.

* admin: provision router modules before registering routes

Instead of adding a nil check for metricsHandler, address the root
cause by provisioning admin router modules before calling Routes().
This ensures all handler state is initialized before routes are
registered on the mux.

Merge newAdminHandler and provisionAdminRouters into a single step,
removing the two-phase setup where routes were registered first and
modules provisioned later. The AdminConfig.routers field is no longer
needed since provisioning happens inline.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix: go fmt admin.go

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-11 17:27:03 -06:00
Rayan Salhab a4a38c3e88 rewrite: escape file matcher paths before rewriting (#7683)
* fix: escape file matcher paths in rewrites

Preserve matched file paths containing literal '?' or '%' when try_files rewrites to http.matchers.file.relative.

* test: cover nested escaped try_files rewrite paths

* test: cover encoded slash try_files rewrite paths

* fix: assert file matcher placeholder as string

---------

Co-authored-by: cyphercodes <cyphercodes@users.noreply.github.com>
2026-05-11 17:16:33 -06:00
Matthew Holt 761347aa63 templates: Explicitly warn about misconfigurations 2026-05-11 16:45:49 -06:00
Steffen Busch 4ba16fe82c docs: add documentation for fileExists and fileStat template functions (#7700)
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 1m42s
Cross-Build / build (~1.26.0, 1.26, aix) (push) Successful in 2m37s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 3m36s
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 3m44s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 3m55s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 1m24s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 2m44s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m20s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 2m35s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 2m51s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 2m53s
Lint / govulncheck (push) Successful in 1m41s
Lint / lint (ubuntu-latest, linux) (push) Successful in 3m4s
Lint / dependency-review (push) Failing after 1m4s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 6m4s
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-12 04:23:58 +10:00
Rijul 0fab9f0f7d caddytls: avoid duplicate automation for wildcard-covered hosts (#7697)
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 1m24s
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 1m39s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 1m48s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 2m32s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m24s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 3m26s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 2m10s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 1m58s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m23s
Lint / dependency-review (push) Failing after 24s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 27s
Lint / govulncheck (push) Successful in 1m20s
Lint / lint (ubuntu-latest, linux) (push) Successful in 1m44s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 3m21s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 2m41s
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
* caddytls: Fix wildcard race in auto-HTTPS launch

When evaluating whether to skip managing an individual subdomain
due to an existing wildcard configuration, we now explicitly consult
the automate loader.

Because Caddy apps can start in any order, relying strictly on the
TLS app's internal management state was non-deterministic if the
HTTP app started first. Checking the automate loader guarantees
predictable behavior since it is fully populated during the
Provision phase, well before any apps are started.

* respond to review comments

1. update requested comment
2. remove personal domain from test
3. add regression test

* remove unnecessary mutex lock

* refactor: -integration test, +explicit cases

* refactor: remove redundant test, add comment

* rename file and add header

* update copyright year
2026-05-11 00:08:40 +10:00
Zen Dodd 5e76b5ee43 tls: add alpn to managed HTTPS records (#7653)
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 1m28s
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 1m56s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 2m18s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m26s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 2m55s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 3m3s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 1m50s
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 2m7s
Lint / govulncheck (push) Successful in 1m14s
Lint / dependency-review (push) Failing after 1m14s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 29s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m43s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 3m54s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 3m48s
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
* tls: add alpn to managed HTTPS records

* tls: centralise HTTPS RR ALPN defaults and registration

Reuse shared protocol defaults instead of repeating the default HTTP protocol list, unify server name registration to carry ALPN in one experimental API and reuse the TLS default ALPN ordering for HTTPS RR publication

* http: centralise effective protocol resolution for HTTPS RR ALPN
2026-05-10 13:10:29 +10:00
Matthew Holt 9c78b97f9e fastcgi: Fix lint
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 42s
Cross-Build / build (~1.26.0, 1.26, aix) (push) Failing after 51s
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 1m28s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 2m42s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 1m24s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 2m59s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 2m21s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 2m32s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m24s
Lint / dependency-review (push) Failing after 24s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m58s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 2m15s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 31s
Lint / govulncheck (push) Successful in 1m55s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m41s
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-08 10:46:28 -06:00
Kévin Dunglas fb324331f4 Merge commit from fork
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 1m15s
Cross-Build / build (~1.26.0, 1.26, aix) (push) Failing after 1m39s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m25s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 3m42s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 3m42s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 3m45s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 2m26s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 1m25s
Lint / govulncheck (push) Successful in 1m14s
Lint / dependency-review (push) Failing after 24s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 2m11s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 2m48s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 2m48s
Lint / lint (ubuntu-latest, linux) (push) Failing after 2m36s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 26s
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
Both fallbacks in splitPos relied on golang.org/x/text/search with
search.IgnoreCase, which performs Unicode equivalence matching far beyond
ASCII case folding. Combined with the validated-ASCII guarantee on every
SplitPath entry, that fallback turned non-PHP filenames into PHP scripts:

- when the inner loop hit a non-ASCII byte and the IndexString fallback
  returned -1, the loop broke without resetting match=false, so a stale
  match=true caused a non-existent .php to be reported (PoC:
  "/name.<U+00A1>.txt").
- search.IgnoreCase folded fullwidth, mathematical and circled letters
  onto ASCII, so "/shell.<math sans-serif php>",
  "/shell.<fullwidth p>hp", "/shell.<circled php>" were all detected as
  ".php" files.

Replace the fallback with strict byte-level ASCII case-insensitive
matching: any byte >= utf8.RuneSelf in the path can never be part of a
match, since SplitPath entries are validated ASCII-only and lower-cased
in Provision(). This keeps the hot path branch-light and removes the
x/text/search dependency from the main module.

Reported against FrankenPHP as GHSA-3g8v-8r37-cgjm and
GHSA-v4h7-cj44-8fc8. The vulnerable function in this module was adapted
from the same FrankenPHP code.
2026-05-07 13:59:42 -06:00
tomholford 0780d4489c httpcaddyfile: accept duration strings for log sampling interval (#7694)
Co-authored-by: tomholford <tomholford@users.noreply.github.com>
2026-05-07 18:32:20 +00:00
Zen Dodd d2172bea61 chore: Fix golangci-lint 2.12.1 findings (#7690)
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 1m25s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Failing after 1m41s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m24s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 3m23s
Cross-Build / build (~1.26.0, 1.26, aix) (push) Successful in 3m43s
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, linux) (push) Successful in 2m18s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 1m24s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m41s
Lint / govulncheck (push) Successful in 1m14s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 2m36s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 2m36s
Lint / dependency-review (push) Failing after 1m14s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m43s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 7m3s
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-07 03:40:26 -04:00
Zen Dodd c7c9f3108a caddyauth: Revert "set user placeholders before auth rejection (#7685)" (#7688)
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 1m27s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Failing after 1m42s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m23s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 3m33s
Cross-Build / build (~1.26.0, 1.26, aix) (push) Successful in 3m38s
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 3m42s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 2m13s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 1m23s
Lint / govulncheck (push) Successful in 1m41s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 2m22s
Lint / dependency-review (push) Failing after 24s
Lint / lint (ubuntu-latest, linux) (push) Failing after 2m37s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 3m6s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 3m7s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 6m14s
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
This reverts commit 7e77eec0ae.
2026-05-05 09:12:46 -06:00
Rayan Salhab 7e77eec0ae caddyauth: set user placeholders before auth rejection (#7685)
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 1m19s
Cross-Build / build (~1.26.0, 1.26, aix) (push) Failing after 2m12s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 1m32s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m22s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 4m16s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 2m32s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 1m23s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 2m11s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m23s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 2m37s
Lint / lint (ubuntu-latest, linux) (push) Failing after 1m45s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 2m27s
Lint / dependency-review (push) Failing after 56s
Lint / govulncheck (push) Successful in 2m26s
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
* caddyauth: set user placeholders before auth rejection
* docs: update auth placeholder comment
2026-05-03 13:40:11 +10:00
Felix Eckhofer ef496e58ef caddytls: Expand ACME credentials (#7554)
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, dragonfly) (push) Failing after 1m44s
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 2m45s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 4m1s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 1m26s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 2m38s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m28s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 6m2s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 4m33s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 2m31s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 2m39s
Lint / dependency-review (push) Failing after 1m22s
Lint / lint (ubuntu-latest, linux) (push) Failing after 2m13s
Lint / govulncheck (push) Successful in 2m53s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 6m41s
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
* caddytls: Expand ACME credentials

This allows using global placeholders such as {file./run/secrets/key_id}
when setting up the tls configuration.

* chore(formatting): gofmt on acmeissuer_test
2026-05-03 07:13:57 +10:00
Amemoyoi 18ab0f955f admin: reject non-canonical config array indices (#7592)
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 1m31s
Cross-Build / build (~1.26.0, 1.26, aix) (push) Failing after 1m47s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m33s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 3m31s
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 3m35s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 3m34s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 2m20s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 1m28s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m50s
Lint / govulncheck (push) Successful in 1m17s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 2m37s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 2m36s
Lint / dependency-review (push) Failing after 1m16s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m46s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 7m10s
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
* admin: reject non-canonical config array indices

* admin: expand canonical array index test coverage

* Update admin.go

Co-authored-by: Matt Holt <mholt@users.noreply.github.com>

* Update admin.go

Co-authored-by: Matt Holt <mholt@users.noreply.github.com>

* admin: improve canonical array index test diagnostics

---------

Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
2026-04-30 09:39:57 -06:00
mfrischknecht 6a64bb2ce5 listeners: clean up stale Unix socket files on Windows (#7676)
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 1m35s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Failing after 1m52s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 3m33s
Cross-Build / build (~1.26.0, 1.26, aix) (push) Successful in 3m49s
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 4m0s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 2m6s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Failing after 2m36s
Lint / lint (ubuntu-latest, linux) (push) Failing after 2m5s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 2m41s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 2m31s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 2m26s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 2m31s
Lint / dependency-review (push) Failing after 1m2s
Lint / govulncheck (push) Failing after 2m35s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 6m54s
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
* Delete old unix domain socket files on Windows

While Windows doesn't have the need to reuse a socket file descriptor
by dup()ing it on config reloads, there still is a valid need for an
equivalent to the `syscall.Unlink()` call in listen_unix.go (also in
`reuseUnixSocket`).

If a previous Caddy instance didn't terminate properly, the chances it
will leave behind a socket file are very high, breaking all subsequent
starting attempts. Other than for regular files, Windows seemingly has
no way for a process to flag a UNIX domain socket file with `FILE_DELETE_ON_CLOSE`,
which means this scenario can never be avoided entirely (e.g. in the case
of crashes).

For the long comment on `isAbstractUnixSocket`: the logic itself is likely
of dubious value, but I thought it better to explicitly reference the
issue, as I have just spent half an hour searching the web to figure out
whether abstract names will work or not on Windows. At least, the logic
as-is should now do the sensible thing if these are ever implemented
properly (and it matches what the Golang standard library does internally).

* Add a dial attempt to check for active server processes

As @steadytao pointed out (thanks!), the previous code didn't have
solid proof that an existing unix socket file had really been orphaned,
as it's also possible that there's another server process (still running).

This would still give the Windows implementation parity with the unix
one (as that one also unlinks the socket file without further checks),
but I've performed a couple of small tests and found this way of handling
socket files still problematic at least problematic if Caddy is used as
a reverse proxy in real world scenarios.

In tests with a simple Caddyfile that only declares an admin socket,
starting two caddy instances with the same Caddyfile works and behaves
like one would expect: the second instance removes the first instance's
socket file and "wins" the race.

When Caddy is used as a reverse proxy, though, what'll happen is more
complicated: While the second instance wins the race for the admin
socket, as long as the Caddyfile specifies a TCP downstream socket,
the second process will not be able to take this one over from the first
(also to be expected, that's how socket binding usually works).

This results in a rather broken state: The first process still holds on
to its TCP listening sockets, the second process fails to start because
of the error in its listening attempt, leaving an orphaned admin socket
file in the file system. Afterwards, the second process won't be running
and the first _will_ be running but unable to be controlled because its
admin socket has been replaced. This leaves the system in another state
that is bad from an ops perspective.

With this new change, we try first to connect to any unix socket that
isn't already covered by our current process (with a very low timeout)
and can easily decide if the socket is still in use by another process:

- If the connection is accepted, there's obviously a server process.

- If Windows returns WSACONNREFUSED [^1], there is either no active
  server process for the socket file anymore, or the socket file does
  not exist.

- Any other errors are likely a sign that there still is a server process
  (e.g. a timeout would indicate that it's just slow in accepting new
  connection attempts).

[^1]: https://learn.microsoft.com/en-us/windows/win32/winsock/windows-sockets-error-codes-2#wsaeconnrefused

* chore: tidy Windows unix socket reuse helper

---------

Co-authored-by: Zen Dodd <mail@steadytao.com>
2026-04-29 21:52:04 +10:00
Matt Holt 4d6945769d reverseproxy: Add ability to clear dynamic upstreams cache during retries (#7662)
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 2m41s
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 1m27s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 1m27s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 1m24s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 1m21s
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 1m26s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 1m43s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m27s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m26s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m27s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m27s
Lint / govulncheck (push) Successful in 1m49s
Lint / dependency-review (push) Failing after 1m3s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 7m12s
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: Add ability to clear dynamic upstreams cache during retries

This is an optional interface for dynamic upstream modules to implement if they cache results.

TODO: More documentation; this is an experiment.

* Add some godoc

* Export interface; update godoc
2026-04-28 09:16:18 -06:00
Amemoyoi 2d33271482 admin: require path segment boundary in remote access control (#7673)
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 1m21s
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 1m24s
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 1m26s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 1m26s
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 1m28s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 1m27s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m21s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m26s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m26s
Lint / lint (ubuntu-latest, linux) (push) Successful in 1m59s
Lint / govulncheck (push) Successful in 1m16s
Lint / dependency-review (push) Failing after 24s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 27s
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-04-27 08:43:39 -06:00
dependabot[bot] c653e7d61a build(deps): bump github.com/jackc/pgx/v5 from 5.9.0 to 5.9.2 (#7668)
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 1m21s
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 1m26s
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 1m31s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 1m26s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m24s
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 1m28s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m26s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m26s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m25s
Lint / lint (ubuntu-latest, linux) (push) Successful in 1m59s
Lint / govulncheck (push) Successful in 1m12s
Lint / dependency-review (push) Failing after 22s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 28s
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 [github.com/jackc/pgx/v5](https://github.com/jackc/pgx) from 5.9.0 to 5.9.2.
- [Changelog](https://github.com/jackc/pgx/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jackc/pgx/compare/v5.9.0...v5.9.2)

---
updated-dependencies:
- dependency-name: github.com/jackc/pgx/v5
  dependency-version: 5.9.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-26 23:51:26 +10:00
Zen Dodd c1918ff1ad httpcaddyfile: inherit global ACME issuer settings in tls shortcuts (#7617) 2026-04-26 23:39:57 +10:00
Zen Dodd fdbef2a6ef logging: add regression coverage for rotated file mode (#7620) 2026-04-26 23:30:44 +10:00
Kévin Dunglas 2a3ed96f8c metrics: Implement pushing via OLTP (#7664)
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 1m23s
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 1m30s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 1m25s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 1m23s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 1m26s
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 1m26s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 1m23s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m24s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m24s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m26s
Lint / lint (ubuntu-latest, linux) (push) Successful in 1m58s
Lint / govulncheck (push) Successful in 1m13s
Lint / dependency-review (push) Failing after 24s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 29s
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-04-25 06:52:08 -04:00
Francis Lavoie eeb13f1ca8 More comments 2026-04-25 05:42:43 -04:00
Francis Lavoie 97f5fe0079 Rename to stream_detached 2026-04-25 05:38:37 -04:00
Francis Lavoie 558ec222db Add note about capturing h 2026-04-25 05:38:37 -04:00
Francis Lavoie e3b1bf80f4 Rename to tunnelTracker, reflow some comments 2026-04-25 05:38:37 -04:00
Francis Lavoie 1b8d60c459 Move type and const down to the bottom 2026-04-25 05:38:37 -04:00
WeidiDeng 733aaba102 only clean up connections when stopped 2026-04-25 05:38:37 -04:00
WeidiDeng ed44e4d3f6 change the log level if hijacking without writing a status code first 2026-04-25 05:38:37 -04:00
WeidiDeng f970f397e2 fix tests 2026-04-25 05:38:37 -04:00
WeidiDeng 6ba6cf5d13 fix tests 2026-04-25 05:38:37 -04:00
WeidiDeng ccc76ac1f6 make handleUpgradeTunnel a standalone func 2026-04-25 05:38:37 -04:00
WeidiDeng cee04ab28e correctly close detached streams 2026-04-25 05:38:37 -04:00
WeidiDeng e7055d85a4 simplify streaming handling 2026-04-25 05:38:37 -04:00
WeidiDeng b9b12025c6 record bytes read and written for response writers unless detached 2026-04-25 05:38:37 -04:00
Francis Lavoie 7ef9ecd48a Adjustments from Weidi's review 2026-04-25 05:38:37 -04:00
Francis Lavoie 307dfd0431 Improved logging facilities 2026-04-25 05:38:37 -04:00
Francis Lavoie daea7788ad lint 2026-04-25 05:38:37 -04:00
Francis Lavoie b68e9bfdd4 reverseproxy: Optionally detach stream (websockets) from config lifecycle 2026-04-25 05:38:37 -04:00
Francis Lavoie 355c178213 chore: Use atomics where appropriate (#7648)
* chore: Use atomics where appropriate

* Use atomic for shutdownAt
2026-04-25 03:47:54 -04:00
Matthew Holt f6ee80be1b go.mod: Upgrade dependencies including CertMagic
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 2m32s
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 1m24s
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 1m26s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 1m25s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m22s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 1m26s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 1m26s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m26s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m23s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m24s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m23s
Lint / govulncheck (push) Successful in 1m43s
Lint / dependency-review (push) Failing after 1m5s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 7m6s
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-04-24 11:40:54 -06:00
Matthew Holt 48c08e3890 admin: Limit config size (by @omercnet)
GitHub was giving me errors related to merge status so we are doing this instead
2026-04-24 11:28:40 -06:00
Matthew Holt cf42f61566 Typo fix in security policy 2026-04-24 09:50:06 -06:00
Zen Dodd 41aee97386 core: propagate ECH keys to the QUIC listener (#7670)
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 1m19s
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 1m27s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 1m25s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 1m26s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 1m23s
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 1m22s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 1m23s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m22s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m24s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m34s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m2s
Lint / govulncheck (push) Successful in 1m20s
Lint / dependency-review (push) Failing after 22s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 24s
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-04-23 13:33:41 -06:00
Matt Holt 441d5eb062 caddyhttp: prefer port 443 in auto-HTTPS and add tests (#7666)
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 1m17s
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 1m24s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 1m27s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 1m23s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 1m22s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m21s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 1m22s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 1m22s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m27s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m25s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m27s
Lint / lint (ubuntu-latest, linux) (push) Successful in 1m59s
Lint / govulncheck (push) Successful in 1m46s
Lint / dependency-review (push) Failing after 24s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 6m45s
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-04-23 17:29:03 +10:00
Daniil Sivak aed1af5976 reverseproxy: add lb_retry_match condition on response status (#7569)
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 15m2s
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 11m36s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 1m32s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Failing after 2m3s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Failing after 2m2s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Failing after 11m12s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 6m45s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Failing after 2m3s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m57s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Failing after 56s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Failing after 51s
Lint / lint (ubuntu-latest, linux) (push) Failing after 1m24s
Lint / govulncheck (push) Failing after 1m51s
Lint / dependency-review (push) Failing after 3m55s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 20s
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-04-21 14:59:31 -04:00
Zen Dodd 4430756d5c admin: Redact sensitive request headers in API logs (#7578)
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 1m51s
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 1m41s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 1m37s
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 1m44s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m36s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 1m36s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 1m31s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m42s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m47s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m56s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m49s
Lint / govulncheck (push) Successful in 2m5s
Lint / dependency-review (push) Failing after 1m0s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 42s
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
* admin: Redact sensitive request headers in API logs

* Fix govulncheck and typed atomic lint failures

* Sync Go module metadata after dependency downgrade
2026-04-17 14:56:42 -06:00
dependabot[bot] af89c5ab02 build(deps): bump github.com/jackc/pgx/v5 from 5.8.0 to 5.9.0 (#7655)
Bumps [github.com/jackc/pgx/v5](https://github.com/jackc/pgx) from 5.8.0 to 5.9.0.
- [Changelog](https://github.com/jackc/pgx/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jackc/pgx/compare/v5.8.0...v5.9.0)

---
updated-dependencies:
- dependency-name: github.com/jackc/pgx/v5
  dependency-version: 5.9.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-17 14:50:53 -06:00
Mohammed Al Sahaf bd9f145321 chore: add AGENTS.md (#7652)
* chore: add `AGENTS.md`

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>

* Apply suggestions from code review

Co-authored-by: Francis Lavoie <lavofr@gmail.com>
Co-authored-by: Matt Holt <mholt@users.noreply.github.com>

* review feedback

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>

---------

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>
Co-authored-by: Francis Lavoie <lavofr@gmail.com>
Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
2026-04-17 14:49:58 -06:00
Steffen Busch 24bebd0a07 caddyhttp: Document missing placeholders for escaped URI and prefixed query (#7659) 2026-04-17 16:13:15 -04:00
prettysunflower 7dedd1486c fix(caddyfile): {block} in snippet (#7558)
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 1m47s
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 1m23s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 1m25s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 1m26s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 1m25s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m44s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 1m35s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 2m32s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 2m18s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m26s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m48s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m34s
Lint / govulncheck (push) Successful in 2m38s
Lint / dependency-review (push) Failing after 1m29s
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
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 7m56s
* fix(caddyfile): {block} in snippet

Resolve issue #7557

So, here is the situation:
- Pull request #7206 included some changes to the doImport's function of
  Caddyfile's parser. What it does is that if there is no token within a
  block that follows the import, and the import contains `{block}`, then
  the `{block}` token is discarded.
- After this pull request:
  - Issue #7518 noticed that in cases that `{block}` was not imported,
    a runtime error was raised due to the assumption that tokens were
    always added to `tokensCopy` on every iteration of `importedTokens`.
    This was fixed by pull request #7543.
  - Issue #7557 notices that {block} can be ignored when imported from a
    certain file. There, it's again an issue with how the import works.
    When `import snippets` is called, this import instruction doesn't
    contains any nested blocks. And when the argument replacer that is
    the `importedTokens` loop is called and finds `{block}`, it uses the
    block from the file's import (which in this case is nothing),
    `{block}` is erased, and unavailable when the import directive is
    called for the imported snippet.

The changed in this commit addresses the second issue by checking before
replacing `{block}` if we're currently in a snippet definition, and
appending the `{block}` token to `tokensCopy` if we are.

With this changes, when importing those snippets, the `{block}` token
will be available to be replaced by the nested blocks in `tokensToAdd`
if needed, or erased if there are no nested blocks and `tokensToAdd` is empty.

Tests added in pull requests #7206 and #7543 passes with this new
implementation, confirming that unused `{block}` are accepted if nothing
is passed to `import`, as well as the other usual tests.
A new test was also added based on issue #7557 reporting, and also passes.

Signed-off-by: prettysunflower <me@prettysunflower.moe>

* caddyfile: add imported snippet block placeholder coverage

---------

Signed-off-by: prettysunflower <me@prettysunflower.moe>
Co-authored-by: Zen Dodd <mail@steadytao.com>
2026-04-15 02:58:53 -04:00
Max Truxa 7586e68e27 fileserver: show symlink targets verbatim (#7579)
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 1m37s
Cross-Build / build (~1.26.0, 1.26, aix) (push) Failing after 2m28s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 2m27s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 52s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 3m9s
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 3m16s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 57s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 59s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m43s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 2m30s
Lint / lint (ubuntu-latest, linux) (push) Successful in 1m43s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 2m7s
Lint / govulncheck (push) Successful in 1m55s
Lint / dependency-review (push) Failing after 1m12s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 19m42s
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
`reveal_symlinks` was exposing symlink targets as fully resolved absolute paths, even if the target is a relative path. With this change the link target is shown as-is, without resolving anything.
2026-04-15 04:49:30 +10:00
Zen Dodd 0c7c91a447 logging: preserve ts for journald-wrapped JSON logs (#7644)
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 4s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Failing after 3s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Failing after 3s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Failing after 3s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Failing after 2s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Failing after 3s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Failing after 2s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Failing after 1s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Failing after 3s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Failing after 1s
Lint / lint (ubuntu-latest, linux) (push) Failing after 4s
Lint / govulncheck (push) Failing after 9s
Lint / dependency-review (push) Failing after 10s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 3s
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 2m6s
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-04-13 17:33:02 -06:00
Dean Ruina 1a3e900b35 chore: bump timberjack to v1.4.1 (#7618)
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 1m23s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Failing after 1m26s
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 1m37s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 26s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 2m20s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 2m31s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m11s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 47s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m26s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m45s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m45s
Lint / lint (ubuntu-latest, linux) (push) Successful in 1m40s
Lint / govulncheck (push) Successful in 1m21s
Lint / dependency-review (push) Failing after 54s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 8m57s
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-04-13 03:31:59 -04:00
tsinglua 0722cf6fd8 chore: replace interface{} with any for modernization (#7571)
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 1m28s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 1m38s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 1m39s
Cross-Build / build (~1.26.0, 1.26, aix) (push) Successful in 1m50s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 1m25s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m32s
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 1m41s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m23s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m36s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m34s
Lint / dependency-review (push) Failing after 23s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 27s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m5s
Lint / govulncheck (push) Successful in 1m25s
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: tsinglua <tsinglua@outlook.com>
2026-04-11 19:53:12 +03:00
Zen Dodd 8e2dd5079c caddyfile: Improve import/global options UX for imports before global options (#7642)
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 35s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Failing after 32s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 1m40s
Cross-Build / build (~1.26.0, 1.26, aix) (push) Successful in 2m2s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 1m29s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m29s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 1m24s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 1m31s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m30s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m31s
Lint / dependency-review (push) Failing after 24s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m20s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 33s
Lint / govulncheck (push) Successful in 1m23s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m17s
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
* caddyfile: improve import/global options UX

Keep standalone global-options braces stable in fmt when they follow import lines.

Also improve validate output for imports before the global options block with a clearer error message.

Add focused formatter and parser regression coverage

* caddyfile: satisfy staticcheck in formatter
2026-04-10 17:17:55 -06:00
Zen Dodd 5f44ea0748 logging: add journald encoder wrapper (#7623) 2026-04-10 17:09:12 -06:00
dependabot[bot] c8e4ac2c8c build(deps): bump github.com/go-jose/go-jose/v4 from 4.1.3 to 4.1.4 (#7621)
Bumps [github.com/go-jose/go-jose/v4](https://github.com/go-jose/go-jose) from 4.1.3 to 4.1.4.
- [Release notes](https://github.com/go-jose/go-jose/releases)
- [Commits](https://github.com/go-jose/go-jose/compare/v4.1.3...v4.1.4)

---
updated-dependencies:
- dependency-name: github.com/go-jose/go-jose/v4
  dependency-version: 4.1.4
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-10 16:33:07 -06:00
Zen Dodd 7dcc041eec vars: Add matcher placeholder handling tests (#7640)
* vars: add matcher placeholder handling tests

* vars: add query placeholder matcher coverage
2026-04-10 16:27:52 -06:00
Zen Dodd ca0ca67fbd reverseproxy: make stream copy buffer size configurable (#7627) 2026-04-10 14:49:32 -06:00
dependabot[bot] 92b62004eb build(deps): bump the all-updates group across 1 directory with 11 updates (#7641)
Bumps the all-updates group with 8 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [github.com/DeRuina/timberjack](https://github.com/DeRuina/timberjack) | `1.4.0` | `1.4.1` |
| [github.com/google/cel-go](https://github.com/google/cel-go) | `0.27.0` | `0.28.0` |
| [go.opentelemetry.io/contrib/exporters/autoexport](https://github.com/open-telemetry/opentelemetry-go-contrib) | `0.67.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.67.0` | `0.68.0` |
| [go.step.sm/crypto](https://github.com/smallstep/crypto) | `0.77.1` | `0.77.2` |
| [golang.org/x/crypto](https://github.com/golang/crypto) | `0.49.0` | `0.50.0` |
| [golang.org/x/net](https://github.com/golang/net) | `0.52.0` | `0.53.0` |



Updates `github.com/DeRuina/timberjack` from 1.4.0 to 1.4.1
- [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.0...v1.4.1)

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

Updates `go.opentelemetry.io/contrib/exporters/autoexport` 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/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.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.step.sm/crypto` from 0.77.1 to 0.77.2
- [Release notes](https://github.com/smallstep/crypto/releases)
- [Commits](https://github.com/smallstep/crypto/compare/v0.77.1...v0.77.2)

Updates `golang.org/x/crypto` from 0.49.0 to 0.50.0
- [Commits](https://github.com/golang/crypto/compare/v0.49.0...v0.50.0)

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

Updates `golang.org/x/term` from 0.41.0 to 0.42.0
- [Commits](https://github.com/golang/term/compare/v0.41.0...v0.42.0)

Updates `golang.org/x/sys` from 0.42.0 to 0.43.0
- [Commits](https://github.com/golang/sys/compare/v0.42.0...v0.43.0)

Updates `golang.org/x/text` from 0.35.0 to 0.36.0
- [Release notes](https://github.com/golang/text/releases)
- [Commits](https://github.com/golang/text/compare/v0.35.0...v0.36.0)

---
updated-dependencies:
- dependency-name: github.com/DeRuina/timberjack
  dependency-version: 1.4.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all-updates
- dependency-name: github.com/google/cel-go
  dependency-version: 0.28.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.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.step.sm/crypto
  dependency-version: 0.77.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all-updates
- dependency-name: golang.org/x/crypto
  dependency-version: 0.50.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all-updates
- dependency-name: golang.org/x/net
  dependency-version: 0.53.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all-updates
- dependency-name: golang.org/x/term
  dependency-version: 0.42.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all-updates
- dependency-name: golang.org/x/sys
  dependency-version: 0.43.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all-updates
- dependency-name: golang.org/x/text
  dependency-version: 0.36.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>
2026-04-10 20:39:00 +00:00
dependabot[bot] 6c23ec2f3c build(deps): bump go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp (#7637)
Bumps [go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp](https://github.com/open-telemetry/opentelemetry-go) from 1.42.0 to 1.43.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.42.0...v1.43.0)

---
updated-dependencies:
- dependency-name: go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp
  dependency-version: 1.43.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-10 14:31:08 -06:00
vnxme 5de1565ff6 vars: Don't expand placeholders in values (#7629) 2026-04-10 09:37:43 -06:00
Harsh Patel d7834676aa tls: add system and combined CA pool modules (#7406)
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 1m59s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 1m58s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 1m58s
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 3m9s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 1m37s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m39s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 1m40s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 1m27s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m39s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m38s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m38s
Lint / dependency-review (push) Failing after 58s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m28s
Lint / govulncheck (push) Successful in 2m11s
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
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 28s
* feat: add system and combined CA pool modules

* fix: combining pools using `CertificateProvider`

* fix: lint issue

* chore: caddyfiletests
* doing it for first time, so not sure if its right.

* fix: use `x509` native addCert

* chore: explicit err handling

* Apply suggestion from @mohammed90

---------

Co-authored-by: Mohammed Al Sahaf <mohammed@caffeinatedwonders.com>
2026-04-06 01:13:34 +03:00
Pieter Berkel 4f50458866 tls: expand placeholders in dns_challenge override_domain (#7609)
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 2m43s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 2m41s
Cross-Build / build (~1.26.0, 1.26, aix) (push) Successful in 2m46s
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 2m53s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 1m30s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 1m38s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m38s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 1m39s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m24s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m33s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m35s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m12s
Lint / dependency-review (push) Failing after 57s
Lint / govulncheck (push) Successful in 1m47s
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
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 8m11s
2026-03-31 05:46:32 +00:00
yubiuser ea4ee3ae5d reverseproxy: Fix check for header_up Host {upstream_hostport} redundancy (#7564)
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 2m32s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 2m28s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 2m31s
Cross-Build / build (~1.26.0, 1.26, aix) (push) Successful in 2m39s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 1m29s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 1m46s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 1m41s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m45s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m29s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m29s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m43s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m12s
Lint / dependency-review (push) Failing after 1m17s
Lint / govulncheck (push) Successful in 1m37s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 8m4s
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 check for header_up

Signed-off-by: yubiuser <github@yubiuser.dev>

* Onyl check in case commonScheme == "https"

Signed-off-by: yubiuser <github@yubiuser.dev>

* Move check after TLS transport is enabled

Signed-off-by: yubiuser <github@yubiuser.dev>

---------

Signed-off-by: yubiuser <github@yubiuser.dev>
2026-03-30 10:56:10 -06:00
Francis Lavoie 30b80bece8 notify: Always send "READY=1" even after an error (#7597)
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 1m39s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 1m40s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 1m40s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m49s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 1m54s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 1m51s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 2m26s
Lint / dependency-review (push) Failing after 29s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 43s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m27s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m35s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m37s
Lint / govulncheck (push) Successful in 1m26s
Lint / lint (ubuntu-latest, linux) (push) Successful in 1m59s
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-03-28 13:08:34 -06:00
Sam Ottenhoff 7a630f2910 encode: make zstd checksum configurable (#7586)
* http: make zstd checksum configurable

* disable_checksum
2026-03-28 13:07:21 -06:00
Marc 62e9c05264 root: introduce down-propagating Helper.BlockState for other directives/plugins to use (#7594)
* add 'root' key to Helper.State for access in frankenphp's `php_server` directive

* clone state before passing it to child directives, but keep sharing it among sibling directives

* propagate named route state from children to parent

* use BlockState to set "root" instead

* gofmt -w .

* go fmt ./...

* here we go
2026-03-28 17:44:42 +00:00
Tao 6f6771aa1d rewrite: skip query rename when source key is absent (#7599) 2026-03-28 13:10:34 -04:00
Matt Holt acf8d6a1ae caddytls: Consolidate empty APs more smartly (#7567)
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 1m39s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 1m38s
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 1m45s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 1m46s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 1m52s
Cross-Build / build (~1.26.0, 1.26, aix) (push) Successful in 1m55s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 2m1s
Lint / dependency-review (push) Failing after 1m4s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m37s
Lint / govulncheck (push) Successful in 1m27s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m35s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m45s
Lint / lint (ubuntu-latest, linux) (push) Successful in 1m59s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 26s
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
* caddytls: Consoldate empty APs more smartly (fix #7559)

* Revise consolidation logic
2026-03-26 14:41:34 -06:00
Matt Holt e98ed6232d chore: Resolve recent CI failures (#7593)
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, freebsd) (push) Successful in 1m28s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 1m37s
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, netbsd) (push) Successful in 1m43s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 1m46s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 1m52s
Cross-Build / build (~1.26.0, 1.26, aix) (push) Successful in 1m54s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m53s
Lint / dependency-review (push) Failing after 23s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m20s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m18s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m18s
Lint / govulncheck (push) Successful in 2m2s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m11s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 7m5s
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-03-25 23:21:27 -06:00
Matthew Holt c35ba5588d Add missing return to handleError in admin server
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, illumos) (push) Failing after 1m28s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Failing after 1m28s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Failing after 1m38s
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 2m27s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 2m27s
Cross-Build / build (~1.26.0, 1.26, aix) (push) Successful in 2m30s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 2m50s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 2m58s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m27s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m26s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m27s
Lint / dependency-review (push) Failing after 1m44s
Lint / govulncheck (push) Failing after 1m58s
Lint / lint (ubuntu-latest, linux) (push) Failing after 2m5s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 6m39s
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
Thanks to @Wernerina's LLM for finding this bug
2026-03-25 16:33:24 -06:00
Tao 5d189aff40 caddytls: Avoid default issuers for implicit tailscale policies (#7577)
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 1m37s
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 1m29s
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 1m23s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 1m26s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m24s
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 1m25s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m23s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m34s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m28s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m22s
Lint / govulncheck (push) Successful in 1m43s
Lint / dependency-review (push) Failing after 59s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 36s
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-03-20 09:36:03 -06:00
vnxme df65455b1f caddyhttp: Sync placeholder expansion in vars and vars_regexp (#7573)
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 1m26s
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 1m29s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 1m27s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 1m24s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 1m20s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m40s
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 1m26s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m22s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m20s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m30s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m7s
Lint / govulncheck (push) Successful in 1m20s
Lint / dependency-review (push) Failing after 23s
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
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 7m23s
* vars: Expand placeholders in custom variables like in `vars_regexp`

* vars: Reuse variables inside match loops
2026-03-17 13:08:47 -06:00
Matthew Holt 8499e34e10 caddytls: Ensure key list always gets set (fix #7555)
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 2m29s
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 1m26s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 1m23s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 1m38s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 1m37s
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 1m32s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 1m24s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m27s
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 1m29s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m23s
Lint / govulncheck (push) Successful in 1m44s
Lint / dependency-review (push) Failing after 59s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 6m18s
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-03-16 16:21:47 -06:00
Matthew Holt 1fbb28720b Fix lint errors
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 2m15s
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 2m36s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 1m14s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 1m27s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 1m14s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m27s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 1m14s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 1m24s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m14s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m26s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m13s
Lint / lint (ubuntu-latest, linux) (push) Failing after 1m13s
Lint / govulncheck (push) Successful in 1m31s
Lint / dependency-review (push) Failing after 59s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 29s
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
Use VerifyConnection instead of VerifyPeerCertificate; the other 2 fixes are "meh" not really a big deal or an issue at all.
2026-03-11 13:33:59 -06:00
Matthew Holt ffb6ab0644 Revert cosign (see #7536)
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 1m21s
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 1m28s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 1m26s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 1m24s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 1m31s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m27s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 1m24s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 1m21s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m37s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m23s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m29s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m2s
Lint / govulncheck (push) Successful in 1m26s
Lint / dependency-review (push) Failing after 27s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 21s
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-03-05 08:41:54 -07:00
dependabot[bot] 9371ee67c6 build(deps): bump the actions-deps group across 1 directory with 12 updates (#7536)
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 2m39s
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 1m28s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 1m24s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 1m22s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 1m25s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m23s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 1m23s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 1m28s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m24s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m27s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m27s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m34s
Lint / govulncheck (push) Successful in 1m26s
Lint / dependency-review (push) Failing after 1m2s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 5m36s
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 actions-deps group with 12 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [actions/checkout](https://github.com/actions/checkout) | `5.0.0` | `6.0.2` |
| [github/ai-moderator](https://github.com/github/ai-moderator) | `1.1.2` | `1.1.4` |
| [step-security/harden-runner](https://github.com/step-security/harden-runner) | `2.13.1` | `2.15.0` |
| [actions/setup-go](https://github.com/actions/setup-go) | `6.0.0` | `6.3.0` |
| [actions/upload-artifact](https://github.com/actions/upload-artifact) | `4.6.2` | `7.0.0` |
| [goreleaser/goreleaser-action](https://github.com/goreleaser/goreleaser-action) | `6.4.0` | `7.0.0` |
| [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) | `8.0.0` | `9.2.0` |
| [actions/dependency-review-action](https://github.com/actions/dependency-review-action) | `4.8.0` | `4.8.3` |
| [sigstore/cosign-installer](https://github.com/sigstore/cosign-installer) | `3.10.0` | `4.0.0` |
| [anchore/sbom-action](https://github.com/anchore/sbom-action) | `0.20.6` | `0.23.0` |
| [peter-evans/repository-dispatch](https://github.com/peter-evans/repository-dispatch) | `4.0.0` | `4.0.1` |
| [github/codeql-action](https://github.com/github/codeql-action) | `3.30.5` | `4.32.4` |



Updates `actions/checkout` from 5.0.0 to 6.0.2
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/08c6903cd8c0fde910a37f88322edcfb5dd907a8...de0fac2e4500dabe0009e67214ff5f5447ce83dd)

Updates `github/ai-moderator` from 1.1.2 to 1.1.4
- [Release notes](https://github.com/github/ai-moderator/releases)
- [Commits](https://github.com/github/ai-moderator/compare/6bcdb2a79c2e564db8d76d7d4439d91a044c4eb6...81159c370785e295c97461ade67d7c33576e9319)

Updates `step-security/harden-runner` from 2.13.1 to 2.15.0
- [Release notes](https://github.com/step-security/harden-runner/releases)
- [Commits](https://github.com/step-security/harden-runner/compare/f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a...a90bcbc6539c36a85cdfeb73f7e2f433735f215b)

Updates `actions/setup-go` from 6.0.0 to 6.3.0
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/44694675825211faa026b3c33043df3e48a5fa00...4b73464bb391d4059bd26b0524d20df3927bd417)

Updates `actions/upload-artifact` from 4.6.2 to 7.0.0
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/ea165f8d65b6e75b540449e92b4886f43607fa02...bbbca2ddaa5d8feaa63e36b76fdaad77386f024f)

Updates `goreleaser/goreleaser-action` from 6.4.0 to 7.0.0
- [Release notes](https://github.com/goreleaser/goreleaser-action/releases)
- [Commits](https://github.com/goreleaser/goreleaser-action/compare/e435ccd777264be153ace6237001ef4d979d3a7a...ec59f474b9834571250b370d4735c50f8e2d1e29)

Updates `golangci/golangci-lint-action` from 8.0.0 to 9.2.0
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](https://github.com/golangci/golangci-lint-action/compare/4afd733a84b1f43292c63897423277bb7f4313a9...1e7e51e771db61008b38414a730f564565cf7c20)

Updates `actions/dependency-review-action` from 4.8.0 to 4.8.3
- [Release notes](https://github.com/actions/dependency-review-action/releases)
- [Commits](https://github.com/actions/dependency-review-action/compare/56339e523c0409420f6c2c9a2f4292bbb3c07dd3...05fe4576374b728f0c523d6a13d64c25081e0803)

Updates `sigstore/cosign-installer` from 3.10.0 to 4.0.0
- [Release notes](https://github.com/sigstore/cosign-installer/releases)
- [Commits](https://github.com/sigstore/cosign-installer/compare/d7543c93d881b35a8faa02e8e3605f69b7a1ce62...faadad0cce49287aee09b3a48701e75088a2c6ad)

Updates `anchore/sbom-action` from 0.20.6 to 0.23.0
- [Release notes](https://github.com/anchore/sbom-action/releases)
- [Changelog](https://github.com/anchore/sbom-action/blob/main/RELEASE.md)
- [Commits](https://github.com/anchore/sbom-action/compare/f8bdd1d8ac5e901a77a92f111440fdb1b593736b...17ae1740179002c89186b61233e0f892c3118b11)

Updates `peter-evans/repository-dispatch` from 4.0.0 to 4.0.1
- [Release notes](https://github.com/peter-evans/repository-dispatch/releases)
- [Commits](https://github.com/peter-evans/repository-dispatch/compare/5fc4efd1a4797ddb68ffd0714a238564e4cc0e6f...28959ce8df70de7be546dd1250a005dd32156697)

Updates `github/codeql-action` from 3.30.5 to 4.32.4
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/3599b3baa15b485a2e49ef411a7a4bb2452e7f93...89a39a4e59826350b863aa6b6252a07ad50cf83e)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 6.0.2
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions-deps
- dependency-name: github/ai-moderator
  dependency-version: 1.1.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-deps
- dependency-name: step-security/harden-runner
  dependency-version: 2.15.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions-deps
- dependency-name: actions/setup-go
  dependency-version: 6.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions-deps
- dependency-name: actions/upload-artifact
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions-deps
- dependency-name: goreleaser/goreleaser-action
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions-deps
- dependency-name: golangci/golangci-lint-action
  dependency-version: 9.2.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions-deps
- dependency-name: actions/dependency-review-action
  dependency-version: 4.8.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-deps
- dependency-name: sigstore/cosign-installer
  dependency-version: 4.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions-deps
- dependency-name: anchore/sbom-action
  dependency-version: 0.23.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions-deps
- dependency-name: peter-evans/repository-dispatch
  dependency-version: 4.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-deps
- dependency-name: github/codeql-action
  dependency-version: 4.32.4
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions-deps
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-04 21:29:44 -07:00
dependabot[bot] 5d20adc7a9 build(deps): bump github.com/smallstep/certificates (#7535)
Bumps the all-updates group with 1 update: [github.com/smallstep/certificates](https://github.com/smallstep/certificates).


Updates `github.com/smallstep/certificates` from 0.30.0-rc2.0.20260211214201-20608299c29c to 0.30.0-rc3
- [Release notes](https://github.com/smallstep/certificates/releases)
- [Changelog](https://github.com/smallstep/certificates/blob/master/CHANGELOG.md)
- [Commits](https://github.com/smallstep/certificates/commits/v0.30.0-rc3)

---
updated-dependencies:
- dependency-name: github.com/smallstep/certificates
  dependency-version: 0.30.0-rc3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-04 21:26:18 -07:00
Francis Lavoie 6e5e08cf58 Wire up Cause for most context cancels (#7538) 2026-03-04 17:14:52 -07:00
Matthew Holt fbfb8fc517 rewrite: Force recomputing path when escaped path matches rewrite target
Thank you for the report by @MaherAzzouzi, and the suggested fix!
2026-03-04 16:18:33 -07:00
Matt Holt e06dfcf6ed Update SECURITY.md
Simplify what versions are supported, clarify our policy for unreleased code (or beta code), and expand our AI policy to require a disclosure in ALL cases, even if AI is not used. As well as an invitation to share in some chocolate milk with us if you're human.
2026-03-04 16:16:24 -07:00
Oleh Konko | semantic verification for trust infra | LLM-augmented operations pipeline (precision-first, claim≤evidence, submit-human) | verify the payload, not the signer 566e710991 fileserver: document hide case-sensitivity (F-CADDY-FILESERVER-HIDE-CASE-001) (#7548) 2026-03-04 17:00:10 -05:00
Tom Paulus a5e7c6e232 reverseproxy: prevent body close on dial-error retries (#7547)
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 2m58s
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 1m42s
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 1m27s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 1m23s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m24s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 1m33s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 1m24s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m29s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m24s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m25s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m30s
Lint / govulncheck (push) Successful in 1m50s
Lint / dependency-review (push) Failing after 1m27s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 5m36s
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-03-04 15:17:02 -05:00
Francis Lavoie db2986028f reverseproxy: Track dynamic upstreams, enable passive healthchecking (#7539)
* reverseproxy: Track dynamic upstreams, enable passive healthchecking

* Add tests for dynamic upstream tracking, admin endpoint, health checks
2026-03-04 15:05:26 -05:00
Sam.An 7e83775e3a Merge commit from fork
Only apply repl.ReplaceAll() on values from literal variable names
(e.g. map outputs), not on values resolved from placeholder keys
(e.g. {http.request.header.*}). The placeholder path already resolves
the value via repl.Get(), so a second expansion allows user-controlled
input containing {env.*} or {file.*} to be evaluated, leaking
environment variables and file contents.

Add regression test to verify placeholder-sourced values are not
re-expanded.
2026-03-04 09:08:39 -07:00
newklei 2dbcdefbbe forward_auth: copy_headers does not strip client-supplied identity headers (Fixes GHSA-7r4p-vjf4-gxv4) (#7545)
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 1m48s
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 1m50s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 1m58s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 1m58s
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 2m2s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 1m54s
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 1m41s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m46s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m39s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m31s
Lint / govulncheck (push) Successful in 1m32s
Lint / dependency-review (push) Failing after 28s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 38s
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 using copy_headers in a forward_auth block, client-supplied headers with
the same names were not being removed before being forwarded to the backend.

This happens because PR #6608 added a MatchNot guard that skips the Set
operation when the auth service does not return a given header. That guard
prevents setting headers to empty strings, which is the correct behavior,
but it also means a client can send X-User-Id: admin in their request and
if the auth service validates the token without returning X-User-Id, Caddy
skips the Set and the client value passes through unchanged to the backend.

The fix adds an unconditional delete route for each copy_headers entry,
placed just before the existing conditional set route. The delete always runs
regardless of what the auth service returns. The conditional set still only
runs when the auth service provides that header.

The end result is:
  - Client-supplied headers are always removed
  - When the auth service returns the header, the backend gets that value
  - When the auth service does not return the header, the backend sees nothing

Existing behavior is unchanged for any deployment where the auth service
returns all of the configured copy_headers entries.

Fixes GHSA-7r4p-vjf4-gxv4
2026-03-03 23:30:49 -05:00
Varun Chawla dc36082859 caddyhttp: Collect metrics once per route instead of per handler (#7492)
* perf: collect metrics once per route instead of per handler (#4644)

Move Prometheus metrics instrumentation from the per-handler level to
the per-route level. Previously, every middleware handler in a route was
individually wrapped with metricsInstrumentedHandler, causing metrics to
be collected N times per request (once per handler in the chain). Since
all handlers in a route see the same request, these per-handler metrics
were redundant and added significant CPU overhead (73% of request
handling time per the original profiling).

The fix introduces metricsInstrumentedRoute which wraps the entire
compiled handler chain once in wrapRoute, collecting metrics only when
the route actually matches. The handler label uses the first handler's
module name, which is the most meaningful identifier for the route.

Benchmark results (5 handlers per route):
  Old (per-handler):  ~4650 ns/op, 4400 B/op, 45 allocs/op
  New (per-route):    ~940 ns/op,  816 B/op,   8 allocs/op
  Improvement:        ~5x faster, ~5.4x less memory, ~5.6x fewer allocs

Signed-off-by: Varun Chawla <varun_6april@hotmail.com>

* Remove unused metricsInstrumentedHandler code

Delete the metricsInstrumentedHandler type, its constructor, and
ServeHTTP method since they are no longer used after switching to
route-level metrics collection via metricsInstrumentedRoute. Also
remove the unused metrics parameter from wrapMiddleware and the
middlewareHandlerFunc test helper, and convert existing tests to
use the new route-level API.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Address review feedback: restore comments, move function to bottom

- Move computeApproximateRequestSize back to bottom of file to minimize diff
- Restore all useful comments that were accidentally dropped
- Old metricsInstrumentedHandler already removed in previous commit

---------

Signed-off-by: Varun Chawla <varun_6april@hotmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 15:15:55 -07:00
Paulo Henrique 88616e86e6 api: Add all in-flight requests /reverse_proxy/upstreams (Fixes #7277) (#7517)
This refactors the initial approach in PR #7281, replacing the UsagePool
with a dedicated package-level sync.Map and atomic.Int64 to track
in-flight requests without global lock contention.

It also introduces a lookup map in the admin API to fix a potential
O(n^2) iteration over upstreams, ensuring that draining upstreams
are correctly exposed across config reloads without leaking memory.

Co-authored-by: Y.Horie <u5.horie@gmail.com>

reverseproxy: optimize in-flight tracking and admin API

- Replaced sync.RWMutex with sync.Map and atomic.Int64 to avoid lock contention under high RPS.
- Introduced a lookup map in the admin API to fix a potential O(n^2) iteration over upstreams.
2026-03-03 15:14:55 -07:00
Salent Olivick 7b34e3107e core: Check whether @id is unique (#7002)
* caddy.go: Check whether @id is unique(#6991)

* Alternate implementation, using Gemini 3.1

---------

Co-authored-by: Francis Lavoie <lavofr@gmail.com>
2026-03-03 15:09:49 -07:00
Francis Lavoie a6acb3902c proxyproto: Generated test coverage (#7540) 2026-03-03 15:08:09 -07:00
Francis Lavoie 45cf61b127 logging: Ensure slog error level logs don't print stack traces (#7512) 2026-03-03 14:44:42 -07:00
Francis Lavoie d935a6956c autohttps: Ensure CertMagic config is recreated after autohttps runs (#7510) 2026-03-03 14:44:06 -07:00
prettysunflower 2dd3852416 fix(caddyfile): Prevent parser to panic when no token were added by empty {block} (#7543)
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 1m24s
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 1m35s
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 1m33s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 1m30s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m37s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 1m40s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 2m8s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m34s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m35s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m33s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m18s
Lint / govulncheck (push) Successful in 1m17s
Lint / dependency-review (push) Failing after 24s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 36s
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-03-03 13:16:21 -05:00
Akın Demirci 11b56c6cfc reverseproxy: Fix health_port being ignored in health checks (#7533) 2026-03-03 13:10:54 -05:00
Alexandre Daubois f283062d37 cmd: Custom binary names through CustomBinaryName and CustomLongDescription (#7513)
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 1m21s
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 1m36s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 1m39s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 1m27s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 1m25s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m37s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 1m50s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 1m39s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m46s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m30s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m30s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m15s
Lint / govulncheck (push) Successful in 1m20s
Lint / dependency-review (push) Failing after 24s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 35s
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-03-02 06:04:28 -05:00
WeidiDeng 2ab043b890 reverseproxy: query escape request urls when proxy protocol is enabled (#7537) 2026-03-02 02:04:06 -05:00
Pavel Siomachkin f145bce553 tls: Add tls_resolvers global option for DNS challenge configuration (#7297)
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 1m37s
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 1m53s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 1m37s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 1m42s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 1m47s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m51s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 2m12s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 1m49s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m39s
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 1m33s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m22s
Lint / govulncheck (push) Successful in 1m24s
Lint / dependency-review (push) Failing after 25s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 37s
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: Francis Lavoie <lavofr@gmail.com>
2026-03-01 15:32:04 -05:00
Matt Holt 174fa2ddb9 caddyhttp: Evaluate tls.client placeholders more accurately (fix #7530) (#7534)
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 2m13s
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 1m33s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 1m40s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 1m33s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 1m38s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m43s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 1m48s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 1m51s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m37s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m35s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m35s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m29s
Lint / govulncheck (push) Successful in 1m24s
Lint / dependency-review (push) Failing after 25s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 34s
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-02-28 22:03:18 -07:00
Matt Holt cd9e1660aa cmd: Pass configFile, not configFlag, for reload command (#7532)
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 1m41s
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 1m40s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 1m46s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 1m36s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 1m31s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m47s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 2m4s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 1m38s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m52s
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 1m36s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m55s
Lint / govulncheck (push) Successful in 2m5s
Lint / dependency-review (push) Failing after 44s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 1m9s
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
* cmd: Pass configFile, not configFlag, for reload command

This *should* fix #7528.

* Remove debug log line

---------

Co-authored-by: Francis Lavoie <lavofr@gmail.com>
2026-02-27 15:24:05 -07:00
Matthew Holt 06a05e383c Revert "encode: Implement Flush for legacy compatibility"
This reverts commit bdcdaf77ba.
2026-02-27 14:14:19 -07:00
Matthew Holt ce203aa9e1 go.mod: Upgrade x/net
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 1m38s
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
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 1m34s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 1m36s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 1m53s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m37s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 1m58s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 1m49s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m46s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m45s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 2m19s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m35s
Lint / govulncheck (push) Successful in 2m4s
Lint / dependency-review (push) Failing after 30s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 1m4s
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-02-27 10:35:24 -07:00
Matthew Holt eac02ee98f caddyhttp: Limit empty Host check to HTTP/1.1 2026-02-27 10:22:39 -07:00
Oleksandr Redko 72eaf2583a chore: Enable modernize linter (#7519)
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 1m39s
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 1m32s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 1m34s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 1m45s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 1m30s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m42s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 2m12s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 1m33s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m28s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m50s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m26s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m20s
Lint / govulncheck (push) Failing after 1m40s
Lint / dependency-review (push) Failing after 32s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 48s
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-02-26 14:01:35 -07:00
Fardjad Davari 9798f6964d caddyhttp: Avoid nil pointer dereference in proxyWrapper (#7521)
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 1m25s
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 1m29s
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 1m34s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 1m39s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m27s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 1m37s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 1m59s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m30s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m28s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m3s
Lint / govulncheck (push) Successful in 1m22s
Lint / dependency-review (push) Failing after 23s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 55s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Failing after 10m20s
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-02-25 04:08:41 -05:00
Francis Lavoie 9873752978 logging: Support zstd roll compression (#7515)
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 1m20s
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 1m30s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 1m22s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 1m34s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 1m28s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m31s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 1m27s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 2m0s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m25s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m26s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m23s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m0s
Lint / govulncheck (push) Successful in 1m18s
Lint / dependency-review (push) Failing after 25s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 33s
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-02-23 16:04:45 -07:00
Dean Ruina 294dfff443 logging: add DirMode options and propagate FileMode to rotations (#7335)
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 1m20s
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 1m28s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 1m20s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 1m22s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 1m25s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m26s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 1m30s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 1m30s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m27s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m27s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m21s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m10s
Lint / govulncheck (push) Successful in 1m20s
Lint / dependency-review (push) Failing after 22s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 28s
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: Francis Lavoie <lavofr@gmail.com>
2026-02-23 07:27:27 +00:00
Paulo Henrique 76b198f586 http: Sort auto-HTTPS redirect routes by host specificity (fixes #7390) (#7502)
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 1m49s
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 1m46s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 1m31s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 1m26s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 1m31s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m38s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 1m36s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 2m22s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m40s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m57s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m27s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m13s
Lint / govulncheck (push) Successful in 2m1s
Lint / dependency-review (push) Failing after 1m12s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 50s
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-02-21 21:42:40 -05:00
Paulo Henrique 7ffb640a4d httpcaddyfile: Fix missing TLS connection policies when auto_https is default (#7325) (#7507) 2026-02-21 21:42:03 -05:00
Mohammed Al Sahaf d7b21c6104 reverseproxy: fix tls dialing w/ proxy protocol (#7508) 2026-02-21 21:37:10 -05:00
Francis Lavoie 6610e2f1bd chore: Disable windows/arm build target (Go 1.26 disabled) (#7503)
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 1m45s
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 1m25s
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 1m28s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 1m24s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m27s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 1m27s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 1m36s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m37s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m23s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m26s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m3s
Lint / govulncheck (push) Successful in 1m35s
Lint / dependency-review (push) Failing after 23s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 41s
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-02-20 22:47:21 +00:00
Matthew Holt 03243e42fe go.mod: Upgrade dependencies 2026-02-20 12:28:11 -07:00
Matthew Holt cb436f0a0e fileserver: Fix tests on Windows 2026-02-20 11:46:45 -07:00
Matt Holt a1081194bf Merge commit from fork
Necessary as otherwise the early-bail in `until =
strings.IndexByte(remaining, nextCh) ... if until == -1` can cause a
case-insensitive mismatch

Co-authored-by: Asim Viladi Oglu Manizada <manizada@users.noreply.github.com>
2026-02-20 10:54:50 -07:00
Asim Viladi Oglu Manizada eec32a0bb5 Merge commit from fork
Normalize exact hosts at provisioning and reqHost in the fast path so case-different Host variants can’t bypass host-gated routes.

Co-authored-by: Asim Viladi Oglu Manizada <manizada@users.noreply.github.com>
2026-02-20 10:19:42 -07:00
Matthew Holt a2825c5dd9 fileserver: Replace \ with \\ in file matcher paths
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 1m15s
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 1m25s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 1m32s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 1m28s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 1m22s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m26s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 1m24s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 1m49s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m24s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m23s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m39s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m4s
Lint / govulncheck (push) Successful in 1m20s
Lint / dependency-review (push) Failing after 25s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 30s
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-02-19 13:18:14 -07:00
dependabot[bot] db256b53e5 build(deps): bump filippo.io/edwards25519 from 1.1.0 to 1.1.1 (#7497)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-19 14:20:06 -05:00
Matthew Holt 6772ffb805 Revert "listeners: Add support for named socket activation (#7243)"
This reverts commit 156ce99d3a.
2026-02-19 11:32:26 -07:00
Matt Holt 95941a71e8 chore: Add nolints to work around haywire linters (#7493)
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 1m23s
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 1m27s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 1m24s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 1m21s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 1m20s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m27s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 1m23s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 1m54s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m32s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m40s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m31s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m0s
Lint / govulncheck (push) Successful in 1m18s
Lint / dependency-review (push) Failing after 23s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 35s
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: Add nolints to work around haywire linters

* More lint wrangling
2026-02-17 16:52:54 -07:00
Francis Lavoie 3adcafd4c1 admin: Fix tests locally, properly isolate storage (#7486)
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 1m21s
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 1m23s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 1m27s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 1m22s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 1m23s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m23s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 1m23s
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 1m28s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m21s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m27s
Lint / lint (ubuntu-latest, linux) (push) Failing after 1m48s
Lint / govulncheck (push) Successful in 1m27s
Lint / dependency-review (push) Failing after 22s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 39s
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
* admin: Fix tests locally, properly isolate storage

* Fix flaky pki_test

* Drop testdata dir logic

* Safer temp dir

* Test handlers without a full server
2026-02-17 13:14:06 -07:00
Amirhf 091add5ae3 caddytest: make TestReverseProxyHealthCheck deterministic with poll instead of sleep (#7474)
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 1m20s
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 1m22s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 1m24s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 1m25s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 1m22s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m22s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 1m21s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 1m30s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m30s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m26s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m27s
Lint / lint (ubuntu-latest, linux) (push) Failing after 1m48s
Lint / govulncheck (push) Successful in 1m15s
Lint / dependency-review (push) Failing after 23s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 33s
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
Start lightweight backend servers before starting Caddy so active health checks
probe a ready backend instead of the same Caddy instance during provisioning.
This removes the startup race without fixed sleeps or polling.
2026-02-17 06:41:38 -05:00
Matthew Holt bdcdaf77ba encode: Implement Flush for legacy compatibility
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 1m25s
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 1m25s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 1m22s
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 1m31s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m21s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 1m24s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 1m33s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m25s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m27s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m23s
Lint / lint (ubuntu-latest, linux) (push) Successful in 1m58s
Lint / govulncheck (push) Successful in 1m15s
Lint / dependency-review (push) Failing after 22s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 33s
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
(By sponsor request)
2026-02-16 15:59:10 -07:00
Francis Lavoie 9fe694c79c caddytls: Enable debug logging for DNSManager (#7491) 2026-02-16 15:38:56 -07:00
wangjingcun b8b00d9160 chore: fix some comments to improve readability (#7395)
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 2m21s
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 1m42s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 1m25s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 1m24s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 1m21s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m21s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 1m32s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 1m32s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 1m22s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m27s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m25s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m21s
Lint / govulncheck (push) Successful in 1m43s
Lint / dependency-review (push) Failing after 1m0s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 4m46s
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: Francis Lavoie <lavofr@gmail.com>
2026-02-16 19:41:21 +00:00
zjumathcode 68d50020ee refactor: use strings.Builder to improve performance (#7364)
* refactor: use strings.Builder to improve performance

Signed-off-by: zjumathcode <pai314159@2980.com>

* refactor: small builder improvements per review (WriteByte / split writes)

also revert builder change in client_test.go

refactor(logging): build IP mask output via join of parts (more efficient)

---------

Signed-off-by: zjumathcode <pai314159@2980.com>
Co-authored-by: Francis Lavoie <lavofr@gmail.com>
2026-02-16 19:30:44 +00:00
dependabot[bot] 8a18acc025 build(deps): bump the all-updates group across 1 directory with 12 updates (#7490)
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.21.1` | `2.23.1` |
| [github.com/cloudflare/circl](https://github.com/cloudflare/circl) | `1.6.2` | `1.6.3` |
| [github.com/go-chi/chi/v5](https://github.com/go-chi/chi) | `5.2.4` | `5.2.5` |
| [github.com/klauspost/compress](https://github.com/klauspost/compress) | `1.18.2` | `1.18.4` |
| [github.com/yuin/goldmark](https://github.com/yuin/goldmark) | `1.7.15` | `1.7.16` |
| [go.opentelemetry.io/contrib/exporters/autoexport](https://github.com/open-telemetry/opentelemetry-go-contrib) | `0.64.0` | `0.65.0` |
| [go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp](https://github.com/open-telemetry/opentelemetry-go-contrib) | `0.64.0` | `0.65.0` |
| [go.opentelemetry.io/contrib/propagators/autoprop](https://github.com/open-telemetry/opentelemetry-go-contrib) | `0.64.0` | `0.65.0` |
| [github.com/pires/go-proxyproto](https://github.com/pires/go-proxyproto) | `0.8.1` | `0.11.0` |



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

Updates `github.com/cloudflare/circl` from 1.6.2 to 1.6.3
- [Release notes](https://github.com/cloudflare/circl/releases)
- [Commits](https://github.com/cloudflare/circl/compare/v1.6.2...v1.6.3)

Updates `github.com/go-chi/chi/v5` from 5.2.4 to 5.2.5
- [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.4...v5.2.5)

Updates `github.com/klauspost/compress` from 1.18.2 to 1.18.4
- [Release notes](https://github.com/klauspost/compress/releases)
- [Commits](https://github.com/klauspost/compress/compare/v1.18.2...v1.18.4)

Updates `github.com/yuin/goldmark` from 1.7.15 to 1.7.16
- [Release notes](https://github.com/yuin/goldmark/releases)
- [Commits](https://github.com/yuin/goldmark/compare/v1.7.15...v1.7.16)

Updates `go.opentelemetry.io/contrib/exporters/autoexport` from 0.64.0 to 0.65.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.64.0...zpages/v0.65.0)

Updates `go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp` from 0.64.0 to 0.65.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.64.0...zpages/v0.65.0)

Updates `go.opentelemetry.io/contrib/propagators/autoprop` from 0.64.0 to 0.65.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.64.0...zpages/v0.65.0)

Updates `go.opentelemetry.io/otel` from 1.39.0 to 1.40.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.39.0...v1.40.0)

Updates `go.opentelemetry.io/otel/sdk` from 1.39.0 to 1.40.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.39.0...v1.40.0)

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

Updates `go.opentelemetry.io/otel/trace` from 1.39.0 to 1.40.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.39.0...v1.40.0)

---
updated-dependencies:
- dependency-name: github.com/alecthomas/chroma/v2
  dependency-version: 2.23.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all-updates
- dependency-name: github.com/cloudflare/circl
  dependency-version: 1.6.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all-updates
- dependency-name: github.com/go-chi/chi/v5
  dependency-version: 5.2.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all-updates
- dependency-name: github.com/klauspost/compress
  dependency-version: 1.18.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all-updates
- dependency-name: github.com/yuin/goldmark
  dependency-version: 1.7.16
  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.65.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.65.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.65.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all-updates
- dependency-name: go.opentelemetry.io/otel
  dependency-version: 1.40.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.40.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.11.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all-updates
- dependency-name: go.opentelemetry.io/otel/trace
  dependency-version: 1.40.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>
2026-02-16 13:38:55 -05:00
Mohammed Al Sahaf 23d07ac89d dep: upgrade cel-go (#7478)
* dep: upgrade cel-go

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>

* Try handling `map[any]any`, fix error messages

---------

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>
Co-authored-by: Francis Lavoie <lavofr@gmail.com>
2026-02-16 18:25:49 +00:00
Francis Lavoie d64c7e67a4 caddyhttp: Option to disable 0-RTT (#7485) 2026-02-16 10:20:47 -07:00
Francis Lavoie ff4f79aebe chore: Remove obsolete comment in ech.go (#7487) 2026-02-16 10:17:01 -07:00
Francis Lavoie f2213e943e chore: Bump zerossl dependency to 0.1.5 (#7489) 2026-02-16 10:08:29 -07:00
Amirhf affbb99275 pki: add per-CA configurable maintenance_interval and renewal_window_ratio (#7479)
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 2m40s
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 1m40s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Successful in 1m23s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Successful in 1m27s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Successful in 1m27s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Successful in 1m38s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Successful in 1m41s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Successful in 1m37s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Successful in 2m11s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Successful in 1m28s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Successful in 1m24s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m17s
Lint / govulncheck (push) Successful in 1m39s
Lint / dependency-review (push) Failing after 58s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 5m0s
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
* pki: add per-CA configurable maintenance_interval and renewal_window_ratio

- Add MaintenanceInterval and RenewalWindowRatio to CA struct (JSON + Caddyfile).
- Run one maintenance goroutine per CA using its own interval.
- needsRenewal uses per-CA RenewalWindowRatio; invalid/zero ratio falls back to defaults.
- Caddyfile: maintenance_interval duration, renewal_window_ratio <0-1>.
- Tests: TestCA_needsRenewal, TestParsePKIApp for new options.

Fixes #7475

* fix codestyle
2026-02-15 09:10:12 -05:00
Aditya Bhargava d6a6b486db httpcaddyfile: Override global dns with acme_dns (fix #7294) (#7458)
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 54s
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 15s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Failing after 16s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Failing after 16s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Failing after 15s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Failing after 15s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Failing after 14s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Failing after 14s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Failing after 14s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Failing after 15s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Failing after 14s
Lint / lint (ubuntu-latest, linux) (push) Failing after 15s
Lint / govulncheck (push) Successful in 1m41s
Lint / dependency-review (push) Failing after 15s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 14s
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
This brings the behaviour in line with what the documentation implies.
2026-02-15 09:04:59 +00:00
mehrdadbn9 929d0e502a caddyfile: Add renewal_window_ratio global option and tls subdirective (#7473)
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 15s
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 14s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Failing after 14s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Failing after 15s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Failing after 15s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Failing after 15s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Failing after 14s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Failing after 25s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Failing after 20s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Failing after 15s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Failing after 34s
Lint / lint (ubuntu-latest, linux) (push) Failing after 15s
Lint / govulncheck (push) Successful in 1m32s
Lint / dependency-review (push) Failing after 16s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 24s
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
* caddyfile: Add renewal_window_ratio global option

Adds support for configuring the TLS certificate renewal window ratio
directly in the Caddyfile global options block. This allows users to
customize when certificates should be renewed without needing to use
JSON configuration.

Example usage:
    {
        renewal_window_ratio 0.1666
    }

Fixes #7467

* caddyfile: Add renewal_window_ratio to tls directive and tests

Adds support for renewal_window_ratio in the tls directive (not just
global options) and adds caddyfile adapt tests for both the global
option and tls directive.

* fix: inherit global renewal_window_ratio in site policies

* fix: correct test expected output for policy consolidation

* fix: properly inherit global renewal_window_ratio without removing other code
2026-02-13 16:47:02 -05:00
Matthew Holt 6718bd470f caddytls: Finish removing prefer_wildcard
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 19s
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 15s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Failing after 17s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Failing after 15s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Failing after 18s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Failing after 18s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Failing after 18s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Failing after 18s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Failing after 17s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Failing after 16s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Failing after 18s
Lint / lint (ubuntu-latest, linux) (push) Failing after 16s
Lint / govulncheck (push) Successful in 1m59s
Lint / dependency-review (push) Failing after 20s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 15s
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
Finish what should have been done a year ago in #6959)
2026-02-12 11:35:28 -07:00
Omer Cohen 80bf81839d go.mod: update nebula v1.10.3 to resolve cve (#7471) 2026-02-12 08:54:48 -07:00
moscowchill d42d39b4bc caddytls: Return errors instead of nil in client auth provisioning (#7464)
Two error returns in ClientAuthentication.provision() were
returning nil instead of the actual error, silently swallowing
failures when converting PEM files to DER and when provisioning
the CA pool. This could cause mTLS client authentication to
silently fall back to the system trust store, accepting any
client certificate signed by a public CA instead of restricting
to the configured trust anchors.
2026-02-12 08:42:54 -07:00
Oleh Konko | trust infra security audit & contribution | deterministic ai-augmented pipeline · human-verified 0188ef2e62 acmeserver: warn when policy rules unset (#7469)
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Failing after 18s
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 15s
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Failing after 39s
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Failing after 16s
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Failing after 14s
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Failing after 15s
Cross-Build / build (~1.26.0, 1.26, linux) (push) Failing after 15s
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Failing after 15s
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Failing after 15s
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Failing after 16s
Cross-Build / build (~1.26.0, 1.26, windows) (push) Failing after 15s
Lint / lint (ubuntu-latest, linux) (push) Failing after 19s
Lint / govulncheck (push) Successful in 1m43s
Lint / dependency-review (push) Failing after 16s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 17s
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-02-11 11:54:51 -07:00
Francis Lavoie c0af7b665f chore: bump Go to v1.26 (#7466) 2026-02-11 11:21:10 -07:00
Matthew Holt 72ac479f5d admin: Enforce origin implicitly based on request headers 2026-02-11 09:52:56 -07:00
WeidiDeng 47f3e8f8dc use math/rand/v2 instead of math/rand (#7413) 2026-02-11 09:15:51 -07:00
XYenon 03e6e439dd reverseproxy: fix X-Forwarded-* headers for Unix socket requests (#7463)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 16s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 52s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 13s
Lint / lint (ubuntu-latest, linux) (push) Failing after 14s
Lint / govulncheck (push) Successful in 1m42s
Lint / dependency-review (push) Failing after 14s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 12m18s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
When a request arrives via a Unix domain socket (RemoteAddr == "@"),
net.SplitHostPort fails, causing addForwardedHeaders to strip all
X-Forwarded-* headers even when the connection is trusted via
trusted_proxies_unix.

Handle Unix socket connections before parsing RemoteAddr: if untrusted,
strip headers for security; if trusted, let clientIP remain empty (no
peer IP for a Unix socket hop) and fall through to the shared header
logic, preserving the existing XFF chain without appending a spurious
entry.

Amp-Thread-ID: https://ampcode.com/threads/T-019c4225-a0ad-7283-ac56-e2c01eae1103

Co-authored-by: Amp <amp@ampcode.com>
2026-02-10 13:00:20 -07:00
Kévin Dunglas 7c28c0c07a Merge commit from fork
* fix: FastCGI split SCRIPT_NAME/PATH_INFO confusion

* fix comment
2026-02-10 11:52:36 -07:00
Matt Holt 96f142c2a6 Update SECURITY.md 2026-02-10 11:44:40 -07:00
Matt Holt 5ff50779cc Update LLM disclosure requirements in SECURITY.md
Clarified disclosure requirements for LLMs in security reports.
2026-02-09 14:40:41 -07:00
Matthew Holt 1f43e8566b caddyhttp: Use case-insensitive comparison for large Host lists 2026-02-09 14:18:55 -07:00
Matthew Holt bd374ca9d7 caddyhttp: Lowercase comparison when matching with escape sequence 2026-02-09 13:12:00 -07:00
Francis Lavoie 2ae0f7af69 reverseproxy: Set Host to {upstream_hostport} automatically if TLS (#7454) 2026-02-09 13:06:19 -07:00
Matthew Holt 58968b3fd3 Update detail in readme
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 16s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 13s
Lint / lint (ubuntu-latest, linux) (push) Failing after 14s
Lint / govulncheck (push) Successful in 1m22s
Lint / dependency-review (push) Failing after 14s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 14s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
2026-02-06 08:45:09 -07:00
Matthew Holt 42ca010e9d admin: Reject requests with Sec-Fetch-Mode headers
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 16s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 17s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 17s
Lint / lint (ubuntu-latest, linux) (push) Failing after 14s
Lint / govulncheck (push) Successful in 1m23s
Lint / dependency-review (push) Failing after 14s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 15s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
And buggy Origin: null headers.

Resolves a low-risk security report by @1seal.
2026-02-05 09:39:11 -07:00
Matt Holt 40927d2f75 Require disclosure of LLM usage in security reports
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 16s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 14s
Lint / lint (ubuntu-latest, linux) (push) Failing after 14s
Lint / govulncheck (push) Successful in 1m28s
Lint / dependency-review (push) Failing after 14s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 14s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
Added requirement to disclose the use of LLMs in security reports.
2026-02-05 06:12:26 -07:00
Matthew Holt e0f8d9b204 caddytls: Check type assertion
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 21s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 14s
Lint / lint (ubuntu-latest, linux) (push) Failing after 14s
Lint / govulncheck (push) Successful in 1m16s
Lint / dependency-review (push) Failing after 14s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 13s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
Fix https://github.com/mholt/caddy-l4/issues/378
2026-02-03 13:59:53 -07:00
Matthew Holt 3bb22672f9 reverseproxy: Customizable dial network for SRV upstreams
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 18s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 14s
Lint / lint (ubuntu-latest, linux) (push) Failing after 14s
Lint / govulncheck (push) Successful in 1m20s
Lint / dependency-review (push) Failing after 14s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 15s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
By request of a sponsor
2026-02-02 11:25:51 -07:00
Matthew Holt 935b09de83 caddtls: Skip .ts.net domains for ECH (#6971)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 53s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 1m24s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 14s
Lint / lint (ubuntu-latest, linux) (push) Failing after 15s
Lint / govulncheck (push) Successful in 1m42s
Lint / dependency-review (push) Failing after 14s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 14s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
As it is also a special case in our automatic HTTPS.
2026-01-30 12:24:59 -07:00
Matthew Holt 7d24124430 caddyhttp: Reject invalid Host header (fix #7449) 2026-01-30 12:24:16 -07:00
Paulo Henrique 565c1c3054 autohttps: deterministic logic and strict bind checking on Linux (#7435)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 17s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 15s
Lint / lint (ubuntu-latest, linux) (push) Failing after 14s
Lint / govulncheck (push) Successful in 1m34s
Lint / dependency-review (push) Failing after 15s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 54s
* http: fix non-deterministic auto-https and improve Linux bind matching

* docs: restore historical context about Linux bind behavior
2026-01-16 08:51:23 -07:00
Francis Lavoie d269405eab core: Show JSON error offsets where possible (#7437)
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 17s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 14s
Lint / lint (ubuntu-latest, linux) (push) Failing after 16s
Lint / govulncheck (push) Successful in 1m26s
Lint / dependency-review (push) Failing after 15s
Tests / goreleaser-check (push) Failing after 14m20s
Tests / test (s390x on IBM Z) (push) Failing after 14m25s
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 14m30s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 14m59s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
2026-01-14 22:54:19 -05:00
Mohammed Al Sahaf e40bd019ff caddyfile: add observe_catchall_hosts option (#7434)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 17s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 13s
Lint / lint (ubuntu-latest, linux) (push) Failing after 14s
Lint / govulncheck (push) Successful in 1m19s
Lint / dependency-review (push) Failing after 15s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 14s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
* caddyfile: add `observe_catchall_hosts` option

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>

* correct JSON field name and doc comment

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>

---------

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>
2026-01-14 00:06:16 +00:00
Francis Lavoie cbebc1292b core: Embed time/tzdata (#7432) 2026-01-13 15:11:35 -07:00
Paulo Henrique e9d290de2f caddyconfig: Fix indentation of multiline strings in fmt (#7425) (#7433)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 18s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 16s
Lint / lint (ubuntu-latest, linux) (push) Failing after 15s
Lint / govulncheck (push) Successful in 1m25s
Lint / dependency-review (push) Failing after 15s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 14s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
2026-01-13 15:22:23 -05:00
Paulo Henrique 62134d65af reverseproxy: fix error when remote address is not an IP (#7429) 2026-01-13 19:52:56 +00:00
Marten Seemann 5168acfb9c update quic-go to v0.59.0 (#7431) 2026-01-13 14:47:36 -05:00
Francis Lavoie 90972fbebc chore: Dumb prealloc lint fix (#7430) 2026-01-13 14:13:43 -05:00
Matthew Holt 28103aafba Revise top of readme to include Warp sponsorship section
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 17s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 14s
Lint / lint (ubuntu-latest, linux) (push) Failing after 14s
Lint / govulncheck (push) Successful in 1m20s
Lint / dependency-review (push) Failing after 14s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 18s
2026-01-06 16:44:11 -07:00
Tom Paulus 6a57142896 headers: Make ApplyTo nil-safe (#7426) 2026-01-06 17:39:58 -05:00
WeidiDeng 80f2ae92cd reverseproxy: make error chan bigger when reverse proxying websocket (#7419)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 30s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 17s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 15s
Lint / lint (ubuntu-latest, linux) (push) Failing after 13s
Lint / govulncheck (push) Successful in 1m23s
Lint / dependency-review (push) Failing after 15s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 14s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
2026-01-06 04:55:47 -05:00
dependabot[bot] 7b031e1eb5 build(deps): bump the all-updates group across 1 directory with 12 updates (#7421)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 17s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 24s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 14s
Lint / lint (ubuntu-latest, linux) (push) Failing after 14s
Lint / govulncheck (push) Successful in 1m20s
Lint / dependency-review (push) Failing after 14s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 14s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, 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/BurntSushi/toml](https://github.com/BurntSushi/toml) | `1.5.0` | `1.6.0` |
| [github.com/alecthomas/chroma/v2](https://github.com/alecthomas/chroma) | `2.20.0` | `2.21.1` |
| [github.com/cloudflare/circl](https://github.com/cloudflare/circl) | `1.6.1` | `1.6.2` |
| [github.com/spf13/cobra](https://github.com/spf13/cobra) | `1.10.1` | `1.10.2` |
| [github.com/yuin/goldmark](https://github.com/yuin/goldmark) | `1.7.13` | `1.7.15` |
| [go.opentelemetry.io/contrib/exporters/autoexport](https://github.com/open-telemetry/opentelemetry-go-contrib) | `0.63.0` | `0.64.0` |
| [go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp](https://github.com/open-telemetry/opentelemetry-go-contrib) | `0.63.0` | `0.64.0` |
| [go.opentelemetry.io/contrib/propagators/autoprop](https://github.com/open-telemetry/opentelemetry-go-contrib) | `0.63.0` | `0.64.0` |
| [go.step.sm/crypto](https://github.com/smallstep/crypto) | `0.74.0` | `0.75.0` |



Updates `github.com/BurntSushi/toml` from 1.5.0 to 1.6.0
- [Release notes](https://github.com/BurntSushi/toml/releases)
- [Commits](https://github.com/BurntSushi/toml/compare/v1.5.0...v1.6.0)

Updates `github.com/alecthomas/chroma/v2` from 2.20.0 to 2.21.1
- [Release notes](https://github.com/alecthomas/chroma/releases)
- [Commits](https://github.com/alecthomas/chroma/compare/v2.20.0...v2.21.1)

Updates `github.com/cloudflare/circl` from 1.6.1 to 1.6.2
- [Release notes](https://github.com/cloudflare/circl/releases)
- [Commits](https://github.com/cloudflare/circl/compare/v1.6.1...v1.6.2)

Updates `github.com/spf13/cobra` from 1.10.1 to 1.10.2
- [Release notes](https://github.com/spf13/cobra/releases)
- [Commits](https://github.com/spf13/cobra/compare/v1.10.1...v1.10.2)

Updates `github.com/yuin/goldmark` from 1.7.13 to 1.7.15
- [Release notes](https://github.com/yuin/goldmark/releases)
- [Commits](https://github.com/yuin/goldmark/compare/v1.7.13...v1.7.15)

Updates `go.opentelemetry.io/contrib/exporters/autoexport` from 0.63.0 to 0.64.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.63.0...zpages/v0.64.0)

Updates `go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp` from 0.63.0 to 0.64.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.63.0...zpages/v0.64.0)

Updates `go.opentelemetry.io/contrib/propagators/autoprop` from 0.63.0 to 0.64.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.63.0...zpages/v0.64.0)

Updates `go.opentelemetry.io/otel` from 1.38.0 to 1.39.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.38.0...v1.39.0)

Updates `go.opentelemetry.io/otel/sdk` from 1.38.0 to 1.39.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.38.0...v1.39.0)

Updates `go.step.sm/crypto` from 0.74.0 to 0.75.0
- [Release notes](https://github.com/smallstep/crypto/releases)
- [Commits](https://github.com/smallstep/crypto/compare/v0.74.0...v0.75.0)

Updates `go.opentelemetry.io/otel/trace` from 1.38.0 to 1.39.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.38.0...v1.39.0)

---
updated-dependencies:
- dependency-name: github.com/BurntSushi/toml
  dependency-version: 1.6.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all-updates
- dependency-name: github.com/alecthomas/chroma/v2
  dependency-version: 2.21.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all-updates
- dependency-name: github.com/cloudflare/circl
  dependency-version: 1.6.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all-updates
- dependency-name: github.com/spf13/cobra
  dependency-version: 1.10.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all-updates
- dependency-name: github.com/yuin/goldmark
  dependency-version: 1.7.15
  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.64.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.64.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.64.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all-updates
- dependency-name: go.opentelemetry.io/otel
  dependency-version: 1.39.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.39.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all-updates
- dependency-name: go.step.sm/crypto
  dependency-version: 0.75.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all-updates
- dependency-name: go.opentelemetry.io/otel/trace
  dependency-version: 1.39.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>
2026-01-05 22:50:46 +03:00
Matthew Holt b2d21f650a go.mod: Upgrade CertMagic and ZeroSSL deps 2026-01-05 12:28:52 -07:00
Mohammed Al Sahaf 99d84be6dd readme: fix fence (#7416)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 17s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 13s
Lint / lint (ubuntu-latest, linux) (push) Failing after 14s
Lint / govulncheck (push) Successful in 1m26s
Lint / dependency-review (push) Failing after 16s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 14s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
2026-01-02 10:51:36 -05:00
Felix Hildén 1f1be3f4fe tracing: Add span attributes to tracing module (#7269)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 16s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 14s
Lint / lint (ubuntu-latest, linux) (push) Failing after 13s
Lint / govulncheck (push) Successful in 1m30s
Lint / dependency-review (push) Failing after 14s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 31s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
* WIP tracing span attributes

* better test

* only write attributes after other middleware (and request)

* Fix test to use header response placeholders
2025-12-31 11:33:18 -07:00
Paulo Henrique 9eabd443cb cmd: Add --json flag to list-modules command (#7409)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 51s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 14s
Lint / lint (ubuntu-latest, linux) (push) Failing after 14s
Lint / govulncheck (push) Successful in 1m44s
Lint / dependency-review (push) Failing after 16s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 13s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
2025-12-26 12:32:03 -05:00
Marten Seemann 5640611dfc chore: update quic-go to v0.58.0 (#7404)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 19s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 26s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 18s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 29s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 17s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 17s
Lint / lint (ubuntu-latest, linux) (push) Failing after 15s
Lint / govulncheck (push) Successful in 1m51s
Lint / dependency-review (push) Failing after 18s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 51s
2025-12-21 12:09:55 +03:00
Francis Lavoie decc8a4d6f logging: log_append Early option, Supports {http.response.body} (#7368)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 18s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 17s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 15s
Lint / lint (ubuntu-latest, linux) (push) Failing after 16s
Lint / govulncheck (push) Successful in 1m30s
Lint / dependency-review (push) Failing after 16s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 17s
* logging: `log_append` early option

* logging: `log_append` supports `{http.response.body}`

* Convenience auto-early for request body
2025-12-16 23:42:42 -05:00
Will Norris 34fd2dfcff go.mod: update tscert package to latest (aea342f6) (#7397)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 19s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 18s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 17s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 22s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 15s
Lint / lint (ubuntu-latest, linux) (push) Failing after 16s
Lint / govulncheck (push) Successful in 1m27s
Lint / dependency-review (push) Failing after 16s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 16s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
2025-12-16 10:38:32 -05:00
Francis Lavoie 4037d05760 caddyhttp: {http.request.body_base64} placeholder (#7367)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 17s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 17s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 33s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 16s
Lint / lint (ubuntu-latest, linux) (push) Failing after 16s
Lint / govulncheck (push) Successful in 1m53s
Lint / dependency-review (push) Failing after 15s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 16s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
2025-12-13 21:01:12 -07:00
EINIER FREYRE CORONA 409a072135 notify: implement windows service status and error notifications (#7389)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 18s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 19s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 55s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 18s
Lint / lint (ubuntu-latest, linux) (push) Failing after 16s
Lint / govulncheck (push) Successful in 1m47s
Lint / dependency-review (push) Failing after 18s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 16s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
* implement service status and error notifications

* adjust return of Error function

* configure accepts on status

* align windows with linux semantics
2025-12-12 07:56:30 -05:00
Paul B 6a4296b1a4 caddytls: panic when using tls.ca_pool.source.http -> tls.ca (#7393)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 19s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 18s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 17s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 31s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 15s
Lint / lint (ubuntu-latest, linux) (push) Failing after 14s
Lint / govulncheck (push) Successful in 1m27s
Lint / dependency-review (push) Failing after 16s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 45s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
2025-12-11 19:27:15 +00:00
Matt Holt 3c9c67e804 caddytls: ECH key rotation (#7356)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 18s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 17s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 33s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 18s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 15s
Lint / lint (ubuntu-latest, linux) (push) Failing after 16s
Lint / govulncheck (push) Successful in 1m32s
Lint / dependency-review (push) Failing after 17s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 15s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
* caddytls: ECH key rotation

* Stop rotation goroutine on config unload

* Publish ECH keys after rotating
2025-12-10 11:50:35 -07:00
Kévin Dunglas 598b08f9ae test: mark Assert* functions as test helpers (#7380)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 18s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 17s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 17s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 18s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 17s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 16s
Lint / lint (ubuntu-latest, linux) (push) Failing after 15s
Lint / govulncheck (push) Successful in 1m29s
Lint / dependency-review (push) Failing after 16s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 16s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
2025-12-08 22:32:00 +00:00
okrc 374b7a637f caddytls: fix preferred chains options by appending values instead of replacing (#7387)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 19s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 18s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 17s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 17s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 16s
Lint / lint (ubuntu-latest, linux) (push) Failing after 16s
Lint / govulncheck (push) Successful in 1m35s
Lint / dependency-review (push) Failing after 16s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 16s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
2025-12-07 16:19:01 +00:00
WeidiDeng 6e0cbd0fa0 caddyhttp: create a placeholder for and log ech status (#7328)
Co-authored-by: Francis Lavoie <lavofr@gmail.com>
2025-12-07 16:01:58 +00:00
Steffen Busch bfdb04912d docs: add maybe template function documentation (#7388)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 18s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 17s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 15s
Lint / lint (ubuntu-latest, linux) (push) Failing after 15s
Lint / govulncheck (push) Successful in 1m26s
Lint / dependency-review (push) Failing after 16s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 14s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
2025-12-06 06:51:28 -05:00
vnxme 31960dc998 Introduce packet conn wrappers (#7180)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 17s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 17s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 18s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 17s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 15s
Lint / lint (ubuntu-latest, linux) (push) Failing after 16s
Lint / govulncheck (push) Successful in 1m35s
Lint / dependency-review (push) Failing after 15s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 17s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
* packet_conn_wrappers: Initial changes

* packet_conn_wrappers: Unwrap a packet conn only if there are no wrappers

---------

Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
2025-12-04 14:15:56 -07:00
Francis Lavoie be5f49fbeb caddyhttp: Fix logging on wildcard sites when SkipUnmappedHosts is true (#7372)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 17s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 20s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 15s
Lint / lint (ubuntu-latest, linux) (push) Failing after 15s
Lint / govulncheck (push) Successful in 1m35s
Lint / dependency-review (push) Failing after 15s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 15s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
2025-12-03 18:46:11 +00:00
Herman Slatman 7ebe72bbfe caddypki: Add support for multiple intermediates in signing chain (#7057)
* caddypki: Add support for multiple intermediates in signing chain

* Move intermediate lifetime configuration check

In #7272 a check was changed to ensure that generated intermediate
certificates would always use a lifetime that falls within the
lifetime of the root. However, when a root and intermediate(s)
are supplied, the configuration value was being used instead of
the actual lifetimes of the certificates. The check was moved to
only be performed when an intermediate is generated; not when
loaded from disk.

* Add tests for `pemDecodeCertificateChain` and `pemDecodeCertificate`

* Use `crypto.Signer` instead of `any` in appropriate places

* Use latest Smallstep packages

---------

Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
2025-12-03 11:30:00 -07:00
dependabot[bot] 8a87bb3ffb build(deps): bump github.com/smallstep/certificates (#7381)
Bumps [github.com/smallstep/certificates](https://github.com/smallstep/certificates) from 0.28.4 to 0.29.0.
- [Release notes](https://github.com/smallstep/certificates/releases)
- [Changelog](https://github.com/smallstep/certificates/blob/master/CHANGELOG.md)
- [Commits](https://github.com/smallstep/certificates/compare/v0.28.4...v0.29.0)

---
updated-dependencies:
- dependency-name: github.com/smallstep/certificates
  dependency-version: 0.29.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-03 10:45:18 -07:00
Mohammed Al Sahaf df9386fa12 ci: escape backticks in changelogs embedded in JS (#7382)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 18s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 17s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 18s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 15s
Lint / lint (ubuntu-latest, linux) (push) Failing after 14s
Lint / govulncheck (push) Successful in 1m33s
Lint / dependency-review (push) Failing after 15s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 15s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, 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>
2025-12-03 16:40:31 +00:00
dependabot[bot] 786d537877 build(deps): bump the all-updates group with 3 updates (#7376)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 50s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 17s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 17s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 20s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 28s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 15s
Lint / lint (ubuntu-latest, linux) (push) Failing after 14s
Lint / govulncheck (push) Successful in 1m25s
Lint / dependency-review (push) Failing after 15s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 14s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, 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 3 updates: [github.com/klauspost/compress](https://github.com/klauspost/compress), [github.com/quic-go/quic-go](https://github.com/quic-go/quic-go) and [go.uber.org/zap](https://github.com/uber-go/zap).


Updates `github.com/klauspost/compress` from 1.18.1 to 1.18.2
- [Release notes](https://github.com/klauspost/compress/releases)
- [Commits](https://github.com/klauspost/compress/compare/v1.18.1...v1.18.2)

Updates `github.com/quic-go/quic-go` from 0.57.0 to 0.57.1
- [Release notes](https://github.com/quic-go/quic-go/releases)
- [Commits](https://github.com/quic-go/quic-go/compare/v0.57.0...v0.57.1)

Updates `go.uber.org/zap` from 1.27.0 to 1.27.1
- [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.0...v1.27.1)

---
updated-dependencies:
- dependency-name: github.com/klauspost/compress
  dependency-version: 1.18.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all-updates
- dependency-name: github.com/quic-go/quic-go
  dependency-version: 0.57.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all-updates
- dependency-name: go.uber.org/zap
  dependency-version: 1.27.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-02 10:32:58 +03:00
Petr 67a9e0657e reverseproxy: Fix retries for requests with bodies (#7360)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 47s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 15s
Lint / lint (ubuntu-latest, linux) (push) Failing after 15s
Lint / govulncheck (push) Successful in 1m49s
Lint / dependency-review (push) Failing after 15s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 17s
* capture the buffered body once, then reset clonedReq.Body before each retry

* no copy

* keep receiver name

* set the buf to nil after extraction and only return it to pool if not nil

---------

Co-authored-by: WeidiDeng <weidi_deng@icloud.com>
2025-11-24 12:03:18 -07:00
ledigang 2cb426776c encode: modernize, replace HasSuffix+TrimSuffix with CutSuffix (#7357)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 16s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 17s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 17s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 17s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 17s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 17s
Lint / lint (ubuntu-latest, linux) (push) Failing after 17s
Lint / govulncheck (push) Successful in 1m39s
Lint / dependency-review (push) Failing after 15s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 29s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, 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: ledigang <shuangcui@msn.com>
2025-11-21 15:30:26 -07:00
Marten Seemann b9e6f3b227 update quic-go to v0.57.0 (#7359)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 20s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 18s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 17s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 15s
Lint / lint (ubuntu-latest, linux) (push) Failing after 15s
Lint / govulncheck (push) Successful in 1m29s
Lint / dependency-review (push) Failing after 15s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 15s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
2025-11-21 14:46:47 +03:00
dependabot[bot] eead249382 build(deps): bump golang.org/x/crypto from 0.43.0 to 0.45.0 (#7355)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 18s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 17s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 27s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 15s
Lint / lint (ubuntu-latest, linux) (push) Failing after 15s
Lint / govulncheck (push) Successful in 1m41s
Lint / dependency-review (push) Failing after 16s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 17s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.43.0 to 0.45.0.
- [Commits](https://github.com/golang/crypto/compare/v0.43.0...v0.45.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-version: 0.45.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-19 20:55:13 -07:00
WeidiDeng a6da1acdc8 reverse_proxy: use interfaces to modify the behaviors of the transports (#7353)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 17s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 17s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 16s
Lint / lint (ubuntu-latest, linux) (push) Failing after 15s
Lint / govulncheck (push) Successful in 1m23s
Lint / dependency-review (push) Failing after 15s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 16s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
2025-11-17 09:51:37 -07:00
Mohammed Al Sahaf 56282c5737 ci: implement new release flow (#7341)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 53s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 17s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 17s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 17s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 16s
Lint / lint (ubuntu-latest, linux) (push) Failing after 16s
Lint / govulncheck (push) Successful in 1m54s
Lint / dependency-review (push) Failing after 16s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 16s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
* ci: implement new release flow

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>

* remove redundant validation

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>

* extract key sha

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>

* pin github-scripts

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>

* switch to PR-based flow

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>

* don't use top-level permissions

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>

* restricted global perms + specific local perms

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>

* make PR draft

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>

---------

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>
2025-11-14 14:55:30 -07:00
Kévin Dunglas b3f2db233b core: custom slog handlers for modules (log contextual data) (#7346)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 50s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 15s
Lint / lint (ubuntu-latest, linux) (push) Failing after 15s
Lint / govulncheck (push) Successful in 1m47s
Lint / dependency-review (push) Failing after 16s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 18s
2025-11-12 13:29:47 -07:00
dependabot[bot] 07d2aaf22e build(deps): bump the all-updates group with 4 updates (#7333)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 19s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 26s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 24s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 15s
Lint / lint (ubuntu-latest, linux) (push) Failing after 15s
Lint / govulncheck (push) Successful in 1m59s
Lint / dependency-review (push) Failing after 15s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 15s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, 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 4 updates: [github.com/DeRuina/timberjack](https://github.com/DeRuina/timberjack), [github.com/KimMachineGun/automemlimit](https://github.com/KimMachineGun/automemlimit), [github.com/klauspost/compress](https://github.com/klauspost/compress) and [go.step.sm/crypto](https://github.com/smallstep/crypto).


Updates `github.com/DeRuina/timberjack` from 1.3.8 to 1.3.9
- [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.3.8...v1.3.9)

Updates `github.com/KimMachineGun/automemlimit` from 0.7.4 to 0.7.5
- [Release notes](https://github.com/KimMachineGun/automemlimit/releases)
- [Commits](https://github.com/KimMachineGun/automemlimit/compare/v0.7.4...v0.7.5)

Updates `github.com/klauspost/compress` from 1.18.0 to 1.18.1
- [Release notes](https://github.com/klauspost/compress/releases)
- [Changelog](https://github.com/klauspost/compress/blob/master/.goreleaser.yml)
- [Commits](https://github.com/klauspost/compress/compare/v1.18.0...v1.18.1)

Updates `go.step.sm/crypto` from 0.72.0 to 0.73.0
- [Release notes](https://github.com/smallstep/crypto/releases)
- [Commits](https://github.com/smallstep/crypto/compare/v0.72.0...v0.73.0)

---
updated-dependencies:
- dependency-name: github.com/DeRuina/timberjack
  dependency-version: 1.3.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all-updates
- dependency-name: github.com/KimMachineGun/automemlimit
  dependency-version: 0.7.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all-updates
- dependency-name: github.com/klauspost/compress
  dependency-version: 1.18.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all-updates
- dependency-name: go.step.sm/crypto
  dependency-version: 0.73.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>
2025-11-08 11:13:16 +03:00
Marten Seemann f2199d48b2 chore: update quic-go to v0.56.0, enable qlog for HTTP/3 (#7345) 2025-11-08 00:41:15 -05:00
Kévin Dunglas 8285eba842 caddyhttp: allow customizing the Server header (#7338)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 16s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 24s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 17s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 14s
Lint / lint (ubuntu-latest, linux) (push) Failing after 14s
Lint / govulncheck (push) Successful in 1m17s
Lint / dependency-review (push) Failing after 14s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 45s
2025-11-03 21:00:27 -07:00
Cooper de Nicola 895b56063a chore: fix golangci-lint error G602 in caddyhttp (#7334)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 16s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 13s
Lint / lint (ubuntu-latest, linux) (push) Failing after 14s
Lint / govulncheck (push) Successful in 1m15s
Lint / dependency-review (push) Failing after 14s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 14s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
2025-11-03 03:04:55 +00:00
Mohammed Al Sahaf ddec1838b3 caddytls: correct documentation of LeafFolderLoader (#7327)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 1m10s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 14s
Lint / lint (ubuntu-latest, linux) (push) Failing after 13s
Lint / govulncheck (push) Successful in 1m35s
Lint / dependency-review (push) Failing after 13s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 14s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
* caddytls: correct documentation of `LeafFolderLoader`

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>

* fmt...

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>

---------

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>
2025-11-01 18:29:55 +03:00
Ted 92c8bc7322 caddyfile: fix nested quotes formatted incorrectly by fmt (#7045)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 49s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 14s
Lint / lint (ubuntu-latest, linux) (push) Failing after 13s
Lint / govulncheck (push) Successful in 1m37s
Lint / dependency-review (push) Failing after 15s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 2m12s
* Fix incorrectly formatted quote within quotes with fmt

* Fix incorrectly formatted quote within quotes with fmt
2025-10-27 16:13:47 -06:00
WeidiDeng abe0acabb6 reverseproxy: set default values for keepalive if only some of them are set (#7318)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 15s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 28s
Lint / lint (ubuntu-latest, linux) (push) Failing after 14s
Lint / govulncheck (push) Successful in 1m19s
Lint / dependency-review (push) Failing after 14s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 13s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
2025-10-25 05:15:55 -04:00
Philipp Keidel b54e870b26 tracing: switch to autoexport for OpenTelemetry span exporter (#7317)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 14s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 16s
Lint / lint (ubuntu-latest, linux) (push) Failing after 14s
Lint / govulncheck (push) Successful in 1m18s
Lint / dependency-review (push) Failing after 14s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 14s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
2025-10-23 17:54:50 -06:00
dependabot[bot] 6d90c7707d build(deps): bump github.com/slackhq/nebula from 1.9.5 to 1.9.7 (#7315)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 16s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 14s
Lint / lint (ubuntu-latest, linux) (push) Failing after 14s
Lint / govulncheck (push) Successful in 1m15s
Lint / dependency-review (push) Failing after 14s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 15s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
Bumps [github.com/slackhq/nebula](https://github.com/slackhq/nebula) from 1.9.5 to 1.9.7.
- [Release notes](https://github.com/slackhq/nebula/releases)
- [Changelog](https://github.com/slackhq/nebula/blob/v1.9.7/CHANGELOG.md)
- [Commits](https://github.com/slackhq/nebula/compare/v1.9.5...v1.9.7)

---
updated-dependencies:
- dependency-name: github.com/slackhq/nebula
  dependency-version: 1.9.7
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-23 11:37:02 -06:00
dependabot[bot] cd1c203777 build(deps): bump the all-updates group across 1 directory with 2 updates (#7307)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 15s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 14s
Lint / lint (ubuntu-latest, linux) (push) Failing after 14s
Lint / govulncheck (push) Successful in 1m12s
Lint / dependency-review (push) Failing after 15s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 13s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, 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 2 updates in the / directory: [github.com/spf13/pflag](https://github.com/spf13/pflag) and [go.step.sm/crypto](https://github.com/smallstep/crypto).


Updates `github.com/spf13/pflag` from 1.0.9 to 1.0.10
- [Release notes](https://github.com/spf13/pflag/releases)
- [Commits](https://github.com/spf13/pflag/compare/v1.0.9...v1.0.10)

Updates `go.step.sm/crypto` from 0.70.0 to 0.72.0
- [Release notes](https://github.com/smallstep/crypto/releases)
- [Commits](https://github.com/smallstep/crypto/compare/v0.70.0...v0.72.0)

---
updated-dependencies:
- dependency-name: github.com/spf13/pflag
  dependency-version: 1.0.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all-updates
- dependency-name: go.step.sm/crypto
  dependency-version: 0.72.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>
2025-10-21 22:32:36 -06:00
wyrapeseed 57587ed18e refactor: use reflect.TypeFor (#7313)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 47s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 44s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 14s
Lint / lint (ubuntu-latest, linux) (push) Failing after 14s
Lint / govulncheck (push) Successful in 1m31s
Lint / dependency-review (push) Failing after 14s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 14s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, 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: wyrapeseed <wyrapeseed@outlook.com>
2025-10-21 13:26:30 -06:00
WeidiDeng 1e21b660c4 reverseproxy: use http.Protocols to handle h2c requests (#6990)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 15s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 16s
Lint / lint (ubuntu-latest, linux) (push) Failing after 14s
Lint / govulncheck (push) Successful in 1m10s
Lint / dependency-review (push) Failing after 15s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 14s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
2025-10-21 16:05:43 +00:00
Mohammed Al Sahaf 595aab8bc0 metrics: resolve per-host inifinite cardinality (#7306)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 16s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 15s
Lint / lint (ubuntu-latest, linux) (push) Failing after 14s
Lint / govulncheck (push) Successful in 1m16s
Lint / dependency-review (push) Failing after 14s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 14s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
2025-10-21 06:06:56 +00:00
WeidiDeng 8aca108d2c reverseproxy: do not disable keepalive if proxy protocol is used (#7300) 2025-10-21 05:09:37 +00:00
Pavel 156ce99d3a listeners: Add support for named socket activation (#7243) 2025-10-21 04:55:42 +00:00
WeidiDeng a7885aabec intercept: use already buffered response if possible when intercepting (#7028) 2025-10-21 04:48:07 +00:00
Bashayer Alrumahi f5f25d845a logging: fix multiple regexp filters on same field (fixes #7049) (#7061)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 42s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 17s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 13s
Lint / lint (ubuntu-latest, linux) (push) Failing after 13s
Lint / govulncheck (push) Successful in 1m33s
Lint / dependency-review (push) Failing after 13s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 14s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
* logging: fix multiple regexp filters on same field (fixes #7049)

* fix: add proper error handling in MultiRegexpFilter tests

* fix: resolve linter and test issues - Fix GCI import formatting issues - Fix MultiRegexpFilter input size limit test by ensuring output doesn't exceed max length after each operation - All tests now pass and linter issues resolved

* fix: update integration test for proper JSON encoding - Fix expected JSON output to use Unicode escape sequence for ampersand character - Integration tests now pass
2025-10-16 05:08:53 +00:00
WeidiDeng 1ce2a13ad1 caddyhttp: wrap accepted connection to suppress tls.ConnectionState (#7247) 2025-10-16 03:13:40 +00:00
Chris Seufert d7185fd002 caddyhttp: Add trusted_proxies_unix for trusting unix socket X-Forwarded-* headers (#7265) 2025-10-16 02:47:32 +00:00
Anthony Biondo 7fb39ec1e5 reverseproxy: Use http1.1 upgrade for websocket for extended connect of http2 and http3 (#7305)
Co-authored-by: WeidiDeng <weidi_deng@icloud.com>
2025-10-16 02:20:20 +00:00
aeris 10ac7da037 logging: Switch from lumberjack to timberjack, add time-rolling options (#7244)
Co-authored-by: Francis Lavoie <lavofr@gmail.com>
2025-10-15 17:11:10 -04:00
wyrapeseed d115cd1042 chore: fix some comments (#7303) 2025-10-15 03:58:53 +00:00
joshuamcbeth de6b78009b caddyhttp: Add server options keepalive_idle and keepalive_count (#7298)
* Add Server options KeepAliveIdle (keepalive_idle) and KeepAliveCount (keepalive_count)

Signed-off-by: Joshua McBeth <joshua.mcbeth@gmail.com>

* Add Server option KeepAliveDisable (keepalive_disable)

Signed-off-by: Joshua McBeth <joshua.mcbeth@gmail.com>

* Remove Server option KeepAliveDisable (keepalive_disable), disable when interval is negative

Signed-off-by: Joshua McBeth <joshua.mcbeth@gmail.com>

* Add keepalive parameters to caddyfiletest

Signed-off-by: Joshua McBeth <joshua.mcbeth@gmail.com>

---------

Signed-off-by: Joshua McBeth <joshua.mcbeth@gmail.com>
2025-10-14 12:03:23 -06:00
WeidiDeng 2ec28bca43 reverse_proxy: use http1 for outbound tls requests with placeholder that are likely websockets (#7296) 2025-10-09 10:36:49 -06:00
Marten Seemann 178294e9d7 chore: Update quic-go to v0.55.0 (#7288)
Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
2025-10-06 19:43:28 -04:00
GreyXor 13a4ec7597 basicauth: Implement argon2id (#7186)
* feat: add argon2id hash-password command

* feat: ardon2id owasp safe value

* feat: add argon2id compare method

* chore: fmt argon2id

* docs: more argon2id docs

* chore: upgrade x/crypto dep

* revert: remove golangci

* refactor: argon2id decode

* chore: update deps

* refactor: simplify argon2id compare return

* chore: upgrade dependencies

* chore: upgrade dependencies
2025-10-06 17:27:06 -06:00
Monviech 2f1d270968 httpcaddyfile: Map default_bind to BindHost in globalACMEDefaults (#7278)
* Implement BindHost fallback in ACME issuer

* Fix indentation

* Skip creating empty challenges stub in adapted json config

* Skip setting BindHost for DNS Challenge

* golangci-lint fix

---------

Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
2025-10-06 16:48:38 -06:00
Aditya Bhargava 3c003deec6 httpcaddyfile: Add missing DNS challenge check for acme_dns (#7270)
* add optional argument to `mock` DNS provider

* preserve local DNS challenge settings when `acme_dns` is specified

* add missing check for `acme_dns`
2025-10-03 14:05:46 -06:00
dependabot[bot] afbdcec08b build(deps): bump the actions-deps group with 8 updates (#7284)
Bumps the actions-deps group with 8 updates:

| Package | From | To |
| --- | --- | --- |
| [step-security/harden-runner](https://github.com/step-security/harden-runner) | `2.13.0` | `2.13.1` |
| [actions/setup-go](https://github.com/actions/setup-go) | `5.5.0` | `6.0.0` |
| [actions/dependency-review-action](https://github.com/actions/dependency-review-action) | `4.7.3` | `4.8.0` |
| [sigstore/cosign-installer](https://github.com/sigstore/cosign-installer) | `3.9.2` | `3.10.0` |
| [anchore/sbom-action](https://github.com/anchore/sbom-action) | `0.20.5` | `0.20.6` |
| [peter-evans/repository-dispatch](https://github.com/peter-evans/repository-dispatch) | `3.0.0` | `4.0.0` |
| [ossf/scorecard-action](https://github.com/ossf/scorecard-action) | `2.4.2` | `2.4.3` |
| [github/codeql-action](https://github.com/github/codeql-action) | `3.30.0` | `3.30.5` |


Updates `step-security/harden-runner` from 2.13.0 to 2.13.1
- [Release notes](https://github.com/step-security/harden-runner/releases)
- [Commits](https://github.com/step-security/harden-runner/compare/ec9f2d5744a09debf3a187a3f4f675c53b671911...f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a)

Updates `actions/setup-go` from 5.5.0 to 6.0.0
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/d35c59abb061a4a6fb18e82ac0862c26744d6ab5...44694675825211faa026b3c33043df3e48a5fa00)

Updates `actions/dependency-review-action` from 4.7.3 to 4.8.0
- [Release notes](https://github.com/actions/dependency-review-action/releases)
- [Commits](https://github.com/actions/dependency-review-action/compare/595b5aeba73380359d98a5e087f648dbb0edce1b...56339e523c0409420f6c2c9a2f4292bbb3c07dd3)

Updates `sigstore/cosign-installer` from 3.9.2 to 3.10.0
- [Release notes](https://github.com/sigstore/cosign-installer/releases)
- [Commits](https://github.com/sigstore/cosign-installer/compare/d58896d6a1865668819e1d91763c7751a165e159...d7543c93d881b35a8faa02e8e3605f69b7a1ce62)

Updates `anchore/sbom-action` from 0.20.5 to 0.20.6
- [Release notes](https://github.com/anchore/sbom-action/releases)
- [Changelog](https://github.com/anchore/sbom-action/blob/main/RELEASE.md)
- [Commits](https://github.com/anchore/sbom-action/compare/da167eac915b4e86f08b264dbdbc867b61be6f0c...f8bdd1d8ac5e901a77a92f111440fdb1b593736b)

Updates `peter-evans/repository-dispatch` from 3.0.0 to 4.0.0
- [Release notes](https://github.com/peter-evans/repository-dispatch/releases)
- [Commits](https://github.com/peter-evans/repository-dispatch/compare/ff45666b9427631e3450c54a1bcbee4d9ff4d7c0...5fc4efd1a4797ddb68ffd0714a238564e4cc0e6f)

Updates `ossf/scorecard-action` from 2.4.2 to 2.4.3
- [Release notes](https://github.com/ossf/scorecard-action/releases)
- [Changelog](https://github.com/ossf/scorecard-action/blob/main/RELEASE.md)
- [Commits](https://github.com/ossf/scorecard-action/compare/05b42c624433fc40578a4040d5cf5e36ddca8cde...4eaacf0543bb3f2c246792bd56e8cdeffafb205a)

Updates `github/codeql-action` from 3.30.0 to 3.30.5
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/2d92b76c45b91eb80fc44c74ce3fce0ee94e8f9d...3599b3baa15b485a2e49ef411a7a4bb2452e7f93)

---
updated-dependencies:
- dependency-name: step-security/harden-runner
  dependency-version: 2.13.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-deps
- dependency-name: actions/setup-go
  dependency-version: 6.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions-deps
- dependency-name: actions/dependency-review-action
  dependency-version: 4.8.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions-deps
- dependency-name: sigstore/cosign-installer
  dependency-version: 3.10.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions-deps
- dependency-name: anchore/sbom-action
  dependency-version: 0.20.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-deps
- dependency-name: peter-evans/repository-dispatch
  dependency-version: 4.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions-deps
- dependency-name: ossf/scorecard-action
  dependency-version: 2.4.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-deps
- dependency-name: github/codeql-action
  dependency-version: 3.30.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-deps
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-01 23:11:09 +00:00
Francis Lavoie 65e0ddc221 core: Reloading with SIGUSR1 if config never changed via admin (#7258)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 54s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 28s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 18s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 13s
Lint / lint (ubuntu-latest, linux) (push) Failing after 14s
Lint / govulncheck (push) Successful in 1m38s
Lint / dependency-review (push) Failing after 15s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Has started running
2025-09-26 16:50:15 +00:00
WeidiDeng b2ab419922 core: use reflect.TypeFor to check for encoding/json.RawMessage (#7274)
* check if the raw message type is the json v2 type

* use reflect.TypeFor to check for encoding/json.RawMessage

---------

Co-authored-by: Mohammed Al Sahaf <msaa1990@gmail.com>
2025-09-26 10:46:18 -06:00
asttool bc0e184130 caddyhttp: omit unnecessary reassignment (#7276)
Signed-off-by: asttool <asttool@outlook.com>
2025-09-26 10:44:58 -06:00
Y.Horie 1e82f9652e caddypki: check intermediate lifetime to actual root cert lifetime (#7272)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 15s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 22s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 17s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 23s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 14s
Lint / lint (ubuntu-latest, linux) (push) Failing after 14s
Lint / govulncheck (push) Successful in 1m14s
Lint / dependency-review (push) Failing after 14s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 14s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
2025-09-26 10:24:52 -06:00
Mohammed Al Sahaf 25be2f26fc chore: ugh, lint fix... (#7275)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 14s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 22s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 15s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 14s
Lint / lint (ubuntu-latest, linux) (push) Failing after 14s
Lint / govulncheck (push) Successful in 1m32s
Lint / dependency-review (push) Failing after 16s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 14s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
* chore: ugh, lint fix...

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>

* more lint fixes

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>

---------

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>
2025-09-26 03:14:48 -04:00
Marten Seemann 0c8798fce3 go.mod: update quic-go to v0.54.1 (#7273) 2025-09-25 19:24:26 -06:00
Gilbert Gilb's f5c3094050 cmd: prevent commas in header values from being split (#7268)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 1s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 0s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 0s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 0s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 0s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 0s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 0s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 0s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 0s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 0s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 1s
Lint / lint (ubuntu-latest, linux) (push) Failing after 0s
Lint / govulncheck (push) Failing after 0s
Lint / dependency-review (push) Failing after 0s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 47s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
`pflag.GetStringSlice` treats commas as delimiters, which causes issues
when passing headers whose values contain commas (`X-Robots-Tag:
noindex, nofollow`). These are incorrectly split into multiple headers
and errors out:

- `X-Robots-Tag: noindex`
- ` nofollow`

Switch to `pflag.GetStringArray`, which does not split on commas[1].

Note that this changes behavior for cases where multiple headers were
provided in a single argument with commas (`--header-down "X-Foo:
Bar,X-Bar: Foo"`). Such cases will now be treated as a single header
value. If this breaking change is unacceptable, we will need a smarter
fallback mechanism.

[1] https://github.com/spf13/pflag/pull/90
2025-09-22 21:12:06 -06:00
Francis Lavoie 39ace450de logging: Adjustments to BufferedLog to keep logs in the correct order (#7257)
* logging: Adjustments to BufferedLog to keep logs in the correct order

* Ignore lints
2025-09-15 09:29:50 -06:00
Artur H. 0ba8786b35 caddyfile: Allow block to do nothing if nothing passed to import (#7206)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 49s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 28s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 16s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 19s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 54s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 14s
Lint / lint (ubuntu-latest, linux) (push) Failing after 15s
Lint / govulncheck (push) Successful in 1m42s
Lint / dependency-review (push) Failing after 14s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Has started running
2025-09-12 20:29:09 +00:00
mickychang9 bcd4055e89 Use WaitGroup.Go to simplify code (#7253)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 57s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 13s
Lint / lint (ubuntu-latest, linux) (push) Failing after 13s
Lint / govulncheck (push) Successful in 1m31s
Lint / dependency-review (push) Failing after 14s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 14s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, 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: mickychang9 <mickychang9@outlook.com>
2025-09-11 10:15:09 -06:00
WeidiDeng b462615439 fileserver: set Content-Length for precompressed files (#7251)
* set Content-Length for precompressed files

* Update modules/caddyhttp/fileserver/staticfiles.go

Co-authored-by: Matt Holt <mholt@users.noreply.github.com>

---------

Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
2025-09-10 22:48:03 -06:00
Francis Lavoie 012b4b3d40 logging: Buffer the logs before config is loaded (#7245) 2025-09-10 16:03:52 +00:00
Pavel d9cc24f3df caddypki: Disable internal auto-CA when auto_https is disabled (fix #7211) (#7238)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 50s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Failing after 13s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Failing after 14s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Failing after 14s
Lint / lint (ubuntu-latest, linux) (push) Failing after 14s
Lint / govulncheck (push) Successful in 1m53s
Lint / dependency-review (push) Failing after 14s
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Failing after 15s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, 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: Matt Holt <mholt@users.noreply.github.com>
2025-09-05 09:41:06 -06:00
Matthew Holt 38848f7f25 caddytls: Allow disabling distributed solving (except http-01) 2025-09-04 08:51:36 -06:00
Siomachkin 5473eb95d8 encode: fix response corruption when handle_errors is used (#7235)
* encode: fix response corruption when handle_errors is used

* Move disabled check before hdr assignment
2025-09-02 15:34:56 -06:00
dependabot[bot] 2d0f3f887b build(deps): bump the actions-deps group with 5 updates (#7237)
Bumps the actions-deps group with 5 updates:

| Package | From | To |
| --- | --- | --- |
| [actions/checkout](https://github.com/actions/checkout) | `4.2.2` | `5.0.0` |
| [goreleaser/goreleaser-action](https://github.com/goreleaser/goreleaser-action) | `6.3.0` | `6.4.0` |
| [actions/dependency-review-action](https://github.com/actions/dependency-review-action) | `4.7.1` | `4.7.3` |
| [anchore/sbom-action](https://github.com/anchore/sbom-action) | `0.20.4` | `0.20.5` |
| [github/codeql-action](https://github.com/github/codeql-action) | `3.29.7` | `3.30.0` |


Updates `actions/checkout` from 4.2.2 to 5.0.0
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4.2.2...08c6903cd8c0fde910a37f88322edcfb5dd907a8)

Updates `goreleaser/goreleaser-action` from 6.3.0 to 6.4.0
- [Release notes](https://github.com/goreleaser/goreleaser-action/releases)
- [Commits](https://github.com/goreleaser/goreleaser-action/compare/9c156ee8a17a598857849441385a2041ef570552...e435ccd777264be153ace6237001ef4d979d3a7a)

Updates `actions/dependency-review-action` from 4.7.1 to 4.7.3
- [Release notes](https://github.com/actions/dependency-review-action/releases)
- [Commits](https://github.com/actions/dependency-review-action/compare/da24556b548a50705dd671f47852072ea4c105d9...595b5aeba73380359d98a5e087f648dbb0edce1b)

Updates `anchore/sbom-action` from 0.20.4 to 0.20.5
- [Release notes](https://github.com/anchore/sbom-action/releases)
- [Changelog](https://github.com/anchore/sbom-action/blob/main/RELEASE.md)
- [Commits](https://github.com/anchore/sbom-action/compare/7b36ad622f042cab6f59a75c2ac24ccb256e9b45...da167eac915b4e86f08b264dbdbc867b61be6f0c)

Updates `github/codeql-action` from 3.29.7 to 3.30.0
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/51f77329afa6477de8c49fc9c7046c15b9a4e79d...2d92b76c45b91eb80fc44c74ce3fce0ee94e8f9d)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 5.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions-deps
- dependency-name: goreleaser/goreleaser-action
  dependency-version: 6.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions-deps
- dependency-name: actions/dependency-review-action
  dependency-version: 4.7.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-deps
- dependency-name: anchore/sbom-action
  dependency-version: 0.20.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-deps
- dependency-name: github/codeql-action
  dependency-version: 3.30.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions-deps
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-02 13:05:58 -06:00
dependabot[bot] 39357d3e5c build(deps): bump the all-updates group with 17 updates (#7236)
Bumps the all-updates group with 17 updates:

| Package | From | To |
| --- | --- | --- |
| [github.com/go-chi/chi/v5](https://github.com/go-chi/chi) | `5.2.2` | `5.2.3` |
| [github.com/google/cel-go](https://github.com/google/cel-go) | `0.26.0` | `0.26.1` |
| [github.com/spf13/cobra](https://github.com/spf13/cobra) | `1.9.1` | `1.10.1` |
| [github.com/spf13/pflag](https://github.com/spf13/pflag) | `1.0.7` | `1.0.9` |
| [github.com/stretchr/testify](https://github.com/stretchr/testify) | `1.10.0` | `1.11.1` |
| [go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp](https://github.com/open-telemetry/opentelemetry-go-contrib) | `0.61.0` | `0.63.0` |
| [go.opentelemetry.io/contrib/propagators/autoprop](https://github.com/open-telemetry/opentelemetry-go-contrib) | `0.62.0` | `0.63.0` |
| [go.opentelemetry.io/otel](https://github.com/open-telemetry/opentelemetry-go) | `1.37.0` | `1.38.0` |
| [go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc](https://github.com/open-telemetry/opentelemetry-go) | `1.37.0` | `1.38.0` |
| [go.opentelemetry.io/otel/sdk](https://github.com/open-telemetry/opentelemetry-go) | `1.37.0` | `1.38.0` |
| [golang.org/x/crypto](https://github.com/golang/crypto) | `0.40.0` | `0.41.0` |
| [golang.org/x/net](https://github.com/golang/net) | `0.42.0` | `0.43.0` |
| [golang.org/x/term](https://github.com/golang/term) | `0.33.0` | `0.34.0` |
| [github.com/libdns/libdns](https://github.com/libdns/libdns) | `1.1.0` | `1.1.1` |
| [go.opentelemetry.io/otel/trace](https://github.com/open-telemetry/opentelemetry-go) | `1.37.0` | `1.38.0` |
| [go.step.sm/crypto](https://github.com/smallstep/crypto) | `0.67.0` | `0.70.0` |
| [golang.org/x/sys](https://github.com/golang/sys) | `0.34.0` | `0.35.0` |


Updates `github.com/go-chi/chi/v5` from 5.2.2 to 5.2.3
- [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.2...v5.2.3)

Updates `github.com/google/cel-go` from 0.26.0 to 0.26.1
- [Release notes](https://github.com/google/cel-go/releases)
- [Commits](https://github.com/google/cel-go/compare/v0.26.0...v0.26.1)

Updates `github.com/spf13/cobra` from 1.9.1 to 1.10.1
- [Release notes](https://github.com/spf13/cobra/releases)
- [Commits](https://github.com/spf13/cobra/compare/v1.9.1...v1.10.1)

Updates `github.com/spf13/pflag` from 1.0.7 to 1.0.9
- [Release notes](https://github.com/spf13/pflag/releases)
- [Commits](https://github.com/spf13/pflag/compare/v1.0.7...v1.0.9)

Updates `github.com/stretchr/testify` from 1.10.0 to 1.11.1
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.10.0...v1.11.1)

Updates `go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp` from 0.61.0 to 0.63.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.61.0...zpages/v0.63.0)

Updates `go.opentelemetry.io/contrib/propagators/autoprop` from 0.62.0 to 0.63.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.62.0...zpages/v0.63.0)

Updates `go.opentelemetry.io/otel` from 1.37.0 to 1.38.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.37.0...v1.38.0)

Updates `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc` from 1.37.0 to 1.38.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.37.0...v1.38.0)

Updates `go.opentelemetry.io/otel/sdk` from 1.37.0 to 1.38.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.37.0...v1.38.0)

Updates `golang.org/x/crypto` from 0.40.0 to 0.41.0
- [Commits](https://github.com/golang/crypto/compare/v0.40.0...v0.41.0)

Updates `golang.org/x/net` from 0.42.0 to 0.43.0
- [Commits](https://github.com/golang/net/compare/v0.42.0...v0.43.0)

Updates `golang.org/x/term` from 0.33.0 to 0.34.0
- [Commits](https://github.com/golang/term/compare/v0.33.0...v0.34.0)

Updates `github.com/libdns/libdns` from 1.1.0 to 1.1.1
- [Release notes](https://github.com/libdns/libdns/releases)
- [Commits](https://github.com/libdns/libdns/compare/v1.1.0...v1.1.1)

Updates `go.opentelemetry.io/otel/trace` from 1.37.0 to 1.38.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.37.0...v1.38.0)

Updates `go.step.sm/crypto` from 0.67.0 to 0.70.0
- [Release notes](https://github.com/smallstep/crypto/releases)
- [Commits](https://github.com/smallstep/crypto/compare/v0.67.0...v0.70.0)

Updates `golang.org/x/sys` from 0.34.0 to 0.35.0
- [Commits](https://github.com/golang/sys/compare/v0.34.0...v0.35.0)

---
updated-dependencies:
- dependency-name: github.com/go-chi/chi/v5
  dependency-version: 5.2.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all-updates
- dependency-name: github.com/google/cel-go
  dependency-version: 0.26.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all-updates
- dependency-name: github.com/spf13/cobra
  dependency-version: 1.10.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all-updates
- dependency-name: github.com/spf13/pflag
  dependency-version: 1.0.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all-updates
- dependency-name: github.com/stretchr/testify
  dependency-version: 1.11.1
  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.63.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.63.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all-updates
- dependency-name: go.opentelemetry.io/otel
  dependency-version: 1.38.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all-updates
- dependency-name: go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc
  dependency-version: 1.38.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.38.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all-updates
- dependency-name: golang.org/x/crypto
  dependency-version: 0.41.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all-updates
- dependency-name: golang.org/x/net
  dependency-version: 0.43.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all-updates
- dependency-name: golang.org/x/term
  dependency-version: 0.34.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all-updates
- dependency-name: github.com/libdns/libdns
  dependency-version: 1.1.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.38.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all-updates
- dependency-name: go.step.sm/crypto
  dependency-version: 0.70.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: all-updates
- dependency-name: golang.org/x/sys
  dependency-version: 0.35.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>
2025-09-02 11:35:09 -06:00
Max Celant 3553cfb6ad caddyhttp: remove redundant middleware next copy (#7217) 2025-09-01 09:30:34 -06:00
aro-lew 806fef85be encode: add graphql-response header to list (#7214)
Tests / test (./cmd/caddy/caddy, ~1.25.0, ubuntu-latest, 0, 1.25, linux) (push) Failing after 2m15s
Tests / test (s390x on IBM Z) (push) Has been skipped
Tests / goreleaser-check (push) Has been skipped
Cross-Build / build (~1.25.0, 1.25, aix) (push) Successful in 1m18s
Cross-Build / build (~1.25.0, 1.25, darwin) (push) Successful in 1m16s
Cross-Build / build (~1.25.0, 1.25, dragonfly) (push) Successful in 1m22s
Cross-Build / build (~1.25.0, 1.25, freebsd) (push) Successful in 1m16s
Cross-Build / build (~1.25.0, 1.25, illumos) (push) Successful in 1m16s
Cross-Build / build (~1.25.0, 1.25, linux) (push) Successful in 1m14s
Cross-Build / build (~1.25.0, 1.25, netbsd) (push) Successful in 1m17s
Cross-Build / build (~1.25.0, 1.25, openbsd) (push) Successful in 1m15s
Cross-Build / build (~1.25.0, 1.25, solaris) (push) Successful in 1m18s
Cross-Build / build (~1.25.0, 1.25, windows) (push) Successful in 1m16s
Lint / lint (ubuntu-latest, linux) (push) Successful in 2m38s
Lint / govulncheck (push) Successful in 1m44s
Lint / dependency-review (push) Failing after 56s
Tests / test (./cmd/caddy/caddy, ~1.25.0, macos-14, 0, 1.25, mac) (push) Has been cancelled
Tests / test (./cmd/caddy/caddy.exe, ~1.25.0, windows-latest, True, 1.25, windows) (push) Has been cancelled
Lint / lint (macos-14, mac) (push) Has been cancelled
Lint / lint (windows-latest, windows) (push) Has been cancelled
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Has started running
2025-08-27 14:58:14 -06:00
Arpan Saha 6d73d85c1f caddyfile: prevent adding trailing space on line before env variable (#7215) 2025-08-26 15:13:54 -06:00
Matthew Holt e0a8f9541d caddyhttp: Normalize (lowercase) {label.N} placeholders 2025-08-25 13:18:13 -06:00
Matthew Holt b866a9e099 It can't be in the subfolder!? 2025-08-25 13:13:27 -06:00
Matthew Holt 1db26128a6 Uhh I guess it has to be named something specific 2025-08-25 13:11:30 -06:00
Matthew Holt 02c9f0ff90 Tweak issue form 2025-08-25 13:07:17 -06:00
Matthew Holt 63ec1f4e1c Issue template chooser config 2025-08-25 13:05:02 -06:00
Matthew Holt 293de94f34 Fix issue form ... again 2025-08-25 13:02:54 -06:00
Matthew Holt d8d359eca2 Fix syntax of issue form 2025-08-25 12:59:41 -06:00
Matt Holt 11a95cee6d AI assistance disclosure (#7212)
* AI disclosure templates

* Update assistance disclosure

* Add AI moderator

* Set read permissions by default

* Pin to hash
2025-08-25 12:50:26 -06:00
Matthew Holt b7c022a61a Set read permissions as default 2025-08-25 10:18:40 -06:00
Bang Lee 5e2953670e caddyhttp: add replacer placeholders for escaped values (#7181) 2025-08-25 09:07:51 -06:00
Artur H. 551f793700 caddyfile: Fix importing nested tokens for {block} (#7189) 2025-08-22 21:29:34 +00:00
Francis Lavoie 4564261d83 httpcaddyfile: Fix acme_dns regression (#7199) 2025-08-22 15:09:25 -06:00
Francis Lavoie 16fe83c7af http: Make logger first, before TLS provisioning (#7198) 2025-08-22 14:24:08 -06:00
234 changed files with 19728 additions and 2157 deletions
+31
View File
@@ -0,0 +1,31 @@
name: Issue
description: An actionable development item, like a bug report or feature request
body:
- type: markdown
attributes:
value: |
Thank you for opening an issue! This is for actionable development items like bug reports and feature requests.
If you have a question about using Caddy, please [post on our forums](https://caddy.community) instead.
- type: textarea
id: content
attributes:
label: Issue Details
placeholder: Describe the issue here. Be specific by providing complete logs and minimal instructions to reproduce, or a thoughtful proposal, etc.
validations:
required: true
- type: dropdown
id: assistance-disclosure
attributes:
label: Assistance Disclosure
description: "Our project allows assistance by AI/LLM tools as long as it is disclosed and described so we can better respond. Please certify whether you have used any such tooling related to this issue:"
options:
-
- AI used
- AI not used
validations:
required: true
- type: input
id: assistance-description
attributes:
label: If AI was used, describe the extent to which it was used.
description: 'Examples: "ChatGPT translated from my native language" or "Claude proposed this change/feature"'
+5
View File
@@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Caddy forum
url: https://caddy.community
about: If you have questions (or answers!) about using Caddy, please use our forum
+12 -7
View File
@@ -1,15 +1,14 @@
# Security Policy
The Caddy project would like to make sure that it stays on top of all practically-exploitable vulnerabilities.
The Caddy project would like to make sure that it stays on top of all relevant and practically-exploitable vulnerabilities.
## Supported Versions
| Version | Supported |
| -------- | ----------|
| 2.latest | ✔️ |
| 1.x | :x: |
| < 1.x | :x: |
| Version | Supported |
| ----------- | ----------|
| 2.latest | ✔️ |
| < 2.latest | :x: |
## Acceptable Scope
@@ -18,7 +17,7 @@ A security report must demonstrate a security bug in the source code from this r
Some security problems are the result of interplay between different components of the Web, rather than a vulnerability in the web server itself. Please only report vulnerabilities in the web server itself, as we cannot coerce the rest of the Web to be fixed (for example, we do not consider IP spoofing, BGP hijacks, or missing/misconfigured HTTP headers a vulnerability in the Caddy web server).
Vulnerabilities caused by misconfigurations are out of scope. Yes, it is entirely possible to craft and use a configuration that is unsafe, just like with every other web server; we recommend against doing that.
Vulnerabilities caused by misconfigurations are out of scope. Yes, it is entirely possible to craft and use a configuration that is unsafe, just like with every other web server; we recommend against doing that. Similarly, external misconfigurations are out of scope. For example, an open or forwarded port from a public network to a Caddy instance intended to serve only internal clients is not a vulnerability in Caddy.
We do not accept reports if the steps imply or require a compromised system or third-party software, as we cannot control those. We expect that users secure their own systems and keep all their software patched. For example, if untrusted users are able to upload/write/host arbitrary files in the web root directory, it is NOT a security bug in Caddy if those files get served to clients; however, it _would_ be a valid report if a bug in Caddy's source code unintentionally gave unauthorized users the ability to upload unsafe files or delete files without relying on an unpatched system or piece of software.
@@ -26,6 +25,10 @@ Client-side exploits are out of scope. In other words, it is not a bug in Caddy
Security bugs in code dependencies (including Go's standard library) are out of scope. Instead, if a dependency has patched a relevant security bug, please feel free to open a public issue or pull request to update that dependency in our code.
Many reports are not security bugs and can be addressed by updating the documentation.
We accept security reports and patches, but do not assign CVEs, for code that has not been released with a non-prerelease tag.
## Reporting a Vulnerability
@@ -33,6 +36,8 @@ We get a lot of difficult reports that turn out to be invalid. Clear, obvious re
First please ensure your report falls within the accepted scope of security bugs (above).
:warning: **YOU MUST DISCLOSE WHETHER YOU USED LLMs ("AI") IN ANY WAY.** Whether you are using AI for discovery, as part of writing the report or its replies, and/or testing or validating proofs and changes, we require you to mention the extent of it. **FAILURE TO INCLUDE A DISCLOSURE EVEN IF YOU DO NOT USE AI MAY LEAD TO IMMEDIATE DISMISSAL OF YOUR REPORT AND POTENTIAL BLOCKLISTING.** We will not waste our time chatting with bots. But if you're a human, pull up a chair and we'll drink some chocolate milk.
We'll need enough information to verify the bug and make a patch. To speed things up, please include:
- Most minimal possible config (without redactions!)
+29
View File
@@ -0,0 +1,29 @@
## Assistance Disclosure
<!--
Thank you for contributing! Please note:
The use of AI/LLM tools is allowed so long as it is disclosed, so
that we can provide better code review and maintain project quality.
If you used AI/LLM tooling in any way related to this PR, please
let us know to what extent it was utilized.
Examples:
"No AI was used."
"I wrote the code, but Claude generated the tests."
"I consulted ChatGPT for a solution, but I authored/coded it myself."
"Cody generated the code, and I verified it is correct."
"Copilot provided tab completion for code and comments."
We expect that you have vetted your contributions for correctness.
Additionally, signing our CLA certifies that you have the rights to
contribute this change.
Replace the text below with your disclosure:
-->
_This PR is missing an assistance disclosure._
+30
View File
@@ -0,0 +1,30 @@
name: AI Moderator
permissions: read-all
on:
issues:
types: [opened]
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
jobs:
spam-detection:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
models: read
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
- uses: github/ai-moderator@81159c370785e295c97461ade67d7c33576e9319
with:
token: ${{ secrets.GITHUB_TOKEN }}
spam-label: 'spam'
ai-label: 'ai-generated'
minimize-detected-comments: true
# Built-in prompt configuration (all enabled by default)
enable-spam-detection: true
enable-link-spam-detection: true
enable-ai-detection: true
# custom-prompt-path: '.github/prompts/my-custom.prompt.yml' # Optional
+221
View File
@@ -0,0 +1,221 @@
name: Release Proposal Approval Tracker
on:
pull_request_review:
types: [submitted, dismissed]
pull_request:
types: [labeled, unlabeled, synchronize, closed]
permissions:
contents: read
pull-requests: write
issues: write
jobs:
check-approvals:
name: Track Maintainer Approvals
runs-on: ubuntu-latest
# Only run on PRs with release-proposal label
if: contains(github.event.pull_request.labels.*.name, 'release-proposal') && github.event.pull_request.state == 'open'
steps:
- name: Check approvals and update PR
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
env:
MAINTAINER_LOGINS: ${{ secrets.MAINTAINER_LOGINS }}
with:
script: |
const pr = context.payload.pull_request;
// Extract version from PR title (e.g., "Release Proposal: v1.2.3")
const versionMatch = pr.title.match(/Release Proposal:\s*(v[\d.]+(?:-[\w.]+)?)/);
const commitMatch = pr.body.match(/\*\*Target Commit:\*\*\s*`([a-f0-9]+)`/);
if (!versionMatch || !commitMatch) {
console.log('Could not extract version from title or commit from body');
return;
}
const version = versionMatch[1];
const targetCommit = commitMatch[1];
console.log(`Version: ${version}, Target Commit: ${targetCommit}`);
// Get all reviews
const reviews = await github.rest.pulls.listReviews({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: pr.number
});
// Get list of maintainers
const maintainerLoginsRaw = process.env.MAINTAINER_LOGINS || '';
const maintainerLogins = maintainerLoginsRaw
.split(/[,;]/)
.map(login => login.trim())
.filter(login => login.length > 0);
console.log(`Maintainer logins: ${maintainerLogins.join(', ')}`);
// Get the latest review from each user
const latestReviewsByUser = {};
reviews.data.forEach(review => {
const username = review.user.login;
if (!latestReviewsByUser[username] || new Date(review.submitted_at) > new Date(latestReviewsByUser[username].submitted_at)) {
latestReviewsByUser[username] = review;
}
});
// Count approvals from maintainers
const maintainerApprovals = Object.entries(latestReviewsByUser)
.filter(([username, review]) =>
maintainerLogins.includes(username) &&
review.state === 'APPROVED'
)
.map(([username, review]) => username);
const approvalCount = maintainerApprovals.length;
console.log(`Found ${approvalCount} maintainer approvals from: ${maintainerApprovals.join(', ')}`);
// Get current labels
const currentLabels = pr.labels.map(label => label.name);
const hasApprovedLabel = currentLabels.includes('approved');
const hasAwaitingApprovalLabel = currentLabels.includes('awaiting-approval');
if (approvalCount >= 2 && !hasApprovedLabel) {
console.log('✅ Quorum reached! Updating PR...');
// Remove awaiting-approval label if present
if (hasAwaitingApprovalLabel) {
await github.rest.issues.removeLabel({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
name: 'awaiting-approval'
}).catch(e => console.log('Label not found:', e.message));
}
// Add approved label
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
labels: ['approved']
});
// Add comment with tagging instructions
const approversList = maintainerApprovals.map(u => `@${u}`).join(', ');
const commentBody = [
'## ✅ Approval Quorum Reached',
'',
`This release proposal has been approved by ${approvalCount} maintainers: ${approversList}`,
'',
'### Tagging Instructions',
'',
'A maintainer should now create and push the signed tag:',
'',
'```bash',
`git checkout ${targetCommit}`,
`git tag -s ${version} -m "Release ${version}"`,
`git push origin ${version}`,
`git checkout -`,
'```',
'',
'The release workflow will automatically start when the tag is pushed.'
].join('\n');
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
body: commentBody
});
console.log('Posted tagging instructions');
} else if (approvalCount < 2 && hasApprovedLabel) {
console.log('⚠️ Approval count dropped below quorum, removing approved label');
// Remove approved label
await github.rest.issues.removeLabel({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
name: 'approved'
}).catch(e => console.log('Label not found:', e.message));
// Add awaiting-approval label
if (!hasAwaitingApprovalLabel) {
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
labels: ['awaiting-approval']
});
}
} else {
console.log(`⏳ Waiting for more approvals (${approvalCount}/2 required)`);
}
handle-pr-closed:
name: Handle PR Closed Without Tag
runs-on: ubuntu-latest
if: |
contains(github.event.pull_request.labels.*.name, 'release-proposal') &&
github.event.action == 'closed' && !contains(github.event.pull_request.labels.*.name, 'released')
steps:
- name: Add cancelled label and comment
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
script: |
const pr = context.payload.pull_request;
// Check if the release-in-progress label is present
const hasReleaseInProgress = pr.labels.some(label => label.name === 'release-in-progress');
if (hasReleaseInProgress) {
// PR was closed while release was in progress - this is unusual
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
body: '⚠️ **Warning:** This PR was closed while a release was in progress. This may indicate an error. Please verify the release status.'
});
} else {
// PR was closed before tag was created - this is normal cancellation
const versionMatch = pr.title.match(/Release Proposal:\s*(v[\d.]+(?:-[\w.]+)?)/);
const version = versionMatch ? versionMatch[1] : 'unknown';
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
body: `## 🚫 Release Proposal Cancelled\n\nThis release proposal for ${version} was closed without creating the tag.\n\nIf you want to proceed with this release later, you can create a new release proposal.`
});
}
// Add cancelled label
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
labels: ['cancelled']
});
// Remove other workflow labels if present
const labelsToRemove = ['awaiting-approval', 'approved', 'release-in-progress'];
for (const label of labelsToRemove) {
try {
await github.rest.issues.removeLabel({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
name: label
});
} catch (e) {
console.log(`Label ${label} not found or already removed`);
}
}
console.log('Added cancelled label and cleaned up workflow labels');
+18 -16
View File
@@ -31,13 +31,13 @@ jobs:
- mac
- windows
go:
- '1.25'
- '1.26'
include:
# Set the minimum Go patch version for the given Go minor
# Usable via ${{ matrix.GO_SEMVER }}
- go: '1.25'
GO_SEMVER: '~1.25.0'
- go: '1.26'
GO_SEMVER: '~1.26.0'
# Set some variables per OS, usable via ${{ matrix.VAR }}
# OS_LABEL: the VM label from GitHub Actions (see https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#standard-github-hosted-runners-for-public-repositories)
@@ -65,15 +65,15 @@ jobs:
actions: write # to allow uploading artifacts and cache
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
uses: step-security/harden-runner@a90bcbc6539c36a85cdfeb73f7e2f433735f215b # v2.15.0
with:
egress-policy: audit
- name: Checkout code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Install Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
with:
go-version: ${{ matrix.GO_SEMVER }}
check-latest: true
@@ -120,7 +120,7 @@ jobs:
./caddy stop
- name: Publish Build Artifact
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: caddy_${{ runner.os }}_go${{ matrix.go }}_${{ steps.vars.outputs.short_sha }}
path: ${{ matrix.CADDY_BIN_PATH }}
@@ -132,6 +132,8 @@ jobs:
- name: Run tests
# id: step_test
# continue-on-error: true
env:
GODEBUG: http2xconnect=1
run: |
# (go test -v -coverprofile=cover-profile.out -race ./... 2>&1) > test-results/test-result.out
go test -v -coverprofile="cover-profile.out" -short -race ./...
@@ -162,13 +164,13 @@ jobs:
continue-on-error: true # August 2020: s390x VM is down due to weather and power issues
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
uses: step-security/harden-runner@a90bcbc6539c36a85cdfeb73f7e2f433735f215b # v2.15.0
with:
egress-policy: audit
allowed-endpoints: ci-s390x.caddyserver.com:22
- name: Checkout code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Run Tests
run: |
set +e
@@ -191,7 +193,7 @@ jobs:
retries=3
exit_code=0
while ((retries > 0)); do
CGO_ENABLED=0 go test -p 1 -v ./...
GODEBUG=http2xconnect=1 CGO_ENABLED=0 go test -p 1 -v ./...
exit_code=$?
if ((exit_code == 0)); then
break
@@ -221,27 +223,27 @@ jobs:
if: github.event.pull_request.head.repo.full_name == 'caddyserver/caddy' && github.actor != 'dependabot[bot]'
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
uses: step-security/harden-runner@a90bcbc6539c36a85cdfeb73f7e2f433735f215b # v2.15.0
with:
egress-policy: audit
- name: Checkout code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: goreleaser/goreleaser-action@9c156ee8a17a598857849441385a2041ef570552 # v6.3.0
- uses: goreleaser/goreleaser-action@ec59f474b9834571250b370d4735c50f8e2d1e29 # v7.0.0
with:
version: latest
args: check
- name: Install Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
with:
go-version: "~1.25"
go-version: "~1.26"
check-latest: true
- name: Install xcaddy
run: |
go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest
xcaddy version
- uses: goreleaser/goreleaser-action@9c156ee8a17a598857849441385a2041ef570552 # v6.3.0
- uses: goreleaser/goreleaser-action@ec59f474b9834571250b370d4735c50f8e2d1e29 # v7.0.0
with:
version: latest
args: build --single-target --snapshot
+6 -6
View File
@@ -36,13 +36,13 @@ jobs:
- 'darwin'
- 'netbsd'
go:
- '1.25'
- '1.26'
include:
# Set the minimum Go patch version for the given Go minor
# Usable via ${{ matrix.GO_SEMVER }}
- go: '1.25'
GO_SEMVER: '~1.25.0'
- go: '1.26'
GO_SEMVER: '~1.26.0'
runs-on: ubuntu-latest
permissions:
@@ -51,15 +51,15 @@ jobs:
continue-on-error: true
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
uses: step-security/harden-runner@a90bcbc6539c36a85cdfeb73f7e2f433735f215b # v2.15.0
with:
egress-policy: audit
- name: Checkout code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Install Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
with:
go-version: ${{ matrix.GO_SEMVER }}
check-latest: true
+10 -10
View File
@@ -45,18 +45,18 @@ jobs:
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
uses: step-security/harden-runner@a90bcbc6539c36a85cdfeb73f7e2f433735f215b # v2.15.0
with:
egress-policy: audit
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
with:
go-version: '~1.25'
go-version: '~1.26'
check-latest: true
- name: golangci-lint
uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 # v8.0.0
uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9.2.0
with:
version: latest
@@ -73,14 +73,14 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
uses: step-security/harden-runner@a90bcbc6539c36a85cdfeb73f7e2f433735f215b # v2.15.0
with:
egress-policy: audit
- name: govulncheck
uses: golang/govulncheck-action@b625fbe08f3bccbe446d94fbf87fcc875a4f50ee # v1.0.4
with:
go-version-input: '~1.25.0'
go-version-input: '~1.26.0'
check-latest: true
dependency-review:
@@ -90,14 +90,14 @@ jobs:
pull-requests: write
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
uses: step-security/harden-runner@a90bcbc6539c36a85cdfeb73f7e2f433735f215b # v2.15.0
with:
egress-policy: audit
- name: 'Checkout Repository'
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: 'Dependency Review'
uses: actions/dependency-review-action@da24556b548a50705dd671f47852072ea4c105d9 # v4.7.1
uses: actions/dependency-review-action@05fe4576374b728f0c523d6a13d64c25081e0803 # v4.8.3
with:
comment-summary-in-pr: on-failure
# https://github.com/actions/dependency-review-action/issues/430#issuecomment-1468975566
+249
View File
@@ -0,0 +1,249 @@
name: Release Proposal
# This workflow creates a release proposal as a PR that requires approval from maintainers
# Triggered manually by maintainers when ready to prepare a release
on:
workflow_dispatch:
inputs:
version:
description: 'Version to release (e.g., v2.8.0)'
required: true
type: string
commit_hash:
description: 'Commit hash to release from'
required: true
type: string
permissions:
contents: read
jobs:
create-proposal:
name: Create Release Proposal
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
issues: write
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@a90bcbc6539c36a85cdfeb73f7e2f433735f215b # v2.15.0
with:
egress-policy: audit
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
- name: Trim and validate inputs
id: inputs
run: |
# Trim whitespace from inputs
VERSION=$(echo "${{ inputs.version }}" | xargs)
COMMIT_HASH=$(echo "${{ inputs.commit_hash }}" | xargs)
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "commit_hash=$COMMIT_HASH" >> $GITHUB_OUTPUT
# Validate version format
if [[ ! "$VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$ ]]; then
echo "Error: Version must follow semver format (e.g., v2.8.0 or v2.8.0-beta.1)"
exit 1
fi
# Validate commit hash format
if [[ ! "$COMMIT_HASH" =~ ^[a-f0-9]{7,40}$ ]]; then
echo "Error: Commit hash must be a valid SHA (7-40 characters)"
exit 1
fi
# Check if commit exists
if ! git cat-file -e "$COMMIT_HASH"; then
echo "Error: Commit $COMMIT_HASH does not exist"
exit 1
fi
- name: Check if tag already exists
run: |
if git rev-parse "${{ steps.inputs.outputs.version }}" >/dev/null 2>&1; then
echo "Error: Tag ${{ steps.inputs.outputs.version }} already exists"
exit 1
fi
- name: Check for existing proposal PR
id: check_existing
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
script: |
const version = '${{ steps.inputs.outputs.version }}';
// Search for existing open PRs with release-proposal label that match this version
const openPRs = await github.rest.pulls.list({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open',
sort: 'updated',
direction: 'desc'
});
const existingOpenPR = openPRs.data.find(pr =>
pr.title.includes(version) &&
pr.labels.some(label => label.name === 'release-proposal')
);
if (existingOpenPR) {
const hasReleased = existingOpenPR.labels.some(label => label.name === 'released');
const hasReleaseInProgress = existingOpenPR.labels.some(label => label.name === 'release-in-progress');
if (hasReleased || hasReleaseInProgress) {
core.setFailed(`A release for ${version} is already in progress or completed: ${existingOpenPR.html_url}`);
} else {
core.setFailed(`An open release proposal already exists for ${version}: ${existingOpenPR.html_url}\n\nPlease use the existing PR or close it first.`);
}
return;
}
// Check for closed PRs with this version that were cancelled
const closedPRs = await github.rest.pulls.list({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'closed',
sort: 'updated',
direction: 'desc'
});
const cancelledPR = closedPRs.data.find(pr =>
pr.title.includes(version) &&
pr.labels.some(label => label.name === 'release-proposal') &&
pr.labels.some(label => label.name === 'cancelled')
);
if (cancelledPR) {
console.log(`Found previously cancelled proposal for ${version}: ${cancelledPR.html_url}`);
console.log('Creating new proposal to replace cancelled one...');
} else {
console.log(`No existing proposal found for ${version}, proceeding...`);
}
- name: Generate changelog and create branch
id: setup
run: |
VERSION="${{ steps.inputs.outputs.version }}"
COMMIT_HASH="${{ steps.inputs.outputs.commit_hash }}"
# Create a new branch for the release proposal
BRANCH_NAME="release_proposal-$VERSION"
git checkout -b "$BRANCH_NAME"
# Calculate how many commits behind HEAD
COMMITS_BEHIND=$(git rev-list --count ${COMMIT_HASH}..HEAD)
if [ "$COMMITS_BEHIND" -eq 0 ]; then
BEHIND_INFO="This is the latest commit (HEAD)"
else
BEHIND_INFO="This commit is **${COMMITS_BEHIND} commits behind HEAD**"
fi
echo "commits_behind=$COMMITS_BEHIND" >> $GITHUB_OUTPUT
echo "behind_info=$BEHIND_INFO" >> $GITHUB_OUTPUT
# Get the last tag
LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
if [ -z "$LAST_TAG" ]; then
echo "No previous tag found, generating full changelog"
COMMITS=$(git log --pretty=format:"- %s (%h)" --reverse "$COMMIT_HASH")
else
echo "Generating changelog since $LAST_TAG"
COMMITS=$(git log --pretty=format:"- %s (%h)" --reverse "${LAST_TAG}..$COMMIT_HASH")
fi
# Store changelog for PR body
CLEANSED_COMMITS=$(echo "$COMMITS" | sed 's/`/\\`/g')
echo "changelog<<EOF" >> $GITHUB_OUTPUT
echo "$CLEANSED_COMMITS" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
# Create empty commit for the PR
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git commit --allow-empty -m "Release proposal for $VERSION"
# Push the branch
git push origin "$BRANCH_NAME"
echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT
- name: Create release proposal PR
id: create_pr
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
script: |
const changelog = `${{ steps.setup.outputs.changelog }}`;
const pr = await github.rest.pulls.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: `Release Proposal: ${{ steps.inputs.outputs.version }}`,
head: '${{ steps.setup.outputs.branch_name }}',
base: 'master',
body: `## Release Proposal: ${{ steps.inputs.outputs.version }}
**Target Commit:** \`${{ steps.inputs.outputs.commit_hash }}\`
**Requested by:** @${{ github.actor }}
**Commit Status:** ${{ steps.setup.outputs.behind_info }}
This PR proposes creating release tag \`${{ steps.inputs.outputs.version }}\` at commit \`${{ steps.inputs.outputs.commit_hash }}\`.
### Approval Process
This PR requires **approval from 2+ maintainers** before the tag can be created.
### What happens next?
1. Maintainers review this proposal
2. When 2+ maintainer approvals are received, an automated workflow will post tagging instructions
3. A maintainer manually creates and pushes the signed tag
4. The release workflow is triggered automatically by the tag push
5. Upon release completion, this PR is closed and the branch is deleted
### Changes Since Last Release
${changelog}
### Release Checklist
- [ ] All tests pass
- [ ] Security review completed
- [ ] Documentation updated
- [ ] Breaking changes documented
---
**Note:** Tag creation is manual and requires a signed tag from a maintainer.`,
draft: true
});
// Add labels
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.data.number,
labels: ['release-proposal', 'awaiting-approval']
});
console.log(`Created PR: ${pr.data.html_url}`);
return { number: pr.data.number, url: pr.data.html_url };
result-encoding: json
- name: Post summary
run: |
echo "## Release Proposal PR Created! 🚀" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Version: **${{ steps.inputs.outputs.version }}**" >> $GITHUB_STEP_SUMMARY
echo "Commit: **${{ steps.inputs.outputs.commit_hash }}**" >> $GITHUB_STEP_SUMMARY
echo "Status: ${{ steps.setup.outputs.behind_info }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "PR: ${{ fromJson(steps.create_pr.outputs.result).url }}" >> $GITHUB_STEP_SUMMARY
+395 -20
View File
@@ -13,20 +13,334 @@ permissions:
contents: read
jobs:
verify-tag:
name: Verify Tag Signature and Approvals
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
issues: write
outputs:
verification_passed: ${{ steps.verify.outputs.passed }}
tag_version: ${{ steps.info.outputs.version }}
proposal_issue_number: ${{ steps.find_proposal.outputs.result && fromJson(steps.find_proposal.outputs.result).number || '' }}
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
# Force fetch upstream tags -- because 65 minutes
# tl;dr: actions/checkout@v3 runs this line:
# git -c protocol.version=2 fetch --no-tags --prune --progress --no-recurse-submodules --depth=1 origin +ebc278ec98bb24f2852b61fde2a9bf2e3d83818b:refs/tags/
# which makes its own local lightweight tag, losing all the annotations in the process. Our earlier script ran:
# git fetch --prune --unshallow
# which doesn't overwrite that tag because that would be destructive.
# Credit to @francislavoie for the investigation.
# https://github.com/actions/checkout/issues/290#issuecomment-680260080
- name: Force fetch upstream tags
run: git fetch --tags --force
- name: Get tag info
id: info
run: |
echo "version=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
echo "sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
# https://github.community/t5/GitHub-Actions/How-to-get-just-the-tag-name/m-p/32167/highlight/true#M1027
- name: Print Go version and environment
id: vars
run: |
printf "Using go at: $(which go)\n"
printf "Go version: $(go version)\n"
printf "\n\nGo environment:\n\n"
go env
printf "\n\nSystem environment:\n\n"
env
echo "version_tag=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_OUTPUT
echo "short_sha=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
# Add "pip install" CLI tools to PATH
echo ~/.local/bin >> $GITHUB_PATH
# Parse semver
TAG=${GITHUB_REF/refs\/tags\//}
SEMVER_RE='[^0-9]*\([0-9]*\)[.]\([0-9]*\)[.]\([0-9]*\)\([0-9A-Za-z\.-]*\)'
TAG_MAJOR=`echo ${TAG#v} | sed -e "s#$SEMVER_RE#\1#"`
TAG_MINOR=`echo ${TAG#v} | sed -e "s#$SEMVER_RE#\2#"`
TAG_PATCH=`echo ${TAG#v} | sed -e "s#$SEMVER_RE#\3#"`
TAG_SPECIAL=`echo ${TAG#v} | sed -e "s#$SEMVER_RE#\4#"`
echo "tag_major=${TAG_MAJOR}" >> $GITHUB_OUTPUT
echo "tag_minor=${TAG_MINOR}" >> $GITHUB_OUTPUT
echo "tag_patch=${TAG_PATCH}" >> $GITHUB_OUTPUT
echo "tag_special=${TAG_SPECIAL}" >> $GITHUB_OUTPUT
- name: Validate commits and tag signatures
id: verify
env:
signing_keys: ${{ secrets.SIGNING_KEYS }}
run: |
# Read the string into an array, splitting by IFS
IFS=";" read -ra keys_collection <<< "$signing_keys"
# ref: https://docs.github.com/en/actions/reference/workflows-and-actions/contexts#example-usage-of-the-runner-context
touch "${{ runner.temp }}/allowed_signers"
# Iterate and print the split elements
for item in "${keys_collection[@]}"; do
# trim leading whitespaces
item="${item##*( )}"
# trim trailing whitespaces
item="${item%%*( )}"
IFS=" " read -ra key_components <<< "$item"
# git wants it in format: email address, type, public key
# ssh has it in format: type, public key, email address
echo "${key_components[2]} namespaces=\"git\" ${key_components[0]} ${key_components[1]}" >> "${{ runner.temp }}/allowed_signers"
done
git config set --global gpg.ssh.allowedSignersFile "${{ runner.temp }}/allowed_signers"
echo "Verifying the tag: ${{ steps.vars.outputs.version_tag }}"
# Verify the tag is signed
if ! git verify-tag -v "${{ steps.vars.outputs.version_tag }}" 2>&1; then
echo "❌ Tag verification failed!"
echo "passed=false" >> $GITHUB_OUTPUT
git push --delete origin "${{ steps.vars.outputs.version_tag }}"
exit 1
fi
# Run it again to capture the output
git verify-tag -v "${{ steps.vars.outputs.version_tag }}" 2>&1 | tee /tmp/verify-output.txt;
# SSH verification output typically includes the key fingerprint
# Use GNU grep with Perl regex for cleaner extraction (Linux environment)
KEY_SHA256=$(grep -oP "SHA256:[\"']?\K[A-Za-z0-9+/=]+(?=[\"']?)" /tmp/verify-output.txt | head -1 || echo "")
if [ -z "$KEY_SHA256" ]; then
# Try alternative pattern with "key" prefix
KEY_SHA256=$(grep -oP "key SHA256:[\"']?\K[A-Za-z0-9+/=]+(?=[\"']?)" /tmp/verify-output.txt | head -1 || echo "")
fi
if [ -z "$KEY_SHA256" ]; then
# Fallback: extract any base64-like string (40+ chars)
KEY_SHA256=$(grep -oP '[A-Za-z0-9+/]{40,}=?' /tmp/verify-output.txt | head -1 || echo "")
fi
if [ -z "$KEY_SHA256" ]; then
echo "Somehow could not extract SSH key fingerprint from git verify-tag output"
echo "Cancelling flow and deleting tag"
echo "passed=false" >> $GITHUB_OUTPUT
git push --delete origin "${{ steps.vars.outputs.version_tag }}"
exit 1
fi
echo "✅ Tag verification succeeded!"
echo "passed=true" >> $GITHUB_OUTPUT
echo "key_id=$KEY_SHA256" >> $GITHUB_OUTPUT
- name: Find related release proposal
id: find_proposal
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
script: |
const version = '${{ steps.vars.outputs.version_tag }}';
// Search for PRs with release-proposal label that match this version
const prs = await github.rest.pulls.list({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open', // Changed to 'all' to find both open and closed PRs
sort: 'updated',
direction: 'desc'
});
// Find the most recent PR for this version
const proposal = prs.data.find(pr =>
pr.title.includes(version) &&
pr.labels.some(label => label.name === 'release-proposal')
);
if (!proposal) {
console.log(`⚠️ No release proposal PR found for ${version}`);
console.log('This might be a hotfix or emergency release');
return { number: null, approved: true, approvals: 0, proposedCommit: null };
}
console.log(`Found proposal PR #${proposal.number} for version ${version}`);
// Extract commit hash from PR body
const commitMatch = proposal.body.match(/\*\*Target Commit:\*\*\s*`([a-f0-9]+)`/);
const proposedCommit = commitMatch ? commitMatch[1] : null;
if (proposedCommit) {
console.log(`Proposal was for commit: ${proposedCommit}`);
} else {
console.log('⚠️ No target commit hash found in PR body');
}
// Get PR reviews to extract approvers
let approvers = 'Validated by automation';
let approvalCount = 2; // Minimum required
try {
const reviews = await github.rest.pulls.listReviews({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: proposal.number
});
// Get latest review per user and filter for approvals
const latestReviewsByUser = {};
reviews.data.forEach(review => {
const username = review.user.login;
if (!latestReviewsByUser[username] || new Date(review.submitted_at) > new Date(latestReviewsByUser[username].submitted_at)) {
latestReviewsByUser[username] = review;
}
});
const approvalReviews = Object.values(latestReviewsByUser).filter(review =>
review.state === 'APPROVED'
);
if (approvalReviews.length > 0) {
approvers = approvalReviews.map(r => '@' + r.user.login).join(', ');
approvalCount = approvalReviews.length;
console.log(`Found ${approvalCount} approvals from: ${approvers}`);
}
} catch (error) {
console.log(`Could not fetch reviews: ${error.message}`);
}
return {
number: proposal.number,
approved: true,
approvals: approvalCount,
approvers: approvers,
proposedCommit: proposedCommit
};
result-encoding: json
- name: Verify proposal commit
run: |
APPROVALS='${{ steps.find_proposal.outputs.result }}'
# Parse JSON
PROPOSED_COMMIT=$(echo "$APPROVALS" | jq -r '.proposedCommit')
CURRENT_COMMIT="${{ steps.info.outputs.sha }}"
echo "Proposed commit: $PROPOSED_COMMIT"
echo "Current commit: $CURRENT_COMMIT"
# Check if commits match (if proposal had a target commit)
if [ "$PROPOSED_COMMIT" != "null" ] && [ -n "$PROPOSED_COMMIT" ]; then
# Normalize both commits to full SHA for comparison
PROPOSED_FULL=$(git rev-parse "$PROPOSED_COMMIT" 2>/dev/null || echo "")
CURRENT_FULL=$(git rev-parse "$CURRENT_COMMIT" 2>/dev/null || echo "")
if [ -z "$PROPOSED_FULL" ]; then
echo "⚠️ Could not resolve proposed commit: $PROPOSED_COMMIT"
elif [ "$PROPOSED_FULL" != "$CURRENT_FULL" ]; then
echo "❌ Commit mismatch!"
echo "The tag points to commit $CURRENT_FULL but the proposal was for $PROPOSED_FULL"
echo "This indicates an error in tag creation."
# Delete the tag remotely
git push --delete origin "${{ steps.vars.outputs.version_tag }}"
echo "Tag ${{steps.vars.outputs.version_tag}} has been deleted"
exit 1
else
echo "✅ Commit hash matches proposal"
fi
else
echo "⚠️ No target commit found in proposal (might be legacy release)"
fi
echo "✅ Tag verification completed"
- name: Update release proposal PR
if: fromJson(steps.find_proposal.outputs.result).number != null
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
script: |
const result = ${{ steps.find_proposal.outputs.result }};
if (result.number) {
// Add in-progress label
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: result.number,
labels: ['release-in-progress']
});
// Remove approved label if present
try {
await github.rest.issues.removeLabel({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: result.number,
name: 'approved'
});
} catch (e) {
console.log('Approved label not found:', e.message);
}
const commentBody = [
'## 🚀 Release Workflow Started',
'',
'- **Tag:** ${{ steps.info.outputs.version }}',
'- **Signed by key:** ${{ steps.verify.outputs.key_id }}',
'- **Commit:** ${{ steps.info.outputs.sha }}',
'- **Approved by:** ' + result.approvers,
'',
'Release workflow is now running. This PR will be updated when the release is published.'
].join('\n');
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: result.number,
body: commentBody
});
}
- name: Summary
run: |
APPROVALS='${{ steps.find_proposal.outputs.result }}'
PROPOSED_COMMIT=$(echo "$APPROVALS" | jq -r '.proposedCommit // "N/A"')
APPROVERS=$(echo "$APPROVALS" | jq -r '.approvers // "N/A"')
echo "## Tag Verification Summary 🔐" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- **Tag:** ${{ steps.info.outputs.version }}" >> $GITHUB_STEP_SUMMARY
echo "- **Commit:** ${{ steps.info.outputs.sha }}" >> $GITHUB_STEP_SUMMARY
echo "- **Proposed Commit:** $PROPOSED_COMMIT" >> $GITHUB_STEP_SUMMARY
echo "- **Signature:** ✅ Verified" >> $GITHUB_STEP_SUMMARY
echo "- **Signed by:** ${{ steps.verify.outputs.key_id }}" >> $GITHUB_STEP_SUMMARY
echo "- **Approvals:** ✅ Sufficient" >> $GITHUB_STEP_SUMMARY
echo "- **Approved by:** $APPROVERS" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Proceeding with release build..." >> $GITHUB_STEP_SUMMARY
release:
name: Release
needs: verify-tag
if: ${{ needs.verify-tag.outputs.verification_passed == 'true' }}
strategy:
matrix:
os:
- ubuntu-latest
go:
- '1.25'
- '1.26'
include:
# Set the minimum Go patch version for the given Go minor
# Usable via ${{ matrix.GO_SEMVER }}
- go: '1.25'
GO_SEMVER: '~1.25.0'
- go: '1.26'
GO_SEMVER: '~1.26.0'
runs-on: ${{ matrix.os }}
# https://github.com/sigstore/cosign/issues/1258#issuecomment-1002251233
@@ -36,26 +350,28 @@ jobs:
# https://docs.github.com/en/rest/overview/permissions-required-for-github-apps#permission-on-contents
# "Releases" is part of `contents`, so it needs the `write`
contents: write
issues: write
pull-requests: write
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
uses: step-security/harden-runner@a90bcbc6539c36a85cdfeb73f7e2f433735f215b # v2.15.0
with:
egress-policy: audit
- name: Checkout code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
- name: Install Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
with:
go-version: ${{ matrix.GO_SEMVER }}
check-latest: true
# Force fetch upstream tags -- because 65 minutes
# tl;dr: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 runs this line:
# tl;dr: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.2.2 runs this line:
# git -c protocol.version=2 fetch --no-tags --prune --progress --no-recurse-submodules --depth=1 origin +ebc278ec98bb24f2852b61fde2a9bf2e3d83818b:refs/tags/
# which makes its own local lightweight tag, losing all the annotations in the process. Our earlier script ran:
# git fetch --prune --unshallow
@@ -98,22 +414,12 @@ jobs:
- name: Install Cloudsmith CLI
run: pip install --upgrade cloudsmith-cli
- name: Validate commits and tag signatures
run: |
# Import Matt Holt's key
curl 'https://github.com/mholt.gpg' | gpg --import
echo "Verifying the tag: ${{ steps.vars.outputs.version_tag }}"
# tags are only accepted if signed by Matt's key
git verify-tag "${{ steps.vars.outputs.version_tag }}" || exit 1
- name: Install Cosign
uses: sigstore/cosign-installer@d58896d6a1865668819e1d91763c7751a165e159 # main
uses: sigstore/cosign-installer@d7543c93d881b35a8faa02e8e3605f69b7a1ce62 # main
- name: Cosign version
run: cosign version
- name: Install Syft
uses: anchore/sbom-action/download-syft@7b36ad622f042cab6f59a75c2ac24ccb256e9b45 # main
uses: anchore/sbom-action/download-syft@17ae1740179002c89186b61233e0f892c3118b11 # main
- name: Syft version
run: syft version
- name: Install xcaddy
@@ -122,7 +428,7 @@ jobs:
xcaddy version
# GoReleaser will take care of publishing those artifacts into the release
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@9c156ee8a17a598857849441385a2041ef570552 # v6.3.0
uses: goreleaser/goreleaser-action@ec59f474b9834571250b370d4735c50f8e2d1e29 # v7.0.0
with:
version: latest
args: release --clean --timeout 60m
@@ -188,3 +494,72 @@ jobs:
echo "Pushing $filename to 'testing'"
cloudsmith push deb caddy/testing/any-distro/any-version $filename
done
- name: Update release proposal PR
if: needs.verify-tag.outputs.proposal_issue_number != ''
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
script: |
const prNumber = parseInt('${{ needs.verify-tag.outputs.proposal_issue_number }}');
if (prNumber) {
// Get PR details to find the branch
const pr = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: prNumber
});
const branchName = pr.data.head.ref;
// Remove in-progress label
try {
await github.rest.issues.removeLabel({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
name: 'release-in-progress'
});
} catch (e) {
console.log('Label not found:', e.message);
}
// Add released label
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
labels: ['released']
});
// Add final comment
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
body: '## ✅ Release Published\n\nThe release has been successfully published and is now available.'
});
// Close the PR if it's still open
if (pr.data.state === 'open') {
await github.rest.pulls.update({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: prNumber,
state: 'closed'
});
console.log(`Closed PR #${prNumber}`);
}
// Delete the branch
try {
await github.rest.git.deleteRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: `heads/${branchName}`
});
console.log(`Deleted branch: ${branchName}`);
} catch (e) {
console.log(`Could not delete branch ${branchName}: ${e.message}`);
}
}
+3 -3
View File
@@ -24,12 +24,12 @@ jobs:
# See https://github.com/peter-evans/repository-dispatch
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
uses: step-security/harden-runner@a90bcbc6539c36a85cdfeb73f7e2f433735f215b # v2.15.0
with:
egress-policy: audit
- name: Trigger event on caddyserver/dist
uses: peter-evans/repository-dispatch@ff45666b9427631e3450c54a1bcbee4d9ff4d7c0 # v3.0.0
uses: peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697 # v4.0.1
with:
token: ${{ secrets.REPO_DISPATCH_TOKEN }}
repository: caddyserver/dist
@@ -37,7 +37,7 @@ jobs:
client-payload: '{"tag": "${{ github.event.release.tag_name }}"}'
- name: Trigger event on caddyserver/caddy-docker
uses: peter-evans/repository-dispatch@ff45666b9427631e3450c54a1bcbee4d9ff4d7c0 # v3.0.0
uses: peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697 # v4.0.1
with:
token: ${{ secrets.REPO_DISPATCH_TOKEN }}
repository: caddyserver/caddy-docker
+5 -5
View File
@@ -37,17 +37,17 @@ jobs:
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
uses: step-security/harden-runner@a90bcbc6539c36a85cdfeb73f7e2f433735f215b # v2.15.0
with:
egress-policy: audit
- name: "Checkout code"
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: "Run analysis"
uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde # v2.4.2
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
with:
results_file: results.sarif
results_format: sarif
@@ -72,7 +72,7 @@ jobs:
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
# format to the repository Actions tab.
- name: "Upload artifact"
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: SARIF file
path: results.sarif
@@ -81,6 +81,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard (optional).
# Commenting out will disable upload of results to your repo's Code Scanning dashboard
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@51f77329afa6477de8c49fc9c7046c15b9a4e79d # v3.29.5
uses: github/codeql-action/upload-sarif@89a39a4e59826350b863aa6b6252a07ad50cf83e # v3.29.5
with:
sarif_file: results.sarif
+1
View File
@@ -32,6 +32,7 @@ linters:
- importas
- ineffassign
- misspell
- modernize
- prealloc
- promlinter
- sloglint
+3 -1
View File
@@ -13,7 +13,7 @@ before:
- cp cmd/caddy/main.go caddy-build/main.go
- /bin/sh -c 'cd ./caddy-build && go mod init caddy'
# prepare syso files for windows embedding
- /bin/sh -c 'for a in amd64 arm arm64; do XCADDY_SKIP_BUILD=1 GOOS=windows GOARCH=$a xcaddy build {{.Env.TAG}}; done'
- /bin/sh -c 'for a in amd64 arm64; do XCADDY_SKIP_BUILD=1 GOOS=windows GOARCH=$a xcaddy build {{.Env.TAG}}; done'
- /bin/sh -c 'mv /tmp/buildenv_*/*.syso caddy-build'
# GoReleaser doesn't seem to offer {{.Tag}} at this stage, so we have to embed it into the env
# so we run: TAG=$(git describe --abbrev=0) goreleaser release --rm-dist --skip-publish --skip-validate
@@ -67,6 +67,8 @@ builds:
goarch: s390x
- goos: windows
goarch: riscv64
- goos: windows
goarch: arm
- goos: freebsd
goarch: ppc64le
- goos: freebsd
+217
View File
@@ -0,0 +1,217 @@
# Caddy Project Guidelines
## Mission
**Every site on HTTPS.** Caddy is a security-first, modular, extensible server platform.
## Code Style
### Go Idioms
Follow [Go Code Review Comments](https://go.dev/wiki/CodeReviewComments):
- **Error flow**: Early return, indent error handling—not else blocks
```go
if err != nil {
return err
}
// normal code
```
- **Naming**: initialisms (`URL`, `HTTP`, `ID`—not `Url`, `Http`, `Id`)
- **Receiver names**: 12 letters reflecting type (`c` for `Client`, `h` for `Handler`)
- **Error strings**: Lowercase, no trailing punctuation (`"something failed"` not `"Something failed."`)
- **Doc comments**: Full sentences starting with the name being documented
```go
// Handler serves HTTP requests for the file server.
type Handler struct { ... }
```
- **Empty slices**: `var t []string` (nil slice), not `t := []string{}` (non-nil zero-length)
- **Don't panic**: Use error returns for normal error handling
### Caddy Patterns
**Module registration**:
```go
func init() {
caddy.RegisterModule(MyModule{})
}
func (MyModule) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
ID: "namespace.category.name",
New: func() caddy.Module { return new(MyModule) },
}
}
```
**Module lifecycle**: `New()` → JSON unmarshal → `Provision()` → `Validate()` → use → `Cleanup()`
**Interface guards** — compile-time verification that modules implement required interfaces:
```go
var (
_ caddy.Provisioner = (*MyModule)(nil)
_ caddy.Validator = (*MyModule)(nil)
_ caddyfile.Unmarshaler = (*MyModule)(nil)
)
```
**Structured logging** — use the module-scoped logger from context:
```go
func (m *MyModule) Provision(ctx caddy.Context) error {
m.logger = ctx.Logger()
m.logger.Debug("provisioning", zap.String("field", m.Field))
return nil
}
```
**Caddyfile support** — implement `UnmarshalCaddyfile(*caddyfile.Dispenser)` using the `Dispenser` API:
```go
// UnmarshalCaddyfile sets up the module from Caddyfile tokens. Syntax:
//
// directive [arg1] [arg2] {
// subdir value
// }
func (m *MyModule) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
d.Next() // consume directive name
for d.NextArg() {
// handle inline arguments
}
for nesting := d.Nesting(); d.NextBlock(nesting); {
switch d.Val() {
case "subdir":
if !d.NextArg() {
return d.ArgErr()
}
m.Field = d.Val()
default:
return d.Errf("unrecognized subdirective: %s", d.Val())
}
}
return nil
}
```
**Admin API**: Implement `caddy.AdminRouter` for custom endpoints.
**Context**: Use `caddy.Context` for accessing other apps/modules and logging—don't store contexts in structs.
## Architecture
Caddy is built around a **module system** where everything is a module registered via `caddy.RegisterModule()`:
- **Apps** (`caddy.App`): Top-level modules like `http`, `tls`, `pki` that Caddy loads and runs
- **Modules** (`caddy.Module`): Extensible components with namespaced IDs (e.g., `http.handlers.file_server`)
- **Configuration**: Native JSON with adapters (Caddyfile → JSON via `caddyconfig/httpcaddyfile`)
| Directory | Purpose |
|-----------|---------|
| `modules/` | All standard modules (HTTP, TLS, PKI, etc.) |
| `modules/standard/imports.go` | Standard module registry |
| `caddyconfig/httpcaddyfile/` | Caddyfile → JSON adapter for HTTP |
| `caddytest/` | Test utilities and integration tests |
| `cmd/caddy/` | CLI entry point with module imports |
### Critical Packages
`caddyhttp` and `caddytls` require **extra scrutiny** in code review—these are security-critical.
## Quality Gates
**All required before PR is merge-ready:**
| Gate | Command | Notes |
|------|---------|-------|
| Tests pass | `go test -race -short ./...` | Race detection enabled |
| Lint clean | `golangci-lint run --timeout 10m` | No warnings in changed files |
| Builds | `go build ./...` | Must compile |
| Benchmarks | `go test -bench=. -benchmem` | Required for optimizations |
CI runs tests on **Linux, macOS, and Windows**—ensure cross-platform compatibility.
### Build & Test
```bash
# Build
cd cmd/caddy && go build
# Tests with race detection (matches CI)
go test -race -short ./...
# Integration tests
go test ./caddytest/integration/...
# Lint (matches CI)
golangci-lint run --timeout 10m
```
## Testing Conventions
**Table-driven tests** (preferred pattern):
```go
func TestFeature(t *testing.T) {
for i, tc := range []struct {
input string
expected string
wantErr bool
}{
{input: "valid", expected: "result", wantErr: false},
{input: "invalid", expected: "", wantErr: true},
} {
actual, err := Function(tc.input)
if tc.wantErr && err == nil {
t.Errorf("Test %d: expected error but got none", i)
}
if !tc.wantErr && err != nil {
t.Errorf("Test %d: unexpected error: %v", i, err)
}
if actual != tc.expected {
t.Errorf("Test %d: expected %q, got %q", i, tc.expected, actual)
}
}
}
```
**Integration tests** use `caddytest.Tester`:
```go
func TestHTTPFeature(t *testing.T) {
tester := caddytest.NewTester(t)
tester.InitServer(`
{
admin localhost:2999
http_port 9080
}
localhost:9080 {
respond "hello"
}`, "caddyfile")
tester.AssertGetResponse("http://localhost:9080/", 200, "hello")
}
```
Use non-standard ports (9080, 9443, 2999) to avoid conflicts with running servers.
## AI Contribution Policy
Per [CONTRIBUTING.md](.github/CONTRIBUTING.md), AI-assisted code **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
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.
## Dependencies
- **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`
## Further Reading
- [CONTRIBUTING.md](.github/CONTRIBUTING.md) — Full PR process and expectations
- [Extending Caddy](https://caddyserver.com/docs/extending-caddy) — Module development guide
- [JSON Config](https://caddyserver.com/docs/json/) — Native configuration reference
- [Caddyfile](https://caddyserver.com/docs/caddyfile/concepts) — Caddyfile syntax guide
+48 -25
View File
@@ -12,24 +12,52 @@
<hr>
<h3 align="center">Every site on HTTPS</h3>
<p align="center">Caddy is an extensible server platform that uses TLS by default.</p>
<p align="center">
<a href="https://github.com/caddyserver/caddy/actions/workflows/ci.yml"><img src="https://github.com/caddyserver/caddy/actions/workflows/ci.yml/badge.svg"></a>
<a href="https://www.bestpractices.dev/projects/7141"><img src="https://www.bestpractices.dev/projects/7141/badge"></a>
<a href="https://pkg.go.dev/github.com/caddyserver/caddy/v2"><img src="https://img.shields.io/badge/godoc-reference-%23007d9c.svg"></a>
<br>
<a href="https://x.com/caddyserver" title="@caddyserver on Twitter"><img src="https://img.shields.io/twitter/follow/caddyserver" alt="@caddyserver on Twitter"></a>
<a href="https://caddy.community" title="Caddy Forum"><img src="https://img.shields.io/badge/community-forum-ff69b4.svg" alt="Caddy Forum"></a>
<br>
<a href="https://sourcegraph.com/github.com/caddyserver/caddy?badge" title="Caddy on Sourcegraph"><img src="https://sourcegraph.com/github.com/caddyserver/caddy/-/badge.svg" alt="Caddy on Sourcegraph"></a>
<a href="https://cloudsmith.io/~caddy/repos/"><img src="https://img.shields.io/badge/OSS%20hosting%20by-cloudsmith-blue?logo=cloudsmith" alt="Cloudsmith"></a>
</p>
<p align="center">
<a href="https://github.com/caddyserver/caddy/releases">Releases</a> ·
<a href="https://caddyserver.com/docs/">Documentation</a> ·
<a href="https://caddy.community">Get Help</a>
</p>
<p align="center">
<a href="https://github.com/caddyserver/caddy/actions/workflows/ci.yml"><img src="https://github.com/caddyserver/caddy/actions/workflows/ci.yml/badge.svg"></a>
&nbsp;
<a href="https://www.bestpractices.dev/projects/7141"><img src="https://www.bestpractices.dev/projects/7141/badge"></a>
&nbsp;
<a href="https://pkg.go.dev/github.com/caddyserver/caddy/v2"><img src="https://img.shields.io/badge/godoc-reference-%23007d9c.svg"></a>
&nbsp;
<a href="https://x.com/caddyserver" title="@caddyserver on Twitter"><img src="https://img.shields.io/twitter/follow/caddyserver" alt="@caddyserver on Twitter"></a>
&nbsp;
<a href="https://caddy.community" title="Caddy Forum"><img src="https://img.shields.io/badge/community-forum-ff69b4.svg" alt="Caddy Forum"></a>
<br>
<a href="https://sourcegraph.com/github.com/caddyserver/caddy?badge" title="Caddy on Sourcegraph"><img src="https://sourcegraph.com/github.com/caddyserver/caddy/-/badge.svg" alt="Caddy on Sourcegraph"></a>
&nbsp;
<a href="https://cloudsmith.io/~caddy/repos/"><img src="https://img.shields.io/badge/OSS%20hosting%20by-cloudsmith-blue?logo=cloudsmith" alt="Cloudsmith"></a>
</p>
<p align="center">
<b>Powered by</b>
<br>
<a href="https://github.com/caddyserver/certmagic">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/55066419/206946718-740b6371-3df3-4d72-a822-47e4c48af999.png">
<source media="(prefers-color-scheme: light)" srcset="https://user-images.githubusercontent.com/1128849/49704830-49d37200-fbd5-11e8-8385-767e0cd033c3.png">
<img src="https://user-images.githubusercontent.com/1128849/49704830-49d37200-fbd5-11e8-8385-767e0cd033c3.png" alt="CertMagic" width="250">
</picture>
</a>
</p>
<!-- Warp sponsorship requests this section -->
<div align="center" markdown="1">
<hr>
<sup>Special thanks to:</sup>
<br>
<a href="https://go.warp.dev/caddy">
<img alt="Warp sponsorship" width="400" src="https://github.com/user-attachments/assets/c8efffde-18c7-4af4-83ed-b1aba2dda394">
</a>
### [Warp, built for coding with multiple AI agents](https://go.warp.dev/caddy)
[Available for MacOS, Linux, & Windows](https://go.warp.dev/caddy)<br>
</div>
<hr>
### Menu
@@ -44,18 +72,6 @@
- [Getting help](#getting-help)
- [About](#about)
<p align="center">
<b>Powered by</b>
<br>
<a href="https://github.com/caddyserver/certmagic">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/55066419/206946718-740b6371-3df3-4d72-a822-47e4c48af999.png">
<source media="(prefers-color-scheme: light)" srcset="https://user-images.githubusercontent.com/1128849/49704830-49d37200-fbd5-11e8-8385-767e0cd033c3.png">
<img src="https://user-images.githubusercontent.com/1128849/49704830-49d37200-fbd5-11e8-8385-767e0cd033c3.png" alt="CertMagic" width="250">
</picture>
</a>
</p>
## [Features](https://caddyserver.com/features)
@@ -117,11 +133,18 @@ username ALL=(ALL:ALL) NOPASSWD: /usr/sbin/setcap
replacing `username` with your actual username. Please be careful and only do this if you know what you are doing! We are only qualified to document how to use Caddy, not Go tooling or your computer, and we are providing these instructions for convenience only; please learn how to use your own computer at your own risk and make any needful adjustments.
Then you can run the tests in all modules or a specific one:
```bash
$ go test ./...
$ go test ./modules/caddyhttp/tracing/
```
### With version information and/or plugins
Using [our builder tool, `xcaddy`](https://github.com/caddyserver/xcaddy)...
```
```bash
$ xcaddy build
```
@@ -197,6 +220,6 @@ Matthew Holt began developing Caddy in 2014 while studying computer science at B
- _Project on X: [@caddyserver](https://x.com/caddyserver)_
- _Author on X: [@mholt6](https://x.com/mholt6)_
Caddy is a project of [ZeroSSL](https://zerossl.com), a Stack Holdings company.
Caddy is a project of [ZeroSSL](https://zerossl.com), an HID Global company.
Debian package repository hosting is graciously provided by [Cloudsmith](https://cloudsmith.com). Cloudsmith is the only fully hosted, cloud-native, universal package management solution, that enables your organization to create, store and share packages in any format, to any place, with total confidence.
+116 -49
View File
@@ -45,8 +45,16 @@ import (
"github.com/prometheus/client_golang/prometheus"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"github.com/caddyserver/caddy/v2/internal"
)
// testCertMagicStorageOverride is a package-level test hook. Tests may set
// this variable to provide a temporary certmagic.Storage so that cert
// management in tests does not hit the real default storage on disk.
// This must NOT be set in production code.
var testCertMagicStorageOverride certmagic.Storage
func init() {
// The hard-coded default `DefaultAdminListen` can be overridden
// by setting the `CADDY_ADMIN` environment variable.
@@ -112,10 +120,6 @@ type AdminConfig struct {
//
// EXPERIMENTAL: This feature is subject to change.
Remote *RemoteAdmin `json:"remote,omitempty"`
// Holds onto the routers so that we can later provision them
// if they require provisioning.
routers []AdminRouter
}
// ConfigSettings configures the management of configuration.
@@ -204,8 +208,8 @@ type AdminAccess struct {
// AdminPermissions specifies what kinds of requests are allowed
// to be made to the admin endpoint.
type AdminPermissions struct {
// The API paths allowed. Paths are simple prefix matches.
// Any subpath of the specified paths will be allowed.
// The API paths allowed. A request path must either equal an
// allowed path or be a subpath with a path-segment boundary.
Paths []string `json:"paths,omitempty"`
// The HTTP methods allowed for the given paths.
@@ -214,7 +218,7 @@ type AdminPermissions struct {
// newAdminHandler reads admin's config and returns an http.Handler suitable
// for use in an admin endpoint server, which will be listening on listenAddr.
func (admin *AdminConfig) newAdminHandler(addr NetworkAddress, remote bool, _ Context) adminHandler {
func (admin *AdminConfig) newAdminHandler(addr NetworkAddress, remote bool, ctx Context) (adminHandler, error) {
muxWrap := adminHandler{mux: http.NewServeMux()}
// secure the local or remote endpoint respectively
@@ -271,34 +275,21 @@ func (admin *AdminConfig) newAdminHandler(addr NetworkAddress, remote bool, _ Co
// register third-party module endpoints
for _, m := range GetModules("admin.api") {
router := m.New().(AdminRouter)
// provision the router before registering its routes, so
// handlers have access to all provisioned state
if provisioner, ok := router.(Provisioner); ok {
if err := provisioner.Provision(ctx); err != nil {
return adminHandler{}, fmt.Errorf("provisioning admin router module %s: %v", m.ID, err)
}
}
for _, route := range router.Routes() {
addRoute(route.Pattern, handlerLabel, route.Handler)
}
admin.routers = append(admin.routers, router)
}
return muxWrap
}
// provisionAdminRouters provisions all the router modules
// in the admin.api namespace that need provisioning.
func (admin *AdminConfig) provisionAdminRouters(ctx Context) error {
for _, router := range admin.routers {
provisioner, ok := router.(Provisioner)
if !ok {
continue
}
err := provisioner.Provision(ctx)
if err != nil {
return err
}
}
// We no longer need the routers once provisioned, allow for GC
admin.routers = nil
return nil
return muxWrap, nil
}
// allowedOrigins returns a list of origins that are allowed.
@@ -422,11 +413,7 @@ func replaceLocalAdminServer(cfg *Config, ctx Context) error {
return err
}
handler := cfg.Admin.newAdminHandler(addr, false, ctx)
// run the provisioners for loaded modules to make sure local
// state is properly re-initialized in the new admin server
err = cfg.Admin.provisionAdminRouters(ctx)
handler, err := cfg.Admin.newAdminHandler(addr, false, ctx)
if err != nil {
return err
}
@@ -550,11 +537,7 @@ func replaceRemoteAdminServer(ctx Context, cfg *Config) error {
// make the HTTP handler but disable Host/Origin enforcement
// because we are using TLS authentication instead
handler := cfg.Admin.newAdminHandler(addr, true, ctx)
// run the provisioners for loaded modules to make sure local
// state is properly re-initialized in the new admin server
err = cfg.Admin.provisionAdminRouters(ctx)
handler, err := cfg.Admin.newAdminHandler(addr, true, ctx)
if err != nil {
return err
}
@@ -633,8 +616,19 @@ func (ident *IdentityConfig) certmagicConfig(logger *zap.Logger, makeCache bool)
// certmagic config, although it'll be mostly useless for remote management
ident = new(IdentityConfig)
}
// Choose storage: prefer the package-level test override when present,
// otherwise use the configured DefaultStorage. Tests may set an override
// to divert storage into a temporary location. Otherwise, in production
// we use the DefaultStorage since we don't want to act as part of a
// cluster; this storage is for the server's local identity only.
var storage certmagic.Storage
if testCertMagicStorageOverride != nil {
storage = testCertMagicStorageOverride
} else {
storage = DefaultStorage
}
template := certmagic.Config{
Storage: DefaultStorage, // do not act as part of a cluster (this is for the server's local identity)
Storage: storage,
Logger: logger,
Issuers: ident.issuers,
}
@@ -699,7 +693,7 @@ func (remote RemoteAdmin) enforceAccessControls(r *http.Request) error {
// verify path
pathFound := accessPerm.Paths == nil
for _, allowedPath := range accessPerm.Paths {
if strings.HasPrefix(r.URL.Path, allowedPath) {
if adminPathAllowed(r.URL.Path, allowedPath) {
pathFound = true
break
}
@@ -728,14 +722,31 @@ func (remote RemoteAdmin) enforceAccessControls(r *http.Request) error {
}
}
func adminPathAllowed(reqPath, allowedPath string) bool {
if allowedPath == "" || allowedPath == "/" {
return strings.HasPrefix(reqPath, allowedPath)
}
if reqPath == allowedPath {
return true
}
if strings.HasSuffix(allowedPath, "/") {
return strings.HasPrefix(reqPath, allowedPath)
}
return strings.HasPrefix(reqPath, allowedPath+"/")
}
func stopAdminServer(srv *http.Server) error {
if srv == nil {
return fmt.Errorf("no admin server")
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
timeout := 10 * time.Second
ctx, cancel := context.WithTimeoutCause(context.Background(), timeout, fmt.Errorf("stopping admin server: %ds timeout", int(timeout.Seconds())))
defer cancel()
err := srv.Shutdown(ctx)
if err != nil {
if cause := context.Cause(ctx); cause != nil && errors.Is(err, context.DeadlineExceeded) {
err = cause
}
return fmt.Errorf("shutting down admin server: %v", err)
}
Log().Named("admin").Info("stopped previous server", zap.String("address", srv.Addr))
@@ -779,7 +790,7 @@ func (h adminHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
zap.String("uri", r.RequestURI),
zap.String("remote_ip", ip),
zap.String("remote_port", port),
zap.Reflect("headers", r.Header),
zap.Object("headers", internal.LoggableHTTPHeader{Header: r.Header}),
)
if r.TLS != nil {
log = log.With(
@@ -807,11 +818,37 @@ func (h adminHandler) serveHTTP(w http.ResponseWriter, r *http.Request) {
}
}
// common mitigations in browser contexts
if strings.Contains(r.Header.Get("Upgrade"), "websocket") {
// I've never been able demonstrate a vulnerability myself, but apparently
// WebSocket connections originating from browsers aren't subject to CORS
// restrictions, so we'll just be on the safe side
h.handleError(w, r, fmt.Errorf("websocket connections aren't allowed"))
h.handleError(w, r, APIError{
HTTPStatus: http.StatusBadRequest,
Err: errors.New("websocket connections aren't allowed"),
Message: "WebSocket connections aren't allowed.",
})
return
}
if strings.Contains(r.Header.Get("Sec-Fetch-Mode"), "no-cors") {
// turns out web pages can just disable the same-origin policy (!???!?)
// but at least browsers let us know that's the case, holy heck
h.handleError(w, r, APIError{
HTTPStatus: http.StatusBadRequest,
Err: errors.New("client attempted to make request by disabling same-origin policy using no-cors mode"),
Message: "Disabling same-origin restrictions is not allowed.",
})
return
}
if r.Header.Get("Origin") == "null" {
// bug in Firefox in certain cross-origin situations (yikes?)
// (not strictly a security vuln on its own, but it's red flaggy,
// since it seems to manifest in cross-origin contexts)
h.handleError(w, r, APIError{
HTTPStatus: http.StatusBadRequest,
Err: errors.New("invalid origin 'null'"),
Message: "Buggy browser is sending null Origin header.",
})
return
}
@@ -824,7 +861,9 @@ func (h adminHandler) serveHTTP(w http.ResponseWriter, r *http.Request) {
}
}
if h.enforceOrigin {
_, hasOriginHeader := r.Header["Origin"]
_, hasSecHeader := r.Header["Sec-Fetch-Mode"]
if h.enforceOrigin || hasOriginHeader || hasSecHeader {
// cross-site mitigation
origin, err := h.checkOrigin(r)
if err != nil {
@@ -1012,6 +1051,9 @@ func handleConfig(w http.ResponseWriter, r *http.Request) error {
buf.Reset()
defer bufPool.Put(buf)
const maxConfigSize = 100 * 1024 * 1024 // 100 MB
r.Body = http.MaxBytesReader(w, r.Body, maxConfigSize)
_, err := io.Copy(buf, r.Body)
if err != nil {
return APIError{
@@ -1029,6 +1071,13 @@ func handleConfig(w http.ResponseWriter, r *http.Request) error {
return err
}
// If this request changed the config, clear the last
// config info we have stored, if it is different from
// the original source.
ClearLastConfigIfDifferent(
r.Header.Get("Caddy-Config-Source-File"),
r.Header.Get("Caddy-Config-Source-Adapter"))
default:
return APIError{
HTTPStatus: http.StatusMethodNotAllowed,
@@ -1087,6 +1136,20 @@ func handleStop(w http.ResponseWriter, r *http.Request) error {
return nil
}
func parseCanonicalArrayIndex(idx string) (int, error) {
if idx == "" {
return 0, fmt.Errorf("empty index")
}
i, err := strconv.Atoi(idx)
if err != nil {
return 0, err
}
if strconv.Itoa(i) != idx {
return 0, fmt.Errorf("non-canonical array index")
}
return i, nil
}
// unsyncedConfigAccess traverses into the current config and performs
// the operation at path according to method, using body and out as
// needed. This is a low-level, unsynchronized function; most callers
@@ -1103,7 +1166,10 @@ func unsyncedConfigAccess(method, path string, body []byte, out io.Writer) error
if len(body) > 0 {
err = json.Unmarshal(body, &val)
if err != nil {
return fmt.Errorf("decoding request body: %v", err)
if jsonErr, ok := err.(*json.SyntaxError); ok {
return fmt.Errorf("decoding request body: %w, at offset %d", jsonErr, jsonErr.Offset)
}
return fmt.Errorf("decoding request body: %w", err)
}
}
@@ -1145,11 +1211,12 @@ traverseLoop:
var idx int
if method != http.MethodPost {
idxStr := parts[len(parts)-1]
idx, err = strconv.Atoi(idxStr)
idx, err = parseCanonicalArrayIndex(idxStr)
if err != nil {
return fmt.Errorf("[%s] invalid array index '%s': %v",
path, idxStr, err)
}
if idx < 0 || (method != http.MethodPut && idx >= len(arr)) || idx > len(arr) {
return fmt.Errorf("[%s] array index out of bounds: %s", path, idxStr)
}
@@ -1249,7 +1316,7 @@ traverseLoop:
}
case []any:
partInt, err := strconv.Atoi(part)
partInt, err := parseCanonicalArrayIndex(part)
if err != nil {
return fmt.Errorf("[/%s] invalid array index '%s': %v",
strings.Join(parts[:i+1], "/"), part, err)
+245 -27
View File
@@ -15,20 +15,28 @@
package caddy
import (
"bytes"
"context"
"crypto"
"crypto/tls"
"crypto/x509"
"encoding/json"
"errors"
"fmt"
"maps"
"net/http"
"net/http/httptest"
"os"
"reflect"
"sync"
"testing"
"time"
"github.com/caddyserver/certmagic"
"github.com/prometheus/client_golang/prometheus"
dto "github.com/prometheus/client_model/go"
"go.uber.org/zap"
"go.uber.org/zap/zaptest/observer"
)
var testCfg = []byte(`{
@@ -49,6 +57,13 @@ var testCfg = []byte(`{
}
`)
type testAdminPublicKey string
func (k testAdminPublicKey) Equal(x crypto.PublicKey) bool {
other, ok := x.(testAdminPublicKey)
return ok && k == other
}
func TestUnsyncedConfigAccess(t *testing.T) {
// each test is performed in sequence, so
// each change builds on the previous ones;
@@ -149,11 +164,9 @@ func TestLoadConcurrent(t *testing.T) {
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
go func() {
wg.Go(func() {
_ = Load(testCfg, true)
wg.Done()
}()
})
}
wg.Wait()
}
@@ -242,6 +255,51 @@ func TestAdminHandlerErrorHandling(t *testing.T) {
}
}
func TestAdminHandlerServeHTTPRedactsSensitiveHeadersInLogs(t *testing.T) {
core, logs := observer.New(zap.InfoLevel)
defaultLoggerMu.Lock()
origLogger := defaultLogger.logger
defaultLogger.logger = zap.New(core)
defaultLoggerMu.Unlock()
t.Cleanup(func() {
defaultLoggerMu.Lock()
defaultLogger.logger = origLogger
defaultLoggerMu.Unlock()
})
handler := adminHandler{
mux: http.NewServeMux(),
}
req := httptest.NewRequest(http.MethodGet, "/", nil)
req.Header.Set("Authorization", "Bearer secret")
req.Header.Set("Cookie", "session=secret")
req.Header.Set("X-Test", "ok")
rr := httptest.NewRecorder()
handler.ServeHTTP(rr, req)
if logs.Len() == 0 {
t.Fatal("expected request log entry")
}
ctx := logs.All()[0].ContextMap()
headers, ok := ctx["headers"].(map[string]any)
if !ok {
t.Fatalf("expected headers field in log context, got %T", ctx["headers"])
}
if got := headers["Authorization"]; !reflect.DeepEqual(got, []any{"REDACTED"}) {
t.Fatalf("expected redacted Authorization header, got %#v", got)
}
if got := headers["Cookie"]; !reflect.DeepEqual(got, []any{"REDACTED"}) {
t.Fatalf("expected redacted Cookie header, got %#v", got)
}
if got := headers["X-Test"]; !reflect.DeepEqual(got, []any{"ok"}) {
t.Fatalf("expected X-Test header to remain visible, got %#v", got)
}
}
func initAdminMetrics() {
if adminMetrics.requestErrors != nil {
prometheus.Unregister(adminMetrics.requestErrors)
@@ -277,13 +335,15 @@ func TestAdminHandlerBuiltinRouteErrors(t *testing.T) {
},
}
err := replaceLocalAdminServer(cfg, Context{})
// Build the admin handler directly (no listener active)
addr, err := ParseNetworkAddress("localhost:2019")
if err != nil {
t.Fatalf("setting up admin server: %v", err)
t.Fatalf("Failed to parse address: %v", err)
}
handler, err := cfg.Admin.newAdminHandler(addr, false, Context{})
if err != nil {
t.Fatalf("Failed to create admin handler: %v", err)
}
defer func() {
stopAdminServer(localAdminServer)
}()
tests := []struct {
name string
@@ -316,7 +376,7 @@ func TestAdminHandlerBuiltinRouteErrors(t *testing.T) {
req := httptest.NewRequest(test.method, fmt.Sprintf("http://localhost:2019%s", test.path), nil)
rr := httptest.NewRecorder()
localAdminServer.Handler.ServeHTTP(rr, req)
handler.ServeHTTP(rr, req)
if rr.Code != test.expectedStatus {
t.Errorf("expected status %d but got %d", test.expectedStatus, rr.Code)
@@ -404,7 +464,10 @@ func TestNewAdminHandlerRouterRegistration(t *testing.T) {
admin := &AdminConfig{
EnforceOrigin: false,
}
handler := admin.newAdminHandler(addr, false, Context{})
handler, err := admin.newAdminHandler(addr, false, Context{})
if err != nil {
t.Fatalf("Failed to create admin handler: %v", err)
}
req := httptest.NewRequest("GET", "/mock", nil)
req.Host = "localhost:2019"
@@ -416,10 +479,6 @@ func TestNewAdminHandlerRouterRegistration(t *testing.T) {
t.Errorf("Expected status code %d but got %d", http.StatusOK, rr.Code)
t.Logf("Response body: %s", rr.Body.String())
}
if len(admin.routers) != 1 {
t.Errorf("Expected 1 router to be stored, got %d", len(admin.routers))
}
}
type mockProvisionableRouter struct {
@@ -457,19 +516,16 @@ func TestAdminRouterProvisioning(t *testing.T) {
name string
provisionErr error
wantErr bool
routersAfter int // expected number of routers after provisioning
}{
{
name: "successful provisioning",
provisionErr: nil,
wantErr: false,
routersAfter: 0,
},
{
name: "provisioning error",
provisionErr: fmt.Errorf("provision failed"),
wantErr: true,
routersAfter: 1,
},
}
@@ -505,8 +561,7 @@ func TestAdminRouterProvisioning(t *testing.T) {
t.Fatalf("Failed to parse address: %v", err)
}
_ = admin.newAdminHandler(addr, false, Context{})
err = admin.provisionAdminRouters(Context{})
_, err = admin.newAdminHandler(addr, false, Context{})
if test.wantErr {
if err == nil {
@@ -517,10 +572,6 @@ func TestAdminRouterProvisioning(t *testing.T) {
t.Errorf("Expected no error but got: %v", err)
}
}
if len(admin.routers) != test.routersAfter {
t.Errorf("Expected %d routers after provisioning, got %d", test.routersAfter, len(admin.routers))
}
})
}
}
@@ -605,6 +656,99 @@ func TestAllowedOriginsUnixSocket(t *testing.T) {
}
}
func TestRemoteAdminAccessControlPathSegmentMatching(t *testing.T) {
const authorizedKey testAdminPublicKey = "authorized"
peerCert := &x509.Certificate{PublicKey: authorizedKey}
tests := []struct {
name string
allowedPath string
requestPath string
wantErr bool
}{
{
name: "exact path",
allowedPath: "/pki/ca/prod",
requestPath: "/pki/ca/prod",
wantErr: false,
},
{
name: "subpath",
allowedPath: "/pki/ca/prod",
requestPath: "/pki/ca/prod/certificates",
wantErr: false,
},
{
name: "trailing slash subpath",
allowedPath: "/pki/ca/prod/",
requestPath: "/pki/ca/prod/certificates",
wantErr: false,
},
{
name: "sibling with shared prefix",
allowedPath: "/pki/ca/prod",
requestPath: "/pki/ca/prod-backup",
wantErr: true,
},
{
name: "same segment plus digit",
allowedPath: "/pki/ca/prod",
requestPath: "/pki/ca/prod1",
wantErr: true,
},
{
name: "root path",
allowedPath: "/",
requestPath: "/pki/ca/prod",
wantErr: false,
},
}
for i, test := range tests {
t.Run(test.name, func(t *testing.T) {
remote := RemoteAdmin{
AccessControl: []*AdminAccess{
{
Permissions: []AdminPermissions{
{
Methods: []string{http.MethodGet},
Paths: []string{test.allowedPath},
},
},
publicKeys: []crypto.PublicKey{authorizedKey},
},
},
}
req := httptest.NewRequest(http.MethodGet, "https://localhost:2021"+test.requestPath, nil)
req.TLS = &tls.ConnectionState{
VerifiedChains: [][]*x509.Certificate{{peerCert}},
}
err := remote.enforceAccessControls(req)
if test.wantErr {
if err == nil {
t.Errorf("test %d (%s): allowed path %q, request path %q: expected forbidden error, got nil", i, test.name, test.allowedPath, test.requestPath)
return
}
var apiErr APIError
if !errors.As(err, &apiErr) {
t.Errorf("test %d (%s): allowed path %q, request path %q: expected APIError with HTTP status %d, got %T: %v", i, test.name, test.allowedPath, test.requestPath, http.StatusForbidden, err, err)
return
}
if apiErr.HTTPStatus != http.StatusForbidden {
t.Errorf("test %d (%s): allowed path %q, request path %q: expected HTTP status %d, got %d", i, test.name, test.allowedPath, test.requestPath, http.StatusForbidden, apiErr.HTTPStatus)
}
return
}
if err != nil {
t.Errorf("test %d (%s): allowed path %q, request path %q: expected no error, got %v", i, test.name, test.allowedPath, test.requestPath, err)
}
})
}
}
func TestReplaceRemoteAdminServer(t *testing.T) {
const testCert = `MIIDCTCCAfGgAwIBAgIUXsqJ1mY8pKlHQtI3HJ23x2eZPqwwDQYJKoZIhvcNAQEL
BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTIzMDEwMTAwMDAwMFoXDTI0MDEw
@@ -801,8 +945,24 @@ MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDRS0LmTwUT0iwP
...
-----END PRIVATE KEY-----`)
testStorage := certmagic.FileStorage{Path: t.TempDir()}
err := testStorage.Store(context.Background(), "localhost/localhost.crt", certPEM)
tmpDir, err := os.MkdirTemp("", "TestManageIdentity-")
if err != nil {
t.Fatal(err)
}
testStorage := certmagic.FileStorage{Path: tmpDir}
// Clean up the temp dir after the test finishes. Ensure any background
// certificate maintenance is stopped first to avoid RemoveAll races.
t.Cleanup(func() {
if identityCertCache != nil {
identityCertCache.Stop()
identityCertCache = nil
}
// Give goroutines a moment to exit and release file handles.
time.Sleep(50 * time.Millisecond)
_ = os.RemoveAll(tmpDir)
})
err = testStorage.Store(context.Background(), "localhost/localhost.crt", certPEM)
if err != nil {
t.Fatal(err)
}
@@ -864,7 +1024,7 @@ MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDRS0LmTwUT0iwP
},
},
},
storage: &certmagic.FileStorage{Path: "testdata"},
storage: &testStorage,
},
checkState: func(t *testing.T, cfg *Config) {
if len(cfg.Admin.Identity.issuers) != 1 {
@@ -902,6 +1062,13 @@ MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDRS0LmTwUT0iwP
identityCertCache.Stop()
identityCertCache = nil
}
// Ensure any cache started by manageIdentity is stopped at the end
defer func() {
if identityCertCache != nil {
identityCertCache.Stop()
identityCertCache = nil
}
}()
ctx := Context{
Context: context.Background(),
@@ -909,6 +1076,13 @@ MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDRS0LmTwUT0iwP
moduleInstances: make(map[string][]Module),
}
// If this test provided a FileStorage, set the package-level
// testCertMagicStorageOverride so certmagicConfig will use it.
if test.cfg != nil && test.cfg.storage != nil {
testCertMagicStorageOverride = test.cfg.storage
defer func() { testCertMagicStorageOverride = nil }()
}
err := manageIdentity(ctx, test.cfg)
if test.wantErr {
@@ -927,3 +1101,47 @@ MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDRS0LmTwUT0iwP
})
}
}
func TestUnsyncedConfigAccessCanonicalArrayIndices(t *testing.T) {
rawCfg = map[string]any{
rawConfigKey: map[string]any{
"list": []any{"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"},
},
}
tests := []struct {
name string
path string
wantOutput string
wantErr bool
}{
{name: "allow zero", path: "/" + rawConfigKey + "/list/0", wantOutput: "\"zero\"\n"},
{name: "allow one", path: "/" + rawConfigKey + "/list/1", wantOutput: "\"one\"\n"},
{name: "allow ten", path: "/" + rawConfigKey + "/list/10", wantOutput: "\"ten\"\n"},
{name: "reject leading zero", path: "/" + rawConfigKey + "/list/01", wantErr: true},
{name: "reject multiple leading zeros", path: "/" + rawConfigKey + "/list/002", wantErr: true},
{name: "reject plus sign", path: "/" + rawConfigKey + "/list/+1", wantErr: true},
{name: "reject negative zero", path: "/" + rawConfigKey + "/list/-0", wantErr: true},
}
for i, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
var gotOutput bytes.Buffer
err := unsyncedConfigAccess(http.MethodGet, tc.path, nil, &gotOutput)
if tc.wantErr {
if err == nil {
t.Errorf("test %d (%s): input path %q: expected error, got nil with output %q", i, tc.name, tc.path, gotOutput.String())
}
return
}
if err != nil {
t.Errorf("test %d (%s): input path %q: expected no error with output %q, got error %v with output %q", i, tc.name, tc.path, tc.wantOutput, err, gotOutput.String())
}
if gotOutput.String() != tc.wantOutput {
t.Errorf("test %d (%s): input path %q: expected output %q, got %q", i, tc.name, tc.path, tc.wantOutput, gotOutput.String())
}
})
}
}
+151 -29
View File
@@ -88,7 +88,7 @@ type Config struct {
storage certmagic.Storage
eventEmitter eventEmitter
cancelFunc context.CancelFunc
cancelFunc context.CancelCauseFunc
// fileSystems is a dict of fileSystems that will later be loaded from and added to.
fileSystems FileSystems
@@ -127,10 +127,9 @@ func Load(cfgJSON []byte, forceReload bool) error {
zap.Error(notifyErr),
zap.String("reload_err", err.Error()))
}
return
}
if err := notify.Ready(); err != nil {
Log().Error("unable to notify to service manager of ready state", zap.Error(err))
if notifyErr := notify.Ready(); notifyErr != nil {
Log().Error("unable to notify to service manager of ready state", zap.Error(notifyErr))
}
}()
@@ -147,8 +146,8 @@ func Load(cfgJSON []byte, forceReload bool) error {
// the new value (if applicable; i.e. "DELETE" doesn't have an input).
// If the resulting config is the same as the previous, no reload will
// occur unless forceReload is true. If the config is unchanged and not
// forcefully reloaded, then errConfigUnchanged This function is safe for
// concurrent use.
// forcefully reloaded, then errConfigUnchanged is returned. This function
// is safe for concurrent use.
// The ifMatchHeader can optionally be given a string of the format:
//
// "<path> <hash>"
@@ -227,8 +226,18 @@ func changeConfig(method, path string, input []byte, ifMatchHeader string, force
idx := make(map[string]string)
err = indexConfigObjects(rawCfg[rawConfigKey], "/"+rawConfigKey, idx)
if err != nil {
if len(rawCfgJSON) > 0 {
var oldCfg any
err2 := json.Unmarshal(rawCfgJSON, &oldCfg)
if err2 != nil {
err = fmt.Errorf("%v; additionally, restoring old config: %v", err, err2)
}
rawCfg[rawConfigKey] = oldCfg
} else {
rawCfg[rawConfigKey] = nil
}
return APIError{
HTTPStatus: http.StatusInternalServerError,
HTTPStatus: http.StatusBadRequest,
Err: fmt.Errorf("indexing config: %v", err),
}
}
@@ -248,6 +257,8 @@ func changeConfig(method, path string, input []byte, ifMatchHeader string, force
err = fmt.Errorf("%v; additionally, restoring old config: %v", err, err2)
}
rawCfg[rawConfigKey] = oldCfg
} else {
rawCfg[rawConfigKey] = nil
}
return fmt.Errorf("loading new config: %v", err)
@@ -281,14 +292,19 @@ func indexConfigObjects(ptr any, configPath string, index map[string]string) err
case map[string]any:
for k, v := range val {
if k == idKey {
var idStr string
switch idVal := v.(type) {
case string:
index[idVal] = configPath
idStr = idVal
case float64: // all JSON numbers decode as float64
index[fmt.Sprintf("%v", idVal)] = configPath
idStr = fmt.Sprintf("%v", idVal)
default:
return fmt.Errorf("%s: %s field must be a string or number", configPath, idKey)
}
if existingPath, ok := index[idStr]; ok {
return fmt.Errorf("duplicate ID '%s' found at %s and %s", idStr, existingPath, configPath)
}
index[idStr] = configPath
continue
}
// traverse this object property recursively
@@ -416,7 +432,7 @@ func run(newCfg *Config, start bool) (Context, error) {
// partially copied from provisionContext
if err != nil {
globalMetrics.configSuccess.Set(0)
ctx.cfg.cancelFunc()
ctx.cfg.cancelFunc(fmt.Errorf("configuration start error: %w", err))
if currentCtx.cfg != nil {
certmagic.Default.Storage = currentCtx.cfg.storage
@@ -424,13 +440,6 @@ func run(newCfg *Config, start bool) (Context, error) {
}
}()
// Provision any admin routers which may need to access
// some of the other apps at runtime
err = ctx.cfg.Admin.provisionAdminRouters(ctx)
if err != nil {
return ctx, err
}
// Start
err = func() error {
started := make([]string, 0, len(ctx.cfg.apps))
@@ -492,7 +501,7 @@ func provisionContext(newCfg *Config, replaceAdminServer bool) (Context, error)
// cleanup occurs when we return if there
// was an error; if no error, it will get
// cleaned up on next config cycle
ctx, cancel := NewContext(Context{Context: context.Background(), cfg: newCfg})
ctx, cancelCause := NewContextWithCause(Context{Context: context.Background(), cfg: newCfg})
defer func() {
if err != nil {
globalMetrics.configSuccess.Set(0)
@@ -501,7 +510,7 @@ func provisionContext(newCfg *Config, replaceAdminServer bool) (Context, error)
// since the associated config won't be used;
// this will cause all modules that were newly
// provisioned to clean themselves up
cancel()
cancelCause(fmt.Errorf("configuration error: %w", err))
// also undo any other state changes we made
if currentCtx.cfg != nil {
@@ -509,7 +518,7 @@ func provisionContext(newCfg *Config, replaceAdminServer bool) (Context, error)
}
}
}()
newCfg.cancelFunc = cancel // clean up later
newCfg.cancelFunc = cancelCause // clean up later
// set up logging before anything bad happens
if newCfg.Logging == nil {
@@ -729,7 +738,7 @@ func unsyncedStop(ctx Context) {
}
// clean up all modules
ctx.cfg.cancelFunc()
ctx.cfg.cancelFunc(fmt.Errorf("stopping apps"))
}
// Validate loads, provisions, and validates
@@ -737,7 +746,7 @@ func unsyncedStop(ctx Context) {
func Validate(cfg *Config) error {
_, err := run(cfg, false)
if err == nil {
cfg.cancelFunc() // call Cleanup on all modules
cfg.cancelFunc(fmt.Errorf("validation complete")) // call Cleanup on all modules
}
return err
}
@@ -750,7 +759,7 @@ func Validate(cfg *Config) error {
// code is emitted.
func exitProcess(ctx context.Context, logger *zap.Logger) {
// let the rest of the program know we're quitting; only do it once
if !atomic.CompareAndSwapInt32(exiting, 0, 1) {
if !exiting.CompareAndSwap(false, true) {
return
}
@@ -829,11 +838,11 @@ func exitProcess(ctx context.Context, logger *zap.Logger) {
}()
}
var exiting = new(int32) // accessed atomically
var exiting atomic.Bool
// Exiting returns true if the process is exiting.
// EXPERIMENTAL API: subject to change or removal.
func Exiting() bool { return atomic.LoadInt32(exiting) == 1 }
func Exiting() bool { return exiting.Load() }
// OnExit registers a callback to invoke during process exit.
// This registration is PROCESS-GLOBAL, meaning that each
@@ -945,6 +954,34 @@ func InstanceID() (uuid.UUID, error) {
// for example.
var CustomVersion string
// CustomBinaryName is an optional string that overrides the root
// command name from the default of "caddy". This is useful for
// downstream projects that embed Caddy but use a different binary
// name. Shell completions and help text will use this name instead
// of "caddy".
//
// Set this variable during `go build` with `-ldflags`:
//
// -ldflags '-X github.com/caddyserver/caddy/v2.CustomBinaryName=my_custom_caddy'
//
// for example.
var CustomBinaryName string
// CustomLongDescription is an optional string that overrides the
// long description of the root Cobra command. This is useful for
// downstream projects that embed Caddy but want different help
// output.
//
// Set this variable in an init() function of a package that is
// imported by your main:
//
// func init() {
// caddy.CustomLongDescription = "My custom server based on Caddy..."
// }
//
// for example.
var CustomLongDescription string
// Version returns the Caddy version in a simple/short form, and
// a full version string. The short form will not have spaces and
// is intended for User-Agent strings and similar, but may be
@@ -975,11 +1012,11 @@ func Version() (simple, full string) {
if CustomVersion != "" {
full = CustomVersion
simple = CustomVersion
return
return simple, full
}
full = "unknown"
simple = "unknown"
return
return simple, full
}
// find the Caddy module in the dependency list
for _, dep := range bi.Deps {
@@ -1059,7 +1096,7 @@ func Version() (simple, full string) {
}
}
return
return simple, full
}
// Event represents something that has happened or is happening.
@@ -1092,7 +1129,7 @@ type Event struct {
}
// NewEvent creates a new event, but does not emit the event. To emit an
// event, call Emit() on the current instance of the caddyevents app insteaad.
// event, call Emit() on the current instance of the caddyevents app instead.
//
// EXPERIMENTAL: Subject to change.
func NewEvent(ctx Context, name string, data map[string]any) (Event, error) {
@@ -1197,6 +1234,91 @@ var (
rawCfgMu sync.RWMutex
)
// lastConfigFile and lastConfigAdapter remember the source config
// file and adapter used when Caddy was started via the CLI "run" command.
// These are consulted by the SIGUSR1 handler to attempt reloading from
// the same source. They are intentionally not set for other entrypoints
// such as "caddy start" or subcommands like file-server.
var (
lastConfigMu sync.RWMutex
lastConfigFile string
lastConfigAdapter string
)
// reloadFromSourceFunc is the type of stored callback
// which is called when we receive a SIGUSR1 signal.
type reloadFromSourceFunc func(file, adapter string) error
// reloadFromSourceCallback is the stored callback
// which is called when we receive a SIGUSR1 signal.
var reloadFromSourceCallback reloadFromSourceFunc
// errReloadFromSourceUnavailable is returned when no reload-from-source callback is set.
var errReloadFromSourceUnavailable = errors.New("reload from source unavailable in this process") //nolint:unused
// SetLastConfig records the given source file and adapter as the
// last-known external configuration source. Intended to be called
// only when starting via "caddy run --config <file> --adapter <adapter>".
func SetLastConfig(file, adapter string, fn reloadFromSourceFunc) {
lastConfigMu.Lock()
lastConfigFile = file
lastConfigAdapter = adapter
reloadFromSourceCallback = fn
lastConfigMu.Unlock()
}
// ClearLastConfigIfDifferent clears the recorded last-config if the provided
// source file/adapter do not match the recorded last-config. If both srcFile
// and srcAdapter are empty, the last-config is cleared.
func ClearLastConfigIfDifferent(srcFile, srcAdapter string) {
if (srcFile != "" || srcAdapter != "") && lastConfigMatches(srcFile, srcAdapter) {
return
}
SetLastConfig("", "", nil)
}
// getLastConfig returns the last-known config file and adapter.
func getLastConfig() (file, adapter string, fn reloadFromSourceFunc) {
lastConfigMu.RLock()
f, a, cb := lastConfigFile, lastConfigAdapter, reloadFromSourceCallback
lastConfigMu.RUnlock()
return f, a, cb
}
// lastConfigMatches returns true if the provided source file and/or adapter
// matches the recorded last-config. Matching rules (in priority order):
// 1. If srcAdapter is provided and differs from the recorded adapter, no match.
// 2. If srcFile exactly equals the recorded file, match.
// 3. If both sides can be made absolute and equal, match.
// 4. If basenames are equal, match.
func lastConfigMatches(srcFile, srcAdapter string) bool {
lf, la, _ := getLastConfig()
// If adapter is provided, it must match.
if srcAdapter != "" && srcAdapter != la {
return false
}
// Quick equality check.
if srcFile == lf {
return true
}
// Try absolute path comparison.
sAbs, sErr := filepath.Abs(srcFile)
lAbs, lErr := filepath.Abs(lf)
if sErr == nil && lErr == nil && sAbs == lAbs {
return true
}
// Final fallback: basename equality.
if filepath.Base(srcFile) == filepath.Base(lf) {
return true
}
return false
}
// errSameConfig is returned if the new config is the same
// as the old one. This isn't usually an actual, actionable
// error; it's mostly a sentinel value.
+19 -7
View File
@@ -270,7 +270,7 @@ func (d *Dispenser) File() string {
// targets are left unchanged. If all the targets are filled,
// then true is returned.
func (d *Dispenser) Args(targets ...*string) bool {
for i := 0; i < len(targets); i++ {
for i := range targets {
if !d.NextArg() {
return false
}
@@ -308,9 +308,9 @@ func (d *Dispenser) CountRemainingArgs() int {
}
// RemainingArgs loads any more arguments (tokens on the same line)
// into a slice and returns them. Open curly brace tokens also indicate
// the end of arguments, and the curly brace is not included in
// the return value nor is it loaded.
// into a slice of strings and returns them. Open curly brace tokens
// also indicate the end of arguments, and the curly brace is not
// included in the return value nor is it loaded.
func (d *Dispenser) RemainingArgs() []string {
var args []string
for d.NextArg() {
@@ -320,9 +320,9 @@ func (d *Dispenser) RemainingArgs() []string {
}
// RemainingArgsRaw loads any more arguments (tokens on the same line,
// retaining quotes) into a slice and returns them. Open curly brace
// tokens also indicate the end of arguments, and the curly brace is
// not included in the return value nor is it loaded.
// retaining quotes) into a slice of strings and returns them.
// Open curly brace tokens also indicate the end of arguments,
// and the curly brace is not included in the return value nor is it loaded.
func (d *Dispenser) RemainingArgsRaw() []string {
var args []string
for d.NextArg() {
@@ -331,6 +331,18 @@ func (d *Dispenser) RemainingArgsRaw() []string {
return args
}
// RemainingArgsAsTokens loads any more arguments (tokens on the same line)
// into a slice of Token-structs and returns them. Open curly brace tokens
// also indicate the end of arguments, and the curly brace is not included
// in the return value nor is it loaded.
func (d *Dispenser) RemainingArgsAsTokens() []Token {
var args []Token
for d.NextArg() {
args = append(args, d.Token())
}
return args
}
// NewFromNextSegment returns a new dispenser with a copy of
// the tokens from the current token until the end of the
// "directive" whether that be to the end of the line or
+60
View File
@@ -274,6 +274,66 @@ func TestDispenser_RemainingArgs(t *testing.T) {
}
}
func TestDispenser_RemainingArgsAsTokens(t *testing.T) {
input := `dir1 arg1 arg2 arg3
dir2 arg4 arg5
dir3 arg6 { arg7
dir4`
d := NewTestDispenser(input)
d.Next() // dir1
args := d.RemainingArgsAsTokens()
tokenTexts := make([]string, 0, len(args))
for _, arg := range args {
tokenTexts = append(tokenTexts, arg.Text)
}
if expected := []string{"arg1", "arg2", "arg3"}; !reflect.DeepEqual(tokenTexts, expected) {
t.Errorf("RemainingArgsAsTokens(): Expected %v, got %v", expected, tokenTexts)
}
d.Next() // dir2
args = d.RemainingArgsAsTokens()
tokenTexts = tokenTexts[:0]
for _, arg := range args {
tokenTexts = append(tokenTexts, arg.Text)
}
if expected := []string{"arg4", "arg5"}; !reflect.DeepEqual(tokenTexts, expected) {
t.Errorf("RemainingArgsAsTokens(): Expected %v, got %v", expected, tokenTexts)
}
d.Next() // dir3
args = d.RemainingArgsAsTokens()
tokenTexts = tokenTexts[:0]
for _, arg := range args {
tokenTexts = append(tokenTexts, arg.Text)
}
if expected := []string{"arg6"}; !reflect.DeepEqual(tokenTexts, expected) {
t.Errorf("RemainingArgsAsTokens(): Expected %v, got %v", expected, tokenTexts)
}
d.Next() // {
d.Next() // arg7
d.Next() // dir4
args = d.RemainingArgsAsTokens()
tokenTexts = tokenTexts[:0]
for _, arg := range args {
tokenTexts = append(tokenTexts, arg.Text)
}
if len(args) != 0 {
t.Errorf("RemainingArgsAsTokens(): Expected %v, got %v", []string{}, tokenTexts)
}
}
func TestDispenser_ArgErr_Err(t *testing.T) {
input := `dir1 {
}
+84 -19
View File
@@ -18,6 +18,7 @@ import (
"bytes"
"io"
"slices"
"strings"
"unicode"
)
@@ -52,19 +53,43 @@ func Format(input []byte) []byte {
newLines int // count of newlines consumed
comment bool // whether we're in a comment
quoted bool // whether we're in a quoted segment
escaped bool // whether current char is escaped
comment bool // whether we're in a comment
quotes string // encountered quotes ('', '`', '"', '"`', '`"')
escaped bool // whether current char is escaped
heredoc heredocState // whether we're in a heredoc
heredocEscaped bool // whether heredoc is escaped
heredocMarker []rune
heredocClosingMarker []rune
nesting int // indentation level
withinBackquote bool
nesting int // indentation level
currentToken strings.Builder
currentLineFirstToken string
previousLineWasTopLevelImport bool
openBraceOwnLine bool
)
finishToken := func() {
if currentToken.Len() == 0 {
return
}
if currentLineFirstToken == "" {
currentLineFirstToken = currentToken.String()
}
currentToken.Reset()
}
finishLine := func() {
finishToken()
if currentLineFirstToken != "" {
previousLineWasTopLevelImport = nesting == 0 && currentLineFirstToken == "import"
} else if !openBrace || !openBraceOwnLine || openBraceWritten {
previousLineWasTopLevelImport = false
}
currentLineFirstToken = ""
}
write := func(ch rune) {
out.WriteRune(ch)
last = ch
@@ -89,12 +114,8 @@ func Format(input []byte) []byte {
}
panic(err)
}
if ch == '`' {
withinBackquote = !withinBackquote
}
// detect whether we have the start of a heredoc
if !quoted && (heredoc == heredocClosed && !heredocEscaped) &&
if quotes == "" && (heredoc == heredocClosed && !heredocEscaped) &&
space && last == '<' && ch == '<' {
write(ch)
heredoc = heredocOpening
@@ -180,22 +201,55 @@ func Format(input []byte) []byte {
continue
}
if quoted {
if ch == '`' {
switch quotes {
case "\"`":
quotes = "\""
case "`":
quotes = ""
case "\"":
quotes = "\"`"
default:
quotes = "`"
}
}
if quotes == "\"" {
if ch == '"' {
quoted = false
quotes = ""
}
write(ch)
continue
}
if space && ch == '"' {
quoted = true
if ch == '"' {
switch quotes {
case "":
if space {
quotes = "\""
}
case "`\"":
quotes = "`"
case "\"`":
quotes = ""
}
}
if strings.Contains(quotes, "`") {
if ch == '`' && space && !beginningOfLine {
write(' ')
}
write(ch)
space = false
continue
}
if unicode.IsSpace(ch) {
finishToken()
space = true
heredocEscaped = false
if ch == '\n' {
finishLine()
newLines++
}
continue
@@ -222,13 +276,19 @@ func Format(input []byte) []byte {
}
openBrace = false
if beginningOfLine {
if openBraceOwnLine && previousLineWasTopLevelImport {
if last != '\n' {
nextLine()
}
indent()
} else if !openBraceSpace {
} else if beginningOfLine {
indent()
} else if !openBraceSpace || !unicode.IsSpace(last) {
write(' ')
}
write('{')
openBraceWritten = true
openBraceOwnLine = false
nextLine()
newLines = 0
// prevent infinite nesting from ridiculous inputs (issue #4169)
@@ -239,21 +299,25 @@ func Format(input []byte) []byte {
switch {
case ch == '{':
finishToken()
openBrace = true
openBraceSpace = spacePrior && !beginningOfLine
if openBraceSpace {
openBraceOwnLine = newLines > 0
if openBraceSpace && newLines == 0 {
write(' ')
}
openBraceWritten = false
if withinBackquote {
if quotes == "`" {
write('{')
openBraceWritten = true
openBraceOwnLine = false
continue
}
continue
case ch == '}' && (spacePrior || !openBrace):
if withinBackquote {
finishToken()
if quotes == "`" {
write('}')
continue
}
@@ -297,6 +361,7 @@ func Format(input []byte) []byte {
space = true
}
currentToken.WriteRune(ch)
write(ch)
beginningOfLine = false
+46
View File
@@ -444,6 +444,52 @@ block2 {
input: "block {respond \"All braces should remain: {{now | date `2006`}}\"}",
expect: "block {respond \"All braces should remain: {{now | date `2006`}}\"}",
},
{
description: "Preserve quoted backticks and backticked quotes",
input: "block { respond \"`\" } block { respond `\"`}",
expect: "block {\n\trespond \"`\"\n}\n\nblock {\n\trespond `\"`\n}",
},
{
description: "No trailing space on line before env variable",
input: `{
a
{$ENV_VAR}
}
`,
expect: `{
a
{$ENV_VAR}
}
`,
},
{
description: "issue #7425: multiline backticked string indentation",
input: `https://localhost:8953 {
respond ` + "`" + `Here are some random numbers:
{{randNumeric 16}}
Hope this helps.` + "`" + `
}`,
expect: "https://localhost:8953 {\n\trespond `Here are some random numbers:\n\n{{randNumeric 16}}\n\nHope this helps.`\n}",
},
{
description: "imports before global options block keep standalone brace",
input: `import ./conf.d/matcher_my_subnet.caddy
import ./conf.d/matcher_not_my_subnet.caddy
{
order crowdsec first
order appsec after crowdsec
}`,
expect: `import ./conf.d/matcher_my_subnet.caddy
import ./conf.d/matcher_not_my_subnet.caddy
{
order crowdsec first
order appsec after crowdsec
}`,
},
} {
// the formatter should output a trailing newline,
// even if the tests aren't written to expect that
+38 -27
View File
@@ -379,28 +379,23 @@ func (p *parser) doImport(nesting int) error {
if len(blockTokens) > 0 {
// use such tokens to create a new dispenser, and then use it to parse each block
bd := NewDispenser(blockTokens)
// one iteration processes one sub-block inside the import
for bd.Next() {
// see if we can grab a key
var currentMappingKey string
if bd.Val() == "{" {
currentMappingKey := bd.Val()
if currentMappingKey == "{" {
return p.Err("anonymous blocks are not supported")
}
currentMappingKey = bd.Val()
currentMappingTokens := []Token{}
// read all args until end of line / {
if bd.NextArg() {
// load up all arguments (if there even are any)
currentMappingTokens := bd.RemainingArgsAsTokens()
// load up the entire block
for mappingNesting := bd.Nesting(); bd.NextBlock(mappingNesting); {
currentMappingTokens = append(currentMappingTokens, bd.Token())
for bd.NextArg() {
currentMappingTokens = append(currentMappingTokens, bd.Token())
}
// TODO(elee1766): we don't enter another mapping here because it's annoying to extract the { and } properly.
// maybe someone can do that in the future
} else {
// attempt to enter a block and add tokens to the currentMappingTokens
for mappingNesting := bd.Nesting(); bd.NextBlock(mappingNesting); {
currentMappingTokens = append(currentMappingTokens, bd.Token())
}
}
blockMapping[currentMappingKey] = currentMappingTokens
}
}
@@ -512,7 +507,7 @@ func (p *parser) doImport(nesting int) error {
// format, won't check for nesting correctness or any other error, that's what parser does.
if !maybeSnippet && nesting == 0 {
// first of the line
if i == 0 || isNextOnNewLine(tokensCopy[i-1], token) {
if i == 0 || isNextOnNewLine(tokensCopy[len(tokensCopy)-1], token) {
index = 0
} else {
index++
@@ -538,25 +533,24 @@ func (p *parser) doImport(nesting int) error {
}
// if it is {block}, we substitute with all tokens in the block
// if it is {blocks.*}, we substitute with the tokens in the mapping for the *
var skip bool
var tokensToAdd []Token
foundBlockDirective := false
switch {
case token.Text == "{block}":
foundBlockDirective = true
tokensToAdd = blockTokens
case strings.HasPrefix(token.Text, "{blocks.") && strings.HasSuffix(token.Text, "}"):
foundBlockDirective = true
// {blocks.foo.bar} will be extracted to key `foo.bar`
blockKey := strings.TrimPrefix(strings.TrimSuffix(token.Text, "}"), "{blocks.")
val, ok := blockMapping[blockKey]
if ok {
tokensToAdd = val
}
default:
skip = true
}
if !skip {
if len(tokensToAdd) == 0 {
// if there is no content in the snippet block, don't do any replacement
// this allows snippets which contained {block}/{block.*} before this change to continue functioning as normal
if foundBlockDirective {
if maybeSnippet {
tokensCopy = append(tokensCopy, token)
} else {
tokensCopy = append(tokensCopy, tokensToAdd...)
@@ -626,7 +620,7 @@ func (p *parser) doSingleImport(importFile string) ([]Token, error) {
if err != nil {
return nil, p.Errf("Failed to get absolute path of file: %s: %v", importFile, err)
}
for i := 0; i < len(importedTokens); i++ {
for i := range importedTokens {
importedTokens[i].File = filename
}
@@ -692,11 +686,28 @@ func (p *parser) directive() error {
// a opening curly brace. It does NOT advance the token.
func (p *parser) openCurlyBrace() error {
if p.Val() != "{" {
if p.valLooksLikeGlobalOptionsAfterImportedSnippets() {
return p.Err("global options block must appear before import directives; move the global options block to the top of the Caddyfile")
}
return p.SyntaxErr("{")
}
return nil
}
func (p *parser) valLooksLikeGlobalOptionsAfterImportedSnippets() bool {
if p.Val() != "import" || len(p.block.Keys) == 0 {
return false
}
for _, key := range p.block.Keys {
if !strings.HasPrefix(key.Text, "(") || !strings.HasSuffix(key.Text, ")") {
return false
}
}
return true
}
// closeCurlyBrace expects the current token to be
// a closing curly brace. This acts like an assertion
// because it returns an error if the token is not
@@ -771,7 +782,7 @@ type ServerBlock struct {
}
func (sb ServerBlock) GetKeysText() []string {
res := []string{}
res := make([]string, 0, len(sb.Keys))
for _, k := range sb.Keys {
res = append(res, k.Text)
}
+147
View File
@@ -18,6 +18,7 @@ import (
"bytes"
"os"
"path/filepath"
"strings"
"testing"
)
@@ -884,6 +885,152 @@ func TestRejectsGlobalMatcher(t *testing.T) {
}
}
func TestRejectAnonymousImportBlock(t *testing.T) {
p := testParser(`
(site) {
http://{args[0]} https://{args[0]} {
{block}
}
}
import site test.domain {
{
header_up Host {host}
header_up X-Real-IP {remote_host}
}
}
`)
_, err := p.parseAll()
if err == nil {
t.Fatal("Expected an error, but got nil")
}
expected := "anonymous blocks are not supported"
if !strings.HasPrefix(err.Error(), "anonymous blocks are not supported") {
t.Errorf("Expected error to start with '%s' but got '%v'", expected, err)
}
}
func TestAcceptSiteImportWithBraces(t *testing.T) {
p := testParser(`
(site) {
http://{args[0]} https://{args[0]} {
{block}
}
}
import site test.domain {
reverse_proxy http://192.168.1.1:8080 {
header_up Host {host}
}
}
`)
_, err := p.parseAll()
if err != nil {
t.Errorf("Expected error to be nil but got '%v'", err)
}
}
func TestGlobalOptionsAfterImportedSnippetsGivesHelpfulError(t *testing.T) {
tempDir := t.TempDir()
importFile1 := filepath.Join(tempDir, "matcher_snippet_1.caddy")
importFile2 := filepath.Join(tempDir, "matcher_snippet_2.caddy")
err := os.WriteFile(importFile1, []byte(`(matcher1)`), 0o644)
if err != nil {
t.Fatalf("writing first import file: %v", err)
}
err = os.WriteFile(importFile2, []byte(`(matcher2)`), 0o644)
if err != nil {
t.Fatalf("writing second import file: %v", err)
}
_, err = Parse("Testfile", []byte(`import `+importFile1+`
import `+importFile2+`
{
debug
}`))
if err == nil {
t.Fatal("Expected an error, but got nil")
}
expected := "global options block must appear before import directives; move the global options block to the top of the Caddyfile"
if !strings.HasPrefix(err.Error(), expected) {
t.Errorf("Expected error to start with '%s' but got '%v'", expected, err)
}
}
func TestImportedSnippetDefinitionRetainsBlockPlaceholder(t *testing.T) {
tempDir := t.TempDir()
importFile := filepath.Join(tempDir, "snippets.caddy")
err := os.WriteFile(importFile, []byte(`
(site) {
http://{args[0]} {
respond "before"
{block}
respond "after"
}
}
`), 0o644)
if err != nil {
t.Fatalf("writing imported snippet file: %v", err)
}
for _, tc := range []struct {
name string
input string
expectedDirectives []string
}{
{
name: "with nested block",
input: `
import ` + importFile + `
import site example.com {
redir https://example.net
}
`,
expectedDirectives: []string{"respond", "redir", "respond"},
},
{
name: "without nested block",
input: `
import ` + importFile + `
import site example.com
`,
expectedDirectives: []string{"respond", "respond"},
},
} {
t.Run(tc.name, func(t *testing.T) {
p := testParser(tc.input)
blocks, err := p.parseAll()
if err != nil {
t.Fatalf("parseAll: %v", err)
}
if len(blocks) != 1 {
t.Fatalf("expected exactly one server block, got %d", len(blocks))
}
if actual := blocks[0].GetKeysText(); len(actual) != 1 || actual[0] != "http://example.com" {
t.Fatalf("expected server block key http://example.com, got %v", actual)
}
if len(blocks[0].Segments) != len(tc.expectedDirectives) {
t.Fatalf("expected %d segments, got %d", len(tc.expectedDirectives), len(blocks[0].Segments))
}
for i, directive := range tc.expectedDirectives {
if actual := blocks[0].Segments[i].Directive(); actual != directive {
t.Fatalf("segment %d: expected directive %q, got %q", i, directive, actual)
}
}
})
}
}
func testParser(input string) parser {
return parser{Dispenser: NewTestDispenser(input)}
}
+5 -1
View File
@@ -81,7 +81,11 @@ func JSONModuleObject(val any, fieldName, fieldVal string, warnings *[]Warning)
err = json.Unmarshal(enc, &tmp)
if err != nil {
if warnings != nil {
*warnings = append(*warnings, Warning{Message: err.Error()})
message := err.Error()
if jsonErr, ok := err.(*json.SyntaxError); ok {
message = fmt.Sprintf("%v, at offset %d", jsonErr.Error(), jsonErr.Offset)
}
*warnings = append(*warnings, Warning{Message: message})
}
return nil
}
+38 -22
View File
@@ -91,7 +91,7 @@ func parseBind(h Helper) ([]ConfigValue, error) {
// curves <curves...>
// client_auth {
// mode [request|require|verify_if_given|require_and_verify]
// trust_pool <module_name> [...]
// trust_pool <module_name> [...]
// trusted_leaf_cert <base64_der>
// trusted_leaf_cert_file <filename>
// }
@@ -113,6 +113,7 @@ func parseBind(h Helper) ([]ConfigValue, error) {
// issuer <module_name> [...]
// get_certificate <module_name> [...]
// insecure_secrets_log <log_file>
// renewal_window_ratio <ratio>
// }
func parseTLS(h Helper) ([]ConfigValue, error) {
h.Next() // consume directive name
@@ -129,6 +130,7 @@ func parseTLS(h Helper) ([]ConfigValue, error) {
var onDemand bool
var reusePrivateKeys bool
var forceAutomate bool
var renewalWindowRatio float64
// Track which DNS challenge options are set
var dnsOptionsSet []string
@@ -473,6 +475,20 @@ func parseTLS(h Helper) ([]ConfigValue, error) {
}
cp.InsecureSecretsLog = h.Val()
case "renewal_window_ratio":
arg := h.RemainingArgs()
if len(arg) != 1 {
return nil, h.ArgErr()
}
ratio, err := strconv.ParseFloat(arg[0], 64)
if err != nil {
return nil, h.Errf("parsing renewal_window_ratio: %v", err)
}
if ratio <= 0 || ratio >= 1 {
return nil, h.Errf("renewal_window_ratio must be between 0 and 1 (exclusive)")
}
renewalWindowRatio = ratio
default:
return nil, h.Errf("unknown subdirective: %s", h.Val())
}
@@ -481,7 +497,7 @@ func parseTLS(h Helper) ([]ConfigValue, error) {
// Validate DNS challenge config: any DNS challenge option except "dns" requires a DNS provider
if acmeIssuer != nil && acmeIssuer.Challenges != nil && acmeIssuer.Challenges.DNS != nil {
dnsCfg := acmeIssuer.Challenges.DNS
providerSet := dnsCfg.ProviderRaw != nil || h.Option("dns") != nil
providerSet := dnsCfg.ProviderRaw != nil || h.Option("dns") != nil || h.Option("acme_dns") != nil
if len(dnsOptionsSet) > 0 && !providerSet {
return nil, h.Errf(
"setting DNS challenge options [%s] requires a DNS provider (set with the 'dns' subdirective or 'acme_dns' global option)",
@@ -534,26 +550,11 @@ func parseTLS(h Helper) ([]ConfigValue, error) {
}
case acmeIssuer != nil:
// implicit ACME issuers (from various subdirectives) - use defaults; there might be more than one
defaultIssuers := caddytls.DefaultIssuers(acmeIssuer.Email)
// if an ACME CA endpoint was set, the user expects to use that specific one,
// not any others that may be defaults, so replace all defaults with that ACME CA
if acmeIssuer.CA != "" {
defaultIssuers = []certmagic.Issuer{acmeIssuer}
}
// implicit ACME issuers (from various subdirectives) should inherit from
// any globally-configured ACME issuer templates, then apply the local
// shortcut settings as overrides.
defaultIssuers := implicitACMEIssuers(h, acmeIssuer)
for _, issuer := range defaultIssuers {
// apply settings from the implicitly-configured ACMEIssuer to any
// default ACMEIssuers, but preserve each default issuer's CA endpoint,
// because, for example, if you configure the DNS challenge, it should
// apply to any of the default ACMEIssuers, but you don't want to trample
// out their unique CA endpoints
if iss, ok := issuer.(*caddytls.ACMEIssuer); ok && iss != nil {
acmeCopy := *acmeIssuer
acmeCopy.CA = iss.CA
issuer = &acmeCopy
}
configVals = append(configVals, ConfigValue{
Class: "tls.cert_issuer",
Value: issuer,
@@ -597,6 +598,14 @@ func parseTLS(h Helper) ([]ConfigValue, error) {
})
}
// renewal window ratio
if renewalWindowRatio > 0 {
configVals = append(configVals, ConfigValue{
Class: "tls.renewal_window_ratio",
Value: renewalWindowRatio,
})
}
// if enabled, the names in the site addresses will be
// added to the automation policies
if forceAutomate {
@@ -644,6 +653,8 @@ func parseRoot(h Helper) ([]ConfigValue, error) {
if !h.NextArg() {
return nil, h.ArgErr()
}
// store the unmatched root in block state so sibling directives can access it
h.BlockState["root"] = h.Val()
return h.NewRoute(nil, caddyhttp.VarsMiddleware{"root": h.Val()}), nil
}
@@ -658,6 +669,10 @@ func parseRoot(h Helper) ([]ConfigValue, error) {
if !h.NextArg() {
return nil, h.ArgErr()
}
// store the unmatched root in state so sibling/child directives can access it
if userMatcherSet == nil {
h.BlockState["root"] = h.Val()
}
// make the route with the matcher
return h.NewRoute(userMatcherSet, caddyhttp.VarsMiddleware{"root": h.Val()}), nil
}
@@ -930,6 +945,7 @@ func parseLogHelper(h Helper, globalLogNames map[string]struct{}) ([]ConfigValue
// modifications to the parsing behavior.
parseAsGlobalOption := globalLogNames != nil
// nolint:prealloc
var configValues []ConfigValue
// Logic below expects that a name is always present when a
@@ -1037,7 +1053,7 @@ func parseLogHelper(h Helper, globalLogNames map[string]struct{}) ([]ConfigValue
if !d.NextArg() {
return nil, d.ArgErr()
}
interval, err := time.ParseDuration(d.Val() + "ns")
interval, err := caddy.ParseDuration(d.Val())
if err != nil {
return nil, d.Errf("failed to parse interval: %v", err)
}
+2 -2
View File
@@ -66,14 +66,14 @@ func TestLogDirectiveSyntax(t *testing.T) {
input: `:8080 {
log {
sampling {
interval 2
interval 2s
first 3
thereafter 4
}
}
}
`,
output: `{"logging":{"logs":{"default":{"exclude":["http.log.access.log0"]},"log0":{"sampling":{"interval":2,"first":3,"thereafter":4},"include":["http.log.access.log0"]}}},"apps":{"http":{"servers":{"srv0":{"listen":[":8080"],"logs":{"default_logger_name":"log0"}}}}}}`,
output: `{"logging":{"logs":{"default":{"exclude":["http.log.access.log0"]},"log0":{"sampling":{"interval":2000000000,"first":3,"thereafter":4},"include":["http.log.access.log0"]}}},"apps":{"http":{"servers":{"srv0":{"listen":[":8080"],"logs":{"default_logger_name":"log0"}}}}}}`,
expectError: false,
},
} {
+10 -1
View File
@@ -202,7 +202,10 @@ func RegisterGlobalOption(opt string, setupFunc UnmarshalGlobalFunc) {
type Helper struct {
*caddyfile.Dispenser
// State stores intermediate variables during caddyfile adaptation.
State map[string]any
State map[string]any
// BlockState stores intermediate variables scoped to the current block.
// It propagates down, but unlike state not back up from child to parent.
BlockState map[string]any
options map[string]any
warnings *[]caddyconfig.Warning
matcherDefs map[string]caddy.ModuleMap
@@ -385,6 +388,11 @@ func parseSegmentAsConfig(h Helper) ([]ConfigValue, error) {
}
}
// clone BlockState once for the entire block so sibling directives
// can share state, but changes don't leak to the parent scope
subBlockState := make(map[string]any, len(h.BlockState))
maps.Copy(subBlockState, h.BlockState)
// with matchers ready to go, evaluate each directive's segment
for _, seg := range segments {
dir := seg.Directive()
@@ -396,6 +404,7 @@ func parseSegmentAsConfig(h Helper) ([]ConfigValue, error) {
subHelper := h
subHelper.Dispenser = caddyfile.NewDispenser(seg)
subHelper.matcherDefs = matcherDefs
subHelper.BlockState = subBlockState
results, err := dirFunc(subHelper)
if err != nil {
+17 -1
View File
@@ -143,6 +143,7 @@ func (st ServerType) Setup(
parentBlock: sb.block,
groupCounter: gc,
State: state,
BlockState: state,
}
results, err := dirFunc(h)
@@ -504,6 +505,7 @@ func (ServerType) extractNamedRoutes(
parentBlock: sb.block,
groupCounter: gc,
State: state,
BlockState: state,
}
handler, err := ParseSegmentAsSubroute(h)
@@ -822,7 +824,7 @@ func (st *ServerType) serversFromPairings(
// https://caddy.community/t/making-sense-of-auto-https-and-why-disabling-it-still-serves-https-instead-of-http/9761
createdTLSConnPolicies, ok := sblock.pile["tls.connection_policy"]
hasTLSEnabled := (ok && len(createdTLSConnPolicies) > 0) ||
(addr.Host != "" && srv.AutoHTTPS != nil && !slices.Contains(srv.AutoHTTPS.Skip, addr.Host))
(addr.Host != "" && (srv.AutoHTTPS == nil || !slices.Contains(srv.AutoHTTPS.Skip, addr.Host)))
// we'll need to remember if the address qualifies for auto-HTTPS, so we
// can add a TLS conn policy if necessary
@@ -851,6 +853,20 @@ func (st *ServerType) serversFromPairings(
srv.ListenerWrappersRaw = append(srv.ListenerWrappersRaw, jsonListenerWrapper)
}
// Look for any config values that provide packet conn wrappers on the server block
for _, listenerConfig := range sblock.pile["packet_conn_wrapper"] {
packetConnWrapper, ok := listenerConfig.Value.(caddy.PacketConnWrapper)
if !ok {
return nil, fmt.Errorf("config for a packet conn wrapper did not provide a value that implements caddy.PacketConnWrapper")
}
jsonPacketConnWrapper := caddyconfig.JSONModuleObject(
packetConnWrapper,
"wrapper",
packetConnWrapper.(caddy.Module).CaddyModule().ID.Name(),
warnings)
srv.PacketConnWrappersRaw = append(srv.PacketConnWrappersRaw, jsonPacketConnWrapper)
}
// set up each handler directive, making sure to honor directive order
dirRoutes := sblock.pile["route"]
siteSubroute, err := buildSubroute(dirRoutes, groupCounter, true)
@@ -1,9 +1,11 @@
package httpcaddyfile
import (
"encoding/json"
"testing"
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
)
func TestMatcherSyntax(t *testing.T) {
@@ -209,3 +211,53 @@ func TestGlobalOptions(t *testing.T) {
}
}
}
func TestDefaultSNIWithoutHTTPS(t *testing.T) {
caddyfileStr := `{
default_sni my-sni.com
}
example.com {
}`
adapter := caddyfile.Adapter{
ServerType: ServerType{},
}
result, _, err := adapter.Adapt([]byte(caddyfileStr), nil)
if err != nil {
t.Fatalf("Failed to adapt Caddyfile: %v", err)
}
var config struct {
Apps struct {
HTTP struct {
Servers map[string]*caddyhttp.Server `json:"servers"`
} `json:"http"`
} `json:"apps"`
}
if err := json.Unmarshal(result, &config); err != nil {
t.Fatalf("Failed to unmarshal JSON config: %v", err)
}
server, ok := config.Apps.HTTP.Servers["srv0"]
if !ok {
t.Fatalf("Expected server 'srv0' to be created")
}
if len(server.TLSConnPolicies) == 0 {
t.Fatalf("Expected TLS connection policies to be generated, got none")
}
found := false
for _, policy := range server.TLSConnPolicies {
if policy.DefaultSNI == "my-sni.com" {
found = true
break
}
}
if !found {
t.Errorf("Expected default_sni 'my-sni.com' in TLS connection policies, but it was missing. Generated JSON: %s", string(result))
}
}
+35 -2
View File
@@ -64,7 +64,9 @@ func init() {
RegisterGlobalOption("preferred_chains", parseOptPreferredChains)
RegisterGlobalOption("persist_config", parseOptPersistConfig)
RegisterGlobalOption("dns", parseOptDNS)
RegisterGlobalOption("tls_resolvers", parseOptTLSResolvers)
RegisterGlobalOption("ech", parseOptECH)
RegisterGlobalOption("renewal_window_ratio", parseOptRenewalWindowRatio)
}
func parseOptTrue(d *caddyfile.Dispenser, _ any) (any, error) { return true, nil }
@@ -305,6 +307,15 @@ func parseOptSingleString(d *caddyfile.Dispenser, _ any) (any, error) {
return val, nil
}
func parseOptTLSResolvers(d *caddyfile.Dispenser, _ any) (any, error) {
d.Next() // consume option name
resolvers := d.RemainingArgs()
if len(resolvers) == 0 {
return nil, d.ArgErr()
}
return resolvers, nil
}
func parseOptDefaultBind(d *caddyfile.Dispenser, _ any) (any, error) {
d.Next() // consume option name
@@ -457,9 +468,8 @@ func parseOptAutoHTTPS(d *caddyfile.Dispenser, _ any) (any, error) {
case "disable_redirects":
case "disable_certs":
case "ignore_loaded_certs":
case "prefer_wildcard":
default:
return "", d.Errf("auto_https must be one of 'off', 'disable_redirects', 'disable_certs', 'ignore_loaded_certs', or 'prefer_wildcard'")
return "", d.Errf("auto_https must be one of 'off', 'disable_redirects', 'disable_certs', or 'ignore_loaded_certs'")
}
}
return val, nil
@@ -472,6 +482,10 @@ func unmarshalCaddyfileMetricsOptions(d *caddyfile.Dispenser) (any, error) {
switch d.Val() {
case "per_host":
metrics.PerHost = true
case "observe_catchall_hosts":
metrics.ObserveCatchallHosts = true
case "otlp":
metrics.OTLP = true
default:
return nil, d.Errf("unrecognized servers option '%s'", d.Val())
}
@@ -623,3 +637,22 @@ func parseOptECH(d *caddyfile.Dispenser, _ any) (any, error) {
return ech, nil
}
func parseOptRenewalWindowRatio(d *caddyfile.Dispenser, _ any) (any, error) {
d.Next() // consume option name
if !d.Next() {
return 0, d.ArgErr()
}
val := d.Val()
ratio, err := strconv.ParseFloat(val, 64)
if err != nil {
return 0, d.Errf("parsing renewal_window_ratio: %v", err)
}
if ratio <= 0 || ratio >= 1 {
return 0, d.Errf("renewal_window_ratio must be between 0 and 1 (exclusive)")
}
if d.Next() {
return 0, d.ArgErr()
}
return ratio, nil
}
+229
View File
@@ -1,9 +1,13 @@
package httpcaddyfile
import (
"encoding/json"
"testing"
"time"
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
"github.com/caddyserver/caddy/v2/modules/caddytls"
_ "github.com/caddyserver/caddy/v2/modules/logging"
)
@@ -62,3 +66,228 @@ func TestGlobalLogOptionSyntax(t *testing.T) {
}
}
}
func TestGlobalResolversOption(t *testing.T) {
tests := []struct {
name string
input string
expectResolvers []string
expectError bool
}{
{
name: "single resolver",
input: `{
tls_resolvers 1.1.1.1
}
example.com {
}`,
expectResolvers: []string{"1.1.1.1"},
expectError: false,
},
{
name: "two resolvers",
input: `{
tls_resolvers 1.1.1.1 8.8.8.8
}
example.com {
}`,
expectResolvers: []string{"1.1.1.1", "8.8.8.8"},
expectError: false,
},
{
name: "multiple resolvers",
input: `{
tls_resolvers 1.1.1.1 8.8.8.8 9.9.9.9
}
example.com {
}`,
expectResolvers: []string{"1.1.1.1", "8.8.8.8", "9.9.9.9"},
expectError: false,
},
{
name: "no resolvers specified",
input: `{
}
example.com {
}`,
expectResolvers: nil,
expectError: false,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
adapter := caddyfile.Adapter{
ServerType: ServerType{},
}
out, _, err := adapter.Adapt([]byte(tc.input), nil)
if (err != nil) != tc.expectError {
t.Errorf("error expectation failed. Expected error: %v, got: %v", tc.expectError, err)
return
}
if tc.expectError {
return
}
// Parse the output JSON to check resolvers
var config struct {
Apps struct {
TLS *caddytls.TLS `json:"tls"`
} `json:"apps"`
}
if err := json.Unmarshal(out, &config); err != nil {
t.Errorf("failed to unmarshal output: %v", err)
return
}
// Check if resolvers match expected
if config.Apps.TLS == nil {
if tc.expectResolvers != nil {
t.Errorf("Expected TLS config with resolvers %v, but TLS config is nil", tc.expectResolvers)
}
return
}
actualResolvers := config.Apps.TLS.Resolvers
if len(tc.expectResolvers) == 0 && len(actualResolvers) == 0 {
return // Both empty, ok
}
if len(actualResolvers) != len(tc.expectResolvers) {
t.Errorf("Expected %d resolvers, got %d. Expected: %v, got: %v", len(tc.expectResolvers), len(actualResolvers), tc.expectResolvers, actualResolvers)
return
}
for j, expected := range tc.expectResolvers {
if actualResolvers[j] != expected {
t.Errorf("Resolver %d mismatch. Expected: %s, got: %s", j, expected, actualResolvers[j])
}
}
})
}
}
func TestGlobalCertIssuerAppliesToImplicitACMEIssuer(t *testing.T) {
adapter := caddyfile.Adapter{
ServerType: ServerType{},
}
input := `{
cert_issuer acme {
disable_tlsalpn_challenge
}
}
report.company.intern {
tls {
ca https://deglacme01.company.intern/acme/acme/directory
ca_root /etc/certs/company_root2.crt
}
respond "ok"
}`
out, _, err := adapter.Adapt([]byte(input), nil)
if err != nil {
t.Fatalf("adapting caddyfile: %v", err)
}
var config struct {
Apps struct {
TLS *caddytls.TLS `json:"tls"`
} `json:"apps"`
}
if err := json.Unmarshal(out, &config); err != nil {
t.Fatalf("unmarshaling adapted config: %v", err)
}
if config.Apps.TLS == nil || config.Apps.TLS.Automation == nil {
t.Fatal("expected tls automation config")
}
var subjectPolicy *caddytls.AutomationPolicy
for _, ap := range config.Apps.TLS.Automation.Policies {
if len(ap.SubjectsRaw) == 1 && ap.SubjectsRaw[0] == "report.company.intern" {
subjectPolicy = ap
break
}
}
if subjectPolicy == nil {
t.Fatal("expected subject-specific automation policy")
}
if len(subjectPolicy.IssuersRaw) != 1 {
t.Fatalf("expected one issuer for subject-specific policy, got %d", len(subjectPolicy.IssuersRaw))
}
var issuer caddytls.ACMEIssuer
if err := json.Unmarshal(subjectPolicy.IssuersRaw[0], &issuer); err != nil {
t.Fatalf("unmarshaling issuer: %v", err)
}
if issuer.CA != "https://deglacme01.company.intern/acme/acme/directory" {
t.Fatalf("expected custom ACME CA, got %q", issuer.CA)
}
if len(issuer.TrustedRootsPEMFiles) != 1 || issuer.TrustedRootsPEMFiles[0] != "/etc/certs/company_root2.crt" {
t.Fatalf("expected trusted roots to include site CA root, got %v", issuer.TrustedRootsPEMFiles)
}
if issuer.Challenges == nil || issuer.Challenges.TLSALPN == nil || !issuer.Challenges.TLSALPN.Disabled {
t.Fatalf("expected tls-alpn challenge to be disabled, got %#v", issuer.Challenges)
}
}
func TestMergeACMEIssuers(t *testing.T) {
base := &caddytls.ACMEIssuer{
Email: "ops@example.com",
Challenges: &caddytls.ChallengesConfig{
HTTP: &caddytls.HTTPChallengeConfig{
AlternatePort: 8080,
},
TLSALPN: &caddytls.TLSALPNChallengeConfig{
Disabled: true,
AlternatePort: 8443,
},
DNS: &caddytls.DNSChallengeConfig{
Resolvers: []string{"1.1.1.1"},
OverrideDomain: "_acme-challenge.example.net",
},
},
TrustedRootsPEMFiles: []string{"global.pem"},
}
overrides := &caddytls.ACMEIssuer{
CA: "https://deglacme01.company.intern/acme/acme/directory",
Challenges: &caddytls.ChallengesConfig{
HTTP: &caddytls.HTTPChallengeConfig{
Disabled: true,
},
DNS: &caddytls.DNSChallengeConfig{
PropagationTimeout: caddy.Duration(time.Minute),
},
},
TrustedRootsPEMFiles: []string{"site.pem"},
}
merged := mergeACMEIssuers(base, overrides)
if merged.CA != overrides.CA {
t.Fatalf("expected merged CA %q, got %q", overrides.CA, merged.CA)
}
if merged.Email != base.Email {
t.Fatalf("expected merged email %q, got %q", base.Email, merged.Email)
}
if len(merged.TrustedRootsPEMFiles) != 2 || merged.TrustedRootsPEMFiles[0] != "global.pem" || merged.TrustedRootsPEMFiles[1] != "site.pem" {
t.Fatalf("expected merged roots [global.pem site.pem], got %v", merged.TrustedRootsPEMFiles)
}
if merged.Challenges == nil || merged.Challenges.HTTP == nil || !merged.Challenges.HTTP.Disabled || merged.Challenges.HTTP.AlternatePort != 8080 {
t.Fatalf("expected merged HTTP challenge config to preserve alternate port and apply disable flag, got %#v", merged.Challenges)
}
if merged.Challenges.TLSALPN == nil || !merged.Challenges.TLSALPN.Disabled || merged.Challenges.TLSALPN.AlternatePort != 8443 {
t.Fatalf("expected merged TLS-ALPN challenge config to preserve global settings, got %#v", merged.Challenges)
}
if merged.Challenges.DNS == nil || merged.Challenges.DNS.PropagationTimeout != caddy.Duration(time.Minute) || len(merged.Challenges.DNS.Resolvers) != 1 || merged.Challenges.DNS.Resolvers[0] != "1.1.1.1" || merged.Challenges.DNS.OverrideDomain != "_acme-challenge.example.net" {
t.Fatalf("expected merged DNS challenge config to preserve global values and apply overrides, got %#v", merged.Challenges)
}
if base.CA != "" {
t.Fatalf("expected base issuer to remain unchanged, got CA %q", base.CA)
}
if len(base.TrustedRootsPEMFiles) != 1 || base.TrustedRootsPEMFiles[0] != "global.pem" {
t.Fatalf("expected base roots to remain unchanged, got %v", base.TrustedRootsPEMFiles)
}
}
+41 -6
View File
@@ -15,6 +15,9 @@
package httpcaddyfile
import (
"slices"
"strconv"
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddyconfig"
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
@@ -25,14 +28,16 @@ func init() {
RegisterGlobalOption("pki", parsePKIApp)
}
// parsePKIApp parses the global log option. Syntax:
// parsePKIApp parses the global pki option. Syntax:
//
// pki {
// ca [<id>] {
// name <name>
// root_cn <name>
// intermediate_cn <name>
// intermediate_lifetime <duration>
// name <name>
// root_cn <name>
// intermediate_cn <name>
// intermediate_lifetime <duration>
// maintenance_interval <duration>
// renewal_window_ratio <ratio>
// root {
// cert <path>
// key <path>
@@ -97,6 +102,26 @@ func parsePKIApp(d *caddyfile.Dispenser, existingVal any) (any, error) {
}
pkiCa.IntermediateLifetime = caddy.Duration(dur)
case "maintenance_interval":
if !d.NextArg() {
return nil, d.ArgErr()
}
dur, err := caddy.ParseDuration(d.Val())
if err != nil {
return nil, err
}
pkiCa.MaintenanceInterval = caddy.Duration(dur)
case "renewal_window_ratio":
if !d.NextArg() {
return nil, d.ArgErr()
}
ratio, err := strconv.ParseFloat(d.Val(), 64)
if err != nil || ratio <= 0 || ratio > 1 {
return nil, d.Errf("renewal_window_ratio must be a number in (0, 1], got %s", d.Val())
}
pkiCa.RenewalWindowRatio = ratio
case "root":
if pkiCa.Root == nil {
pkiCa.Root = new(caddypki.KeyPair)
@@ -178,6 +203,15 @@ func (st ServerType) buildPKIApp(
if _, ok := options["skip_install_trust"]; ok {
skipInstallTrust = true
}
// check if auto_https is off - in that case we should not create
// any PKI infrastructure even with skip_install_trust directive
autoHTTPS := []string{}
if ah, ok := options["auto_https"].([]string); ok {
autoHTTPS = ah
}
autoHTTPSOff := slices.Contains(autoHTTPS, "off")
falseBool := false
// Load the PKI app configured via global options
@@ -218,7 +252,8 @@ func (st ServerType) buildPKIApp(
// if there was no CAs defined in any of the servers,
// and we were requested to not install trust, then
// add one for the default/local CA to do so
if len(pkiApp.CAs) == 0 && skipInstallTrust {
// only if auto_https is not completely disabled
if len(pkiApp.CAs) == 0 && skipInstallTrust && !autoHTTPSOff {
ca := new(caddypki.CA)
ca.ID = caddypki.DefaultCAID
ca.InstallTrust = &falseBool
+86
View File
@@ -0,0 +1,86 @@
// 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 httpcaddyfile
import (
"encoding/json"
"testing"
"time"
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
)
func TestParsePKIApp_maintenanceIntervalAndRenewalWindowRatio(t *testing.T) {
input := `{
pki {
ca local {
maintenance_interval 5m
renewal_window_ratio 0.15
}
}
}
:8080 {
}
`
adapter := caddyfile.Adapter{ServerType: ServerType{}}
out, _, err := adapter.Adapt([]byte(input), nil)
if err != nil {
t.Fatalf("Adapt failed: %v", err)
}
var cfg struct {
Apps struct {
PKI struct {
CertificateAuthorities map[string]struct {
MaintenanceInterval int64 `json:"maintenance_interval,omitempty"`
RenewalWindowRatio float64 `json:"renewal_window_ratio,omitempty"`
} `json:"certificate_authorities,omitempty"`
} `json:"pki,omitempty"`
} `json:"apps"`
}
if err := json.Unmarshal(out, &cfg); err != nil {
t.Fatalf("unmarshal config: %v", err)
}
ca, ok := cfg.Apps.PKI.CertificateAuthorities["local"]
if !ok {
t.Fatal("expected certificate_authorities.local to exist")
}
wantInterval := 5 * time.Minute.Nanoseconds()
if ca.MaintenanceInterval != wantInterval {
t.Errorf("maintenance_interval = %d, want %d (5m)", ca.MaintenanceInterval, wantInterval)
}
if ca.RenewalWindowRatio != 0.15 {
t.Errorf("renewal_window_ratio = %v, want 0.15", ca.RenewalWindowRatio)
}
}
func TestParsePKIApp_renewalWindowRatioInvalid(t *testing.T) {
input := `{
pki {
ca local {
renewal_window_ratio 1.5
}
}
}
:8080 {
}
`
adapter := caddyfile.Adapter{ServerType: ServerType{}}
_, _, err := adapter.Adapt([]byte(input), nil)
if err == nil {
t.Error("expected error for renewal_window_ratio > 1")
}
}
+88 -17
View File
@@ -18,6 +18,7 @@ import (
"encoding/json"
"fmt"
"slices"
"strconv"
"github.com/dustin/go-humanize"
@@ -35,23 +36,30 @@ type serverOptions struct {
ListenerAddress string
// These will all map 1:1 to the caddyhttp.Server struct
Name string
ListenerWrappersRaw []json.RawMessage
ReadTimeout caddy.Duration
ReadHeaderTimeout caddy.Duration
WriteTimeout caddy.Duration
IdleTimeout caddy.Duration
KeepAliveInterval caddy.Duration
MaxHeaderBytes int
EnableFullDuplex bool
Protocols []string
StrictSNIHost *bool
TrustedProxiesRaw json.RawMessage
TrustedProxiesStrict int
ClientIPHeaders []string
ShouldLogCredentials bool
Metrics *caddyhttp.Metrics
Trace bool // TODO: EXPERIMENTAL
Name string
ListenerWrappersRaw []json.RawMessage
PacketConnWrappersRaw []json.RawMessage
ReadTimeout caddy.Duration
ReadHeaderTimeout caddy.Duration
WriteTimeout caddy.Duration
IdleTimeout caddy.Duration
KeepAliveInterval caddy.Duration
KeepAliveIdle caddy.Duration
KeepAliveCount int
MaxHeaderBytes int
EnableFullDuplex bool
Protocols []string
StrictSNIHost *bool
TrustedProxiesRaw json.RawMessage
TrustedProxiesStrict int
TrustedProxiesUnix bool
ClientIPHeaders []string
ShouldLogCredentials bool
Metrics *caddyhttp.Metrics
Trace bool // TODO: EXPERIMENTAL
// If set, overrides whether QUIC listeners allow 0-RTT (early data).
// If nil, the default behavior is used (currently allowed).
Allow0RTT *bool
}
func unmarshalCaddyfileServerOptions(d *caddyfile.Dispenser) (any, error) {
@@ -95,6 +103,26 @@ func unmarshalCaddyfileServerOptions(d *caddyfile.Dispenser) (any, error) {
serverOpts.ListenerWrappersRaw = append(serverOpts.ListenerWrappersRaw, jsonListenerWrapper)
}
case "packet_conn_wrappers":
for nesting := d.Nesting(); d.NextBlock(nesting); {
modID := "caddy.packetconns." + d.Val()
unm, err := caddyfile.UnmarshalModule(d, modID)
if err != nil {
return nil, err
}
packetConnWrapper, ok := unm.(caddy.PacketConnWrapper)
if !ok {
return nil, fmt.Errorf("module %s (%T) is not a packet conn wrapper", modID, unm)
}
jsonPacketConnWrapper := caddyconfig.JSONModuleObject(
packetConnWrapper,
"wrapper",
packetConnWrapper.(caddy.Module).CaddyModule().ID.Name(),
nil,
)
serverOpts.PacketConnWrappersRaw = append(serverOpts.PacketConnWrappersRaw, jsonPacketConnWrapper)
}
case "timeouts":
for nesting := d.Nesting(); d.NextBlock(nesting); {
switch d.Val() {
@@ -142,6 +170,7 @@ func unmarshalCaddyfileServerOptions(d *caddyfile.Dispenser) (any, error) {
return nil, d.Errf("unrecognized timeouts option '%s'", d.Val())
}
}
case "keepalive_interval":
if !d.NextArg() {
return nil, d.ArgErr()
@@ -152,6 +181,26 @@ func unmarshalCaddyfileServerOptions(d *caddyfile.Dispenser) (any, error) {
}
serverOpts.KeepAliveInterval = caddy.Duration(dur)
case "keepalive_idle":
if !d.NextArg() {
return nil, d.ArgErr()
}
dur, err := caddy.ParseDuration(d.Val())
if err != nil {
return nil, d.Errf("parsing keepalive idle duration: %v", err)
}
serverOpts.KeepAliveIdle = caddy.Duration(dur)
case "keepalive_count":
if !d.NextArg() {
return nil, d.ArgErr()
}
cnt, err := strconv.ParseInt(d.Val(), 10, 32)
if err != nil {
return nil, d.Errf("parsing keepalive count int: %v", err)
}
serverOpts.KeepAliveCount = int(cnt)
case "max_header_size":
var sizeStr string
if !d.AllArgs(&sizeStr) {
@@ -227,6 +276,12 @@ func unmarshalCaddyfileServerOptions(d *caddyfile.Dispenser) (any, error) {
}
serverOpts.TrustedProxiesStrict = 1
case "trusted_proxies_unix":
if d.NextArg() {
return nil, d.ArgErr()
}
serverOpts.TrustedProxiesUnix = true
case "client_ip_headers":
headers := d.RemainingArgs()
for _, header := range headers {
@@ -257,6 +312,17 @@ func unmarshalCaddyfileServerOptions(d *caddyfile.Dispenser) (any, error) {
}
serverOpts.Trace = true
case "0rtt":
// only supports "off" for now
if !d.NextArg() {
return nil, d.ArgErr()
}
if d.Val() != "off" {
return nil, d.Errf("unsupported 0rtt argument '%s' (only 'off' is supported)", d.Val())
}
boolVal := false
serverOpts.Allow0RTT = &boolVal
default:
return nil, d.Errf("unrecognized servers option '%s'", d.Val())
}
@@ -304,11 +370,14 @@ func applyServerOptions(
// set all the options
server.ListenerWrappersRaw = opts.ListenerWrappersRaw
server.PacketConnWrappersRaw = opts.PacketConnWrappersRaw
server.ReadTimeout = opts.ReadTimeout
server.ReadHeaderTimeout = opts.ReadHeaderTimeout
server.WriteTimeout = opts.WriteTimeout
server.IdleTimeout = opts.IdleTimeout
server.KeepAliveInterval = opts.KeepAliveInterval
server.KeepAliveIdle = opts.KeepAliveIdle
server.KeepAliveCount = opts.KeepAliveCount
server.MaxHeaderBytes = opts.MaxHeaderBytes
server.EnableFullDuplex = opts.EnableFullDuplex
server.Protocols = opts.Protocols
@@ -316,7 +385,9 @@ func applyServerOptions(
server.TrustedProxiesRaw = opts.TrustedProxiesRaw
server.ClientIPHeaders = opts.ClientIPHeaders
server.TrustedProxiesStrict = opts.TrustedProxiesStrict
server.TrustedProxiesUnix = opts.TrustedProxiesUnix
server.Metrics = opts.Metrics
server.Allow0RTT = opts.Allow0RTT
if opts.ShouldLogCredentials {
if server.Logs == nil {
server.Logs = new(caddyhttp.ServerLogConfig)
+3
View File
@@ -64,10 +64,13 @@ func placeholderShorthands() []string {
"{orig_?query}", "{http.request.orig_uri.prefixed_query}",
"{method}", "{http.request.method}",
"{uri}", "{http.request.uri}",
"{%uri}", "{http.request.uri_escaped}",
"{path}", "{http.request.uri.path}",
"{%path}", "{http.request.uri.path_escaped}",
"{dir}", "{http.request.uri.path.dir}",
"{file}", "{http.request.uri.path.file}",
"{query}", "{http.request.uri.query}",
"{%query}", "{http.request.uri.query_escaped}",
"{?query}", "{http.request.uri.prefixed_query}",
"{remote}", "{http.request.remote}",
"{remote_host}", "{http.request.remote.host}",
+356 -68
View File
@@ -92,26 +92,8 @@ func (st ServerType) buildTLSApp(
tlsApp.Automation.Policies = append(tlsApp.Automation.Policies, catchAllAP)
}
var wildcardHosts []string // collect all hosts that have a wildcard in them, and aren't HTTP
forcedAutomatedNames := make(map[string]struct{}) // explicitly configured to be automated, even if covered by a wildcard
for _, p := range pairings {
var addresses []string
for _, addressWithProtocols := range p.addressesWithProtocols {
addresses = append(addresses, addressWithProtocols.address)
}
if !listenersUseAnyPortOtherThan(addresses, httpPort) {
continue
}
for _, sblock := range p.serverBlocks {
for _, addr := range sblock.parsedKeys {
if strings.HasPrefix(addr.Host, "*.") {
wildcardHosts = append(wildcardHosts, addr.Host[2:])
}
}
}
}
for _, p := range pairings {
// avoid setting up TLS automation policies for a server that is HTTP-only
var addresses []string
@@ -135,12 +117,6 @@ func (st ServerType) buildTLSApp(
return nil, warnings, err
}
// make a plain copy so we can compare whether we made any changes
apCopy, err := newBaseAutomationPolicy(options, warnings, true)
if err != nil {
return nil, warnings, err
}
sblockHosts := sblock.hostsFromKeys(false)
if len(sblockHosts) == 0 && catchAllAP != nil {
ap = catchAllAP
@@ -167,6 +143,12 @@ func (st ServerType) buildTLSApp(
ap.KeyType = keyTypeVals[0].Value.(string)
}
if renewalWindowRatioVals, ok := sblock.pile["tls.renewal_window_ratio"]; ok {
ap.RenewalWindowRatio = renewalWindowRatioVals[0].Value.(float64)
} else if globalRenewalWindowRatio, ok := options["renewal_window_ratio"]; ok {
ap.RenewalWindowRatio = globalRenewalWindowRatio.(float64)
}
// certificate issuers
if issuerVals, ok := sblock.pile["tls.cert_issuer"]; ok {
var issuers []certmagic.Issuer
@@ -253,16 +235,6 @@ func (st ServerType) buildTLSApp(
hostsNotHTTP := sblock.hostsFromKeysNotHTTP(httpPort)
sort.Strings(hostsNotHTTP) // solely for deterministic test results
// if the we prefer wildcards and the AP is unchanged,
// then we can skip this AP because it should be covered
// by an AP with a wildcard
if slices.Contains(autoHTTPS, "prefer_wildcard") {
if hostsCoveredByWildcard(hostsNotHTTP, wildcardHosts) &&
reflect.DeepEqual(ap, apCopy) {
continue
}
}
// associate our new automation policy with this server block's hosts
ap.SubjectsRaw = hostsNotHTTP
@@ -362,6 +334,11 @@ func (st ServerType) buildTLSApp(
tlsApp.DNSRaw = caddyconfig.JSONModuleObject(globalDNS, "name", globalDNS.(caddy.Module).CaddyModule().ID.Name(), nil)
}
// set up "global" (to the TLS app) DNS resolvers config
if globalResolvers, ok := options["tls_resolvers"]; ok && globalResolvers != nil {
tlsApp.Resolvers = globalResolvers.([]string)
}
// set up ECH from Caddyfile options
if ech, ok := options["ech"].(*caddytls.ECH); ok {
tlsApp.EncryptedClientHello = ech
@@ -554,6 +531,7 @@ func fillInGlobalACMEDefaults(issuer certmagic.Issuer, options map[string]any) e
globalPreferredChains := options["preferred_chains"]
globalCertLifetime := options["cert_lifetime"]
globalHTTPPort, globalHTTPSPort := options["http_port"], options["https_port"]
globalDefaultBind := options["default_bind"]
if globalEmail != nil && acmeIssuer.Email == "" {
acmeIssuer.Email = globalEmail.(string)
@@ -564,21 +542,20 @@ func fillInGlobalACMEDefaults(issuer certmagic.Issuer, options map[string]any) e
if globalACMECARoot != nil && !slices.Contains(acmeIssuer.TrustedRootsPEMFiles, globalACMECARoot.(string)) {
acmeIssuer.TrustedRootsPEMFiles = append(acmeIssuer.TrustedRootsPEMFiles, globalACMECARoot.(string))
}
if globalACMEDNSok && (acmeIssuer.Challenges == nil || acmeIssuer.Challenges.DNS == nil) {
if globalACMEDNS == nil {
globalACMEDNS = options["dns"]
if globalACMEDNS == nil {
return fmt.Errorf("acme_dns specified without DNS provider config, but no provider specified with 'dns' global option")
}
if globalACMEDNSok && (acmeIssuer.Challenges == nil || acmeIssuer.Challenges.DNS == nil || acmeIssuer.Challenges.DNS.ProviderRaw == nil) {
globalDNS := options["dns"]
if globalDNS == nil && globalACMEDNS == nil {
return fmt.Errorf("acme_dns specified without DNS provider config, but no provider specified with 'dns' global option")
}
acmeIssuer.Challenges = &caddytls.ChallengesConfig{
DNS: new(caddytls.DNSChallengeConfig),
if acmeIssuer.Challenges == nil {
acmeIssuer.Challenges = new(caddytls.ChallengesConfig)
}
} else if globalACMEDNS != nil {
acmeIssuer.Challenges = &caddytls.ChallengesConfig{
DNS: &caddytls.DNSChallengeConfig{
ProviderRaw: caddyconfig.JSONModuleObject(globalACMEDNS, "name", globalACMEDNS.(caddy.Module).CaddyModule().ID.Name(), nil),
},
if acmeIssuer.Challenges.DNS == nil {
acmeIssuer.Challenges.DNS = new(caddytls.DNSChallengeConfig)
}
if globalACMEDNS != nil && acmeIssuer.Challenges.DNS.ProviderRaw == nil {
// Set a global DNS provider if `acme_dns` is set
acmeIssuer.Challenges.DNS.ProviderRaw = caddyconfig.JSONModuleObject(globalACMEDNS, "name", globalACMEDNS.(caddy.Module).CaddyModule().ID.Name(), nil)
}
}
if globalACMEEAB != nil && acmeIssuer.ExternalAccount == nil {
@@ -606,12 +583,318 @@ func fillInGlobalACMEDefaults(issuer certmagic.Issuer, options map[string]any) e
}
acmeIssuer.Challenges.TLSALPN.AlternatePort = globalHTTPSPort.(int)
}
// If BindHost is still unset, fall back to the first default_bind address if set
// This avoids binding the automation policy to the wildcard socket, which is unexpected behavior when a more selective socket is specified via default_bind
// In BSD it is valid to bind to the wildcard socket even though a more selective socket is already open (still unexpected behavior by the caller though)
// In Linux the same call will error with EADDRINUSE whenever the listener for the automation policy is opened
if acmeIssuer.Challenges == nil || (acmeIssuer.Challenges.DNS == nil && acmeIssuer.Challenges.BindHost == "") {
if defBinds, ok := globalDefaultBind.([]ConfigValue); ok && len(defBinds) > 0 {
if abp, ok := defBinds[0].Value.(addressesWithProtocols); ok && len(abp.addresses) > 0 {
if acmeIssuer.Challenges == nil {
acmeIssuer.Challenges = new(caddytls.ChallengesConfig)
}
acmeIssuer.Challenges.BindHost = abp.addresses[0]
}
}
}
if globalCertLifetime != nil && acmeIssuer.CertificateLifetime == 0 {
acmeIssuer.CertificateLifetime = globalCertLifetime.(caddy.Duration)
}
// apply global resolvers if DNS challenge is configured and resolvers are not already set
globalResolvers := options["tls_resolvers"]
if globalResolvers != nil && acmeIssuer.Challenges != nil && acmeIssuer.Challenges.DNS != nil {
// Check if DNS challenge is actually configured
hasDNSChallenge := globalACMEDNSok || acmeIssuer.Challenges.DNS.ProviderRaw != nil
if hasDNSChallenge && len(acmeIssuer.Challenges.DNS.Resolvers) == 0 {
acmeIssuer.Challenges.DNS.Resolvers = globalResolvers.([]string)
}
}
return nil
}
// implicitACMEIssuers returns the issuers to use for ACME-related tls
// shortcuts such as ca, ca_root, and dns. If any global cert_issuer options
// configure ACME issuers, those become the templates for the local shortcut
// configuration; otherwise, default ACME issuers are used.
func implicitACMEIssuers(h Helper, acmeIssuer *caddytls.ACMEIssuer) []certmagic.Issuer {
globalIssuers, _ := h.Option("cert_issuer").([]certmagic.Issuer)
var implicitIssuers []certmagic.Issuer
for _, issuer := range globalIssuers {
acmeWrapper, ok := issuer.(acmeCapable)
if !ok {
continue
}
baseIssuer := acmeWrapper.GetACMEIssuer()
if baseIssuer == nil {
continue
}
implicitIssuers = append(implicitIssuers, mergeACMEIssuers(baseIssuer, acmeIssuer))
}
if len(implicitIssuers) > 0 {
return implicitIssuers
}
// If an ACME CA endpoint was set locally, the user expects to use only that
// CA rather than the usual default fallback issuers.
defaultIssuers := caddytls.DefaultIssuers(acmeIssuer.Email)
if acmeIssuer.CA != "" {
defaultIssuers = []certmagic.Issuer{new(caddytls.ACMEIssuer)}
}
implicitIssuers = make([]certmagic.Issuer, 0, len(defaultIssuers))
for _, issuer := range defaultIssuers {
acmeWrapper, ok := issuer.(acmeCapable)
if !ok {
implicitIssuers = append(implicitIssuers, issuer)
continue
}
baseIssuer := acmeWrapper.GetACMEIssuer()
if baseIssuer == nil {
implicitIssuers = append(implicitIssuers, issuer)
continue
}
implicitIssuers = append(implicitIssuers, mergeACMEIssuers(baseIssuer, acmeIssuer))
}
return implicitIssuers
}
func mergeACMEIssuers(base, overrides *caddytls.ACMEIssuer) *caddytls.ACMEIssuer {
if base == nil {
return cloneACMEIssuer(overrides)
}
merged := cloneACMEIssuer(base)
if overrides == nil {
return merged
}
if overrides.CA != "" {
merged.CA = overrides.CA
}
if overrides.TestCA != "" {
merged.TestCA = overrides.TestCA
}
if overrides.Email != "" {
merged.Email = overrides.Email
}
if overrides.Profile != "" {
merged.Profile = overrides.Profile
}
if overrides.AccountKey != "" {
merged.AccountKey = overrides.AccountKey
}
if overrides.ExternalAccount != nil {
merged.ExternalAccount = cloneACMEEAB(overrides.ExternalAccount)
}
if overrides.ACMETimeout != 0 {
merged.ACMETimeout = overrides.ACMETimeout
}
if len(overrides.TrustedRootsPEMFiles) > 0 {
merged.TrustedRootsPEMFiles = appendUniqueStrings(merged.TrustedRootsPEMFiles, overrides.TrustedRootsPEMFiles...)
}
if overrides.PreferredChains != nil {
merged.PreferredChains = cloneChainPreference(overrides.PreferredChains)
}
if overrides.CertificateLifetime != 0 {
merged.CertificateLifetime = overrides.CertificateLifetime
}
if len(overrides.NetworkProxyRaw) > 0 {
merged.NetworkProxyRaw = slices.Clone(overrides.NetworkProxyRaw)
}
merged.Challenges = mergeChallengesConfig(merged.Challenges, overrides.Challenges)
return merged
}
func mergeChallengesConfig(base, overrides *caddytls.ChallengesConfig) *caddytls.ChallengesConfig {
if base == nil {
return cloneChallengesConfig(overrides)
}
merged := cloneChallengesConfig(base)
if overrides == nil {
return merged
}
merged.HTTP = mergeHTTPChallengeConfig(merged.HTTP, overrides.HTTP)
merged.TLSALPN = mergeTLSALPNChallengeConfig(merged.TLSALPN, overrides.TLSALPN)
merged.DNS = mergeDNSChallengeConfig(merged.DNS, overrides.DNS)
if overrides.BindHost != "" {
merged.BindHost = overrides.BindHost
}
if overrides.Distributed != nil {
value := *overrides.Distributed
merged.Distributed = &value
}
return merged
}
func mergeHTTPChallengeConfig(base, overrides *caddytls.HTTPChallengeConfig) *caddytls.HTTPChallengeConfig {
if base == nil {
return cloneHTTPChallengeConfig(overrides)
}
merged := cloneHTTPChallengeConfig(base)
if overrides == nil {
return merged
}
if overrides.Disabled {
merged.Disabled = true
}
if overrides.AlternatePort != 0 {
merged.AlternatePort = overrides.AlternatePort
}
return merged
}
func mergeTLSALPNChallengeConfig(base, overrides *caddytls.TLSALPNChallengeConfig) *caddytls.TLSALPNChallengeConfig {
if base == nil {
return cloneTLSALPNChallengeConfig(overrides)
}
merged := cloneTLSALPNChallengeConfig(base)
if overrides == nil {
return merged
}
if overrides.Disabled {
merged.Disabled = true
}
if overrides.AlternatePort != 0 {
merged.AlternatePort = overrides.AlternatePort
}
return merged
}
func mergeDNSChallengeConfig(base, overrides *caddytls.DNSChallengeConfig) *caddytls.DNSChallengeConfig {
if base == nil {
return cloneDNSChallengeConfig(overrides)
}
merged := cloneDNSChallengeConfig(base)
if overrides == nil {
return merged
}
if len(overrides.ProviderRaw) > 0 {
merged.ProviderRaw = slices.Clone(overrides.ProviderRaw)
}
if overrides.PropagationDelay != 0 {
merged.PropagationDelay = overrides.PropagationDelay
}
if overrides.PropagationTimeout != 0 {
merged.PropagationTimeout = overrides.PropagationTimeout
}
if overrides.Resolvers != nil {
merged.Resolvers = slices.Clone(overrides.Resolvers)
}
if overrides.OverrideDomain != "" {
merged.OverrideDomain = overrides.OverrideDomain
}
if overrides.TTL != 0 {
merged.TTL = overrides.TTL
}
return merged
}
func cloneACMEIssuer(iss *caddytls.ACMEIssuer) *caddytls.ACMEIssuer {
if iss == nil {
return nil
}
cloned := *iss
cloned.Challenges = cloneChallengesConfig(iss.Challenges)
cloned.ExternalAccount = cloneACMEEAB(iss.ExternalAccount)
cloned.TrustedRootsPEMFiles = slices.Clone(iss.TrustedRootsPEMFiles)
cloned.PreferredChains = cloneChainPreference(iss.PreferredChains)
cloned.NetworkProxyRaw = slices.Clone(iss.NetworkProxyRaw)
return &cloned
}
func cloneChallengesConfig(cfg *caddytls.ChallengesConfig) *caddytls.ChallengesConfig {
if cfg == nil {
return nil
}
cloned := *cfg
cloned.HTTP = cloneHTTPChallengeConfig(cfg.HTTP)
cloned.TLSALPN = cloneTLSALPNChallengeConfig(cfg.TLSALPN)
cloned.DNS = cloneDNSChallengeConfig(cfg.DNS)
if cfg.Distributed != nil {
value := *cfg.Distributed
cloned.Distributed = &value
}
return &cloned
}
func cloneHTTPChallengeConfig(cfg *caddytls.HTTPChallengeConfig) *caddytls.HTTPChallengeConfig {
if cfg == nil {
return nil
}
cloned := *cfg
return &cloned
}
func cloneTLSALPNChallengeConfig(cfg *caddytls.TLSALPNChallengeConfig) *caddytls.TLSALPNChallengeConfig {
if cfg == nil {
return nil
}
cloned := *cfg
return &cloned
}
func cloneDNSChallengeConfig(cfg *caddytls.DNSChallengeConfig) *caddytls.DNSChallengeConfig {
if cfg == nil {
return nil
}
cloned := *cfg
cloned.ProviderRaw = slices.Clone(cfg.ProviderRaw)
cloned.Resolvers = slices.Clone(cfg.Resolvers)
return &cloned
}
func cloneACMEEAB(eab *acme.EAB) *acme.EAB {
if eab == nil {
return nil
}
cloned := *eab
return &cloned
}
func cloneChainPreference(pref *caddytls.ChainPreference) *caddytls.ChainPreference {
if pref == nil {
return nil
}
cloned := *pref
cloned.RootCommonName = slices.Clone(pref.RootCommonName)
cloned.AnyCommonName = slices.Clone(pref.AnyCommonName)
if pref.Smallest != nil {
value := *pref.Smallest
cloned.Smallest = &value
}
return &cloned
}
func appendUniqueStrings(existing []string, additions ...string) []string {
for _, value := range additions {
if !slices.Contains(existing, value) {
existing = append(existing, value)
}
}
return existing
}
// newBaseAutomationPolicy returns a new TLS automation policy that gets
// its values from the global options map. It should be used as the base
// for any other automation policies. A nil policy (and no error) will be
@@ -626,7 +909,8 @@ func newBaseAutomationPolicy(
_, hasLocalCerts := options["local_certs"]
keyType, hasKeyType := options["key_type"]
ocspStapling, hasOCSPStapling := options["ocsp_stapling"]
hasGlobalAutomationOpts := hasIssuers || hasLocalCerts || hasKeyType || hasOCSPStapling
renewalWindowRatio, hasRenewalWindowRatio := options["renewal_window_ratio"]
hasGlobalAutomationOpts := hasIssuers || hasLocalCerts || hasKeyType || hasOCSPStapling || hasRenewalWindowRatio
globalACMECA := options["acme_ca"]
globalACMECARoot := options["acme_ca_root"]
@@ -673,6 +957,10 @@ func newBaseAutomationPolicy(
ap.OCSPOverrides = ocspConfig.ResponderOverrides
}
if hasRenewalWindowRatio {
ap.RenewalWindowRatio = renewalWindowRatio.(float64)
}
return ap, nil
}
@@ -693,14 +981,31 @@ func consolidateAutomationPolicies(aps []*caddytls.AutomationPolicy) []*caddytls
emptyAPCount := 0
origLenAPs := len(aps)
// compute the number of empty policies (disregarding subjects) - see #4128
// while we're at it,
emptyAP := new(caddytls.AutomationPolicy)
for i := 0; i < len(aps); i++ {
emptyAP.SubjectsRaw = aps[i].SubjectsRaw
emptyAP.ManagersRaw = nil
if reflect.DeepEqual(aps[i], emptyAP) {
// AP is empty
emptyAPCount++
if !automationPolicyHasAllPublicNames(aps[i]) {
// if this automation policy has internal names, we might as well remove it
// so auto-https can implicitly use the internal issuer
// see if this AP shadows something later
shadowIdx := automationPolicyShadows(i, aps)
emptyAP.SubjectsRaw = nil
if shadowIdx >= 0 {
emptyAP.SubjectsRaw = aps[shadowIdx].SubjectsRaw
// allow the later policy, which is likely for a wildcard, to have cert
// managers ("get_certificate"), since wildcards now cover specific
// subdomains by default, when configured (see discussion in #7559)
emptyAP.ManagersRaw = aps[shadowIdx].ManagersRaw
}
// if this is the last AP, we can delete it, since auto-https should
// pick it up; if it shadows something later that is also empty, we
// can similarly delete this; but if it shadows something that is NOT
// empty, we must not delete it since the shadowing has a purpose
if i == len(aps)-1 || (shadowIdx >= 0 && reflect.DeepEqual(aps[shadowIdx], emptyAP)) {
aps = slices.Delete(aps, i, i+1)
i--
}
@@ -834,20 +1139,3 @@ func automationPolicyHasAllPublicNames(ap *caddytls.AutomationPolicy) bool {
func isTailscaleDomain(name string) bool {
return strings.HasSuffix(strings.ToLower(name), ".ts.net")
}
func hostsCoveredByWildcard(hosts []string, wildcards []string) bool {
if len(hosts) == 0 || len(wildcards) == 0 {
return false
}
for _, host := range hosts {
for _, wildcard := range wildcards {
if strings.HasPrefix(host, "*.") {
continue
}
if certmagic.MatchWildcard(host, "*."+wildcard) {
return true
}
}
}
return false
}
+2 -2
View File
@@ -136,7 +136,7 @@ func (hl HTTPLoader) LoadConfig(ctx caddy.Context) ([]byte, error) {
}
func attemptHttpCall(client *http.Client, request *http.Request) (*http.Response, error) {
resp, err := client.Do(request)
resp, err := client.Do(request) //nolint:gosec // no SSRF; comes from trusted config
if err != nil {
return nil, fmt.Errorf("problem calling http loader url: %v", err)
} else if resp.StatusCode < 200 || resp.StatusCode > 499 {
@@ -151,7 +151,7 @@ func doHttpCallWithRetries(ctx caddy.Context, client *http.Client, request *http
var err error
const maxAttempts = 10
for i := 0; i < maxAttempts; i++ {
for i := range maxAttempts {
resp, err = attemptHttpCall(client, request)
if err != nil && i < maxAttempts-1 {
select {
+8 -1
View File
@@ -106,7 +106,7 @@ func (adminLoad) handleLoad(w http.ResponseWriter, r *http.Request) error {
if err != nil {
caddy.Log().Named("admin.api.load").Error(err.Error())
}
_, _ = w.Write(respBody)
_, _ = w.Write(respBody) //nolint:gosec // false positive: no XSS here
}
body = result
}
@@ -121,6 +121,13 @@ func (adminLoad) handleLoad(w http.ResponseWriter, r *http.Request) error {
}
}
// If this request changed the config, clear the last
// config info we have stored, if it is different from
// the original source.
caddy.ClearLastConfigIfDifferent(
r.Header.Get("Caddy-Config-Source-File"),
r.Header.Get("Caddy-Config-Source-Adapter"))
caddy.Log().Named("admin.api").Info("load complete")
return nil
+25 -3
View File
@@ -187,7 +187,7 @@ func (tc *Tester) initServer(rawConfig string, configType string) error {
req.Header.Add("Content-Type", "text/"+configType)
}
res, err := client.Do(req)
res, err := client.Do(req) //nolint:gosec // no SSRF because URL is hard-coded to localhost, and port comes from config
if err != nil {
tc.t.Errorf("unable to contact caddy server. %s", err)
return err
@@ -279,7 +279,7 @@ func validateTestPrerequisites(tc *Tester) error {
return err
}
tc.t.Cleanup(func() {
os.Remove(f.Name())
os.Remove(f.Name()) //nolint:gosec // false positive, filename comes from std lib, no path traversal
})
if _, err := fmt.Fprintf(f, initConfig, tc.config.AdminPort); err != nil {
return err
@@ -362,6 +362,8 @@ func CreateTestingTransport() *http.Transport {
// AssertLoadError will load a config and expect an error
func AssertLoadError(t *testing.T, rawConfig string, configType string, expectedError string) {
t.Helper()
tc := NewTester(t)
err := tc.initServer(rawConfig, configType)
@@ -372,6 +374,8 @@ func AssertLoadError(t *testing.T, rawConfig string, configType string, expected
// AssertRedirect makes a request and asserts the redirection happens
func (tc *Tester) AssertRedirect(requestURI string, expectedToLocation string, expectedStatusCode int) *http.Response {
tc.t.Helper()
redirectPolicyFunc := func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
}
@@ -409,6 +413,8 @@ func (tc *Tester) AssertRedirect(requestURI string, expectedToLocation string, e
// CompareAdapt adapts a config and then compares it against an expected result
func CompareAdapt(t testing.TB, filename, rawConfig string, adapterName string, expectedResponse string) bool {
t.Helper()
cfgAdapter := caddyconfig.GetAdapter(adapterName)
if cfgAdapter == nil {
t.Logf("unrecognized config adapter '%s'", adapterName)
@@ -468,6 +474,8 @@ func CompareAdapt(t testing.TB, filename, rawConfig string, adapterName string,
// AssertAdapt adapts a config and then tests it against an expected result
func AssertAdapt(t testing.TB, rawConfig string, adapterName string, expectedResponse string) {
t.Helper()
ok := CompareAdapt(t, "Caddyfile", rawConfig, adapterName, expectedResponse)
if !ok {
t.Fail()
@@ -496,7 +504,9 @@ func applyHeaders(t testing.TB, req *http.Request, requestHeaders []string) {
// AssertResponseCode will execute the request and verify the status code, returns a response for additional assertions
func (tc *Tester) AssertResponseCode(req *http.Request, expectedStatusCode int) *http.Response {
resp, err := tc.Client.Do(req)
tc.t.Helper()
resp, err := tc.Client.Do(req) //nolint:gosec // no SSRFs demonstrated
if err != nil {
tc.t.Fatalf("failed to call server %s", err)
}
@@ -510,6 +520,8 @@ func (tc *Tester) AssertResponseCode(req *http.Request, expectedStatusCode int)
// AssertResponse request a URI and assert the status code and the body contains a string
func (tc *Tester) AssertResponse(req *http.Request, expectedStatusCode int, expectedBody string) (*http.Response, string) {
tc.t.Helper()
resp := tc.AssertResponseCode(req, expectedStatusCode)
defer resp.Body.Close()
@@ -531,6 +543,8 @@ func (tc *Tester) AssertResponse(req *http.Request, expectedStatusCode int, expe
// AssertGetResponse GET a URI and expect a statusCode and body text
func (tc *Tester) AssertGetResponse(requestURI string, expectedStatusCode int, expectedBody string) (*http.Response, string) {
tc.t.Helper()
req, err := http.NewRequest("GET", requestURI, nil)
if err != nil {
tc.t.Fatalf("unable to create request %s", err)
@@ -541,6 +555,8 @@ func (tc *Tester) AssertGetResponse(requestURI string, expectedStatusCode int, e
// AssertDeleteResponse request a URI and expect a statusCode and body text
func (tc *Tester) AssertDeleteResponse(requestURI string, expectedStatusCode int, expectedBody string) (*http.Response, string) {
tc.t.Helper()
req, err := http.NewRequest("DELETE", requestURI, nil)
if err != nil {
tc.t.Fatalf("unable to create request %s", err)
@@ -551,6 +567,8 @@ func (tc *Tester) AssertDeleteResponse(requestURI string, expectedStatusCode int
// AssertPostResponseBody POST to a URI and assert 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()
req, err := http.NewRequest("POST", requestURI, requestBody)
if err != nil {
tc.t.Errorf("failed to create request %s", err)
@@ -564,6 +582,8 @@ func (tc *Tester) AssertPostResponseBody(requestURI string, requestHeaders []str
// AssertPutResponseBody PUT to a URI and assert 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()
req, err := http.NewRequest("PUT", requestURI, requestBody)
if err != nil {
tc.t.Errorf("failed to create request %s", err)
@@ -577,6 +597,8 @@ func (tc *Tester) AssertPutResponseBody(requestURI string, requestHeaders []stri
// AssertPatchResponseBody PATCH to a URI and assert 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()
req, err := http.NewRequest("PATCH", requestURI, requestBody)
if err != nil {
tc.t.Errorf("failed to create request %s", err)
+116
View File
@@ -1,6 +1,7 @@
package caddytest
import (
"bytes"
"net/http"
"strings"
"testing"
@@ -126,3 +127,118 @@ func TestLoadUnorderedJSON(t *testing.T) {
}
tester.AssertResponseCode(req, 200)
}
func TestCheckID(t *testing.T) {
tester := NewTester(t)
tester.InitServer(`{
"admin": {
"listen": "localhost:2999"
},
"apps": {
"http": {
"http_port": 9080,
"servers": {
"s_server": {
"@id": "s_server",
"listen": [
":9080"
],
"routes": [
{
"handle": [
{
"handler": "static_response",
"body": "Hello"
}
]
}
]
}
}
}
}
}
`, "json")
headers := []string{"Content-Type:application/json"}
sServer1 := []byte(`{"@id":"s_server","listen":[":9080"],"routes":[{"@id":"route1","handle":[{"handler":"static_response","body":"Hello 2"}]}]}`)
// PUT to an existing ID should fail with a 409 conflict
tester.AssertPutResponseBody(
"http://localhost:2999/id/s_server",
headers,
bytes.NewBuffer(sServer1),
409,
`{"error":"[/config/apps/http/servers/s_server] key already exists: s_server"}`+"\n")
// POST replaces the object fully
tester.AssertPostResponseBody(
"http://localhost:2999/id/s_server",
headers,
bytes.NewBuffer(sServer1),
200,
"")
// Verify the server is running the new route
tester.AssertGetResponse(
"http://localhost:9080/",
200,
"Hello 2")
// Update the existing route to ensure IDs are handled correctly when replaced
tester.AssertPostResponseBody(
"http://localhost:2999/id/s_server",
headers,
bytes.NewBuffer([]byte(`{"@id":"s_server","listen":[":9080"],"routes":[{"@id":"route1","handle":[{"handler":"static_response","body":"Hello2"}],"match":[{"path":["/route_1/*"]}]}]}`)),
200,
"")
sServer2 := []byte(`{"@id":"s_server","listen":[":9080"],"routes":[{"@id":"route1","handle":[{"handler":"static_response","body":"Hello2"}],"match":[{"path":["/route_1/*"]}]}]}`)
// Identical patch should succeed and return 200 (config is unchanged branch)
tester.AssertPatchResponseBody(
"http://localhost:2999/id/s_server",
headers,
bytes.NewBuffer(sServer2),
200,
"")
route2 := []byte(`{"@id":"route2","handle": [{"handler": "static_response","body": "route2"}],"match":[{"path":["/route_2/*"]}]}`)
// Put a new route2 object before the route1 object due to the path of /id/route1
// Being translated to: /config/apps/http/servers/s_server/routes/0
tester.AssertPutResponseBody(
"http://localhost:2999/id/route1",
headers,
bytes.NewBuffer(route2),
200,
"")
// Verify that the whole config looks correct, now containing both route1 and route2
tester.AssertGetResponse(
"http://localhost:2999/config/",
200,
`{"admin":{"listen":"localhost:2999"},"apps":{"http":{"http_port":9080,"servers":{"s_server":{"@id":"s_server","listen":[":9080"],"routes":[{"@id":"route2","handle":[{"body":"route2","handler":"static_response"}],"match":[{"path":["/route_2/*"]}]},{"@id":"route1","handle":[{"body":"Hello2","handler":"static_response"}],"match":[{"path":["/route_1/*"]}]}]}}}}}`+"\n")
// Try to add another copy of route2 using POST to test duplicate ID handling
// Since the first route2 ended up at array index 0, and we are appending to the array, the index for the new element would be 2
tester.AssertPostResponseBody(
"http://localhost:2999/id/route2",
headers,
bytes.NewBuffer(route2),
400,
`{"error":"indexing config: duplicate ID 'route2' found at /config/apps/http/servers/s_server/routes/0 and /config/apps/http/servers/s_server/routes/2"}`+"\n")
// Use PATCH to modify an existing object successfully
tester.AssertPatchResponseBody(
"http://localhost:2999/id/route1",
headers,
bytes.NewBuffer([]byte(`{"@id":"route1","handle":[{"handler":"static_response","body":"route1"}],"match":[{"path":["/route_1/*"]}]}`)),
200,
"")
// Verify the PATCH updated the server state
tester.AssertGetResponse(
"http://localhost:9080/route_1/",
200,
"route1")
}
+2 -2
View File
@@ -51,7 +51,7 @@ func TestACMEServerWithDefaults(t *testing.T) {
Client: &acme.Client{
Directory: "https://acme.localhost:9443/acme/local/directory",
HTTPClient: tester.Client,
Logger: slog.New(zapslog.NewHandler(logger.Core())),
Logger: slog.New(zapslog.NewHandler(logger.Core(), zapslog.WithName("acmez"))),
},
ChallengeSolvers: map[string]acmez.Solver{
acme.ChallengeTypeHTTP01: &naiveHTTPSolver{logger: logger},
@@ -120,7 +120,7 @@ func TestACMEServerWithMismatchedChallenges(t *testing.T) {
Client: &acme.Client{
Directory: "https://acme.localhost:9443/acme/local/directory",
HTTPClient: tester.Client,
Logger: slog.New(zapslog.NewHandler(logger.Core())),
Logger: slog.New(zapslog.NewHandler(logger.Core(), zapslog.WithName("acmez"))),
},
ChallengeSolvers: map[string]acmez.Solver{
acme.ChallengeTypeHTTP01: &naiveHTTPSolver{logger: logger},
+2 -2
View File
@@ -127,7 +127,7 @@ func TestACMEServerAllowPolicy(t *testing.T) {
_, err := client.ObtainCertificateForSANs(ctx, account, certPrivateKey, []string{"not-matching.localhost"})
if err == nil {
t.Errorf("obtaining certificate for 'not-matching.localhost' domain")
} else if err != nil && !strings.Contains(err.Error(), "urn:ietf:params:acme:error:rejectedIdentifier") {
} else if !strings.Contains(err.Error(), "urn:ietf:params:acme:error:rejectedIdentifier") {
t.Logf("unexpected error: %v", err)
}
}
@@ -200,7 +200,7 @@ func TestACMEServerDenyPolicy(t *testing.T) {
_, err := client.ObtainCertificateForSANs(ctx, account, certPrivateKey, []string{"deny.localhost"})
if err == nil {
t.Errorf("obtaining certificate for 'deny.localhost' domain")
} else if err != nil && !strings.Contains(err.Error(), "urn:ietf:params:acme:error:rejectedIdentifier") {
} else if !strings.Contains(err.Error(), "urn:ietf:params:acme:error:rejectedIdentifier") {
t.Logf("unexpected error: %v", err)
}
}
+45
View File
@@ -55,6 +55,28 @@ func TestAutoHTTPtoHTTPSRedirectsExplicitPortDifferentFromHTTPSPort(t *testing.T
tester.AssertRedirect("http://localhost:9080/", "https://localhost:1234/", http.StatusPermanentRedirect)
}
func TestAutoHTTPtoHTTPSRedirectsPreferHTTPSPortOverAlternatePort(t *testing.T) {
tester := caddytest.NewTester(t)
tester.InitServer(`
{
skip_install_trust
admin localhost:2999
http_port 9080
https_port 9443
local_certs
}
localhost {
respond "Canonical"
}
localhost:10443 {
respond "Alternate"
}
`, "caddyfile")
tester.AssertRedirect("http://localhost:9080/", "https://localhost/", http.StatusPermanentRedirect)
}
func TestAutoHTTPRedirectsWithHTTPListenerFirstInAddresses(t *testing.T) {
tester := caddytest.NewTester(t)
tester.InitServer(`
@@ -143,3 +165,26 @@ func TestAutoHTTPRedirectsInsertedBeforeUserDefinedCatchAllWithNoExplicitHTTPSit
tester.AssertGetResponse("http://foo.localhost:9080/", 200, "Foo")
tester.AssertGetResponse("http://baz.localhost:9080/", 200, "Foo")
}
func TestAutoHTTPSRedirectSortingExactMatchOverWildcard(t *testing.T) {
tester := caddytest.NewTester(t)
tester.InitServer(`
{
skip_install_trust
admin localhost:2999
http_port 9080
https_port 9443
local_certs
}
*.localhost:10443 {
respond "Wildcard"
}
dev.localhost {
respond "Exact"
}
`, "caddyfile")
tester.AssertRedirect("http://dev.localhost:9080/", "https://dev.localhost/", http.StatusPermanentRedirect)
tester.AssertRedirect("http://foo.localhost:9080/", "https://foo.localhost:10443/", http.StatusPermanentRedirect)
}
@@ -0,0 +1,69 @@
{
acme_dns mock foo
}
example.com {
respond "Hello World"
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"example.com"
]
}
],
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"body": "Hello World",
"handler": "static_response"
}
]
}
]
}
],
"terminal": true
}
]
}
}
},
"tls": {
"automation": {
"policies": [
{
"issuers": [
{
"challenges": {
"dns": {
"provider": {
"argument": "foo",
"name": "mock"
}
}
},
"module": "acme"
}
]
}
]
}
}
}
}
@@ -0,0 +1,9 @@
{
acme_dns
}
example.com {
respond "Hello World"
}
----------
acme_dns specified without DNS provider config, but no provider specified with 'dns' global option
@@ -18,7 +18,9 @@ encode gzip zstd {
# Long way with a block for each encoding
encode {
zstd
zstd {
disable_checksum
}
gzip 5
}
@@ -71,7 +73,9 @@ encode
"gzip": {
"level": 5
},
"zstd": {}
"zstd": {
"checksum": false
}
},
"handler": "encode",
"prefer": [
@@ -46,6 +46,18 @@ app.example.com {
}
]
},
{
"handle": [
{
"handler": "headers",
"request": {
"delete": [
"Remote-Email"
]
}
}
]
},
{
"handle": [
{
@@ -73,6 +85,18 @@ app.example.com {
}
]
},
{
"handle": [
{
"handler": "headers",
"request": {
"delete": [
"Remote-Groups"
]
}
}
]
},
{
"handle": [
{
@@ -100,6 +124,18 @@ app.example.com {
}
]
},
{
"handle": [
{
"handler": "headers",
"request": {
"delete": [
"Remote-Name"
]
}
}
]
},
{
"handle": [
{
@@ -127,6 +163,18 @@ app.example.com {
}
]
},
{
"handle": [
{
"handler": "headers",
"request": {
"delete": [
"Remote-User"
]
}
}
]
},
{
"handle": [
{
@@ -200,4 +248,4 @@ app.example.com {
}
}
}
}
}
@@ -0,0 +1,146 @@
:8080
forward_auth 127.0.0.1:9091 {
uri /
copy_headers X-User-Id X-User-Role
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":8080"
],
"routes": [
{
"handle": [
{
"handle_response": [
{
"match": {
"status_code": [
2
]
},
"routes": [
{
"handle": [
{
"handler": "vars"
}
]
},
{
"handle": [
{
"handler": "headers",
"request": {
"delete": [
"X-User-Id"
]
}
}
]
},
{
"handle": [
{
"handler": "headers",
"request": {
"set": {
"X-User-Id": [
"{http.reverse_proxy.header.X-User-Id}"
]
}
}
}
],
"match": [
{
"not": [
{
"vars": {
"{http.reverse_proxy.header.X-User-Id}": [
""
]
}
}
]
}
]
},
{
"handle": [
{
"handler": "headers",
"request": {
"delete": [
"X-User-Role"
]
}
}
]
},
{
"handle": [
{
"handler": "headers",
"request": {
"set": {
"X-User-Role": [
"{http.reverse_proxy.header.X-User-Role}"
]
}
}
}
],
"match": [
{
"not": [
{
"vars": {
"{http.reverse_proxy.header.X-User-Role}": [
""
]
}
}
]
}
]
}
]
}
],
"handler": "reverse_proxy",
"headers": {
"request": {
"set": {
"X-Forwarded-Method": [
"{http.request.method}"
],
"X-Forwarded-Uri": [
"{http.request.uri}"
]
}
}
},
"rewrite": {
"method": "GET",
"uri": "/"
},
"upstreams": [
{
"dial": "127.0.0.1:9091"
}
]
}
]
}
]
}
}
}
}
}
@@ -35,6 +35,18 @@ forward_auth localhost:9000 {
}
]
},
{
"handle": [
{
"handler": "headers",
"request": {
"delete": [
"1"
]
}
}
]
},
{
"handle": [
{
@@ -62,6 +74,18 @@ forward_auth localhost:9000 {
}
]
},
{
"handle": [
{
"handler": "headers",
"request": {
"delete": [
"B"
]
}
}
]
},
{
"handle": [
{
@@ -89,6 +113,18 @@ forward_auth localhost:9000 {
}
]
},
{
"handle": [
{
"handler": "headers",
"request": {
"delete": [
"3"
]
}
}
]
},
{
"handle": [
{
@@ -116,6 +152,18 @@ forward_auth localhost:9000 {
}
]
},
{
"handle": [
{
"handler": "headers",
"request": {
"delete": [
"D"
]
}
}
]
},
{
"handle": [
{
@@ -143,6 +191,18 @@ forward_auth localhost:9000 {
}
]
},
{
"handle": [
{
"handler": "headers",
"request": {
"delete": [
"5"
]
}
}
]
},
{
"handle": [
{
@@ -203,4 +263,4 @@ forward_auth localhost:9000 {
}
}
}
}
}
@@ -1,7 +1,7 @@
{
log {
sampling {
interval 300
interval 5m
first 50
thereafter 40
}
@@ -13,7 +13,7 @@
"logs": {
"default": {
"sampling": {
"interval": 300,
"interval": 300000000000,
"first": 50,
"thereafter": 40
}
@@ -0,0 +1,77 @@
{
email test@example.com
dns mock
tls_resolvers 1.1.1.1 8.8.8.8
acme_dns
}
example.com {
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"example.com"
]
}
],
"terminal": true
}
]
}
}
},
"tls": {
"automation": {
"policies": [
{
"issuers": [
{
"challenges": {
"dns": {
"resolvers": [
"1.1.1.1",
"8.8.8.8"
]
}
},
"email": "test@example.com",
"module": "acme"
},
{
"ca": "https://acme.zerossl.com/v2/DV90",
"challenges": {
"dns": {
"resolvers": [
"1.1.1.1",
"8.8.8.8"
]
}
},
"email": "test@example.com",
"module": "acme"
}
]
}
]
},
"dns": {
"name": "mock"
},
"resolvers": [
"1.1.1.1",
"8.8.8.8"
]
}
}
}
@@ -0,0 +1,38 @@
{
tls_resolvers 1.1.1.1 8.8.8.8
}
example.com {
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"example.com"
]
}
],
"terminal": true
}
]
}
}
},
"tls": {
"resolvers": [
"1.1.1.1",
"8.8.8.8"
]
}
}
}
@@ -0,0 +1,72 @@
{
email test@example.com
dns mock
tls_resolvers 1.1.1.1 8.8.8.8
}
example.com {
tls {
dns mock
}
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"example.com"
]
}
],
"terminal": true
}
]
}
}
},
"tls": {
"automation": {
"policies": [
{
"subjects": [
"example.com"
],
"issuers": [
{
"challenges": {
"dns": {
"provider": {
"name": "mock"
},
"resolvers": [
"1.1.1.1",
"8.8.8.8"
]
}
},
"email": "test@example.com",
"module": "acme"
}
]
}
]
},
"dns": {
"name": "mock"
},
"resolvers": [
"1.1.1.1",
"8.8.8.8"
]
}
}
}
@@ -0,0 +1,98 @@
{
email test@example.com
dns mock
tls_resolvers 1.1.1.1 8.8.8.8
acme_dns
}
example.com {
tls {
resolvers 9.9.9.9
}
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"example.com"
]
}
],
"terminal": true
}
]
}
}
},
"tls": {
"automation": {
"policies": [
{
"subjects": [
"example.com"
],
"issuers": [
{
"challenges": {
"dns": {
"resolvers": [
"9.9.9.9"
]
}
},
"email": "test@example.com",
"module": "acme"
}
]
},
{
"issuers": [
{
"challenges": {
"dns": {
"resolvers": [
"1.1.1.1",
"8.8.8.8"
]
}
},
"email": "test@example.com",
"module": "acme"
},
{
"ca": "https://acme.zerossl.com/v2/DV90",
"challenges": {
"dns": {
"resolvers": [
"1.1.1.1",
"8.8.8.8"
]
}
},
"email": "test@example.com",
"module": "acme"
}
]
}
]
},
"dns": {
"name": "mock"
},
"resolvers": [
"1.1.1.1",
"8.8.8.8"
]
}
}
}
@@ -0,0 +1,112 @@
{
email test@example.com
dns mock
tls_resolvers 1.1.1.1 8.8.8.8
acme_dns
}
site1.example.com {
}
site2.example.com {
tls {
resolvers 9.9.9.9 8.8.4.4
}
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"site1.example.com"
]
}
],
"terminal": true
},
{
"match": [
{
"host": [
"site2.example.com"
]
}
],
"terminal": true
}
]
}
}
},
"tls": {
"automation": {
"policies": [
{
"subjects": [
"site2.example.com"
],
"issuers": [
{
"challenges": {
"dns": {
"resolvers": [
"9.9.9.9",
"8.8.4.4"
]
}
},
"email": "test@example.com",
"module": "acme"
}
]
},
{
"issuers": [
{
"challenges": {
"dns": {
"resolvers": [
"1.1.1.1",
"8.8.8.8"
]
}
},
"email": "test@example.com",
"module": "acme"
},
{
"ca": "https://acme.zerossl.com/v2/DV90",
"challenges": {
"dns": {
"resolvers": [
"1.1.1.1",
"8.8.8.8"
]
}
},
"email": "test@example.com",
"module": "acme"
}
]
}
]
},
"dns": {
"name": "mock"
},
"resolvers": [
"1.1.1.1",
"8.8.8.8"
]
}
}
}
@@ -18,6 +18,10 @@
trusted_proxies static private_ranges
client_ip_headers Custom-Real-Client-IP X-Forwarded-For
client_ip_headers A-Third-One
keepalive_interval 20s
keepalive_idle 20s
keepalive_count 10
0rtt off
}
}
@@ -45,6 +49,9 @@ foo.com {
"read_header_timeout": 30000000000,
"write_timeout": 30000000000,
"idle_timeout": 30000000000,
"keepalive_interval": 20000000000,
"keepalive_idle": 20000000000,
"keepalive_count": 10,
"max_header_bytes": 100000000,
"enable_full_duplex": true,
"routes": [
@@ -84,9 +91,10 @@ foo.com {
"h2",
"h2c",
"h3"
]
],
"allow_0rtt": false
}
}
}
}
}
}
@@ -0,0 +1,13 @@
(site) {
http://{args[0]} https://{args[0]} {
{block}
}
}
import site test.domain {
{
header_up Host {host}
header_up X-Real-IP {remote_host}
}
}
----------
anonymous blocks are not supported
@@ -0,0 +1,15 @@
{
admin off
auto_https off
}
import testdata/issue_7557_invalid_subdirective_snippet.conf
:8080 {
import test {
this_is_nonsense
}
}
----------
parsing caddyfile tokens for 'reverse_proxy': unrecognized subdirective this_is_nonsense
@@ -0,0 +1,57 @@
(snippet) {
header {
reverse_proxy localhost:3000
{block}
}
}
example.com {
import snippet
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"example.com"
]
}
],
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"handler": "headers",
"response": {
"set": {
"Reverse_proxy": [
"localhost:3000"
]
}
}
}
]
}
]
}
],
"terminal": true
}
]
}
}
}
}
}
@@ -0,0 +1,52 @@
import testdata/issue_7518_unused_block_panic_snippets.conf
example.com {
import snippet
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"example.com"
]
}
],
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"handler": "headers",
"response": {
"set": {
"Reverse_proxy": [
"localhost:3000"
]
}
}
}
]
}
]
}
],
"terminal": true
}
]
}
}
}
}
}
@@ -0,0 +1,57 @@
(snippet) {
header {
reverse_proxy localhost:3000
{blocks.content_type}
}
}
example.com {
import snippet
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"example.com"
]
}
],
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"handler": "headers",
"response": {
"set": {
"Reverse_proxy": [
"localhost:3000"
]
}
}
}
]
}
]
}
],
"terminal": true
}
]
}
}
}
}
}
@@ -0,0 +1,65 @@
(site) {
https://{args[0]} {
{block}
}
}
import site test.domain {
reverse_proxy http://192.168.1.1:8080 {
header_up Host {host}
}
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"test.domain"
]
}
],
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"handler": "reverse_proxy",
"headers": {
"request": {
"set": {
"Host": [
"{http.request.host}"
]
}
}
},
"upstreams": [
{
"dial": "192.168.1.1:8080"
}
]
}
]
}
]
}
],
"terminal": true
}
]
}
}
}
}
}
@@ -0,0 +1,47 @@
{
log {
format journald {
wrap console
}
}
}
:80 {
respond "Hello, World!"
}
----------
{
"logging": {
"logs": {
"default": {
"encoder": {
"format": "journald",
"wrap": {
"format": "console"
}
}
}
}
},
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":80"
],
"routes": [
{
"handle": [
{
"body": "Hello, World!",
"handler": "static_response"
}
]
}
]
}
}
}
}
}
@@ -0,0 +1,95 @@
:80
log {
output stdout
format filter {
wrap console
# Multiple regexp filters for the same field - this should work now!
request>headers>Authorization regexp "Bearer\s+([A-Za-z0-9_-]+)" "Bearer [REDACTED]"
request>headers>Authorization regexp "Basic\s+([A-Za-z0-9+/=]+)" "Basic [REDACTED]"
request>headers>Authorization regexp "token=([^&\s]+)" "token=[REDACTED]"
# Single regexp filter - this should continue to work as before
request>headers>Cookie regexp "sessionid=[^;]+" "sessionid=[REDACTED]"
# Mixed filters (non-regexp) - these should work normally
request>headers>Server delete
request>remote_ip ip_mask {
ipv4 24
ipv6 32
}
}
}
----------
{
"logging": {
"logs": {
"default": {
"exclude": [
"http.log.access.log0"
]
},
"log0": {
"writer": {
"output": "stdout"
},
"encoder": {
"fields": {
"request\u003eheaders\u003eAuthorization": {
"filter": "multi_regexp",
"operations": [
{
"regexp": "Bearer\\s+([A-Za-z0-9_-]+)",
"value": "Bearer [REDACTED]"
},
{
"regexp": "Basic\\s+([A-Za-z0-9+/=]+)",
"value": "Basic [REDACTED]"
},
{
"regexp": "token=([^\u0026\\s]+)",
"value": "token=[REDACTED]"
}
]
},
"request\u003eheaders\u003eCookie": {
"filter": "regexp",
"regexp": "sessionid=[^;]+",
"value": "sessionid=[REDACTED]"
},
"request\u003eheaders\u003eServer": {
"filter": "delete"
},
"request\u003eremote_ip": {
"filter": "ip_mask",
"ipv4_cidr": 24,
"ipv6_cidr": 32
}
},
"format": "filter",
"wrap": {
"format": "console"
}
},
"include": [
"http.log.access.log0"
]
}
}
},
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":80"
],
"logs": {
"default_logger_name": "log0"
}
}
}
}
}
}
@@ -1,27 +1,47 @@
:80
log {
log one {
output file /var/log/access.log {
mode 0644
dir_mode 0755
roll_size 1gb
roll_uncompressed
roll_compression none
roll_local_time
roll_keep 5
roll_keep_for 90d
}
}
log two {
output file /var/log/access-2.log {
mode 0777
dir_mode from_file
roll_size 1gib
roll_compression zstd
roll_interval 12h
roll_at 00:00 06:00 12:00,18:00
roll_minutes 10 40 45,46
roll_keep 10
roll_keep_for 90d
}
}
----------
{
"logging": {
"logs": {
"default": {
"exclude": [
"http.log.access.log0"
"http.log.access.one",
"http.log.access.two"
]
},
"log0": {
"one": {
"writer": {
"dir_mode": "0755",
"filename": "/var/log/access.log",
"mode": "0644",
"output": "file",
"roll_compression": "none",
"roll_gzip": false,
"roll_keep": 5,
"roll_keep_days": 90,
@@ -29,7 +49,35 @@ log {
"roll_size_mb": 954
},
"include": [
"http.log.access.log0"
"http.log.access.one"
]
},
"two": {
"writer": {
"dir_mode": "from_file",
"filename": "/var/log/access-2.log",
"mode": "0777",
"output": "file",
"roll_at": [
"00:00",
"06:00",
"12:00",
"18:00"
],
"roll_compression": "zstd",
"roll_interval": 43200000000000,
"roll_keep": 10,
"roll_keep_days": 90,
"roll_minutes": [
10,
40,
45,
46
],
"roll_size_mb": 1024
},
"include": [
"http.log.access.two"
]
}
}
@@ -42,7 +90,7 @@ log {
":80"
],
"logs": {
"default_logger_name": "log0"
"default_logger_name": "two"
}
}
}
@@ -1,7 +1,7 @@
:80 {
log {
sampling {
interval 300
interval 5m
first 50
thereafter 40
}
@@ -18,7 +18,7 @@
},
"log0": {
"sampling": {
"interval": 300,
"interval": 300000000000,
"first": 50,
"thereafter": 40
},
@@ -0,0 +1,35 @@
{
metrics {
otlp
}
}
:80 {
respond "Hello"
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":80"
],
"routes": [
{
"handle": [
{
"body": "Hello",
"handler": "static_response"
}
]
}
]
}
},
"metrics": {
"otlp": true
}
}
}
}
@@ -0,0 +1,41 @@
{
renewal_window_ratio 0.1666
}
example.com {
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"example.com"
]
}
],
"terminal": true
}
]
}
}
},
"tls": {
"automation": {
"policies": [
{
"renewal_window_ratio": 0.1666
}
]
}
}
}
}
@@ -0,0 +1,63 @@
{
renewal_window_ratio 0.1666
}
a.example.com {
tls {
renewal_window_ratio 0.25
}
}
b.example.com {
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"a.example.com"
]
}
],
"terminal": true
},
{
"match": [
{
"host": [
"b.example.com"
]
}
],
"terminal": true
}
]
}
}
},
"tls": {
"automation": {
"policies": [
{
"subjects": [
"a.example.com"
],
"renewal_window_ratio": 0.25
},
{
"renewal_window_ratio": 0.1666
}
]
}
}
}
}
@@ -0,0 +1,58 @@
:8884
reverse_proxy 127.0.0.1:65535 {
lb_retries 3
lb_retry_match expression `{rp.status_code} in [502, 503]`
lb_retry_match expression `{rp.is_transport_error} || {rp.status_code} == 502`
lb_retry_match expression `method('POST') && {rp.status_code} == 503`
lb_retry_match `{rp.status_code} == 504`
lb_retry_match `{rp.is_transport_error} && method('PUT')`
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":8884"
],
"routes": [
{
"handle": [
{
"handler": "reverse_proxy",
"load_balancing": {
"retries": 3,
"retry_match": [
{
"expression": "{http.reverse_proxy.status_code} in [502, 503]"
},
{
"expression": "{http.reverse_proxy.is_transport_error} || {http.reverse_proxy.status_code} == 502"
},
{
"expression": "method('POST') \u0026\u0026 {http.reverse_proxy.status_code} == 503"
},
{
"expression": "{http.reverse_proxy.status_code} == 504"
},
{
"expression": "{http.reverse_proxy.is_transport_error} \u0026\u0026 method('PUT')"
}
]
},
"upstreams": [
{
"dial": "127.0.0.1:65535"
}
]
}
]
}
]
}
}
}
}
}
@@ -0,0 +1,147 @@
:8884
reverse_proxy 127.0.0.1:65535 {
lb_retries 5
# request matchers (backward-compatible, non-expression)
lb_retry_match {
method POST PUT
}
lb_retry_match {
path /foo*
}
lb_retry_match {
header X-Idempotency-Key *
}
# response status code via expression
lb_retry_match {
expression `{rp.status_code} in [502, 503, 504]`
}
# response header via expression
lb_retry_match {
expression `{rp.header.X-Retry} == "true"`
}
# CEL request functions combined with response placeholders
lb_retry_match {
expression `method('POST') && {rp.status_code} >= 500`
}
lb_retry_match {
expression `path('/api*') && {rp.status_code} in [502, 503]`
}
lb_retry_match {
expression `host('example.com') && {rp.status_code} == 503`
}
lb_retry_match {
expression `query({'retry': 'true'}) && {rp.status_code} >= 500`
}
lb_retry_match {
expression `header({'X-Idempotency-Key': '*'}) && {rp.status_code} in [502, 503]`
}
lb_retry_match {
expression `protocol('https') && {rp.status_code} == 502`
}
lb_retry_match {
expression `path_regexp('^/api/v[0-9]+/') && {rp.status_code} >= 500`
}
lb_retry_match {
expression `header_regexp('Content-Type', '^application/json') && {rp.status_code} == 502`
}
# transport error handling via placeholder
lb_retry_match {
expression `{rp.is_transport_error} || {rp.status_code} in [502, 503]`
}
lb_retry_match {
expression `{rp.is_transport_error} && method('POST')`
}
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":8884"
],
"routes": [
{
"handle": [
{
"handler": "reverse_proxy",
"load_balancing": {
"retries": 5,
"retry_match": [
{
"method": [
"POST",
"PUT"
]
},
{
"path": [
"/foo*"
]
},
{
"header": {
"X-Idempotency-Key": [
"*"
]
}
},
{
"expression": "{http.reverse_proxy.status_code} in [502, 503, 504]"
},
{
"expression": "{http.reverse_proxy.header.X-Retry} == \"true\""
},
{
"expression": "method('POST') \u0026\u0026 {http.reverse_proxy.status_code} \u003e= 500"
},
{
"expression": "path('/api*') \u0026\u0026 {http.reverse_proxy.status_code} in [502, 503]"
},
{
"expression": "host('example.com') \u0026\u0026 {http.reverse_proxy.status_code} == 503"
},
{
"expression": "query({'retry': 'true'}) \u0026\u0026 {http.reverse_proxy.status_code} \u003e= 500"
},
{
"expression": "header({'X-Idempotency-Key': '*'}) \u0026\u0026 {http.reverse_proxy.status_code} in [502, 503]"
},
{
"expression": "protocol('https') \u0026\u0026 {http.reverse_proxy.status_code} == 502"
},
{
"expression": "path_regexp('^/api/v[0-9]+/') \u0026\u0026 {http.reverse_proxy.status_code} \u003e= 500"
},
{
"expression": "header_regexp('Content-Type', '^application/json') \u0026\u0026 {http.reverse_proxy.status_code} == 502"
},
{
"expression": "{http.reverse_proxy.is_transport_error} || {http.reverse_proxy.status_code} in [502, 503]"
},
{
"expression": "{http.reverse_proxy.is_transport_error} \u0026\u0026 method('POST')"
}
]
},
"upstreams": [
{
"dial": "127.0.0.1:65535"
}
]
}
]
}
]
}
}
}
}
}
@@ -0,0 +1,56 @@
https://example.com {
reverse_proxy https://localhost:54321 {
stream_buffer_size 8KB
}
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"example.com"
]
}
],
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"handler": "reverse_proxy",
"stream_buffer_size": 8000,
"transport": {
"protocol": "http",
"tls": {}
},
"upstreams": [
{
"dial": "localhost:54321"
}
]
}
]
}
]
}
],
"terminal": true
}
]
}
}
}
}
}
@@ -0,0 +1,59 @@
{
servers {
trusted_proxies_unix
}
}
example.com {
reverse_proxy https://local:8080
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"example.com"
]
}
],
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"handler": "reverse_proxy",
"transport": {
"protocol": "http",
"tls": {}
},
"upstreams": [
{
"dial": "local:8080"
}
]
}
]
}
]
}
],
"terminal": true
}
],
"trusted_proxies_unix": true
}
}
}
}
}
@@ -0,0 +1,83 @@
{
dns mock foo
acme_dns mock bar
}
localhost {
tls {
resolvers 8.8.8.8 8.8.4.4
}
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"localhost"
]
}
],
"terminal": true
}
]
}
}
},
"tls": {
"automation": {
"policies": [
{
"subjects": [
"localhost"
],
"issuers": [
{
"challenges": {
"dns": {
"provider": {
"argument": "bar",
"name": "mock"
},
"resolvers": [
"8.8.8.8",
"8.8.4.4"
]
}
},
"module": "acme"
}
]
},
{
"issuers": [
{
"challenges": {
"dns": {
"provider": {
"argument": "bar",
"name": "mock"
}
}
},
"module": "acme"
}
]
}
]
},
"dns": {
"argument": "foo",
"name": "mock"
}
}
}
}
@@ -54,11 +54,6 @@ b.com {
"via": "http"
}
]
},
{
"subjects": [
"b.com"
]
}
]
}
@@ -0,0 +1,96 @@
# example from https://github.com/caddyserver/caddy/issues/7559
*.test.local {
tls {
get_certificate http http://cert-server:9000/certs
}
respond "wildcard"
}
# certificate for this subdomain is covered by wildcard above
subdomain.test.local {
respond "subdomain"
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"subdomain.test.local"
]
}
],
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"body": "subdomain",
"handler": "static_response"
}
]
}
]
}
],
"terminal": true
},
{
"match": [
{
"host": [
"*.test.local"
]
}
],
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"body": "wildcard",
"handler": "static_response"
}
]
}
]
}
],
"terminal": true
}
]
}
}
},
"tls": {
"automation": {
"policies": [
{
"subjects": [
"*.test.local"
],
"get_certificate": [
{
"url": "http://cert-server:9000/certs",
"via": "http"
}
]
}
]
}
}
}
}
@@ -0,0 +1,87 @@
localhost
respond "hello from localhost"
tls {
client_auth {
mode request
trust_pool combined {
source inline {
trust_der MIIDSzCCAjOgAwIBAgIUfIRObjWNUA4jxQ/0x8BOCvE2Vw4wDQYJKoZIhvcNAQELBQAwFjEUMBIGA1UEAwwLRWFzeS1SU0EgQ0EwHhcNMTkwODI4MTYyNTU5WhcNMjkwODI1MTYyNTU5WjAWMRQwEgYDVQQDDAtFYXN5LVJTQSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK5m5elxhQfMp/3aVJ4JnpN9PUSz6LlP6LePAPFU7gqohVVFVtDkChJAG3FNkNQNlieVTja/bgH9IcC6oKbROwdY1h0MvNV8AHHigvl03WuJD8g2ReVFXXwsnrPmKXCFzQyMI6TYk3m2gYrXsZOU1GLnfMRC3KAMRgE2F45twOs9hqG169YJ6mM2eQjzjCHWI6S2/iUYvYxRkCOlYUbLsMD/AhgAf1plzg6LPqNxtdlwxZnA0ytgkmhK67HtzJu0+ovUCsMv0RwcMhsEo9T8nyFAGt9XLZ63X5WpBCTUApaAUhnG0XnerjmUWb6eUWw4zev54sEfY5F3x002iQaW6cECAwEAAaOBkDCBjTAdBgNVHQ4EFgQU4CBUbZsS2GaNIkGRz/cBsD5ivjswUQYDVR0jBEowSIAU4CBUbZsS2GaNIkGRz/cBsD5ivjuhGqQYMBYxFDASBgNVBAMMC0Vhc3ktUlNBIENBghR8hE5uNY1QDiPFD/THwE4K8TZXDjAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAKB3V4HIzoiO/Ch6WMj9bLJ2FGbpkMrcb/Eq01hT5zcfKD66lVS1MlK+cRL446Z2b2KDP1oFyVs+qmrmtdwrWgD+nfe2sBmmIHo9m9KygMkEOfG3MghGTEcS+0cTKEcoHYWYyOqQh6jnedXY8Cdm4GM1hAc9MiL3/sqV8YCVSLNnkoNysmr06/rZ0MCUZPGUtRmfd0heWhrfzAKw2HLgX+RAmpOE2MZqWcjvqKGyaRiaZks4nJkP6521aC2Lgp0HhCz1j8/uQ5ldoDszCnu/iro0NAsNtudTMD+YoLQxLqdleIh6CW+illc2VdXwj7mn6J04yns9jfE2jRjW/yTLFuQ==
}
source file {
pem_file ../caddy.ca.cer
}
}
}
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"localhost"
]
}
],
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"body": "hello from localhost",
"handler": "static_response"
}
]
}
]
}
],
"terminal": true
}
],
"tls_connection_policies": [
{
"match": {
"sni": [
"localhost"
]
},
"client_authentication": {
"ca": {
"provider": "combined",
"sources": [
{
"provider": "inline",
"trusted_ca_certs": [
"MIIDSzCCAjOgAwIBAgIUfIRObjWNUA4jxQ/0x8BOCvE2Vw4wDQYJKoZIhvcNAQELBQAwFjEUMBIGA1UEAwwLRWFzeS1SU0EgQ0EwHhcNMTkwODI4MTYyNTU5WhcNMjkwODI1MTYyNTU5WjAWMRQwEgYDVQQDDAtFYXN5LVJTQSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK5m5elxhQfMp/3aVJ4JnpN9PUSz6LlP6LePAPFU7gqohVVFVtDkChJAG3FNkNQNlieVTja/bgH9IcC6oKbROwdY1h0MvNV8AHHigvl03WuJD8g2ReVFXXwsnrPmKXCFzQyMI6TYk3m2gYrXsZOU1GLnfMRC3KAMRgE2F45twOs9hqG169YJ6mM2eQjzjCHWI6S2/iUYvYxRkCOlYUbLsMD/AhgAf1plzg6LPqNxtdlwxZnA0ytgkmhK67HtzJu0+ovUCsMv0RwcMhsEo9T8nyFAGt9XLZ63X5WpBCTUApaAUhnG0XnerjmUWb6eUWw4zev54sEfY5F3x002iQaW6cECAwEAAaOBkDCBjTAdBgNVHQ4EFgQU4CBUbZsS2GaNIkGRz/cBsD5ivjswUQYDVR0jBEowSIAU4CBUbZsS2GaNIkGRz/cBsD5ivjuhGqQYMBYxFDASBgNVBAMMC0Vhc3ktUlNBIENBghR8hE5uNY1QDiPFD/THwE4K8TZXDjAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAKB3V4HIzoiO/Ch6WMj9bLJ2FGbpkMrcb/Eq01hT5zcfKD66lVS1MlK+cRL446Z2b2KDP1oFyVs+qmrmtdwrWgD+nfe2sBmmIHo9m9KygMkEOfG3MghGTEcS+0cTKEcoHYWYyOqQh6jnedXY8Cdm4GM1hAc9MiL3/sqV8YCVSLNnkoNysmr06/rZ0MCUZPGUtRmfd0heWhrfzAKw2HLgX+RAmpOE2MZqWcjvqKGyaRiaZks4nJkP6521aC2Lgp0HhCz1j8/uQ5ldoDszCnu/iro0NAsNtudTMD+YoLQxLqdleIh6CW+illc2VdXwj7mn6J04yns9jfE2jRjW/yTLFuQ=="
]
},
{
"pem_files": [
"../caddy.ca.cer"
],
"provider": "file"
}
]
},
"mode": "request"
}
},
{}
]
}
}
}
}
}
@@ -0,0 +1,87 @@
localhost
respond "hello from localhost"
tls {
client_auth {
mode require_and_verify
trust_pool combined {
source inline {
trust_der MIIDSzCCAjOgAwIBAgIUfIRObjWNUA4jxQ/0x8BOCvE2Vw4wDQYJKoZIhvcNAQELBQAwFjEUMBIGA1UEAwwLRWFzeS1SU0EgQ0EwHhcNMTkwODI4MTYyNTU5WhcNMjkwODI1MTYyNTU5WjAWMRQwEgYDVQQDDAtFYXN5LVJTQSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK5m5elxhQfMp/3aVJ4JnpN9PUSz6LlP6LePAPFU7gqohVVFVtDkChJAG3FNkNQNlieVTja/bgH9IcC6oKbROwdY1h0MvNV8AHHigvl03WuJD8g2ReVFXXwsnrPmKXCFzQyMI6TYk3m2gYrXsZOU1GLnfMRC3KAMRgE2F45twOs9hqG169YJ6mM2eQjzjCHWI6S2/iUYvYxRkCOlYUbLsMD/AhgAf1plzg6LPqNxtdlwxZnA0ytgkmhK67HtzJu0+ovUCsMv0RwcMhsEo9T8nyFAGt9XLZ63X5WpBCTUApaAUhnG0XnerjmUWb6eUWw4zev54sEfY5F3x002iQaW6cECAwEAAaOBkDCBjTAdBgNVHQ4EFgQU4CBUbZsS2GaNIkGRz/cBsD5ivjswUQYDVR0jBEowSIAU4CBUbZsS2GaNIkGRz/cBsD5ivjuhGqQYMBYxFDASBgNVBAMMC0Vhc3ktUlNBIENBghR8hE5uNY1QDiPFD/THwE4K8TZXDjAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAKB3V4HIzoiO/Ch6WMj9bLJ2FGbpkMrcb/Eq01hT5zcfKD66lVS1MlK+cRL446Z2b2KDP1oFyVs+qmrmtdwrWgD+nfe2sBmmIHo9m9KygMkEOfG3MghGTEcS+0cTKEcoHYWYyOqQh6jnedXY8Cdm4GM1hAc9MiL3/sqV8YCVSLNnkoNysmr06/rZ0MCUZPGUtRmfd0heWhrfzAKw2HLgX+RAmpOE2MZqWcjvqKGyaRiaZks4nJkP6521aC2Lgp0HhCz1j8/uQ5ldoDszCnu/iro0NAsNtudTMD+YoLQxLqdleIh6CW+illc2VdXwj7mn6J04yns9jfE2jRjW/yTLFuQ==
}
source pki_root {
authority local
}
}
}
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"localhost"
]
}
],
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"body": "hello from localhost",
"handler": "static_response"
}
]
}
]
}
],
"terminal": true
}
],
"tls_connection_policies": [
{
"match": {
"sni": [
"localhost"
]
},
"client_authentication": {
"ca": {
"provider": "combined",
"sources": [
{
"provider": "inline",
"trusted_ca_certs": [
"MIIDSzCCAjOgAwIBAgIUfIRObjWNUA4jxQ/0x8BOCvE2Vw4wDQYJKoZIhvcNAQELBQAwFjEUMBIGA1UEAwwLRWFzeS1SU0EgQ0EwHhcNMTkwODI4MTYyNTU5WhcNMjkwODI1MTYyNTU5WjAWMRQwEgYDVQQDDAtFYXN5LVJTQSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK5m5elxhQfMp/3aVJ4JnpN9PUSz6LlP6LePAPFU7gqohVVFVtDkChJAG3FNkNQNlieVTja/bgH9IcC6oKbROwdY1h0MvNV8AHHigvl03WuJD8g2ReVFXXwsnrPmKXCFzQyMI6TYk3m2gYrXsZOU1GLnfMRC3KAMRgE2F45twOs9hqG169YJ6mM2eQjzjCHWI6S2/iUYvYxRkCOlYUbLsMD/AhgAf1plzg6LPqNxtdlwxZnA0ytgkmhK67HtzJu0+ovUCsMv0RwcMhsEo9T8nyFAGt9XLZ63X5WpBCTUApaAUhnG0XnerjmUWb6eUWw4zev54sEfY5F3x002iQaW6cECAwEAAaOBkDCBjTAdBgNVHQ4EFgQU4CBUbZsS2GaNIkGRz/cBsD5ivjswUQYDVR0jBEowSIAU4CBUbZsS2GaNIkGRz/cBsD5ivjuhGqQYMBYxFDASBgNVBAMMC0Vhc3ktUlNBIENBghR8hE5uNY1QDiPFD/THwE4K8TZXDjAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAKB3V4HIzoiO/Ch6WMj9bLJ2FGbpkMrcb/Eq01hT5zcfKD66lVS1MlK+cRL446Z2b2KDP1oFyVs+qmrmtdwrWgD+nfe2sBmmIHo9m9KygMkEOfG3MghGTEcS+0cTKEcoHYWYyOqQh6jnedXY8Cdm4GM1hAc9MiL3/sqV8YCVSLNnkoNysmr06/rZ0MCUZPGUtRmfd0heWhrfzAKw2HLgX+RAmpOE2MZqWcjvqKGyaRiaZks4nJkP6521aC2Lgp0HhCz1j8/uQ5ldoDszCnu/iro0NAsNtudTMD+YoLQxLqdleIh6CW+illc2VdXwj7mn6J04yns9jfE2jRjW/yTLFuQ=="
]
},
{
"authority": [
"local"
],
"provider": "pki_root"
}
]
},
"mode": "require_and_verify"
}
},
{}
]
}
}
}
}
}
@@ -0,0 +1,66 @@
localhost
respond "hello from localhost"
tls {
client_auth {
mode request
trust_pool system
}
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"localhost"
]
}
],
"handle": [
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"body": "hello from localhost",
"handler": "static_response"
}
]
}
]
}
],
"terminal": true
}
],
"tls_connection_policies": [
{
"match": {
"sni": [
"localhost"
]
},
"client_authentication": {
"ca": {
"provider": "system"
},
"mode": "request"
}
},
{}
]
}
}
}
}
}
@@ -0,0 +1,79 @@
{
acme_dns mock foo
}
localhost {
tls {
dns mock bar
resolvers 8.8.8.8 8.8.4.4
}
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"localhost"
]
}
],
"terminal": true
}
]
}
}
},
"tls": {
"automation": {
"policies": [
{
"subjects": [
"localhost"
],
"issuers": [
{
"challenges": {
"dns": {
"provider": {
"argument": "bar",
"name": "mock"
},
"resolvers": [
"8.8.8.8",
"8.8.4.4"
]
}
},
"module": "acme"
}
]
},
{
"issuers": [
{
"challenges": {
"dns": {
"provider": {
"argument": "foo",
"name": "mock"
}
}
},
"module": "acme"
}
]
}
]
}
}
}
}
@@ -0,0 +1,68 @@
{
dns mock foo
}
localhost {
tls {
dns mock bar
resolvers 8.8.8.8 8.8.4.4
}
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"localhost"
]
}
],
"terminal": true
}
]
}
}
},
"tls": {
"automation": {
"policies": [
{
"subjects": [
"localhost"
],
"issuers": [
{
"challenges": {
"dns": {
"provider": {
"argument": "bar",
"name": "mock"
},
"resolvers": [
"8.8.8.8",
"8.8.4.4"
]
}
},
"module": "acme"
}
]
}
]
},
"dns": {
"argument": "foo",
"name": "mock"
}
}
}
}
@@ -0,0 +1,76 @@
{
acme_dns mock
}
localhost {
tls {
resolvers 8.8.8.8 8.8.4.4
}
}
----------
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [
":443"
],
"routes": [
{
"match": [
{
"host": [
"localhost"
]
}
],
"terminal": true
}
]
}
}
},
"tls": {
"automation": {
"policies": [
{
"subjects": [
"localhost"
],
"issuers": [
{
"challenges": {
"dns": {
"provider": {
"name": "mock"
},
"resolvers": [
"8.8.8.8",
"8.8.4.4"
]
}
},
"module": "acme"
}
]
},
{
"issuers": [
{
"challenges": {
"dns": {
"provider": {
"name": "mock"
}
}
},
"module": "acme"
}
]
}
]
}
}
}
}
+206
View File
@@ -0,0 +1,206 @@
// 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 integration
import (
"fmt"
"net/http"
"net/http/httptest"
"strings"
"sync"
"testing"
"github.com/caddyserver/caddy/v2/caddytest"
)
// TestForwardAuthCopyHeadersStripsClientHeaders is a regression test for the
// header injection vulnerability in forward_auth copy_headers.
//
// When the auth service returns 200 OK without one of the copy_headers headers,
// the MatchNot guard skips the Set operation. Before this fix, the original
// client-supplied header survived unchanged into the backend request, allowing
// privilege escalation with only a valid (non-privileged) bearer token. After
// the fix, an unconditional delete route runs first, so the backend always
// sees an absent header rather than the attacker-supplied value.
func TestForwardAuthCopyHeadersStripsClientHeaders(t *testing.T) {
// Mock auth service: accepts any Bearer token, returns 200 OK with NO
// identity headers. This is the stateless JWT validator pattern that
// triggers the vulnerability.
authSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if strings.HasPrefix(r.Header.Get("Authorization"), "Bearer ") {
w.WriteHeader(http.StatusOK)
return
}
w.WriteHeader(http.StatusUnauthorized)
}))
defer authSrv.Close()
// Mock backend: records the identity headers it receives. A real application
// would use X-User-Id / X-User-Role to make authorization decisions.
type received struct{ userID, userRole string }
var (
mu sync.Mutex
last received
)
backendSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
mu.Lock()
last = received{
userID: r.Header.Get("X-User-Id"),
userRole: r.Header.Get("X-User-Role"),
}
mu.Unlock()
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, "ok")
}))
defer backendSrv.Close()
authAddr := strings.TrimPrefix(authSrv.URL, "http://")
backendAddr := strings.TrimPrefix(backendSrv.URL, "http://")
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 X-User-Id X-User-Role
}
reverse_proxy %s
}
`, authAddr, backendAddr), "caddyfile")
// Case 1: no token. Auth must still reject the request even when the client
// includes identity headers. This confirms the auth check is not bypassed.
req, _ := http.NewRequest(http.MethodGet, "http://localhost:9080/", nil)
req.Header.Set("X-User-Id", "injected")
req.Header.Set("X-User-Role", "injected")
resp := tester.AssertResponseCode(req, http.StatusUnauthorized)
resp.Body.Close()
// Case 2: valid token, no injected headers. The backend should see absent
// identity headers (the auth service never returns them).
req, _ = http.NewRequest(http.MethodGet, "http://localhost:9080/", nil)
req.Header.Set("Authorization", "Bearer token123")
tester.AssertResponse(req, http.StatusOK, "ok")
mu.Lock()
gotID, gotRole := last.userID, last.userRole
mu.Unlock()
if gotID != "" {
t.Errorf("baseline: X-User-Id should be absent, got %q", gotID)
}
if gotRole != "" {
t.Errorf("baseline: X-User-Role should be absent, got %q", gotRole)
}
// Case 3 (the security regression): valid token plus forged identity headers.
// The fix must strip those values so the backend never sees them.
req, _ = http.NewRequest(http.MethodGet, "http://localhost:9080/", nil)
req.Header.Set("Authorization", "Bearer token123")
req.Header.Set("X-User-Id", "admin") // forged
req.Header.Set("X-User-Role", "superadmin") // forged
tester.AssertResponse(req, http.StatusOK, "ok")
mu.Lock()
gotID, gotRole = last.userID, last.userRole
mu.Unlock()
if gotID != "" {
t.Errorf("injection: X-User-Id must be stripped, got %q", gotID)
}
if gotRole != "" {
t.Errorf("injection: X-User-Role must be stripped, got %q", gotRole)
}
}
// TestForwardAuthCopyHeadersAuthResponseWins verifies that when the auth
// service does include a copy_headers header in its response, that value
// is forwarded to the backend and takes precedence over any client-supplied
// value for the same header.
func TestForwardAuthCopyHeadersAuthResponseWins(t *testing.T) {
const wantUserID = "service-user-42"
const wantUserRole = "editor"
// Auth service: accepts bearer token and sets identity headers.
authSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if strings.HasPrefix(r.Header.Get("Authorization"), "Bearer ") {
w.Header().Set("X-User-Id", wantUserID)
w.Header().Set("X-User-Role", wantUserRole)
w.WriteHeader(http.StatusOK)
return
}
w.WriteHeader(http.StatusUnauthorized)
}))
defer authSrv.Close()
type received struct{ userID, userRole string }
var (
mu sync.Mutex
last received
)
backendSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
mu.Lock()
last = received{
userID: r.Header.Get("X-User-Id"),
userRole: r.Header.Get("X-User-Role"),
}
mu.Unlock()
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, "ok")
}))
defer backendSrv.Close()
authAddr := strings.TrimPrefix(authSrv.URL, "http://")
backendAddr := strings.TrimPrefix(backendSrv.URL, "http://")
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 X-User-Id X-User-Role
}
reverse_proxy %s
}
`, authAddr, backendAddr), "caddyfile")
// The client sends forged headers; the auth service overrides them with
// its own values. The backend must receive the auth service values.
req, _ := http.NewRequest(http.MethodGet, "http://localhost:9080/", nil)
req.Header.Set("Authorization", "Bearer token123")
req.Header.Set("X-User-Id", "forged-id") // must be overwritten
req.Header.Set("X-User-Role", "forged-role") // must be overwritten
tester.AssertResponse(req, http.StatusOK, "ok")
mu.Lock()
gotID, gotRole := last.userID, last.userRole
mu.Unlock()
if gotID != wantUserID {
t.Errorf("X-User-Id: want %q, got %q", wantUserID, gotID)
}
if gotRole != wantUserRole {
t.Errorf("X-User-Role: want %q, got %q", wantUserRole, gotRole)
}
}
+129
View File
@@ -0,0 +1,129 @@
package integration
import (
"fmt"
"net/http"
"slices"
"strings"
"testing"
"github.com/caddyserver/caddy/v2/caddytest"
)
func newH2ListenerWithVersionsWithTLSTester(t *testing.T, serverVersions []string, clientVersions []string) *caddytest.Tester {
const baseConfig = `
{
skip_install_trust
admin localhost:2999
http_port 9080
https_port 9443
servers :9443 {
protocols %s
}
}
localhost {
respond "{http.request.tls.proto} {http.request.proto}"
}
`
tester := caddytest.NewTester(t)
tester.InitServer(fmt.Sprintf(baseConfig, strings.Join(serverVersions, " ")), "caddyfile")
tr := tester.Client.Transport.(*http.Transport)
tr.TLSClientConfig.NextProtos = clientVersions
tr.Protocols = new(http.Protocols)
if slices.Contains(clientVersions, "h2") {
tr.ForceAttemptHTTP2 = true
tr.Protocols.SetHTTP2(true)
}
if !slices.Contains(clientVersions, "http/1.1") {
tr.Protocols.SetHTTP1(false)
}
return tester
}
func TestH2ListenerWithTLS(t *testing.T) {
tests := []struct {
serverVersions []string
clientVersions []string
expectedBody string
failed bool
}{
{[]string{"h2"}, []string{"h2"}, "h2 HTTP/2.0", false},
{[]string{"h2"}, []string{"http/1.1"}, "", true},
{[]string{"h1"}, []string{"http/1.1"}, "http/1.1 HTTP/1.1", false},
{[]string{"h1"}, []string{"h2"}, "", true},
{[]string{"h2", "h1"}, []string{"h2"}, "h2 HTTP/2.0", false},
{[]string{"h2", "h1"}, []string{"http/1.1"}, "http/1.1 HTTP/1.1", false},
}
for _, tc := range tests {
tester := newH2ListenerWithVersionsWithTLSTester(t, tc.serverVersions, tc.clientVersions)
t.Logf("running with server versions %v and client versions %v:", tc.serverVersions, tc.clientVersions)
if tc.failed {
resp, err := tester.Client.Get("https://localhost:9443")
if err == nil {
t.Errorf("unexpected response: %d", resp.StatusCode)
}
} else {
tester.AssertGetResponse("https://localhost:9443", 200, tc.expectedBody)
}
}
}
func newH2ListenerWithVersionsWithoutTLSTester(t *testing.T, serverVersions []string, clientVersions []string) *caddytest.Tester {
const baseConfig = `
{
skip_install_trust
admin localhost:2999
http_port 9080
servers :9080 {
protocols %s
}
}
http://localhost {
respond "{http.request.proto}"
}
`
tester := caddytest.NewTester(t)
tester.InitServer(fmt.Sprintf(baseConfig, strings.Join(serverVersions, " ")), "caddyfile")
tr := tester.Client.Transport.(*http.Transport)
tr.Protocols = new(http.Protocols)
if slices.Contains(clientVersions, "h2c") {
tr.Protocols.SetHTTP1(false)
tr.Protocols.SetUnencryptedHTTP2(true)
} else if slices.Contains(clientVersions, "http/1.1") {
tr.Protocols.SetHTTP1(true)
tr.Protocols.SetUnencryptedHTTP2(false)
}
return tester
}
func TestH2ListenerWithoutTLS(t *testing.T) {
tests := []struct {
serverVersions []string
clientVersions []string
expectedBody string
failed bool
}{
{[]string{"h2c"}, []string{"h2c"}, "HTTP/2.0", false},
{[]string{"h2c"}, []string{"http/1.1"}, "", true},
{[]string{"h1"}, []string{"http/1.1"}, "HTTP/1.1", false},
{[]string{"h1"}, []string{"h2c"}, "", true},
{[]string{"h2c", "h1"}, []string{"h2c"}, "HTTP/2.0", false},
{[]string{"h2c", "h1"}, []string{"http/1.1"}, "HTTP/1.1", false},
}
for _, tc := range tests {
tester := newH2ListenerWithVersionsWithoutTLSTester(t, tc.serverVersions, tc.clientVersions)
t.Logf("running with server versions %v and client versions %v:", tc.serverVersions, tc.clientVersions)
if tc.failed {
resp, err := tester.Client.Get("http://localhost:9080")
if err == nil {
t.Errorf("unexpected response: %d", resp.StatusCode)
}
} else {
tester.AssertGetResponse("http://localhost:9080", 200, tc.expectedBody)
}
}
}
+2 -2
View File
@@ -3,7 +3,7 @@ package integration
import (
"bytes"
"fmt"
"math/rand"
"math/rand/v2"
"net"
"net/http"
"strings"
@@ -54,7 +54,7 @@ func TestHTTPRedirectWrapperWithLargeUpload(t *testing.T) {
const uploadSize = (1024 * 1024) + 1 // 1 MB + 1 byte
// 1 more than an MB
body := make([]byte, uploadSize)
rand.New(rand.NewSource(0)).Read(body)
rand.NewChaCha8([32]byte{}).Read(body)
tester := setupListenerWrapperTest(t, func(writer http.ResponseWriter, request *http.Request) {
buf := new(bytes.Buffer)
+12 -2
View File
@@ -15,7 +15,9 @@ func init() {
}
// MockDNSProvider is a mock DNS provider, for testing config with DNS modules.
type MockDNSProvider struct{}
type MockDNSProvider struct {
Argument string `json:"argument,omitempty"` // optional argument useful for testing
}
// CaddyModule returns the Caddy module information.
func (MockDNSProvider) CaddyModule() caddy.ModuleInfo {
@@ -31,7 +33,15 @@ func (MockDNSProvider) Provision(ctx caddy.Context) error {
}
// UnmarshalCaddyfile sets up the module from Caddyfile tokens.
func (MockDNSProvider) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
func (p *MockDNSProvider) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
d.Next() // consume directive name
if d.NextArg() {
p.Argument = d.Val()
}
if d.NextArg() {
return d.Errf("unexpected argument '%s'", d.Val())
}
return nil
}
+2 -2
View File
@@ -53,7 +53,7 @@ func TestLeafCertLifetimeLessThanIntermediate(t *testing.T) {
}
}
}
`, "json", "certificate lifetime (168h0m0s) should be less than intermediate certificate lifetime (168h0m0s)")
`, "json", "should be less than intermediate certificate lifetime")
}
func TestIntermediateLifetimeLessThanRoot(t *testing.T) {
@@ -103,5 +103,5 @@ func TestIntermediateLifetimeLessThanRoot(t *testing.T) {
}
}
}
`, "json", "intermediate certificate lifetime must be less than root certificate lifetime (86400h0m0s)")
`, "json", "intermediate certificate lifetime must be less than actual root certificate lifetime")
}
+595
View File
@@ -0,0 +1,595 @@
// 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.
// Integration tests for Caddy's PROXY protocol support, covering two distinct
// roles that Caddy can play:
//
// 1. As a PROXY protocol *sender* (reverse proxy outbound transport):
// Caddy receives an inbound request from a test client and the
// reverse_proxy handler forwards it to an upstream with a PROXY protocol
// header (v1 or v2) prepended to the connection. A lightweight backend
// built with go-proxyproto validates that the header was received and
// carries the correct client address.
//
// Transport versions tested:
// - "1.1" -> plain HTTP/1.1 to the upstream
// - "h2c" -> HTTP/2 cleartext (h2c) to the upstream (regression for #7529)
// - "2" -> HTTP/2 over TLS (h2) to the upstream
//
// For each transport version both PROXY protocol v1 and v2 are exercised.
//
// HTTP/3 (h3) is not included because it uses QUIC/UDP and therefore
// bypasses the TCP-level dialContext that injects PROXY protocol headers;
// there is no meaningful h3 + proxy protocol sender combination to test.
//
// 2. As a PROXY protocol *receiver* (server-side listener wrapper):
// A raw TCP client dials Caddy directly, injects a PROXY v2 header
// spoofing a source address, and sends a normal HTTP/1.1 request. The
// Caddy server is configured with the proxy_protocol listener wrapper and
// is expected to surface the spoofed address via the
// {http.request.remote.host} placeholder.
package integration
import (
"crypto/tls"
"encoding/json"
"fmt"
"net"
"net/http"
"net/http/httptest"
"slices"
"strings"
"sync"
"testing"
goproxy "github.com/pires/go-proxyproto"
"golang.org/x/net/http2"
"golang.org/x/net/http2/h2c"
"github.com/caddyserver/caddy/v2/caddytest"
)
// proxyProtoBackend is a minimal HTTP server that sits behind a
// go-proxyproto listener and records the source address that was
// delivered in the PROXY header for each request.
type proxyProtoBackend struct {
mu sync.Mutex
headerAddrs []string // host:port strings extracted from each PROXY header
ln net.Listener
srv *http.Server
}
// newProxyProtoBackend starts a TCP listener wrapped with go-proxyproto on a
// random local port and serves requests with a simple "OK" body. The PROXY
// header source addresses are accumulated in headerAddrs so tests can
// inspect them.
func newProxyProtoBackend(t *testing.T) *proxyProtoBackend {
t.Helper()
b := &proxyProtoBackend{}
rawLn, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("backend: listen: %v", err)
}
// Wrap with go-proxyproto so the PROXY header is stripped and parsed
// before the HTTP server sees the connection. We use REQUIRE so that a
// missing header returns an error instead of silently passing through.
pLn := &goproxy.Listener{
Listener: rawLn,
Policy: func(_ net.Addr) (goproxy.Policy, error) {
return goproxy.REQUIRE, nil
},
}
b.ln = pLn
// Wrap the handler with h2c support so the backend can speak HTTP/2
// cleartext (h2c) as well as plain HTTP/1.1. Without this, Caddy's
// reverse proxy would receive a 'frame too large' error when the
// upstream transport is configured to use h2c.
h2Server := &http2.Server{}
handlerFn := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// go-proxyproto has already updated the net.Conn's remote
// address to the value from the PROXY header; the HTTP server
// surfaces it in r.RemoteAddr.
b.mu.Lock()
b.headerAddrs = append(b.headerAddrs, r.RemoteAddr)
b.mu.Unlock()
w.WriteHeader(http.StatusOK)
_, _ = fmt.Fprint(w, "OK")
})
b.srv = &http.Server{
Handler: h2c.NewHandler(handlerFn, h2Server),
}
go b.srv.Serve(pLn) //nolint:errcheck
t.Cleanup(func() {
_ = b.srv.Close()
_ = rawLn.Close()
})
return b
}
// addr returns the listening address (host:port) of the backend.
func (b *proxyProtoBackend) addr() string {
return b.ln.Addr().String()
}
// recordedAddrs returns a snapshot of all PROXY-header source addresses seen
// so far.
func (b *proxyProtoBackend) recordedAddrs() []string {
b.mu.Lock()
defer b.mu.Unlock()
cp := make([]string, len(b.headerAddrs))
copy(cp, b.headerAddrs)
return cp
}
// tlsProxyProtoBackend is a TLS-enabled backend that sits behind a
// go-proxyproto listener. The PROXY header is stripped before the TLS
// handshake so the layer order on a connection is:
//
// raw TCP → go-proxyproto (strips PROXY header) → TLS handshake → HTTP/2
type tlsProxyProtoBackend struct {
mu sync.Mutex
headerAddrs []string
srv *httptest.Server
}
// newTLSProxyProtoBackend starts a TLS listener that first reads and strips
// PROXY protocol headers (go-proxyproto, REQUIRE policy) and then performs a
// TLS handshake. The backend speaks HTTP/2 over TLS (h2).
//
// The certificate is the standard self-signed certificate generated by
// httptest.Server; the Caddy transport must be configured with
// insecure_skip_verify: true to trust it.
func newTLSProxyProtoBackend(t *testing.T) *tlsProxyProtoBackend {
t.Helper()
b := &tlsProxyProtoBackend{}
handlerFn := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
b.mu.Lock()
b.headerAddrs = append(b.headerAddrs, r.RemoteAddr)
b.mu.Unlock()
w.WriteHeader(http.StatusOK)
_, _ = fmt.Fprint(w, "OK")
})
rawLn, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("tlsBackend: listen: %v", err)
}
// Wrap with go-proxyproto so the PROXY header is consumed before TLS.
pLn := &goproxy.Listener{
Listener: rawLn,
Policy: func(_ net.Addr) (goproxy.Policy, error) {
return goproxy.REQUIRE, nil
},
}
// httptest.NewUnstartedServer lets us replace the listener before
// calling StartTLS(), which wraps our proxyproto listener with
// tls.NewListener. This gives us the right layer order.
b.srv = httptest.NewUnstartedServer(handlerFn)
b.srv.Listener = pLn
// StartTLS enables HTTP/2 on the server automatically.
b.srv.StartTLS()
t.Cleanup(func() {
b.srv.Close()
})
return b
}
// addr returns the listening address (host:port) of the TLS backend.
func (b *tlsProxyProtoBackend) addr() string {
return b.srv.Listener.Addr().String()
}
// tlsConfig returns the *tls.Config used by the backend server.
// Tests can use it to verify cert details if needed.
func (b *tlsProxyProtoBackend) tlsConfig() *tls.Config {
return b.srv.TLS
}
// recordedAddrs returns a snapshot of all PROXY-header source addresses.
func (b *tlsProxyProtoBackend) recordedAddrs() []string {
b.mu.Lock()
defer b.mu.Unlock()
cp := make([]string, len(b.headerAddrs))
copy(cp, b.headerAddrs)
return cp
}
// proxyProtoTLSConfig builds a Caddy JSON configuration that proxies to a TLS
// upstream with PROXY protocol. The transport uses insecure_skip_verify so
// the self-signed certificate generated by httptest.Server is accepted.
func proxyProtoTLSConfig(listenPort int, backendAddr, ppVersion string, transportVersions []string) string {
versionsJSON, _ := json.Marshal(transportVersions)
return fmt.Sprintf(`{
"admin": {
"listen": "localhost:2999"
},
"apps": {
"pki": {
"certificate_authorities": {
"local": {
"install_trust": false
}
}
},
"http": {
"grace_period": 1,
"servers": {
"proxy": {
"listen": [":%d"],
"automatic_https": {
"disable": true
},
"routes": [
{
"handle": [
{
"handler": "reverse_proxy",
"upstreams": [{"dial": "%s"}],
"transport": {
"protocol": "http",
"proxy_protocol": "%s",
"versions": %s,
"tls": {
"insecure_skip_verify": true
}
}
}
]
}
]
}
}
}
}
}`, listenPort, backendAddr, ppVersion, string(versionsJSON))
}
// testTLSProxyProtocolMatrix is the shared implementation for TLS-based proxy
// protocol tests. It mirrors testProxyProtocolMatrix but uses a TLS backend.
func testTLSProxyProtocolMatrix(t *testing.T, ppVersion string, transportVersions []string, numRequests int) {
t.Helper()
backend := newTLSProxyProtoBackend(t)
listenPort := freePort(t)
tester := caddytest.NewTester(t)
tester.WithDefaultOverrides(caddytest.Config{
AdminPort: 2999,
})
cfg := proxyProtoTLSConfig(listenPort, backend.addr(), ppVersion, transportVersions)
tester.InitServer(cfg, "json")
proxyURL := fmt.Sprintf("http://127.0.0.1:%d/", listenPort)
for i := 0; i < numRequests; i++ {
resp, err := tester.Client.Get(proxyURL)
if err != nil {
t.Fatalf("request %d/%d: GET %s: %v", i+1, numRequests, proxyURL, err)
}
resp.Body.Close()
if resp.StatusCode != http.StatusOK {
t.Errorf("request %d/%d: expected status 200, got %d", i+1, numRequests, resp.StatusCode)
}
}
addrs := backend.recordedAddrs()
if len(addrs) == 0 {
t.Fatalf("backend recorded no PROXY protocol addresses (expected at least 1)")
}
for i, addr := range addrs {
host, _, err := net.SplitHostPort(addr)
if err != nil {
t.Errorf("addr[%d] %q: SplitHostPort: %v", i, addr, err)
continue
}
if host != "127.0.0.1" {
t.Errorf("addr[%d]: expected source 127.0.0.1, got %q", i, host)
}
}
}
// proxyProtoConfig builds a Caddy JSON configuration that:
// - listens on listenPort for inbound HTTP requests
// - proxies them to backendAddr with PROXY protocol ppVersion ("v1"/"v2")
// - uses the given transport versions (e.g. ["1.1"] or ["h2c"])
func proxyProtoConfig(listenPort int, backendAddr, ppVersion string, transportVersions []string) string {
versionsJSON, _ := json.Marshal(transportVersions)
return fmt.Sprintf(`{
"admin": {
"listen": "localhost:2999"
},
"apps": {
"pki": {
"certificate_authorities": {
"local": {
"install_trust": false
}
}
},
"http": {
"grace_period": 1,
"servers": {
"proxy": {
"listen": [":%d"],
"automatic_https": {
"disable": true
},
"routes": [
{
"handle": [
{
"handler": "reverse_proxy",
"upstreams": [{"dial": "%s"}],
"transport": {
"protocol": "http",
"proxy_protocol": "%s",
"versions": %s
}
}
]
}
]
}
}
}
}
}`, listenPort, backendAddr, ppVersion, string(versionsJSON))
}
// freePort returns a free local TCP port by binding briefly and releasing it.
func freePort(t *testing.T) int {
t.Helper()
ln, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("freePort: %v", err)
}
port := ln.Addr().(*net.TCPAddr).Port
_ = ln.Close()
return port
}
// TestProxyProtocolV1WithH1 verifies that PROXY protocol v1 headers are sent
// correctly when the transport uses HTTP/1.1 to the upstream.
func TestProxyProtocolV1WithH1(t *testing.T) {
testProxyProtocolMatrix(t, "v1", []string{"1.1"}, 1)
}
// TestProxyProtocolV2WithH1 verifies that PROXY protocol v2 headers are sent
// correctly when the transport uses HTTP/1.1 to the upstream.
func TestProxyProtocolV2WithH1(t *testing.T) {
testProxyProtocolMatrix(t, "v2", []string{"1.1"}, 1)
}
// TestProxyProtocolV1WithH2C verifies that PROXY protocol v1 headers are sent
// correctly when the transport uses h2c (HTTP/2 cleartext) to the upstream.
func TestProxyProtocolV1WithH2C(t *testing.T) {
testProxyProtocolMatrix(t, "v1", []string{"h2c"}, 1)
}
// TestProxyProtocolV2WithH2C verifies that PROXY protocol v2 headers are sent
// correctly when the transport uses h2c (HTTP/2 cleartext) to the upstream.
// This is the primary regression test for github.com/caddyserver/caddy/issues/7529:
// before the fix, the h2 transport opened a new TCP connection per request
// (because req.URL.Host was mangled differently for each request due to the
// varying client port), which caused file-descriptor exhaustion under load.
func TestProxyProtocolV2WithH2C(t *testing.T) {
testProxyProtocolMatrix(t, "v2", []string{"h2c"}, 1)
}
// TestProxyProtocolV2WithH2CMultipleRequests sends several sequential requests
// through the h2c + PROXY-protocol path and confirms that:
// 1. Every request receives a 200 response (no connection exhaustion).
// 2. The backend received at least one PROXY header (connection was reused).
//
// This is the core regression guard for issue #7529: without the fix, a new
// TCP connection was opened per request, quickly exhausting file descriptors.
func TestProxyProtocolV2WithH2CMultipleRequests(t *testing.T) {
testProxyProtocolMatrix(t, "v2", []string{"h2c"}, 5)
}
// TestProxyProtocolV1WithH2 verifies that PROXY protocol v1 headers are sent
// correctly when the transport uses HTTP/2 over TLS (h2) to the upstream.
func TestProxyProtocolV1WithH2(t *testing.T) {
testTLSProxyProtocolMatrix(t, "v1", []string{"2"}, 1)
}
// TestProxyProtocolV2WithH2 verifies that PROXY protocol v2 headers are sent
// correctly when the transport uses HTTP/2 over TLS (h2) to the upstream.
func TestProxyProtocolV2WithH2(t *testing.T) {
testTLSProxyProtocolMatrix(t, "v2", []string{"2"}, 1)
}
// TestProxyProtocolServerAndProxy is an end-to-end matrix test that exercises
// all combinations of PROXY protocol version x transport version.
func TestProxyProtocolServerAndProxy(t *testing.T) {
plainTests := []struct {
name string
ppVersion string
transportVersions []string
numRequests int
}{
{"h1-v1", "v1", []string{"1.1"}, 3},
{"h1-v2", "v2", []string{"1.1"}, 3},
{"h2c-v1", "v1", []string{"h2c"}, 3},
{"h2c-v2", "v2", []string{"h2c"}, 3},
}
for _, tc := range plainTests {
t.Run(tc.name, func(t *testing.T) {
testProxyProtocolMatrix(t, tc.ppVersion, tc.transportVersions, tc.numRequests)
})
}
tlsTests := []struct {
name string
ppVersion string
transportVersions []string
numRequests int
}{
{"h2-v1", "v1", []string{"2"}, 3},
{"h2-v2", "v2", []string{"2"}, 3},
}
for _, tc := range tlsTests {
t.Run(tc.name, func(t *testing.T) {
testTLSProxyProtocolMatrix(t, tc.ppVersion, tc.transportVersions, tc.numRequests)
})
}
}
// testProxyProtocolMatrix is the shared implementation for the proxy protocol
// tests. It:
// 1. Starts a go-proxyproto-wrapped backend.
// 2. Configures Caddy as a reverse proxy with the given PROXY protocol
// version and transport versions.
// 3. Sends numRequests GET requests through Caddy and asserts 200 OK each time.
// 4. Asserts the backend recorded at least one PROXY header whose source host
// is 127.0.0.1 (the loopback address used by the test client).
func testProxyProtocolMatrix(t *testing.T, ppVersion string, transportVersions []string, numRequests int) {
t.Helper()
backend := newProxyProtoBackend(t)
listenPort := freePort(t)
tester := caddytest.NewTester(t)
tester.WithDefaultOverrides(caddytest.Config{
AdminPort: 2999,
})
cfg := proxyProtoConfig(listenPort, backend.addr(), ppVersion, transportVersions)
tester.InitServer(cfg, "json")
// If the test is h2c-only (no "1.1" in versions), reconfigure the test
// client transport to use unencrypted HTTP/2 so we actually exercise the
// h2c code path through Caddy.
if slices.Contains(transportVersions, "h2c") && !slices.Contains(transportVersions, "1.1") {
tr, ok := tester.Client.Transport.(*http.Transport)
if ok {
tr.Protocols = new(http.Protocols)
tr.Protocols.SetHTTP1(false)
tr.Protocols.SetUnencryptedHTTP2(true)
}
}
proxyURL := fmt.Sprintf("http://127.0.0.1:%d/", listenPort)
for i := 0; i < numRequests; i++ {
resp, err := tester.Client.Get(proxyURL)
if err != nil {
t.Fatalf("request %d/%d: GET %s: %v", i+1, numRequests, proxyURL, err)
}
resp.Body.Close()
if resp.StatusCode != http.StatusOK {
t.Errorf("request %d/%d: expected status 200, got %d", i+1, numRequests, resp.StatusCode)
}
}
// The backend must have seen at least one PROXY header. For h1, there is
// one per request; for h2c, requests share the same connection so only one
// header is written at connection establishment.
addrs := backend.recordedAddrs()
if len(addrs) == 0 {
t.Fatalf("backend recorded no PROXY protocol addresses (expected at least 1)")
}
// Every PROXY-decoded source address must be the loopback address since
// the test client always connects from 127.0.0.1.
for i, addr := range addrs {
host, _, err := net.SplitHostPort(addr)
if err != nil {
t.Errorf("addr[%d] %q: SplitHostPort: %v", i, addr, err)
continue
}
if host != "127.0.0.1" {
t.Errorf("addr[%d]: expected source 127.0.0.1, got %q", i, host)
}
}
}
// TestProxyProtocolListenerWrapper verifies that Caddy's
// caddy.listeners.proxy_protocol listener wrapper can successfully parse
// incoming PROXY protocol headers.
//
// The test dials Caddy's listening port directly, injects a raw PROXY v2
// header spoofing source address 10.0.0.1:1234, then sends a normal
// HTTP/1.1 GET request. The Caddy server is configured to echo back the
// remote address ({http.request.remote.host}). The test asserts that the
// echoed address is the spoofed 10.0.0.1.
func TestProxyProtocolListenerWrapper(t *testing.T) {
tester := caddytest.NewTester(t)
tester.InitServer(`{
skip_install_trust
admin localhost:2999
http_port 9080
https_port 9443
grace_period 1ns
servers :9080 {
listener_wrappers {
proxy_protocol {
timeout 5s
allow 127.0.0.0/8
}
}
}
}
http://localhost:9080 {
respond "{http.request.remote.host}"
}`, "caddyfile")
// Dial the Caddy listener directly and inject a PROXY v2 header that
// claims the connection originates from 10.0.0.1:1234.
conn, err := net.Dial("tcp", "127.0.0.1:9080")
if err != nil {
t.Fatalf("dial: %v", err)
}
defer conn.Close()
spoofedSrc := &net.TCPAddr{IP: net.ParseIP("10.0.0.1"), Port: 1234}
spoofedDst := &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 9080}
hdr := goproxy.HeaderProxyFromAddrs(2, spoofedSrc, spoofedDst)
if _, err := hdr.WriteTo(conn); err != nil {
t.Fatalf("write proxy header: %v", err)
}
// Write a minimal HTTP/1.1 GET request.
_, err = fmt.Fprintf(conn,
"GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n")
if err != nil {
t.Fatalf("write HTTP request: %v", err)
}
// Read the raw response and look for the spoofed address in the body.
buf := make([]byte, 4096)
n, _ := conn.Read(buf)
raw := string(buf[:n])
if !strings.Contains(raw, "10.0.0.1") {
t.Errorf("expected spoofed address 10.0.0.1 in response body; full response:\n%s", raw)
}
}
@@ -0,0 +1,328 @@
package integration
import (
"bytes"
"crypto/tls"
"errors"
"fmt"
"io"
"net"
"net/http"
"strings"
"testing"
"time"
"golang.org/x/net/http2"
"golang.org/x/net/http2/hpack"
"github.com/caddyserver/caddy/v2/caddytest"
)
var errExtendedConnectUnsupportedByPeer = errors.New("peer did not advertise RFC 8441 extended CONNECT support")
func TestReverseProxyExtendedConnectOverH2(t *testing.T) {
tester := caddytest.NewTester(t)
backend := newWebsocketUpgradeEchoBackend(t)
defer backend.Close()
tester.InitServer(fmt.Sprintf(`
{
admin localhost:2999
http_port 9080
https_port 9443
grace_period 1ns
skip_install_trust
servers :9443 {
protocols h2
}
}
https://localhost:9443 {
reverse_proxy %s
}
`, backend.addr), "caddyfile")
const payload = "extended-connect-echo\n"
if err := assertExtendedConnectH2Echo("localhost:9443", payload); err != nil {
if errors.Is(err, errExtendedConnectUnsupportedByPeer) {
t.Skipf("skipping extended CONNECT integration test: %v", err)
}
t.Fatalf("extended connect h2 echo failed: %v", err)
}
}
func assertExtendedConnectH2Echo(addr, payload string) error {
conn, err := tlsDialH2(addr)
if err != nil {
return fmt.Errorf("dialing h2 tls: %w", err)
}
defer conn.Close()
if err := conn.SetDeadline(time.Now().Add(5 * time.Second)); err != nil {
return fmt.Errorf("setting deadline: %w", err)
}
fr := http2.NewFramer(conn, conn)
if _, err := conn.Write([]byte(http2.ClientPreface)); err != nil {
return fmt.Errorf("writing client preface: %w", err)
}
if err := fr.WriteSettings(http2.Setting{ID: http2.SettingEnableConnectProtocol, Val: 1}); err != nil {
return fmt.Errorf("writing client settings: %w", err)
}
supported, err := waitForServerSettings(fr)
if err != nil {
return err
}
if !supported {
return errExtendedConnectUnsupportedByPeer
}
if err := waitForSettingsAck(fr); err != nil {
return err
}
if err := writeExtendedConnectHeaders(fr, addr); err != nil {
return err
}
status, err := readResponseStatus(fr, 1)
if err != nil {
return err
}
if status != "200" {
return fmt.Errorf("unexpected extended connect status: got=%s want=200", status)
}
if err := fr.WriteData(1, false, []byte(payload)); err != nil {
return fmt.Errorf("writing stream data: %w", err)
}
echo, err := readStreamData(fr, 1, len(payload))
if err != nil {
return err
}
if echo != payload {
return fmt.Errorf("unexpected echoed payload: got=%q want=%q", echo, payload)
}
_ = fr.WriteRSTStream(1, http2.ErrCodeNo)
return nil
}
func tlsDialH2(addr string) (net.Conn, error) {
var lastErr error
for i := 0; i < 30; i++ {
dialer := &net.Dialer{Timeout: 2 * time.Second}
conn, err := tls.DialWithDialer(dialer, "tcp", addr, &tls.Config{
ServerName: "localhost",
InsecureSkipVerify: true,
NextProtos: []string{"h2"},
})
if err == nil {
return conn, nil
}
lastErr = err
time.Sleep(100 * time.Millisecond)
}
return nil, lastErr
}
func waitForServerSettings(fr *http2.Framer) (bool, error) {
for {
frame, err := fr.ReadFrame()
if err != nil {
return false, fmt.Errorf("reading frame before connect: %w", err)
}
settings, ok := frame.(*http2.SettingsFrame)
if !ok {
continue
}
if settings.IsAck() {
continue
}
supported := false
if err := settings.ForeachSetting(func(s http2.Setting) error {
if s.ID == http2.SettingEnableConnectProtocol && s.Val == 1 {
supported = true
}
return nil
}); err != nil {
return false, fmt.Errorf("reading server settings: %w", err)
}
if err := fr.WriteSettingsAck(); err != nil {
return false, fmt.Errorf("writing settings ack: %w", err)
}
return supported, nil
}
}
func waitForSettingsAck(fr *http2.Framer) error {
for {
frame, err := fr.ReadFrame()
if err != nil {
return fmt.Errorf("reading settings ack: %w", err)
}
settings, ok := frame.(*http2.SettingsFrame)
if ok && settings.IsAck() {
return nil
}
}
}
func writeExtendedConnectHeaders(fr *http2.Framer, addr string) error {
var hb bytes.Buffer
enc := hpack.NewEncoder(&hb)
for _, hf := range []hpack.HeaderField{
{Name: ":method", Value: "CONNECT"},
{Name: ":scheme", Value: "https"},
{Name: ":authority", Value: addr},
{Name: ":path", Value: "/upgrade"},
{Name: ":protocol", Value: "websocket"},
} {
if err := enc.WriteField(hf); err != nil {
return fmt.Errorf("encoding request headers: %w", err)
}
}
if err := fr.WriteHeaders(http2.HeadersFrameParam{
StreamID: 1,
BlockFragment: hb.Bytes(),
EndHeaders: true,
EndStream: false,
}); err != nil {
return fmt.Errorf("writing extended connect headers: %w", err)
}
return nil
}
func readResponseStatus(fr *http2.Framer, streamID uint32) (string, error) {
var block bytes.Buffer
for {
frame, err := fr.ReadFrame()
if err != nil {
return "", fmt.Errorf("reading response headers: %w", err)
}
if rst, ok := frame.(*http2.RSTStreamFrame); ok && rst.StreamID == streamID {
return "", fmt.Errorf("stream reset before response headers: %s", rst.ErrCode)
}
h, ok := frame.(*http2.HeadersFrame)
if !ok || h.StreamID != streamID {
continue
}
if _, err := block.Write(h.HeaderBlockFragment()); err != nil {
return "", fmt.Errorf("buffering response header fragment: %w", err)
}
for !h.HeadersEnded() {
next, err := fr.ReadFrame()
if err != nil {
return "", fmt.Errorf("reading continuation frame: %w", err)
}
c, ok := next.(*http2.ContinuationFrame)
if !ok || c.StreamID != streamID {
continue
}
if _, err := block.Write(c.HeaderBlockFragment()); err != nil {
return "", fmt.Errorf("buffering continuation fragment: %w", err)
}
if c.HeadersEnded() {
break
}
}
break
}
var status string
dec := hpack.NewDecoder(4096, func(f hpack.HeaderField) {
if f.Name == ":status" {
status = f.Value
}
})
if _, err := dec.Write(block.Bytes()); err != nil {
return "", fmt.Errorf("decoding response header block: %w", err)
}
if status == "" {
return "", fmt.Errorf("missing :status in response headers")
}
return status, nil
}
func readStreamData(fr *http2.Framer, streamID uint32, n int) (string, error) {
buf := make([]byte, 0, n)
for len(buf) < n {
frame, err := fr.ReadFrame()
if err != nil {
return "", fmt.Errorf("reading stream data: %w", err)
}
d, ok := frame.(*http2.DataFrame)
if !ok || d.StreamID != streamID {
continue
}
buf = append(buf, d.Data()...)
}
return string(buf[:n]), nil
}
type websocketUpgradeEchoBackend struct {
addr string
ln net.Listener
server *http.Server
}
func newWebsocketUpgradeEchoBackend(t *testing.T) *websocketUpgradeEchoBackend {
t.Helper()
backend := &websocketUpgradeEchoBackend{}
backend.server = &http.Server{
Handler: http.HandlerFunc(backend.serveHTTP),
}
ln, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("listening for websocket backend: %v", err)
}
backend.ln = ln
backend.addr = ln.Addr().String()
go func() {
_ = backend.server.Serve(ln)
}()
return backend
}
func (b *websocketUpgradeEchoBackend) serveHTTP(w http.ResponseWriter, r *http.Request) {
if !strings.EqualFold(r.Header.Get("Connection"), "Upgrade") || !strings.EqualFold(r.Header.Get("Upgrade"), "websocket") {
http.Error(w, "upgrade required", http.StatusUpgradeRequired)
return
}
hijacker, ok := w.(http.Hijacker)
if !ok {
http.Error(w, "hijacking not supported", http.StatusInternalServerError)
return
}
conn, rw, err := hijacker.Hijack()
if err != nil {
return
}
_, _ = rw.WriteString("HTTP/1.1 101 Switching Protocols\r\nConnection: Upgrade\r\nUpgrade: websocket\r\n\r\n")
_ = rw.Flush()
go func() {
defer conn.Close()
_, _ = io.Copy(conn, conn)
}()
}
func (b *websocketUpgradeEchoBackend) Close() {
_ = b.server.Close()
_ = b.ln.Close()
}
+327 -8
View File
@@ -7,8 +7,8 @@ import (
"os"
"runtime"
"strings"
"sync/atomic"
"testing"
"time"
"github.com/caddyserver/caddy/v2/caddytest"
)
@@ -327,6 +327,41 @@ func TestReverseProxyWithPlaceholderTCPDialAddress(t *testing.T) {
}
func TestReverseProxyHealthCheck(t *testing.T) {
// Start lightweight backend servers so they're ready before Caddy's
// active health checker runs; this avoids a startup race where the
// health checker probes backends that haven't yet begun accepting
// connections and marks them unhealthy.
//
// This mirrors how health checks are typically used in practice (to a separate
// backend service) and avoids probing the same Caddy instance while it's still
// provisioning and not ready to accept connections.
// backend server that responds to proxied requests
helloSrv := &http.Server{
Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
_, _ = w.Write([]byte("Hello, World!"))
}),
}
ln0, err := net.Listen("tcp", "127.0.0.1:2020")
if err != nil {
t.Fatalf("failed to listen on 127.0.0.1:2020: %v", err)
}
go helloSrv.Serve(ln0)
t.Cleanup(func() { helloSrv.Close(); ln0.Close() })
// backend server that serves health checks
healthSrv := &http.Server{
Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
_, _ = w.Write([]byte("ok"))
}),
}
ln1, err := net.Listen("tcp", "127.0.0.1:2021")
if err != nil {
t.Fatalf("failed to listen on 127.0.0.1:2021: %v", err)
}
go healthSrv.Serve(ln1)
t.Cleanup(func() { healthSrv.Close(); ln1.Close() })
tester := caddytest.NewTester(t)
tester.InitServer(`
{
@@ -336,12 +371,6 @@ func TestReverseProxyHealthCheck(t *testing.T) {
https_port 9443
grace_period 1ns
}
http://localhost:2020 {
respond "Hello, World!"
}
http://localhost:2021 {
respond "ok"
}
http://localhost:9080 {
reverse_proxy {
to localhost:2020
@@ -355,8 +384,68 @@ func TestReverseProxyHealthCheck(t *testing.T) {
}
}
`, "caddyfile")
tester.AssertGetResponse("http://localhost:9080/", 200, "Hello, World!")
}
time.Sleep(100 * time.Millisecond) // TODO: for some reason this test seems particularly flaky, getting 503 when it should be 200, unless we wait
// TestReverseProxyHealthCheckPortUsed verifies that health_port is actually
// used for active health checks and not the upstream's main port. This is a
// regression test for https://github.com/caddyserver/caddy/issues/7524.
func TestReverseProxyHealthCheckPortUsed(t *testing.T) {
// upstream server: serves proxied requests normally, but returns 503 for
// /health so that if health checks mistakenly hit this port the upstream
// gets marked unhealthy and the proxy returns 503.
upstreamSrv := &http.Server{
Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
if req.URL.Path == "/health" {
w.WriteHeader(http.StatusServiceUnavailable)
return
}
_, _ = w.Write([]byte("Hello, World!"))
}),
}
ln0, err := net.Listen("tcp", "127.0.0.1:2022")
if err != nil {
t.Fatalf("failed to listen on 127.0.0.1:2022: %v", err)
}
go upstreamSrv.Serve(ln0)
t.Cleanup(func() { upstreamSrv.Close(); ln0.Close() })
// separate health check server on the configured health_port: returns 200
// so the upstream is marked healthy only if health checks go to this port.
healthSrv := &http.Server{
Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
_, _ = w.Write([]byte("ok"))
}),
}
ln1, err := net.Listen("tcp", "127.0.0.1:2023")
if err != nil {
t.Fatalf("failed to listen on 127.0.0.1:2023: %v", err)
}
go healthSrv.Serve(ln1)
t.Cleanup(func() { healthSrv.Close(); ln1.Close() })
tester := caddytest.NewTester(t)
tester.InitServer(`
{
skip_install_trust
admin localhost:2999
http_port 9080
https_port 9443
grace_period 1ns
}
http://localhost:9080 {
reverse_proxy {
to localhost:2022
health_uri /health
health_port 2023
health_interval 10ms
health_timeout 100ms
health_passes 1
health_fails 1
}
}
`, "caddyfile")
tester.AssertGetResponse("http://localhost:9080/", 200, "Hello, World!")
}
@@ -474,3 +563,233 @@ func TestReverseProxyHealthCheckUnixSocketWithoutPort(t *testing.T) {
tester.AssertGetResponse("http://localhost:9080/", 200, "Hello, World!")
}
// TestReverseProxyRetryMatchStatusCode verifies that lb_retry_match with a
// CEL expression matching on {rp.status_code} causes the request to be
// retried on the next upstream when the first upstream returns a matching
// status code
func TestReverseProxyRetryMatchStatusCode(t *testing.T) {
// Bad upstream: returns 502
badSrv := &http.Server{
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusBadGateway)
}),
}
badLn, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("failed to listen: %v", err)
}
go badSrv.Serve(badLn)
t.Cleanup(func() { badSrv.Close(); badLn.Close() })
// Good upstream: returns 200
goodSrv := &http.Server{
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("ok"))
}),
}
goodLn, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("failed to listen: %v", err)
}
go goodSrv.Serve(goodLn)
t.Cleanup(func() { goodSrv.Close(); goodLn.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 {
reverse_proxy %s %s {
lb_policy round_robin
lb_retries 1
lb_retry_match {
expression `+"`{rp.status_code} in [502, 503]`"+`
}
}
}
`, goodLn.Addr().String(), badLn.Addr().String()), "caddyfile")
tester.AssertGetResponse("http://localhost:9080/", 200, "ok")
}
// TestReverseProxyRetryMatchHeader verifies that lb_retry_match with a CEL
// expression matching on {rp.header.*} causes the request to be retried when
// the upstream sets a matching response header
func TestReverseProxyRetryMatchHeader(t *testing.T) {
var badHits atomic.Int32
// Bad upstream: returns 200 but signals retry via header
badSrv := &http.Server{
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
badHits.Add(1)
w.Header().Set("X-Upstream-Retry", "true")
w.Write([]byte("bad"))
}),
}
badLn, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("failed to listen: %v", err)
}
go badSrv.Serve(badLn)
t.Cleanup(func() { badSrv.Close(); badLn.Close() })
// Good upstream: returns 200 without retry header
goodSrv := &http.Server{
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("good"))
}),
}
goodLn, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("failed to listen: %v", err)
}
go goodSrv.Serve(goodLn)
t.Cleanup(func() { goodSrv.Close(); goodLn.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 {
reverse_proxy %s %s {
lb_policy round_robin
lb_retries 1
lb_retry_match {
expression `+"`{rp.header.X-Upstream-Retry} == \"true\"`"+`
}
}
}
`, goodLn.Addr().String(), badLn.Addr().String()), "caddyfile")
tester.AssertGetResponse("http://localhost:9080/", 200, "good")
if badHits.Load() != 1 {
t.Errorf("bad upstream hits: got %d, want 1", badHits.Load())
}
}
// TestReverseProxyRetryMatchCombined verifies that a CEL expression combining
// request path matching with response status code matching works correctly -
// only retrying when both conditions are met
func TestReverseProxyRetryMatchCombined(t *testing.T) {
// Upstream: returns 502 for all requests
srv := &http.Server{
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusBadGateway)
}),
}
ln, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("failed to listen: %v", err)
}
go srv.Serve(ln)
t.Cleanup(func() { srv.Close(); ln.Close() })
// Good upstream
goodSrv := &http.Server{
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("ok"))
}),
}
goodLn, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("failed to listen: %v", err)
}
go goodSrv.Serve(goodLn)
t.Cleanup(func() { goodSrv.Close(); goodLn.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 {
reverse_proxy %s %s {
lb_policy round_robin
lb_retries 1
lb_retry_match {
expression `+"`path('/retry*') && {rp.status_code} in [502, 503]`"+`
}
}
}
`, goodLn.Addr().String(), ln.Addr().String()), "caddyfile")
// /retry path matches the expression - should retry to good upstream
tester.AssertGetResponse("http://localhost:9080/retry", 200, "ok")
// /other path does NOT match - should return the 502
req, _ := http.NewRequest(http.MethodGet, "http://localhost:9080/other", nil)
tester.AssertResponse(req, 502, "")
}
// TestReverseProxyRetryMatchIsTransportError verifies that the
// {rp.is_transport_error} == true CEL function correctly identifies transport errors
// and allows retrying them alongside response-based matching
func TestReverseProxyRetryMatchIsTransportError(t *testing.T) {
// Good upstream: returns 200
goodSrv := &http.Server{
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("ok"))
}),
}
goodLn, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("failed to listen: %v", err)
}
go goodSrv.Serve(goodLn)
t.Cleanup(func() { goodSrv.Close(); goodLn.Close() })
// Broken upstream: accepts connections but closes immediately
brokenLn, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("failed to listen: %v", err)
}
t.Cleanup(func() { brokenLn.Close() })
go func() {
for {
conn, err := brokenLn.Accept()
if err != nil {
return
}
conn.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 {
reverse_proxy %s %s {
lb_policy round_robin
lb_retries 1
lb_retry_match {
expression `+"`{rp.is_transport_error} || {rp.status_code} in [502, 503]`"+`
}
}
}
`, goodLn.Addr().String(), brokenLn.Addr().String()), "caddyfile")
// Transport error on broken upstream should be retried to good upstream
tester.AssertGetResponse("http://localhost:9080/", 200, "ok")
}
@@ -0,0 +1,130 @@
package integration
import (
"bufio"
"fmt"
"io"
"net"
"net/textproto"
"strings"
"testing"
"time"
"github.com/caddyserver/caddy/v2/caddytest"
)
func TestReverseProxyUpgradeWithEncode(t *testing.T) {
tester := caddytest.NewTester(t)
backend := newUpgradeEchoBackend(t)
defer backend.Close()
tester.InitServer(fmt.Sprintf(`
{
admin localhost:2999
http_port 9080
https_port 9443
grace_period 1ns
skip_install_trust
}
localhost:9080 {
route {
encode gzip
reverse_proxy %s
}
}
`, backend.addr), "caddyfile")
client := newUpgradedStreamClientWithHeaders(t, map[string]string{
"Accept-Encoding": "gzip",
})
defer client.Close()
if err := client.echo("encode-upgrade\n"); err != nil {
t.Fatalf("upgraded stream echo through encode failed: %v", err)
}
}
func TestReverseProxyUpgradeWithInterceptHandleResponse(t *testing.T) {
tester := caddytest.NewTester(t)
backend := newUpgradeEchoBackend(t)
defer backend.Close()
tester.InitServer(fmt.Sprintf(`
{
admin localhost:2999
http_port 9080
https_port 9443
grace_period 1ns
skip_install_trust
}
localhost:9080 {
route {
intercept {
@upgrade status 101
handle_response @upgrade {
respond "should-not-run"
}
}
reverse_proxy %s
}
}
`, backend.addr), "caddyfile")
client := newUpgradedStreamClientWithHeaders(t, nil)
defer client.Close()
if err := client.echo("intercept-upgrade\n"); err != nil {
t.Fatalf("upgraded stream echo through intercept failed: %v", err)
}
}
func newUpgradedStreamClientWithHeaders(t *testing.T, extraHeaders map[string]string) *upgradedStreamClient {
t.Helper()
conn, err := net.DialTimeout("tcp", "127.0.0.1:9080", 5*time.Second)
if err != nil {
t.Fatalf("dialing caddy: %v", err)
}
requestLines := []string{
"GET /upgrade HTTP/1.1",
"Host: localhost:9080",
"Connection: Upgrade",
"Upgrade: stress-stream",
}
for k, v := range extraHeaders {
requestLines = append(requestLines, k+": "+v)
}
requestLines = append(requestLines, "", "")
if _, err := io.WriteString(conn, strings.Join(requestLines, "\r\n")); err != nil {
_ = conn.Close()
t.Fatalf("writing upgrade request: %v", err)
}
reader := bufio.NewReader(conn)
tproto := textproto.NewReader(reader)
statusLine, err := tproto.ReadLine()
if err != nil {
_ = conn.Close()
t.Fatalf("reading upgrade status line: %v", err)
}
if !strings.Contains(statusLine, "101") {
_ = conn.Close()
t.Fatalf("unexpected upgrade status: %s", statusLine)
}
headers, err := tproto.ReadMIMEHeader()
if err != nil {
_ = conn.Close()
t.Fatalf("reading upgrade headers: %v", err)
}
if !strings.EqualFold(headers.Get("Connection"), "Upgrade") {
_ = conn.Close()
t.Fatalf("unexpected upgrade response headers: %v", headers)
}
return &upgradedStreamClient{conn: conn, reader: reader}
}
@@ -0,0 +1,504 @@
package integration
import (
"bufio"
"bytes"
"fmt"
"io"
"net"
"net/http"
"net/textproto"
"os"
"runtime"
"runtime/debug"
"runtime/pprof"
"strconv"
"strings"
"sync"
"testing"
"time"
"github.com/caddyserver/caddy/v2/caddytest"
)
const (
defaultStressStreamCount = 1
defaultStressReloadCount = 1
defaultStressCloseDelay = 500 * time.Millisecond
)
func TestReverseProxyReloadStressUpgradedStreamsHeapProfiles(t *testing.T) {
tester := caddytest.NewTester(t).WithDefaultOverrides(caddytest.Config{
LoadRequestTimeout: 30 * time.Second,
TestRequestTimeout: 30 * time.Second,
})
backend := newUpgradeEchoBackend(t)
defer backend.Close()
// Three scenarios, each sequential so they don't share Caddy state:
//
// legacy no delay, close on reload immediately (old default)
// close_delay stream_close_delay, the old "keep-alive workaround"
// detached stream_detached, the new explicit detached flag
//
// Reloads are spread across time and interleaved with echo-checks so
// stream health is exercised at each reload boundary, not only at the end.
legacy := runReloadStress(t, tester, backend.addr, "legacy", false, 0)
closeDelay := runReloadStress(t, tester, backend.addr, "close_delay", false, stressCloseDelay(t))
detached := runReloadStress(t, tester, backend.addr, "detached", true, 0)
if legacy.aliveAfterReloads != 0 {
t.Fatalf("legacy mode left %d upgraded streams alive after reloads", legacy.aliveAfterReloads)
}
if closeDelay.aliveBeforeDelayExpiry == 0 {
t.Fatalf("close_delay mode: all streams closed before delay expired (expected them alive)")
}
if closeDelay.aliveAfterReloads != 0 {
t.Fatalf("close_delay mode left %d upgraded streams alive after delay expiry", closeDelay.aliveAfterReloads)
}
if detached.aliveAfterReloads != detached.streamCount {
t.Fatalf("detached mode kept %d/%d upgraded streams alive after reloads", detached.aliveAfterReloads, detached.streamCount)
}
t.Logf("legacy heap: before=%s mid=%s after=%s delta(before→after)=%s objects(before=%d after=%d) handler_frames(before=%d after=%d)",
formatBytes(legacy.beforeReload.HeapInuse),
formatBytes(legacy.midReload.HeapInuse),
formatBytes(legacy.afterReload.HeapInuse),
formatBytesDiff(legacy.beforeReload.HeapInuse, legacy.afterReload.HeapInuse),
legacy.beforeReload.HeapObjects, legacy.afterReload.HeapObjects,
legacy.beforeReload.handlerFrames, legacy.afterReload.handlerFrames,
)
t.Logf("close_delay heap: before=%s mid=%s after=%s delta(before→after)=%s objects(before=%d after=%d) handler_frames(before=%d after=%d)",
formatBytes(closeDelay.beforeReload.HeapInuse),
formatBytes(closeDelay.midReload.HeapInuse),
formatBytes(closeDelay.afterReload.HeapInuse),
formatBytesDiff(closeDelay.beforeReload.HeapInuse, closeDelay.afterReload.HeapInuse),
closeDelay.beforeReload.HeapObjects, closeDelay.afterReload.HeapObjects,
closeDelay.beforeReload.handlerFrames, closeDelay.afterReload.handlerFrames,
)
t.Logf("detached heap: before=%s mid=%s after=%s delta(before→after)=%s objects(before=%d after=%d) handler_frames(before=%d after=%d)",
formatBytes(detached.beforeReload.HeapInuse),
formatBytes(detached.midReload.HeapInuse),
formatBytes(detached.afterReload.HeapInuse),
formatBytesDiff(detached.beforeReload.HeapInuse, detached.afterReload.HeapInuse),
detached.beforeReload.HeapObjects, detached.afterReload.HeapObjects,
detached.beforeReload.handlerFrames, detached.afterReload.handlerFrames,
)
}
type stressRunResult struct {
streamCount int
aliveAfterReloads int
aliveBeforeDelayExpiry int // only meaningful for close_delay mode
beforeReload heapSnapshot
midReload heapSnapshot // after all reloads, before delay expiry clean-up
afterReload heapSnapshot // after all streams have been fully cleaned up
}
type heapSnapshot struct {
HeapInuse uint64
HeapObjects uint64
handlerFrames int
profileBytes int
}
// runReloadStress opens streamCount upgraded streams, then performs reloadCount
// config reloads spread over time. An echo check is performed every 6 reloads so
// stream health is exercised at each reload boundary rather than only at the end.
// closeDelay mirrors the stream_close_delay config option; pass 0 to disable.
func runReloadStress(t *testing.T, tester *caddytest.Tester, backendAddr, mode string, detach bool, closeDelay time.Duration) stressRunResult {
t.Helper()
const echoEvery = 6 // perform an echo check every N reloads
streamCount := envIntOrDefault(t, "CADDY_STRESS_STREAM_COUNT", defaultStressStreamCount)
reloadCount := envIntOrDefault(t, "CADDY_STRESS_RELOAD_COUNT", defaultStressReloadCount)
tester.InitServer(reloadStressConfig(backendAddr, detach, closeDelay, 0), "caddyfile")
clients := make([]*upgradedStreamClient, 0, streamCount)
for i := 0; i < streamCount; i++ {
client := newUpgradedStreamClient(t)
clients = append(clients, client)
if err := client.echo(fmt.Sprintf("%s-warmup-%02d\n", mode, i)); err != nil {
closeClients(clients)
t.Fatalf("warmup echo failed in %s mode: %v", mode, err)
}
}
defer closeClients(clients)
before := captureHeapSnapshot(t)
// Reloads are spread across time; between batches of echoEvery reloads we
// pause briefly and measure stream health so the snapshot reflects real-world
// reload cadence rather than a tight loop.
for i := 1; i <= reloadCount; i++ {
loadCaddyfileConfig(t, reloadStressConfig(backendAddr, detach, closeDelay, i))
// Small pause after each reload to let connection teardown propagate.
time.Sleep(50 * time.Millisecond)
if i%echoEvery == 0 {
alive := countAliveStreams(clients)
t.Logf("%s mode: %d/%d streams alive after reload %d", mode, alive, streamCount, i)
// In detached mode, every stream must survive every reload (upstream unchanged).
if detach {
for j, client := range clients {
if err := client.echo(fmt.Sprintf("%s-mid-%02d-%02d\n", mode, i, j)); err != nil {
t.Fatalf("detached mode stream %d died at reload %d: %v", j, i, err)
}
}
}
}
}
// mid snapshot: after all reloads but before any close_delay timer has fired
// (the delay is long enough to still be running at this point).
mid := captureHeapSnapshot(t)
// For legacy mode: the reloads close streams immediately; wait for that to complete.
// For close_delay mode: streams are still alive here; wait for the delay to fire.
// For detached mode: streams survive indefinitely; no wait needed.
var aliveBeforeDelayExpiry int
aliveAfterReloads := countAliveStreams(clients)
switch {
case detach:
// nothing to wait for
case closeDelay > 0:
// streams should still be alive at this point (delay hasn't expired)
aliveBeforeDelayExpiry = aliveAfterReloads
t.Logf("%s mode: %d/%d streams alive before close_delay expires; waiting %v for cleanup",
mode, aliveBeforeDelayExpiry, streamCount, closeDelay)
time.Sleep(closeDelay + 200*time.Millisecond)
aliveAfterReloads = countAliveStreams(clients)
default:
deadline := time.Now().Add(2 * time.Second)
for aliveAfterReloads > 0 && time.Now().Before(deadline) {
time.Sleep(50 * time.Millisecond)
aliveAfterReloads = countAliveStreams(clients)
}
}
after := captureHeapSnapshot(t)
t.Logf("%s mode heap profile size: before=%dB mid=%dB after=%dB objects(before=%d mid=%d after=%d)",
mode,
before.profileBytes, mid.profileBytes, after.profileBytes,
before.HeapObjects, mid.HeapObjects, after.HeapObjects,
)
return stressRunResult{
streamCount: streamCount,
aliveAfterReloads: aliveAfterReloads,
aliveBeforeDelayExpiry: aliveBeforeDelayExpiry,
beforeReload: before,
midReload: mid,
afterReload: after,
}
}
func envIntOrDefault(t *testing.T, key string, def int) int {
t.Helper()
raw := strings.TrimSpace(os.Getenv(key))
if raw == "" {
return def
}
v, err := strconv.Atoi(raw)
if err != nil || v <= 0 {
t.Fatalf("invalid %s=%q: must be a positive integer", key, raw)
}
return v
}
func stressCloseDelay(t *testing.T) time.Duration {
t.Helper()
const key = "CADDY_STRESS_CLOSE_DELAY"
raw := strings.TrimSpace(os.Getenv(key))
if raw == "" {
return defaultStressCloseDelay
}
v, err := time.ParseDuration(raw)
if err != nil || v <= 0 {
t.Fatalf("invalid %s=%q: must be a positive duration", key, raw)
}
return v
}
func loadCaddyfileConfig(t *testing.T, rawConfig string) {
t.Helper()
client := &http.Client{Timeout: 30 * time.Second}
req, err := http.NewRequest(http.MethodPost, "http://localhost:2999/load", strings.NewReader(rawConfig))
if err != nil {
t.Fatalf("creating load request: %v", err)
}
req.Header.Set("Content-Type", "text/caddyfile")
resp, err := client.Do(req)
if err != nil {
t.Fatalf("loading config: %v", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
t.Fatalf("reading load response: %v", err)
}
if resp.StatusCode != http.StatusOK {
t.Fatalf("loading config failed: status=%d body=%s", resp.StatusCode, body)
}
}
func reloadStressConfig(backendAddr string, detach bool, closeDelay time.Duration, revision int) string {
var directives string
if detach {
directives += "\n\t\tstream_detached"
}
if closeDelay > 0 {
directives += fmt.Sprintf("\n\t\tstream_close_delay %s", closeDelay)
}
return fmt.Sprintf(`
{
admin localhost:2999
http_port 9080
https_port 9443
grace_period 1ns
skip_install_trust
}
localhost:9080 {
reverse_proxy %s {
header_up X-Reload-Revision %d%s
}
}
`, backendAddr, revision, directives)
}
func captureHeapSnapshot(t *testing.T) heapSnapshot {
t.Helper()
runtime.GC()
debug.FreeOSMemory()
var mem runtime.MemStats
runtime.ReadMemStats(&mem)
var buf bytes.Buffer
if err := pprof.Lookup("heap").WriteTo(&buf, 1); err != nil {
t.Fatalf("capturing heap profile: %v", err)
}
profile := buf.String()
return heapSnapshot{
HeapInuse: mem.HeapInuse,
HeapObjects: mem.HeapObjects,
handlerFrames: strings.Count(profile, "modules/caddyhttp/reverseproxy.(*Handler)"),
profileBytes: buf.Len(),
}
}
func countAliveStreams(clients []*upgradedStreamClient) int {
alive := 0
for index, client := range clients {
if err := client.echo(fmt.Sprintf("alive-check-%02d\n", index)); err == nil {
alive++
}
}
return alive
}
func closeClients(clients []*upgradedStreamClient) {
for _, client := range clients {
if client != nil {
_ = client.Close()
}
}
}
func formatBytes(value uint64) string {
const unit = 1024
if value < unit {
return fmt.Sprintf("%d B", value)
}
div, exp := uint64(unit), 0
for n := value / unit; n >= unit; n /= unit {
div *= unit
exp++
}
return fmt.Sprintf("%.1f %ciB", float64(value)/float64(div), "KMGTPE"[exp])
}
func formatBytesDiff(before, after uint64) string {
if after >= before {
return "+" + formatBytes(after-before)
}
return "-" + formatBytes(before-after)
}
type upgradedStreamClient struct {
conn net.Conn
reader *bufio.Reader
mu sync.Mutex
}
func newUpgradedStreamClient(t *testing.T) *upgradedStreamClient {
t.Helper()
conn, err := net.DialTimeout("tcp", "127.0.0.1:9080", 5*time.Second)
if err != nil {
t.Fatalf("dialing caddy: %v", err)
}
request := strings.Join([]string{
"GET /upgrade HTTP/1.1",
"Host: localhost:9080",
"Connection: Upgrade",
"Upgrade: stress-stream",
"",
"",
}, "\r\n")
if _, err := io.WriteString(conn, request); err != nil {
_ = conn.Close()
t.Fatalf("writing upgrade request: %v", err)
}
reader := bufio.NewReader(conn)
tproto := textproto.NewReader(reader)
statusLine, err := tproto.ReadLine()
if err != nil {
_ = conn.Close()
t.Fatalf("reading upgrade status line: %v", err)
}
if !strings.Contains(statusLine, "101") {
_ = conn.Close()
t.Fatalf("unexpected upgrade status: %s", statusLine)
}
headers, err := tproto.ReadMIMEHeader()
if err != nil {
_ = conn.Close()
t.Fatalf("reading upgrade headers: %v", err)
}
if !strings.EqualFold(headers.Get("Connection"), "Upgrade") {
_ = conn.Close()
t.Fatalf("unexpected upgrade response headers: %v", headers)
}
return &upgradedStreamClient{conn: conn, reader: reader}
}
func (c *upgradedStreamClient) echo(payload string) error {
c.mu.Lock()
defer c.mu.Unlock()
deadline := time.Now().Add(1 * time.Second)
if err := c.conn.SetWriteDeadline(deadline); err != nil {
return err
}
if _, err := io.WriteString(c.conn, payload); err != nil {
return err
}
if err := c.conn.SetReadDeadline(deadline); err != nil {
return err
}
buf := make([]byte, len(payload))
if _, err := io.ReadFull(c.reader, buf); err != nil {
return err
}
if string(buf) != payload {
return fmt.Errorf("unexpected echoed payload: got %q want %q", string(buf), payload)
}
return nil
}
func (c *upgradedStreamClient) Close() error {
c.mu.Lock()
defer c.mu.Unlock()
return c.conn.Close()
}
type upgradeEchoBackend struct {
addr string
ln net.Listener
mu sync.Mutex
conns map[net.Conn]struct{}
server *http.Server
}
func newUpgradeEchoBackend(t *testing.T) *upgradeEchoBackend {
t.Helper()
backend := &upgradeEchoBackend{conns: make(map[net.Conn]struct{})}
backend.server = &http.Server{
Handler: http.HandlerFunc(backend.serveHTTP),
}
ln, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("listening for backend: %v", err)
}
backend.ln = ln
backend.addr = ln.Addr().String()
go func() {
_ = backend.server.Serve(ln)
}()
return backend
}
func (b *upgradeEchoBackend) serveHTTP(w http.ResponseWriter, r *http.Request) {
if !strings.EqualFold(r.Header.Get("Connection"), "Upgrade") || !strings.EqualFold(r.Header.Get("Upgrade"), "stress-stream") {
http.Error(w, "upgrade required", http.StatusUpgradeRequired)
return
}
hijacker, ok := w.(http.Hijacker)
if !ok {
http.Error(w, "hijacking not supported", http.StatusInternalServerError)
return
}
conn, rw, err := hijacker.Hijack()
if err != nil {
return
}
b.trackConn(conn)
_, _ = rw.WriteString("HTTP/1.1 101 Switching Protocols\r\nConnection: Upgrade\r\nUpgrade: stress-stream\r\n\r\n")
_ = rw.Flush()
go func() {
defer b.untrackConn(conn)
defer conn.Close()
_, _ = io.Copy(conn, conn)
}()
}
func (b *upgradeEchoBackend) trackConn(conn net.Conn) {
b.mu.Lock()
b.conns[conn] = struct{}{}
b.mu.Unlock()
}
func (b *upgradeEchoBackend) untrackConn(conn net.Conn) {
b.mu.Lock()
delete(b.conns, conn)
b.mu.Unlock()
}
func (b *upgradeEchoBackend) Close() {
_ = b.server.Close()
_ = b.ln.Close()
b.mu.Lock()
defer b.mu.Unlock()
for conn := range b.conns {
_ = conn.Close()
}
clear(b.conns)
}
@@ -0,0 +1,15 @@
# Used by import_block_snippet_non_replaced_block_from_separate_file.caddyfiletest
(snippet) {
header {
reverse_proxy localhost:3000
{block}
}
}
# This snippet being unused by the test Caddyfile is intentional.
# This is to test that a panic runtime error triggered by an out-of-range slice index access
# will not happen again, please see issue #7518 and pull request #7543 for more information
(unused_snippet) {
header SomeHeader SomeValue
}
@@ -0,0 +1,7 @@
# Used by import_block_snippet_invalid_subdirective.caddyfiletest
(test) {
reverse_proxy {
{block}
}
}
+2
View File
@@ -29,6 +29,8 @@
package main
import (
_ "time/tzdata"
caddycmd "github.com/caddyserver/caddy/v2/cmd"
// plug in Caddy modules here
+14 -4
View File
@@ -9,9 +9,14 @@ import (
)
var defaultFactory = newRootCommandFactory(func() *cobra.Command {
return &cobra.Command{
Use: "caddy",
Long: `Caddy is an extensible server platform written in Go.
bin := caddy.CustomBinaryName
if bin == "" {
bin = "caddy"
}
long := caddy.CustomLongDescription
if long == "" {
long = `Caddy is an extensible server platform written in Go.
At its core, Caddy merely manages configuration. Modules are plugged
in statically at compile-time to provide useful functionality. Caddy's
@@ -91,7 +96,12 @@ package installers: https://caddyserver.com/docs/install
Instructions for running Caddy in production are also available:
https://caddyserver.com/docs/running
`,
`
}
return &cobra.Command{
Use: bin,
Long: long,
Example: ` $ caddy run
$ caddy run --config caddy.json
$ caddy reload --config caddy.json
+116 -23
View File
@@ -74,7 +74,7 @@ func cmdStart(fl Flags) (int, error) {
// ensure it's the process we're expecting - we can be
// sure by giving it some random bytes and having it echo
// them back to us)
cmd := exec.Command(os.Args[0], "run", "--pingback", ln.Addr().String())
cmd := exec.Command(os.Args[0], "run", "--pingback", ln.Addr().String()) //nolint:gosec // no command injection that I can determine...
// we should be able to run caddy in relative paths
if errors.Is(cmd.Err, exec.ErrDot) {
cmd.Err = nil
@@ -172,9 +172,19 @@ func cmdStart(fl Flags) (int, error) {
func cmdRun(fl Flags) (int, error) {
caddy.TrapSignals()
logger := caddy.Log()
// set up buffered logging for early startup
// so that we can hold onto logs until after
// the config is loaded (or fails to load)
// so that we can write the logs to the user's
// configured output. we must be sure to flush
// on any error before the config is loaded.
logger, defaultLogger, logBuffer := caddy.BufferedLog()
undoMaxProcs := setResourceLimits(logger)
defer undoMaxProcs()
// release the local reference to the undo function so it can be GC'd;
// the deferred call above has already captured the actual function value.
undoMaxProcs = nil //nolint:ineffassign,wastedassign
configFlag := fl.String("config")
configAdapterFlag := fl.String("adapter")
@@ -187,6 +197,7 @@ func cmdRun(fl Flags) (int, error) {
// load all additional envs as soon as possible
err := handleEnvFileFlag(fl)
if err != nil {
logBuffer.FlushTo(defaultLogger)
return caddy.ExitCodeFailedStartup, err
}
@@ -204,6 +215,7 @@ func cmdRun(fl Flags) (int, error) {
logger.Info("no autosave file exists", zap.String("autosave_file", caddy.ConfigAutosavePath))
resumeFlag = false
} else if err != nil {
logBuffer.FlushTo(defaultLogger)
return caddy.ExitCodeFailedStartup, err
} else {
if configFlag == "" {
@@ -219,9 +231,11 @@ func cmdRun(fl Flags) (int, error) {
}
// we don't use 'else' here since this value might have been changed in 'if' block; i.e. not mutually exclusive
var configFile string
var adapterUsed string
if !resumeFlag {
config, configFile, err = LoadConfig(configFlag, configAdapterFlag)
config, configFile, adapterUsed, err = LoadConfig(configFlag, configAdapterFlag)
if err != nil {
logBuffer.FlushTo(defaultLogger)
return caddy.ExitCodeFailedStartup, err
}
}
@@ -236,11 +250,35 @@ func cmdRun(fl Flags) (int, error) {
}
}
// If we have a source config file (we're running via 'caddy run --config ...'),
// record it so SIGUSR1 can reload from the same file. Also provide a callback
// that knows how to load/adapt that source when requested by the main process.
if configFile != "" {
caddy.SetLastConfig(configFile, adapterUsed, func(file, adapter string) error {
cfg, _, _, err := LoadConfig(file, adapter)
if err != nil {
return err
}
return caddy.Load(cfg, true)
})
}
// run the initial config
err = caddy.Load(config, true)
if err != nil {
logBuffer.FlushTo(defaultLogger)
return caddy.ExitCodeFailedStartup, fmt.Errorf("loading initial config: %v", err)
}
// release the reference to the config so it can be GC'd
config = nil //nolint:ineffassign,wastedassign
// at this stage the config will have replaced the
// default logger to the configured one, so we can
// log normally, now that the config is running.
// also clear our ref to the buffer so it can get GC'd
logger = caddy.Log()
defaultLogger = nil //nolint:ineffassign,wastedassign
logBuffer = nil //nolint:wastedassign,ineffassign
logger.Info("serving initial configuration")
// if we are to report to another process the successful start
@@ -256,18 +294,22 @@ func cmdRun(fl Flags) (int, error) {
return caddy.ExitCodeFailedStartup,
fmt.Errorf("dialing confirmation address: %v", err)
}
defer conn.Close()
_, err = conn.Write(confirmationBytes)
if err != nil {
return caddy.ExitCodeFailedStartup,
fmt.Errorf("writing confirmation bytes to %s: %v", pingbackFlag, err)
}
// close (non-defer because we `select {}` below)
// and release references so they can be GC'd
conn.Close()
confirmationBytes = nil //nolint:ineffassign,wastedassign
conn = nil //nolint:wastedassign,ineffassign
}
// if enabled, reload config file automatically on changes
// (this better only be used in dev!)
if watchFlag {
go watchConfigFile(configFile, configAdapterFlag)
go watchConfigFile(configFile, adapterUsed)
}
// warn if the environment does not provide enough information about the disk
@@ -289,6 +331,9 @@ func cmdRun(fl Flags) (int, error) {
}
}
// release the last local logger reference
logger = nil //nolint:wastedassign,ineffassign
select {}
}
@@ -319,7 +364,7 @@ func cmdReload(fl Flags) (int, error) {
forceFlag := fl.Bool("force")
// get the config in caddy's native format
config, configFile, err := LoadConfig(configFlag, configAdapterFlag)
config, configFile, adapterUsed, err := LoadConfig(configFlag, configAdapterFlag)
if err != nil {
return caddy.ExitCodeFailedStartup, err
}
@@ -327,7 +372,7 @@ func cmdReload(fl Flags) (int, error) {
return caddy.ExitCodeFailedStartup, fmt.Errorf("no config file to load")
}
adminAddr, err := DetermineAdminAPIAddress(addressFlag, config, configFlag, configAdapterFlag)
adminAddr, err := DetermineAdminAPIAddress(addressFlag, config, configFile, configAdapterFlag)
if err != nil {
return caddy.ExitCodeFailedStartup, fmt.Errorf("couldn't determine admin API address: %v", err)
}
@@ -337,6 +382,10 @@ func cmdReload(fl Flags) (int, error) {
if forceFlag {
headers.Set("Cache-Control", "must-revalidate")
}
// Provide the source file/adapter to the running process so it can
// preserve its last-config knowledge if this reload came from the same source.
headers.Set("Caddy-Config-Source-File", configFile)
headers.Set("Caddy-Config-Source-Adapter", adapterUsed)
resp, err := AdminAPIRequest(adminAddr, http.MethodPost, "/load", headers, bytes.NewReader(config))
if err != nil {
@@ -362,11 +411,65 @@ func cmdBuildInfo(_ Flags) (int, error) {
return caddy.ExitCodeSuccess, nil
}
// jsonModuleInfo holds metadata about a Caddy module for JSON output.
type jsonModuleInfo struct {
ModuleName string `json:"module_name"`
ModuleType string `json:"module_type"`
Version string `json:"version,omitempty"`
PackageURL string `json:"package_url,omitempty"`
}
func cmdListModules(fl Flags) (int, error) {
packages := fl.Bool("packages")
versions := fl.Bool("versions")
skipStandard := fl.Bool("skip-standard")
jsonOutput := fl.Bool("json")
// Organize modules by whether they come with the standard distribution
standard, nonstandard, unknown, err := getModules()
if err != nil {
// If module info can't be fetched, just print the IDs and exit
for _, m := range caddy.Modules() {
fmt.Println(m)
}
return caddy.ExitCodeSuccess, nil
}
// Logic for JSON output
if jsonOutput {
output := []jsonModuleInfo{}
// addToOutput is a helper to convert internal module info to the JSON-serializable struct
addToOutput := func(list []moduleInfo, moduleType string) {
for _, mi := range list {
item := jsonModuleInfo{
ModuleName: mi.caddyModuleID,
ModuleType: moduleType, // Mapping the type here
}
if mi.goModule != nil {
item.Version = mi.goModule.Version
item.PackageURL = mi.goModule.Path
}
output = append(output, item)
}
}
// Pass the respective type for each category
if !skipStandard {
addToOutput(standard, "standard")
}
addToOutput(nonstandard, "non-standard")
addToOutput(unknown, "unknown")
jsonBytes, err := json.MarshalIndent(output, "", " ")
if err != nil {
return caddy.ExitCodeFailedQuit, err
}
fmt.Println(string(jsonBytes))
return caddy.ExitCodeSuccess, nil
}
// Logic for Text output (Fallback)
printModuleInfo := func(mi moduleInfo) {
fmt.Print(mi.caddyModuleID)
if versions && mi.goModule != nil {
@@ -384,16 +487,6 @@ func cmdListModules(fl Flags) (int, error) {
fmt.Println()
}
// organize modules by whether they come with the standard distribution
standard, nonstandard, unknown, err := getModules()
if err != nil {
// oh well, just print the module IDs and exit
for _, m := range caddy.Modules() {
fmt.Println(m)
}
return caddy.ExitCodeSuccess, nil
}
// Standard modules (always shipped with Caddy)
if !skipStandard {
if len(standard) > 0 {
@@ -412,8 +505,8 @@ func cmdListModules(fl Flags) (int, error) {
for _, mod := range nonstandard {
printModuleInfo(mod)
}
fmt.Printf("\n Non-standard modules: %d\n", len(nonstandard))
}
fmt.Printf("\n Non-standard modules: %d\n", len(nonstandard))
// Unknown modules (couldn't get Caddy module info)
if len(unknown) > 0 {
@@ -423,8 +516,8 @@ func cmdListModules(fl Flags) (int, error) {
for _, mod := range unknown {
printModuleInfo(mod)
}
fmt.Printf("\n Unknown modules: %d\n", len(unknown))
}
fmt.Printf("\n Unknown modules: %d\n", len(unknown))
return caddy.ExitCodeSuccess, nil
}
@@ -551,7 +644,7 @@ func cmdValidateConfig(fl Flags) (int, error) {
fmt.Errorf("input file required when there is no Caddyfile in current directory (use --config flag)")
}
input, _, err := LoadConfig(configFlag, adapterFlag)
input, _, _, err := LoadConfig(configFlag, adapterFlag)
if err != nil {
return caddy.ExitCodeFailedStartup, err
}
@@ -604,7 +697,7 @@ func cmdFmt(fl Flags) (int, error) {
output := caddyfile.Format(input)
if fl.Bool("overwrite") {
if err := os.WriteFile(configFile, output, 0o600); err != nil {
if err := os.WriteFile(configFile, output, 0o600); err != nil { //nolint:gosec // path traversal is not really a thing here, this is either "Caddyfile" or admin-controlled
return caddy.ExitCodeFailedStartup, fmt.Errorf("overwriting formatted file: %v", err)
}
return caddy.ExitCodeSuccess, nil
@@ -727,7 +820,7 @@ func AdminAPIRequest(adminAddr, method, uri string, headers http.Header, body io
},
}
resp, err := client.Do(req)
resp, err := client.Do(req) //nolint:gosec // the only SSRF here would be self-sabatoge I think
if err != nil {
return nil, fmt.Errorf("performing request: %v", err)
}
@@ -766,7 +859,7 @@ func DetermineAdminAPIAddress(address string, config []byte, configFile, configA
loadedConfig := config
if len(loadedConfig) == 0 {
// get the config in caddy's native format
loadedConfig, loadedConfigFile, err = LoadConfig(configFile, configAdapter)
loadedConfig, loadedConfigFile, _, err = LoadConfig(configFile, configAdapter)
if err != nil {
return "", err
}

Some files were not shown because too many files have changed in this diff Show More