Skip to content

Commit 7d68b2c

Browse files
authored
Add passthrough for gRPC connection when using a proxy (#1375)
1 parent 7d08f5d commit 7d68b2c

File tree

2 files changed

+168
-5
lines changed

2 files changed

+168
-5
lines changed

internal/grpc/grpc.go

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,18 +73,15 @@ var (
7373
func NewGrpcConnection(ctx context.Context, agentConfig *config.Config,
7474
commandConfig *config.Command,
7575
) (*GrpcConnection, error) {
76-
if commandConfig == nil || commandConfig.Server.Type != config.Grpc {
76+
if commandConfig == nil || commandConfig.Server == nil || commandConfig.Server.Type != config.Grpc {
7777
return nil, errors.New("invalid command server settings")
7878
}
7979

8080
grpcConnection := &GrpcConnection{
8181
commandConfig: commandConfig,
8282
}
8383

84-
serverAddr := net.JoinHostPort(
85-
commandConfig.Server.Host,
86-
strconv.Itoa(commandConfig.Server.Port),
87-
)
84+
serverAddr := serverAddress(ctx, commandConfig)
8885

8986
slog.InfoContext(ctx, "Dialing grpc server", "server_addr", serverAddr)
9087

@@ -406,3 +403,21 @@ func tlsConfigForCredentials(c *config.TLSConfig) (*tls.Config, error) {
406403

407404
return tlsConfig, nil
408405
}
406+
407+
func serverAddress(ctx context.Context, commandConfig *config.Command) string {
408+
serverAddr := net.JoinHostPort(
409+
commandConfig.Server.Host,
410+
strconv.Itoa(commandConfig.Server.Port),
411+
)
412+
413+
// If using proxy, modify the server address to use passthrough resolver
414+
// This prevents gRPC from trying to resolve DNS before calling our dialer
415+
if commandConfig.Server.Proxy != nil &&
416+
commandConfig.Server.Proxy.URL != "" &&
417+
!strings.HasPrefix(serverAddr, "passthrough:///") {
418+
serverAddr = "passthrough:///" + serverAddr
419+
slog.InfoContext(ctx, "Using passthrough resolver for proxy", "updated_server_address", serverAddr)
420+
}
421+
422+
return serverAddr
423+
}

internal/grpc/grpc_test.go

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,3 +490,151 @@ func Test_tlsConfig(t *testing.T) {
490490
})
491491
}
492492
}
493+
494+
func Test_serverAddress(t *testing.T) {
495+
ctx := context.Background()
496+
497+
tests := []struct {
498+
commandConfig *config.Command
499+
name string
500+
expectedAddr string
501+
logMessage string
502+
expectLog bool
503+
}{
504+
{
505+
name: "Test 1: No proxy configuration",
506+
commandConfig: &config.Command{
507+
Server: &config.ServerConfig{
508+
Host: "example.com",
509+
Port: 443,
510+
Type: config.Grpc,
511+
},
512+
},
513+
expectedAddr: "example.com:443",
514+
},
515+
{
516+
name: "Test 2: Proxy with nil proxy config",
517+
commandConfig: &config.Command{
518+
Server: &config.ServerConfig{
519+
Host: "example.com",
520+
Port: 443,
521+
Type: config.Grpc,
522+
Proxy: nil,
523+
},
524+
},
525+
expectedAddr: "example.com:443",
526+
},
527+
{
528+
name: "Test 3: Proxy with empty URL",
529+
commandConfig: &config.Command{
530+
Server: &config.ServerConfig{
531+
Host: "example.com",
532+
Port: 443,
533+
Type: config.Grpc,
534+
Proxy: &config.Proxy{
535+
URL: "",
536+
},
537+
},
538+
},
539+
expectedAddr: "example.com:443",
540+
},
541+
{
542+
name: "Test 4: Proxy with valid URL",
543+
commandConfig: &config.Command{
544+
Server: &config.ServerConfig{
545+
Host: "agent.connect.nginxlab.net",
546+
Port: 443,
547+
Type: config.Grpc,
548+
Proxy: &config.Proxy{
549+
URL: "http://squid-container:3128",
550+
},
551+
},
552+
},
553+
expectedAddr: "passthrough:///agent.connect.nginxlab.net:443",
554+
},
555+
{
556+
name: "Test 5: IPv6 address with proxy",
557+
commandConfig: &config.Command{
558+
Server: &config.ServerConfig{
559+
Host: "2001:db8::1",
560+
Port: 443,
561+
Type: config.Grpc,
562+
Proxy: &config.Proxy{
563+
URL: "http://proxy.example.com:8080",
564+
},
565+
},
566+
},
567+
expectedAddr: "passthrough:///[2001:db8::1]:443",
568+
},
569+
{
570+
name: "Test 6: Already has passthrough prefix - no double prefix",
571+
commandConfig: &config.Command{
572+
Server: &config.ServerConfig{
573+
Host: "example.com",
574+
Port: 443,
575+
Type: config.Grpc,
576+
Proxy: &config.Proxy{
577+
URL: "http://proxy.example.com:8080",
578+
},
579+
},
580+
},
581+
expectedAddr: "passthrough:///example.com:443",
582+
},
583+
{
584+
name: "Test 7: Localhost with proxy",
585+
commandConfig: &config.Command{
586+
Server: &config.ServerConfig{
587+
Host: "localhost",
588+
Port: 8080,
589+
Type: config.Grpc,
590+
Proxy: &config.Proxy{
591+
URL: "http://proxy.local:3128",
592+
AuthMethod: "basic",
593+
Username: "user",
594+
Password: "pass",
595+
},
596+
},
597+
},
598+
expectedAddr: "passthrough:///localhost:8080",
599+
},
600+
{
601+
name: "Test 8: Custom port with proxy",
602+
commandConfig: &config.Command{
603+
Server: &config.ServerConfig{
604+
Host: "api.example.com",
605+
Port: 9090,
606+
Type: config.Grpc,
607+
Proxy: &config.Proxy{
608+
URL: "https://secure-proxy.example.com:8443",
609+
Timeout: 30000000000, // 30 seconds in nanoseconds
610+
},
611+
},
612+
},
613+
expectedAddr: "passthrough:///api.example.com:9090",
614+
},
615+
{
616+
name: "Test 9: Edge case - hostname that looks like passthrough but isn't",
617+
commandConfig: &config.Command{
618+
Server: &config.ServerConfig{
619+
Host: "passthrough.example.com", // hostname that contains "passthrough" but isn't a passthrough URL
620+
Port: 443,
621+
Type: config.Grpc,
622+
Proxy: &config.Proxy{
623+
URL: "http://proxy.example.com:8080",
624+
},
625+
},
626+
},
627+
expectedAddr: "passthrough:///passthrough.example.com:443",
628+
},
629+
}
630+
631+
for _, tt := range tests {
632+
t.Run(tt.name, func(t *testing.T) {
633+
// Call the function
634+
result := serverAddress(ctx, tt.commandConfig)
635+
636+
// Verify the result
637+
assert.Equal(t, tt.expectedAddr, result, "serverAddress() should return expected address")
638+
})
639+
}
640+
}

0 commit comments

Comments
 (0)