fix: archive only domain-related files on revoke (#1874)

Co-authored-by: Fernandez Ludovic <ldez@users.noreply.github.com>
This commit is contained in:
Aofei Sheng 2023-05-06 06:14:07 +08:00 committed by GitHub
parent 3d44184074
commit 510aa8f871
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 138 additions and 8 deletions

View file

@ -27,6 +27,15 @@ const (
baseArchivesFolderName = "archives" baseArchivesFolderName = "archives"
) )
const (
issuerExt = ".issuer.crt"
certExt = ".crt"
keyExt = ".key"
pemExt = ".pem"
pfxExt = ".pfx"
resourceExt = ".json"
)
// CertificatesStorage a certificates' storage. // CertificatesStorage a certificates' storage.
// //
// rootPath: // rootPath:
@ -84,13 +93,13 @@ func (s *CertificatesStorage) SaveResource(certRes *certificate.Resource) {
// We store the certificate, private key and metadata in different files // We store the certificate, private key and metadata in different files
// as web servers would not be able to work with a combined file. // as web servers would not be able to work with a combined file.
err := s.WriteFile(domain, ".crt", certRes.Certificate) err := s.WriteFile(domain, certExt, certRes.Certificate)
if err != nil { if err != nil {
log.Fatalf("Unable to save Certificate for domain %s\n\t%v", domain, err) log.Fatalf("Unable to save Certificate for domain %s\n\t%v", domain, err)
} }
if certRes.IssuerCertificate != nil { if certRes.IssuerCertificate != nil {
err = s.WriteFile(domain, ".issuer.crt", certRes.IssuerCertificate) err = s.WriteFile(domain, issuerExt, certRes.IssuerCertificate)
if err != nil { if err != nil {
log.Fatalf("Unable to save IssuerCertificate for domain %s\n\t%v", domain, err) log.Fatalf("Unable to save IssuerCertificate for domain %s\n\t%v", domain, err)
} }
@ -112,14 +121,14 @@ func (s *CertificatesStorage) SaveResource(certRes *certificate.Resource) {
log.Fatalf("Unable to marshal CertResource for domain %s\n\t%v", domain, err) log.Fatalf("Unable to marshal CertResource for domain %s\n\t%v", domain, err)
} }
err = s.WriteFile(domain, ".json", jsonBytes) err = s.WriteFile(domain, resourceExt, jsonBytes)
if err != nil { if err != nil {
log.Fatalf("Unable to save CertResource for domain %s\n\t%v", domain, err) log.Fatalf("Unable to save CertResource for domain %s\n\t%v", domain, err)
} }
} }
func (s *CertificatesStorage) ReadResource(domain string) certificate.Resource { func (s *CertificatesStorage) ReadResource(domain string) certificate.Resource {
raw, err := s.ReadFile(domain, ".json") raw, err := s.ReadFile(domain, resourceExt)
if err != nil { if err != nil {
log.Fatalf("Error while loading the meta data for domain %s\n\t%v", domain, err) log.Fatalf("Error while loading the meta data for domain %s\n\t%v", domain, err)
} }
@ -176,13 +185,13 @@ func (s *CertificatesStorage) WriteFile(domain, extension string, data []byte) e
} }
func (s *CertificatesStorage) WriteCertificateFiles(domain string, certRes *certificate.Resource) error { func (s *CertificatesStorage) WriteCertificateFiles(domain string, certRes *certificate.Resource) error {
err := s.WriteFile(domain, ".key", certRes.PrivateKey) err := s.WriteFile(domain, keyExt, certRes.PrivateKey)
if err != nil { if err != nil {
return fmt.Errorf("unable to save key file: %w", err) return fmt.Errorf("unable to save key file: %w", err)
} }
if s.pem { if s.pem {
err = s.WriteFile(domain, ".pem", bytes.Join([][]byte{certRes.Certificate, certRes.PrivateKey}, nil)) err = s.WriteFile(domain, pemExt, bytes.Join([][]byte{certRes.Certificate, certRes.PrivateKey}, nil))
if err != nil { if err != nil {
return fmt.Errorf("unable to save PEM file: %w", err) return fmt.Errorf("unable to save PEM file: %w", err)
} }
@ -247,16 +256,22 @@ func (s *CertificatesStorage) WritePFXFile(domain string, certRes *certificate.R
return fmt.Errorf("unable to encode PFX data for domain %s: %w", domain, err) return fmt.Errorf("unable to encode PFX data for domain %s: %w", domain, err)
} }
return s.WriteFile(domain, ".pfx", pfxBytes) return s.WriteFile(domain, pfxExt, pfxBytes)
} }
func (s *CertificatesStorage) MoveToArchive(domain string) error { func (s *CertificatesStorage) MoveToArchive(domain string) error {
matches, err := filepath.Glob(filepath.Join(s.rootPath, sanitizedDomain(domain)+".*")) baseFilename := filepath.Join(s.rootPath, sanitizedDomain(domain))
matches, err := filepath.Glob(baseFilename + ".*")
if err != nil { if err != nil {
return err return err
} }
for _, oldFile := range matches { for _, oldFile := range matches {
if strings.TrimSuffix(oldFile, filepath.Ext(oldFile)) != baseFilename && oldFile != baseFilename+issuerExt {
continue
}
date := strconv.FormatInt(time.Now().Unix(), 10) date := strconv.FormatInt(time.Now().Unix(), 10)
filename := date + "." + filepath.Base(oldFile) filename := date + "." + filepath.Base(oldFile)
newFile := filepath.Join(s.archivePath, filename) newFile := filepath.Join(s.archivePath, filename)

115
cmd/certs_storage_test.go Normal file
View file

@ -0,0 +1,115 @@
package cmd
import (
"os"
"path/filepath"
"regexp"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestCertificatesStorage_MoveToArchive(t *testing.T) {
domain := "example.com"
storage := CertificatesStorage{
rootPath: t.TempDir(),
archivePath: t.TempDir(),
}
domainFiles := generateTestFiles(t, storage.rootPath, domain)
err := storage.MoveToArchive(domain)
require.NoError(t, err)
for _, file := range domainFiles {
assert.NoFileExists(t, file)
}
root, err := os.ReadDir(storage.rootPath)
require.NoError(t, err)
require.Empty(t, root)
archive, err := os.ReadDir(storage.archivePath)
require.NoError(t, err)
require.Len(t, archive, len(domainFiles))
assert.Regexp(t, `\d+\.`+regexp.QuoteMeta(domain), archive[0].Name())
}
func TestCertificatesStorage_MoveToArchive_noFileRelatedToDomain(t *testing.T) {
domain := "example.com"
storage := CertificatesStorage{
rootPath: t.TempDir(),
archivePath: t.TempDir(),
}
domainFiles := generateTestFiles(t, storage.rootPath, "example.org")
err := storage.MoveToArchive(domain)
require.NoError(t, err)
for _, file := range domainFiles {
assert.FileExists(t, file)
}
root, err := os.ReadDir(storage.rootPath)
require.NoError(t, err)
assert.Len(t, root, len(domainFiles))
archive, err := os.ReadDir(storage.archivePath)
require.NoError(t, err)
assert.Empty(t, archive)
}
func TestCertificatesStorage_MoveToArchive_ambiguousDomain(t *testing.T) {
domain := "example.com"
storage := CertificatesStorage{
rootPath: t.TempDir(),
archivePath: t.TempDir(),
}
domainFiles := generateTestFiles(t, storage.rootPath, domain)
otherDomainFiles := generateTestFiles(t, storage.rootPath, domain+".example.org")
err := storage.MoveToArchive(domain)
require.NoError(t, err)
for _, file := range domainFiles {
assert.NoFileExists(t, file)
}
for _, file := range otherDomainFiles {
assert.FileExists(t, file)
}
root, err := os.ReadDir(storage.rootPath)
require.NoError(t, err)
require.Len(t, root, len(otherDomainFiles))
archive, err := os.ReadDir(storage.archivePath)
require.NoError(t, err)
require.Len(t, archive, len(domainFiles))
assert.Regexp(t, `\d+\.`+regexp.QuoteMeta(domain), archive[0].Name())
}
func generateTestFiles(t *testing.T, dir, domain string) []string {
t.Helper()
var filenames []string
for _, ext := range []string{issuerExt, certExt, keyExt, pemExt, pfxExt, resourceExt} {
filename := filepath.Join(dir, domain+ext)
err := os.WriteFile(filename, []byte("test"), 0o666)
require.NoError(t, err)
filenames = append(filenames, filename)
}
return filenames
}