tls_certificate_loader.go 1.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
  1. // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
  2. // SPDX-License-Identifier: Apache-2.0
  3. package server
  4. import (
  5. "crypto/tls"
  6. "log/slog"
  7. "path/filepath"
  8. "sync"
  9. )
  10. // certificateLoader loads and caches a TLS certificate from disk, and
  11. // provides a reload method that can be triggered on SIGHUP.
  12. type certificateLoader struct {
  13. mu sync.RWMutex
  14. cert *tls.Certificate
  15. certFile string
  16. keyFile string
  17. }
  18. func newCertificateLoader(certFile, keyFile string) (*certificateLoader, error) {
  19. cert, err := tls.LoadX509KeyPair(certFile, keyFile)
  20. if err != nil {
  21. return nil, err
  22. }
  23. loader := &certificateLoader{
  24. cert: &cert,
  25. certFile: filepath.Clean(certFile),
  26. keyFile: filepath.Clean(keyFile),
  27. }
  28. slog.Info("TLS certificate loaded",
  29. slog.String("cert_file", loader.certFile),
  30. slog.String("key_file", loader.keyFile),
  31. )
  32. return loader, nil
  33. }
  34. // getCertificate returns the currently cached TLS certificate. It satisfies
  35. // the tls.Config.GetCertificate callback and is called by the TLS layer on
  36. // every handshake.
  37. func (cl *certificateLoader) getCertificate(*tls.ClientHelloInfo) (*tls.Certificate, error) {
  38. cl.mu.RLock()
  39. defer cl.mu.RUnlock()
  40. return cl.cert, nil
  41. }
  42. // Reload loads the certificate and key from disk and replaces the cached
  43. // copy. If loading fails, the existing certificate is kept and the error
  44. // is logged.
  45. func (cl *certificateLoader) Reload() {
  46. cert, err := tls.LoadX509KeyPair(cl.certFile, cl.keyFile)
  47. if err != nil {
  48. slog.Error("Unable to reload TLS certificate",
  49. slog.String("cert_file", cl.certFile),
  50. slog.String("key_file", cl.keyFile),
  51. slog.Any("error", err),
  52. )
  53. return
  54. }
  55. cl.mu.Lock()
  56. cl.cert = &cert
  57. cl.mu.Unlock()
  58. slog.Info("TLS certificate reloaded successfully",
  59. slog.String("cert_file", cl.certFile),
  60. slog.String("key_file", cl.keyFile),
  61. )
  62. }