hoverfly icon indicating copy to clipboard operation
hoverfly copied to clipboard

HTTPS error when using Go core package to setup hoverfly instance

Open AgustinBettati opened this issue 11 months ago • 3 comments

Description of the bug

I have been looking into managing hoverfly from my Go tests directly. In the documentation I do not see any official mention for a Go SDK, however I did find https://pkg.go.dev/github.com/SpectoLabs/[email protected]/core which seems to expose all the functionality I need. The issue I am facing is a TLS error due to unknown certificate authority, however when running a hoverfly instance with hoverctl I do not face this issue and can confirm I have configured the hoverfly ca cert in my keychain following the documentation.

Steps to reproduce the issue

Go code which configures the hoverfly instance using "github.com/SpectoLabs/hoverfly/core"

settings := hoverfly.InitSettings()
settings.Verbose = true
hv := hoverfly.NewHoverflyWithConfiguration(settings)

err := hv.StartProxy()
if err != nil {
log.Fatalf("Failed to start Hoverfly: %v", err)
}

err = hv.SetModeWithArguments(v2.ModeView{Mode: "capture", Arguments: v2.ModeArgumentsView{Stateful: true}})
if err != nil {
log.Fatalf("Failed to set Hoverfly mode: %v", err)
}

Subsequent calls fail, detailed error below.

Observed result

output from verbose logs:

024/03/20 14:24:08 [001] INFO: Running 1 CONNECT handlers
2024/03/20 14:24:08 [001] INFO: on 0th handler: &{2 <nil> 0x10597a7e0} cloud.mongodb.com:443
2024/03/20 14:24:08 [001] INFO: Assuming CONNECT is TLS, mitm proxying it
2024/03/20 14:24:08 [001] INFO: signing for cloud-dev.mongodb.com
2024/03/20 14:24:08 [001] WARN: Cannot handshake client cloud.mongodb.com:443 remote error: tls: bad certificate

error from client call:

tls: failed to verify certificate: x509: certificate signed by unknown authority

Expected result

I would expect the same behaviour as when I setup hoverfly using hoverctl:

hoverctl start
hoverctl mode capture --stateful

In this case my https requests are successfully captured.

Additional relevant information

  1. Hoverfly version: 1.8.0

Any inputs would be greatly appreciated, also want to confirm that using the github.com/SpectoLabs/hoverfly/core package is advised.

AgustinBettati avatar Mar 20 '24 13:03 AgustinBettati

how do you call cloud.mongodb.com:443? your http client needs to configure to trust the hoverfly ca cert as well.

tommysitu avatar Mar 20 '24 21:03 tommysitu

cloud.mongodb.com is being called directly by a regular Go client which also has configured the env variable "HTTPS_PROXY": "http://localhost:<port>"

func NewTransport(username, password string) *Transport {
	t := &Transport{
		Username: username,
		Password: password,
	}
	t.Transport = http.DefaultTransport
	return t
}

// Client returns an HTTP client that uses the digest transport.
func (t *Transport) Client() (*http.Client, error) {
	if t.Transport == nil {
		return nil, ErrNilTransport
	}
	return &http.Client{Transport: t}, nil
}

What is suprising is that if I run hoverfly through the cli using hoverctl with the command mentioned above I face no issue, however when starting hoverfly with the Go module I am facing the error (no changes in how the client calls the proxy).

AgustinBettati avatar Mar 21 '24 17:03 AgustinBettati

Hey @AgustinBettati , sorry for the radio silence, since hoverfly has its own custom CA cert as I mentioned before, your go http client need to be able to trust it, otherwise you will get the bad tls cert error. You can do something like this to load the hoverfly ca cert in pem format , and add it to a CertPool, then sets up a http.Transport with TLSClientConfig that uses the CertPool, and finally a http.Client that uses the Transport:

// Read the certificate file
    cert, err := ioutil.ReadFile("/path/to/your/certificate.pem")
    if err != nil {
        log.Fatalf("Couldn't load certificate: %v", err)
    }

    // Create a CertPool and add the certificate to it
    certPool := x509.NewCertPool()
    if ok := certPool.AppendCertsFromPEM(cert); !ok {
        log.Fatalf("Failed to append certificate")
    }

    // Create a new Transport that uses the CertPool
    tr := &http.Transport{
        TLSClientConfig: &tls.Config{
            RootCAs: certPool,
        },
    }

    // Create a new Client that uses the Transport
    client := &http.Client{Transport: tr}

    // Now you can use this client as you would normally
    // It will trust your custom certificate authority
	_, err = client.Get("https://your.domain")
	if err != nil {
		log.Fatalf("Could not get response: %v", err)
	}

tommysitu avatar Apr 10 '24 17:04 tommysitu