2021-04-06 10:08:43 +00:00
|
|
|
package backup
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
portainer "github.com/portainer/portainer/api"
|
|
|
|
"github.com/portainer/portainer/api/archive"
|
|
|
|
"github.com/portainer/portainer/api/crypto"
|
2021-08-17 01:12:07 +00:00
|
|
|
"github.com/portainer/portainer/api/filesystem"
|
2021-04-06 10:08:43 +00:00
|
|
|
"github.com/portainer/portainer/api/http/offlinegate"
|
|
|
|
)
|
|
|
|
|
2021-04-07 00:12:19 +00:00
|
|
|
const rwxr__r__ os.FileMode = 0744
|
2021-04-06 10:08:43 +00:00
|
|
|
|
2021-09-09 23:12:21 +00:00
|
|
|
var filesToBackup = []string{
|
|
|
|
"certs",
|
|
|
|
"compose",
|
|
|
|
"config.json",
|
|
|
|
"custom_templates",
|
|
|
|
"edge_jobs",
|
|
|
|
"edge_stacks",
|
|
|
|
"extensions",
|
|
|
|
"portainer.key",
|
|
|
|
"portainer.pub",
|
|
|
|
"tls",
|
|
|
|
}
|
2021-04-06 10:08:43 +00:00
|
|
|
|
|
|
|
// Creates a tar.gz system archive and encrypts it if password is not empty. Returns a path to the archive file.
|
|
|
|
func CreateBackupArchive(password string, gate *offlinegate.OfflineGate, datastore portainer.DataStore, filestorePath string) (string, error) {
|
|
|
|
unlock := gate.Lock()
|
|
|
|
defer unlock()
|
|
|
|
|
|
|
|
backupDirPath := filepath.Join(filestorePath, "backup", time.Now().Format("2006-01-02_15-04-05"))
|
|
|
|
if err := os.MkdirAll(backupDirPath, rwxr__r__); err != nil {
|
|
|
|
return "", errors.Wrap(err, "Failed to create backup dir")
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := backupDb(backupDirPath, datastore); err != nil {
|
|
|
|
return "", errors.Wrap(err, "Failed to backup database")
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, filename := range filesToBackup {
|
2021-08-17 01:12:07 +00:00
|
|
|
err := filesystem.CopyPath(filepath.Join(filestorePath, filename), backupDirPath)
|
2021-04-06 10:08:43 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", errors.Wrap(err, "Failed to create backup file")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
archivePath, err := archive.TarGzDir(backupDirPath)
|
|
|
|
if err != nil {
|
|
|
|
return "", errors.Wrap(err, "Failed to make an archive")
|
|
|
|
}
|
|
|
|
|
|
|
|
if password != "" {
|
|
|
|
archivePath, err = encrypt(archivePath, password)
|
|
|
|
if err != nil {
|
|
|
|
return "", errors.Wrap(err, "Failed to encrypt backup with the password")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return archivePath, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func backupDb(backupDirPath string, datastore portainer.DataStore) error {
|
|
|
|
backupWriter, err := os.Create(filepath.Join(backupDirPath, "portainer.db"))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = datastore.BackupTo(backupWriter); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return backupWriter.Close()
|
|
|
|
}
|
|
|
|
|
|
|
|
func encrypt(path string, passphrase string) (string, error) {
|
|
|
|
in, err := os.Open(path)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
defer in.Close()
|
|
|
|
|
|
|
|
outFileName := fmt.Sprintf("%s.encrypted", path)
|
|
|
|
out, err := os.Create(outFileName)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = crypto.AesEncrypt(in, out, []byte(passphrase))
|
|
|
|
|
|
|
|
return outFileName, err
|
|
|
|
}
|