From e081d8b5c2b7827d46db55ff33eb792925fdad14 Mon Sep 17 00:00:00 2001 From: Matthew Holt Date: Tue, 9 Aug 2016 16:46:51 -0600 Subject: [PATCH] Maintainence routine deletes old (expired) OCSP staple files --- caddytls/crypto.go | 1 - caddytls/maintain.go | 47 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/caddytls/crypto.go b/caddytls/crypto.go index 0f371fa3b..25f66b0ac 100644 --- a/caddytls/crypto.go +++ b/caddytls/crypto.go @@ -89,7 +89,6 @@ func stapleOCSP(cert *Certificate, pemBundle []byte) error { // First try to load OCSP staple from storage and see if // we can still use it. // TODO: Use Storage interface instead of disk directly - ocspFolder := filepath.Join(caddy.AssetsPath(), "ocsp") ocspFileName := cert.Names[0] + "-" + fastHash(pemBundle) ocspCachePath := filepath.Join(ocspFolder, ocspFileName) cachedOCSP, err := ioutil.ReadFile(ocspCachePath) diff --git a/caddytls/maintain.go b/caddytls/maintain.go index ec56aaef2..f2251ebd4 100644 --- a/caddytls/maintain.go +++ b/caddytls/maintain.go @@ -1,9 +1,14 @@ package caddytls import ( + "io/ioutil" "log" + "os" + "path/filepath" "time" + "github.com/mholt/caddy" + "golang.org/x/crypto/ocsp" ) @@ -47,6 +52,7 @@ func maintainAssets(stopChan chan struct{}) { case <-ocspTicker.C: log.Println("[INFO] Scanning for stale OCSP staples") UpdateOCSPStaples() + DeleteOldStapleFiles() log.Println("[INFO] Done checking OCSP staples") case <-stopChan: renewalTicker.Stop() @@ -231,8 +237,49 @@ func UpdateOCSPStaples() { certCacheMu.Unlock() } +// DeleteOldStapleFiles deletes cached OCSP staples that have expired. +// TODO: Should we do this for certificates too? +func DeleteOldStapleFiles() { + files, err := ioutil.ReadDir(ocspFolder) + if err != nil { + // maybe just hasn't been created yet; no big deal + return + } + for _, file := range files { + if file.IsDir() { + // wierd, what's a folder doing inside the OCSP cache? + continue + } + stapleFile := filepath.Join(ocspFolder, file.Name()) + ocspBytes, err := ioutil.ReadFile(stapleFile) + if err != nil { + continue + } + resp, err := ocsp.ParseResponse(ocspBytes, nil) + if err != nil { + // contents are invalid; delete it + err = os.Remove(stapleFile) + if err != nil { + log.Printf("[ERROR] Purging corrupt staple file %s: %v", stapleFile, err) + } + } + if time.Now().After(resp.NextUpdate) { + // response has expired; delete it + err = os.Remove(stapleFile) + if err != nil { + log.Printf("[ERROR] Purging expired staple file %s: %v", stapleFile, err) + } + } + } +} + +// freshOCSP returns true if resp is still fresh, +// meaning that it is not expedient to get an +// updated response from the OCSP server. func freshOCSP(resp *ocsp.Response) bool { // start checking OCSP staple about halfway through validity period for good measure refreshTime := resp.ThisUpdate.Add(resp.NextUpdate.Sub(resp.ThisUpdate) / 2) return time.Now().Before(refreshTime) } + +var ocspFolder = filepath.Join(caddy.AssetsPath(), "ocsp")