From 5c8dc344181c49bc7feade4a293bb4eed882b838 Mon Sep 17 00:00:00 2001 From: Francis Lavoie Date: Tue, 5 Nov 2024 12:47:41 -0500 Subject: [PATCH] caddytls: Allow disabling storage cleaning, avoids writing two files (#6593) --- caddyconfig/httpcaddyfile/options.go | 37 ++++++++++++++++++- caddyconfig/httpcaddyfile/tlsapp.go | 10 +++++ .../global_options.caddyfiletest | 6 ++- modules/caddytls/tls.go | 17 ++++++++- 4 files changed, 67 insertions(+), 3 deletions(-) diff --git a/caddyconfig/httpcaddyfile/options.go b/caddyconfig/httpcaddyfile/options.go index 03b9ba23..cfd8f709 100644 --- a/caddyconfig/httpcaddyfile/options.go +++ b/caddyconfig/httpcaddyfile/options.go @@ -39,7 +39,8 @@ func init() { RegisterGlobalOption("fallback_sni", parseOptSingleString) RegisterGlobalOption("order", parseOptOrder) RegisterGlobalOption("storage", parseOptStorage) - RegisterGlobalOption("storage_clean_interval", parseOptDuration) + RegisterGlobalOption("storage_check", parseStorageCheck) + RegisterGlobalOption("storage_clean_interval", parseStorageCleanInterval) RegisterGlobalOption("renew_interval", parseOptDuration) RegisterGlobalOption("ocsp_interval", parseOptDuration) RegisterGlobalOption("acme_ca", parseOptSingleString) @@ -189,6 +190,40 @@ func parseOptStorage(d *caddyfile.Dispenser, _ any) (any, error) { return storage, nil } +func parseStorageCheck(d *caddyfile.Dispenser, _ any) (any, error) { + d.Next() // consume option name + if !d.Next() { + return "", d.ArgErr() + } + val := d.Val() + if d.Next() { + return "", d.ArgErr() + } + if val != "off" { + return "", d.Errf("storage_check must be 'off'") + } + return val, nil +} + +func parseStorageCleanInterval(d *caddyfile.Dispenser, _ any) (any, error) { + d.Next() // consume option name + if !d.Next() { + return "", d.ArgErr() + } + val := d.Val() + if d.Next() { + return "", d.ArgErr() + } + if val == "off" { + return false, nil + } + dur, err := caddy.ParseDuration(d.Val()) + if err != nil { + return nil, d.Errf("failed to parse storage_clean_interval, must be a duration or 'off' %w", err) + } + return caddy.Duration(dur), nil +} + func parseOptDuration(d *caddyfile.Dispenser, _ any) (any, error) { if !d.Next() { // consume option name return nil, d.ArgErr() diff --git a/caddyconfig/httpcaddyfile/tlsapp.go b/caddyconfig/httpcaddyfile/tlsapp.go index bec86061..ea5ac92c 100644 --- a/caddyconfig/httpcaddyfile/tlsapp.go +++ b/caddyconfig/httpcaddyfile/tlsapp.go @@ -349,6 +349,16 @@ func (st ServerType) buildTLSApp( tlsApp.Automation.OnDemand = onDemand } + // if the storage clean interval is a boolean, then it's "off" to disable cleaning + if sc, ok := options["storage_check"].(string); ok && sc == "off" { + tlsApp.DisableStorageCheck = true + } + + // if the storage clean interval is a boolean, then it's "off" to disable cleaning + if sci, ok := options["storage_clean_interval"].(bool); ok && !sci { + tlsApp.DisableStorageClean = true + } + // set the storage clean interval if configured if storageCleanInterval, ok := options["storage_clean_interval"].(caddy.Duration); ok { if tlsApp.Automation == nil { diff --git a/caddytest/integration/caddyfile_adapt/global_options.caddyfiletest b/caddytest/integration/caddyfile_adapt/global_options.caddyfiletest index af301615..99f45cdd 100644 --- a/caddytest/integration/caddyfile_adapt/global_options.caddyfiletest +++ b/caddytest/integration/caddyfile_adapt/global_options.caddyfiletest @@ -9,6 +9,8 @@ storage file_system { root /data } + storage_check off + storage_clean_interval off acme_ca https://example.com acme_ca_root /path/to/ca.crt ocsp_stapling off @@ -73,7 +75,9 @@ } } }, - "disable_ocsp_stapling": true + "disable_ocsp_stapling": true, + "disable_storage_check": true, + "disable_storage_clean": true } } } diff --git a/modules/caddytls/tls.go b/modules/caddytls/tls.go index 6e660dea..abb519eb 100644 --- a/modules/caddytls/tls.go +++ b/modules/caddytls/tls.go @@ -92,6 +92,17 @@ type TLS struct { // EXPERIMENTAL. Subject to change. DisableStorageCheck bool `json:"disable_storage_check,omitempty"` + // Disables the automatic cleanup of the storage backend. + // This is useful when TLS is not being used to store certificates + // and the user wants run their server in a read-only mode. + // + // Storage cleaning creates two files: instance.uuid and last_clean.json. + // The instance.uuid file is used to identify the instance of Caddy + // in a cluster. The last_clean.json file is used to store the last + // time the storage was cleaned. + // EXPERIMENTAL. Subject to change. + DisableStorageClean bool `json:"disable_storage_clean,omitempty"` + certificateLoaders []CertificateLoader automateNames []string ctx caddy.Context @@ -328,7 +339,11 @@ func (t *TLS) Start() error { return fmt.Errorf("automate: managing %v: %v", t.automateNames, err) } - t.keepStorageClean() + if !t.DisableStorageClean { + // start the storage cleaner goroutine and ticker, + // which cleans out expired certificates and more + t.keepStorageClean() + } return nil }