mirror of
https://github.com/cloudflare/cloudflared.git
synced 2025-07-27 19:59:58 +00:00
TUN-6380: Enforce connect and keep-alive timeouts for TCP connections in both WARP routing and websocket based TCP proxy.
For WARP routing the defaults for these new settings are 5 seconds for connect timeout and 30 seconds for keep-alive timeout. These values can be configured either remotely or locally. Local config lives under "warp-routing" section in config.yaml. For websocket-based proxy, the defaults come from originConfig settings (either global or per-service) and use the same defaults as HTTP proxying.
This commit is contained in:
@@ -12,10 +12,11 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
defaultConnectTimeout = config.CustomDuration{Duration: 30 * time.Second}
|
||||
defaultTLSTimeout = config.CustomDuration{Duration: 10 * time.Second}
|
||||
defaultTCPKeepAlive = config.CustomDuration{Duration: 30 * time.Second}
|
||||
defaultKeepAliveTimeout = config.CustomDuration{Duration: 90 * time.Second}
|
||||
defaultHTTPConnectTimeout = config.CustomDuration{Duration: 30 * time.Second}
|
||||
defaultWarpRoutingConnectTimeout = config.CustomDuration{Duration: 5 * time.Second}
|
||||
defaultTLSTimeout = config.CustomDuration{Duration: 10 * time.Second}
|
||||
defaultTCPKeepAlive = config.CustomDuration{Duration: 30 * time.Second}
|
||||
defaultKeepAliveTimeout = config.CustomDuration{Duration: 90 * time.Second}
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -41,10 +42,44 @@ const (
|
||||
socksProxy = "socks"
|
||||
)
|
||||
|
||||
type WarpRoutingConfig struct {
|
||||
Enabled bool `yaml:"enabled" json:"enabled"`
|
||||
ConnectTimeout config.CustomDuration `yaml:"connectTimeout" json:"connectTimeout,omitempty"`
|
||||
TCPKeepAlive config.CustomDuration `yaml:"tcpKeepAlive" json:"tcpKeepAlive,omitempty"`
|
||||
}
|
||||
|
||||
func NewWarpRoutingConfig(raw *config.WarpRoutingConfig) WarpRoutingConfig {
|
||||
cfg := WarpRoutingConfig{
|
||||
Enabled: raw.Enabled,
|
||||
ConnectTimeout: defaultWarpRoutingConnectTimeout,
|
||||
TCPKeepAlive: defaultTCPKeepAlive,
|
||||
}
|
||||
if raw.ConnectTimeout != nil {
|
||||
cfg.ConnectTimeout = *raw.ConnectTimeout
|
||||
}
|
||||
if raw.TCPKeepAlive != nil {
|
||||
cfg.TCPKeepAlive = *raw.TCPKeepAlive
|
||||
}
|
||||
return cfg
|
||||
}
|
||||
|
||||
func (c *WarpRoutingConfig) RawConfig() config.WarpRoutingConfig {
|
||||
raw := config.WarpRoutingConfig{
|
||||
Enabled: c.Enabled,
|
||||
}
|
||||
if c.ConnectTimeout.Duration != defaultWarpRoutingConnectTimeout.Duration {
|
||||
raw.ConnectTimeout = &c.ConnectTimeout
|
||||
}
|
||||
if c.TCPKeepAlive.Duration != defaultTCPKeepAlive.Duration {
|
||||
raw.TCPKeepAlive = &c.TCPKeepAlive
|
||||
}
|
||||
return raw
|
||||
}
|
||||
|
||||
// RemoteConfig models ingress settings that can be managed remotely, for example through the dashboard.
|
||||
type RemoteConfig struct {
|
||||
Ingress Ingress
|
||||
WarpRouting config.WarpRoutingConfig
|
||||
WarpRouting WarpRoutingConfig
|
||||
}
|
||||
|
||||
type RemoteConfigJSON struct {
|
||||
@@ -72,18 +107,18 @@ func (rc *RemoteConfig) UnmarshalJSON(b []byte) error {
|
||||
}
|
||||
|
||||
rc.Ingress = ingress
|
||||
rc.WarpRouting = rawConfig.WarpRouting
|
||||
rc.WarpRouting = NewWarpRoutingConfig(&rawConfig.WarpRouting)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func originRequestFromSingeRule(c *cli.Context) OriginRequestConfig {
|
||||
var connectTimeout config.CustomDuration = defaultConnectTimeout
|
||||
var tlsTimeout config.CustomDuration = defaultTLSTimeout
|
||||
var tcpKeepAlive config.CustomDuration = defaultTCPKeepAlive
|
||||
var connectTimeout = defaultHTTPConnectTimeout
|
||||
var tlsTimeout = defaultTLSTimeout
|
||||
var tcpKeepAlive = defaultTCPKeepAlive
|
||||
var noHappyEyeballs bool
|
||||
var keepAliveConnections int = defaultKeepAliveConnections
|
||||
var keepAliveTimeout config.CustomDuration = defaultKeepAliveTimeout
|
||||
var keepAliveConnections = defaultKeepAliveConnections
|
||||
var keepAliveTimeout = defaultKeepAliveTimeout
|
||||
var httpHostHeader string
|
||||
var originServerName string
|
||||
var caPool string
|
||||
@@ -160,7 +195,7 @@ func originRequestFromSingeRule(c *cli.Context) OriginRequestConfig {
|
||||
|
||||
func originRequestFromConfig(c config.OriginRequestConfig) OriginRequestConfig {
|
||||
out := OriginRequestConfig{
|
||||
ConnectTimeout: defaultConnectTimeout,
|
||||
ConnectTimeout: defaultHTTPConnectTimeout,
|
||||
TLSTimeout: defaultTLSTimeout,
|
||||
TCPKeepAlive: defaultTCPKeepAlive,
|
||||
KeepAliveConnections: defaultKeepAliveConnections,
|
||||
@@ -404,7 +439,7 @@ func ConvertToRawOriginConfig(c OriginRequestConfig) config.OriginRequestConfig
|
||||
var keepAliveTimeout *config.CustomDuration
|
||||
var proxyAddress *string
|
||||
|
||||
if c.ConnectTimeout != defaultConnectTimeout {
|
||||
if c.ConnectTimeout != defaultHTTPConnectTimeout {
|
||||
connectTimeout = &c.ConnectTimeout
|
||||
}
|
||||
if c.TLSTimeout != defaultTLSTimeout {
|
||||
|
@@ -274,7 +274,7 @@ func TestOriginRequestConfigDefaults(t *testing.T) {
|
||||
// Rule 0 didn't override anything, so it inherits the cloudflared defaults
|
||||
actual0 := ing.Rules[0].Config
|
||||
expected0 := OriginRequestConfig{
|
||||
ConnectTimeout: defaultConnectTimeout,
|
||||
ConnectTimeout: defaultHTTPConnectTimeout,
|
||||
TLSTimeout: defaultTLSTimeout,
|
||||
TCPKeepAlive: defaultTCPKeepAlive,
|
||||
KeepAliveConnections: defaultKeepAliveConnections,
|
||||
@@ -404,7 +404,7 @@ func TestDefaultConfigFromCLI(t *testing.T) {
|
||||
c := cli.NewContext(nil, set, nil)
|
||||
|
||||
expected := OriginRequestConfig{
|
||||
ConnectTimeout: defaultConnectTimeout,
|
||||
ConnectTimeout: defaultHTTPConnectTimeout,
|
||||
TLSTimeout: defaultTLSTimeout,
|
||||
TCPKeepAlive: defaultTCPKeepAlive,
|
||||
KeepAliveConnections: defaultKeepAliveConnections,
|
||||
|
@@ -97,8 +97,16 @@ type WarpRoutingService struct {
|
||||
Proxy StreamBasedOriginProxy
|
||||
}
|
||||
|
||||
func NewWarpRoutingService() *WarpRoutingService {
|
||||
return &WarpRoutingService{Proxy: &rawTCPService{name: ServiceWarpRouting}}
|
||||
func NewWarpRoutingService(config WarpRoutingConfig) *WarpRoutingService {
|
||||
svc := &rawTCPService{
|
||||
name: ServiceWarpRouting,
|
||||
dialer: net.Dialer{
|
||||
Timeout: config.ConnectTimeout.Duration,
|
||||
KeepAlive: config.TCPKeepAlive.Duration,
|
||||
},
|
||||
}
|
||||
|
||||
return &WarpRoutingService{Proxy: svc}
|
||||
}
|
||||
|
||||
// Get a single origin service from the CLI/config.
|
||||
|
@@ -1,26 +1,20 @@
|
||||
package ingress
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
errUnsupportedConnectionType = errors.New("internal error: unsupported connection type")
|
||||
)
|
||||
|
||||
// HTTPOriginProxy can be implemented by origin services that want to proxy http requests.
|
||||
type HTTPOriginProxy interface {
|
||||
// RoundTrip is how cloudflared proxies eyeball requests to the actual origin services
|
||||
// RoundTripper is how cloudflared proxies eyeball requests to the actual origin services
|
||||
http.RoundTripper
|
||||
}
|
||||
|
||||
// StreamBasedOriginProxy can be implemented by origin services that want to proxy ws/TCP.
|
||||
type StreamBasedOriginProxy interface {
|
||||
EstablishConnection(dest string) (OriginConnection, error)
|
||||
EstablishConnection(ctx context.Context, dest string) (OriginConnection, error)
|
||||
}
|
||||
|
||||
func (o *unixSocketPath) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
@@ -59,8 +53,8 @@ func (o *statusCode) RoundTrip(_ *http.Request) (*http.Response, error) {
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (o *rawTCPService) EstablishConnection(dest string) (OriginConnection, error) {
|
||||
conn, err := net.Dial("tcp", dest)
|
||||
func (o *rawTCPService) EstablishConnection(ctx context.Context, dest string) (OriginConnection, error) {
|
||||
conn, err := o.dialer.DialContext(ctx, "tcp", dest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -71,13 +65,13 @@ func (o *rawTCPService) EstablishConnection(dest string) (OriginConnection, erro
|
||||
return originConn, nil
|
||||
}
|
||||
|
||||
func (o *tcpOverWSService) EstablishConnection(dest string) (OriginConnection, error) {
|
||||
func (o *tcpOverWSService) EstablishConnection(ctx context.Context, dest string) (OriginConnection, error) {
|
||||
var err error
|
||||
if !o.isBastion {
|
||||
dest = o.dest
|
||||
}
|
||||
|
||||
conn, err := net.Dial("tcp", dest)
|
||||
conn, err := o.dialer.DialContext(ctx, "tcp", dest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -89,6 +83,6 @@ func (o *tcpOverWSService) EstablishConnection(dest string) (OriginConnection, e
|
||||
|
||||
}
|
||||
|
||||
func (o *socksProxyOverWSService) EstablishConnection(dest string) (OriginConnection, error) {
|
||||
func (o *socksProxyOverWSService) EstablishConnection(_ctx context.Context, _dest string) (OriginConnection, error) {
|
||||
return o.conn, nil
|
||||
}
|
||||
|
@@ -36,7 +36,7 @@ func TestRawTCPServiceEstablishConnection(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
// Origin not listening for new connection, should return an error
|
||||
_, err = rawTCPService.EstablishConnection(req.URL.String())
|
||||
_, err = rawTCPService.EstablishConnection(context.Background(), req.URL.String())
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ func TestTCPOverWSServiceEstablishConnection(t *testing.T) {
|
||||
t.Run(test.testCase, func(t *testing.T) {
|
||||
if test.expectErr {
|
||||
bastionHost, _ := carrier.ResolveBastionDest(test.req)
|
||||
_, err := test.service.EstablishConnection(bastionHost)
|
||||
_, err := test.service.EstablishConnection(context.Background(), bastionHost)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
})
|
||||
@@ -99,7 +99,7 @@ func TestTCPOverWSServiceEstablishConnection(t *testing.T) {
|
||||
for _, service := range []*tcpOverWSService{newTCPOverWSService(originURL), newBastionService()} {
|
||||
// Origin not listening for new connection, should return an error
|
||||
bastionHost, _ := carrier.ResolveBastionDest(bastionReq)
|
||||
_, err := service.EstablishConnection(bastionHost)
|
||||
_, err := service.EstablishConnection(context.Background(), bastionHost)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
}
|
||||
|
@@ -91,7 +91,8 @@ func (o httpService) MarshalJSON() ([]byte, error) {
|
||||
// rawTCPService dials TCP to the destination specified by the client
|
||||
// It's used by warp routing
|
||||
type rawTCPService struct {
|
||||
name string
|
||||
name string
|
||||
dialer net.Dialer
|
||||
}
|
||||
|
||||
func (o *rawTCPService) String() string {
|
||||
@@ -113,6 +114,7 @@ type tcpOverWSService struct {
|
||||
dest string
|
||||
isBastion bool
|
||||
streamHandler streamHandlerFunc
|
||||
dialer net.Dialer
|
||||
}
|
||||
|
||||
type socksProxyOverWSService struct {
|
||||
@@ -176,6 +178,8 @@ func (o *tcpOverWSService) start(log *zerolog.Logger, _ <-chan struct{}, cfg Ori
|
||||
} else {
|
||||
o.streamHandler = DefaultStreamHandler
|
||||
}
|
||||
o.dialer.Timeout = cfg.ConnectTimeout.Duration
|
||||
o.dialer.KeepAlive = cfg.TCPKeepAlive.Duration
|
||||
return nil
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user