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}