Featured image of post Practical unit-testing web client in Go - part 2

Practical unit-testing web client in Go - part 2

Practical web client unit-testing in Go by mocking the server side, now with HTTPS enabled

Testing webserver with TLS

In the previous post, I overviewed how to practically test web client in go.

The next step, I want to test a web server with self-signed certificates. To cover this use-case, we need to:

  1. Create self-signed certificates, with SAN property of 127.0.0.1.
  2. Start the testing webserver using this certificate.
  3. Configure my client to trust the RootCA.

Most of the people, when they reach this stage of testing, usually bend corners. You can simply disable the TLS verification with &tls.Config{InsecureSkipVerify: true} and be done with it.

I can’t. Can’t treat security precautions with no care; this is a package that works with a Secret Manager. In my case, testing the client works with self-signed certificate is significant.

Create self-signed certificates

Luckily, there’s a great github repository that covers the creation of certificates: https://github.com/jcbsmpsn/golang-https-example

The repo contains solutions to common issues raised when trying to achieve this task. If you encounter any errors during this procedure, go check it out. To create certificates:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# Create CA key + certificate
openssl req \
    -newkey rsa:2048 \
    -nodes \
    -days 3650 \
    -x509 \
    -keyout ca.key \
    -out ca.crt \
    -subj "/CN=*"

# Create server certificate request and key
openssl req \
    -newkey rsa:2048 \
    -nodes \
    -keyout server.key \
    -out server.csr \
    -subj "/C=GB/ST=London/L=London/O=libvault consultants/OU=IT Department/CN=*"

# Sign the server certificate request with the CA key
# adding SAN IP
openssl x509 \
    -req \
    -days 365 \
    -sha256 \
    -in server.csr \
    -CA ca.crt \
    -CAkey ca.key \
    -CAcreateserial \
    -out server.crt \
    -extfile <(echo subjectAltName = IP:127.0.0.1)

Running the above code results in 2 sets of certificates: ca and server certificates. Now let’s use them.

Start webserver with TLS

There’s no single API to start the test server with TLS configuration, so we need to do it in few steps. First I’ll create a unstarted test server, load the x509 keypairs and configure the server to use them. Only then I start the web server. In code, it looks like this

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
const (
	caCertPath     = "testdata/certs/ca.crt"
	serverCertPath = "testdata/certs/server.crt"
	serverKeyPath  = "testdata/certs/server.key"
)

func TestClientLogin(t *testing.T*) {
	mux := http.NewServeMux()  
	ts := httptest.NewUnstartedServer(mux)

	cert, err := tls.LoadX509KeyPair(serverCertPath, serverKeyPath)
			if err != nil {
				// handle error
			}
	// setp the server's TLS configurations
	ts.TLS = &tls.Config{Certificates: []tls.Certificate{cert}}
	
	// start the server with TLS support
	ts.StartTLS()
	...

Configure the http.Client to trust my RootCA

Now we have a webserver that uses our own self-signed certificate. Cool. Remember the CA key and certificate we created earlier? Now we need to configure our client to trust certificates signed by our CA.

My client wraps the http.Client structure. To simplify things, here I’ll work straight with the http.Client struct.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package main
import (
	"crypto/tls"
	"crypto/x509"
)

var (
	caCertPath  = "testdata/certs/ca.crt"
	certPool 	= x509.NewCertPool()
)

caCert, _ := ioutil.ReadFile(caCertPath)
ok := certPool.AppendCertsFromPEM(caCert)
if !ok {
	// error handling
}


tlsCfg := &tls.Config{
	RootCAs: certPool,
}

client := &http.Client{
	Transport: &http.Transport{
        TLSClientConfig: tlsCfg,
    }
}

...

Now, requests made from my client to my secured test webserver are validated.