mirror of
https://github.com/cloudflare/cloudflared.git
synced 2025-07-27 12:49:58 +00:00
TUN-9470: Add OriginDialerService to include TCP
Adds an OriginDialerService that takes over the roles of both DialUDP and DialTCP towards the origin. This provides the possibility to leverage dialer "middleware" to inject virtual origins, such as the DNS resolver service. DNS Resolver service also gains access to the DialTCP operation to service TCP DNS requests. Minor refactoring includes changes to remove the needs previously provided by the warp-routing configuration. This configuration cannot be disabled by cloudflared so many of the references have been adjusted or removed. Closes TUN-9470
This commit is contained in:
@@ -220,7 +220,15 @@ func prepareTunnelConfig(
|
|||||||
resolvedRegion = endpoint
|
resolvedRegion = endpoint
|
||||||
}
|
}
|
||||||
|
|
||||||
dnsService := origins.NewDNSResolver(log)
|
warpRoutingConfig := ingress.NewWarpRoutingConfig(&cfg.WarpRouting)
|
||||||
|
|
||||||
|
// Setup origin dialer service and virtual services
|
||||||
|
originDialerService := ingress.NewOriginDialer(ingress.OriginConfig{
|
||||||
|
DefaultDialer: ingress.NewDialer(warpRoutingConfig),
|
||||||
|
TCPWriteTimeout: c.Duration(flags.WriteStreamTimeout),
|
||||||
|
}, log)
|
||||||
|
dnsService := origins.NewDNSResolverService(origins.NewDNSDialer(), log)
|
||||||
|
originDialerService.AddReservedService(dnsService, []netip.AddrPort{origins.VirtualDNSServiceAddr})
|
||||||
|
|
||||||
tunnelConfig := &supervisor.TunnelConfig{
|
tunnelConfig := &supervisor.TunnelConfig{
|
||||||
ClientConfig: clientConfig,
|
ClientConfig: clientConfig,
|
||||||
@@ -250,6 +258,7 @@ func prepareTunnelConfig(
|
|||||||
QUICConnectionLevelFlowControlLimit: c.Uint64(flags.QuicConnLevelFlowControlLimit),
|
QUICConnectionLevelFlowControlLimit: c.Uint64(flags.QuicConnLevelFlowControlLimit),
|
||||||
QUICStreamLevelFlowControlLimit: c.Uint64(flags.QuicStreamLevelFlowControlLimit),
|
QUICStreamLevelFlowControlLimit: c.Uint64(flags.QuicStreamLevelFlowControlLimit),
|
||||||
OriginDNSService: dnsService,
|
OriginDNSService: dnsService,
|
||||||
|
OriginDialerService: originDialerService,
|
||||||
}
|
}
|
||||||
icmpRouter, err := newICMPRouter(c, log)
|
icmpRouter, err := newICMPRouter(c, log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -259,9 +268,9 @@ func prepareTunnelConfig(
|
|||||||
}
|
}
|
||||||
orchestratorConfig := &orchestration.Config{
|
orchestratorConfig := &orchestration.Config{
|
||||||
Ingress: &ingressRules,
|
Ingress: &ingressRules,
|
||||||
WarpRouting: ingress.NewWarpRoutingConfig(&cfg.WarpRouting),
|
WarpRouting: warpRoutingConfig,
|
||||||
|
OriginDialerService: originDialerService,
|
||||||
ConfigurationFlags: parseConfigFlags(c),
|
ConfigurationFlags: parseConfigFlags(c),
|
||||||
WriteTimeout: tunnelConfig.WriteStreamTimeout,
|
|
||||||
}
|
}
|
||||||
return tunnelConfig, orchestratorConfig, nil
|
return tunnelConfig, orchestratorConfig, nil
|
||||||
}
|
}
|
||||||
|
@@ -30,6 +30,7 @@ import (
|
|||||||
"golang.org/x/net/nettest"
|
"golang.org/x/net/nettest"
|
||||||
|
|
||||||
"github.com/cloudflare/cloudflared/client"
|
"github.com/cloudflare/cloudflared/client"
|
||||||
|
"github.com/cloudflare/cloudflared/config"
|
||||||
cfdflow "github.com/cloudflare/cloudflared/flow"
|
cfdflow "github.com/cloudflare/cloudflared/flow"
|
||||||
|
|
||||||
"github.com/cloudflare/cloudflared/datagramsession"
|
"github.com/cloudflare/cloudflared/datagramsession"
|
||||||
@@ -823,6 +824,15 @@ func testTunnelConnection(t *testing.T, serverAddr netip.AddrPort, index uint8)
|
|||||||
sessionManager := datagramsession.NewManager(&log, datagramMuxer.SendToSession, sessionDemuxChan)
|
sessionManager := datagramsession.NewManager(&log, datagramMuxer.SendToSession, sessionDemuxChan)
|
||||||
var connIndex uint8 = 0
|
var connIndex uint8 = 0
|
||||||
packetRouter := ingress.NewPacketRouter(nil, datagramMuxer, connIndex, &log)
|
packetRouter := ingress.NewPacketRouter(nil, datagramMuxer, connIndex, &log)
|
||||||
|
testDefaultDialer := ingress.NewDialer(ingress.WarpRoutingConfig{
|
||||||
|
ConnectTimeout: config.CustomDuration{Duration: 1 * time.Second},
|
||||||
|
TCPKeepAlive: config.CustomDuration{Duration: 15 * time.Second},
|
||||||
|
MaxActiveFlows: 0,
|
||||||
|
})
|
||||||
|
originDialer := ingress.NewOriginDialer(ingress.OriginConfig{
|
||||||
|
DefaultDialer: testDefaultDialer,
|
||||||
|
TCPWriteTimeout: 1 * time.Second,
|
||||||
|
}, &log)
|
||||||
|
|
||||||
datagramConn := &datagramV2Connection{
|
datagramConn := &datagramV2Connection{
|
||||||
conn,
|
conn,
|
||||||
@@ -830,7 +840,7 @@ func testTunnelConnection(t *testing.T, serverAddr netip.AddrPort, index uint8)
|
|||||||
sessionManager,
|
sessionManager,
|
||||||
cfdflow.NewLimiter(0),
|
cfdflow.NewLimiter(0),
|
||||||
datagramMuxer,
|
datagramMuxer,
|
||||||
ingress.DefaultUDPDialer,
|
originDialer,
|
||||||
packetRouter,
|
packetRouter,
|
||||||
15 * time.Second,
|
15 * time.Second,
|
||||||
0 * time.Second,
|
0 * time.Second,
|
||||||
|
@@ -57,8 +57,8 @@ type datagramV2Connection struct {
|
|||||||
|
|
||||||
// datagramMuxer mux/demux datagrams from quic connection
|
// datagramMuxer mux/demux datagrams from quic connection
|
||||||
datagramMuxer *cfdquic.DatagramMuxerV2
|
datagramMuxer *cfdquic.DatagramMuxerV2
|
||||||
// ingressUDPProxy acts as the origin dialer for UDP requests
|
// originDialer is the origin dialer for UDP requests
|
||||||
ingressUDPProxy ingress.UDPOriginProxy
|
originDialer ingress.OriginUDPDialer
|
||||||
// packetRouter acts as the origin router for ICMP requests
|
// packetRouter acts as the origin router for ICMP requests
|
||||||
packetRouter *ingress.PacketRouter
|
packetRouter *ingress.PacketRouter
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@ type datagramV2Connection struct {
|
|||||||
|
|
||||||
func NewDatagramV2Connection(ctx context.Context,
|
func NewDatagramV2Connection(ctx context.Context,
|
||||||
conn quic.Connection,
|
conn quic.Connection,
|
||||||
ingressUDPProxy ingress.UDPOriginProxy,
|
originDialer ingress.OriginUDPDialer,
|
||||||
icmpRouter ingress.ICMPRouter,
|
icmpRouter ingress.ICMPRouter,
|
||||||
index uint8,
|
index uint8,
|
||||||
rpcTimeout time.Duration,
|
rpcTimeout time.Duration,
|
||||||
@@ -89,7 +89,7 @@ func NewDatagramV2Connection(ctx context.Context,
|
|||||||
sessionManager: sessionManager,
|
sessionManager: sessionManager,
|
||||||
flowLimiter: flowLimiter,
|
flowLimiter: flowLimiter,
|
||||||
datagramMuxer: datagramMuxer,
|
datagramMuxer: datagramMuxer,
|
||||||
ingressUDPProxy: ingressUDPProxy,
|
originDialer: originDialer,
|
||||||
packetRouter: packetRouter,
|
packetRouter: packetRouter,
|
||||||
rpcTimeout: rpcTimeout,
|
rpcTimeout: rpcTimeout,
|
||||||
streamWriteTimeout: streamWriteTimeout,
|
streamWriteTimeout: streamWriteTimeout,
|
||||||
@@ -159,7 +159,7 @@ func (q *datagramV2Connection) RegisterUdpSession(ctx context.Context, sessionID
|
|||||||
|
|
||||||
// Each session is a series of datagram from an eyeball to a dstIP:dstPort.
|
// Each session is a series of datagram from an eyeball to a dstIP:dstPort.
|
||||||
// (src port, dst IP, dst port) uniquely identifies a session, so it needs a dedicated connected socket.
|
// (src port, dst IP, dst port) uniquely identifies a session, so it needs a dedicated connected socket.
|
||||||
originProxy, err := q.ingressUDPProxy.DialUDP(dstAddrPort)
|
originProxy, err := q.originDialer.DialUDP(dstAddrPort)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Err(err).Msgf("Failed to create udp proxy to %s", dstAddrPort)
|
log.Err(err).Msgf("Failed to create udp proxy to %s", dstAddrPort)
|
||||||
tracing.EndWithErrorStatus(registerSpan, err)
|
tracing.EndWithErrorStatus(registerSpan, err)
|
||||||
|
@@ -13,7 +13,6 @@ import (
|
|||||||
"go.uber.org/mock/gomock"
|
"go.uber.org/mock/gomock"
|
||||||
|
|
||||||
cfdflow "github.com/cloudflare/cloudflared/flow"
|
cfdflow "github.com/cloudflare/cloudflared/flow"
|
||||||
"github.com/cloudflare/cloudflared/ingress"
|
|
||||||
"github.com/cloudflare/cloudflared/mocks"
|
"github.com/cloudflare/cloudflared/mocks"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -84,7 +83,7 @@ func TestRateLimitOnNewDatagramV2UDPSession(t *testing.T) {
|
|||||||
datagramConn := NewDatagramV2Connection(
|
datagramConn := NewDatagramV2Connection(
|
||||||
t.Context(),
|
t.Context(),
|
||||||
conn,
|
conn,
|
||||||
ingress.DefaultUDPDialer,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
0,
|
0,
|
||||||
0*time.Second,
|
0*time.Second,
|
||||||
|
@@ -19,7 +19,7 @@ import (
|
|||||||
type OriginConnection interface {
|
type OriginConnection interface {
|
||||||
// Stream should generally be implemented as a bidirectional io.Copy.
|
// Stream should generally be implemented as a bidirectional io.Copy.
|
||||||
Stream(ctx context.Context, tunnelConn io.ReadWriter, log *zerolog.Logger)
|
Stream(ctx context.Context, tunnelConn io.ReadWriter, log *zerolog.Logger)
|
||||||
Close()
|
Close() error
|
||||||
}
|
}
|
||||||
|
|
||||||
type streamHandlerFunc func(originConn io.ReadWriter, remoteConn net.Conn, log *zerolog.Logger)
|
type streamHandlerFunc func(originConn io.ReadWriter, remoteConn net.Conn, log *zerolog.Logger)
|
||||||
@@ -48,16 +48,7 @@ func (tc *tcpConnection) Write(b []byte) (int, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nBytes, err := tc.Conn.Write(b)
|
return tc.Conn.Write(b)
|
||||||
if err != nil {
|
|
||||||
tc.logger.Err(err).Msg("Error writing to the TCP connection")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nBytes, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tc *tcpConnection) Close() {
|
|
||||||
tc.Conn.Close()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// tcpOverWSConnection is an OriginConnection that streams to TCP over WS.
|
// tcpOverWSConnection is an OriginConnection that streams to TCP over WS.
|
||||||
@@ -75,8 +66,8 @@ func (wc *tcpOverWSConnection) Stream(ctx context.Context, tunnelConn io.ReadWri
|
|||||||
wsConn.Close()
|
wsConn.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wc *tcpOverWSConnection) Close() {
|
func (wc *tcpOverWSConnection) Close() error {
|
||||||
wc.conn.Close()
|
return wc.conn.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// socksProxyOverWSConnection is an OriginConnection that streams SOCKS connections over WS.
|
// socksProxyOverWSConnection is an OriginConnection that streams SOCKS connections over WS.
|
||||||
@@ -95,5 +86,6 @@ func (sp *socksProxyOverWSConnection) Stream(ctx context.Context, tunnelConn io.
|
|||||||
wsConn.Close()
|
wsConn.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sp *socksProxyOverWSConnection) Close() {
|
func (sp *socksProxyOverWSConnection) Close() error {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
146
ingress/origin_dialer.go
Normal file
146
ingress/origin_dialer.go
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
package ingress
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/netip"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog"
|
||||||
|
)
|
||||||
|
|
||||||
|
// OriginTCPDialer provides a TCP dial operation to a requested address.
|
||||||
|
type OriginTCPDialer interface {
|
||||||
|
DialTCP(ctx context.Context, addr netip.AddrPort) (net.Conn, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OriginUDPDialer provides a UDP dial operation to a requested address.
|
||||||
|
type OriginUDPDialer interface {
|
||||||
|
DialUDP(addr netip.AddrPort) (net.Conn, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OriginDialer provides both TCP and UDP dial operations to an address.
|
||||||
|
type OriginDialer interface {
|
||||||
|
OriginTCPDialer
|
||||||
|
OriginUDPDialer
|
||||||
|
}
|
||||||
|
|
||||||
|
type OriginConfig struct {
|
||||||
|
// The default Dialer used if no reserved services are found for an origin request.
|
||||||
|
DefaultDialer OriginDialer
|
||||||
|
// Timeout on write operations for TCP connections to the origin.
|
||||||
|
TCPWriteTimeout time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// OriginDialerService provides a proxy TCP and UDP dialer to origin services while allowing reserved
|
||||||
|
// services to be provided. These reserved services are assigned to specific [netip.AddrPort]s
|
||||||
|
// and provide their own [OriginDialer]'s to handle origin dialing per protocol.
|
||||||
|
type OriginDialerService struct {
|
||||||
|
// Reserved TCP services for reserved AddrPort values
|
||||||
|
reservedTCPServices map[netip.AddrPort]OriginTCPDialer
|
||||||
|
// Reserved UDP services for reserved AddrPort values
|
||||||
|
reservedUDPServices map[netip.AddrPort]OriginUDPDialer
|
||||||
|
// The default Dialer used if no reserved services are found for an origin request
|
||||||
|
defaultDialer OriginDialer
|
||||||
|
defaultDialerM sync.RWMutex
|
||||||
|
// Write timeout for TCP connections
|
||||||
|
writeTimeout time.Duration
|
||||||
|
|
||||||
|
logger *zerolog.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewOriginDialer(config OriginConfig, logger *zerolog.Logger) *OriginDialerService {
|
||||||
|
return &OriginDialerService{
|
||||||
|
reservedTCPServices: map[netip.AddrPort]OriginTCPDialer{},
|
||||||
|
reservedUDPServices: map[netip.AddrPort]OriginUDPDialer{},
|
||||||
|
defaultDialer: config.DefaultDialer,
|
||||||
|
writeTimeout: config.TCPWriteTimeout,
|
||||||
|
logger: logger,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddReservedService adds a reserved virtual service to dial to.
|
||||||
|
// Not locked and expected to be initialized before calling first dial and not afterwards.
|
||||||
|
func (d *OriginDialerService) AddReservedService(service OriginDialer, addrs []netip.AddrPort) {
|
||||||
|
for _, addr := range addrs {
|
||||||
|
d.reservedTCPServices[addr] = service
|
||||||
|
d.reservedUDPServices[addr] = service
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateDefaultDialer updates the default dialer.
|
||||||
|
func (d *OriginDialerService) UpdateDefaultDialer(dialer *Dialer) {
|
||||||
|
d.defaultDialerM.Lock()
|
||||||
|
defer d.defaultDialerM.Unlock()
|
||||||
|
d.defaultDialer = dialer
|
||||||
|
}
|
||||||
|
|
||||||
|
// DialTCP will perform a dial TCP to the requested addr.
|
||||||
|
func (d *OriginDialerService) DialTCP(ctx context.Context, addr netip.AddrPort) (net.Conn, error) {
|
||||||
|
conn, err := d.dialTCP(ctx, addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Assign the write timeout for the TCP operations
|
||||||
|
return &tcpConnection{
|
||||||
|
Conn: conn,
|
||||||
|
writeTimeout: d.writeTimeout,
|
||||||
|
logger: d.logger,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *OriginDialerService) dialTCP(ctx context.Context, addr netip.AddrPort) (net.Conn, error) {
|
||||||
|
// Check to see if any reserved services are available for this addr and call their dialer instead.
|
||||||
|
if dialer, ok := d.reservedTCPServices[addr]; ok {
|
||||||
|
return dialer.DialTCP(ctx, addr)
|
||||||
|
}
|
||||||
|
d.defaultDialerM.RLock()
|
||||||
|
dialer := d.defaultDialer
|
||||||
|
d.defaultDialerM.RUnlock()
|
||||||
|
return dialer.DialTCP(ctx, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DialUDP will perform a dial UDP to the requested addr.
|
||||||
|
func (d *OriginDialerService) DialUDP(addr netip.AddrPort) (net.Conn, error) {
|
||||||
|
// Check to see if any reserved services are available for this addr and call their dialer instead.
|
||||||
|
if dialer, ok := d.reservedUDPServices[addr]; ok {
|
||||||
|
return dialer.DialUDP(addr)
|
||||||
|
}
|
||||||
|
d.defaultDialerM.RLock()
|
||||||
|
dialer := d.defaultDialer
|
||||||
|
d.defaultDialerM.RUnlock()
|
||||||
|
return dialer.DialUDP(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Dialer struct {
|
||||||
|
Dialer net.Dialer
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDialer(config WarpRoutingConfig) *Dialer {
|
||||||
|
return &Dialer{
|
||||||
|
Dialer: net.Dialer{
|
||||||
|
Timeout: config.ConnectTimeout.Duration,
|
||||||
|
KeepAlive: config.TCPKeepAlive.Duration,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Dialer) DialTCP(ctx context.Context, dest netip.AddrPort) (net.Conn, error) {
|
||||||
|
conn, err := d.Dialer.DialContext(ctx, "tcp", dest.String())
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to dial tcp to origin %s: %w", dest, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Dialer) DialUDP(dest netip.AddrPort) (net.Conn, error) {
|
||||||
|
conn, err := d.Dialer.Dial("udp", dest.String())
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to dial udp to origin %s: %w", dest, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn, nil
|
||||||
|
}
|
@@ -1,66 +0,0 @@
|
|||||||
package ingress
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"net/netip"
|
|
||||||
|
|
||||||
"github.com/rs/zerolog"
|
|
||||||
)
|
|
||||||
|
|
||||||
// UDPOriginService provides a proxy UDP dialer to origin services while allowing reserved
|
|
||||||
// services to be provided. These reserved services are assigned to specific [netip.AddrPort]s
|
|
||||||
// and provide their own [UDPOriginProxy]s to handle UDP origin dialing.
|
|
||||||
type UDPOriginService struct {
|
|
||||||
// Reserved services for reserved AddrPort values
|
|
||||||
reservedServices map[netip.AddrPort]UDPOriginProxy
|
|
||||||
// The default UDP Dialer used if no reserved services are found for an origin request.
|
|
||||||
defaultDialer UDPOriginProxy
|
|
||||||
|
|
||||||
logger *zerolog.Logger
|
|
||||||
}
|
|
||||||
|
|
||||||
// UDPOriginProxy provides a UDP dial operation to a requested addr.
|
|
||||||
type UDPOriginProxy interface {
|
|
||||||
DialUDP(addr netip.AddrPort) (net.Conn, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewUDPOriginService(reserved map[netip.AddrPort]UDPOriginProxy, logger *zerolog.Logger) *UDPOriginService {
|
|
||||||
return &UDPOriginService{
|
|
||||||
reservedServices: reserved,
|
|
||||||
defaultDialer: DefaultUDPDialer,
|
|
||||||
logger: logger,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetUDPDialer updates the default UDP Dialer used.
|
|
||||||
// Typically used in unit testing.
|
|
||||||
func (s *UDPOriginService) SetDefaultDialer(dialer UDPOriginProxy) {
|
|
||||||
s.defaultDialer = dialer
|
|
||||||
}
|
|
||||||
|
|
||||||
// DialUDP will perform a dial UDP to the requested addr.
|
|
||||||
func (s *UDPOriginService) DialUDP(addr netip.AddrPort) (net.Conn, error) {
|
|
||||||
// Check to see if any reserved services are available for this addr and call their dialer instead.
|
|
||||||
if dialer, ok := s.reservedServices[addr]; ok {
|
|
||||||
return dialer.DialUDP(addr)
|
|
||||||
}
|
|
||||||
return s.defaultDialer.DialUDP(addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
type defaultUDPDialer struct{}
|
|
||||||
|
|
||||||
var DefaultUDPDialer UDPOriginProxy = &defaultUDPDialer{}
|
|
||||||
|
|
||||||
func (d *defaultUDPDialer) DialUDP(dest netip.AddrPort) (net.Conn, error) {
|
|
||||||
addr := net.UDPAddrFromAddrPort(dest)
|
|
||||||
|
|
||||||
// We use nil as local addr to force runtime to find the best suitable local address IP given the destination
|
|
||||||
// address as context.
|
|
||||||
udpConn, err := net.DialUDP("udp", nil, addr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("unable to dial udp to origin %s: %w", dest, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return udpConn, nil
|
|
||||||
}
|
|
@@ -45,20 +45,28 @@ type DNSResolverService struct {
|
|||||||
address netip.AddrPort
|
address netip.AddrPort
|
||||||
addressM sync.RWMutex
|
addressM sync.RWMutex
|
||||||
|
|
||||||
dialer ingress.UDPOriginProxy
|
dialer ingress.OriginDialer
|
||||||
resolver peekResolver
|
resolver peekResolver
|
||||||
logger *zerolog.Logger
|
logger *zerolog.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDNSResolver(logger *zerolog.Logger) *DNSResolverService {
|
func NewDNSResolverService(dialer ingress.OriginDialer, logger *zerolog.Logger) *DNSResolverService {
|
||||||
return &DNSResolverService{
|
return &DNSResolverService{
|
||||||
address: defaultResolverAddr,
|
address: defaultResolverAddr,
|
||||||
dialer: ingress.DefaultUDPDialer,
|
dialer: dialer,
|
||||||
resolver: &resolver{dialFunc: net.Dial},
|
resolver: &resolver{dialFunc: net.Dial},
|
||||||
logger: logger,
|
logger: logger,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *DNSResolverService) DialTCP(ctx context.Context, _ netip.AddrPort) (net.Conn, error) {
|
||||||
|
s.addressM.RLock()
|
||||||
|
dest := s.address
|
||||||
|
s.addressM.RUnlock()
|
||||||
|
// The dialer ignores the provided address because the request will instead go to the local DNS resolver.
|
||||||
|
return s.dialer.DialTCP(ctx, dest)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *DNSResolverService) DialUDP(_ netip.AddrPort) (net.Conn, error) {
|
func (s *DNSResolverService) DialUDP(_ netip.AddrPort) (net.Conn, error) {
|
||||||
s.addressM.RLock()
|
s.addressM.RLock()
|
||||||
dest := s.address
|
dest := s.address
|
||||||
@@ -155,3 +163,18 @@ func (r *resolver) peekDial(ctx context.Context, network, address string) (net.C
|
|||||||
r.address = address
|
r.address = address
|
||||||
return r.dialFunc(network, address)
|
return r.dialFunc(network, address)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewDNSDialer creates a custom dialer for the DNS resolver service to utilize.
|
||||||
|
func NewDNSDialer() *ingress.Dialer {
|
||||||
|
return &ingress.Dialer{
|
||||||
|
Dialer: net.Dialer{
|
||||||
|
// We want short timeouts for the DNS requests
|
||||||
|
Timeout: 5 * time.Second,
|
||||||
|
// We do not want keep alive since the edge will not reuse TCP connections per request
|
||||||
|
KeepAlive: -1,
|
||||||
|
KeepAliveConfig: net.KeepAliveConfig{
|
||||||
|
Enable: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -12,7 +12,7 @@ import (
|
|||||||
|
|
||||||
func TestDNSResolver_DefaultResolver(t *testing.T) {
|
func TestDNSResolver_DefaultResolver(t *testing.T) {
|
||||||
log := zerolog.Nop()
|
log := zerolog.Nop()
|
||||||
service := NewDNSResolver(&log)
|
service := NewDNSResolverService(NewDNSDialer(), &log)
|
||||||
mockResolver := &mockPeekResolver{
|
mockResolver := &mockPeekResolver{
|
||||||
address: "127.0.0.2:53",
|
address: "127.0.0.2:53",
|
||||||
}
|
}
|
||||||
@@ -24,7 +24,7 @@ func TestDNSResolver_DefaultResolver(t *testing.T) {
|
|||||||
|
|
||||||
func TestDNSResolver_UpdateResolverAddress(t *testing.T) {
|
func TestDNSResolver_UpdateResolverAddress(t *testing.T) {
|
||||||
log := zerolog.Nop()
|
log := zerolog.Nop()
|
||||||
service := NewDNSResolver(&log)
|
service := NewDNSResolverService(NewDNSDialer(), &log)
|
||||||
|
|
||||||
mockResolver := &mockPeekResolver{}
|
mockResolver := &mockPeekResolver{}
|
||||||
service.resolver = mockResolver
|
service.resolver = mockResolver
|
||||||
@@ -51,7 +51,7 @@ func TestDNSResolver_UpdateResolverAddress(t *testing.T) {
|
|||||||
|
|
||||||
func TestDNSResolver_UpdateResolverAddressInvalid(t *testing.T) {
|
func TestDNSResolver_UpdateResolverAddressInvalid(t *testing.T) {
|
||||||
log := zerolog.Nop()
|
log := zerolog.Nop()
|
||||||
service := NewDNSResolver(&log)
|
service := NewDNSResolverService(NewDNSDialer(), &log)
|
||||||
mockResolver := &mockPeekResolver{}
|
mockResolver := &mockPeekResolver{}
|
||||||
service.resolver = mockResolver
|
service.resolver = mockResolver
|
||||||
|
|
||||||
@@ -77,7 +77,7 @@ func TestDNSResolver_UpdateResolverAddressInvalid(t *testing.T) {
|
|||||||
|
|
||||||
func TestDNSResolver_UpdateResolverErrorIgnored(t *testing.T) {
|
func TestDNSResolver_UpdateResolverErrorIgnored(t *testing.T) {
|
||||||
log := zerolog.Nop()
|
log := zerolog.Nop()
|
||||||
service := NewDNSResolver(&log)
|
service := NewDNSResolverService(NewDNSDialer(), &log)
|
||||||
resolverErr := errors.New("test resolver error")
|
resolverErr := errors.New("test resolver error")
|
||||||
mockResolver := &mockPeekResolver{err: resolverErr}
|
mockResolver := &mockPeekResolver{err: resolverErr}
|
||||||
service.resolver = mockResolver
|
service.resolver = mockResolver
|
||||||
@@ -93,13 +93,12 @@ func TestDNSResolver_UpdateResolverErrorIgnored(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDNSResolver_DialUsesResolvedAddress(t *testing.T) {
|
func TestDNSResolver_DialUDPUsesResolvedAddress(t *testing.T) {
|
||||||
log := zerolog.Nop()
|
log := zerolog.Nop()
|
||||||
service := NewDNSResolver(&log)
|
mockDialer := &mockDialer{expected: defaultResolverAddr}
|
||||||
|
service := NewDNSResolverService(mockDialer, &log)
|
||||||
mockResolver := &mockPeekResolver{}
|
mockResolver := &mockPeekResolver{}
|
||||||
service.resolver = mockResolver
|
service.resolver = mockResolver
|
||||||
mockDialer := &mockDialer{expected: defaultResolverAddr}
|
|
||||||
service.dialer = mockDialer
|
|
||||||
|
|
||||||
// Attempt a dial to 127.0.0.2:53 which should be ignored and instead resolve to 127.0.0.1:53
|
// Attempt a dial to 127.0.0.2:53 which should be ignored and instead resolve to 127.0.0.1:53
|
||||||
_, err := service.DialUDP(netip.MustParseAddrPort("127.0.0.2:53"))
|
_, err := service.DialUDP(netip.MustParseAddrPort("127.0.0.2:53"))
|
||||||
@@ -108,6 +107,20 @@ func TestDNSResolver_DialUsesResolvedAddress(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDNSResolver_DialTCPUsesResolvedAddress(t *testing.T) {
|
||||||
|
log := zerolog.Nop()
|
||||||
|
mockDialer := &mockDialer{expected: defaultResolverAddr}
|
||||||
|
service := NewDNSResolverService(mockDialer, &log)
|
||||||
|
mockResolver := &mockPeekResolver{}
|
||||||
|
service.resolver = mockResolver
|
||||||
|
|
||||||
|
// Attempt a dial to 127.0.0.2:53 which should be ignored and instead resolve to 127.0.0.1:53
|
||||||
|
_, err := service.DialTCP(t.Context(), netip.MustParseAddrPort("127.0.0.2:53"))
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type mockPeekResolver struct {
|
type mockPeekResolver struct {
|
||||||
err error
|
err error
|
||||||
address string
|
address string
|
||||||
@@ -126,6 +139,13 @@ type mockDialer struct {
|
|||||||
expected netip.AddrPort
|
expected netip.AddrPort
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *mockDialer) DialTCP(ctx context.Context, addr netip.AddrPort) (net.Conn, error) {
|
||||||
|
if d.expected != addr {
|
||||||
|
return nil, errors.New("unexpected address dialed")
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (d *mockDialer) DialUDP(addr netip.AddrPort) (net.Conn, error) {
|
func (d *mockDialer) DialUDP(addr netip.AddrPort) (net.Conn, error) {
|
||||||
if d.expected != addr {
|
if d.expected != addr {
|
||||||
return nil, errors.New("unexpected address dialed")
|
return nil, errors.New("unexpected address dialed")
|
||||||
|
@@ -2,7 +2,6 @@ package orchestration
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/cloudflare/cloudflared/config"
|
"github.com/cloudflare/cloudflared/config"
|
||||||
"github.com/cloudflare/cloudflared/ingress"
|
"github.com/cloudflare/cloudflared/ingress"
|
||||||
@@ -22,7 +21,7 @@ type newLocalConfig struct {
|
|||||||
type Config struct {
|
type Config struct {
|
||||||
Ingress *ingress.Ingress
|
Ingress *ingress.Ingress
|
||||||
WarpRouting ingress.WarpRoutingConfig
|
WarpRouting ingress.WarpRoutingConfig
|
||||||
WriteTimeout time.Duration
|
OriginDialerService *ingress.OriginDialerService
|
||||||
|
|
||||||
// Extra settings used to configure this instance but that are not eligible for remotely management
|
// Extra settings used to configure this instance but that are not eligible for remotely management
|
||||||
// ie. (--protocol, --loglevel, ...)
|
// ie. (--protocol, --loglevel, ...)
|
||||||
|
@@ -38,6 +38,8 @@ type Orchestrator struct {
|
|||||||
tags []pogs.Tag
|
tags []pogs.Tag
|
||||||
// flowLimiter tracks active sessions across the tunnel and limits new sessions if they are above the limit.
|
// flowLimiter tracks active sessions across the tunnel and limits new sessions if they are above the limit.
|
||||||
flowLimiter cfdflow.Limiter
|
flowLimiter cfdflow.Limiter
|
||||||
|
// Origin dialer service to manage egress socket dialing.
|
||||||
|
originDialerService *ingress.OriginDialerService
|
||||||
log *zerolog.Logger
|
log *zerolog.Logger
|
||||||
|
|
||||||
// orchestrator must not handle any more updates after shutdownC is closed
|
// orchestrator must not handle any more updates after shutdownC is closed
|
||||||
@@ -50,7 +52,8 @@ func NewOrchestrator(ctx context.Context,
|
|||||||
config *Config,
|
config *Config,
|
||||||
tags []pogs.Tag,
|
tags []pogs.Tag,
|
||||||
internalRules []ingress.Rule,
|
internalRules []ingress.Rule,
|
||||||
log *zerolog.Logger) (*Orchestrator, error) {
|
log *zerolog.Logger,
|
||||||
|
) (*Orchestrator, error) {
|
||||||
o := &Orchestrator{
|
o := &Orchestrator{
|
||||||
// Lowest possible version, any remote configuration will have version higher than this
|
// Lowest possible version, any remote configuration will have version higher than this
|
||||||
// Starting at -1 allows a configuration migration (local to remote) to override the current configuration as it
|
// Starting at -1 allows a configuration migration (local to remote) to override the current configuration as it
|
||||||
@@ -60,6 +63,7 @@ func NewOrchestrator(ctx context.Context,
|
|||||||
config: config,
|
config: config,
|
||||||
tags: tags,
|
tags: tags,
|
||||||
flowLimiter: cfdflow.NewLimiter(config.WarpRouting.MaxActiveFlows),
|
flowLimiter: cfdflow.NewLimiter(config.WarpRouting.MaxActiveFlows),
|
||||||
|
originDialerService: config.OriginDialerService,
|
||||||
log: log,
|
log: log,
|
||||||
shutdownC: ctx.Done(),
|
shutdownC: ctx.Done(),
|
||||||
}
|
}
|
||||||
@@ -175,7 +179,15 @@ func (o *Orchestrator) updateIngress(ingressRules ingress.Ingress, warpRouting i
|
|||||||
// Update the flow limit since the configuration might have changed
|
// Update the flow limit since the configuration might have changed
|
||||||
o.flowLimiter.SetLimit(warpRouting.MaxActiveFlows)
|
o.flowLimiter.SetLimit(warpRouting.MaxActiveFlows)
|
||||||
|
|
||||||
proxy := proxy.NewOriginProxy(ingressRules, warpRouting, o.tags, o.flowLimiter, o.config.WriteTimeout, o.log)
|
// Update the origin dialer service with the new dialer settings
|
||||||
|
// We need to update the dialer here instead of creating a new instance of OriginDialerService because it has
|
||||||
|
// its own references and go routines. Specifically, the UDP dialer is a reference to this same service all the
|
||||||
|
// way into the datagram manager. Reconstructing the datagram manager is not something we currently provide during
|
||||||
|
// runtime in response to a configuration push except when starting a tunnel connection.
|
||||||
|
o.originDialerService.UpdateDefaultDialer(ingress.NewDialer(warpRouting))
|
||||||
|
|
||||||
|
// Create and replace the origin proxy with a new instance
|
||||||
|
proxy := proxy.NewOriginProxy(ingressRules, o.originDialerService, o.tags, o.flowLimiter, o.log)
|
||||||
o.proxy.Store(proxy)
|
o.proxy.Store(proxy)
|
||||||
o.config.Ingress = &ingressRules
|
o.config.Ingress = &ingressRules
|
||||||
o.config.WarpRouting = warpRouting
|
o.config.WarpRouting = warpRouting
|
||||||
|
@@ -41,6 +41,11 @@ var (
|
|||||||
Value: "test",
|
Value: "test",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
testDefaultDialer = ingress.NewDialer(ingress.WarpRoutingConfig{
|
||||||
|
ConnectTimeout: config.CustomDuration{Duration: 1 * time.Second},
|
||||||
|
TCPKeepAlive: config.CustomDuration{Duration: 15 * time.Second},
|
||||||
|
MaxActiveFlows: 0,
|
||||||
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestUpdateConfiguration tests that
|
// TestUpdateConfiguration tests that
|
||||||
@@ -50,8 +55,13 @@ var (
|
|||||||
// - configurations can be deserialized
|
// - configurations can be deserialized
|
||||||
// - receiving an old version is noop
|
// - receiving an old version is noop
|
||||||
func TestUpdateConfiguration(t *testing.T) {
|
func TestUpdateConfiguration(t *testing.T) {
|
||||||
|
originDialer := ingress.NewOriginDialer(ingress.OriginConfig{
|
||||||
|
DefaultDialer: testDefaultDialer,
|
||||||
|
TCPWriteTimeout: 1 * time.Second,
|
||||||
|
}, &testLogger)
|
||||||
initConfig := &Config{
|
initConfig := &Config{
|
||||||
Ingress: &ingress.Ingress{},
|
Ingress: &ingress.Ingress{},
|
||||||
|
OriginDialerService: originDialer,
|
||||||
}
|
}
|
||||||
orchestrator, err := NewOrchestrator(t.Context(), initConfig, testTags, []ingress.Rule{ingress.NewManagementRule(management.New("management.argotunnel.com", false, "1.1.1.1:80", uuid.Nil, "", &testLogger, nil))}, &testLogger)
|
orchestrator, err := NewOrchestrator(t.Context(), initConfig, testTags, []ingress.Rule{ingress.NewManagementRule(management.New("management.argotunnel.com", false, "1.1.1.1:80", uuid.Nil, "", &testLogger, nil))}, &testLogger)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -179,8 +189,13 @@ func TestUpdateConfiguration(t *testing.T) {
|
|||||||
// Validates that a new version 0 will be applied if the configuration is loaded locally.
|
// Validates that a new version 0 will be applied if the configuration is loaded locally.
|
||||||
// This will happen when a locally managed tunnel is migrated to remote configuration and receives its first configuration.
|
// This will happen when a locally managed tunnel is migrated to remote configuration and receives its first configuration.
|
||||||
func TestUpdateConfiguration_FromMigration(t *testing.T) {
|
func TestUpdateConfiguration_FromMigration(t *testing.T) {
|
||||||
|
originDialer := ingress.NewOriginDialer(ingress.OriginConfig{
|
||||||
|
DefaultDialer: testDefaultDialer,
|
||||||
|
TCPWriteTimeout: 1 * time.Second,
|
||||||
|
}, &testLogger)
|
||||||
initConfig := &Config{
|
initConfig := &Config{
|
||||||
Ingress: &ingress.Ingress{},
|
Ingress: &ingress.Ingress{},
|
||||||
|
OriginDialerService: originDialer,
|
||||||
}
|
}
|
||||||
orchestrator, err := NewOrchestrator(t.Context(), initConfig, testTags, []ingress.Rule{}, &testLogger)
|
orchestrator, err := NewOrchestrator(t.Context(), initConfig, testTags, []ingress.Rule{}, &testLogger)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -205,8 +220,13 @@ func TestUpdateConfiguration_FromMigration(t *testing.T) {
|
|||||||
|
|
||||||
// Validates that the default ingress rule will be set if there is no rule provided from the remote.
|
// Validates that the default ingress rule will be set if there is no rule provided from the remote.
|
||||||
func TestUpdateConfiguration_WithoutIngressRule(t *testing.T) {
|
func TestUpdateConfiguration_WithoutIngressRule(t *testing.T) {
|
||||||
|
originDialer := ingress.NewOriginDialer(ingress.OriginConfig{
|
||||||
|
DefaultDialer: testDefaultDialer,
|
||||||
|
TCPWriteTimeout: 1 * time.Second,
|
||||||
|
}, &testLogger)
|
||||||
initConfig := &Config{
|
initConfig := &Config{
|
||||||
Ingress: &ingress.Ingress{},
|
Ingress: &ingress.Ingress{},
|
||||||
|
OriginDialerService: originDialer,
|
||||||
}
|
}
|
||||||
orchestrator, err := NewOrchestrator(t.Context(), initConfig, testTags, []ingress.Rule{}, &testLogger)
|
orchestrator, err := NewOrchestrator(t.Context(), initConfig, testTags, []ingress.Rule{}, &testLogger)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -244,6 +264,11 @@ func TestConcurrentUpdateAndRead(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer tcpOrigin.Close()
|
defer tcpOrigin.Close()
|
||||||
|
|
||||||
|
originDialer := ingress.NewOriginDialer(ingress.OriginConfig{
|
||||||
|
DefaultDialer: testDefaultDialer,
|
||||||
|
TCPWriteTimeout: 1 * time.Second,
|
||||||
|
}, &testLogger)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
configJSONV1 = []byte(fmt.Sprintf(`
|
configJSONV1 = []byte(fmt.Sprintf(`
|
||||||
{
|
{
|
||||||
@@ -297,6 +322,7 @@ func TestConcurrentUpdateAndRead(t *testing.T) {
|
|||||||
|
|
||||||
initConfig = &Config{
|
initConfig = &Config{
|
||||||
Ingress: &ingress.Ingress{},
|
Ingress: &ingress.Ingress{},
|
||||||
|
OriginDialerService: originDialer,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -313,7 +339,7 @@ func TestConcurrentUpdateAndRead(t *testing.T) {
|
|||||||
go func() {
|
go func() {
|
||||||
serveTCPOrigin(t, tcpOrigin, &wg)
|
serveTCPOrigin(t, tcpOrigin, &wg)
|
||||||
}()
|
}()
|
||||||
for i := 0; i < concurrentRequests; i++ {
|
for i := range concurrentRequests {
|
||||||
originProxy, err := orchestrator.GetOriginProxy()
|
originProxy, err := orchestrator.GetOriginProxy()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
@@ -323,48 +349,37 @@ func TestConcurrentUpdateAndRead(t *testing.T) {
|
|||||||
assert.NoError(t, err, "proxyHTTP %d failed %v", i, err)
|
assert.NoError(t, err, "proxyHTTP %d failed %v", i, err)
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
var warpRoutingDisabled bool
|
|
||||||
// The response can be from initOrigin, http_status:204 or http_status:418
|
// The response can be from initOrigin, http_status:204 or http_status:418
|
||||||
switch resp.StatusCode {
|
switch resp.StatusCode {
|
||||||
// v1 proxy, warp enabled
|
// v1 proxy
|
||||||
case 200:
|
case 200:
|
||||||
body, err := io.ReadAll(resp.Body)
|
body, err := io.ReadAll(resp.Body)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, t.Name(), string(body))
|
assert.Equal(t, t.Name(), string(body))
|
||||||
warpRoutingDisabled = false
|
// v2 proxy
|
||||||
// v2 proxy, warp disabled
|
|
||||||
case 204:
|
case 204:
|
||||||
assert.Greater(t, i, concurrentRequests/4)
|
assert.Greater(t, i, concurrentRequests/4)
|
||||||
warpRoutingDisabled = true
|
// v3 proxy
|
||||||
// v3 proxy, warp enabled
|
|
||||||
case 418:
|
case 418:
|
||||||
assert.Greater(t, i, concurrentRequests/2)
|
assert.Greater(t, i, concurrentRequests/2)
|
||||||
warpRoutingDisabled = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Once we have originProxy, it won't be changed by configuration updates.
|
// Once we have originProxy, it won't be changed by configuration updates.
|
||||||
// We can infer the version by the ProxyHTTP response code
|
// We can infer the version by the ProxyHTTP response code
|
||||||
pr, pw := io.Pipe()
|
pr, pw := io.Pipe()
|
||||||
|
|
||||||
w := newRespReadWriteFlusher()
|
w := newRespReadWriteFlusher()
|
||||||
|
|
||||||
// Write TCP message and make sure it's echo back. This has to be done in a go routune since ProxyTCP doesn't
|
// Write TCP message and make sure it's echo back. This has to be done in a go routune since ProxyTCP doesn't
|
||||||
// return until the stream is closed.
|
// return until the stream is closed.
|
||||||
if !warpRoutingDisabled {
|
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
defer pw.Close()
|
defer pw.Close()
|
||||||
tcpEyeball(t, pw, tcpBody, w)
|
tcpEyeball(t, pw, tcpBody, w)
|
||||||
}()
|
}()
|
||||||
}
|
|
||||||
|
|
||||||
err = proxyTCP(ctx, originProxy, tcpOrigin.Addr().String(), w, pr)
|
err = proxyTCP(ctx, originProxy, tcpOrigin.Addr().String(), w, pr)
|
||||||
if warpRoutingDisabled {
|
|
||||||
assert.Error(t, err, "expect proxyTCP %d to return error", i)
|
|
||||||
} else {
|
|
||||||
assert.NoError(t, err, "proxyTCP %d failed %v", i, err)
|
assert.NoError(t, err, "proxyTCP %d failed %v", i, err)
|
||||||
}
|
|
||||||
}(i, originProxy)
|
}(i, originProxy)
|
||||||
|
|
||||||
if i == concurrentRequests/4 {
|
if i == concurrentRequests/4 {
|
||||||
@@ -406,39 +421,47 @@ func TestOverrideWarpRoutingConfigWithLocalValues(t *testing.T) {
|
|||||||
require.EqualValues(t, expectedValue, warpRouting["maxActiveFlows"])
|
require.EqualValues(t, expectedValue, warpRouting["maxActiveFlows"])
|
||||||
}
|
}
|
||||||
|
|
||||||
remoteValue := uint64(100)
|
originDialer := ingress.NewOriginDialer(ingress.OriginConfig{
|
||||||
remoteIngress := ingress.Ingress{}
|
DefaultDialer: testDefaultDialer,
|
||||||
|
TCPWriteTimeout: 1 * time.Second,
|
||||||
|
}, &testLogger)
|
||||||
|
|
||||||
|
// All the possible values set for MaxActiveFlows from the various points that can provide it:
|
||||||
|
// 1. Initialized value
|
||||||
|
// 2. Local CLI flag config
|
||||||
|
// 3. Remote configuration value
|
||||||
|
initValue := uint64(0)
|
||||||
|
localValue := uint64(100)
|
||||||
|
remoteValue := uint64(500)
|
||||||
|
|
||||||
|
initConfig := &Config{
|
||||||
|
Ingress: &ingress.Ingress{},
|
||||||
|
WarpRouting: ingress.WarpRoutingConfig{
|
||||||
|
MaxActiveFlows: initValue,
|
||||||
|
},
|
||||||
|
OriginDialerService: originDialer,
|
||||||
|
ConfigurationFlags: map[string]string{
|
||||||
|
flags.MaxActiveFlows: fmt.Sprintf("%d", localValue),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// We expect the local configuration flag to be the starting value
|
||||||
|
orchestrator, err := NewOrchestrator(ctx, initConfig, testTags, []ingress.Rule{}, &testLogger)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
assertMaxActiveFlows(orchestrator, localValue)
|
||||||
|
|
||||||
|
// Assigning the MaxActiveFlows in the remote config should be ignored over the local config
|
||||||
remoteWarpConfig := ingress.WarpRoutingConfig{
|
remoteWarpConfig := ingress.WarpRoutingConfig{
|
||||||
MaxActiveFlows: remoteValue,
|
MaxActiveFlows: remoteValue,
|
||||||
}
|
}
|
||||||
remoteConfig := &Config{
|
|
||||||
Ingress: &remoteIngress,
|
|
||||||
WarpRouting: remoteWarpConfig,
|
|
||||||
ConfigurationFlags: map[string]string{},
|
|
||||||
}
|
|
||||||
orchestrator, err := NewOrchestrator(ctx, remoteConfig, testTags, []ingress.Rule{}, &testLogger)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
assertMaxActiveFlows(orchestrator, remoteValue)
|
|
||||||
|
|
||||||
// Add a local override for the maxActiveFlows
|
|
||||||
localValue := uint64(500)
|
|
||||||
remoteConfig.ConfigurationFlags[flags.MaxActiveFlows] = fmt.Sprintf("%d", localValue)
|
|
||||||
// Force a configuration refresh
|
// Force a configuration refresh
|
||||||
err = orchestrator.updateIngress(remoteIngress, remoteWarpConfig)
|
err = orchestrator.updateIngress(ingress.Ingress{}, remoteWarpConfig)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Check the value being used is the local one
|
// Check the value being used is the local one
|
||||||
assertMaxActiveFlows(orchestrator, localValue)
|
assertMaxActiveFlows(orchestrator, localValue)
|
||||||
|
|
||||||
// Remove local override for the maxActiveFlows
|
|
||||||
delete(remoteConfig.ConfigurationFlags, flags.MaxActiveFlows)
|
|
||||||
// Force a configuration refresh
|
|
||||||
err = orchestrator.updateIngress(remoteIngress, remoteWarpConfig)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
// Check the value being used is now the remote again
|
|
||||||
assertMaxActiveFlows(orchestrator, remoteValue)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func proxyHTTP(originProxy connection.OriginProxy, hostname string) (*http.Response, error) {
|
func proxyHTTP(originProxy connection.OriginProxy, hostname string) (*http.Response, error) {
|
||||||
@@ -546,6 +569,10 @@ func updateWithValidation(t *testing.T, orchestrator *Orchestrator, version int3
|
|||||||
|
|
||||||
// TestClosePreviousProxies makes sure proxies started in the previous configuration version are shutdown
|
// TestClosePreviousProxies makes sure proxies started in the previous configuration version are shutdown
|
||||||
func TestClosePreviousProxies(t *testing.T) {
|
func TestClosePreviousProxies(t *testing.T) {
|
||||||
|
originDialer := ingress.NewOriginDialer(ingress.OriginConfig{
|
||||||
|
DefaultDialer: testDefaultDialer,
|
||||||
|
TCPWriteTimeout: 1 * time.Second,
|
||||||
|
}, &testLogger)
|
||||||
var (
|
var (
|
||||||
hostname = "hello.tunnel1.org"
|
hostname = "hello.tunnel1.org"
|
||||||
configWithHelloWorld = []byte(fmt.Sprintf(`
|
configWithHelloWorld = []byte(fmt.Sprintf(`
|
||||||
@@ -577,6 +604,7 @@ func TestClosePreviousProxies(t *testing.T) {
|
|||||||
`)
|
`)
|
||||||
initConfig = &Config{
|
initConfig = &Config{
|
||||||
Ingress: &ingress.Ingress{},
|
Ingress: &ingress.Ingress{},
|
||||||
|
OriginDialerService: originDialer,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -638,8 +666,13 @@ func TestPersistentConnection(t *testing.T) {
|
|||||||
hostname = "http://ws.tunnel.org"
|
hostname = "http://ws.tunnel.org"
|
||||||
)
|
)
|
||||||
msg := t.Name()
|
msg := t.Name()
|
||||||
|
originDialer := ingress.NewOriginDialer(ingress.OriginConfig{
|
||||||
|
DefaultDialer: testDefaultDialer,
|
||||||
|
TCPWriteTimeout: 1 * time.Second,
|
||||||
|
}, &testLogger)
|
||||||
initConfig := &Config{
|
initConfig := &Config{
|
||||||
Ingress: &ingress.Ingress{},
|
Ingress: &ingress.Ingress{},
|
||||||
|
OriginDialerService: originDialer,
|
||||||
}
|
}
|
||||||
orchestrator, err := NewOrchestrator(t.Context(), initConfig, testTags, []ingress.Rule{}, &testLogger)
|
orchestrator, err := NewOrchestrator(t.Context(), initConfig, testTags, []ingress.Rule{}, &testLogger)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -752,8 +785,9 @@ func TestSerializeLocalConfig(t *testing.T) {
|
|||||||
ConfigurationFlags: map[string]string{"a": "b"},
|
ConfigurationFlags: map[string]string{"a": "b"},
|
||||||
}
|
}
|
||||||
|
|
||||||
result, _ := json.Marshal(c)
|
result, err := json.Marshal(c)
|
||||||
fmt.Println(string(result))
|
require.NoError(t, err)
|
||||||
|
require.JSONEq(t, `{"__configuration_flags":{"a":"b"},"ingress":[],"warp-routing":{"connectTimeout":0,"tcpKeepAlive":0}}`, string(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
func wsEcho(w http.ResponseWriter, r *http.Request) {
|
func wsEcho(w http.ResponseWriter, r *http.Request) {
|
||||||
|
@@ -5,11 +5,11 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/netip"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
pkgerrors "github.com/pkg/errors"
|
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"go.opentelemetry.io/otel/attribute"
|
"go.opentelemetry.io/otel/attribute"
|
||||||
"go.opentelemetry.io/otel/trace"
|
"go.opentelemetry.io/otel/trace"
|
||||||
@@ -35,7 +35,7 @@ const (
|
|||||||
// Proxy represents a means to Proxy between cloudflared and the origin services.
|
// Proxy represents a means to Proxy between cloudflared and the origin services.
|
||||||
type Proxy struct {
|
type Proxy struct {
|
||||||
ingressRules ingress.Ingress
|
ingressRules ingress.Ingress
|
||||||
warpRouting *ingress.WarpRoutingService
|
originDialer ingress.OriginTCPDialer
|
||||||
tags []pogs.Tag
|
tags []pogs.Tag
|
||||||
flowLimiter cfdflow.Limiter
|
flowLimiter cfdflow.Limiter
|
||||||
log *zerolog.Logger
|
log *zerolog.Logger
|
||||||
@@ -44,21 +44,19 @@ type Proxy struct {
|
|||||||
// NewOriginProxy returns a new instance of the Proxy struct.
|
// NewOriginProxy returns a new instance of the Proxy struct.
|
||||||
func NewOriginProxy(
|
func NewOriginProxy(
|
||||||
ingressRules ingress.Ingress,
|
ingressRules ingress.Ingress,
|
||||||
warpRouting ingress.WarpRoutingConfig,
|
originDialer ingress.OriginDialer,
|
||||||
tags []pogs.Tag,
|
tags []pogs.Tag,
|
||||||
flowLimiter cfdflow.Limiter,
|
flowLimiter cfdflow.Limiter,
|
||||||
writeTimeout time.Duration,
|
|
||||||
log *zerolog.Logger,
|
log *zerolog.Logger,
|
||||||
) *Proxy {
|
) *Proxy {
|
||||||
proxy := &Proxy{
|
proxy := &Proxy{
|
||||||
ingressRules: ingressRules,
|
ingressRules: ingressRules,
|
||||||
|
originDialer: originDialer,
|
||||||
tags: tags,
|
tags: tags,
|
||||||
flowLimiter: flowLimiter,
|
flowLimiter: flowLimiter,
|
||||||
log: log,
|
log: log,
|
||||||
}
|
}
|
||||||
|
|
||||||
proxy.warpRouting = ingress.NewWarpRoutingService(warpRouting, writeTimeout)
|
|
||||||
|
|
||||||
return proxy
|
return proxy
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,24 +144,18 @@ func (p *Proxy) ProxyHTTP(
|
|||||||
// ProxyTCP proxies to a TCP connection between the origin service and cloudflared.
|
// ProxyTCP proxies to a TCP connection between the origin service and cloudflared.
|
||||||
func (p *Proxy) ProxyTCP(
|
func (p *Proxy) ProxyTCP(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
rwa connection.ReadWriteAcker,
|
conn connection.ReadWriteAcker,
|
||||||
req *connection.TCPRequest,
|
req *connection.TCPRequest,
|
||||||
) error {
|
) error {
|
||||||
incrementTCPRequests()
|
incrementTCPRequests()
|
||||||
defer decrementTCPConcurrentRequests()
|
defer decrementTCPConcurrentRequests()
|
||||||
|
|
||||||
if p.warpRouting == nil {
|
|
||||||
err := errors.New(`cloudflared received a request from WARP client, but your configuration has disabled ingress from WARP clients. To enable this, set "warp-routing:\n\t enabled: true" in your config.yaml`)
|
|
||||||
p.log.Error().Msg(err.Error())
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
logger := newTCPLogger(p.log, req)
|
logger := newTCPLogger(p.log, req)
|
||||||
|
|
||||||
// Try to start a new flow
|
// Try to start a new flow
|
||||||
if err := p.flowLimiter.Acquire(management.TCP.String()); err != nil {
|
if err := p.flowLimiter.Acquire(management.TCP.String()); err != nil {
|
||||||
logger.Warn().Msg("Too many concurrent flows being handled, rejecting tcp proxy")
|
logger.Warn().Msg("Too many concurrent flows being handled, rejecting tcp proxy")
|
||||||
return pkgerrors.Wrap(err, "failed to start tcp flow due to rate limiting")
|
return errors.Wrap(err, "failed to start tcp flow due to rate limiting")
|
||||||
}
|
}
|
||||||
defer p.flowLimiter.Release()
|
defer p.flowLimiter.Release()
|
||||||
|
|
||||||
@@ -173,7 +165,14 @@ func (p *Proxy) ProxyTCP(
|
|||||||
tracedCtx := tracing.NewTracedContext(serveCtx, req.CfTraceID, &logger)
|
tracedCtx := tracing.NewTracedContext(serveCtx, req.CfTraceID, &logger)
|
||||||
logger.Debug().Msg("tcp proxy stream started")
|
logger.Debug().Msg("tcp proxy stream started")
|
||||||
|
|
||||||
if err := p.proxyStream(tracedCtx, rwa, req.Dest, p.warpRouting.Proxy, &logger); err != nil {
|
// Parse the destination into a netip.AddrPort
|
||||||
|
dest, err := netip.ParseAddrPort(req.Dest)
|
||||||
|
if err != nil {
|
||||||
|
logRequestError(&logger, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := p.proxyTCPStream(tracedCtx, conn, dest, p.originDialer, &logger); err != nil {
|
||||||
logRequestError(&logger, err)
|
logRequestError(&logger, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -279,14 +278,14 @@ func (p *Proxy) proxyStream(
|
|||||||
tr *tracing.TracedContext,
|
tr *tracing.TracedContext,
|
||||||
rwa connection.ReadWriteAcker,
|
rwa connection.ReadWriteAcker,
|
||||||
dest string,
|
dest string,
|
||||||
connectionProxy ingress.StreamBasedOriginProxy,
|
originDialer ingress.StreamBasedOriginProxy,
|
||||||
logger *zerolog.Logger,
|
logger *zerolog.Logger,
|
||||||
) error {
|
) error {
|
||||||
ctx := tr.Context
|
ctx := tr.Context
|
||||||
_, connectSpan := tr.Tracer().Start(ctx, "stream-connect")
|
_, connectSpan := tr.Tracer().Start(ctx, "stream-connect")
|
||||||
|
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
originConn, err := connectionProxy.EstablishConnection(ctx, dest, logger)
|
originConn, err := originDialer.EstablishConnection(ctx, dest, logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
connectStreamErrors.Inc()
|
connectStreamErrors.Inc()
|
||||||
tracing.EndWithErrorStatus(connectSpan, err)
|
tracing.EndWithErrorStatus(connectSpan, err)
|
||||||
@@ -310,6 +309,45 @@ func (p *Proxy) proxyStream(
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// proxyTCPStream proxies private network type TCP connections as a stream towards an available origin.
|
||||||
|
//
|
||||||
|
// This is different than proxyStream because it's not leveraged ingress rule services and uses the
|
||||||
|
// originDialer from OriginDialerService.
|
||||||
|
func (p *Proxy) proxyTCPStream(
|
||||||
|
tr *tracing.TracedContext,
|
||||||
|
tunnelConn connection.ReadWriteAcker,
|
||||||
|
dest netip.AddrPort,
|
||||||
|
originDialer ingress.OriginTCPDialer,
|
||||||
|
logger *zerolog.Logger,
|
||||||
|
) error {
|
||||||
|
ctx := tr.Context
|
||||||
|
_, connectSpan := tr.Tracer().Start(ctx, "stream-connect")
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
originConn, err := originDialer.DialTCP(ctx, dest)
|
||||||
|
if err != nil {
|
||||||
|
connectStreamErrors.Inc()
|
||||||
|
tracing.EndWithErrorStatus(connectSpan, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
connectSpan.End()
|
||||||
|
defer originConn.Close()
|
||||||
|
logger.Debug().Msg("origin connection established")
|
||||||
|
|
||||||
|
encodedSpans := tr.GetSpans()
|
||||||
|
|
||||||
|
if err := tunnelConn.AckConnection(encodedSpans); err != nil {
|
||||||
|
connectStreamErrors.Inc()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
connectLatency.Observe(float64(time.Since(start).Milliseconds()))
|
||||||
|
logger.Debug().Msg("proxy stream acknowledged")
|
||||||
|
|
||||||
|
stream.Pipe(tunnelConn, originConn, logger)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Proxy) proxyLocalRequest(proxy ingress.HTTPLocalProxy, w connection.ResponseWriter, req *http.Request, isWebsocket bool) {
|
func (p *Proxy) proxyLocalRequest(proxy ingress.HTTPLocalProxy, w connection.ResponseWriter, req *http.Request, isWebsocket bool) {
|
||||||
if isWebsocket {
|
if isWebsocket {
|
||||||
// These headers are added since they are stripped off during an eyeball request to origintunneld, but they
|
// These headers are added since they are stripped off during an eyeball request to origintunneld, but they
|
||||||
|
@@ -33,17 +33,17 @@ import (
|
|||||||
"github.com/cloudflare/cloudflared/connection"
|
"github.com/cloudflare/cloudflared/connection"
|
||||||
"github.com/cloudflare/cloudflared/hello"
|
"github.com/cloudflare/cloudflared/hello"
|
||||||
"github.com/cloudflare/cloudflared/ingress"
|
"github.com/cloudflare/cloudflared/ingress"
|
||||||
"github.com/cloudflare/cloudflared/logger"
|
|
||||||
"github.com/cloudflare/cloudflared/tracing"
|
"github.com/cloudflare/cloudflared/tracing"
|
||||||
"github.com/cloudflare/cloudflared/tunnelrpc/pogs"
|
"github.com/cloudflare/cloudflared/tunnelrpc/pogs"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
testTags = []pogs.Tag{{Name: "Name", Value: "value"}}
|
testTags = []pogs.Tag{{Name: "Name", Value: "value"}}
|
||||||
noWarpRouting = ingress.WarpRoutingConfig{}
|
testDefaultDialer = ingress.NewDialer(ingress.WarpRoutingConfig{
|
||||||
testWarpRouting = ingress.WarpRoutingConfig{
|
ConnectTimeout: config.CustomDuration{Duration: 1 * time.Second},
|
||||||
ConnectTimeout: config.CustomDuration{Duration: time.Second},
|
TCPKeepAlive: config.CustomDuration{Duration: 15 * time.Second},
|
||||||
}
|
MaxActiveFlows: 0,
|
||||||
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
type mockHTTPRespWriter struct {
|
type mockHTTPRespWriter struct {
|
||||||
@@ -162,7 +162,12 @@ func TestProxySingleOrigin(t *testing.T) {
|
|||||||
|
|
||||||
require.NoError(t, ingressRule.StartOrigins(&log, ctx.Done()))
|
require.NoError(t, ingressRule.StartOrigins(&log, ctx.Done()))
|
||||||
|
|
||||||
proxy := NewOriginProxy(ingressRule, noWarpRouting, testTags, cfdflow.NewLimiter(0), time.Duration(0), &log)
|
originDialer := ingress.NewOriginDialer(ingress.OriginConfig{
|
||||||
|
DefaultDialer: testDefaultDialer,
|
||||||
|
TCPWriteTimeout: 1 * time.Second,
|
||||||
|
}, &log)
|
||||||
|
|
||||||
|
proxy := NewOriginProxy(ingressRule, originDialer, testTags, cfdflow.NewLimiter(0), &log)
|
||||||
t.Run("testProxyHTTP", testProxyHTTP(proxy))
|
t.Run("testProxyHTTP", testProxyHTTP(proxy))
|
||||||
t.Run("testProxyWebsocket", testProxyWebsocket(proxy))
|
t.Run("testProxyWebsocket", testProxyWebsocket(proxy))
|
||||||
t.Run("testProxySSE", testProxySSE(proxy))
|
t.Run("testProxySSE", testProxySSE(proxy))
|
||||||
@@ -357,7 +362,7 @@ type MultipleIngressTest struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func runIngressTestScenarios(t *testing.T, unvalidatedIngress []config.UnvalidatedIngressRule, tests []MultipleIngressTest) {
|
func runIngressTestScenarios(t *testing.T, unvalidatedIngress []config.UnvalidatedIngressRule, tests []MultipleIngressTest) {
|
||||||
ingress, err := ingress.ParseIngress(&config.Configuration{
|
ingressRule, err := ingress.ParseIngress(&config.Configuration{
|
||||||
TunnelID: t.Name(),
|
TunnelID: t.Name(),
|
||||||
Ingress: unvalidatedIngress,
|
Ingress: unvalidatedIngress,
|
||||||
})
|
})
|
||||||
@@ -366,9 +371,14 @@ func runIngressTestScenarios(t *testing.T, unvalidatedIngress []config.Unvalidat
|
|||||||
log := zerolog.Nop()
|
log := zerolog.Nop()
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(t.Context())
|
ctx, cancel := context.WithCancel(t.Context())
|
||||||
require.NoError(t, ingress.StartOrigins(&log, ctx.Done()))
|
require.NoError(t, ingressRule.StartOrigins(&log, ctx.Done()))
|
||||||
|
|
||||||
proxy := NewOriginProxy(ingress, noWarpRouting, testTags, cfdflow.NewLimiter(0), time.Duration(0), &log)
|
originDialer := ingress.NewOriginDialer(ingress.OriginConfig{
|
||||||
|
DefaultDialer: testDefaultDialer,
|
||||||
|
TCPWriteTimeout: 1 * time.Second,
|
||||||
|
}, &log)
|
||||||
|
|
||||||
|
proxy := NewOriginProxy(ingressRule, originDialer, testTags, cfdflow.NewLimiter(0), &log)
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
responseWriter := newMockHTTPRespWriter()
|
responseWriter := newMockHTTPRespWriter()
|
||||||
@@ -416,7 +426,12 @@ func TestProxyError(t *testing.T) {
|
|||||||
|
|
||||||
log := zerolog.Nop()
|
log := zerolog.Nop()
|
||||||
|
|
||||||
proxy := NewOriginProxy(ing, noWarpRouting, testTags, cfdflow.NewLimiter(0), time.Duration(0), &log)
|
originDialer := ingress.NewOriginDialer(ingress.OriginConfig{
|
||||||
|
DefaultDialer: testDefaultDialer,
|
||||||
|
TCPWriteTimeout: 1 * time.Second,
|
||||||
|
}, &log)
|
||||||
|
|
||||||
|
proxy := NewOriginProxy(ing, originDialer, testTags, cfdflow.NewLimiter(0), &log)
|
||||||
|
|
||||||
responseWriter := newMockHTTPRespWriter()
|
responseWriter := newMockHTTPRespWriter()
|
||||||
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1", nil)
|
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1", nil)
|
||||||
@@ -467,7 +482,7 @@ func (r *replayer) Bytes() []byte {
|
|||||||
// WS - TCP: When a tcp based ingress is configured on the origin and the
|
// WS - TCP: When a tcp based ingress is configured on the origin and the
|
||||||
// eyeball sends tcp packets wrapped in websockets. (E.g: cloudflared access).
|
// eyeball sends tcp packets wrapped in websockets. (E.g: cloudflared access).
|
||||||
func TestConnections(t *testing.T) {
|
func TestConnections(t *testing.T) {
|
||||||
logger := logger.Create(nil)
|
log := zerolog.Nop()
|
||||||
replayer := &replayer{rw: bytes.NewBuffer([]byte{})}
|
replayer := &replayer{rw: bytes.NewBuffer([]byte{})}
|
||||||
type args struct {
|
type args struct {
|
||||||
ingressServiceScheme string
|
ingressServiceScheme string
|
||||||
@@ -475,9 +490,6 @@ func TestConnections(t *testing.T) {
|
|||||||
eyeballResponseWriter connection.ResponseWriter
|
eyeballResponseWriter connection.ResponseWriter
|
||||||
eyeballRequestBody io.ReadCloser
|
eyeballRequestBody io.ReadCloser
|
||||||
|
|
||||||
// Can be set to nil to show warp routing is not enabled.
|
|
||||||
warpRoutingService *ingress.WarpRoutingService
|
|
||||||
|
|
||||||
// eyeball connection type.
|
// eyeball connection type.
|
||||||
connectionType connection.Type
|
connectionType connection.Type
|
||||||
|
|
||||||
@@ -488,6 +500,11 @@ func TestConnections(t *testing.T) {
|
|||||||
flowLimiterResponse error
|
flowLimiterResponse error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
originDialer := ingress.NewOriginDialer(ingress.OriginConfig{
|
||||||
|
DefaultDialer: testDefaultDialer,
|
||||||
|
TCPWriteTimeout: 0,
|
||||||
|
}, &log)
|
||||||
|
|
||||||
type want struct {
|
type want struct {
|
||||||
message []byte
|
message []byte
|
||||||
headers http.Header
|
headers http.Header
|
||||||
@@ -530,7 +547,6 @@ func TestConnections(t *testing.T) {
|
|||||||
originService: runEchoTCPService,
|
originService: runEchoTCPService,
|
||||||
eyeballResponseWriter: newTCPRespWriter(replayer),
|
eyeballResponseWriter: newTCPRespWriter(replayer),
|
||||||
eyeballRequestBody: newTCPRequestBody([]byte("test2")),
|
eyeballRequestBody: newTCPRequestBody([]byte("test2")),
|
||||||
warpRoutingService: ingress.NewWarpRoutingService(testWarpRouting, time.Duration(0)),
|
|
||||||
connectionType: connection.TypeTCP,
|
connectionType: connection.TypeTCP,
|
||||||
requestHeaders: map[string][]string{
|
requestHeaders: map[string][]string{
|
||||||
"Cf-Cloudflared-Proxy-Src": {"non-blank-value"},
|
"Cf-Cloudflared-Proxy-Src": {"non-blank-value"},
|
||||||
@@ -548,7 +564,6 @@ func TestConnections(t *testing.T) {
|
|||||||
originService: runEchoWSService,
|
originService: runEchoWSService,
|
||||||
// eyeballResponseWriter gets set after roundtrip dial.
|
// eyeballResponseWriter gets set after roundtrip dial.
|
||||||
eyeballRequestBody: newPipedWSRequestBody([]byte("test3")),
|
eyeballRequestBody: newPipedWSRequestBody([]byte("test3")),
|
||||||
warpRoutingService: ingress.NewWarpRoutingService(testWarpRouting, time.Duration(0)),
|
|
||||||
requestHeaders: map[string][]string{
|
requestHeaders: map[string][]string{
|
||||||
"Cf-Cloudflared-Proxy-Src": {"non-blank-value"},
|
"Cf-Cloudflared-Proxy-Src": {"non-blank-value"},
|
||||||
},
|
},
|
||||||
@@ -601,23 +616,6 @@ func TestConnections(t *testing.T) {
|
|||||||
headers: map[string][]string{},
|
headers: map[string][]string{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "tcp-tcp proxy without warpRoutingService enabled",
|
|
||||||
args: args{
|
|
||||||
ingressServiceScheme: "tcp://",
|
|
||||||
originService: runEchoTCPService,
|
|
||||||
eyeballResponseWriter: newTCPRespWriter(replayer),
|
|
||||||
eyeballRequestBody: newTCPRequestBody([]byte("test2")),
|
|
||||||
connectionType: connection.TypeTCP,
|
|
||||||
requestHeaders: map[string][]string{
|
|
||||||
"Cf-Cloudflared-Proxy-Src": {"non-blank-value"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
want: want{
|
|
||||||
message: []byte{},
|
|
||||||
err: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "ws-ws proxy when origin is different",
|
name: "ws-ws proxy when origin is different",
|
||||||
args: args{
|
args: args{
|
||||||
@@ -670,7 +668,6 @@ func TestConnections(t *testing.T) {
|
|||||||
originService: runEchoTCPService,
|
originService: runEchoTCPService,
|
||||||
eyeballResponseWriter: newTCPRespWriter(replayer),
|
eyeballResponseWriter: newTCPRespWriter(replayer),
|
||||||
eyeballRequestBody: newTCPRequestBody([]byte("rate-limited")),
|
eyeballRequestBody: newTCPRequestBody([]byte("rate-limited")),
|
||||||
warpRoutingService: ingress.NewWarpRoutingService(testWarpRouting, time.Duration(0)),
|
|
||||||
connectionType: connection.TypeTCP,
|
connectionType: connection.TypeTCP,
|
||||||
requestHeaders: map[string][]string{
|
requestHeaders: map[string][]string{
|
||||||
"Cf-Cloudflared-Proxy-Src": {"non-blank-value"},
|
"Cf-Cloudflared-Proxy-Src": {"non-blank-value"},
|
||||||
@@ -693,7 +690,7 @@ func TestConnections(t *testing.T) {
|
|||||||
test.args.originService(t, ln)
|
test.args.originService(t, ln)
|
||||||
|
|
||||||
ingressRule := createSingleIngressConfig(t, test.args.ingressServiceScheme+ln.Addr().String())
|
ingressRule := createSingleIngressConfig(t, test.args.ingressServiceScheme+ln.Addr().String())
|
||||||
_ = ingressRule.StartOrigins(logger, ctx.Done())
|
_ = ingressRule.StartOrigins(&log, ctx.Done())
|
||||||
|
|
||||||
// Mock flow limiter
|
// Mock flow limiter
|
||||||
ctrl := gomock.NewController(t)
|
ctrl := gomock.NewController(t)
|
||||||
@@ -702,8 +699,7 @@ func TestConnections(t *testing.T) {
|
|||||||
flowLimiter.EXPECT().Acquire("tcp").AnyTimes().Return(test.args.flowLimiterResponse)
|
flowLimiter.EXPECT().Acquire("tcp").AnyTimes().Return(test.args.flowLimiterResponse)
|
||||||
flowLimiter.EXPECT().Release().AnyTimes()
|
flowLimiter.EXPECT().Release().AnyTimes()
|
||||||
|
|
||||||
proxy := NewOriginProxy(ingressRule, testWarpRouting, testTags, flowLimiter, time.Duration(0), logger)
|
proxy := NewOriginProxy(ingressRule, originDialer, testTags, flowLimiter, &log)
|
||||||
proxy.warpRouting = test.args.warpRoutingService
|
|
||||||
|
|
||||||
dest := ln.Addr().String()
|
dest := ln.Addr().String()
|
||||||
req, err := http.NewRequest(
|
req, err := http.NewRequest(
|
||||||
|
@@ -40,13 +40,13 @@ type SessionManager interface {
|
|||||||
type sessionManager struct {
|
type sessionManager struct {
|
||||||
sessions map[RequestID]Session
|
sessions map[RequestID]Session
|
||||||
mutex sync.RWMutex
|
mutex sync.RWMutex
|
||||||
originDialer ingress.UDPOriginProxy
|
originDialer ingress.OriginUDPDialer
|
||||||
limiter cfdflow.Limiter
|
limiter cfdflow.Limiter
|
||||||
metrics Metrics
|
metrics Metrics
|
||||||
log *zerolog.Logger
|
log *zerolog.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSessionManager(metrics Metrics, log *zerolog.Logger, originDialer ingress.UDPOriginProxy, limiter cfdflow.Limiter) SessionManager {
|
func NewSessionManager(metrics Metrics, log *zerolog.Logger, originDialer ingress.OriginUDPDialer, limiter cfdflow.Limiter) SessionManager {
|
||||||
return &sessionManager{
|
return &sessionManager{
|
||||||
sessions: make(map[RequestID]Session),
|
sessions: make(map[RequestID]Session),
|
||||||
originDialer: originDialer,
|
originDialer: originDialer,
|
||||||
|
@@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"go.uber.org/mock/gomock"
|
"go.uber.org/mock/gomock"
|
||||||
|
|
||||||
|
"github.com/cloudflare/cloudflared/config"
|
||||||
"github.com/cloudflare/cloudflared/mocks"
|
"github.com/cloudflare/cloudflared/mocks"
|
||||||
|
|
||||||
cfdflow "github.com/cloudflare/cloudflared/flow"
|
cfdflow "github.com/cloudflare/cloudflared/flow"
|
||||||
@@ -18,9 +19,21 @@ import (
|
|||||||
v3 "github.com/cloudflare/cloudflared/quic/v3"
|
v3 "github.com/cloudflare/cloudflared/quic/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
testDefaultDialer = ingress.NewDialer(ingress.WarpRoutingConfig{
|
||||||
|
ConnectTimeout: config.CustomDuration{Duration: 1 * time.Second},
|
||||||
|
TCPKeepAlive: config.CustomDuration{Duration: 15 * time.Second},
|
||||||
|
MaxActiveFlows: 0,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
func TestRegisterSession(t *testing.T) {
|
func TestRegisterSession(t *testing.T) {
|
||||||
log := zerolog.Nop()
|
log := zerolog.Nop()
|
||||||
manager := v3.NewSessionManager(&noopMetrics{}, &log, ingress.DefaultUDPDialer, cfdflow.NewLimiter(0))
|
originDialerService := ingress.NewOriginDialer(ingress.OriginConfig{
|
||||||
|
DefaultDialer: testDefaultDialer,
|
||||||
|
TCPWriteTimeout: 0,
|
||||||
|
}, &log)
|
||||||
|
manager := v3.NewSessionManager(&noopMetrics{}, &log, originDialerService, cfdflow.NewLimiter(0))
|
||||||
|
|
||||||
request := v3.UDPSessionRegistrationDatagram{
|
request := v3.UDPSessionRegistrationDatagram{
|
||||||
RequestID: testRequestID,
|
RequestID: testRequestID,
|
||||||
@@ -76,7 +89,11 @@ func TestRegisterSession(t *testing.T) {
|
|||||||
|
|
||||||
func TestGetSession_Empty(t *testing.T) {
|
func TestGetSession_Empty(t *testing.T) {
|
||||||
log := zerolog.Nop()
|
log := zerolog.Nop()
|
||||||
manager := v3.NewSessionManager(&noopMetrics{}, &log, ingress.DefaultUDPDialer, cfdflow.NewLimiter(0))
|
originDialerService := ingress.NewOriginDialer(ingress.OriginConfig{
|
||||||
|
DefaultDialer: testDefaultDialer,
|
||||||
|
TCPWriteTimeout: 0,
|
||||||
|
}, &log)
|
||||||
|
manager := v3.NewSessionManager(&noopMetrics{}, &log, originDialerService, cfdflow.NewLimiter(0))
|
||||||
|
|
||||||
_, err := manager.GetSession(testRequestID)
|
_, err := manager.GetSession(testRequestID)
|
||||||
if !errors.Is(err, v3.ErrSessionNotFound) {
|
if !errors.Is(err, v3.ErrSessionNotFound) {
|
||||||
@@ -86,6 +103,10 @@ func TestGetSession_Empty(t *testing.T) {
|
|||||||
|
|
||||||
func TestRegisterSessionRateLimit(t *testing.T) {
|
func TestRegisterSessionRateLimit(t *testing.T) {
|
||||||
log := zerolog.Nop()
|
log := zerolog.Nop()
|
||||||
|
originDialerService := ingress.NewOriginDialer(ingress.OriginConfig{
|
||||||
|
DefaultDialer: testDefaultDialer,
|
||||||
|
TCPWriteTimeout: 0,
|
||||||
|
}, &log)
|
||||||
ctrl := gomock.NewController(t)
|
ctrl := gomock.NewController(t)
|
||||||
|
|
||||||
flowLimiterMock := mocks.NewMockLimiter(ctrl)
|
flowLimiterMock := mocks.NewMockLimiter(ctrl)
|
||||||
@@ -93,7 +114,7 @@ func TestRegisterSessionRateLimit(t *testing.T) {
|
|||||||
flowLimiterMock.EXPECT().Acquire("udp").Return(cfdflow.ErrTooManyActiveFlows)
|
flowLimiterMock.EXPECT().Acquire("udp").Return(cfdflow.ErrTooManyActiveFlows)
|
||||||
flowLimiterMock.EXPECT().Release().Times(0)
|
flowLimiterMock.EXPECT().Release().Times(0)
|
||||||
|
|
||||||
manager := v3.NewSessionManager(&noopMetrics{}, &log, ingress.DefaultUDPDialer, flowLimiterMock)
|
manager := v3.NewSessionManager(&noopMetrics{}, &log, originDialerService, flowLimiterMock)
|
||||||
|
|
||||||
request := v3.UDPSessionRegistrationDatagram{
|
request := v3.UDPSessionRegistrationDatagram{
|
||||||
RequestID: testRequestID,
|
RequestID: testRequestID,
|
||||||
|
@@ -88,7 +88,11 @@ func (m *mockEyeball) SendICMPTTLExceed(icmp *packet.ICMP, rawPacket packet.RawP
|
|||||||
|
|
||||||
func TestDatagramConn_New(t *testing.T) {
|
func TestDatagramConn_New(t *testing.T) {
|
||||||
log := zerolog.Nop()
|
log := zerolog.Nop()
|
||||||
conn := v3.NewDatagramConn(newMockQuicConn(), v3.NewSessionManager(&noopMetrics{}, &log, ingress.DefaultUDPDialer, cfdflow.NewLimiter(0)), &noopICMPRouter{}, 0, &noopMetrics{}, &log)
|
originDialerService := ingress.NewOriginDialer(ingress.OriginConfig{
|
||||||
|
DefaultDialer: testDefaultDialer,
|
||||||
|
TCPWriteTimeout: 0,
|
||||||
|
}, &log)
|
||||||
|
conn := v3.NewDatagramConn(newMockQuicConn(), v3.NewSessionManager(&noopMetrics{}, &log, originDialerService, cfdflow.NewLimiter(0)), &noopICMPRouter{}, 0, &noopMetrics{}, &log)
|
||||||
if conn == nil {
|
if conn == nil {
|
||||||
t.Fatal("expected valid connection")
|
t.Fatal("expected valid connection")
|
||||||
}
|
}
|
||||||
@@ -96,8 +100,12 @@ func TestDatagramConn_New(t *testing.T) {
|
|||||||
|
|
||||||
func TestDatagramConn_SendUDPSessionDatagram(t *testing.T) {
|
func TestDatagramConn_SendUDPSessionDatagram(t *testing.T) {
|
||||||
log := zerolog.Nop()
|
log := zerolog.Nop()
|
||||||
|
originDialerService := ingress.NewOriginDialer(ingress.OriginConfig{
|
||||||
|
DefaultDialer: testDefaultDialer,
|
||||||
|
TCPWriteTimeout: 0,
|
||||||
|
}, &log)
|
||||||
quic := newMockQuicConn()
|
quic := newMockQuicConn()
|
||||||
conn := v3.NewDatagramConn(quic, v3.NewSessionManager(&noopMetrics{}, &log, ingress.DefaultUDPDialer, cfdflow.NewLimiter(0)), &noopICMPRouter{}, 0, &noopMetrics{}, &log)
|
conn := v3.NewDatagramConn(quic, v3.NewSessionManager(&noopMetrics{}, &log, originDialerService, cfdflow.NewLimiter(0)), &noopICMPRouter{}, 0, &noopMetrics{}, &log)
|
||||||
|
|
||||||
payload := []byte{0xef, 0xef}
|
payload := []byte{0xef, 0xef}
|
||||||
err := conn.SendUDPSessionDatagram(payload)
|
err := conn.SendUDPSessionDatagram(payload)
|
||||||
@@ -111,8 +119,12 @@ func TestDatagramConn_SendUDPSessionDatagram(t *testing.T) {
|
|||||||
|
|
||||||
func TestDatagramConn_SendUDPSessionResponse(t *testing.T) {
|
func TestDatagramConn_SendUDPSessionResponse(t *testing.T) {
|
||||||
log := zerolog.Nop()
|
log := zerolog.Nop()
|
||||||
|
originDialerService := ingress.NewOriginDialer(ingress.OriginConfig{
|
||||||
|
DefaultDialer: testDefaultDialer,
|
||||||
|
TCPWriteTimeout: 0,
|
||||||
|
}, &log)
|
||||||
quic := newMockQuicConn()
|
quic := newMockQuicConn()
|
||||||
conn := v3.NewDatagramConn(quic, v3.NewSessionManager(&noopMetrics{}, &log, ingress.DefaultUDPDialer, cfdflow.NewLimiter(0)), &noopICMPRouter{}, 0, &noopMetrics{}, &log)
|
conn := v3.NewDatagramConn(quic, v3.NewSessionManager(&noopMetrics{}, &log, originDialerService, cfdflow.NewLimiter(0)), &noopICMPRouter{}, 0, &noopMetrics{}, &log)
|
||||||
|
|
||||||
err := conn.SendUDPSessionResponse(testRequestID, v3.ResponseDestinationUnreachable)
|
err := conn.SendUDPSessionResponse(testRequestID, v3.ResponseDestinationUnreachable)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -133,8 +145,12 @@ func TestDatagramConn_SendUDPSessionResponse(t *testing.T) {
|
|||||||
|
|
||||||
func TestDatagramConnServe_ApplicationClosed(t *testing.T) {
|
func TestDatagramConnServe_ApplicationClosed(t *testing.T) {
|
||||||
log := zerolog.Nop()
|
log := zerolog.Nop()
|
||||||
|
originDialerService := ingress.NewOriginDialer(ingress.OriginConfig{
|
||||||
|
DefaultDialer: testDefaultDialer,
|
||||||
|
TCPWriteTimeout: 0,
|
||||||
|
}, &log)
|
||||||
quic := newMockQuicConn()
|
quic := newMockQuicConn()
|
||||||
conn := v3.NewDatagramConn(quic, v3.NewSessionManager(&noopMetrics{}, &log, ingress.DefaultUDPDialer, cfdflow.NewLimiter(0)), &noopICMPRouter{}, 0, &noopMetrics{}, &log)
|
conn := v3.NewDatagramConn(quic, v3.NewSessionManager(&noopMetrics{}, &log, originDialerService, cfdflow.NewLimiter(0)), &noopICMPRouter{}, 0, &noopMetrics{}, &log)
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(t.Context(), 1*time.Second)
|
ctx, cancel := context.WithTimeout(t.Context(), 1*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
@@ -146,11 +162,15 @@ func TestDatagramConnServe_ApplicationClosed(t *testing.T) {
|
|||||||
|
|
||||||
func TestDatagramConnServe_ConnectionClosed(t *testing.T) {
|
func TestDatagramConnServe_ConnectionClosed(t *testing.T) {
|
||||||
log := zerolog.Nop()
|
log := zerolog.Nop()
|
||||||
|
originDialerService := ingress.NewOriginDialer(ingress.OriginConfig{
|
||||||
|
DefaultDialer: testDefaultDialer,
|
||||||
|
TCPWriteTimeout: 0,
|
||||||
|
}, &log)
|
||||||
quic := newMockQuicConn()
|
quic := newMockQuicConn()
|
||||||
ctx, cancel := context.WithTimeout(t.Context(), 1*time.Second)
|
ctx, cancel := context.WithTimeout(t.Context(), 1*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
quic.ctx = ctx
|
quic.ctx = ctx
|
||||||
conn := v3.NewDatagramConn(quic, v3.NewSessionManager(&noopMetrics{}, &log, ingress.DefaultUDPDialer, cfdflow.NewLimiter(0)), &noopICMPRouter{}, 0, &noopMetrics{}, &log)
|
conn := v3.NewDatagramConn(quic, v3.NewSessionManager(&noopMetrics{}, &log, originDialerService, cfdflow.NewLimiter(0)), &noopICMPRouter{}, 0, &noopMetrics{}, &log)
|
||||||
|
|
||||||
err := conn.Serve(t.Context())
|
err := conn.Serve(t.Context())
|
||||||
if !errors.Is(err, context.DeadlineExceeded) {
|
if !errors.Is(err, context.DeadlineExceeded) {
|
||||||
@@ -160,8 +180,12 @@ func TestDatagramConnServe_ConnectionClosed(t *testing.T) {
|
|||||||
|
|
||||||
func TestDatagramConnServe_ReceiveDatagramError(t *testing.T) {
|
func TestDatagramConnServe_ReceiveDatagramError(t *testing.T) {
|
||||||
log := zerolog.Nop()
|
log := zerolog.Nop()
|
||||||
|
originDialerService := ingress.NewOriginDialer(ingress.OriginConfig{
|
||||||
|
DefaultDialer: testDefaultDialer,
|
||||||
|
TCPWriteTimeout: 0,
|
||||||
|
}, &log)
|
||||||
quic := &mockQuicConnReadError{err: net.ErrClosed}
|
quic := &mockQuicConnReadError{err: net.ErrClosed}
|
||||||
conn := v3.NewDatagramConn(quic, v3.NewSessionManager(&noopMetrics{}, &log, ingress.DefaultUDPDialer, cfdflow.NewLimiter(0)), &noopICMPRouter{}, 0, &noopMetrics{}, &log)
|
conn := v3.NewDatagramConn(quic, v3.NewSessionManager(&noopMetrics{}, &log, originDialerService, cfdflow.NewLimiter(0)), &noopICMPRouter{}, 0, &noopMetrics{}, &log)
|
||||||
|
|
||||||
err := conn.Serve(t.Context())
|
err := conn.Serve(t.Context())
|
||||||
if !errors.Is(err, net.ErrClosed) {
|
if !errors.Is(err, net.ErrClosed) {
|
||||||
|
@@ -4,7 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -14,8 +13,6 @@ import (
|
|||||||
|
|
||||||
"github.com/cloudflare/cloudflared/connection"
|
"github.com/cloudflare/cloudflared/connection"
|
||||||
"github.com/cloudflare/cloudflared/edgediscovery"
|
"github.com/cloudflare/cloudflared/edgediscovery"
|
||||||
"github.com/cloudflare/cloudflared/ingress"
|
|
||||||
"github.com/cloudflare/cloudflared/ingress/origins"
|
|
||||||
"github.com/cloudflare/cloudflared/orchestration"
|
"github.com/cloudflare/cloudflared/orchestration"
|
||||||
v3 "github.com/cloudflare/cloudflared/quic/v3"
|
v3 "github.com/cloudflare/cloudflared/quic/v3"
|
||||||
"github.com/cloudflare/cloudflared/retry"
|
"github.com/cloudflare/cloudflared/retry"
|
||||||
@@ -81,16 +78,11 @@ func NewSupervisor(config *TunnelConfig, orchestrator *orchestration.Orchestrato
|
|||||||
|
|
||||||
datagramMetrics := v3.NewMetrics(prometheus.DefaultRegisterer)
|
datagramMetrics := v3.NewMetrics(prometheus.DefaultRegisterer)
|
||||||
|
|
||||||
// Setup the reserved virtual origins
|
sessionManager := v3.NewSessionManager(datagramMetrics, config.Log, config.OriginDialerService, orchestrator.GetFlowLimiter())
|
||||||
reservedServices := map[netip.AddrPort]ingress.UDPOriginProxy{}
|
|
||||||
reservedServices[origins.VirtualDNSServiceAddr] = config.OriginDNSService
|
|
||||||
ingressUDPService := ingress.NewUDPOriginService(reservedServices, config.Log)
|
|
||||||
sessionManager := v3.NewSessionManager(datagramMetrics, config.Log, ingressUDPService, orchestrator.GetFlowLimiter())
|
|
||||||
|
|
||||||
edgeTunnelServer := EdgeTunnelServer{
|
edgeTunnelServer := EdgeTunnelServer{
|
||||||
config: config,
|
config: config,
|
||||||
orchestrator: orchestrator,
|
orchestrator: orchestrator,
|
||||||
ingressUDPProxy: ingressUDPService,
|
|
||||||
sessionManager: sessionManager,
|
sessionManager: sessionManager,
|
||||||
datagramMetrics: datagramMetrics,
|
datagramMetrics: datagramMetrics,
|
||||||
edgeAddrs: edgeIPs,
|
edgeAddrs: edgeIPs,
|
||||||
|
@@ -66,6 +66,7 @@ type TunnelConfig struct {
|
|||||||
EdgeTLSConfigs map[connection.Protocol]*tls.Config
|
EdgeTLSConfigs map[connection.Protocol]*tls.Config
|
||||||
ICMPRouterServer ingress.ICMPRouterServer
|
ICMPRouterServer ingress.ICMPRouterServer
|
||||||
OriginDNSService *origins.DNSResolverService
|
OriginDNSService *origins.DNSResolverService
|
||||||
|
OriginDialerService *ingress.OriginDialerService
|
||||||
|
|
||||||
RPCTimeout time.Duration
|
RPCTimeout time.Duration
|
||||||
WriteStreamTimeout time.Duration
|
WriteStreamTimeout time.Duration
|
||||||
@@ -168,7 +169,6 @@ func (f *ipAddrFallback) ShouldGetNewAddress(connIndex uint8, err error) (needsN
|
|||||||
type EdgeTunnelServer struct {
|
type EdgeTunnelServer struct {
|
||||||
config *TunnelConfig
|
config *TunnelConfig
|
||||||
orchestrator *orchestration.Orchestrator
|
orchestrator *orchestration.Orchestrator
|
||||||
ingressUDPProxy ingress.UDPOriginProxy
|
|
||||||
sessionManager v3.SessionManager
|
sessionManager v3.SessionManager
|
||||||
datagramMetrics v3.Metrics
|
datagramMetrics v3.Metrics
|
||||||
edgeAddrHandler EdgeAddrHandler
|
edgeAddrHandler EdgeAddrHandler
|
||||||
@@ -616,7 +616,7 @@ func (e *EdgeTunnelServer) serveQUIC(
|
|||||||
datagramSessionManager = connection.NewDatagramV2Connection(
|
datagramSessionManager = connection.NewDatagramV2Connection(
|
||||||
ctx,
|
ctx,
|
||||||
conn,
|
conn,
|
||||||
e.ingressUDPProxy,
|
e.config.OriginDialerService,
|
||||||
e.config.ICMPRouterServer,
|
e.config.ICMPRouterServer,
|
||||||
connIndex,
|
connIndex,
|
||||||
e.config.RPCTimeout,
|
e.config.RPCTimeout,
|
||||||
|
Reference in New Issue
Block a user