Configurable max request header size
This commit is contained in:
parent
ac1dad3d14
commit
9750bbc353
8 changed files with 108 additions and 5 deletions
|
@ -141,6 +141,9 @@ HTTP configuration.
|
||||||
`--entrypoints.<name>.http.encodequerysemicolons`:
|
`--entrypoints.<name>.http.encodequerysemicolons`:
|
||||||
Defines whether request query semicolons should be URLEncoded. (Default: ```false```)
|
Defines whether request query semicolons should be URLEncoded. (Default: ```false```)
|
||||||
|
|
||||||
|
`--entrypoints.<name>.http.maxheaderbytes`:
|
||||||
|
Maximum size of request headers in bytes. (Default: ```1048576```)
|
||||||
|
|
||||||
`--entrypoints.<name>.http.middlewares`:
|
`--entrypoints.<name>.http.middlewares`:
|
||||||
Default middlewares for the routers linked to the entry point.
|
Default middlewares for the routers linked to the entry point.
|
||||||
|
|
||||||
|
|
|
@ -150,6 +150,9 @@ UDP port to advertise, on which HTTP/3 is available. (Default: ```0```)
|
||||||
`TRAEFIK_ENTRYPOINTS_<NAME>_HTTP_ENCODEQUERYSEMICOLONS`:
|
`TRAEFIK_ENTRYPOINTS_<NAME>_HTTP_ENCODEQUERYSEMICOLONS`:
|
||||||
Defines whether request query semicolons should be URLEncoded. (Default: ```false```)
|
Defines whether request query semicolons should be URLEncoded. (Default: ```false```)
|
||||||
|
|
||||||
|
`TRAEFIK_ENTRYPOINTS_<NAME>_HTTP_MAXHEADERBYTES`:
|
||||||
|
Maximum size of request headers in bytes. (Default: ```1048576```)
|
||||||
|
|
||||||
`TRAEFIK_ENTRYPOINTS_<NAME>_HTTP_MIDDLEWARES`:
|
`TRAEFIK_ENTRYPOINTS_<NAME>_HTTP_MIDDLEWARES`:
|
||||||
Default middlewares for the routers linked to the entry point.
|
Default middlewares for the routers linked to the entry point.
|
||||||
|
|
||||||
|
|
|
@ -51,6 +51,7 @@
|
||||||
[entryPoints.EntryPoint0.http]
|
[entryPoints.EntryPoint0.http]
|
||||||
middlewares = ["foobar", "foobar"]
|
middlewares = ["foobar", "foobar"]
|
||||||
encodeQuerySemicolons = true
|
encodeQuerySemicolons = true
|
||||||
|
maxHeaderBytes = 42
|
||||||
[entryPoints.EntryPoint0.http.redirections]
|
[entryPoints.EntryPoint0.http.redirections]
|
||||||
[entryPoints.EntryPoint0.http.redirections.entryPoint]
|
[entryPoints.EntryPoint0.http.redirections.entryPoint]
|
||||||
to = "foobar"
|
to = "foobar"
|
||||||
|
|
|
@ -80,6 +80,7 @@ entryPoints:
|
||||||
- foobar
|
- foobar
|
||||||
- foobar
|
- foobar
|
||||||
encodeQuerySemicolons: true
|
encodeQuerySemicolons: true
|
||||||
|
maxHeaderBytes: 42
|
||||||
http2:
|
http2:
|
||||||
maxConcurrentStreams: 42
|
maxConcurrentStreams: 42
|
||||||
http3:
|
http3:
|
||||||
|
|
25
integration/fixtures/simple_max_header_size.toml
Normal file
25
integration/fixtures/simple_max_header_size.toml
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
[global]
|
||||||
|
checkNewVersion = false
|
||||||
|
sendAnonymousUsage = false
|
||||||
|
|
||||||
|
[entryPoints]
|
||||||
|
[entryPoints.web]
|
||||||
|
address = ":8000"
|
||||||
|
[entryPoints.web.http]
|
||||||
|
maxHeaderBytes = 1310720
|
||||||
|
|
||||||
|
[providers.file]
|
||||||
|
filename = "{{ .SelfFilename }}"
|
||||||
|
|
||||||
|
## dynamic configuration ##
|
||||||
|
|
||||||
|
[http.routers]
|
||||||
|
[http.routers.test-router]
|
||||||
|
entryPoints = ["web"]
|
||||||
|
service = "test-service"
|
||||||
|
rule = "Host(`127.0.0.1`)"
|
||||||
|
|
||||||
|
[http.services]
|
||||||
|
[http.services.test-service]
|
||||||
|
[[http.services.test-service.loadBalancer.servers]]
|
||||||
|
url = "{{ .TestServer }}"
|
|
@ -1511,3 +1511,63 @@ func (s *SimpleSuite) TestDenyFragment() {
|
||||||
require.NoError(s.T(), err)
|
require.NoError(s.T(), err)
|
||||||
assert.Equal(s.T(), http.StatusBadRequest, resp.StatusCode)
|
assert.Equal(s.T(), http.StatusBadRequest, resp.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *SimpleSuite) TestMaxHeaderBytes() {
|
||||||
|
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
})
|
||||||
|
|
||||||
|
listener, err := net.Listen("tcp", "127.0.0.1:9000")
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
ts := &httptest.Server{
|
||||||
|
Listener: listener,
|
||||||
|
Config: &http.Server{
|
||||||
|
Handler: handler,
|
||||||
|
MaxHeaderBytes: 1.25 * 1024 * 1024, // 1.25 MB
|
||||||
|
},
|
||||||
|
}
|
||||||
|
ts.Start()
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
// The test server and traefik config file both specify a max request header size of 1.25 MB.
|
||||||
|
file := s.adaptFile("fixtures/simple_max_header_size.toml", struct {
|
||||||
|
TestServer string
|
||||||
|
}{ts.URL})
|
||||||
|
|
||||||
|
s.traefikCmd(withConfigFile(file))
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
headerSize int
|
||||||
|
expectedStatus int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "1.25MB header",
|
||||||
|
headerSize: int(1.25 * 1024 * 1024),
|
||||||
|
expectedStatus: http.StatusOK,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "1.5MB header",
|
||||||
|
headerSize: int(1.5 * 1024 * 1024),
|
||||||
|
expectedStatus: http.StatusRequestHeaderFieldsTooLarge,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "500KB header",
|
||||||
|
headerSize: int(500 * 1024),
|
||||||
|
expectedStatus: http.StatusOK,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range testCases {
|
||||||
|
s.Run(test.name, func() {
|
||||||
|
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000", nil)
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
|
||||||
|
req.Header.Set("X-Large-Header", strings.Repeat("A", test.headerSize))
|
||||||
|
|
||||||
|
err = try.Request(req, 2*time.Second, try.StatusCodeIs(test.expectedStatus))
|
||||||
|
require.NoError(s.T(), err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package static
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
ptypes "github.com/traefik/paerser/types"
|
ptypes "github.com/traefik/paerser/types"
|
||||||
|
@ -53,6 +54,8 @@ func (ep *EntryPoint) SetDefaults() {
|
||||||
ep.ForwardedHeaders = &ForwardedHeaders{}
|
ep.ForwardedHeaders = &ForwardedHeaders{}
|
||||||
ep.UDP = &UDPConfig{}
|
ep.UDP = &UDPConfig{}
|
||||||
ep.UDP.SetDefaults()
|
ep.UDP.SetDefaults()
|
||||||
|
ep.HTTP = HTTPConfig{}
|
||||||
|
ep.HTTP.SetDefaults()
|
||||||
ep.HTTP2 = &HTTP2Config{}
|
ep.HTTP2 = &HTTP2Config{}
|
||||||
ep.HTTP2.SetDefaults()
|
ep.HTTP2.SetDefaults()
|
||||||
}
|
}
|
||||||
|
@ -63,6 +66,12 @@ type HTTPConfig struct {
|
||||||
Middlewares []string `description:"Default middlewares for the routers linked to the entry point." json:"middlewares,omitempty" toml:"middlewares,omitempty" yaml:"middlewares,omitempty" export:"true"`
|
Middlewares []string `description:"Default middlewares for the routers linked to the entry point." json:"middlewares,omitempty" toml:"middlewares,omitempty" yaml:"middlewares,omitempty" export:"true"`
|
||||||
TLS *TLSConfig `description:"Default TLS configuration for the routers linked to the entry point." json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
TLS *TLSConfig `description:"Default TLS configuration for the routers linked to the entry point." json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
||||||
EncodeQuerySemicolons bool `description:"Defines whether request query semicolons should be URLEncoded." json:"encodeQuerySemicolons,omitempty" toml:"encodeQuerySemicolons,omitempty" yaml:"encodeQuerySemicolons,omitempty"`
|
EncodeQuerySemicolons bool `description:"Defines whether request query semicolons should be URLEncoded." json:"encodeQuerySemicolons,omitempty" toml:"encodeQuerySemicolons,omitempty" yaml:"encodeQuerySemicolons,omitempty"`
|
||||||
|
MaxHeaderBytes int `description:"Maximum size of request headers in bytes." json:"maxHeaderBytes,omitempty" toml:"maxHeaderBytes,omitempty" yaml:"maxHeaderBytes,omitempty" export:"true"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDefaults sets the default values.
|
||||||
|
func (c *HTTPConfig) SetDefaults() {
|
||||||
|
c.MaxHeaderBytes = http.DefaultMaxHeaderBytes
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTTP2Config is the HTTP2 configuration of an entry point.
|
// HTTP2Config is the HTTP2 configuration of an entry point.
|
||||||
|
|
|
@ -633,11 +633,12 @@ func createHTTPServer(ctx context.Context, ln net.Listener, configuration *stati
|
||||||
}
|
}
|
||||||
|
|
||||||
serverHTTP := &http.Server{
|
serverHTTP := &http.Server{
|
||||||
Handler: handler,
|
Handler: handler,
|
||||||
ErrorLog: stdlog.New(logs.NoLevel(log.Logger, zerolog.DebugLevel), "", 0),
|
ErrorLog: stdlog.New(logs.NoLevel(log.Logger, zerolog.DebugLevel), "", 0),
|
||||||
ReadTimeout: time.Duration(configuration.Transport.RespondingTimeouts.ReadTimeout),
|
ReadTimeout: time.Duration(configuration.Transport.RespondingTimeouts.ReadTimeout),
|
||||||
WriteTimeout: time.Duration(configuration.Transport.RespondingTimeouts.WriteTimeout),
|
WriteTimeout: time.Duration(configuration.Transport.RespondingTimeouts.WriteTimeout),
|
||||||
IdleTimeout: time.Duration(configuration.Transport.RespondingTimeouts.IdleTimeout),
|
IdleTimeout: time.Duration(configuration.Transport.RespondingTimeouts.IdleTimeout),
|
||||||
|
MaxHeaderBytes: configuration.HTTP.MaxHeaderBytes,
|
||||||
}
|
}
|
||||||
if debugConnection || (configuration.Transport != nil && (configuration.Transport.KeepAliveMaxTime > 0 || configuration.Transport.KeepAliveMaxRequests > 0)) {
|
if debugConnection || (configuration.Transport != nil && (configuration.Transport.KeepAliveMaxTime > 0 || configuration.Transport.KeepAliveMaxRequests > 0)) {
|
||||||
serverHTTP.ConnContext = func(ctx context.Context, c net.Conn) context.Context {
|
serverHTTP.ConnContext = func(ctx context.Context, c net.Conn) context.Context {
|
||||||
|
|
Loading…
Reference in a new issue