feat(tls): automatically reload tls certs

Fixes issue #3747

Currently, zot requires a restart whenever tls certs change, which can
occur whenever there are tls cert rotation etc.

This PR checks if the tls certs have be modified and if so reloads them
without restarting zot.

Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com>
This commit is contained in:
Ramkumar Chinchani
2026-02-01 12:32:03 -08:00
parent b9aad15ad0
commit 7ead92b82f
4 changed files with 847 additions and 2 deletions
+22 -2
View File
@@ -54,6 +54,7 @@ type Controller struct {
HTPasswd *HTPasswd
HTPasswdWatcher *HTPasswdWatcher
LDAPClient *LDAPClient
CertReloader *CertReloader
taskScheduler *scheduler.Scheduler
Healthz *common.Healthz
// runtime params
@@ -204,6 +205,18 @@ func (c *Controller) Run() error {
tlsConfig := c.Config.CopyTLSConfig()
if tlsConfig != nil && tlsConfig.Key != "" && tlsConfig.Cert != "" {
// Create certificate reloader for automatic TLS certificate updates
certReloader, err := NewCertReloader(tlsConfig.Cert, tlsConfig.Key, c.Log)
if err != nil {
c.Log.Error().Err(err).Str("cert", tlsConfig.Cert).Str("key", tlsConfig.Key).
Msg("failed to load TLS certificates")
return err
}
// Store the CertReloader so it can be closed during shutdown
c.CertReloader = certReloader
// These are the same as the cipher suites in defaultCipherSuitesFIPS for TLS 1.2
// see https://cs.opensource.google/go/go/+/refs/tags/go1.24.9:src/crypto/tls/defaults.go;l=123
// Note: Order doesn't matter - Go 1.17+ automatically orders cipher suites based on
@@ -239,7 +252,8 @@ func (c *Controller) Run() error {
CipherSuites: cipherSuites,
CurvePreferences: curvePreferences,
// PreferServerCipherSuites is ignored in Go 1.17+ - Go automatically orders cipher suites
MinVersion: tls.VersionTLS12,
MinVersion: tls.VersionTLS12,
GetCertificate: certReloader.GetCertificateFunc(),
}
if tlsConfig.CACert != "" {
@@ -266,7 +280,8 @@ func (c *Controller) Run() error {
c.Healthz.Ready()
return server.ServeTLS(listener, tlsConfig.Cert, tlsConfig.Key)
// Pass empty strings to ServeTLS - certificates will be loaded via GetCertificate callback
return server.ServeTLS(listener, "", "")
}
c.Healthz.Ready()
@@ -484,6 +499,11 @@ func (c *Controller) StopBackgroundTasks() {
if c.HTPasswdWatcher != nil {
_ = c.HTPasswdWatcher.Close()
}
// Close CertReloader to prevent resource leaks
if c.CertReloader != nil {
_ = c.CertReloader.Close()
}
}
func (c *Controller) StartBackgroundTasks() {