diff --git a/doc/reference/filters.md b/doc/reference/filters.md index 77cd09c3f9..6ed9562ea2 100644 --- a/doc/reference/filters.md +++ b/doc/reference/filters.md @@ -132,6 +132,7 @@ name: proxy-example-1 pools: - servers: - url: http://127.0.0.1:9095 +maxRedirection: 10 ``` Pool without `filter` is considered the main pool, other pools with `filter` @@ -159,6 +160,7 @@ pools: policy: random - servers: - url: http://127.0.0.1:9097 +maxRedirection: 10 ``` Servers of a pool can also be dynamically configured via service discovery, @@ -172,6 +174,7 @@ pools: - serverTags: ["v2"] serviceName: service-001 serviceRegistry: eureka-service-registry-example +maxRedirection: 10 ``` When there are multiple servers in a pool, the Proxy can do a load balance @@ -186,6 +189,7 @@ pools: serviceRegistry: eureka-service-registry-example loadBalance: policy: roundRobin +maxRedirection: 10 ``` ### Configuration @@ -198,6 +202,7 @@ pools: | maxIdleConns | int | Controls the maximum number of idle (keep-alive) connections across all hosts. Default is 10240 | No | | maxIdleConnsPerHost | int | Controls the maximum idle (keep-alive) connections to keep per-host. Default is 1024 | No | | serverMaxBodySize | int64 | Max size of response body. the default value is 4MB. Responses with a body larger than this option are discarded. When this option is set to `-1`, Easegress takes the response body as a stream and the body can be any size, but some features are not possible in this case, please refer [Stream](./stream.md) for more information. | No | +| maxRedirection | int | The maxRedirection parameter determines the maximum number of redirections allowed by the HTTP client for each request. A default value of zero means that redirection is not allowed, while a number greater than zero specifies the maximum allowed number of redirections. | No | ### Results @@ -1596,6 +1601,7 @@ The relationship between `methods` and `url` is `AND`. | certBase64 | string | Base64 encoded certificate | Yes | | keyBase64 | string | Base64 encoded key | Yes | | rootCertBase64 | string | Base64 encoded root certificate | Yes | +| insecureSkipVerify| bool | insecureSkipVerify controls whether a client verifies the server's certificate chain and host name. If insecureSkipVerify is true, crypto/tls accepts any certificate presented by the server and any host name in that certificate. In this mode, TLS is susceptible to machine-in-the-middle attacks unless custom verification is used. This should be used only for testing or in combination with VerifyConnection or VerifyPeerCertificate. | No | ### websocketproxy.WebSocketServerPoolSpec diff --git a/pkg/filters/proxies/httpproxy/proxy.go b/pkg/filters/proxies/httpproxy/proxy.go index 9501e37117..e67cb19eba 100644 --- a/pkg/filters/proxies/httpproxy/proxy.go +++ b/pkg/filters/proxies/httpproxy/proxy.go @@ -112,6 +112,7 @@ type ( MTLS *MTLS `json:"mtls,omitempty" jsonschema:"omitempty"` MaxIdleConns int `json:"maxIdleConns" jsonschema:"omitempty"` MaxIdleConnsPerHost int `json:"maxIdleConnsPerHost" jsonschema:"omitempty"` + MaxRedirection int `json:"maxRedirection" jsonschema:"omitempty"` ServerMaxBodySize int64 `json:"serverMaxBodySize" jsonschema:"omitempty"` } @@ -124,9 +125,17 @@ type ( // MTLS is the configuration for client side mTLS. MTLS struct { - CertBase64 string `json:"certBase64" jsonschema:"required,format=base64"` - KeyBase64 string `json:"keyBase64" jsonschema:"required,format=base64"` - RootCertBase64 string `json:"rootCertBase64" jsonschema:"required,format=base64"` + CertBase64 string `json:"certBase64" jsonschema:"required,format=base64"` + KeyBase64 string `json:"keyBase64" jsonschema:"required,format=base64"` + RootCertBase64 string `json:"rootCertBase64" jsonschema:"required,format=base64"` + InsecureSkipVerify bool `json:"insecureSkipVerify" jsonschema:"omitempty"` + } + + // HTTPClientSpec is the spec of HTTPClient. + HTTPClientSpec struct { + MaxIdleConns int + MaxIdleConnsPerHost int + MaxRedirection *int } // Server is the backend server. @@ -171,7 +180,7 @@ func (s *Spec) Validate() error { return nil } -func HTTPClient(tlsCfg *tls.Config, maxIdleConns, maxIdleConnsPerHost int, timeout time.Duration) *http.Client { +func HTTPClient(tlsCfg *tls.Config, spec *HTTPClientSpec, timeout time.Duration) *http.Client { dialFunc := func(ctx stdctx.Context, network, addr string) (net.Conn, error) { return (&net.Dialer{ @@ -180,7 +189,7 @@ func HTTPClient(tlsCfg *tls.Config, maxIdleConns, maxIdleConnsPerHost int, timeo }).DialContext(ctx, network, addr) } - return &http.Client{ + client := &http.Client{ // NOTE: Timeout could be no limit, real client or server could cancel it. Timeout: timeout, Transport: &http.Transport{ @@ -190,16 +199,25 @@ func HTTPClient(tlsCfg *tls.Config, maxIdleConns, maxIdleConnsPerHost int, timeo DisableCompression: false, // NOTE: The large number of Idle Connections can // reduce overhead of building connections. - MaxIdleConns: maxIdleConns, - MaxIdleConnsPerHost: maxIdleConnsPerHost, + MaxIdleConns: spec.MaxIdleConns, + MaxIdleConnsPerHost: spec.MaxIdleConnsPerHost, IdleConnTimeout: 90 * time.Second, TLSHandshakeTimeout: 10 * time.Second, ExpectContinueTimeout: 1 * time.Second, }, - CheckRedirect: func(req *http.Request, via []*http.Request) error { - return http.ErrUseLastResponse - }, } + if spec.MaxRedirection != nil { + client.CheckRedirect = func(req *http.Request, via []*http.Request) error { + if *spec.MaxRedirection <= 0 { + return http.ErrUseLastResponse + } + if len(via) >= *spec.MaxRedirection { + return fmt.Errorf("stopped after %d redirects", *spec.MaxRedirection) + } + return nil + } + } + return client } // Name returns the name of the Proxy filter instance. @@ -247,8 +265,9 @@ func (p *Proxy) tlsConfig() (*tls.Config, error) { caCertPool.AppendCertsFromPEM(rootCertPem) return &tls.Config{ - Certificates: []tls.Certificate{cert}, - RootCAs: caCertPool, + Certificates: []tls.Certificate{cert}, + RootCAs: caCertPool, + InsecureSkipVerify: mtls.InsecureSkipVerify, }, nil } @@ -281,7 +300,12 @@ func (p *Proxy) reload() { } tlsCfg, _ := p.tlsConfig() - p.client = HTTPClient(tlsCfg, p.spec.MaxIdleConns, p.spec.MaxIdleConnsPerHost, 0) + clientSpec := &HTTPClientSpec{ + MaxIdleConns: p.spec.MaxIdleConns, + MaxIdleConnsPerHost: p.spec.MaxIdleConnsPerHost, + MaxRedirection: &p.spec.MaxRedirection, + } + p.client = HTTPClient(tlsCfg, clientSpec, 0) } // Status returns Proxy status. diff --git a/pkg/filters/proxies/httpproxy/simplehttpproxy.go b/pkg/filters/proxies/httpproxy/simplehttpproxy.go index e4e483602a..4641931d25 100644 --- a/pkg/filters/proxies/httpproxy/simplehttpproxy.go +++ b/pkg/filters/proxies/httpproxy/simplehttpproxy.go @@ -146,7 +146,11 @@ func (shp *SimpleHTTPProxy) reload() { shp.compression = newCompression(shp.spec.Compression) } // create http.Client - shp.client = HTTPClient(nil, shp.spec.MaxIdleConns, shp.spec.MaxIdleConnsPerHost, shp.timeout) + clientSpec := &HTTPClientSpec{ + MaxIdleConns: shp.spec.MaxIdleConns, + MaxIdleConnsPerHost: shp.spec.MaxIdleConnsPerHost, + } + shp.client = HTTPClient(nil, clientSpec, shp.timeout) } // Status returns SimpleHTTPProxy status. diff --git a/pkg/filters/proxies/httpproxy/simplehttpproxy_test.go b/pkg/filters/proxies/httpproxy/simplehttpproxy_test.go index 7107c22928..6a3d90882f 100644 --- a/pkg/filters/proxies/httpproxy/simplehttpproxy_test.go +++ b/pkg/filters/proxies/httpproxy/simplehttpproxy_test.go @@ -92,7 +92,7 @@ compression: ctx = getCtx(stdr) assert.Equal("", proxy.Handle(ctx)) fmt.Println(ctx.GetOutputResponse().(*httpprot.Response).Status) - bodyBytes, err = io.ReadAll(ctx.GetOutputResponse().(*httpprot.Response).Body) + _, err = io.ReadAll(ctx.GetOutputResponse().(*httpprot.Response).Body) // assert headers contains compression header := ctx.GetOutputResponse().(*httpprot.Response).Header() encoding := header.Get("Content-Encoding") @@ -113,7 +113,7 @@ maxBodySize: 1024 ctx = getCtx(stdr) assert.Equal("", proxy.Handle(ctx)) fmt.Println(ctx.GetOutputResponse().(*httpprot.Response).Status) - bodyBytes, err = io.ReadAll(ctx.GetOutputResponse().(*httpprot.Response).Body) + _, err = io.ReadAll(ctx.GetOutputResponse().(*httpprot.Response).Body) if err != nil { fmt.Println(err) assert.Fail("read body error") diff --git a/pkg/filters/proxies/httpproxy/wspool.go b/pkg/filters/proxies/httpproxy/wspool.go index edfe30b421..5fffa26c4a 100644 --- a/pkg/filters/proxies/httpproxy/wspool.go +++ b/pkg/filters/proxies/httpproxy/wspool.go @@ -97,7 +97,6 @@ func buildServerURL(svr *Server, req *httpprot.Request) (string, error) { switch u1.Scheme { case "ws", "wss": u.Scheme = u1.Scheme - break case "http": u.Scheme = "ws" case "https":