forked from TrueCloudLab/lego
fix: archive only domain-related files on revoke (#1874)
Co-authored-by: Fernandez Ludovic <ldez@users.noreply.github.com>
This commit is contained in:
parent
3d44184074
commit
510aa8f871
2 changed files with 138 additions and 8 deletions
|
@ -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
115
cmd/certs_storage_test.go
Normal 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
|
||||||
|
}
|
Loading…
Reference in a new issue