Add SSH remote backup
This commit is contained in:
@@ -24,12 +24,13 @@ ENV AWS_REGION="us-west-2"
|
|||||||
ENV AWS_DISABLE_SSL="false"
|
ENV AWS_DISABLE_SSL="false"
|
||||||
ENV GPG_PASSPHRASE=""
|
ENV GPG_PASSPHRASE=""
|
||||||
ENV SSH_USER=""
|
ENV SSH_USER=""
|
||||||
|
ENV SSH_REMOTE_PATH=""
|
||||||
ENV SSH_PASSWORD=""
|
ENV SSH_PASSWORD=""
|
||||||
ENV SSH_HOST_NAME=""
|
ENV SSH_HOST_NAME=""
|
||||||
ENV SSH_IDENTIFY_FILE="/root/.ssh/id_rsa"
|
ENV SSH_IDENTIFY_FILE=""
|
||||||
ENV SSH_PORT="22"
|
ENV SSH_PORT="22"
|
||||||
ARG DEBIAN_FRONTEND=noninteractive
|
ARG DEBIAN_FRONTEND=noninteractive
|
||||||
ENV VERSION="v0.8"
|
ENV VERSION="v1.0"
|
||||||
ARG WORKDIR="/app"
|
ARG WORKDIR="/app"
|
||||||
ARG BACKUPDIR="/backup"
|
ARG BACKUPDIR="/backup"
|
||||||
ARG BACKUP_TMP_DIR="/tmp/backup"
|
ARG BACKUP_TMP_DIR="/tmp/backup"
|
||||||
|
|||||||
2
go.mod
2
go.mod
@@ -9,8 +9,10 @@ require (
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/aws/aws-sdk-go v1.55.3 // indirect
|
github.com/aws/aws-sdk-go v1.55.3 // indirect
|
||||||
|
github.com/bramvdbogaerde/go-scp v1.5.0 // indirect
|
||||||
github.com/hpcloud/tail v1.0.0 // indirect
|
github.com/hpcloud/tail v1.0.0 // indirect
|
||||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||||
|
golang.org/x/crypto v0.18.0 // indirect
|
||||||
golang.org/x/sys v0.22.0 // indirect
|
golang.org/x/sys v0.22.0 // indirect
|
||||||
gopkg.in/fsnotify.v1 v1.4.7 // indirect
|
gopkg.in/fsnotify.v1 v1.4.7 // indirect
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||||
|
|||||||
4
go.sum
4
go.sum
@@ -1,5 +1,7 @@
|
|||||||
github.com/aws/aws-sdk-go v1.55.3 h1:0B5hOX+mIx7I5XPOrjrHlKSDQV/+ypFZpIHOx5LOk3E=
|
github.com/aws/aws-sdk-go v1.55.3 h1:0B5hOX+mIx7I5XPOrjrHlKSDQV/+ypFZpIHOx5LOk3E=
|
||||||
github.com/aws/aws-sdk-go v1.55.3/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
|
github.com/aws/aws-sdk-go v1.55.3/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
|
||||||
|
github.com/bramvdbogaerde/go-scp v1.5.0 h1:a9BinAjTfQh273eh7vd3qUgmBC+bx+3TRDtkZWmIpzM=
|
||||||
|
github.com/bramvdbogaerde/go-scp v1.5.0/go.mod h1:on2aH5AxaFb2G0N5Vsdy6B0Ml7k9HuHSwfo1y0QzAbQ=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
@@ -35,6 +37,8 @@ github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyh
|
|||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
|
||||||
|
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||||
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
|
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
|
||||||
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
|
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
|
||||||
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
|
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ func StartBackup(cmd *cobra.Command) {
|
|||||||
case "ssh":
|
case "ssh":
|
||||||
sshBackup(backupFileName, s3Path, disableCompression, prune, backupRetention, encryption)
|
sshBackup(backupFileName, s3Path, disableCompression, prune, backupRetention, encryption)
|
||||||
case "ftp":
|
case "ftp":
|
||||||
fmt.Println("x is 3")
|
utils.Fatalf("Not supported storage type: %s", storage)
|
||||||
default:
|
default:
|
||||||
localBackup(backupFileName, disableCompression, prune, backupRetention, encryption)
|
localBackup(backupFileName, disableCompression, prune, backupRetention, encryption)
|
||||||
}
|
}
|
||||||
@@ -241,8 +241,35 @@ func s3Backup(backupFileName string, s3Path string, disableCompression bool, pru
|
|||||||
}
|
}
|
||||||
utils.Done("Database has been backed up and uploaded to s3 ")
|
utils.Done("Database has been backed up and uploaded to s3 ")
|
||||||
}
|
}
|
||||||
func sshBackup(backupFileName string, s3Path string, disableCompression bool, prune bool, backupRetention int, encrypt bool) {
|
func sshBackup(backupFileName string, remotePath string, disableCompression bool, prune bool, backupRetention int, encrypt bool) {
|
||||||
|
utils.Info("Backup database to Remote server")
|
||||||
|
//Backup database
|
||||||
|
BackupDatabase(backupFileName, disableCompression)
|
||||||
|
finalFileName := backupFileName
|
||||||
|
if encrypt {
|
||||||
|
encryptBackup(backupFileName)
|
||||||
|
finalFileName = fmt.Sprintf("%s.%s", backupFileName, "gpg")
|
||||||
|
}
|
||||||
|
utils.Info("Uploading backup file to S3 storage...")
|
||||||
|
utils.Info("Backup name is ", backupFileName)
|
||||||
|
err := CopyToRemote(filepath.Join(tmpPath, finalFileName), remotePath)
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("Error uploading file to S3: %s ", err)
|
||||||
|
|
||||||
|
}
|
||||||
|
//Delete backup file from tmp folder
|
||||||
|
err = utils.DeleteFile(filepath.Join(tmpPath, finalFileName))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error deleting file:", err)
|
||||||
|
|
||||||
|
}
|
||||||
|
if prune {
|
||||||
|
//TODO: Delete old backup from remote server
|
||||||
|
utils.Info("Deleting old backup from a remote server is not implemented yet")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.Done("Database has been backed up and uploaded to remote server ")
|
||||||
}
|
}
|
||||||
|
|
||||||
func encryptBackup(backupFileName string) {
|
func encryptBackup(backupFileName string) {
|
||||||
|
|||||||
100
pkg/scp.go
Normal file
100
pkg/scp.go
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
package pkg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/bramvdbogaerde/go-scp"
|
||||||
|
"github.com/bramvdbogaerde/go-scp/auth"
|
||||||
|
"github.com/jkaninda/pg-bkup/utils"
|
||||||
|
"golang.org/x/crypto/ssh"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CopyToRemote(fileName, remotePath string) error {
|
||||||
|
sshUser := os.Getenv("SSH_USER")
|
||||||
|
sshPassword := os.Getenv("SSH_PASSWORD")
|
||||||
|
sshHostName := os.Getenv("SSH_HOST_NAME")
|
||||||
|
sshPort := os.Getenv("SSH_PORT")
|
||||||
|
sshIdentifyFile := os.Getenv("SSH_IDENTIFY_FILE")
|
||||||
|
|
||||||
|
clientConfig, _ := auth.PasswordKey(sshUser, sshPassword, ssh.InsecureIgnoreHostKey())
|
||||||
|
if sshIdentifyFile != "" && utils.FileExists(sshIdentifyFile) {
|
||||||
|
clientConfig, _ = auth.PrivateKey(sshUser, sshIdentifyFile, ssh.InsecureIgnoreHostKey())
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if sshPassword == "" {
|
||||||
|
return errors.New("SSH_PASSWORD environment variable is required if SSH_IDENTIFY_FILE is empty\n")
|
||||||
|
}
|
||||||
|
clientConfig, _ = auth.PasswordKey(sshUser, sshPassword, ssh.InsecureIgnoreHostKey())
|
||||||
|
|
||||||
|
}
|
||||||
|
// Create a new SCP client
|
||||||
|
client := scp.NewClient(fmt.Sprintf("%s:%s", sshHostName, sshPort), &clientConfig)
|
||||||
|
|
||||||
|
// Connect to the remote server
|
||||||
|
err := client.Connect()
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("Couldn't establish a connection to the remote server\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open a file
|
||||||
|
file, _ := os.Open(fileName)
|
||||||
|
|
||||||
|
// Close client connection after the file has been copied
|
||||||
|
defer client.Close()
|
||||||
|
// Close the file after it has been copied
|
||||||
|
defer file.Close()
|
||||||
|
// the context can be adjusted to provide time-outs or inherit from other contexts if this is embedded in a larger application.
|
||||||
|
err = client.CopyFromFile(context.Background(), *file, remotePath, "0655")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error while copying file ")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CopyFromRemote(fileName, remotePath string) error {
|
||||||
|
sshUser := os.Getenv("SSH_USER")
|
||||||
|
sshPassword := os.Getenv("SSH_PASSWORD")
|
||||||
|
sshHostName := os.Getenv("SSH_HOST_NAME")
|
||||||
|
sshPort := os.Getenv("SSH_PORT")
|
||||||
|
sshIdentifyFile := os.Getenv("SSH_IDENTIFY_FILE")
|
||||||
|
|
||||||
|
clientConfig, _ := auth.PasswordKey(sshUser, sshPassword, ssh.InsecureIgnoreHostKey())
|
||||||
|
if sshIdentifyFile != "" && utils.FileExists(sshIdentifyFile) {
|
||||||
|
clientConfig, _ = auth.PrivateKey(sshUser, sshIdentifyFile, ssh.InsecureIgnoreHostKey())
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if sshPassword == "" {
|
||||||
|
return errors.New("SSH_PASSWORD environment variable is required if SSH_IDENTIFY_FILE is empty\n")
|
||||||
|
}
|
||||||
|
clientConfig, _ = auth.PasswordKey(sshUser, sshPassword, ssh.InsecureIgnoreHostKey())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new SCP client
|
||||||
|
client := scp.NewClient(fmt.Sprintf("%s:%s", sshHostName, sshPort), &clientConfig)
|
||||||
|
// Connect to the remote server
|
||||||
|
err := client.Connect()
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("Couldn't establish a connection to the remote server\n")
|
||||||
|
}
|
||||||
|
// Close client connection after the file has been copied
|
||||||
|
defer client.Close()
|
||||||
|
file, err := os.OpenFile(fileName, os.O_RDWR|os.O_CREATE, 0777)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Couldn't open the output file")
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
// the context can be adjusted to provide time-outs or inherit from other contexts if this is embedded in a larger application.
|
||||||
|
err = client.CopyFromRemote(context.Background(), file, remotePath)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error while copying file ", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user