Practical unit-testing web client in Go part 2 - https
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:
- Create self-signed certificates, with SAN property of 127.0.0.1.
- Start the testing webserver using this certificate.
- 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:
# 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
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.
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.