SECRET OF CSS

Hitless TLS Certificate Rotation in Go | by Sanad Haj | Jun, 2022


In this tutorial, we’ll be discussing how to handle certificate rotation in Golang without any downtime.

0*zoXYVe2K3211cwsw
Photo by Joanna Kosinska on Unsplash

Certificate rotation means the replacement of existing certificates with new ones.

There is an old TLS maxim that states that:

If a certificate got issued, it will have to be rotated.

Well, It’s needed when:

  • Any certificate expires.
  • A new CA authority is substituted for the old; thus requiring a replacement root certificate for all services.
  • New or modified constraints need to be imposed on one or more certificates.
  • A security breach has occurred, such that existing certificate chains can no longer be trusted.

Rotating TLS certificates manually may quickly get out of hand — particularly when you have to manage hundreds of certificates — and becomes completely unmanageable if you issue certificates that expire within hours, instead of months.

tlsreconciler is here to help with that, by reloading rotated certificate including root CA and providing TLS reconciliation to connections in real-time and without restarting the application.

We are going to start by creating our project.

mkdir medium && cd medium && go mod init medium && touch main.go

This will create a new directory called “medium” and initialize the project with go.mod.

We also need to install tlsreconciler.

go get github.com/shaj13/tlsreconciler

Before we write any certificate rotation code we need to write some mandatory code that describes our tutorial program.

package main

import (
"crypto/tls"
"log"
"net/http"

)

func HelloMedium(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Content-Type", "text/plain")
w.Write([]byte("Hello Medium.\n"))
}

func main() {

config := new(tls.Config)
server := http.Server{
Addr: ":443",
Handler: http.HandlerFunc(HelloMedium),
TLSConfig: config,
}

err := server.ListenAndServeTLS("", "")
if err != nil {
log.Fatal(err)
}

}

Running the program will return the following error.

open : no such file or directory

Basically, this error returned from http.ListenAndServeTLS because we have initialized server with empty tls.Config, So it’s fallback to certs paths arguments that are not defined, will fix that later.

We need to make our program to catch SIGHUB so we can reload the rotated certs.

This can be done, like so:

sigc := make(chan os.Signal, 1)
defer close(sigc)

signal.Notify(sigc, syscall.SIGHUP)

Now, we need to configure tlsreconciler, like so.

sig := tlsreconciler.WithSIGHUPReload(sigc)
certs := tlsreconciler.WithCertificatesPaths("./cert", "./key", "")
cb := tlsreconciler.WithOnReload(func(c *tls.Config) {
log.Println("TLS certificates rotated !!")
})

The above create tlsreconciler configuration to reload certificate when an SIGHUB received and call our callback function to print a pretty log entry beside to reload certs from the given paths.

Its time to connect the dots, we need to replace the following line:

config := new(tls.Config)

With returned tls.Config from tlsreconciler:

config := tlsreconciler.TLSConfig(sig, certs, cb)

Your file now should look like this.

First let’s generate a certificate and private key:

openssl genrsa -out ./key 2048
openssl req -new -x509 -sha256 -key ./key -out ./cert -days 3650

Now let’s run our program:

go build main.go
./main

From a different shell let’s send an HTTPS request:

curl -k https://127.0.0.1:443/

In the program logs, you will find out the following log entry “TLS certificates rotated !!”.

This happened because tlsreconciler has reloaded certificate for the first time, running more curls will no longer print the above log entry.

Now let’s check the rotation, for that we will use openssl to show certificates

openssl s_client -showcerts -connect 127.0.0.1:443 2>&1|openssl x509 -noout -serial

The above command will print the certificate serial number.

Let’s rotate certificates by generating new ones using openssl and sending SIGHUP to our program

rm -rf ./key ./cert 
openssl genrsa -out ./key 2048
openssl req -new -x509 -sha256 -key ./key -out ./cert -days 3650
kill -SIGHUP <PID>

Now let’s verify the serial number is different from the previous one

openssl s_client -showcerts -connect 127.0.0.1:443 2>&1|openssl x509 -noout -serial



News Credit

%d bloggers like this: