diff --git a/go.mod b/go.mod index 6afc2e4..00cf44b 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/jkaninda/pg-bkup -go 1.21.0 +go 1.23.2 require ( github.com/ProtonMail/gopenpgp/v2 v2.7.5 @@ -21,6 +21,7 @@ require ( github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jkaninda/encryptor v0.0.0-20241013043504-6641402116a4 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/spf13/pflag v1.0.5 // indirect diff --git a/go.sum b/go.sum index b55f810..605560f 100644 --- a/go.sum +++ b/go.sum @@ -24,6 +24,10 @@ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+l github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jkaninda/encryptor v0.0.0-20241012162628-4c2d36abccd2 h1:UxaO/pwcwKXJvOXs114IiLP5fGcf4EOuh3tUFuJDCD8= +github.com/jkaninda/encryptor v0.0.0-20241012162628-4c2d36abccd2/go.mod h1:ZSChjAr+RL0h1fAgnPuINEcwZXxzWrUiTzm/JdVZ8W4= +github.com/jkaninda/encryptor v0.0.0-20241013043504-6641402116a4 h1:FfVePubMVwx9LVEisqtgCxP/P83gLhenzYRyyFnKceU= +github.com/jkaninda/encryptor v0.0.0-20241013043504-6641402116a4/go.mod h1:9F8ZJ+ZXE8DZBo77+aneGj8LMjrYXX6eFUCC/uqZOUo= github.com/jlaffaye/ftp v0.2.0 h1:lXNvW7cBu7R/68bknOX3MrRIIqZ61zELs1P2RAiA3lg= github.com/jlaffaye/ftp v0.2.0/go.mod h1:is2Ds5qkhceAPy2xD6RLI6hmp/qysSoymZ+Z2uTnspI= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= diff --git a/pkg/backup.go b/pkg/backup.go index 9861f46..f25fa4c 100644 --- a/pkg/backup.go +++ b/pkg/backup.go @@ -8,6 +8,7 @@ package pkg import ( "fmt" + "github.com/jkaninda/encryptor" "github.com/jkaninda/pg-bkup/utils" "github.com/robfig/cron/v3" "github.com/spf13/cobra" @@ -423,16 +424,30 @@ func ftpBackup(db *dbConfig, config *BackupConfig) { } func encryptBackup(config *BackupConfig) { + backupFile, err := os.ReadFile(filepath.Join(tmpPath, config.backupFileName)) + outputFile := fmt.Sprintf("%s.%s", filepath.Join(tmpPath, config.backupFileName), gpgExtension) + if err != nil { + utils.Fatal("Error reading backup file: %s ", err) + } if config.usingKey { - err := encryptWithGPGPublicKey(filepath.Join(tmpPath, config.backupFileName), config.publicKey) + utils.Info("Encrypting backup using public key...") + pubKey, err := os.ReadFile(config.publicKey) if err != nil { - utils.Fatal("error during encrypting backup %v", err) + utils.Fatal("Error reading public key: %s ", err) } + err = encryptor.EncryptWithPublicKey(backupFile, fmt.Sprintf("%s.%s", filepath.Join(tmpPath, config.backupFileName), gpgExtension), pubKey) + if err != nil { + utils.Fatal("Error encrypting backup file: %v ", err) + } + utils.Info("Encrypting backup using public key...done") + } else if config.passphrase != "" { - err := encryptWithGPG(filepath.Join(tmpPath, config.backupFileName), config.passphrase) + utils.Info("Encrypting backup using passphrase...") + err := encryptor.Encrypt(backupFile, outputFile, config.passphrase) if err != nil { utils.Fatal("error during encrypting backup %v", err) } + utils.Info("Encrypting backup using passphrase...done") } diff --git a/pkg/encrypt.go b/pkg/encrypt.go deleted file mode 100644 index cf5a7ef..0000000 --- a/pkg/encrypt.go +++ /dev/null @@ -1,182 +0,0 @@ -// Package pkg / -/***** -@author Jonas Kaninda -@license MIT License -@Copyright © 2024 Jonas Kaninda -**/ -package pkg - -import ( - "errors" - "fmt" - "github.com/ProtonMail/gopenpgp/v2/crypto" - "github.com/jkaninda/pg-bkup/utils" - "os" - "strings" -) - -// decryptWithGPG decrypts backup file using a passphrase -func decryptWithGPG(inputFile string, passphrase string) error { - utils.Info("Decrypting backup using passphrase...") - // Read the encrypted file - encFileContent, err := os.ReadFile(inputFile) - if err != nil { - return errors.New(fmt.Sprintf("Error reading encrypted file: %s", err)) - } - // Define the passphrase used to encrypt the file - _passphrase := []byte(passphrase) - // Create a PGP message object from the encrypted file content - encryptedMessage := crypto.NewPGPMessage(encFileContent) - // Decrypt the message using the passphrase - plainMessage, err := crypto.DecryptMessageWithPassword(encryptedMessage, _passphrase) - if err != nil { - return errors.New(fmt.Sprintf("Error decrypting file: %s", err)) - } - - // Save the decrypted file (restore it) - err = os.WriteFile(RemoveLastExtension(inputFile), plainMessage.GetBinary(), 0644) - if err != nil { - return errors.New(fmt.Sprintf("Error saving decrypted file: %s", err)) - } - utils.Info("Decrypting backup using passphrase...done") - utils.Info("Backup file decrypted successful!") - return nil -} - -// encryptWithGPG encrypts backup using a passphrase -func encryptWithGPG(inputFile string, passphrase string) error { - utils.Info("Encrypting backup using passphrase...") - // Read the file to be encrypted - plainFileContent, err := os.ReadFile(inputFile) - if err != nil { - return errors.New(fmt.Sprintf("Error reading file: %s", err)) - } - // Define the passphrase to encrypt the file - _passphrase := []byte(passphrase) - - // Create a message object from the file content - message := crypto.NewPlainMessage(plainFileContent) - // Encrypt the message using the passphrase - encryptedMessage, err := crypto.EncryptMessageWithPassword(message, _passphrase) - if err != nil { - return errors.New(fmt.Sprintf("Error encrypting backup file: %s", err)) - } - // Save the encrypted .tar file - err = os.WriteFile(fmt.Sprintf("%s.%s", inputFile, gpgExtension), encryptedMessage.GetBinary(), 0644) - if err != nil { - return errors.New(fmt.Sprintf("Error saving encrypted filee: %s", err)) - } - utils.Info("Encrypting backup using passphrase...done") - utils.Info("Backup file encrypted successful!") - return nil -} - -// encryptWithGPGPublicKey encrypts backup using a public key -func encryptWithGPGPublicKey(inputFile string, publicKey string) error { - utils.Info("Encrypting backup using public key...") - // Read the public key - pubKeyBytes, err := os.ReadFile(publicKey) - if err != nil { - return errors.New(fmt.Sprintf("Error reading public key: %s", err)) - } - // Create a new keyring with the public key - publicKeyObj, err := crypto.NewKeyFromArmored(string(pubKeyBytes)) - if err != nil { - return errors.New(fmt.Sprintf("Error parsing public key: %s", err)) - } - - keyRing, err := crypto.NewKeyRing(publicKeyObj) - if err != nil { - - return errors.New(fmt.Sprintf("Error creating key ring: %v", err)) - } - - // Read the file to encryptWithGPGPublicKey - fileContent, err := os.ReadFile(inputFile) - if err != nil { - return errors.New(fmt.Sprintf("Error reading file: %v", err)) - } - - // encryptWithGPG the file - message := crypto.NewPlainMessage(fileContent) - encMessage, err := keyRing.Encrypt(message, nil) - if err != nil { - return errors.New(fmt.Sprintf("Error encrypting file: %v", err)) - } - - // Save the encrypted file - err = os.WriteFile(fmt.Sprintf("%s.%s", inputFile, gpgExtension), encMessage.GetBinary(), 0644) - if err != nil { - return errors.New(fmt.Sprintf("Error saving encrypted file: %v", err)) - } - utils.Info("Encrypting backup using public key...done") - utils.Info("Backup file encrypted successful!") - return nil - -} - -// decryptWithGPGPrivateKey decrypts backup file using a private key and passphrase. -// privateKey GPG private key -// passphrase GPG passphrase -func decryptWithGPGPrivateKey(inputFile, privateKey, passphrase string) error { - utils.Info("Encrypting backup using private key...") - - // Read the private key - priKeyBytes, err := os.ReadFile(privateKey) - if err != nil { - return errors.New(fmt.Sprintf("Error reading private key: %s", err)) - } - - // Read the password for the private key (if it’s password-protected) - password := []byte(passphrase) - - // Create a key object from the armored private key - privateKeyObj, err := crypto.NewKeyFromArmored(string(priKeyBytes)) - if err != nil { - return errors.New(fmt.Sprintf("Error parsing private key: %s", err)) - } - - // Unlock the private key with the password - if passphrase != "" { - // Unlock the private key with the password - _, err = privateKeyObj.Unlock(password) - if err != nil { - return errors.New(fmt.Sprintf("Error unlocking private key: %s", err)) - } - - } - - // Create a new keyring with the private key - keyRing, err := crypto.NewKeyRing(privateKeyObj) - if err != nil { - return errors.New(fmt.Sprintf("Error creating key ring: %v", err)) - } - - // Read the encrypted file - encFileContent, err := os.ReadFile(inputFile) - if err != nil { - return errors.New(fmt.Sprintf("Error reading encrypted file: %s", err)) - } - - // decryptWithGPG the file - encryptedMessage := crypto.NewPGPMessage(encFileContent) - message, err := keyRing.Decrypt(encryptedMessage, nil, 0) - if err != nil { - return errors.New(fmt.Sprintf("Error decrypting file: %s", err)) - } - - // Save the decrypted file - err = os.WriteFile(RemoveLastExtension(inputFile), message.GetBinary(), 0644) - if err != nil { - return errors.New(fmt.Sprintf("Error saving decrypted file: %s", err)) - } - utils.Info("Encrypting backup using public key...done") - fmt.Println("File successfully decrypted!") - return nil -} -func RemoveLastExtension(filename string) string { - if idx := strings.LastIndex(filename, "."); idx != -1 { - return filename[:idx] - } - return filename -} diff --git a/pkg/helper.go b/pkg/helper.go index 8586631..2095e0a 100644 --- a/pkg/helper.go +++ b/pkg/helper.go @@ -14,6 +14,7 @@ import ( "os" "os/exec" "path/filepath" + "strings" "time" ) @@ -233,3 +234,9 @@ func checkConfigFile(filePath string) (string, error) { // Return an error if neither file exists return "", fmt.Errorf("no config file found") } +func RemoveLastExtension(filename string) string { + if idx := strings.LastIndex(filename, "."); idx != -1 { + return filename[:idx] + } + return filename +} diff --git a/pkg/restore.go b/pkg/restore.go index 7f5811f..dbb1e1d 100644 --- a/pkg/restore.go +++ b/pkg/restore.go @@ -7,6 +7,7 @@ package pkg import ( + "github.com/jkaninda/encryptor" "github.com/jkaninda/pg-bkup/utils" "github.com/spf13/cobra" "os" @@ -68,24 +69,40 @@ func RestoreDatabase(db *dbConfig, conf *RestoreConfig) { utils.Fatal("Error, file required") } extension := filepath.Ext(filepath.Join(tmpPath, conf.file)) + rFile, err := os.ReadFile(filepath.Join(tmpPath, conf.file)) + outputFile := RemoveLastExtension(filepath.Join(tmpPath, conf.file)) + if err != nil { + utils.Fatal("Error reading backup file: %s ", err) + } + if extension == ".gpg" { if conf.usingKey { + utils.Info("Decrypting backup using private key...") utils.Warn("Backup decryption using a private key is not fully supported") - err := decryptWithGPGPrivateKey(filepath.Join(tmpPath, conf.file), conf.privateKey, conf.passphrase) + prKey, err := os.ReadFile(conf.privateKey) + if err != nil { + utils.Fatal("Error reading public key: %s ", err) + } + err = encryptor.DecryptWithPrivateKey(rFile, outputFile, prKey, conf.passphrase) if err != nil { utils.Fatal("error during decrypting backup %v", err) } + utils.Info("Decrypting backup using private key...done") + } else { if conf.passphrase == "" { utils.Error("Error, passphrase or private key required") utils.Fatal("Your file seems to be a GPG file.\nYou need to provide GPG keys. GPG_PASSPHRASE or GPG_PRIVATE_KEY environment variable is required.") } else { + utils.Info("Decrypting backup using passphrase...") + //decryptWithGPG file - err := decryptWithGPG(filepath.Join(tmpPath, conf.file), conf.passphrase) + err := encryptor.Decrypt(rFile, outputFile, conf.passphrase) if err != nil { utils.Fatal("Error decrypting file %s %v", file, err) } + utils.Info("Decrypting backup using private key...done") //Update file name conf.file = RemoveLastExtension(file) }