From 793be85e8c6bd4c75e0f8b05d2690ac286a674be Mon Sep 17 00:00:00 2001 From: Sahilb315 Date: Thu, 12 Feb 2026 17:10:12 +0530 Subject: [PATCH 1/2] verify upstream certs & reject unverified --- proxy/proxy.go | 39 +++++++++++++++++++++++++++++++++ proxy/proxy_test.go | 53 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 proxy/proxy_test.go diff --git a/proxy/proxy.go b/proxy/proxy.go index 64faaa4..6107e18 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -96,6 +96,7 @@ func NewProxyServer(config *ProxyConfig) (ProxyServer, error) { proxy := goproxy.NewProxyHttpServer() proxy.Logger = &goproxyLoggerWrapper{} + proxy.Tr = newUpstreamTransport(config) // Set verbose to true for verbose logging. // Logging is handled by our own logger which has log level controls. @@ -131,6 +132,44 @@ func NewProxyServer(config *ProxyConfig) (ProxyServer, error) { return ps, nil } +func newUpstreamTransport(config *ProxyConfig) *http.Transport { + dialer := &net.Dialer{ + Timeout: config.ConnectTimeout, + } + + defaultTransport, ok := http.DefaultTransport.(*http.Transport) + if !ok { + return &http.Transport{ + Proxy: http.ProxyFromEnvironment, + DialContext: dialer.DialContext, + TLSHandshakeTimeout: config.ConnectTimeout, + TLSClientConfig: &tls.Config{ + MinVersion: tls.VersionTLS12, + InsecureSkipVerify: false, + }, + } + } + + transport := defaultTransport.Clone() + transport.Proxy = http.ProxyFromEnvironment + transport.DialContext = dialer.DialContext + transport.TLSHandshakeTimeout = config.ConnectTimeout + + tlsConfig := &tls.Config{MinVersion: tls.VersionTLS12} + if transport.TLSClientConfig != nil { + tlsConfig = transport.TLSClientConfig.Clone() + if tlsConfig.MinVersion < tls.VersionTLS12 { + tlsConfig.MinVersion = tls.VersionTLS12 + } + } + + // Explicitly enforce upstream certificate validation. + tlsConfig.InsecureSkipVerify = false + transport.TLSClientConfig = tlsConfig + + return transport +} + func (ps *proxyServer) Start() error { listener, err := net.Listen("tcp", ps.config.ListenAddr) if err != nil { diff --git a/proxy/proxy_test.go b/proxy/proxy_test.go new file mode 100644 index 0000000..64d0f45 --- /dev/null +++ b/proxy/proxy_test.go @@ -0,0 +1,53 @@ +package proxy + +import ( + "crypto/tls" + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestNewProxyServerSecuresUpstreamTLSConfig(t *testing.T) { + server, err := NewProxyServer(&ProxyConfig{ + ListenAddr: "127.0.0.1:0", + EnableMITM: false, + ConnectTimeout: 30 * time.Second, + RequestTimeout: 5 * time.Minute, + }) + assert.NoError(t, err) + + internalProxy, ok := server.(*proxyServer) + assert.True(t, ok) + assert.NotNil(t, internalProxy.proxy.Tr) + assert.NotNil(t, internalProxy.proxy.Tr.TLSClientConfig) + assert.False(t, internalProxy.proxy.Tr.TLSClientConfig.InsecureSkipVerify, "upstream TLS verification must stay enabled") + assert.GreaterOrEqual(t, internalProxy.proxy.Tr.TLSClientConfig.MinVersion, uint16(tls.VersionTLS12), "minimum TLS version should be 1.2+") +} + +func TestNewProxyServerRejectsUntrustedUpstreamCertByDefault(t *testing.T) { + target := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusOK) + })) + defer target.Close() + + server, err := NewProxyServer(&ProxyConfig{ + ListenAddr: "127.0.0.1:0", + EnableMITM: false, + ConnectTimeout: 30 * time.Second, + RequestTimeout: 5 * time.Minute, + }) + assert.NoError(t, err) + + internalProxy, ok := server.(*proxyServer) + assert.True(t, ok) + + req, err := http.NewRequest(http.MethodGet, target.URL, nil) + assert.NoError(t, err) + + resp, err := internalProxy.proxy.Tr.RoundTrip(req) + assert.Error(t, err, "untrusted upstream certificate should fail verification") + assert.Nil(t, resp) +} From a3ca03f9c315fd75da1f25512d3d4e76a502a182 Mon Sep 17 00:00:00 2001 From: Sahilb315 Date: Thu, 12 Feb 2026 17:32:33 +0530 Subject: [PATCH 2/2] update transport to only harden TLS & rm (http.Transport).Clone --- proxy/proxy.go | 40 ++++++++++------------------------------ 1 file changed, 10 insertions(+), 30 deletions(-) diff --git a/proxy/proxy.go b/proxy/proxy.go index 6107e18..48616b7 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -137,37 +137,17 @@ func newUpstreamTransport(config *ProxyConfig) *http.Transport { Timeout: config.ConnectTimeout, } - defaultTransport, ok := http.DefaultTransport.(*http.Transport) - if !ok { - return &http.Transport{ - Proxy: http.ProxyFromEnvironment, - DialContext: dialer.DialContext, - TLSHandshakeTimeout: config.ConnectTimeout, - TLSClientConfig: &tls.Config{ - MinVersion: tls.VersionTLS12, - InsecureSkipVerify: false, - }, - } + // Keep transport behavior close to goproxy defaults and only harden TLS: + // enforce server certificate verification and require TLS 1.2+. + return &http.Transport{ + Proxy: http.ProxyFromEnvironment, + DialContext: dialer.DialContext, + TLSHandshakeTimeout: config.ConnectTimeout, + TLSClientConfig: &tls.Config{ + MinVersion: tls.VersionTLS12, + InsecureSkipVerify: false, + }, } - - transport := defaultTransport.Clone() - transport.Proxy = http.ProxyFromEnvironment - transport.DialContext = dialer.DialContext - transport.TLSHandshakeTimeout = config.ConnectTimeout - - tlsConfig := &tls.Config{MinVersion: tls.VersionTLS12} - if transport.TLSClientConfig != nil { - tlsConfig = transport.TLSClientConfig.Clone() - if tlsConfig.MinVersion < tls.VersionTLS12 { - tlsConfig.MinVersion = tls.VersionTLS12 - } - } - - // Explicitly enforce upstream certificate validation. - tlsConfig.InsecureSkipVerify = false - transport.TLSClientConfig = tlsConfig - - return transport } func (ps *proxyServer) Start() error {