From 3afa02ba4e5bee5628ee9334a64a0002a102a60b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?June=20=F0=9F=8D=93=F0=9F=A6=B4?= Date: Mon, 22 Jul 2024 19:37:44 -0400 Subject: [PATCH 01/23] ci: correct `-tags nobadger` on binary build (#6470) --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cae0b984..a006d26b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -99,7 +99,7 @@ jobs: env: CGO_ENABLED: 0 run: | - go build -tags nobdger -trimpath -ldflags="-w -s" -v + go build -tags nobadger -trimpath -ldflags="-w -s" -v - name: Smoke test Caddy working-directory: ./cmd/caddy From 61fe152c603e6efb6ca3d3522923beebe6d6701f Mon Sep 17 00:00:00 2001 From: vnxme <46669194+vnxme@users.noreply.github.com> Date: Wed, 24 Jul 2024 18:26:09 +0300 Subject: [PATCH 02/23] caddytls: Caddyfile support for TLS handshake matchers (#6461) * Caddyfile support for TLS handshake matchers: - caddytls.MatchLocalIP - caddytls.MatchRemoteIP - caddytls.MatchServerName * Caddyfile support for TLS handshake matchers: - fix imports order Co-authored-by: Francis Lavoie --------- Co-authored-by: Francis Lavoie --- modules/caddytls/matchers.go | 103 +++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/modules/caddytls/matchers.go b/modules/caddytls/matchers.go index b01fb833..a23a1931 100644 --- a/modules/caddytls/matchers.go +++ b/modules/caddytls/matchers.go @@ -25,6 +25,7 @@ import ( "go.uber.org/zap" "github.com/caddyserver/caddy/v2" + "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" ) func init() { @@ -56,6 +57,29 @@ func (m MatchServerName) Match(hello *tls.ClientHelloInfo) bool { return false } +// UnmarshalCaddyfile sets up the MatchServerName from Caddyfile tokens. Syntax: +// +// sni +func (m *MatchServerName) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { + for d.Next() { + wrapper := d.Val() + + // At least one same-line option must be provided + if d.CountRemainingArgs() == 0 { + return d.ArgErr() + } + + *m = append(*m, d.RemainingArgs()...) + + // No blocks are supported + if d.NextBlock(d.Nesting()) { + return d.Errf("malformed TLS handshake matcher '%s': blocks are not supported", wrapper) + } + } + + return nil +} + // MatchRemoteIP matches based on the remote IP of the // connection. Specific IPs or CIDR ranges can be specified. // @@ -145,6 +169,50 @@ func (MatchRemoteIP) matches(ip netip.Addr, ranges []netip.Prefix) bool { return false } +// UnmarshalCaddyfile sets up the MatchRemoteIP from Caddyfile tokens. Syntax: +// +// remote_ip +// +// Note: IPs and CIDRs prefixed with ! symbol are treated as not_ranges +func (m *MatchRemoteIP) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { + for d.Next() { + wrapper := d.Val() + + // At least one same-line option must be provided + if d.CountRemainingArgs() == 0 { + return d.ArgErr() + } + + for d.NextArg() { + val := d.Val() + if len(val) > 1 && val[0] == '!' { + prefixes, err := m.parseIPRange(val[1:]) + if err != nil { + return err + } + for _, prefix := range prefixes { + m.NotRanges = append(m.NotRanges, prefix.String()) + } + } else { + prefixes, err := m.parseIPRange(val) + if err != nil { + return err + } + for _, prefix := range prefixes { + m.Ranges = append(m.Ranges, prefix.String()) + } + } + } + + // No blocks are supported + if d.NextBlock(d.Nesting()) { + return d.Errf("malformed TLS handshake matcher '%s': blocks are not supported", wrapper) + } + } + + return nil +} + // MatchLocalIP matches based on the IP address of the interface // receiving the connection. Specific IPs or CIDR ranges can be specified. type MatchLocalIP struct { @@ -219,6 +287,37 @@ func (MatchLocalIP) matches(ip netip.Addr, ranges []netip.Prefix) bool { return false } +// UnmarshalCaddyfile sets up the MatchLocalIP from Caddyfile tokens. Syntax: +// +// local_ip +func (m *MatchLocalIP) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { + for d.Next() { + wrapper := d.Val() + + // At least one same-line option must be provided + if d.CountRemainingArgs() == 0 { + return d.ArgErr() + } + + for d.NextArg() { + prefixes, err := m.parseIPRange(d.Val()) + if err != nil { + return err + } + for _, prefix := range prefixes { + m.Ranges = append(m.Ranges, prefix.String()) + } + } + + // No blocks are supported + if d.NextBlock(d.Nesting()) { + return d.Errf("malformed TLS handshake matcher '%s': blocks are not supported", wrapper) + } + } + + return nil +} + // Interface guards var ( _ ConnectionMatcher = (*MatchServerName)(nil) @@ -226,4 +325,8 @@ var ( _ caddy.Provisioner = (*MatchLocalIP)(nil) _ ConnectionMatcher = (*MatchLocalIP)(nil) + + _ caddyfile.Unmarshaler = (*MatchLocalIP)(nil) + _ caddyfile.Unmarshaler = (*MatchRemoteIP)(nil) + _ caddyfile.Unmarshaler = (*MatchServerName)(nil) ) From 3579815a6c099bfbcff0143f7a251a92b4fa4198 Mon Sep 17 00:00:00 2001 From: vnxme <46669194+vnxme@users.noreply.github.com> Date: Wed, 24 Jul 2024 20:01:06 +0300 Subject: [PATCH 03/23] caddytls: Caddyfile support for TLS conn and cert sel policies (#6462) * Caddyfile support for TLS custom certificate selection policy * Caddyfile support for TLS connection policy --- modules/caddytls/certselection.go | 78 ++++++++++++++ modules/caddytls/connpolicy.go | 174 +++++++++++++++++++++++++++++- 2 files changed, 251 insertions(+), 1 deletion(-) diff --git a/modules/caddytls/certselection.go b/modules/caddytls/certselection.go index 1bef890a..84ca2e11 100644 --- a/modules/caddytls/certselection.go +++ b/modules/caddytls/certselection.go @@ -22,6 +22,8 @@ import ( "math/big" "github.com/caddyserver/certmagic" + + "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" ) // CustomCertSelectionPolicy represents a policy for selecting the certificate @@ -122,6 +124,79 @@ nextChoice: return certmagic.DefaultCertificateSelector(hello, viable) } +// UnmarshalCaddyfile sets up the CustomCertSelectionPolicy from Caddyfile tokens. Syntax: +// +// cert_selection { +// all_tags +// any_tag +// public_key_algorithm +// serial_number +// subject_organization +// } +func (p *CustomCertSelectionPolicy) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { + _, wrapper := d.Next(), d.Val() // consume wrapper name + + // No same-line options are supported + if d.CountRemainingArgs() > 0 { + return d.ArgErr() + } + + var hasPublicKeyAlgorithm bool + for nesting := d.Nesting(); d.NextBlock(nesting); { + optionName := d.Val() + switch optionName { + case "all_tags": + if d.CountRemainingArgs() == 0 { + return d.ArgErr() + } + p.AllTags = append(p.AllTags, d.RemainingArgs()...) + case "any_tag": + if d.CountRemainingArgs() == 0 { + return d.ArgErr() + } + p.AnyTag = append(p.AnyTag, d.RemainingArgs()...) + case "public_key_algorithm": + if hasPublicKeyAlgorithm { + return d.Errf("duplicate %s option '%s'", wrapper, optionName) + } + if d.CountRemainingArgs() != 1 { + return d.ArgErr() + } + d.NextArg() + if err := p.PublicKeyAlgorithm.UnmarshalJSON([]byte(d.Val())); err != nil { + return d.Errf("parsing %s option '%s': %v", wrapper, optionName, err) + } + hasPublicKeyAlgorithm = true + case "serial_number": + if d.CountRemainingArgs() == 0 { + return d.ArgErr() + } + for d.NextArg() { + val, bi := d.Val(), bigInt{} + _, ok := bi.SetString(val, 10) + if !ok { + return d.Errf("parsing %s option '%s': invalid big.int value %s", wrapper, optionName, val) + } + p.SerialNumber = append(p.SerialNumber, bi) + } + case "subject_organization": + if d.CountRemainingArgs() == 0 { + return d.ArgErr() + } + p.SubjectOrganization = append(p.SubjectOrganization, d.RemainingArgs()...) + default: + return d.ArgErr() + } + + // No nested blocks are supported + if d.NextBlock(nesting + 1) { + return d.Errf("malformed %s option '%s': blocks are not supported", wrapper, optionName) + } + } + + return nil +} + // bigInt is a big.Int type that interops with JSON encodings as a string. type bigInt struct{ big.Int } @@ -144,3 +219,6 @@ func (bi *bigInt) UnmarshalJSON(p []byte) error { } return nil } + +// Interface guard +var _ caddyfile.Unmarshaler = (*CustomCertSelectionPolicy)(nil) diff --git a/modules/caddytls/connpolicy.go b/modules/caddytls/connpolicy.go index c9705ffd..4ec0e673 100644 --- a/modules/caddytls/connpolicy.go +++ b/modules/caddytls/connpolicy.go @@ -363,6 +363,136 @@ func (p ConnectionPolicy) SettingsEmpty() bool { p.InsecureSecretsLog == "" } +// UnmarshalCaddyfile sets up the ConnectionPolicy from Caddyfile tokens. Syntax: +// +// connection_policy { +// alpn +// cert_selection { +// ... +// } +// ciphers +// client_auth { +// ... +// } +// curves +// default_sni +// match { +// ... +// } +// protocols [] +// # EXPERIMENTAL: +// drop +// fallback_sni +// insecure_secrets_log +// } +func (cp *ConnectionPolicy) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { + _, wrapper := d.Next(), d.Val() + + // No same-line options are supported + if d.CountRemainingArgs() > 0 { + return d.ArgErr() + } + + var hasCertSelection, hasClientAuth, hasDefaultSNI, hasDrop, + hasFallbackSNI, hasInsecureSecretsLog, hasMatch, hasProtocols bool + for nesting := d.Nesting(); d.NextBlock(nesting); { + optionName := d.Val() + switch optionName { + case "alpn": + if d.CountRemainingArgs() == 0 { + return d.ArgErr() + } + cp.ALPN = append(cp.ALPN, d.RemainingArgs()...) + case "cert_selection": + if hasCertSelection { + return d.Errf("duplicate %s option '%s'", wrapper, optionName) + } + p := &CustomCertSelectionPolicy{} + if err := p.UnmarshalCaddyfile(d.NewFromNextSegment()); err != nil { + return err + } + cp.CertSelection, hasCertSelection = p, true + case "client_auth": + if hasClientAuth { + return d.Errf("duplicate %s option '%s'", wrapper, optionName) + } + ca := &ClientAuthentication{} + if err := ca.UnmarshalCaddyfile(d.NewFromNextSegment()); err != nil { + return err + } + cp.ClientAuthentication, hasClientAuth = ca, true + case "ciphers": + if d.CountRemainingArgs() == 0 { + return d.ArgErr() + } + cp.CipherSuites = append(cp.CipherSuites, d.RemainingArgs()...) + case "curves": + if d.CountRemainingArgs() == 0 { + return d.ArgErr() + } + cp.Curves = append(cp.Curves, d.RemainingArgs()...) + case "default_sni": + if hasDefaultSNI { + return d.Errf("duplicate %s option '%s'", wrapper, optionName) + } + if d.CountRemainingArgs() != 1 { + return d.ArgErr() + } + _, cp.DefaultSNI, hasDefaultSNI = d.NextArg(), d.Val(), true + case "drop": // EXPERIMENTAL + if hasDrop { + return d.Errf("duplicate %s option '%s'", wrapper, optionName) + } + cp.Drop, hasDrop = true, true + case "fallback_sni": // EXPERIMENTAL + if hasFallbackSNI { + return d.Errf("duplicate %s option '%s'", wrapper, optionName) + } + if d.CountRemainingArgs() != 1 { + return d.ArgErr() + } + _, cp.FallbackSNI, hasFallbackSNI = d.NextArg(), d.Val(), true + case "insecure_secrets_log": // EXPERIMENTAL + if hasInsecureSecretsLog { + return d.Errf("duplicate %s option '%s'", wrapper, optionName) + } + if d.CountRemainingArgs() != 1 { + return d.ArgErr() + } + _, cp.InsecureSecretsLog, hasInsecureSecretsLog = d.NextArg(), d.Val(), true + case "match": + if hasMatch { + return d.Errf("duplicate %s option '%s'", wrapper, optionName) + } + matcherSet, err := ParseCaddyfileNestedMatcherSet(d) + if err != nil { + return err + } + cp.MatchersRaw, hasMatch = matcherSet, true + case "protocols": + if hasProtocols { + return d.Errf("duplicate %s option '%s'", wrapper, optionName) + } + if d.CountRemainingArgs() == 0 || d.CountRemainingArgs() > 2 { + return d.ArgErr() + } + _, cp.ProtocolMin, hasProtocols = d.NextArg(), d.Val(), true + if d.NextArg() { + cp.ProtocolMax = d.Val() + } + default: + return d.ArgErr() + } + + // No nested blocks are supported + if d.NextBlock(nesting + 1) { + return d.Errf("malformed %s option '%s': blocks are not supported", wrapper, optionName) + } + } + + return nil +} + // ClientAuthentication configures TLS client auth. type ClientAuthentication struct { // Certificate authority module which provides the certificate pool of trusted certificates @@ -819,4 +949,46 @@ func (d destructableWriter) Destruct() error { return d.Close() } var secretsLogPool = caddy.NewUsagePool() -var _ caddyfile.Unmarshaler = (*ClientAuthentication)(nil) +// Interface guards +var ( + _ caddyfile.Unmarshaler = (*ClientAuthentication)(nil) + _ caddyfile.Unmarshaler = (*ConnectionPolicy)(nil) +) + +// ParseCaddyfileNestedMatcherSet parses the Caddyfile tokens for a nested +// matcher set, and returns its raw module map value. +func ParseCaddyfileNestedMatcherSet(d *caddyfile.Dispenser) (caddy.ModuleMap, error) { + matcherMap := make(map[string]ConnectionMatcher) + + tokensByMatcherName := make(map[string][]caddyfile.Token) + for nesting := d.Nesting(); d.NextArg() || d.NextBlock(nesting); { + matcherName := d.Val() + tokensByMatcherName[matcherName] = append(tokensByMatcherName[matcherName], d.NextSegment()...) + } + + for matcherName, tokens := range tokensByMatcherName { + dd := caddyfile.NewDispenser(tokens) + dd.Next() // consume wrapper name + + unm, err := caddyfile.UnmarshalModule(dd, "tls.handshake_match."+matcherName) + if err != nil { + return nil, err + } + cm, ok := unm.(ConnectionMatcher) + if !ok { + return nil, fmt.Errorf("matcher module '%s' is not a connection matcher", matcherName) + } + matcherMap[matcherName] = cm + } + + matcherSet := make(caddy.ModuleMap) + for name, matcher := range matcherMap { + jsonBytes, err := json.Marshal(matcher) + if err != nil { + return nil, fmt.Errorf("marshaling %T matcher: %v", matcher, err) + } + matcherSet[name] = jsonBytes + } + + return matcherSet, nil +} From 9cc26ee7bf323f5fdffdfe7349794aa320a6ce6c Mon Sep 17 00:00:00 2001 From: Mohammed Al Sahaf Date: Sun, 28 Jul 2024 02:20:09 +0300 Subject: [PATCH 04/23] chore: update golangci config (#6479) Signed-off-by: Mohammed Al Sahaf --- .golangci.yml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index d144395d..74e3503c 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,7 +1,9 @@ linters-settings: errcheck: - ignore: fmt:.*,go.uber.org/zap/zapcore:^Add.* - ignoretests: true + exclude-functions: + - fmt.* + - (go.uber.org/zap/zapcore.ObjectEncoder).AddObject + - (go.uber.org/zap/zapcore.ObjectEncoder).AddArray gci: sections: - standard # Standard section: captures all standard packages. @@ -130,13 +132,14 @@ linters: run: # default concurrency is a available CPU number. # concurrency: 4 # explicitly omit this value to fully utilize available resources. - deadline: 5m + timeout: 5m issues-exit-code: 1 tests: false # output configuration options output: - format: 'colored-line-number' + formats: + - format: 'colored-line-number' print-issued-lines: true print-linter-name: true @@ -166,3 +169,6 @@ issues: - path: modules/logging/filters.go linters: - dupl + - path: _test\.go + linters: + - errcheck From 976469ca0dbbbd948dbfd6a4caedb1e7c914ef3d Mon Sep 17 00:00:00 2001 From: WeidiDeng Date: Sun, 28 Jul 2024 07:46:56 +0800 Subject: [PATCH 05/23] encode: flush already compressed data from the encoder (#6471) --- modules/caddyhttp/encode/encode.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/modules/caddyhttp/encode/encode.go b/modules/caddyhttp/encode/encode.go index cf3d17b6..00e50727 100644 --- a/modules/caddyhttp/encode/encode.go +++ b/modules/caddyhttp/encode/encode.go @@ -266,6 +266,14 @@ func (rw *responseWriter) FlushError() error { // to rw.Write (see bug in #4314) return nil } + // also flushes the encoder, if any + // see: https://github.com/jjiang-stripe/caddy-slow-gzip + if rw.w != nil { + err := rw.w.Flush() + if err != nil { + return err + } + } //nolint:bodyclose return http.NewResponseController(rw.ResponseWriter).Flush() } @@ -475,6 +483,7 @@ type encodingPreference struct { type Encoder interface { io.WriteCloser Reset(io.Writer) + Flush() error // encoder by default buffers data to maximize compressing rate } // Encoding is a type which can create encoders of its kind From 840094ac65c2c27dbf0be63478d36969a57ce7e0 Mon Sep 17 00:00:00 2001 From: Prakhar Awasthi <53934224+pi-prakhar@users.noreply.github.com> Date: Sat, 3 Aug 2024 22:21:50 +0530 Subject: [PATCH 06/23] proxyprotocol: Update WrapListener to use ConnPolicyFunc for PROXY protocol (#6485) * proxyprotocol : Update WrapListener to use ConnPolicyFunc for PROXY protocol support * proxyprotocol : Updated dependency pires/go-proxyproto to pseudo latest version --- go.mod | 2 +- go.sum | 2 ++ modules/caddyhttp/proxyprotocol/listenerwrapper.go | 11 ++++++----- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index f5559a8d..d2fec7db 100644 --- a/go.mod +++ b/go.mod @@ -123,7 +123,7 @@ require ( github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-ps v1.0.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect - github.com/pires/go-proxyproto v0.7.0 + github.com/pires/go-proxyproto v0.7.1-0.20240628150027-b718e7ce4964 github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_model v0.5.0 // indirect github.com/prometheus/common v0.48.0 // indirect diff --git a/go.sum b/go.sum index 63306efc..0ed480b2 100644 --- a/go.sum +++ b/go.sum @@ -322,6 +322,8 @@ github.com/peterbourgon/diskv/v3 v3.0.1 h1:x06SQA46+PKIUftmEujdwSEpIx8kR+M9eLYsU github.com/peterbourgon/diskv/v3 v3.0.1/go.mod h1:kJ5Ny7vLdARGU3WUuy6uzO6T0nb/2gWcT1JiBvRmb5o= github.com/pires/go-proxyproto v0.7.0 h1:IukmRewDQFWC7kfnb66CSomk2q/seBuilHBYFwyq0Hs= github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP40O9LbuiFR4= +github.com/pires/go-proxyproto v0.7.1-0.20240628150027-b718e7ce4964 h1:ct/vxNBgHpASQ4sT8NaBX9LtsEtluZqaUJydLG50U3E= +github.com/pires/go-proxyproto v0.7.1-0.20240628150027-b718e7ce4964/go.mod h1:iknsfgnH8EkjrMeMyvfKByp9TiBZCKZM0jx2xmKqnVY= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= diff --git a/modules/caddyhttp/proxyprotocol/listenerwrapper.go b/modules/caddyhttp/proxyprotocol/listenerwrapper.go index e0d9b86c..e25fe02a 100644 --- a/modules/caddyhttp/proxyprotocol/listenerwrapper.go +++ b/modules/caddyhttp/proxyprotocol/listenerwrapper.go @@ -50,7 +50,7 @@ type ListenerWrapper struct { // Policy definitions are here: https://pkg.go.dev/github.com/pires/go-proxyproto@v0.7.0#Policy FallbackPolicy Policy `json:"fallback_policy,omitempty"` - policy goproxy.PolicyFunc + policy goproxy.ConnPolicyFunc } // Provision sets up the listener wrapper. @@ -69,13 +69,14 @@ func (pp *ListenerWrapper) Provision(ctx caddy.Context) error { } pp.deny = append(pp.deny, ipnet) } - pp.policy = func(upstream net.Addr) (goproxy.Policy, error) { + + pp.policy = func(options goproxy.ConnPolicyOptions) (goproxy.Policy, error) { // trust unix sockets - if network := upstream.Network(); caddy.IsUnixNetwork(network) { + if network := options.Upstream.Network(); caddy.IsUnixNetwork(network) { return goproxy.USE, nil } ret := pp.FallbackPolicy - host, _, err := net.SplitHostPort(upstream.String()) + host, _, err := net.SplitHostPort(options.Upstream.String()) if err != nil { return goproxy.REJECT, err } @@ -106,6 +107,6 @@ func (pp *ListenerWrapper) WrapListener(l net.Listener) net.Listener { Listener: l, ReadHeaderTimeout: time.Duration(pp.Timeout), } - pl.Policy = pp.policy + pl.ConnPolicy = pp.policy return pl } From b1986781740b2b7f546c6ce56496c5cc7145674d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?lollipopkit=F0=9F=8F=B3=EF=B8=8F=E2=80=8D=E2=9A=A7?= =?UTF-8?q?=EF=B8=8F?= <10864310+lollipopkit@users.noreply.github.com> Date: Mon, 5 Aug 2024 22:27:45 +0800 Subject: [PATCH 07/23] browse: Customizable default sort options (#6468) * fileserver: add `sort` options * fix: test * fileserver: check options in `Provison` * fileserver: more obvious err alerts in sort options --- .../file_server_sort.caddyfiletest | 36 +++++++++++++++++++ modules/caddyhttp/fileserver/browse.go | 31 +++++++++++++--- .../caddyhttp/fileserver/browsetplcontext.go | 3 ++ modules/caddyhttp/fileserver/caddyfile.go | 11 ++++++ modules/caddyhttp/fileserver/staticfiles.go | 26 ++++++++++++++ 5 files changed, 103 insertions(+), 4 deletions(-) create mode 100644 caddytest/integration/caddyfile_adapt/file_server_sort.caddyfiletest diff --git a/caddytest/integration/caddyfile_adapt/file_server_sort.caddyfiletest b/caddytest/integration/caddyfile_adapt/file_server_sort.caddyfiletest new file mode 100644 index 00000000..62bfd0cb --- /dev/null +++ b/caddytest/integration/caddyfile_adapt/file_server_sort.caddyfiletest @@ -0,0 +1,36 @@ +:80 + +file_server browse { + sort size desc +} +---------- +{ + "apps": { + "http": { + "servers": { + "srv0": { + "listen": [ + ":80" + ], + "routes": [ + { + "handle": [ + { + "browse": {}, + "handler": "file_server", + "hide": [ + "./Caddyfile" + ], + "sort": [ + "size", + "desc" + ] + } + ] + } + ] + } + } + } + } +} \ No newline at end of file diff --git a/modules/caddyhttp/fileserver/browse.go b/modules/caddyhttp/fileserver/browse.go index 8b2b48e7..bd02c584 100644 --- a/modules/caddyhttp/fileserver/browse.go +++ b/modules/caddyhttp/fileserver/browse.go @@ -206,11 +206,34 @@ func (fsrv *FileServer) loadDirectoryContents(ctx context.Context, fileSystem fs // browseApplyQueryParams applies query parameters to the listing. // It mutates the listing and may set cookies. func (fsrv *FileServer) browseApplyQueryParams(w http.ResponseWriter, r *http.Request, listing *browseTemplateContext) { + var orderParam, sortParam string + + // The configs in Caddyfile have lower priority than Query params, + // so put it at first. + for idx, item := range fsrv.SortOptions { + // Only `sort` & `order`, 2 params are allowed + if idx >= 2 { + break + } + switch item { + case sortByName, sortByNameDirFirst, sortBySize, sortByTime: + sortParam = item + case sortOrderAsc, sortOrderDesc: + orderParam = item + } + } + layoutParam := r.URL.Query().Get("layout") - sortParam := r.URL.Query().Get("sort") - orderParam := r.URL.Query().Get("order") limitParam := r.URL.Query().Get("limit") offsetParam := r.URL.Query().Get("offset") + sortParamTmp := r.URL.Query().Get("sort") + if sortParamTmp != "" { + sortParam = sortParamTmp + } + orderParamTmp := r.URL.Query().Get("order") + if orderParamTmp != "" { + orderParam = orderParamTmp + } switch layoutParam { case "list", "grid", "": @@ -233,11 +256,11 @@ func (fsrv *FileServer) browseApplyQueryParams(w http.ResponseWriter, r *http.Re // then figure out the order switch orderParam { case "": - orderParam = "asc" + orderParam = sortOrderAsc if orderCookie, orderErr := r.Cookie("order"); orderErr == nil { orderParam = orderCookie.Value } - case "asc", "desc": + case sortOrderAsc, sortOrderDesc: http.SetCookie(w, &http.Cookie{Name: "order", Value: orderParam, Secure: r.TLS != nil}) } diff --git a/modules/caddyhttp/fileserver/browsetplcontext.go b/modules/caddyhttp/fileserver/browsetplcontext.go index 81e260c7..0251bc58 100644 --- a/modules/caddyhttp/fileserver/browsetplcontext.go +++ b/modules/caddyhttp/fileserver/browsetplcontext.go @@ -373,4 +373,7 @@ const ( sortByNameDirFirst = "namedirfirst" sortBySize = "size" sortByTime = "time" + + sortOrderAsc = "asc" + sortOrderDesc = "desc" ) diff --git a/modules/caddyhttp/fileserver/caddyfile.go b/modules/caddyhttp/fileserver/caddyfile.go index f6569501..603a828c 100644 --- a/modules/caddyhttp/fileserver/caddyfile.go +++ b/modules/caddyhttp/fileserver/caddyfile.go @@ -171,6 +171,17 @@ func (fsrv *FileServer) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { } fsrv.EtagFileExtensions = etagFileExtensions + case "sort": + for d.NextArg() { + dVal := d.Val() + switch dVal { + case sortByName, sortBySize, sortByTime, sortOrderAsc, sortOrderDesc: + fsrv.SortOptions = append(fsrv.SortOptions, dVal) + default: + return d.Errf("unknown sort option '%s'", dVal) + } + } + default: return d.Errf("unknown subdirective '%s'", d.Val()) } diff --git a/modules/caddyhttp/fileserver/staticfiles.go b/modules/caddyhttp/fileserver/staticfiles.go index 3d703280..48812cfe 100644 --- a/modules/caddyhttp/fileserver/staticfiles.go +++ b/modules/caddyhttp/fileserver/staticfiles.go @@ -153,6 +153,16 @@ type FileServer struct { // a 404 error. By default, this is false (disabled). PassThru bool `json:"pass_thru,omitempty"` + // Override the default sort. + // It includes the following options: + // - sort_by: name(default), namedirfirst, size, time + // - order: asc(default), desc + // eg.: + // - `sort time desc` will sort by time in descending order + // - `sort size` will sort by size in ascending order + // The first option must be `sort_by` and the second option must be `order` (if exists). + SortOptions []string `json:"sort,omitempty"` + // Selection of encoders to use to check for precompressed files. PrecompressedRaw caddy.ModuleMap `json:"precompressed,omitempty" caddy:"namespace=http.precompressed"` @@ -236,6 +246,22 @@ func (fsrv *FileServer) Provision(ctx caddy.Context) error { fsrv.precompressors[ae] = p } + // check sort options + for idx, sortOption := range fsrv.SortOptions { + switch idx { + case 0: + if sortOption != sortByName && sortOption != sortByNameDirFirst && sortOption != sortBySize && sortOption != sortByTime { + return fmt.Errorf("the first option must be one of the following: %s, %s, %s, %s, but got %s", sortByName, sortByNameDirFirst, sortBySize, sortByTime, sortOption) + } + case 1: + if sortOption != sortOrderAsc && sortOption != sortOrderDesc { + return fmt.Errorf("the second option must be one of the following: %s, %s, but got %s", sortOrderAsc, sortOrderDesc, sortOption) + } + default: + return fmt.Errorf("only max 2 sort options are allowed, but got %d", idx+1) + } + } + return nil } From a8b0dfa8da5616837f7e028578ddb9b7ad36b64e Mon Sep 17 00:00:00 2001 From: WeidiDeng Date: Wed, 7 Aug 2024 12:08:32 +0800 Subject: [PATCH 08/23] go.mod: update quic-go package (#6498) --- go.mod | 2 +- go.sum | 6 ++---- modules/caddyhttp/server.go | 1 + 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index d2fec7db..d4153c37 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/klauspost/cpuid/v2 v2.2.7 github.com/mholt/acmez/v2 v2.0.1 github.com/prometheus/client_golang v1.19.1 - github.com/quic-go/quic-go v0.44.0 + github.com/quic-go/quic-go v0.46.0 github.com/smallstep/certificates v0.26.1 github.com/smallstep/nosql v0.6.1 github.com/smallstep/truststore v0.13.0 diff --git a/go.sum b/go.sum index 0ed480b2..2a8218fd 100644 --- a/go.sum +++ b/go.sum @@ -320,8 +320,6 @@ github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8P github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/peterbourgon/diskv/v3 v3.0.1 h1:x06SQA46+PKIUftmEujdwSEpIx8kR+M9eLYsUxeYveU= github.com/peterbourgon/diskv/v3 v3.0.1/go.mod h1:kJ5Ny7vLdARGU3WUuy6uzO6T0nb/2gWcT1JiBvRmb5o= -github.com/pires/go-proxyproto v0.7.0 h1:IukmRewDQFWC7kfnb66CSomk2q/seBuilHBYFwyq0Hs= -github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP40O9LbuiFR4= github.com/pires/go-proxyproto v0.7.1-0.20240628150027-b718e7ce4964 h1:ct/vxNBgHpASQ4sT8NaBX9LtsEtluZqaUJydLG50U3E= github.com/pires/go-proxyproto v0.7.1-0.20240628150027-b718e7ce4964/go.mod h1:iknsfgnH8EkjrMeMyvfKByp9TiBZCKZM0jx2xmKqnVY= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -341,8 +339,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.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= -github.com/quic-go/quic-go v0.44.0 h1:So5wOr7jyO4vzL2sd8/pD9Kesciv91zSk8BoFngItQ0= -github.com/quic-go/quic-go v0.44.0/go.mod h1:z4cx/9Ny9UtGITIPzmPTXh1ULfOyWh4qGQlpnPcWmek= +github.com/quic-go/quic-go v0.46.0 h1:uuwLClEEyk1DNvchH8uCByQVjo3yKL9opKulExNDs7Y= +github.com/quic-go/quic-go v0.46.0/go.mod h1:1dLehS7TIR64+vxGR70GDcatWTOtMX2PUtnKsjbTurI= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= diff --git a/modules/caddyhttp/server.go b/modules/caddyhttp/server.go index 96a819b4..6caaabcd 100644 --- a/modules/caddyhttp/server.go +++ b/modules/caddyhttp/server.go @@ -602,6 +602,7 @@ func (s *Server) serveHTTP3(addr caddy.NetworkAddress, tlsCfg *tls.Config) error QUICConfig: &quic.Config{ Versions: []quic.Version{quic.Version1, quic.Version2}, }, + IdleTimeout: time.Duration(s.IdleTimeout), ConnContext: func(ctx context.Context, c quic.Connection) context.Context { return context.WithValue(ctx, quicConnCtxKey, c) }, From 59cbb2c83a03b6fe352ae0b5d05581d9148a4d24 Mon Sep 17 00:00:00 2001 From: vnxme <46669194+vnxme@users.noreply.github.com> Date: Wed, 7 Aug 2024 20:02:23 +0300 Subject: [PATCH 09/23] caddytls,caddyhttp: Placeholders for some TLS and HTTP matchers (#6480) * Runtime placeholders for caddytls matchers (1/3): - remove IPs validation in UnmarshalCaddyfile * Runtime placeholders for caddytls matchers (2/3): - add placeholder replacement for IPs in Provision * Runtime placeholders for caddytls matchers (3/3): - add placeholder replacement for other strings * Runtime placeholders for caddyhttp matchers (1/1): - add placeholder replacement for IPs in Provision * Runtime placeholders for caddyhttp/caddytls matchers: - move PrivateRandesCIDR under internal --- internal/ranges.go | 14 +++++ modules/caddyhttp/ip_matchers.go | 7 ++- modules/caddyhttp/ip_range.go | 16 +----- modules/caddyhttp/reverseproxy/caddyfile.go | 3 +- modules/caddytls/matchers.go | 58 ++++++++++++--------- 5 files changed, 57 insertions(+), 41 deletions(-) create mode 100644 internal/ranges.go diff --git a/internal/ranges.go b/internal/ranges.go new file mode 100644 index 00000000..e9429e26 --- /dev/null +++ b/internal/ranges.go @@ -0,0 +1,14 @@ +package internal + +// PrivateRangesCIDR returns a list of private CIDR range +// strings, which can be used as a configuration shortcut. +func PrivateRangesCIDR() []string { + return []string{ + "192.168.0.0/16", + "172.16.0.0/12", + "10.0.0.0/8", + "127.0.0.1/8", + "fd00::/8", + "::1", + } +} diff --git a/modules/caddyhttp/ip_matchers.go b/modules/caddyhttp/ip_matchers.go index 9101a035..2e735cb6 100644 --- a/modules/caddyhttp/ip_matchers.go +++ b/modules/caddyhttp/ip_matchers.go @@ -29,6 +29,7 @@ import ( "github.com/caddyserver/caddy/v2" "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" + "github.com/caddyserver/caddy/v2/internal" ) // MatchRemoteIP matches requests by the remote IP address, @@ -79,7 +80,7 @@ func (m *MatchRemoteIP) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { return d.Err("the 'forwarded' option is no longer supported; use the 'client_ip' matcher instead") } if d.Val() == "private_ranges" { - m.Ranges = append(m.Ranges, PrivateRangesCIDR()...) + m.Ranges = append(m.Ranges, internal.PrivateRangesCIDR()...) continue } m.Ranges = append(m.Ranges, d.Val()) @@ -173,7 +174,7 @@ func (m *MatchClientIP) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { for d.Next() { for d.NextArg() { if d.Val() == "private_ranges" { - m.Ranges = append(m.Ranges, PrivateRangesCIDR()...) + m.Ranges = append(m.Ranges, internal.PrivateRangesCIDR()...) continue } m.Ranges = append(m.Ranges, d.Val()) @@ -250,7 +251,9 @@ func (m MatchClientIP) Match(r *http.Request) bool { func provisionCidrsZonesFromRanges(ranges []string) ([]*netip.Prefix, []string, error) { cidrs := []*netip.Prefix{} zones := []string{} + repl := caddy.NewReplacer() for _, str := range ranges { + str = repl.ReplaceAll(str, "") // Exclude the zone_id from the IP if strings.Contains(str, "%") { split := strings.Split(str, "%") diff --git a/modules/caddyhttp/ip_range.go b/modules/caddyhttp/ip_range.go index b1db2547..7632e460 100644 --- a/modules/caddyhttp/ip_range.go +++ b/modules/caddyhttp/ip_range.go @@ -22,6 +22,7 @@ import ( "github.com/caddyserver/caddy/v2" "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" + "github.com/caddyserver/caddy/v2/internal" ) func init() { @@ -92,7 +93,7 @@ func (m *StaticIPRange) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { } for d.NextArg() { if d.Val() == "private_ranges" { - m.Ranges = append(m.Ranges, PrivateRangesCIDR()...) + m.Ranges = append(m.Ranges, internal.PrivateRangesCIDR()...) continue } m.Ranges = append(m.Ranges, d.Val()) @@ -121,19 +122,6 @@ func CIDRExpressionToPrefix(expr string) (netip.Prefix, error) { return prefix, nil } -// PrivateRangesCIDR returns a list of private CIDR range -// strings, which can be used as a configuration shortcut. -func PrivateRangesCIDR() []string { - return []string{ - "192.168.0.0/16", - "172.16.0.0/12", - "10.0.0.0/8", - "127.0.0.1/8", - "fd00::/8", - "::1", - } -} - // Interface guards var ( _ caddy.Provisioner = (*StaticIPRange)(nil) diff --git a/modules/caddyhttp/reverseproxy/caddyfile.go b/modules/caddyhttp/reverseproxy/caddyfile.go index 1c3b4944..4ca5d0e0 100644 --- a/modules/caddyhttp/reverseproxy/caddyfile.go +++ b/modules/caddyhttp/reverseproxy/caddyfile.go @@ -28,6 +28,7 @@ import ( "github.com/caddyserver/caddy/v2/caddyconfig" "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" "github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile" + "github.com/caddyserver/caddy/v2/internal" "github.com/caddyserver/caddy/v2/modules/caddyhttp" "github.com/caddyserver/caddy/v2/modules/caddyhttp/headers" "github.com/caddyserver/caddy/v2/modules/caddyhttp/rewrite" @@ -688,7 +689,7 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { case "trusted_proxies": for d.NextArg() { if d.Val() == "private_ranges" { - h.TrustedProxies = append(h.TrustedProxies, caddyhttp.PrivateRangesCIDR()...) + h.TrustedProxies = append(h.TrustedProxies, internal.PrivateRangesCIDR()...) continue } h.TrustedProxies = append(h.TrustedProxies, d.Val()) diff --git a/modules/caddytls/matchers.go b/modules/caddytls/matchers.go index a23a1931..83a46471 100644 --- a/modules/caddytls/matchers.go +++ b/modules/caddytls/matchers.go @@ -26,6 +26,7 @@ import ( "github.com/caddyserver/caddy/v2" "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" + "github.com/caddyserver/caddy/v2/internal" ) func init() { @@ -49,8 +50,17 @@ func (MatchServerName) CaddyModule() caddy.ModuleInfo { // Match matches hello based on SNI. func (m MatchServerName) Match(hello *tls.ClientHelloInfo) bool { + // caddytls.TestServerNameMatcher calls this function without any context + var repl *caddy.Replacer + if ctx := hello.Context(); ctx != nil { + repl = ctx.Value(caddy.ReplacerCtxKey).(*caddy.Replacer) + } else { + repl = caddy.NewReplacer() + } + for _, name := range m { - if certmagic.MatchWildcard(hello.ServerName, name) { + rs := repl.ReplaceAll(name, "") + if certmagic.MatchWildcard(hello.ServerName, rs) { return true } } @@ -107,16 +117,19 @@ func (MatchRemoteIP) CaddyModule() caddy.ModuleInfo { // Provision parses m's IP ranges, either from IP or CIDR expressions. func (m *MatchRemoteIP) Provision(ctx caddy.Context) error { + repl := caddy.NewReplacer() m.logger = ctx.Logger() for _, str := range m.Ranges { - cidrs, err := m.parseIPRange(str) + rs := repl.ReplaceAll(str, "") + cidrs, err := m.parseIPRange(rs) if err != nil { return err } m.cidrs = append(m.cidrs, cidrs...) } for _, str := range m.NotRanges { - cidrs, err := m.parseIPRange(str) + rs := repl.ReplaceAll(str, "") + cidrs, err := m.parseIPRange(rs) if err != nil { return err } @@ -185,22 +198,18 @@ func (m *MatchRemoteIP) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { for d.NextArg() { val := d.Val() + var exclamation bool if len(val) > 1 && val[0] == '!' { - prefixes, err := m.parseIPRange(val[1:]) - if err != nil { - return err - } - for _, prefix := range prefixes { - m.NotRanges = append(m.NotRanges, prefix.String()) - } + exclamation, val = true, val[1:] + } + ranges := []string{val} + if val == "private_ranges" { + ranges = internal.PrivateRangesCIDR() + } + if exclamation { + m.NotRanges = append(m.NotRanges, ranges...) } else { - prefixes, err := m.parseIPRange(val) - if err != nil { - return err - } - for _, prefix := range prefixes { - m.Ranges = append(m.Ranges, prefix.String()) - } + m.Ranges = append(m.Ranges, ranges...) } } @@ -233,9 +242,11 @@ func (MatchLocalIP) CaddyModule() caddy.ModuleInfo { // Provision parses m's IP ranges, either from IP or CIDR expressions. func (m *MatchLocalIP) Provision(ctx caddy.Context) error { + repl := caddy.NewReplacer() m.logger = ctx.Logger() for _, str := range m.Ranges { - cidrs, err := m.parseIPRange(str) + rs := repl.ReplaceAll(str, "") + cidrs, err := m.parseIPRange(rs) if err != nil { return err } @@ -300,13 +311,12 @@ func (m *MatchLocalIP) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { } for d.NextArg() { - prefixes, err := m.parseIPRange(d.Val()) - if err != nil { - return err - } - for _, prefix := range prefixes { - m.Ranges = append(m.Ranges, prefix.String()) + val := d.Val() + if val == "private_ranges" { + m.Ranges = append(m.Ranges, internal.PrivateRangesCIDR()...) + continue } + m.Ranges = append(m.Ranges, val) } // No blocks are supported From b85b6c64690dc75fd3096e452dbf8881456d0da7 Mon Sep 17 00:00:00 2001 From: Steffen Busch <37350514+steffenbusch@users.noreply.github.com> Date: Wed, 7 Aug 2024 21:39:15 +0200 Subject: [PATCH 10/23] replacer: `{file.*}` global placeholder strips trailing newline (#6411) Co-authored-by: Kanashimia --- .../foo_with_multiple_trailing_newlines.txt | 2 ++ .../testdata/foo_with_trailing_newline.txt | 1 + replacer.go | 3 +++ replacer_test.go | 15 +++++++++++++++ 4 files changed, 21 insertions(+) create mode 100644 caddytest/integration/testdata/foo_with_multiple_trailing_newlines.txt create mode 100644 caddytest/integration/testdata/foo_with_trailing_newline.txt diff --git a/caddytest/integration/testdata/foo_with_multiple_trailing_newlines.txt b/caddytest/integration/testdata/foo_with_multiple_trailing_newlines.txt new file mode 100644 index 00000000..75d7bfb8 --- /dev/null +++ b/caddytest/integration/testdata/foo_with_multiple_trailing_newlines.txt @@ -0,0 +1,2 @@ +foo + diff --git a/caddytest/integration/testdata/foo_with_trailing_newline.txt b/caddytest/integration/testdata/foo_with_trailing_newline.txt new file mode 100644 index 00000000..257cc564 --- /dev/null +++ b/caddytest/integration/testdata/foo_with_trailing_newline.txt @@ -0,0 +1 @@ +foo diff --git a/replacer.go b/replacer.go index e5d2913e..65815c92 100644 --- a/replacer.go +++ b/replacer.go @@ -15,6 +15,7 @@ package caddy import ( + "bytes" "fmt" "io" "net/http" @@ -354,6 +355,8 @@ func (f fileReplacementProvider) replace(key string) (any, bool) { zap.Error(err)) return nil, true } + body = bytes.TrimSuffix(body, []byte("\n")) + body = bytes.TrimSuffix(body, []byte("\r")) return string(body), true } diff --git a/replacer_test.go b/replacer_test.go index cf4d321b..1c1a7048 100644 --- a/replacer_test.go +++ b/replacer_test.go @@ -431,6 +431,14 @@ func TestReplacerNew(t *testing.T) { variable: "file.caddytest/integration/testdata/foo.txt", value: "foo", }, + { + variable: "file.caddytest/integration/testdata/foo_with_trailing_newline.txt", + value: "foo", + }, + { + variable: "file.caddytest/integration/testdata/foo_with_multiple_trailing_newlines.txt", + value: "foo" + getEOL(), + }, } { if val, ok := repl.providers[1].replace(tc.variable); ok { if val != tc.value { @@ -442,6 +450,13 @@ func TestReplacerNew(t *testing.T) { } } +func getEOL() string { + if os.PathSeparator == '\\' { + return "\r\n" // Windows EOL + } + return "\n" // Unix and modern macOS EOL +} + func TestReplacerNewWithoutFile(t *testing.T) { repl := NewReplacer().WithoutFile() From 52bad451819ca142e66a66e8424821f19cd96358 Mon Sep 17 00:00:00 2001 From: WeidiDeng Date: Thu, 8 Aug 2024 09:52:50 +0800 Subject: [PATCH 11/23] go.mod: update update golang/x/net (#6500) --- go.mod | 14 +++++++------- go.sum | 28 ++++++++++++++-------------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/go.mod b/go.mod index d4153c37..e6dd928b 100644 --- a/go.mod +++ b/go.mod @@ -37,11 +37,11 @@ require ( go.uber.org/automaxprocs v1.5.3 go.uber.org/zap v1.27.0 go.uber.org/zap/exp v0.2.0 - golang.org/x/crypto v0.23.0 + golang.org/x/crypto v0.26.0 golang.org/x/crypto/x509roots/fallback v0.0.0-20240507223354-67b13616a595 - golang.org/x/net v0.25.0 - golang.org/x/sync v0.7.0 - golang.org/x/term v0.20.0 + golang.org/x/net v0.28.0 + golang.org/x/sync v0.8.0 + golang.org/x/term v0.23.0 golang.org/x/time v0.5.0 gopkg.in/natefinch/lumberjack.v2 v2.2.1 gopkg.in/yaml.v3 v3.0.1 @@ -147,9 +147,9 @@ require ( go.step.sm/linkedca v0.20.1 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/mod v0.17.0 // indirect - golang.org/x/sys v0.20.0 - golang.org/x/text v0.15.0 // indirect - golang.org/x/tools v0.21.0 // indirect + golang.org/x/sys v0.23.0 + golang.org/x/text v0.17.0 // indirect + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect google.golang.org/grpc v1.63.2 // indirect google.golang.org/protobuf v1.34.1 // indirect howett.net/plist v1.0.0 // indirect diff --git a/go.sum b/go.sum index 2a8218fd..5bf39b42 100644 --- a/go.sum +++ b/go.sum @@ -510,8 +510,8 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= -golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= +golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/crypto/x509roots/fallback v0.0.0-20240507223354-67b13616a595 h1:TgSqweA595vD0Zt86JzLv3Pb/syKg8gd5KMGGbJPYFw= golang.org/x/crypto/x509roots/fallback v0.0.0-20240507223354-67b13616a595/go.mod h1:kNa9WdvYnzFwC79zRpLRMJbdEFlhyM5RPFBBZp/wWH8= golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= @@ -532,15 +532,15 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= +golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo= golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -567,8 +567,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= +golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -576,8 +576,8 @@ golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= -golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= -golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= +golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -588,8 +588,8 @@ golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -603,8 +603,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= -golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From 21af88fefc9a8239a024f635f1c6fdd9defd7eb7 Mon Sep 17 00:00:00 2001 From: WeidiDeng Date: Thu, 8 Aug 2024 20:53:30 +0800 Subject: [PATCH 12/23] reverseproxy: Disable keep alive for h2c requests (#6343) --- modules/caddyhttp/reverseproxy/httptransport.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/caddyhttp/reverseproxy/httptransport.go b/modules/caddyhttp/reverseproxy/httptransport.go index d4245368..9a82341d 100644 --- a/modules/caddyhttp/reverseproxy/httptransport.go +++ b/modules/caddyhttp/reverseproxy/httptransport.go @@ -446,6 +446,9 @@ func (h *HTTPTransport) RoundTrip(req *http.Request) (*http.Response, error) { // if H2C ("HTTP/2 over cleartext") is enabled and the upstream request is // HTTP without TLS, use the alternate H2C-capable transport instead if req.URL.Scheme == "http" && h.h2cTransport != nil { + // There is no dedicated DisableKeepAlives field in *http2.Transport. + // This is an alternative way to disable keep-alive. + req.Close = h.Transport.DisableKeepAlives return h.h2cTransport.RoundTrip(req) } From 7cf8376e638948490be3e9eb5c7d58ce2a4b93b3 Mon Sep 17 00:00:00 2001 From: vnxme <46669194+vnxme@users.noreply.github.com> Date: Mon, 12 Aug 2024 10:01:09 +0300 Subject: [PATCH 13/23] matchers: fix a regression in #6480 (#6510) The context may have no replacer --- modules/caddytls/matchers.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/modules/caddytls/matchers.go b/modules/caddytls/matchers.go index 83a46471..f9462237 100644 --- a/modules/caddytls/matchers.go +++ b/modules/caddytls/matchers.go @@ -50,12 +50,13 @@ func (MatchServerName) CaddyModule() caddy.ModuleInfo { // Match matches hello based on SNI. func (m MatchServerName) Match(hello *tls.ClientHelloInfo) bool { + repl := caddy.NewReplacer() // caddytls.TestServerNameMatcher calls this function without any context - var repl *caddy.Replacer if ctx := hello.Context(); ctx != nil { - repl = ctx.Value(caddy.ReplacerCtxKey).(*caddy.Replacer) - } else { - repl = caddy.NewReplacer() + // In some situations the existing context may have no replacer + if replAny := ctx.Value(caddy.ReplacerCtxKey); replAny != nil { + repl = replAny.(*caddy.Replacer) + } } for _, name := range m { From 3a48b0336961e43e6f6f062bde7cecaa11043941 Mon Sep 17 00:00:00 2001 From: vnxme <46669194+vnxme@users.noreply.github.com> Date: Mon, 12 Aug 2024 12:47:05 +0300 Subject: [PATCH 14/23] Move PrivateRangesCIDR() back: add a pass-through function (#6514) --- modules/caddyhttp/ip_range.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/modules/caddyhttp/ip_range.go b/modules/caddyhttp/ip_range.go index 7632e460..bfd76c14 100644 --- a/modules/caddyhttp/ip_range.go +++ b/modules/caddyhttp/ip_range.go @@ -128,3 +128,10 @@ var ( _ caddyfile.Unmarshaler = (*StaticIPRange)(nil) _ IPRangeSource = (*StaticIPRange)(nil) ) + +// PrivateRangesCIDR returns a list of private CIDR range +// strings, which can be used as a configuration shortcut. +// Note: this function is used at least by mholt/caddy-l4. +func PrivateRangesCIDR() []string { + return internal.PrivateRangesCIDR() +} From 9ddb78fadcdbec89a609127918604174121dcf42 Mon Sep 17 00:00:00 2001 From: WeidiDeng Date: Tue, 13 Aug 2024 10:26:20 +0800 Subject: [PATCH 15/23] cmd: ignore exec.ErrDot when starting caddy in background (#6512) Co-authored-by: Mohammed Al Sahaf --- cmd/commandfuncs.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cmd/commandfuncs.go b/cmd/commandfuncs.go index 746cf3da..49d0321e 100644 --- a/cmd/commandfuncs.go +++ b/cmd/commandfuncs.go @@ -74,6 +74,10 @@ func cmdStart(fl Flags) (int, error) { // 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()) + // we should be able to run caddy in relative paths + if errors.Is(cmd.Err, exec.ErrDot) { + cmd.Err = nil + } if configFlag != "" { cmd.Args = append(cmd.Args, "--config", configFlag) } From 043fe41ab8d39615f346b63c1e51bcf16feda1d8 Mon Sep 17 00:00:00 2001 From: Mohammed Al Sahaf Date: Sun, 18 Aug 2024 12:54:12 +0300 Subject: [PATCH 16/23] ci: don't exit early on error in remote CI machine (#6519) --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a006d26b..0c6846fc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -150,6 +150,7 @@ jobs: uses: actions/checkout@v4 - name: Run Tests run: | + set +e mkdir -p ~/.ssh && echo -e "${SSH_KEY//_/\\n}" > ~/.ssh/id_ecdsa && chmod og-rwx ~/.ssh/id_ecdsa # short sha is enough? From 54a0c8f94821f393edec1a1bc9459f2363c95117 Mon Sep 17 00:00:00 2001 From: Jesper Brix Rosenkilde Date: Mon, 19 Aug 2024 18:55:55 +0200 Subject: [PATCH 17/23] reverseproxy: Active health checks request body option (#6520) * Add an option to specify the body used for active health checks * Replacer on request body --- ...reverse_proxy_health_reqbody.caddyfiletest | 40 +++++++++++++++++++ modules/caddyhttp/reverseproxy/caddyfile.go | 31 +++++++++----- .../caddyhttp/reverseproxy/healthchecks.go | 19 +++++++-- 3 files changed, 78 insertions(+), 12 deletions(-) create mode 100644 caddytest/integration/caddyfile_adapt/reverse_proxy_health_reqbody.caddyfiletest diff --git a/caddytest/integration/caddyfile_adapt/reverse_proxy_health_reqbody.caddyfiletest b/caddytest/integration/caddyfile_adapt/reverse_proxy_health_reqbody.caddyfiletest new file mode 100644 index 00000000..ae5a6791 --- /dev/null +++ b/caddytest/integration/caddyfile_adapt/reverse_proxy_health_reqbody.caddyfiletest @@ -0,0 +1,40 @@ +:8884 + +reverse_proxy 127.0.0.1:65535 { + health_uri /health + health_request_body "test body" +} +---------- +{ + "apps": { + "http": { + "servers": { + "srv0": { + "listen": [ + ":8884" + ], + "routes": [ + { + "handle": [ + { + "handler": "reverse_proxy", + "health_checks": { + "active": { + "body": "test body", + "uri": "/health" + } + }, + "upstreams": [ + { + "dial": "127.0.0.1:65535" + } + ] + } + ] + } + ] + } + } + } + } +} diff --git a/modules/caddyhttp/reverseproxy/caddyfile.go b/modules/caddyhttp/reverseproxy/caddyfile.go index 4ca5d0e0..cd0e5d94 100644 --- a/modules/caddyhttp/reverseproxy/caddyfile.go +++ b/modules/caddyhttp/reverseproxy/caddyfile.go @@ -69,19 +69,20 @@ func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) // lb_retry_match // // # active health checking -// health_uri -// health_port -// health_interval -// health_passes -// health_fails -// health_timeout -// health_status -// health_body +// health_uri +// health_port +// health_interval +// health_passes +// health_fails +// health_timeout +// health_status +// health_body +// health_method +// health_request_body // health_follow_redirects // health_headers { // [] // } -// health_method // // # passive health checking // fail_duration @@ -425,6 +426,18 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { } h.HealthChecks.Active.Method = d.Val() + case "health_request_body": + if !d.NextArg() { + return d.ArgErr() + } + if h.HealthChecks == nil { + h.HealthChecks = new(HealthChecks) + } + if h.HealthChecks.Active == nil { + h.HealthChecks.Active = new(ActiveHealthChecks) + } + h.HealthChecks.Active.Body = d.Val() + case "health_interval": if !d.NextArg() { return d.ArgErr() diff --git a/modules/caddyhttp/reverseproxy/healthchecks.go b/modules/caddyhttp/reverseproxy/healthchecks.go index 3b5a6a3a..efa1dbf0 100644 --- a/modules/caddyhttp/reverseproxy/healthchecks.go +++ b/modules/caddyhttp/reverseproxy/healthchecks.go @@ -24,6 +24,7 @@ import ( "regexp" "runtime/debug" "strconv" + "strings" "time" "go.uber.org/zap" @@ -93,6 +94,9 @@ type ActiveHealthChecks struct { // The HTTP method to use for health checks (default "GET"). Method string `json:"method,omitempty"` + // The body to send with the health check request. + Body string `json:"body,omitempty"` + // Whether to follow HTTP redirects in response to active health checks (default off). FollowRedirects bool `json:"follow_redirects,omitempty"` @@ -396,6 +400,16 @@ func (h *Handler) doActiveHealthCheck(dialInfo DialInfo, hostAddr string, networ u.Path = h.HealthChecks.Active.Path } + // replacer used for both body and headers. Only globals (env vars, system info, etc.) are available + repl := caddy.NewReplacer() + + // if body is provided, create a reader for it, otherwise nil + var requestBody io.Reader + if h.HealthChecks.Active.Body != "" { + // set body, using replacer + requestBody = strings.NewReader(repl.ReplaceAll(h.HealthChecks.Active.Body, "")) + } + // attach dialing information to this request, as well as context values that // may be expected by handlers of this request ctx := h.ctx.Context @@ -403,15 +417,14 @@ func (h *Handler) doActiveHealthCheck(dialInfo DialInfo, hostAddr string, networ ctx = context.WithValue(ctx, caddyhttp.VarsCtxKey, map[string]any{ dialInfoVarKey: dialInfo, }) - req, err := http.NewRequestWithContext(ctx, h.HealthChecks.Active.Method, u.String(), nil) + req, err := http.NewRequestWithContext(ctx, h.HealthChecks.Active.Method, u.String(), requestBody) if err != nil { return fmt.Errorf("making request: %v", err) } ctx = context.WithValue(ctx, caddyhttp.OriginalRequestCtxKey, *req) req = req.WithContext(ctx) - // set headers, using a replacer with only globals (env vars, system info, etc.) - repl := caddy.NewReplacer() + // set headers, using replacer repl.Set("http.reverse_proxy.active.target_upstream", networkAddr) for key, vals := range h.HealthChecks.Active.Headers { key = repl.ReplaceAll(key, "") From 2bb2ecc5498c99d535f5b8f56fb8a4732e818ad3 Mon Sep 17 00:00:00 2001 From: Jens-Uwe Mager Date: Wed, 21 Aug 2024 19:39:20 +0200 Subject: [PATCH 18/23] reverseproxy: Change errors writing the response to warning. (#6532) Most of the errors that can be seen here are write errors due to clients aborting the request from their side. Often seen ones include: * writing: ... write: broken pipe * writing: ... connection timed out * writing: http2: stream closed * writing: timeout... * writing: h3 error... Most of these errors are beyond of the control of caddy on the client side, probably nothing can be done on the server side. It still warrants researching when these errors occur very often, so a change in level from error to warn is better here to not polute the logs with errors in the normal case. --- modules/caddyhttp/reverseproxy/reverseproxy.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/caddyhttp/reverseproxy/reverseproxy.go b/modules/caddyhttp/reverseproxy/reverseproxy.go index 4f97edea..1883ac07 100644 --- a/modules/caddyhttp/reverseproxy/reverseproxy.go +++ b/modules/caddyhttp/reverseproxy/reverseproxy.go @@ -979,7 +979,7 @@ func (h *Handler) finalizeResponse( // we'll just log the error and abort the stream here and panic just as // the standard lib's proxy to propagate the stream error. // see issue https://github.com/caddyserver/caddy/issues/5951 - logger.Error("aborting with incomplete response", zap.Error(err)) + logger.Warn("aborting with incomplete response", zap.Error(err)) // no extra logging from stdlib panic(http.ErrAbortHandler) } From 8ccfedf2bb0a1759aa68d4989f05d9a52ba41a34 Mon Sep 17 00:00:00 2001 From: a Date: Wed, 21 Aug 2024 22:29:42 -0500 Subject: [PATCH 19/23] cmd: Use a factory to create the caddy root command (#6533) Co-authored-by: Francis Lavoie --- cmd/cobra.go | 26 ++++++----- cmd/commandfactory.go | 28 ++++++++++++ cmd/commands.go | 100 ++++++++++++++++++++++-------------------- cmd/main.go | 2 +- 4 files changed, 96 insertions(+), 60 deletions(-) create mode 100644 cmd/commandfactory.go diff --git a/cmd/cobra.go b/cmd/cobra.go index 1a250920..9ecb389e 100644 --- a/cmd/cobra.go +++ b/cmd/cobra.go @@ -8,9 +8,10 @@ import ( "github.com/caddyserver/caddy/v2" ) -var rootCmd = &cobra.Command{ - Use: "caddy", - Long: `Caddy is an extensible server platform written in Go. +var defaultFactory = newRootCommandFactory(func() *cobra.Command { + return &cobra.Command{ + Use: "caddy", + 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,23 +92,26 @@ package installers: https://caddyserver.com/docs/install Instructions for running Caddy in production are also available: https://caddyserver.com/docs/running `, - Example: ` $ caddy run + Example: ` $ caddy run $ caddy run --config caddy.json $ caddy reload --config caddy.json $ caddy stop`, - // kind of annoying to have all the help text printed out if - // caddy has an error provisioning its modules, for instance... - SilenceUsage: true, - Version: onlyVersionText(), -} + // kind of annoying to have all the help text printed out if + // caddy has an error provisioning its modules, for instance... + SilenceUsage: true, + Version: onlyVersionText(), + } +}) const fullDocsFooter = `Full documentation is available at: https://caddyserver.com/docs/command-line` func init() { - rootCmd.SetVersionTemplate("{{.Version}}\n") - rootCmd.SetHelpTemplate(rootCmd.HelpTemplate() + "\n" + fullDocsFooter + "\n") + defaultFactory.Use(func(rootCmd *cobra.Command) { + rootCmd.SetVersionTemplate("{{.Version}}\n") + rootCmd.SetHelpTemplate(rootCmd.HelpTemplate() + "\n" + fullDocsFooter + "\n") + }) } func onlyVersionText() string { diff --git a/cmd/commandfactory.go b/cmd/commandfactory.go new file mode 100644 index 00000000..ac571a21 --- /dev/null +++ b/cmd/commandfactory.go @@ -0,0 +1,28 @@ +package caddycmd + +import ( + "github.com/spf13/cobra" +) + +type rootCommandFactory struct { + constructor func() *cobra.Command + options []func(*cobra.Command) +} + +func newRootCommandFactory(fn func() *cobra.Command) *rootCommandFactory { + return &rootCommandFactory{ + constructor: fn, + } +} + +func (f *rootCommandFactory) Use(fn func(cmd *cobra.Command)) { + f.options = append(f.options, fn) +} + +func (f *rootCommandFactory) Build() *cobra.Command { + o := f.constructor() + for _, v := range f.options { + v(o) + } + return o +} diff --git a/cmd/commands.go b/cmd/commands.go index e5e1265e..0853ebf8 100644 --- a/cmd/commands.go +++ b/cmd/commands.go @@ -438,43 +438,44 @@ EXPERIMENTAL: May be changed or removed. }, }) - RegisterCommand(Command{ - Name: "manpage", - Usage: "--directory ", - Short: "Generates the manual pages for Caddy commands", - Long: ` + defaultFactory.Use(func(rootCmd *cobra.Command) { + RegisterCommand(Command{ + Name: "manpage", + Usage: "--directory ", + Short: "Generates the manual pages for Caddy commands", + Long: ` Generates the manual pages for Caddy commands into the designated directory tagged into section 8 (System Administration). The manual page files are generated into the directory specified by the argument of --directory. If the directory does not exist, it will be created. `, - CobraFunc: func(cmd *cobra.Command) { - cmd.Flags().StringP("directory", "o", "", "The output directory where the manpages are generated") - cmd.RunE = WrapCommandFuncForCobra(func(fl Flags) (int, error) { - dir := strings.TrimSpace(fl.String("directory")) - if dir == "" { - return caddy.ExitCodeFailedQuit, fmt.Errorf("designated output directory and specified section are required") - } - if err := os.MkdirAll(dir, 0o755); err != nil { - return caddy.ExitCodeFailedQuit, err - } - if err := doc.GenManTree(rootCmd, &doc.GenManHeader{ - Title: "Caddy", - Section: "8", // https://en.wikipedia.org/wiki/Man_page#Manual_sections - }, dir); err != nil { - return caddy.ExitCodeFailedQuit, err - } - return caddy.ExitCodeSuccess, nil - }) - }, - }) + CobraFunc: func(cmd *cobra.Command) { + cmd.Flags().StringP("directory", "o", "", "The output directory where the manpages are generated") + cmd.RunE = WrapCommandFuncForCobra(func(fl Flags) (int, error) { + dir := strings.TrimSpace(fl.String("directory")) + if dir == "" { + return caddy.ExitCodeFailedQuit, fmt.Errorf("designated output directory and specified section are required") + } + if err := os.MkdirAll(dir, 0o755); err != nil { + return caddy.ExitCodeFailedQuit, err + } + if err := doc.GenManTree(rootCmd, &doc.GenManHeader{ + Title: "Caddy", + Section: "8", // https://en.wikipedia.org/wiki/Man_page#Manual_sections + }, dir); err != nil { + return caddy.ExitCodeFailedQuit, err + } + return caddy.ExitCodeSuccess, nil + }) + }, + }) - // source: https://github.com/spf13/cobra/blob/main/shell_completions.md - rootCmd.AddCommand(&cobra.Command{ - Use: "completion [bash|zsh|fish|powershell]", - Short: "Generate completion script", - Long: fmt.Sprintf(`To load completions: + // source: https://github.com/spf13/cobra/blob/main/shell_completions.md + rootCmd.AddCommand(&cobra.Command{ + Use: "completion [bash|zsh|fish|powershell]", + Short: "Generate completion script", + Long: fmt.Sprintf(`To load completions: Bash: @@ -513,23 +514,24 @@ argument of --directory. If the directory does not exist, it will be created. PS> %[1]s completion powershell > %[1]s.ps1 # and source this file from your PowerShell profile. `, rootCmd.Root().Name()), - DisableFlagsInUseLine: true, - ValidArgs: []string{"bash", "zsh", "fish", "powershell"}, - Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs), - RunE: func(cmd *cobra.Command, args []string) error { - switch args[0] { - case "bash": - return cmd.Root().GenBashCompletion(os.Stdout) - case "zsh": - return cmd.Root().GenZshCompletion(os.Stdout) - case "fish": - return cmd.Root().GenFishCompletion(os.Stdout, true) - case "powershell": - return cmd.Root().GenPowerShellCompletionWithDesc(os.Stdout) - default: - return fmt.Errorf("unrecognized shell: %s", args[0]) - } - }, + DisableFlagsInUseLine: true, + ValidArgs: []string{"bash", "zsh", "fish", "powershell"}, + Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs), + RunE: func(cmd *cobra.Command, args []string) error { + switch args[0] { + case "bash": + return cmd.Root().GenBashCompletion(os.Stdout) + case "zsh": + return cmd.Root().GenZshCompletion(os.Stdout) + case "fish": + return cmd.Root().GenFishCompletion(os.Stdout, true) + case "powershell": + return cmd.Root().GenPowerShellCompletionWithDesc(os.Stdout) + default: + return fmt.Errorf("unrecognized shell: %s", args[0]) + } + }, + }) }) } @@ -563,7 +565,9 @@ func RegisterCommand(cmd Command) { if !commandNameRegex.MatchString(cmd.Name) { panic("invalid command name") } - rootCmd.AddCommand(caddyCmdToCobra(cmd)) + defaultFactory.Use(func(rootCmd *cobra.Command) { + rootCmd.AddCommand(caddyCmdToCobra(cmd)) + }) } var commandNameRegex = regexp.MustCompile(`^[a-z0-9]$|^([a-z0-9]+-?[a-z0-9]*)+[a-z0-9]$`) diff --git a/cmd/main.go b/cmd/main.go index 3c3ae627..655c0084 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -72,7 +72,7 @@ func Main() { caddy.Log().Warn("failed to set GOMAXPROCS", zap.Error(err)) } - if err := rootCmd.Execute(); err != nil { + if err := defaultFactory.Build().Execute(); err != nil { var exitError *exitError if errors.As(err, &exitError) { os.Exit(exitError.ExitCode) From 098897bdea67eb31634f440a4d9a69b5753a9ac3 Mon Sep 17 00:00:00 2001 From: Cuckoo Chickoo Date: Thu, 22 Aug 2024 15:45:58 +0530 Subject: [PATCH 20/23] chore: Fix a typo (#6534) Fixes Typo in Docs --- modules/caddyhttp/proxyprotocol/listenerwrapper.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/caddyhttp/proxyprotocol/listenerwrapper.go b/modules/caddyhttp/proxyprotocol/listenerwrapper.go index e25fe02a..440e7071 100644 --- a/modules/caddyhttp/proxyprotocol/listenerwrapper.go +++ b/modules/caddyhttp/proxyprotocol/listenerwrapper.go @@ -40,7 +40,7 @@ type ListenerWrapper struct { Allow []string `json:"allow,omitempty"` allow []netip.Prefix - // Denby is an optional list of CIDR ranges to + // Deny is an optional list of CIDR ranges to // deny PROXY headers from. Deny []string `json:"deny,omitempty"` deny []netip.Prefix From 8af646730be93f4a00b873d1822bfde6be106696 Mon Sep 17 00:00:00 2001 From: Mohammed Al Sahaf Date: Thu, 22 Aug 2024 20:32:44 +0300 Subject: [PATCH 21/23] caddyhttp: run `error` (msg) through replacer (#6536) * error: run `error` (msg) through replacer Signed-off-by: Mohammed Al Sahaf * fix integration test Signed-off-by: Mohammed Al Sahaf --------- Signed-off-by: Mohammed Al Sahaf --- caddytest/caddytest_test.go | 1 - modules/caddyhttp/staticerror.go | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/caddytest/caddytest_test.go b/caddytest/caddytest_test.go index 937537fa..a9d5da93 100644 --- a/caddytest/caddytest_test.go +++ b/caddytest/caddytest_test.go @@ -84,7 +84,6 @@ func TestLoadUnorderedJSON(t *testing.T) { "servers": { "s_server": { "listen": [ - ":9443", ":9080" ], "routes": [ diff --git a/modules/caddyhttp/staticerror.go b/modules/caddyhttp/staticerror.go index b6e10ff3..aeb31140 100644 --- a/modules/caddyhttp/staticerror.go +++ b/modules/caddyhttp/staticerror.go @@ -105,8 +105,7 @@ func (e StaticError) ServeHTTP(w http.ResponseWriter, r *http.Request, _ Handler } statusCode = intVal } - - return Error(statusCode, fmt.Errorf("%s", e.Error)) + return Error(statusCode, fmt.Errorf("%s", repl.ReplaceKnown(e.Error, ""))) } // Interface guard From 4ade967005929e98ae2265d9d7c89b33f1ca951b Mon Sep 17 00:00:00 2001 From: Mohammed Al Sahaf Date: Thu, 22 Aug 2024 22:52:05 +0300 Subject: [PATCH 22/23] reverseproxy: allow user to define source address (#6504) * reverseproxy: allow user to define source address Closes #6503 Signed-off-by: Mohammed Al Sahaf * reverse_proxy: caddyfile support for local_address Signed-off-by: Mohammed Al Sahaf --------- Signed-off-by: Mohammed Al Sahaf --- .../reverse_proxy_localaddr.caddyfiletest | 57 +++++++++++++++++++ modules/caddyhttp/reverseproxy/caddyfile.go | 6 +- .../caddyhttp/reverseproxy/httptransport.go | 29 ++++++++++ 3 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 caddytest/integration/caddyfile_adapt/reverse_proxy_localaddr.caddyfiletest diff --git a/caddytest/integration/caddyfile_adapt/reverse_proxy_localaddr.caddyfiletest b/caddytest/integration/caddyfile_adapt/reverse_proxy_localaddr.caddyfiletest new file mode 100644 index 00000000..d734c9ce --- /dev/null +++ b/caddytest/integration/caddyfile_adapt/reverse_proxy_localaddr.caddyfiletest @@ -0,0 +1,57 @@ +https://example.com { + reverse_proxy http://localhost:54321 { + transport http { + local_address 192.168.0.1 + } + } +} + +---------- +{ + "apps": { + "http": { + "servers": { + "srv0": { + "listen": [ + ":443" + ], + "routes": [ + { + "match": [ + { + "host": [ + "example.com" + ] + } + ], + "handle": [ + { + "handler": "subroute", + "routes": [ + { + "handle": [ + { + "handler": "reverse_proxy", + "transport": { + "local_address": "192.168.0.1", + "protocol": "http" + }, + "upstreams": [ + { + "dial": "localhost:54321" + } + ] + } + ] + } + ] + } + ], + "terminal": true + } + ] + } + } + } + } +} diff --git a/modules/caddyhttp/reverseproxy/caddyfile.go b/modules/caddyhttp/reverseproxy/caddyfile.go index cd0e5d94..12e2b9b9 100644 --- a/modules/caddyhttp/reverseproxy/caddyfile.go +++ b/modules/caddyhttp/reverseproxy/caddyfile.go @@ -1326,7 +1326,11 @@ func (h *HTTPTransport) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { return d.Err("cannot specify \"tls_trust_pool\" twice in caddyfile") } h.TLS.CARaw = caddyconfig.JSONModuleObject(ca, "provider", modStem, nil) - + case "local_address": + if !d.NextArg() { + return d.ArgErr() + } + h.LocalAddress = d.Val() default: return d.Errf("unrecognized subdirective %s", d.Val()) } diff --git a/modules/caddyhttp/reverseproxy/httptransport.go b/modules/caddyhttp/reverseproxy/httptransport.go index 9a82341d..9929ae5d 100644 --- a/modules/caddyhttp/reverseproxy/httptransport.go +++ b/modules/caddyhttp/reverseproxy/httptransport.go @@ -132,6 +132,10 @@ type HTTPTransport struct { // to change or removal while experimental. Versions []string `json:"versions,omitempty"` + // Specify the address to bind to when connecting to an upstream. In other words, + // it is the address the upstream sees as the remote address. + LocalAddress string `json:"local_address,omitempty"` + // The pre-configured underlying HTTP transport. Transport *http.Transport `json:"-"` @@ -185,6 +189,31 @@ func (h *HTTPTransport) NewTransport(caddyCtx caddy.Context) (*http.Transport, e FallbackDelay: time.Duration(h.FallbackDelay), } + if h.LocalAddress != "" { + netaddr, err := caddy.ParseNetworkAddressWithDefaults(h.LocalAddress, "tcp", 0) + if err != nil { + return nil, err + } + if netaddr.PortRangeSize() > 1 { + return nil, fmt.Errorf("local_address must be a single address, not a port range") + } + switch netaddr.Network { + case "tcp", "tcp4", "tcp6": + dialer.LocalAddr, err = net.ResolveTCPAddr(netaddr.Network, netaddr.JoinHostPort(0)) + if err != nil { + return nil, err + } + case "unix", "unixgram", "unixpacket": + dialer.LocalAddr, err = net.ResolveUnixAddr(netaddr.Network, netaddr.JoinHostPort(0)) + if err != nil { + return nil, err + } + case "udp", "udp4", "udp6": + return nil, fmt.Errorf("local_address must be a TCP address, not a UDP address") + default: + return nil, fmt.Errorf("unsupported network") + } + } if h.Resolver != nil { err := h.Resolver.ParseAddresses() if err != nil { From 2028da4e74cd41f0f7f94222c6599da1a371d4b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Fri, 23 Aug 2024 19:01:28 +0200 Subject: [PATCH 23/23] ci: build and test with Go 1.23 (#6526) * chore: build and test with Go 1.23 * ci: bump golangci-lint to v1.60 * fix: make properly wrap errors * ci: remove Go 1.21 --- .github/workflows/ci.yml | 8 ++++---- .github/workflows/cross-build.yml | 4 ++++ .github/workflows/lint.yml | 6 +++--- .github/workflows/release.yml | 6 +++--- README.md | 2 +- caddyconfig/caddyfile/dispenser.go | 2 +- go.mod | 4 ++-- modules/caddyhttp/celmatcher.go | 4 ++-- 8 files changed, 20 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0c6846fc..2c7a67c5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,18 +23,18 @@ jobs: - mac - windows go: - - '1.21' - '1.22' + - '1.23' include: # Set the minimum Go patch version for the given Go minor # Usable via ${{ matrix.GO_SEMVER }} - - go: '1.21' - GO_SEMVER: '~1.21.0' - - go: '1.22' GO_SEMVER: '~1.22.3' + - go: '1.23' + GO_SEMVER: '~1.23.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) # CADDY_BIN_PATH: the path to the compiled Caddy binary, for artifact publishing diff --git a/.github/workflows/cross-build.yml b/.github/workflows/cross-build.yml index c54a9a80..e77e4e99 100644 --- a/.github/workflows/cross-build.yml +++ b/.github/workflows/cross-build.yml @@ -28,6 +28,7 @@ jobs: - 'netbsd' go: - '1.22' + - '1.23' include: # Set the minimum Go patch version for the given Go minor @@ -35,6 +36,9 @@ jobs: - go: '1.22' GO_SEMVER: '~1.22.3' + - go: '1.23' + GO_SEMVER: '~1.23.0' + runs-on: ubuntu-latest continue-on-error: true steps: diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 1ebdb0af..94250f88 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -43,13 +43,13 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: - go-version: '~1.22.3' + go-version: '~1.23' check-latest: true - name: golangci-lint uses: golangci/golangci-lint-action@v6 with: - version: v1.55 + version: v1.60 # Windows times out frequently after about 5m50s if we don't set a longer timeout. args: --timeout 10m @@ -63,5 +63,5 @@ jobs: - name: govulncheck uses: golang/govulncheck-action@v1 with: - go-version-input: '~1.22.3' + go-version-input: '~1.23.0' check-latest: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cb5d750d..1eb59e9d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,13 +13,13 @@ jobs: os: - ubuntu-latest go: - - '1.22' + - '1.23' include: # Set the minimum Go patch version for the given Go minor # Usable via ${{ matrix.GO_SEMVER }} - - go: '1.22' - GO_SEMVER: '~1.22.3' + - go: '1.23' + GO_SEMVER: '~1.23.0' runs-on: ${{ matrix.os }} # https://github.com/sigstore/cosign/issues/1258#issuecomment-1002251233 diff --git a/README.md b/README.md index 57463180..2185eccd 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ See [our online documentation](https://caddyserver.com/docs/install) for other i Requirements: -- [Go 1.21 or newer](https://golang.org/dl/) +- [Go 1.22.3 or newer](https://golang.org/dl/) ### For development diff --git a/caddyconfig/caddyfile/dispenser.go b/caddyconfig/caddyfile/dispenser.go index e36275a1..325bb54d 100644 --- a/caddyconfig/caddyfile/dispenser.go +++ b/caddyconfig/caddyfile/dispenser.go @@ -415,7 +415,7 @@ func (d *Dispenser) EOFErr() error { // Err generates a custom parse-time error with a message of msg. func (d *Dispenser) Err(msg string) error { - return d.Errf(msg) + return d.WrapErr(errors.New(msg)) } // Errf is like Err, but for formatted error messages diff --git a/go.mod b/go.mod index e6dd928b..6ae4a4a8 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,8 @@ module github.com/caddyserver/caddy/v2 -go 1.21.0 +go 1.22.3 -toolchain go1.22.2 +toolchain go1.23.0 require ( github.com/BurntSushi/toml v1.3.2 diff --git a/modules/caddyhttp/celmatcher.go b/modules/caddyhttp/celmatcher.go index d4016478..a5565eb9 100644 --- a/modules/caddyhttp/celmatcher.go +++ b/modules/caddyhttp/celmatcher.go @@ -340,7 +340,7 @@ func (celTypeAdapter) NativeToValue(value any) ref.Val { case time.Time: return types.Timestamp{Time: v} case error: - types.NewErr(v.Error()) + return types.WrapErr(v) } return types.DefaultTypeAdapter.NativeToValue(value) } @@ -499,7 +499,7 @@ func CELMatcherRuntimeFunction(funcName string, fac CELMatcherFactory) functions return func(celReq, matcherData ref.Val) ref.Val { matcher, err := fac(matcherData) if err != nil { - return types.NewErr(err.Error()) + return types.WrapErr(err) } httpReq := celReq.Value().(celHTTPRequest) return types.Bool(matcher.Match(httpReq.Request))