Skip to content

Instantly share code, notes, and snippets.

@erkanzileli
Last active January 4, 2022 20:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save erkanzileli/ee889be693fb3fbb586f4415519cf564 to your computer and use it in GitHub Desktop.
Save erkanzileli/ee889be693fb3fbb586f4415519cf564 to your computer and use it in GitHub Desktop.
refresh certificates on runtime
package main
import (
"crypto/tls"
"github.com/fsnotify/fsnotify"
"log"
"net/http"
"sync"
)
var (
certPath = "/etc/webhook/cert/tls.crt"
keyPath = "/etc/webhook/cert/tls.key"
)
func main() {
certUpdater, err := newCertificateUpdater()
if err != nil {
log.Fatal(err)
}
go certUpdater.startFollowing()
server := http.Server{
TLSConfig: &tls.Config{
GetCertificate: certUpdater.GetCertificateFunc,
},
Addr: ":8080",
}
if err = server.ListenAndServeTLS("", ""); err != nil {
log.Printf("server stopped due to %+v", err)
}
}
type certificateUpdater struct {
certMu sync.RWMutex
cert *tls.Certificate
}
func newCertificateUpdater() (*certificateUpdater, error) {
result := &certificateUpdater{}
cert, err := tls.LoadX509KeyPair(certPath, keyPath)
if err != nil {
return nil, err
}
result.cert = &cert
return result, nil
}
func (cu *certificateUpdater) startFollowing() {
watcher, err := fsnotify.NewWatcher()
if err != nil {
log.Fatal(err)
}
defer watcher.Close()
if err = watcher.Add(certPath); err != nil {
log.Fatal(err)
}
if err = watcher.Add(keyPath); err != nil {
log.Fatal(err)
}
for {
select {
case event, ok := <-watcher.Events:
if !ok {
return
}
log.Println("event:", event)
if event.Op == fsnotify.Remove {
if err := cu.maybeReload(); err != nil {
log.Printf("failed to reload certificates: %+v", err)
}
}
case err, ok := <-watcher.Errors:
if !ok {
return
}
log.Println("error:", err)
}
}
}
func (cu *certificateUpdater) maybeReload() error {
log.Printf("reloading TLS certificate and key from %q and %q", certPath, keyPath)
newCert, err := tls.LoadX509KeyPair(certPath, keyPath)
if err != nil {
log.Printf("Keeping old TLS certificate because the new one could not be loaded: %v", err)
return err
}
cu.certMu.Lock()
defer cu.certMu.Unlock()
cu.cert = &newCert
return nil
}
func (cu *certificateUpdater) GetCertificateFunc(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
cu.certMu.RLock()
defer cu.certMu.RUnlock()
return cu.cert, nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment