From fab6375a8bebd952abc80e63fa31b648ae1ebc0b Mon Sep 17 00:00:00 2001 From: Jason Yuan Date: Sat, 15 Jun 2024 09:50:31 -0400 Subject: [PATCH 01/40] reverseproxy: add Max-Age option to sticky cookie (#6398) * reverseproxy: add Max-Age option to sticky cookie * Update selectionpolicies.go Co-authored-by: Francis Lavoie * Update selectionpolicies.go Co-authored-by: Francis Lavoie --------- Co-authored-by: Francis Lavoie --- .../reverseproxy/selectionpolicies.go | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/modules/caddyhttp/reverseproxy/selectionpolicies.go b/modules/caddyhttp/reverseproxy/selectionpolicies.go index e61b3e0f..293ff75e 100644 --- a/modules/caddyhttp/reverseproxy/selectionpolicies.go +++ b/modules/caddyhttp/reverseproxy/selectionpolicies.go @@ -26,6 +26,7 @@ import ( "strconv" "strings" "sync/atomic" + "time" "github.com/cespare/xxhash/v2" @@ -613,6 +614,8 @@ type CookieHashSelection struct { Name string `json:"name,omitempty"` // Secret to hash (Hmac256) chosen upstream in cookie Secret string `json:"secret,omitempty"` + // The cookie's Max-Age before it expires. Default is no expiry. + MaxAge caddy.Duration `json:"max_age,omitempty"` // The fallback policy to use if the cookie is not present. Defaults to `random`. FallbackRaw json.RawMessage `json:"fallback,omitempty" caddy:"namespace=http.reverse_proxy.selection_policies inline_key=policy"` @@ -671,6 +674,9 @@ func (s CookieHashSelection) Select(pool UpstreamPool, req *http.Request, w http cookie.Secure = true cookie.SameSite = http.SameSiteNoneMode } + if s.MaxAge > 0 { + cookie.MaxAge = int(time.Duration(s.MaxAge).Seconds()) + } http.SetCookie(w, cookie) return upstream } @@ -699,6 +705,7 @@ func (s CookieHashSelection) Select(pool UpstreamPool, req *http.Request, w http // // lb_policy cookie [ []] { // fallback +// max_age // } // // By default name is `lb` @@ -728,6 +735,24 @@ func (s *CookieHashSelection) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { return err } s.FallbackRaw = mod + case "max_age": + if !d.NextArg() { + return d.ArgErr() + } + if s.MaxAge != 0 { + return d.Err("cookie max_age already specified") + } + maxAge, err := caddy.ParseDuration(d.Val()) + if err != nil { + return d.Errf("invalid duration: %s", d.Val()) + } + if maxAge <= 0 { + return d.Errf("invalid duration: %s, max_age should be non-zero and positive", d.Val()) + } + if d.NextArg() { + return d.ArgErr() + } + s.MaxAge = caddy.Duration(maxAge) default: return d.Errf("unrecognized option '%s'", d.Val()) } From 99dcdf7e426f0dcbdffe510f241ae8a4fd5a56e6 Mon Sep 17 00:00:00 2001 From: Matthew Holt Date: Tue, 18 Jun 2024 14:43:54 -0600 Subject: [PATCH 02/40] caddyhttp: Convert IDNs to ASCII when provisioning Host matcher --- modules/caddyhttp/matchers.go | 20 ++++++++++++++------ modules/caddyhttp/matchers_test.go | 9 +++++++++ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/modules/caddyhttp/matchers.go b/modules/caddyhttp/matchers.go index b1da1468..392312b6 100644 --- a/modules/caddyhttp/matchers.go +++ b/modules/caddyhttp/matchers.go @@ -34,6 +34,7 @@ import ( "github.com/google/cel-go/cel" "github.com/google/cel-go/common/types" "github.com/google/cel-go/common/types/ref" + "golang.org/x/net/idna" "github.com/caddyserver/caddy/v2" "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" @@ -239,13 +240,20 @@ func (m *MatchHost) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { func (m MatchHost) Provision(_ caddy.Context) error { // check for duplicates; they are nonsensical and reduce efficiency // (we could just remove them, but the user should know their config is erroneous) - seen := make(map[string]int) - for i, h := range m { - h = strings.ToLower(h) - if firstI, ok := seen[h]; ok { - return fmt.Errorf("host at index %d is repeated at index %d: %s", firstI, i, h) + seen := make(map[string]int, len(m)) + for i, host := range m { + asciiHost, err := idna.ToASCII(host) + if err != nil { + return fmt.Errorf("converting hostname '%s' to ASCII: %v", host, err) } - seen[h] = i + if asciiHost != host { + m[i] = asciiHost + } + normalizedHost := strings.ToLower(asciiHost) + if firstI, ok := seen[normalizedHost]; ok { + return fmt.Errorf("host at index %d is repeated at index %d: %s", firstI, i, host) + } + seen[normalizedHost] = i } if m.large() { diff --git a/modules/caddyhttp/matchers_test.go b/modules/caddyhttp/matchers_test.go index 5f76a36b..05eaade5 100644 --- a/modules/caddyhttp/matchers_test.go +++ b/modules/caddyhttp/matchers_test.go @@ -78,6 +78,11 @@ func TestHostMatcher(t *testing.T) { input: "bar.example.com", expect: false, }, + { + match: MatchHost{"éxàmplê.com"}, + input: "xn--xmpl-0na6cm.com", + expect: true, + }, { match: MatchHost{"*.example.com"}, input: "example.com", @@ -149,6 +154,10 @@ func TestHostMatcher(t *testing.T) { ctx := context.WithValue(req.Context(), caddy.ReplacerCtxKey, repl) req = req.WithContext(ctx) + if err := tc.match.Provision(caddy.Context{}); err != nil { + t.Errorf("Test %d %v: provisioning failed: %v", i, tc.match, err) + } + actual := tc.match.Match(req) if actual != tc.expect { t.Errorf("Test %d %v: Expected %t, got %t for '%s'", i, tc.match, tc.expect, actual, tc.input) From c2ccf8690f315aa0ebab930c3aadcc6cd11fc9e9 Mon Sep 17 00:00:00 2001 From: Aziz Rmadi <46684200+armadi1809@users.noreply.github.com> Date: Wed, 19 Jun 2024 08:27:10 -0500 Subject: [PATCH 03/40] fileserver: Remove newline characters from precomputed etags (#6394) * Removed newline characters from precomputed etags * Update modules/caddyhttp/fileserver/staticfiles.go --------- Co-authored-by: Matt Holt --- modules/caddyhttp/fileserver/staticfiles.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/modules/caddyhttp/fileserver/staticfiles.go b/modules/caddyhttp/fileserver/staticfiles.go index 5d54742d..3d703280 100644 --- a/modules/caddyhttp/fileserver/staticfiles.go +++ b/modules/caddyhttp/fileserver/staticfiles.go @@ -15,6 +15,7 @@ package fileserver import ( + "bytes" "errors" "fmt" "io" @@ -690,6 +691,10 @@ func (fsrv *FileServer) getEtagFromFile(fileSystem fs.FS, filename string) (stri if err != nil { return "", fmt.Errorf("cannot read etag from file %s: %v", etagFilename, err) } + + // Etags should not contain newline characters + etag = bytes.ReplaceAll(etag, []byte("\n"), []byte{}) + return string(etag), nil } return "", nil From f8861ca16bd475e8519e7dbf5a2b55e81b329874 Mon Sep 17 00:00:00 2001 From: Matthew Holt Date: Fri, 28 Jun 2024 12:15:41 -0600 Subject: [PATCH 04/40] reverseproxy: Wire up TLS options for H3 transport --- modules/caddyhttp/reverseproxy/httptransport.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/modules/caddyhttp/reverseproxy/httptransport.go b/modules/caddyhttp/reverseproxy/httptransport.go index 80a49806..d4245368 100644 --- a/modules/caddyhttp/reverseproxy/httptransport.go +++ b/modules/caddyhttp/reverseproxy/httptransport.go @@ -363,6 +363,13 @@ func (h *HTTPTransport) NewTransport(caddyCtx caddy.Context) (*http.Transport, e // site owners control the backends), so it must be exclusive if len(h.Versions) == 1 && h.Versions[0] == "3" { h.h3Transport = new(http3.RoundTripper) + if h.TLS != nil { + var err error + h.h3Transport.TLSClientConfig, err = h.TLS.MakeTLSClientConfig(caddyCtx) + if err != nil { + return nil, fmt.Errorf("making TLS client config for HTTP/3 transport: %v", err) + } + } } else if len(h.Versions) > 1 && sliceContains(h.Versions, "3") { return nil, fmt.Errorf("if HTTP/3 is enabled to the upstream, no other HTTP versions are supported") } From 0287009ee5fbe171e7a84f7d5b965992bb5488a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Wed, 3 Jul 2024 16:43:13 +0200 Subject: [PATCH 05/40] intercept: fix http.intercept.header.* placeholder (#6429) --- caddytest/integration/intercept_test.go | 8 +++++++- modules/caddyhttp/intercept/intercept.go | 3 +-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/caddytest/integration/intercept_test.go b/caddytest/integration/intercept_test.go index 81db6a7d..6f8ffc92 100644 --- a/caddytest/integration/intercept_test.go +++ b/caddytest/integration/intercept_test.go @@ -18,17 +18,23 @@ func TestIntercept(t *testing.T) { localhost:9080 { respond /intercept "I'm a teapot" 408 + header /intercept To-Intercept ok respond /no-intercept "I'm not a teapot" intercept { @teapot status 408 handle_response @teapot { + header /intercept intercepted {resp.header.To-Intercept} respond /intercept "I'm a combined coffee/tea pot that is temporarily out of coffee" 503 } } } `, "caddyfile") - tester.AssertGetResponse("http://localhost:9080/intercept", 503, "I'm a combined coffee/tea pot that is temporarily out of coffee") + r, _ := tester.AssertGetResponse("http://localhost:9080/intercept", 503, "I'm a combined coffee/tea pot that is temporarily out of coffee") + if r.Header.Get("intercepted") != "ok" { + t.Fatalf(`header "intercepted" value is not "ok": %s`, r.Header.Get("intercepted")) + } + tester.AssertGetResponse("http://localhost:9080/no-intercept", 200, "I'm not a teapot") } diff --git a/modules/caddyhttp/intercept/intercept.go b/modules/caddyhttp/intercept/intercept.go index 47d7511f..720a0933 100644 --- a/modules/caddyhttp/intercept/intercept.go +++ b/modules/caddyhttp/intercept/intercept.go @@ -50,7 +50,6 @@ type Intercept struct { // // Three new placeholders are available in this handler chain: // - `{http.intercept.status_code}` The status code from the response - // - `{http.intercept.status_text}` The status text from the response // - `{http.intercept.header.*}` The headers from the response HandleResponse []caddyhttp.ResponseHandler `json:"handle_response,omitempty"` @@ -161,7 +160,7 @@ func (ir Intercept) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddy // set up the replacer so that parts of the original response can be // used for routing decisions - for field, value := range r.Header { + for field, value := range rec.Header() { repl.Set("http.intercept.header."+field, strings.Join(value, ",")) } repl.Set("http.intercept.status_code", rec.Status()) From f350e001b6319dd8833fbdb31ffb0ccadb2aa2e0 Mon Sep 17 00:00:00 2001 From: klaxa Date: Wed, 3 Jul 2024 21:05:52 +0200 Subject: [PATCH 06/40] reverseproxy: Only log host is up status on change (fixes #6415) (#6419) --- modules/caddyhttp/reverseproxy/healthchecks.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/caddyhttp/reverseproxy/healthchecks.go b/modules/caddyhttp/reverseproxy/healthchecks.go index 90db9b34..888dadb7 100644 --- a/modules/caddyhttp/reverseproxy/healthchecks.go +++ b/modules/caddyhttp/reverseproxy/healthchecks.go @@ -426,6 +426,7 @@ func (h *Handler) doActiveHealthCheck(dialInfo DialInfo, hostAddr string, upstre } if upstream.Host.activeHealthPasses() >= h.HealthChecks.Active.Passes { if upstream.setHealthy(true) { + h.HealthChecks.Active.logger.Info("host is up", zap.String("host", hostAddr)) h.events.Emit(h.ctx, "healthy", map[string]any{"host": hostAddr}) upstream.Host.resetHealth() } @@ -492,7 +493,6 @@ func (h *Handler) doActiveHealthCheck(dialInfo DialInfo, hostAddr string, upstre } // passed health check parameters, so mark as healthy - h.HealthChecks.Active.logger.Info("host is up", zap.String("host", hostAddr)) markHealthy() return nil From 15d986e1c9decae4d753d7cbec41275264697b2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Thu, 4 Jul 2024 22:57:13 +0200 Subject: [PATCH 07/40] encode: Don't compress already-compressed fonts (#6432) * fix: don't compress already compressed fonts * fix: remove WOFF --- modules/caddyhttp/encode/encode.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/caddyhttp/encode/encode.go b/modules/caddyhttp/encode/encode.go index 908e37b3..cf3d17b6 100644 --- a/modules/caddyhttp/encode/encode.go +++ b/modules/caddyhttp/encode/encode.go @@ -112,7 +112,8 @@ func (enc *Encode) Provision(ctx caddy.Context) error { "application/x-ttf*", "application/xhtml+xml*", "application/xml*", - "font/*", + "font/ttf*", + "font/otf*", "image/svg+xml*", "image/vnd.microsoft.icon*", "image/x-icon*", From c3fb5f4d3fb3eed9136f766cb88f2d8ac54de685 Mon Sep 17 00:00:00 2001 From: Matt Holt Date: Fri, 5 Jul 2024 10:46:20 -0600 Subject: [PATCH 08/40] caddyhttp: Reject 0-RTT early data in IP matchers and set Early-Data header when proxying (#6427) * caddyhttp: Reject 0-RTT early data in IP matchers and set Early-Data header when proxying See RFC 8470: https://httpwg.org/specs/rfc8470.html Thanks to Michael Wedl (@MWedl) at the University of Applied Sciences St. Poelten for reporting this. * Don't return value for {remote} placeholder in early data * Add Caddyfile support --- listeners.go | 6 -- modules/caddyhttp/ip_matchers.go | 6 ++ modules/caddyhttp/matchers.go | 64 +++++++++++++++++++ modules/caddyhttp/replacer.go | 8 +++ .../caddyhttp/reverseproxy/reverseproxy.go | 12 ++++ 5 files changed, 90 insertions(+), 6 deletions(-) diff --git a/listeners.go b/listeners.go index bb0e9b69..fa5ac1f5 100644 --- a/listeners.go +++ b/listeners.go @@ -60,8 +60,6 @@ type NetworkAddress struct { // ListenAll calls Listen() for all addresses represented by this struct, i.e. all ports in the range. // (If the address doesn't use ports or has 1 port only, then only 1 listener will be created.) // It returns an error if any listener failed to bind, and closes any listeners opened up to that point. -// -// TODO: Experimental API: subject to change or removal. func (na NetworkAddress) ListenAll(ctx context.Context, config net.ListenConfig) ([]any, error) { var listeners []any var err error @@ -130,8 +128,6 @@ func (na NetworkAddress) ListenAll(ctx context.Context, config net.ListenConfig) // Unix sockets will be unlinked before being created, to ensure we can bind to // it even if the previous program using it exited uncleanly; it will also be // unlinked upon a graceful exit (or when a new config does not use that socket). -// -// TODO: Experimental API: subject to change or removal. func (na NetworkAddress) Listen(ctx context.Context, portOffset uint, config net.ListenConfig) (any, error) { if na.IsUnixNetwork() { unixSocketsMu.Lock() @@ -221,8 +217,6 @@ func (na NetworkAddress) JoinHostPort(offset uint) string { } // Expand returns one NetworkAddress for each port in the port range. -// -// This is EXPERIMENTAL and subject to change or removal. func (na NetworkAddress) Expand() []NetworkAddress { size := na.PortRangeSize() addrs := make([]NetworkAddress, size) diff --git a/modules/caddyhttp/ip_matchers.go b/modules/caddyhttp/ip_matchers.go index baa7c51c..9101a035 100644 --- a/modules/caddyhttp/ip_matchers.go +++ b/modules/caddyhttp/ip_matchers.go @@ -143,6 +143,9 @@ func (m *MatchRemoteIP) Provision(ctx caddy.Context) error { // Match returns true if r matches m. func (m MatchRemoteIP) Match(r *http.Request) bool { + if r.TLS != nil && !r.TLS.HandshakeComplete { + return false // if handshake is not finished, we infer 0-RTT that has not verified remote IP; could be spoofed + } address := r.RemoteAddr clientIP, zoneID, err := parseIPZoneFromString(address) if err != nil { @@ -228,6 +231,9 @@ func (m *MatchClientIP) Provision(ctx caddy.Context) error { // Match returns true if r matches m. func (m MatchClientIP) Match(r *http.Request) bool { + if r.TLS != nil && !r.TLS.HandshakeComplete { + return false // if handshake is not finished, we infer 0-RTT that has not verified remote IP; could be spoofed + } address := GetVar(r.Context(), ClientIPVarKey).(string) clientIP, zoneID, err := parseIPZoneFromString(address) if err != nil { diff --git a/modules/caddyhttp/matchers.go b/modules/caddyhttp/matchers.go index 392312b6..b7952ab6 100644 --- a/modules/caddyhttp/matchers.go +++ b/modules/caddyhttp/matchers.go @@ -178,6 +178,22 @@ type ( // "http/2", "http/3", or minimum versions: "http/2+", etc. MatchProtocol string + // MatchTLS matches HTTP requests based on the underlying + // TLS connection state. If this matcher is specified but + // the request did not come over TLS, it will never match. + // If this matcher is specified but is empty and the request + // did come in over TLS, it will always match. + MatchTLS struct { + // Matches if the TLS handshake has completed. QUIC 0-RTT early + // data may arrive before the handshake completes. Generally, it + // is unsafe to replay these requests if they are not idempotent; + // additionally, the remote IP of early data packets can more + // easily be spoofed. It is conventional to respond with HTTP 425 + // Too Early if the request cannot risk being processed in this + // state. + HandshakeComplete *bool `json:"handshake_complete,omitempty"` + } + // MatchNot matches requests by negating the results of its matcher // sets. A single "not" matcher takes one or more matcher sets. Each // matcher set is OR'ed; in other words, if any matcher set returns @@ -213,6 +229,7 @@ func init() { caddy.RegisterModule(MatchHeader{}) caddy.RegisterModule(MatchHeaderRE{}) caddy.RegisterModule(new(MatchProtocol)) + caddy.RegisterModule(MatchTLS{}) caddy.RegisterModule(MatchNot{}) } @@ -1236,6 +1253,53 @@ func (MatchProtocol) CELLibrary(_ caddy.Context) (cel.Library, error) { ) } +// CaddyModule returns the Caddy module information. +func (MatchTLS) CaddyModule() caddy.ModuleInfo { + return caddy.ModuleInfo{ + ID: "http.matchers.tls", + New: func() caddy.Module { return new(MatchTLS) }, + } +} + +// Match returns true if r matches m. +func (m MatchTLS) Match(r *http.Request) bool { + if r.TLS == nil { + return false + } + if m.HandshakeComplete != nil { + if (!*m.HandshakeComplete && r.TLS.HandshakeComplete) || + (*m.HandshakeComplete && !r.TLS.HandshakeComplete) { + return false + } + } + return true +} + +// UnmarshalCaddyfile parses Caddyfile tokens for this matcher. Syntax: +// +// ... tls [early_data] +// +// EXPERIMENTAL SYNTAX: Subject to change. +func (m *MatchTLS) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { + // iterate to merge multiple matchers into one + for d.Next() { + if d.NextArg() { + switch d.Val() { + case "early_data": + var false bool + m.HandshakeComplete = &false + } + } + if d.NextArg() { + return d.ArgErr() + } + if d.NextBlock(0) { + return d.Err("malformed tls matcher: blocks are not supported yet") + } + } + return nil +} + // CaddyModule returns the Caddy module information. func (MatchNot) CaddyModule() caddy.ModuleInfo { return caddy.ModuleInfo{ diff --git a/modules/caddyhttp/replacer.go b/modules/caddyhttp/replacer.go index 1cf3ec47..2c0f3235 100644 --- a/modules/caddyhttp/replacer.go +++ b/modules/caddyhttp/replacer.go @@ -142,8 +142,16 @@ func addHTTPVarsToReplacer(repl *caddy.Replacer, req *http.Request, w http.Respo } return port, true case "http.request.remote": + if req.TLS != nil && !req.TLS.HandshakeComplete { + // without a complete handshake (QUIC "early data") we can't trust the remote IP address to not be spoofed + return nil, true + } return req.RemoteAddr, true case "http.request.remote.host": + if req.TLS != nil && !req.TLS.HandshakeComplete { + // without a complete handshake (QUIC "early data") we can't trust the remote IP address to not be spoofed + return nil, true + } host, _, err := net.SplitHostPort(req.RemoteAddr) if err != nil { // req.RemoteAddr is host:port for tcp and udp sockets and /unix/socket.path diff --git a/modules/caddyhttp/reverseproxy/reverseproxy.go b/modules/caddyhttp/reverseproxy/reverseproxy.go index 1a559e5d..4f97edea 100644 --- a/modules/caddyhttp/reverseproxy/reverseproxy.go +++ b/modules/caddyhttp/reverseproxy/reverseproxy.go @@ -605,6 +605,18 @@ func (h Handler) prepareRequest(req *http.Request, repl *caddy.Replacer) (*http. req.Header.Set("User-Agent", "") } + // Indicate if request has been conveyed in early data. + // RFC 8470: "An intermediary that forwards a request prior to the + // completion of the TLS handshake with its client MUST send it with + // the Early-Data header field set to “1” (i.e., it adds it if not + // present in the request). An intermediary MUST use the Early-Data + // header field if the request might have been subject to a replay and + // might already have been forwarded by it or another instance + // (see Section 6.2)." + if req.TLS != nil && !req.TLS.HandshakeComplete { + req.Header.Set("Early-Data", "1") + } + reqUpType := upgradeType(req.Header) removeConnectionHeaders(req.Header) From 7142d7c1e43ba2dad8e0118aa29d77dc74b44dda Mon Sep 17 00:00:00 2001 From: Francis Lavoie Date: Sat, 6 Jul 2024 12:43:19 -0400 Subject: [PATCH 09/40] reverseproxy: Add placeholder for host in active health check headers (#6440) --- modules/caddyhttp/reverseproxy/healthchecks.go | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/caddyhttp/reverseproxy/healthchecks.go b/modules/caddyhttp/reverseproxy/healthchecks.go index 888dadb7..ac92604c 100644 --- a/modules/caddyhttp/reverseproxy/healthchecks.go +++ b/modules/caddyhttp/reverseproxy/healthchecks.go @@ -386,6 +386,7 @@ func (h *Handler) doActiveHealthCheck(dialInfo DialInfo, hostAddr string, upstre // set headers, using a replacer with only globals (env vars, system info, etc.) repl := caddy.NewReplacer() + repl.Set("http.reverse_proxy.active.target_host", hostAddr) for key, vals := range h.HealthChecks.Active.Headers { key = repl.ReplaceAll(key, "") if key == "Host" { From 4ef360745dab1023a7d4c04aebca3d05499dd5e1 Mon Sep 17 00:00:00 2001 From: Steffen Busch <37350514+steffenbusch@users.noreply.github.com> Date: Sat, 6 Jul 2024 18:46:08 +0200 Subject: [PATCH 10/40] browse: add Content-Security-Policy w/ nonce (#6425) * browse: add Content-Security-Policy w/ nonce * Add backward-compat values to script-src * Remove dummy "#" href from layout anchors --- modules/caddyhttp/fileserver/browse.html | 73 ++++++++++++++++-------- 1 file changed, 49 insertions(+), 24 deletions(-) diff --git a/modules/caddyhttp/fileserver/browse.html b/modules/caddyhttp/fileserver/browse.html index 7b0df1e5..43d5f451 100644 --- a/modules/caddyhttp/fileserver/browse.html +++ b/modules/caddyhttp/fileserver/browse.html @@ -1,10 +1,17 @@ +{{ $nonce := uuidv4 -}} +{{ $nonceAttribute := print "nonce=" (quote $nonce) -}} +{{ $csp := printf "default-src 'none'; img-src 'self'; object-src 'none'; base-uri 'none'; script-src 'strict-dynamic' 'nonce-%s' 'unsafe-inline' https: http:; style-src 'strict-dynamic' 'nonce-%s'; frame-ancestors 'self'; form-action 'self'; block-all-mixed-content;" $nonce $nonce -}} +{{/* To disable the Content-Security-Policy, set this to false */}}{{ $enableCsp := true -}} +{{ if $enableCsp -}} + {{- .RespHeader.Set "Content-Security-Policy" $csp -}} +{{ end -}} {{- define "icon"}} {{- if .IsDir}} {{- if .IsSymlink}} - + {{- else}} @@ -303,7 +310,7 @@ - {{- if eq .Layout "grid"}} - + {{- end}} - +
@@ -799,7 +810,7 @@ footer { {{- end}}
- + @@ -807,7 +818,7 @@ footer { List - + @@ -886,7 +897,7 @@ footer { - + @@ -1000,70 +1011,70 @@ footer { -