- Check cluster metadata first before single DB metadata - For cluster backups, mark as encrypted only if ANY database is encrypted - Remove double confirmation requirement for --workdir in dry-run mode - Fixes false positive 'encrypted backup detected' for unencrypted cluster backups This allows --clean-cluster and --workdir flags to work correctly with unencrypted backups.
127 lines
3.5 KiB
Go
127 lines
3.5 KiB
Go
package backup
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"dbbackup/internal/crypto"
|
|
"dbbackup/internal/logger"
|
|
"dbbackup/internal/metadata"
|
|
)
|
|
|
|
// EncryptBackupFile encrypts a backup file in-place
|
|
// The original file is replaced with the encrypted version
|
|
func EncryptBackupFile(backupPath string, key []byte, log logger.Logger) error {
|
|
log.Info("Encrypting backup file", "file", filepath.Base(backupPath))
|
|
|
|
// Validate key
|
|
if err := crypto.ValidateKey(key); err != nil {
|
|
return fmt.Errorf("invalid encryption key: %w", err)
|
|
}
|
|
|
|
// Create encryptor
|
|
encryptor := crypto.NewAESEncryptor()
|
|
|
|
// Generate encrypted file path
|
|
encryptedPath := backupPath + ".encrypted.tmp"
|
|
|
|
// Encrypt file
|
|
if err := encryptor.EncryptFile(backupPath, encryptedPath, key); err != nil {
|
|
// Clean up temp file on failure
|
|
os.Remove(encryptedPath)
|
|
return fmt.Errorf("encryption failed: %w", err)
|
|
}
|
|
|
|
// Update metadata to indicate encryption
|
|
metaPath := backupPath + ".meta.json"
|
|
if _, err := os.Stat(metaPath); err == nil {
|
|
// Load existing metadata
|
|
meta, err := metadata.Load(metaPath)
|
|
if err != nil {
|
|
log.Warn("Failed to load metadata for encryption update", "error", err)
|
|
} else {
|
|
// Mark as encrypted
|
|
meta.Encrypted = true
|
|
meta.EncryptionAlgorithm = string(crypto.AlgorithmAES256GCM)
|
|
|
|
// Save updated metadata
|
|
if err := metadata.Save(metaPath, meta); err != nil {
|
|
log.Warn("Failed to update metadata with encryption info", "error", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Remove original unencrypted file
|
|
if err := os.Remove(backupPath); err != nil {
|
|
log.Warn("Failed to remove original unencrypted file", "error", err)
|
|
// Don't fail - encrypted file exists
|
|
}
|
|
|
|
// Rename encrypted file to original name
|
|
if err := os.Rename(encryptedPath, backupPath); err != nil {
|
|
return fmt.Errorf("failed to rename encrypted file: %w", err)
|
|
}
|
|
|
|
log.Info("Backup encrypted successfully", "file", filepath.Base(backupPath))
|
|
return nil
|
|
}
|
|
|
|
// IsBackupEncrypted checks if a backup file is encrypted
|
|
func IsBackupEncrypted(backupPath string) bool {
|
|
// Check metadata first - try cluster metadata (for cluster backups)
|
|
// Try cluster metadata first
|
|
if clusterMeta, err := metadata.LoadCluster(backupPath); err == nil {
|
|
// For cluster backups, check if ANY database is encrypted
|
|
for _, db := range clusterMeta.Databases {
|
|
if db.Encrypted {
|
|
return true
|
|
}
|
|
}
|
|
// All databases are unencrypted
|
|
return false
|
|
}
|
|
|
|
// Try single database metadata
|
|
if meta, err := metadata.Load(backupPath); err == nil {
|
|
return meta.Encrypted
|
|
}
|
|
|
|
// Fallback: check if file starts with encryption nonce
|
|
file, err := os.Open(backupPath)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
defer file.Close()
|
|
|
|
// Try to read nonce - if it succeeds, likely encrypted
|
|
nonce := make([]byte, crypto.NonceSize)
|
|
if n, err := file.Read(nonce); err != nil || n != crypto.NonceSize {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// DecryptBackupFile decrypts an encrypted backup file
|
|
// Creates a new decrypted file
|
|
func DecryptBackupFile(encryptedPath, outputPath string, key []byte, log logger.Logger) error {
|
|
log.Info("Decrypting backup file", "file", filepath.Base(encryptedPath))
|
|
|
|
// Validate key
|
|
if err := crypto.ValidateKey(key); err != nil {
|
|
return fmt.Errorf("invalid decryption key: %w", err)
|
|
}
|
|
|
|
// Create encryptor
|
|
encryptor := crypto.NewAESEncryptor()
|
|
|
|
// Decrypt file
|
|
if err := encryptor.DecryptFile(encryptedPath, outputPath, key); err != nil {
|
|
return fmt.Errorf("decryption failed (wrong key?): %w", err)
|
|
}
|
|
|
|
log.Info("Backup decrypted successfully", "output", filepath.Base(outputPath))
|
|
return nil
|
|
}
|