certreloader.go

 1package serve
 2
 3import (
 4	"crypto/tls"
 5	"sync"
 6
 7	"charm.land/log/v2"
 8)
 9
10// CertReloader is responsible for reloading TLS certificates when a SIGHUP signal is received.
11type CertReloader struct {
12	certMu   sync.RWMutex
13	cert     *tls.Certificate
14	certPath string
15	keyPath  string
16}
17
18// NewCertReloader creates a new CertReloader that watches for SIGHUP signals.
19func NewCertReloader(certPath, keyPath string, logger *log.Logger) (*CertReloader, error) {
20	reloader := &CertReloader{
21		certPath: certPath,
22		keyPath:  keyPath,
23	}
24
25	cert, err := tls.LoadX509KeyPair(certPath, keyPath)
26	if err != nil {
27		return nil, err
28	}
29	reloader.cert = &cert
30
31	return reloader, nil
32}
33
34// Reload attempts to reload the certificate and key.
35func (cr *CertReloader) Reload() error {
36	newCert, err := tls.LoadX509KeyPair(cr.certPath, cr.keyPath)
37	if err != nil {
38		return err
39	}
40
41	cr.certMu.Lock()
42	defer cr.certMu.Unlock()
43	cr.cert = &newCert
44	return nil
45}
46
47// GetCertificateFunc returns a function that can be used with tls.Config.GetCertificate.
48func (cr *CertReloader) GetCertificateFunc() func(*tls.ClientHelloInfo) (*tls.Certificate, error) {
49	return func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
50		cr.certMu.RLock()
51		defer cr.certMu.RUnlock()
52		return cr.cert, nil
53	}
54}