mirror of
https://github.com/caddyserver/caddy.git
synced 2026-03-17 14:34:03 +00:00
reverseproxy: fix tls dialing w/ proxy protocol (#7508)
This commit is contained in:
committed by
GitHub
parent
6610e2f1bd
commit
d7b21c6104
@@ -412,8 +412,13 @@ func (h *HTTPTransport) NewTransport(caddyCtx caddy.Context) (*http.Transport, e
|
||||
return nil, fmt.Errorf("making TLS client config: %v", err)
|
||||
}
|
||||
|
||||
// servername has a placeholder, so we need to replace it
|
||||
if strings.Contains(h.TLS.ServerName, "{") {
|
||||
serverNameHasPlaceholder := strings.Contains(h.TLS.ServerName, "{")
|
||||
|
||||
// We need to use custom DialTLSContext if:
|
||||
// 1. ServerName has a placeholder that needs to be replaced at request-time, OR
|
||||
// 2. ProxyProtocol is enabled, because req.URL.Host is modified to include
|
||||
// client address info with "->" separator which breaks Go's address parsing
|
||||
if serverNameHasPlaceholder || h.ProxyProtocol != "" {
|
||||
rt.DialTLSContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
// reuses the dialer from above to establish a plaintext connection
|
||||
conn, err := dialContext(ctx, network, addr)
|
||||
@@ -422,9 +427,11 @@ func (h *HTTPTransport) NewTransport(caddyCtx caddy.Context) (*http.Transport, e
|
||||
}
|
||||
|
||||
// but add our own handshake logic
|
||||
repl := ctx.Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
|
||||
tlsConfig := rt.TLSClientConfig.Clone()
|
||||
tlsConfig.ServerName = repl.ReplaceAll(tlsConfig.ServerName, "")
|
||||
if serverNameHasPlaceholder {
|
||||
repl := ctx.Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
|
||||
tlsConfig.ServerName = repl.ReplaceAll(tlsConfig.ServerName, "")
|
||||
}
|
||||
|
||||
// h1 only
|
||||
if caddyhttp.GetVar(ctx, tlsH1OnlyVarKey) == true {
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
package reverseproxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/caddyserver/caddy/v2"
|
||||
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
|
||||
)
|
||||
|
||||
@@ -115,3 +117,81 @@ func TestHTTPTransport_RequestHeaderOps_TLS(t *testing.T) {
|
||||
t.Fatalf("unexpected Host value; want placeholder, got: %s", got)
|
||||
}
|
||||
}
|
||||
|
||||
// TestHTTPTransport_DialTLSContext_ProxyProtocol verifies that when TLS and
|
||||
// ProxyProtocol are both enabled, DialTLSContext is set. This is critical because
|
||||
// ProxyProtocol modifies req.URL.Host to include client info with "->" separator
|
||||
// (e.g., "[2001:db8::1]:12345->127.0.0.1:443"), which breaks Go's address parsing.
|
||||
// Without a custom DialTLSContext, Go's HTTP library would fail with
|
||||
// "too many colons in address" when trying to parse the mangled host.
|
||||
func TestHTTPTransport_DialTLSContext_ProxyProtocol(t *testing.T) {
|
||||
ctx, cancel := caddy.NewContext(caddy.Context{Context: context.Background()})
|
||||
defer cancel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
tls *TLSConfig
|
||||
proxyProtocol string
|
||||
serverNameHasPlaceholder bool
|
||||
expectDialTLSContext bool
|
||||
}{
|
||||
{
|
||||
name: "no TLS, no proxy protocol",
|
||||
tls: nil,
|
||||
proxyProtocol: "",
|
||||
expectDialTLSContext: false,
|
||||
},
|
||||
{
|
||||
name: "TLS without proxy protocol",
|
||||
tls: &TLSConfig{},
|
||||
proxyProtocol: "",
|
||||
expectDialTLSContext: false,
|
||||
},
|
||||
{
|
||||
name: "TLS with proxy protocol v1",
|
||||
tls: &TLSConfig{},
|
||||
proxyProtocol: "v1",
|
||||
expectDialTLSContext: true,
|
||||
},
|
||||
{
|
||||
name: "TLS with proxy protocol v2",
|
||||
tls: &TLSConfig{},
|
||||
proxyProtocol: "v2",
|
||||
expectDialTLSContext: true,
|
||||
},
|
||||
{
|
||||
name: "TLS with placeholder ServerName",
|
||||
tls: &TLSConfig{ServerName: "{http.request.host}"},
|
||||
proxyProtocol: "",
|
||||
serverNameHasPlaceholder: true,
|
||||
expectDialTLSContext: true,
|
||||
},
|
||||
{
|
||||
name: "TLS with placeholder ServerName and proxy protocol",
|
||||
tls: &TLSConfig{ServerName: "{http.request.host}"},
|
||||
proxyProtocol: "v2",
|
||||
serverNameHasPlaceholder: true,
|
||||
expectDialTLSContext: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ht := &HTTPTransport{
|
||||
TLS: tt.tls,
|
||||
ProxyProtocol: tt.proxyProtocol,
|
||||
}
|
||||
|
||||
rt, err := ht.NewTransport(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("NewTransport() error = %v", err)
|
||||
}
|
||||
|
||||
hasDialTLSContext := rt.DialTLSContext != nil
|
||||
if hasDialTLSContext != tt.expectDialTLSContext {
|
||||
t.Errorf("DialTLSContext set = %v, want %v", hasDialTLSContext, tt.expectDialTLSContext)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user