Skip to content

Instantly share code, notes, and snippets.

@jasonhancock
Created April 22, 2025 20:38
Show Gist options
  • Save jasonhancock/501a8c7bcb6162189807051e083ea6a4 to your computer and use it in GitHub Desktop.
Save jasonhancock/501a8c7bcb6162189807051e083ea6a4 to your computer and use it in GitHub Desktop.
Golang graceful reload of SSL Certificate

Reloading an SSL certificate on the fly with Golang

Generate two self-signed certificates:

openssl genrsa -out server1.key 2048
openssl req -new -x509 -sha256 -key server1.key -out server1.pem -days 3650
openssl genrsa -out server2.key 2048
openssl req -new -x509 -sha256 -key server2.key -out server2.pem -days 3650

Copy cert1 into place:

cp server1.key server.key
cp server1.pem server.pem

Start the program:

go run main.go

Verify you can curl it

curl -k https://127.0.0.1:8443
Hello World!

Show which cert is being served:

$ echo | openssl s_client -servername 127.0.0.1 -connect 127.0.0.1:8443 2>/dev/null | openssl x509 -noout -text | grep -A1 Serial
            4f:d6:6a:09:48:54:8e:df:20:9d:fe:d8:52:3e:c9:c5:64:69:90:3b

In my case, that is the serial number of server1.pem

Copy server2.{key,pem} into place:

cp server2.key server.key
cp server2.pem server.pem

Determine the PID of your process and send it a SIGHUP:

$ kill -1 <pid>

Verify the new certificate is being served:

$ echo | openssl s_client -servername 127.0.0.1 -connect 127.0.0.1:8443 2>/dev/null | openssl x509 -noout -text | grep -A1 Serial
            61:c2:1c:5e:97:d1:60:10:1e:ee:75:f4:48:71:da:64:8f:02:6b:af
package main
import (
"context"
"crypto/tls"
"log"
"net/http"
"os"
"os/signal"
"syscall"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGHUP)
cert, err := tls.LoadX509KeyPair("server.pem", "server.key")
if err != nil {
log.Fatal(err)
}
go func() {
for {
select {
case <-ctx.Done():
return
case <-c:
log.Printf("Got a SIGHUP.")
newCert, err := tls.LoadX509KeyPair("server.pem", "server.key")
if err != nil {
log.Fatal(err)
}
cert = newCert
}
}
}()
handler := http.NewServeMux()
handler.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello World!"))
})
server := http.Server{
Addr: "127.0.0.1:8443",
Handler: handler,
TLSConfig: &tls.Config{
GetCertificate: func(*tls.ClientHelloInfo) (*tls.Certificate, error) {
return &cert, nil
},
},
}
server.ListenAndServeTLS("", "")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment