Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions proxy/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -131,6 +132,24 @@ func NewProxyServer(config *ProxyConfig) (ProxyServer, error) {
return ps, nil
}

func newUpstreamTransport(config *ProxyConfig) *http.Transport {
dialer := &net.Dialer{
Timeout: config.ConnectTimeout,
}

// 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,
},
}
}

func (ps *proxyServer) Start() error {
listener, err := net.Listen("tcp", ps.config.ListenAddr)
if err != nil {
Expand Down
53 changes: 53 additions & 0 deletions proxy/proxy_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
Loading