From 50778b55425d378f709599c0d424b0138af592f4 Mon Sep 17 00:00:00 2001 From: Hyeonggeun Oh Date: Tue, 7 Jan 2025 16:21:57 -0800 Subject: [PATCH 01/22] fix: disable h3 for unix domain socket (#6769) --- modules/caddyhttp/app.go | 42 ++++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/modules/caddyhttp/app.go b/modules/caddyhttp/app.go index 850d3aa8f..5477ed8fe 100644 --- a/modules/caddyhttp/app.go +++ b/modules/caddyhttp/app.go @@ -529,21 +529,6 @@ func (app *App) Start() error { // enable TLS if there is a policy and if this is not the HTTP port useTLS := len(srv.TLSConnPolicies) > 0 && int(listenAddr.StartPort+portOffset) != app.httpPort() - // enable HTTP/3 if configured - if h3ok && useTLS { - app.logger.Info("enabling HTTP/3 listener", zap.String("addr", hostport)) - if err := srv.serveHTTP3(listenAddr.At(portOffset), tlsCfg); err != nil { - return err - } - } - - if h3ok && !useTLS { - // Can only serve h3 with TLS enabled - app.logger.Warn("HTTP/3 skipped because it requires TLS", - zap.String("network", listenAddr.Network), - zap.String("addr", hostport)) - } - if h1ok || h2ok && useTLS || h2cok { // create the listener for this socket lnAny, err := listenAddr.Listen(app.ctx, portOffset, net.ListenConfig{KeepAlive: time.Duration(srv.KeepAliveInterval)}) @@ -614,6 +599,33 @@ func (app *App) Start() error { zap.String("network", listenAddr.Network), zap.String("addr", hostport)) } + + if h3ok { + // Can't serve HTTP/3 on the same socket as HTTP/1 and 2 because it uses + // a different transport mechanism... which is fine, but the OS doesn't + // differentiate between a SOCK_STREAM file and a SOCK_DGRAM file; they + // are still one file on the system. So even though "unixpacket" and + // "unixgram" are different network types just as "tcp" and "udp" are, + // the OS will not let us use the same file as both STREAM and DGRAM. + if listenAddr.IsUnixNetwork() { + app.logger.Warn("HTTP/3 disabled because Unix can't multiplex STREAM and DGRAM on same socket", + zap.String("file", hostport)) + continue + } + + if useTLS { + // enable HTTP/3 if configured + app.logger.Info("enabling HTTP/3 listener", zap.String("addr", hostport)) + if err := srv.serveHTTP3(listenAddr.At(portOffset), tlsCfg); err != nil { + return err + } + } else { + // Can only serve h3 with TLS enabled + app.logger.Warn("HTTP/3 skipped because it requires TLS", + zap.String("network", listenAddr.Network), + zap.String("addr", hostport)) + } + } } } From 1f927d6b07d52d7cf46f1f3020c1ea5993a3e5e8 Mon Sep 17 00:00:00 2001 From: Matt Holt Date: Tue, 7 Jan 2025 21:51:03 -0700 Subject: [PATCH 02/22] log: Only chmod if permission bits differ; make log dir (#6761) * log: Only chmod if permission bits differ Follow-up to #6314 and https://caddy.community/t/caddy-2-9-0-breaking-change/27576/11 * Fix test * Refactor FileWriter * Ooooh octal... right... --- modules/logging/filewriter.go | 92 ++++++++++++++++++------------ modules/logging/filewriter_test.go | 5 +- 2 files changed, 58 insertions(+), 39 deletions(-) diff --git a/modules/logging/filewriter.go b/modules/logging/filewriter.go index 62d500dca..ef3211cbb 100644 --- a/modules/logging/filewriter.go +++ b/modules/logging/filewriter.go @@ -20,6 +20,7 @@ import ( "io" "math" "os" + "path/filepath" "strconv" "github.com/dustin/go-humanize" @@ -146,51 +147,68 @@ func (fw FileWriter) WriterKey() string { // OpenWriter opens a new file writer. func (fw FileWriter) OpenWriter() (io.WriteCloser, error) { - if fw.Mode == 0 { - fw.Mode = 0o600 + modeIfCreating := os.FileMode(fw.Mode) + if modeIfCreating == 0 { + modeIfCreating = 0o600 } - // roll log files by default - if fw.Roll == nil || *fw.Roll { - if fw.RollSizeMB == 0 { - fw.RollSizeMB = 100 - } - if fw.RollCompress == nil { - compress := true - fw.RollCompress = &compress - } - if fw.RollKeep == 0 { - fw.RollKeep = 10 - } - if fw.RollKeepDays == 0 { - fw.RollKeepDays = 90 - } + // roll log files as a sensible default to avoid disk space exhaustion + roll := fw.Roll == nil || *fw.Roll - // create the file if it does not exist with the right mode. - // lumberjack will reuse the file mode across log rotation. - f_tmp, err := os.OpenFile(fw.Filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, os.FileMode(fw.Mode)) + // create the file if it does not exist; create with the configured mode, or default + // to restrictive if not set. (lumberjack will reuse the file mode across log rotation) + if err := os.MkdirAll(filepath.Dir(fw.Filename), 0o700); err != nil { + return nil, err + } + file, err := os.OpenFile(fw.Filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, modeIfCreating) + if err != nil { + return nil, err + } + info, err := file.Stat() + if roll { + file.Close() // lumberjack will reopen it on its own + } + + // Ensure already existing files have the right mode, since OpenFile will not set the mode in such case. + if configuredMode := os.FileMode(fw.Mode); configuredMode != 0 { if err != nil { - return nil, err + return nil, fmt.Errorf("unable to stat log file to see if we need to set permissions: %v", err) } - f_tmp.Close() - // ensure already existing files have the right mode, - // since OpenFile will not set the mode in such case. - if err = os.Chmod(fw.Filename, os.FileMode(fw.Mode)); err != nil { - return nil, err + // only chmod if the configured mode is different + if info.Mode()&os.ModePerm != configuredMode&os.ModePerm { + if err = os.Chmod(fw.Filename, configuredMode); err != nil { + return nil, err + } } - - return &lumberjack.Logger{ - Filename: fw.Filename, - MaxSize: fw.RollSizeMB, - MaxAge: fw.RollKeepDays, - MaxBackups: fw.RollKeep, - LocalTime: fw.RollLocalTime, - Compress: *fw.RollCompress, - }, nil } - // otherwise just open a regular file - return os.OpenFile(fw.Filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, os.FileMode(fw.Mode)) + // if not rolling, then the plain file handle is all we need + if !roll { + return file, nil + } + + // otherwise, return a rolling log + if fw.RollSizeMB == 0 { + fw.RollSizeMB = 100 + } + if fw.RollCompress == nil { + compress := true + fw.RollCompress = &compress + } + if fw.RollKeep == 0 { + fw.RollKeep = 10 + } + if fw.RollKeepDays == 0 { + fw.RollKeepDays = 90 + } + return &lumberjack.Logger{ + Filename: fw.Filename, + MaxSize: fw.RollSizeMB, + MaxAge: fw.RollKeepDays, + MaxBackups: fw.RollKeep, + LocalTime: fw.RollLocalTime, + Compress: *fw.RollCompress, + }, nil } // UnmarshalCaddyfile sets up the module from Caddyfile tokens. Syntax: diff --git a/modules/logging/filewriter_test.go b/modules/logging/filewriter_test.go index 0c54a6590..f9072f98a 100644 --- a/modules/logging/filewriter_test.go +++ b/modules/logging/filewriter_test.go @@ -20,6 +20,7 @@ import ( "encoding/json" "os" "path" + "path/filepath" "syscall" "testing" @@ -77,7 +78,7 @@ func TestFileCreationMode(t *testing.T) { t.Fatalf("failed to create tempdir: %v", err) } defer os.RemoveAll(dir) - fpath := path.Join(dir, "test.log") + fpath := filepath.Join(dir, "test.log") tt.fw.Filename = fpath logger, err := tt.fw.OpenWriter() @@ -92,7 +93,7 @@ func TestFileCreationMode(t *testing.T) { } if st.Mode() != tt.wantMode { - t.Errorf("file mode is %v, want %v", st.Mode(), tt.wantMode) + t.Errorf("%s: file mode is %v, want %v", tt.name, st.Mode(), tt.wantMode) } }) } From e48b75843b7eff2948b573391fb41535b5e333ef Mon Sep 17 00:00:00 2001 From: Arsh <69170106+lilnasy@users.noreply.github.com> Date: Wed, 8 Jan 2025 11:18:06 +0530 Subject: [PATCH 03/22] header: `match` subdirective for response matching (#6765) --- .../caddyfile_adapt/header.caddyfiletest | 14 ++++++++++++ modules/caddyhttp/headers/caddyfile.go | 10 +++++++++ modules/caddyhttp/headers/headers_test.go | 22 +++++++++++++++++++ 3 files changed, 46 insertions(+) diff --git a/caddytest/integration/caddyfile_adapt/header.caddyfiletest b/caddytest/integration/caddyfile_adapt/header.caddyfiletest index ec2a842a3..a0af24ff6 100644 --- a/caddytest/integration/caddyfile_adapt/header.caddyfiletest +++ b/caddytest/integration/caddyfile_adapt/header.caddyfiletest @@ -12,10 +12,14 @@ @images path /images/* header @images { Cache-Control "public, max-age=3600, stale-while-revalidate=86400" + match { + status 200 + } } header { +Link "Foo" +Link "Bar" + match status 200 } header >Set Defer header >Replace Deferred Replacement @@ -42,6 +46,11 @@ { "handler": "headers", "response": { + "require": { + "status_code": [ + 200 + ] + }, "set": { "Cache-Control": [ "public, max-age=3600, stale-while-revalidate=86400" @@ -136,6 +145,11 @@ "Foo", "Bar" ] + }, + "require": { + "status_code": [ + 200 + ] } } }, diff --git a/modules/caddyhttp/headers/caddyfile.go b/modules/caddyhttp/headers/caddyfile.go index e55e9fab2..f060471b1 100644 --- a/modules/caddyhttp/headers/caddyfile.go +++ b/modules/caddyhttp/headers/caddyfile.go @@ -99,6 +99,16 @@ func parseCaddyfile(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error) handler.Response.Deferred = true continue } + if field == "match" { + responseMatchers := make(map[string]caddyhttp.ResponseMatcher) + err := caddyhttp.ParseNamedResponseMatcher(h.NewFromNextSegment(), responseMatchers) + if err != nil { + return nil, err + } + matcher := responseMatchers["match"] + handler.Response.Require = &matcher + continue + } if hasArgs { return nil, h.Err("cannot specify headers in both arguments and block") // because it would be weird } diff --git a/modules/caddyhttp/headers/headers_test.go b/modules/caddyhttp/headers/headers_test.go index d74e6fc3a..9808c29c9 100644 --- a/modules/caddyhttp/headers/headers_test.go +++ b/modules/caddyhttp/headers/headers_test.go @@ -143,6 +143,28 @@ func TestHandler(t *testing.T) { "Cache-Control": []string{"no-cache"}, }, }, + { // same as above, but checks that response headers are left alone when "Require" conditions are unmet + handler: Handler{ + Response: &RespHeaderOps{ + Require: &caddyhttp.ResponseMatcher{ + Headers: http.Header{ + "Cache-Control": nil, + }, + }, + HeaderOps: &HeaderOps{ + Add: http.Header{ + "Cache-Control": []string{"no-cache"}, + }, + }, + }, + }, + respHeader: http.Header{ + "Cache-Control": []string{"something"}, + }, + expectedRespHeader: http.Header{ + "Cache-Control": []string{"something"}, + }, + }, { handler: Handler{ Response: &RespHeaderOps{ From 0e570e0cc717f02cf3800ae741df70cd074c7275 Mon Sep 17 00:00:00 2001 From: Matthew Holt Date: Wed, 8 Jan 2025 07:43:27 -0700 Subject: [PATCH 04/22] go.mod: UPgrade CertMagic to 0.21.6 (fix ARI handshake maintenance) --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 67443562f..d36925d81 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/Masterminds/sprig/v3 v3.3.0 github.com/alecthomas/chroma/v2 v2.14.0 github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b - github.com/caddyserver/certmagic v0.21.5 + github.com/caddyserver/certmagic v0.21.6 github.com/caddyserver/zerossl v0.1.3 github.com/dustin/go-humanize v1.0.1 github.com/go-chi/chi/v5 v5.0.12 diff --git a/go.sum b/go.sum index 538304a28..169666412 100644 --- a/go.sum +++ b/go.sum @@ -89,8 +89,8 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= -github.com/caddyserver/certmagic v0.21.5 h1:iIga4nZRgd27EIEbX7RZmoRMul+EVBn/h7bAGL83dnY= -github.com/caddyserver/certmagic v0.21.5/go.mod h1:n1sCo7zV1Ez2j+89wrzDxo4N/T1Ws/Vx8u5NvuBFabw= +github.com/caddyserver/certmagic v0.21.6 h1:1th6GfprVfsAtFNOu4StNMF5IxK5XiaI0yZhAHlZFPE= +github.com/caddyserver/certmagic v0.21.6/go.mod h1:n1sCo7zV1Ez2j+89wrzDxo4N/T1Ws/Vx8u5NvuBFabw= github.com/caddyserver/zerossl v0.1.3 h1:onS+pxp3M8HnHpN5MMbOMyNjmTheJyWRaZYwn+YTAyA= github.com/caddyserver/zerossl v0.1.3/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= From 1f35a8a4029a338e89998acafa95e1e931a46a27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Thu, 9 Jan 2025 19:54:44 +0100 Subject: [PATCH 05/22] fastcgi: improve parsePHPFastCGI docs (#6779) --- modules/caddyhttp/reverseproxy/fastcgi/caddyfile.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/caddyhttp/reverseproxy/fastcgi/caddyfile.go b/modules/caddyhttp/reverseproxy/fastcgi/caddyfile.go index 6fe7df3fd..5db73a4a2 100644 --- a/modules/caddyhttp/reverseproxy/fastcgi/caddyfile.go +++ b/modules/caddyhttp/reverseproxy/fastcgi/caddyfile.go @@ -131,15 +131,18 @@ func (t *Transport) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { // is equivalent to a route consisting of: // // # Add trailing slash for directory requests +// # This redirection is automatically disabled if "{http.request.uri.path}/index.php" +// # doesn't appear in the try_files list // @canonicalPath { // file {path}/index.php // not path */ // } // redir @canonicalPath {path}/ 308 // -// # If the requested file does not exist, try index files +// # If the requested file does not exist, try index files and assume index.php always exists // @indexFiles file { // try_files {path} {path}/index.php index.php +// try_policy first_exist_fallback // split_path .php // } // rewrite @indexFiles {http.matchers.file.relative} From 2c4295ee48f494bc8dda5fa09b37612d520c8b3b Mon Sep 17 00:00:00 2001 From: Matthew Holt Date: Thu, 9 Jan 2025 13:57:00 -0700 Subject: [PATCH 06/22] caddytls: Initial support for ACME profiles Still very experimental; only deployed to LE staging so far. --- go.mod | 4 ++-- go.sum | 8 ++++---- modules/caddytls/acmeissuer.go | 15 +++++++++++++++ 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index d36925d81..495d893b1 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/Masterminds/sprig/v3 v3.3.0 github.com/alecthomas/chroma/v2 v2.14.0 github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b - github.com/caddyserver/certmagic v0.21.6 + github.com/caddyserver/certmagic v0.21.7-0.20250109205135-32654015b016 github.com/caddyserver/zerossl v0.1.3 github.com/dustin/go-humanize v1.0.1 github.com/go-chi/chi/v5 v5.0.12 @@ -17,7 +17,7 @@ require ( github.com/google/uuid v1.6.0 github.com/klauspost/compress v1.17.11 github.com/klauspost/cpuid/v2 v2.2.9 - github.com/mholt/acmez/v3 v3.0.0 + github.com/mholt/acmez/v3 v3.0.1 github.com/prometheus/client_golang v1.19.1 github.com/quic-go/quic-go v0.48.2 github.com/smallstep/certificates v0.26.1 diff --git a/go.sum b/go.sum index 169666412..c2179ad87 100644 --- a/go.sum +++ b/go.sum @@ -89,8 +89,8 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= -github.com/caddyserver/certmagic v0.21.6 h1:1th6GfprVfsAtFNOu4StNMF5IxK5XiaI0yZhAHlZFPE= -github.com/caddyserver/certmagic v0.21.6/go.mod h1:n1sCo7zV1Ez2j+89wrzDxo4N/T1Ws/Vx8u5NvuBFabw= +github.com/caddyserver/certmagic v0.21.7-0.20250109205135-32654015b016 h1:bwnFMkCXIgw3WO7vvMwpr7Zf8qfADmMzYe6mxSKC7zI= +github.com/caddyserver/certmagic v0.21.7-0.20250109205135-32654015b016/go.mod h1:LCPG3WLxcnjVKl/xpjzM0gqh0knrKKKiO5WVttX2eEI= github.com/caddyserver/zerossl v0.1.3 h1:onS+pxp3M8HnHpN5MMbOMyNjmTheJyWRaZYwn+YTAyA= github.com/caddyserver/zerossl v0.1.3/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= @@ -344,8 +344,8 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= -github.com/mholt/acmez/v3 v3.0.0 h1:r1NcjuWR0VaKP2BTjDK9LRFBw/WvURx3jlaEUl9Ht8E= -github.com/mholt/acmez/v3 v3.0.0/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ= +github.com/mholt/acmez/v3 v3.0.1 h1:4PcjKjaySlgXK857aTfDuRbmnM5gb3Ruz3tvoSJAUp8= +github.com/mholt/acmez/v3 v3.0.1/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ= github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ= diff --git a/modules/caddytls/acmeissuer.go b/modules/caddytls/acmeissuer.go index 29a5954e7..2fe5eec97 100644 --- a/modules/caddytls/acmeissuer.go +++ b/modules/caddytls/acmeissuer.go @@ -60,6 +60,14 @@ type ACMEIssuer struct { // other than ACME transactions. Email string `json:"email,omitempty"` + // Optionally select an ACME profile to use for certificate + // orders. Must be a profile name offered by the ACME server, + // which are listed at its directory endpoint. + // + // EXPERIMENTAL: Subject to change. + // See https://datatracker.ietf.org/doc/draft-aaron-acme-profiles/ + Profile string `json:"profile,omitempty"` + // If you have an existing account with the ACME server, put // the private key here in PEM format. The ACME client will // look up your account information with this key first before @@ -184,6 +192,7 @@ func (iss *ACMEIssuer) makeIssuerTemplate() (certmagic.ACMEIssuer, error) { CA: iss.CA, TestCA: iss.TestCA, Email: iss.Email, + Profile: iss.Profile, AccountKeyPEM: iss.AccountKey, CertObtainTimeout: time.Duration(iss.ACMETimeout), TrustedRoots: iss.rootPool, @@ -338,6 +347,7 @@ func (iss *ACMEIssuer) generateZeroSSLEABCredentials(ctx context.Context, acct a // dir // test_dir // email +// profile // timeout // disable_http_challenge // disable_tlsalpn_challenge @@ -400,6 +410,11 @@ func (iss *ACMEIssuer) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { return d.ArgErr() } + case "profile": + if !d.AllArgs(&iss.Profile) { + return d.ArgErr() + } + case "timeout": var timeoutStr string if !d.AllArgs(&timeoutStr) { From 9e0e5a4b4c2babda81c58f28fe61adfa91d04524 Mon Sep 17 00:00:00 2001 From: Omar Ramadan Date: Thu, 16 Jan 2025 09:06:29 -0800 Subject: [PATCH 07/22] logging: Fix crash if logging error is not HandlerError (#6777) Co-authored-by: Matt Holt --- modules/caddyhttp/logging.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/modules/caddyhttp/logging.go b/modules/caddyhttp/logging.go index 0a389fe16..87298ac3c 100644 --- a/modules/caddyhttp/logging.go +++ b/modules/caddyhttp/logging.go @@ -211,6 +211,11 @@ func errLogValues(err error) (status int, msg string, fields func() []zapcore.Fi } return } + fields = func() []zapcore.Field { + return []zapcore.Field{ + zap.Error(err), + } + } status = http.StatusInternalServerError msg = err.Error() return From e7da3b267bcec986aaca960dd22ef834d3b9d4a6 Mon Sep 17 00:00:00 2001 From: Matthew Holt Date: Fri, 17 Jan 2025 06:49:01 -0700 Subject: [PATCH 08/22] reverseproxy: Via header (#6275) --- .../caddyhttp/reverseproxy/reverseproxy.go | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/modules/caddyhttp/reverseproxy/reverseproxy.go b/modules/caddyhttp/reverseproxy/reverseproxy.go index 230bec951..a15dbe424 100644 --- a/modules/caddyhttp/reverseproxy/reverseproxy.go +++ b/modules/caddyhttp/reverseproxy/reverseproxy.go @@ -683,7 +683,7 @@ func (h Handler) prepareRequest(req *http.Request, repl *caddy.Replacer) (*http. req.Header.Set("Early-Data", "1") } - reqUpType := upgradeType(req.Header) + reqUpgradeType := upgradeType(req.Header) removeConnectionHeaders(req.Header) // Remove hop-by-hop headers to the backend. Especially @@ -704,9 +704,9 @@ func (h Handler) prepareRequest(req *http.Request, repl *caddy.Replacer) (*http. // After stripping all the hop-by-hop connection headers above, add back any // necessary for protocol upgrades, such as for websockets. - if reqUpType != "" { + if reqUpgradeType != "" { req.Header.Set("Connection", "Upgrade") - req.Header.Set("Upgrade", reqUpType) + req.Header.Set("Upgrade", reqUpgradeType) normalizeWebsocketHeaders(req.Header) } @@ -732,6 +732,9 @@ func (h Handler) prepareRequest(req *http.Request, repl *caddy.Replacer) (*http. return nil, err } + // Via header(s) + req.Header.Add("Via", fmt.Sprintf("%d.%d Caddy", req.ProtoMajor, req.ProtoMinor)) + return req, nil } @@ -882,13 +885,15 @@ func (h *Handler) reverseProxy(rw http.ResponseWriter, req *http.Request, origRe }), ) + const logMessage = "upstream roundtrip" + if err != nil { - if c := logger.Check(zapcore.DebugLevel, "upstream roundtrip"); c != nil { + if c := logger.Check(zapcore.DebugLevel, logMessage); c != nil { c.Write(zap.Error(err)) } return err } - if c := logger.Check(zapcore.DebugLevel, "upstream roundtrip"); c != nil { + if c := logger.Check(zapcore.DebugLevel, logMessage); c != nil { c.Write( zap.Object("headers", caddyhttp.LoggableHTTPHeader{ Header: res.Header, @@ -1024,6 +1029,14 @@ func (h *Handler) finalizeResponse( res.Header.Del(h) } + // delete our Server header and use Via instead (see #6275) + rw.Header().Del("Server") + var protoPrefix string + if !strings.HasPrefix(strings.ToUpper(res.Proto), "HTTP/") { + protoPrefix = res.Proto[:strings.Index(res.Proto, "/")+1] + } + rw.Header().Add("Via", fmt.Sprintf("%s%d.%d Caddy", protoPrefix, res.ProtoMajor, res.ProtoMinor)) + // apply any response header operations if h.Headers != nil && h.Headers.Response != nil { if h.Headers.Response.Require == nil || From 99073eaa33af62bff51c31305e3437c57d936284 Mon Sep 17 00:00:00 2001 From: Matthew Holt Date: Fri, 17 Jan 2025 06:54:58 -0700 Subject: [PATCH 09/22] go.mod: Upgrade CertMagic to v0.21.7 Fixes rare edge case panics related to ARI --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 495d893b1..6bd356bbd 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/Masterminds/sprig/v3 v3.3.0 github.com/alecthomas/chroma/v2 v2.14.0 github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b - github.com/caddyserver/certmagic v0.21.7-0.20250109205135-32654015b016 + github.com/caddyserver/certmagic v0.21.7 github.com/caddyserver/zerossl v0.1.3 github.com/dustin/go-humanize v1.0.1 github.com/go-chi/chi/v5 v5.0.12 diff --git a/go.sum b/go.sum index c2179ad87..71867a8f8 100644 --- a/go.sum +++ b/go.sum @@ -89,8 +89,8 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= -github.com/caddyserver/certmagic v0.21.7-0.20250109205135-32654015b016 h1:bwnFMkCXIgw3WO7vvMwpr7Zf8qfADmMzYe6mxSKC7zI= -github.com/caddyserver/certmagic v0.21.7-0.20250109205135-32654015b016/go.mod h1:LCPG3WLxcnjVKl/xpjzM0gqh0knrKKKiO5WVttX2eEI= +github.com/caddyserver/certmagic v0.21.7 h1:66KJioPFJwttL43KYSWk7ErSmE6LfaJgCQuhm8Sg6fg= +github.com/caddyserver/certmagic v0.21.7/go.mod h1:LCPG3WLxcnjVKl/xpjzM0gqh0knrKKKiO5WVttX2eEI= github.com/caddyserver/zerossl v0.1.3 h1:onS+pxp3M8HnHpN5MMbOMyNjmTheJyWRaZYwn+YTAyA= github.com/caddyserver/zerossl v0.1.3/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= From 8d748bee711e6678510b8387140e07f5f4b2976f Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Fri, 24 Jan 2025 05:07:19 +0100 Subject: [PATCH 10/22] chore: update quic-go to v0.49.0 (#6803) --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 6bd356bbd..35f06bcd8 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/klauspost/cpuid/v2 v2.2.9 github.com/mholt/acmez/v3 v3.0.1 github.com/prometheus/client_golang v1.19.1 - github.com/quic-go/quic-go v0.48.2 + github.com/quic-go/quic-go v0.49.0 github.com/smallstep/certificates v0.26.1 github.com/smallstep/nosql v0.6.1 github.com/smallstep/truststore v0.13.0 @@ -74,7 +74,7 @@ require ( go.opentelemetry.io/contrib/propagators/b3 v1.17.0 // indirect go.opentelemetry.io/contrib/propagators/jaeger v1.17.0 // indirect go.opentelemetry.io/contrib/propagators/ot v1.17.0 // indirect - go.uber.org/mock v0.4.0 // indirect + go.uber.org/mock v0.5.0 // indirect golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 // indirect diff --git a/go.sum b/go.sum index 71867a8f8..b5622ba51 100644 --- a/go.sum +++ b/go.sum @@ -392,8 +392,8 @@ github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= -github.com/quic-go/quic-go v0.48.2 h1:wsKXZPeGWpMpCGSWqOcqpW2wZYic/8T3aqiOID0/KWE= -github.com/quic-go/quic-go v0.48.2/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs= +github.com/quic-go/quic-go v0.49.0 h1:w5iJHXwHxs1QxyBv1EHKuC50GX5to8mJAxvtnttJp94= +github.com/quic-go/quic-go v0.49.0/go.mod h1:s2wDnmCdooUQBmQfpUSTCYBl1/D4FcqbULMMkASvR6s= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= @@ -564,8 +564,8 @@ go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= -go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= +go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= +go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= From 30743c361ad2277a3bffe67c436805e2b224fbe7 Mon Sep 17 00:00:00 2001 From: Mohammed Al Sahaf Date: Sat, 25 Jan 2025 02:37:16 +0300 Subject: [PATCH 11/22] chore: don't use deprecated `archives.format_overrides.format` (#6807) Signed-off-by: Mohammed Al Sahaf --- .goreleaser.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.goreleaser.yml b/.goreleaser.yml index 005fdbaf3..c7ed4b365 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -111,7 +111,7 @@ archives: - id: default format_overrides: - goos: windows - format: zip + formats: zip name_template: >- {{ .ProjectName }}_ {{- .Version }}_ From 7b8f3505e33139de0d542566478e98b361bb84bf Mon Sep 17 00:00:00 2001 From: vnxme <46669194+vnxme@users.noreply.github.com> Date: Sat, 25 Jan 2025 17:45:41 +0300 Subject: [PATCH 12/22] caddytls: Fix sni_regexp matcher to obtain layer4 contexts (#6804) * caddytls: Fix sni_regexp matcher * caddytls: Refactor sni_regexp matcher --- modules/caddytls/matchers.go | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/modules/caddytls/matchers.go b/modules/caddytls/matchers.go index a74d6ea80..fec1ffd8e 100644 --- a/modules/caddytls/matchers.go +++ b/modules/caddytls/matchers.go @@ -15,6 +15,7 @@ package caddytls import ( + "context" "crypto/tls" "fmt" "net" @@ -224,15 +225,28 @@ func (MatchServerNameRE) CaddyModule() caddy.ModuleInfo { // Match matches hello based on SNI using a regular expression. func (m MatchServerNameRE) Match(hello *tls.ClientHelloInfo) bool { - repl := caddy.NewReplacer() - // caddytls.TestServerNameMatcher calls this function without any context - if ctx := hello.Context(); ctx != nil { + // Note: caddytls.TestServerNameMatcher calls this function without any context + ctx := hello.Context() + if ctx == nil { + // layer4.Connection implements GetContext() to pass its context here, + // since hello.Context() returns nil + if mayHaveContext, ok := hello.Conn.(interface{ GetContext() context.Context }); ok { + ctx = mayHaveContext.GetContext() + } + } + + var repl *caddy.Replacer + if ctx != nil { // In some situations the existing context may have no replacer if replAny := ctx.Value(caddy.ReplacerCtxKey); replAny != nil { repl = replAny.(*caddy.Replacer) } } + if repl == nil { + repl = caddy.NewReplacer() + } + return m.MatchRegexp.Match(hello.ServerName, repl) } From 11151586165946453275b66ef33794d41a5e047b Mon Sep 17 00:00:00 2001 From: Matthew Holt Date: Mon, 27 Jan 2025 08:18:34 -0700 Subject: [PATCH 13/22] caddyhttp: ResponseRecorder sets stream regardless of 1xx Fixes a panic where rr.stream is not true when it should be in the event of 1xx, because the buf is nil --- modules/caddyhttp/responsewriter.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/caddyhttp/responsewriter.go b/modules/caddyhttp/responsewriter.go index 3c0f89d0b..904c30c03 100644 --- a/modules/caddyhttp/responsewriter.go +++ b/modules/caddyhttp/responsewriter.go @@ -154,16 +154,16 @@ func (rr *responseRecorder) WriteHeader(statusCode int) { // connections by manually setting headers and writing status 101 rr.statusCode = statusCode + // decide whether we should buffer the response + if rr.shouldBuffer == nil { + rr.stream = true + } else { + rr.stream = !rr.shouldBuffer(rr.statusCode, rr.ResponseWriterWrapper.Header()) + } + // 1xx responses aren't final; just informational if statusCode < 100 || statusCode > 199 { rr.wroteHeader = true - - // decide whether we should buffer the response - if rr.shouldBuffer == nil { - rr.stream = true - } else { - rr.stream = !rr.shouldBuffer(rr.statusCode, rr.ResponseWriterWrapper.Header()) - } } // if informational or not buffered, immediately write header From 066d770409917b409d0bdc14cb5ba33d3e4cb33e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Mon, 27 Jan 2025 17:32:24 +0100 Subject: [PATCH 14/22] cmd: automatically set GOMEMLIMIT (#6809) * feat: automatically set GOMEMLIMIT * add system support * comments * add logs --- cmd/main.go | 23 ++++++++++++++++++++++- go.mod | 2 ++ go.sum | 4 ++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/cmd/main.go b/cmd/main.go index 655c0084b..c41738be0 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -24,6 +24,7 @@ import ( "io" "io/fs" "log" + "log/slog" "net" "os" "path/filepath" @@ -33,10 +34,12 @@ import ( "strings" "time" + "github.com/KimMachineGun/automemlimit/memlimit" "github.com/caddyserver/certmagic" "github.com/spf13/pflag" "go.uber.org/automaxprocs/maxprocs" "go.uber.org/zap" + "go.uber.org/zap/exp/zapslog" "github.com/caddyserver/caddy/v2" "github.com/caddyserver/caddy/v2/caddyconfig" @@ -66,12 +69,30 @@ func Main() { os.Exit(caddy.ExitCodeFailedStartup) } - undo, err := maxprocs.Set() + logger := caddy.Log() + + // Configure the maximum number of CPUs to use to match the Linux container quota (if any) + // See https://pkg.go.dev/runtime#GOMAXPROCS + undo, err := maxprocs.Set(maxprocs.Logger(logger.Sugar().Infof)) defer undo() if err != nil { caddy.Log().Warn("failed to set GOMAXPROCS", zap.Error(err)) } + // Configure the maximum memory to use to match the Linux container quota (if any) or system memory + // See https://pkg.go.dev/runtime/debug#SetMemoryLimit + _, _ = memlimit.SetGoMemLimitWithOpts( + memlimit.WithLogger( + slog.New(zapslog.NewHandler(logger.Core())), + ), + memlimit.WithProvider( + memlimit.ApplyFallback( + memlimit.FromCgroup, + memlimit.FromSystem, + ), + ), + ) + if err := defaultFactory.Build().Execute(); err != nil { var exitError *exitError if errors.As(err, &exitError) { diff --git a/go.mod b/go.mod index 35f06bcd8..4d9f5ff36 100644 --- a/go.mod +++ b/go.mod @@ -49,6 +49,7 @@ require ( require ( dario.cat/mergo v1.0.1 // indirect + github.com/KimMachineGun/automemlimit v0.7.0 // indirect github.com/Microsoft/go-winio v0.6.0 // indirect github.com/antlr4-go/antlr/v4 v4.13.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect @@ -63,6 +64,7 @@ require ( github.com/google/pprof v0.0.0-20231212022811-ec68065c825e // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect github.com/onsi/ginkgo/v2 v2.13.2 // indirect + github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/quic-go/qpack v0.5.1 // indirect github.com/smallstep/go-attestation v0.4.4-0.20240109183208-413678f90935 // indirect diff --git a/go.sum b/go.sum index b5622ba51..909c7e0b9 100644 --- a/go.sum +++ b/go.sum @@ -31,6 +31,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/KimMachineGun/automemlimit v0.7.0 h1:7G06p/dMSf7G8E6oq+f2uOPuVncFyIlDI/pBWK49u88= +github.com/KimMachineGun/automemlimit v0.7.0/go.mod h1:QZxpHaGOQoYvFhv/r4u3U0JTC2ZcOwbSr11UZF46UBM= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= @@ -366,6 +368,8 @@ github.com/onsi/ginkgo/v2 v2.13.2/go.mod h1:XStQ8QcGwLyF4HdfcZB8SFOS/MWCgDuXMSBe github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg= github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= +github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= +github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/peterbourgon/diskv/v3 v3.0.1 h1:x06SQA46+PKIUftmEujdwSEpIx8kR+M9eLYsUxeYveU= github.com/peterbourgon/diskv/v3 v3.0.1/go.mod h1:kJ5Ny7vLdARGU3WUuy6uzO6T0nb/2gWcT1JiBvRmb5o= From d7872c3bfa673ce9584d00f01a725b93fa7bedf1 Mon Sep 17 00:00:00 2001 From: vnxme <46669194+vnxme@users.noreply.github.com> Date: Mon, 27 Jan 2025 21:42:09 +0300 Subject: [PATCH 15/22] caddytls: Refactor sni matcher (#6812) --- modules/caddytls/matchers.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/caddytls/matchers.go b/modules/caddytls/matchers.go index fec1ffd8e..dfbec94cc 100644 --- a/modules/caddytls/matchers.go +++ b/modules/caddytls/matchers.go @@ -56,7 +56,7 @@ func (MatchServerName) CaddyModule() caddy.ModuleInfo { // Match matches hello based on SNI. func (m MatchServerName) Match(hello *tls.ClientHelloInfo) bool { - repl := caddy.NewReplacer() + var repl *caddy.Replacer // caddytls.TestServerNameMatcher calls this function without any context if ctx := hello.Context(); ctx != nil { // In some situations the existing context may have no replacer @@ -65,6 +65,10 @@ func (m MatchServerName) Match(hello *tls.ClientHelloInfo) bool { } } + if repl == nil { + repl = caddy.NewReplacer() + } + for _, name := range m { rs := repl.ReplaceAll(name, "") if certmagic.MatchWildcard(hello.ServerName, rs) { From 904a0fa368b7eacac3c7156ce4a1f6ced8f61f34 Mon Sep 17 00:00:00 2001 From: Mohammed Al Sahaf Date: Tue, 28 Jan 2025 00:30:54 +0300 Subject: [PATCH 16/22] reverse_proxy: re-add healthy upstreams metric (#6806) * reverse_proxy: re-add healthy upstreams metric Signed-off-by: Mohammed Al Sahaf * lint Signed-off-by: Mohammed Al Sahaf --------- Signed-off-by: Mohammed Al Sahaf --- modules/caddyhttp/reverseproxy/metrics.go | 20 ++++++++++--------- .../caddyhttp/reverseproxy/reverseproxy.go | 4 ++-- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/modules/caddyhttp/reverseproxy/metrics.go b/modules/caddyhttp/reverseproxy/metrics.go index f744756d3..8a7105c11 100644 --- a/modules/caddyhttp/reverseproxy/metrics.go +++ b/modules/caddyhttp/reverseproxy/metrics.go @@ -2,26 +2,26 @@ package reverseproxy import ( "runtime/debug" - "sync" "time" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" "go.uber.org/zap" "go.uber.org/zap/zapcore" + + "github.com/caddyserver/caddy/v2" ) var reverseProxyMetrics = struct { - init sync.Once upstreamsHealthy *prometheus.GaugeVec logger *zap.Logger }{} -func initReverseProxyMetrics(handler *Handler) { +func initReverseProxyMetrics(handler *Handler, registry *prometheus.Registry) { const ns, sub = "caddy", "reverse_proxy" upstreamsLabels := []string{"upstream"} - reverseProxyMetrics.upstreamsHealthy = promauto.NewGaugeVec(prometheus.GaugeOpts{ + reverseProxyMetrics.upstreamsHealthy = promauto.With(registry).NewGaugeVec(prometheus.GaugeOpts{ Namespace: ns, Subsystem: sub, Name: "upstreams_healthy", @@ -35,17 +35,19 @@ type metricsUpstreamsHealthyUpdater struct { handler *Handler } -func newMetricsUpstreamsHealthyUpdater(handler *Handler) *metricsUpstreamsHealthyUpdater { - reverseProxyMetrics.init.Do(func() { - initReverseProxyMetrics(handler) - }) +const upstreamsHealthyMetrics caddy.CtxKey = "reverse_proxy_upstreams_healthy" +func newMetricsUpstreamsHealthyUpdater(handler *Handler, ctx caddy.Context) *metricsUpstreamsHealthyUpdater { + if set := ctx.Value(upstreamsHealthyMetrics); set == nil { + initReverseProxyMetrics(handler, ctx.GetMetricsRegistry()) + ctx = ctx.WithValue(upstreamsHealthyMetrics, true) + } reverseProxyMetrics.upstreamsHealthy.Reset() return &metricsUpstreamsHealthyUpdater{handler} } -func (m *metricsUpstreamsHealthyUpdater) Init() { +func (m *metricsUpstreamsHealthyUpdater) init() { go func() { defer func() { if err := recover(); err != nil { diff --git a/modules/caddyhttp/reverseproxy/reverseproxy.go b/modules/caddyhttp/reverseproxy/reverseproxy.go index a15dbe424..d799c5a6e 100644 --- a/modules/caddyhttp/reverseproxy/reverseproxy.go +++ b/modules/caddyhttp/reverseproxy/reverseproxy.go @@ -382,8 +382,8 @@ func (h *Handler) Provision(ctx caddy.Context) error { } } - upstreamHealthyUpdater := newMetricsUpstreamsHealthyUpdater(h) - upstreamHealthyUpdater.Init() + upstreamHealthyUpdater := newMetricsUpstreamsHealthyUpdater(h, ctx) + upstreamHealthyUpdater.init() return nil } From cfc3af67492eba22686fd13a2b2201c66cd737f3 Mon Sep 17 00:00:00 2001 From: Sander Bruens Date: Tue, 28 Jan 2025 16:19:02 -0500 Subject: [PATCH 17/22] fix: update broken link to Ardan Labs (#6800) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index abc136f68..3f071e6f0 100644 --- a/README.md +++ b/README.md @@ -176,7 +176,7 @@ The docs are also open source. You can contribute to them here: https://github.c ## Getting help -- We advise companies using Caddy to secure a support contract through [Ardan Labs](https://www.ardanlabs.com/my/contact-us?dd=caddy) before help is needed. +- We advise companies using Caddy to secure a support contract through [Ardan Labs](https://www.ardanlabs.com) before help is needed. - A [sponsorship](https://github.com/sponsors/mholt) goes a long way! We can offer private help to sponsors. If Caddy is benefitting your company, please consider a sponsorship. This not only helps fund full-time work to ensure the longevity of the project, it provides your company the resources, support, and discounts you need; along with being a great look for your company to your customers and potential customers! From 9996d6a70ba76a94dfc90548f25fbac0ce9da497 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Jan 2025 17:25:11 -0700 Subject: [PATCH 18/22] build(deps): bump github.com/golang/glog from 1.2.2 to 1.2.4 (#6814) Bumps [github.com/golang/glog](https://github.com/golang/glog) from 1.2.2 to 1.2.4. - [Release notes](https://github.com/golang/glog/releases) - [Commits](https://github.com/golang/glog/compare/v1.2.2...v1.2.4) --- updated-dependencies: - dependency-name: github.com/golang/glog dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 4d9f5ff36..158450951 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ toolchain go1.23.0 require ( github.com/BurntSushi/toml v1.4.0 + github.com/KimMachineGun/automemlimit v0.7.0 github.com/Masterminds/sprig/v3 v3.3.0 github.com/alecthomas/chroma/v2 v2.14.0 github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b @@ -49,7 +50,6 @@ require ( require ( dario.cat/mergo v1.0.1 // indirect - github.com/KimMachineGun/automemlimit v0.7.0 // indirect github.com/Microsoft/go-winio v0.6.0 // indirect github.com/antlr4-go/antlr/v4 v4.13.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect @@ -57,7 +57,7 @@ require ( github.com/fxamacker/cbor/v2 v2.6.0 // indirect github.com/go-jose/go-jose/v3 v3.0.3 // indirect github.com/go-kit/log v0.2.1 // indirect - github.com/golang/glog v1.2.2 // indirect + github.com/golang/glog v1.2.4 // indirect github.com/google/certificate-transparency-go v1.1.8-0.20240110162603-74a5dd331745 // indirect github.com/google/go-tpm v0.9.0 // indirect github.com/google/go-tspi v0.3.0 // indirect diff --git a/go.sum b/go.sum index 909c7e0b9..8dc6f822d 100644 --- a/go.sum +++ b/go.sum @@ -188,8 +188,8 @@ github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPh github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.2.2 h1:1+mZ9upx1Dh6FmUTFR1naJ77miKiXgALjWOZ3NVFPmY= -github.com/golang/glog v1.2.2/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= +github.com/golang/glog v1.2.4 h1:CNNw5U8lSiiBk7druxtSHHTsRWcxKoac6kZKm2peBBc= +github.com/golang/glog v1.2.4/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= From 9283770f68f570f47ca20aa9c6f9de8cc50063ba Mon Sep 17 00:00:00 2001 From: Mohammed Al Sahaf Date: Tue, 4 Feb 2025 10:55:30 +0300 Subject: [PATCH 19/22] reverseproxy: ignore duplicate collector registration error (#6820) Signed-off-by: Mohammed Al Sahaf --- modules/caddyhttp/reverseproxy/metrics.go | 36 +++++++++++++++-------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/modules/caddyhttp/reverseproxy/metrics.go b/modules/caddyhttp/reverseproxy/metrics.go index 8a7105c11..248842730 100644 --- a/modules/caddyhttp/reverseproxy/metrics.go +++ b/modules/caddyhttp/reverseproxy/metrics.go @@ -1,11 +1,12 @@ package reverseproxy import ( + "errors" "runtime/debug" + "sync" "time" "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promauto" "go.uber.org/zap" "go.uber.org/zap/zapcore" @@ -13,6 +14,7 @@ import ( ) var reverseProxyMetrics = struct { + once sync.Once upstreamsHealthy *prometheus.GaugeVec logger *zap.Logger }{} @@ -21,12 +23,25 @@ func initReverseProxyMetrics(handler *Handler, registry *prometheus.Registry) { const ns, sub = "caddy", "reverse_proxy" upstreamsLabels := []string{"upstream"} - reverseProxyMetrics.upstreamsHealthy = promauto.With(registry).NewGaugeVec(prometheus.GaugeOpts{ - Namespace: ns, - Subsystem: sub, - Name: "upstreams_healthy", - Help: "Health status of reverse proxy upstreams.", - }, upstreamsLabels) + reverseProxyMetrics.once.Do(func() { + reverseProxyMetrics.upstreamsHealthy = prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Namespace: ns, + Subsystem: sub, + Name: "upstreams_healthy", + Help: "Health status of reverse proxy upstreams.", + }, upstreamsLabels) + }) + + // duplicate registration could happen if multiple sites with reverse proxy are configured; so ignore the error because + // there's no good way to capture having multiple sites with reverse proxy. If this happens, the metrics will be + // registered twice, but the second registration will be ignored. + if err := registry.Register(reverseProxyMetrics.upstreamsHealthy); err != nil && + !errors.Is(err, prometheus.AlreadyRegisteredError{ + ExistingCollector: reverseProxyMetrics.upstreamsHealthy, + NewCollector: reverseProxyMetrics.upstreamsHealthy, + }) { + panic(err) + } reverseProxyMetrics.logger = handler.logger.Named("reverse_proxy.metrics") } @@ -35,13 +50,8 @@ type metricsUpstreamsHealthyUpdater struct { handler *Handler } -const upstreamsHealthyMetrics caddy.CtxKey = "reverse_proxy_upstreams_healthy" - func newMetricsUpstreamsHealthyUpdater(handler *Handler, ctx caddy.Context) *metricsUpstreamsHealthyUpdater { - if set := ctx.Value(upstreamsHealthyMetrics); set == nil { - initReverseProxyMetrics(handler, ctx.GetMetricsRegistry()) - ctx = ctx.WithValue(upstreamsHealthyMetrics, true) - } + initReverseProxyMetrics(handler, ctx.GetMetricsRegistry()) reverseProxyMetrics.upstreamsHealthy.Reset() return &metricsUpstreamsHealthyUpdater{handler} From 96c5c554c1241430ac9ddea6f4b68948adcc961b Mon Sep 17 00:00:00 2001 From: Mahdi Mohammadi <46979964+debug-ing@users.noreply.github.com> Date: Tue, 4 Feb 2025 19:27:32 +0330 Subject: [PATCH 20/22] admin: fix index validation for PUT requests (#6824) --- admin.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin.go b/admin.go index 89fce1d28..6df5a23f7 100644 --- a/admin.go +++ b/admin.go @@ -1139,7 +1139,7 @@ traverseLoop: return fmt.Errorf("[%s] invalid array index '%s': %v", path, idxStr, err) } - if idx < 0 || idx >= len(arr) { + if idx < 0 || (method != http.MethodPut && idx >= len(arr)) || idx > len(arr) { return fmt.Errorf("[%s] array index out of bounds: %s", path, idxStr) } } From 932dac157a3c4693b80576477498bb86208b9b30 Mon Sep 17 00:00:00 2001 From: Matthew Holt Date: Fri, 7 Feb 2025 06:18:34 -0700 Subject: [PATCH 21/22] logging: Always set fields func; fix #6829 --- modules/caddyhttp/logging.go | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/caddyhttp/logging.go b/modules/caddyhttp/logging.go index 87298ac3c..cef9ea139 100644 --- a/modules/caddyhttp/logging.go +++ b/modules/caddyhttp/logging.go @@ -195,6 +195,7 @@ func (sa *StringArray) UnmarshalJSON(b []byte) error { // have richer information. func errLogValues(err error) (status int, msg string, fields func() []zapcore.Field) { var handlerErr HandlerError + fields = func() []zapcore.Field { return []zapcore.Field{} } // don't be nil (fix #6829) if errors.As(err, &handlerErr) { status = handlerErr.StatusCode if handlerErr.Err == nil { From 9b74a53e510d395e0cbe617a29db6de9828388b8 Mon Sep 17 00:00:00 2001 From: Matthew Holt Date: Fri, 7 Feb 2025 06:23:43 -0700 Subject: [PATCH 22/22] Revert "logging: Always set fields func; fix #6829" This reverts commit 932dac157a3c4693b80576477498bb86208b9b30. Somehow the code I was looking at changed when I committed, without realizing it. This has already been fixed in #6777. --- modules/caddyhttp/logging.go | 1 - modules/caddyhttp/server.go | 1 - 2 files changed, 2 deletions(-) diff --git a/modules/caddyhttp/logging.go b/modules/caddyhttp/logging.go index cef9ea139..87298ac3c 100644 --- a/modules/caddyhttp/logging.go +++ b/modules/caddyhttp/logging.go @@ -195,7 +195,6 @@ func (sa *StringArray) UnmarshalJSON(b []byte) error { // have richer information. func errLogValues(err error) (status int, msg string, fields func() []zapcore.Field) { var handlerErr HandlerError - fields = func() []zapcore.Field { return []zapcore.Field{} } // don't be nil (fix #6829) if errors.As(err, &handlerErr) { status = handlerErr.StatusCode if handlerErr.Err == nil { diff --git a/modules/caddyhttp/server.go b/modules/caddyhttp/server.go index 12c032dee..a2b29d658 100644 --- a/modules/caddyhttp/server.go +++ b/modules/caddyhttp/server.go @@ -408,7 +408,6 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { if fields == nil { fields = errFields() } - c.Write(fields...) } }