Thanks to visit codestin.com
Credit goes to github.com

Skip to content
This repository was archived by the owner on Nov 3, 2022. It is now read-only.

Commit bfb0a43

Browse files
author
Endre Czirbesz
committed
Introduce encryption - GnuPG
1 parent 1a40e5a commit bfb0a43

File tree

8 files changed

+155
-16
lines changed

8 files changed

+155
-16
lines changed

Dockerfile

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ ARG VCS_REF
1919
ARG VERSION
2020

2121
ENV MONGODB_TOOLS_VERSION 4.2.1-r1
22-
ENV GOOGLE_CLOUD_SDK_VERSION 276.0.0
22+
ENV GNUPG_VERSION 2.2.19-r0
23+
ENV GOOGLE_CLOUD_SDK_VERSION 315.0.0
2324
ENV AZURE_CLI_VERSION 2.13.0
2425
ENV AWS_CLI_VERSION 1.18.159
2526
ENV PATH /root/google-cloud-sdk/bin:$PATH
@@ -34,7 +35,7 @@ LABEL org.label-schema.build-date=$BUILD_DATE \
3435
org.label-schema.version=$VERSION \
3536
org.label-schema.schema-version="1.0"
3637

37-
RUN apk add --no-cache ca-certificates tzdata mongodb-tools=${MONGODB_TOOLS_VERSION}
38+
RUN apk add --no-cache ca-certificates tzdata mongodb-tools=${MONGODB_TOOLS_VERSION} gnupg=${GNUPG_VERSION}
3839
ADD https://dl.minio.io/client/mc/release/linux-amd64/mc /usr/bin
3940
RUN chmod u+x /usr/bin/mc
4041

@@ -71,7 +72,7 @@ RUN apk --no-cache add \
7172
# install azure-cli and aws-cli
7273
RUN apk --no-cache add --virtual=build gcc libffi-dev musl-dev openssl-dev python3-dev make && \
7374
pip --no-cache-dir install cffi && \
74-
pip --no-cache-dir install azure-cli==${AZURE_CLI_VERSION} && \
75+
pip --no-cache-dir --use-feature=2020-resolver install azure-cli==${AZURE_CLI_VERSION} && \
7576
pip --no-cache-dir install awscli==${AWS_CLI_VERSION} && \
7677
apk del --purge build
7778

cmd/mgob/mgob.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import (
1818

1919
var (
2020
appConfig = &config.AppConfig{}
21-
version = "v1.2.0-dev"
21+
version = "v1.3.0-dev"
2222
)
2323

2424
func beforeApp(c *cli.Context) error {
@@ -95,10 +95,12 @@ func start(c *cli.Context) error {
9595
appConfig.TmpPath = c.String("TmpPath")
9696
appConfig.DataPath = c.String("DataPath")
9797
appConfig.Version = version
98-
appConfig.UseAwsCli = true
9998

10099
log.Infof("starting with config: %+v", appConfig)
101100

101+
appConfig.UseAwsCli = true
102+
appConfig.HasGpg = true
103+
102104
info, err := backup.CheckMongodump()
103105
if err != nil {
104106
log.Fatal(err)
@@ -118,6 +120,13 @@ func start(c *cli.Context) error {
118120
}
119121
log.Info(info)
120122

123+
info, err = backup.CheckGpg()
124+
if err != nil {
125+
log.Warn(err)
126+
appConfig.HasGpg = false
127+
}
128+
log.Info(info)
129+
121130
info, err = backup.CheckGCloudClient()
122131
if err != nil {
123132
log.Fatal(err)

pkg/backup/backup.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,18 @@ func Run(plan config.Plan, conf *config.AppConfig) (Result, error) {
7373

7474
file := filepath.Join(planDir, res.Name)
7575

76+
if plan.Encryption != nil {
77+
encryptedFile := fmt.Sprintf("%v.encrypted", file)
78+
output, err := encrypt(file, encryptedFile, plan, conf)
79+
if err != nil {
80+
return res, err
81+
} else {
82+
removeUnencrypted(file, encryptedFile)
83+
file = encryptedFile
84+
log.WithField("plan", plan.Name).Infof("Encryption finished %v", output)
85+
}
86+
}
87+
7688
if plan.SFTP != nil {
7789
sftpOutput, err := sftpUpload(file, plan)
7890
if err != nil {

pkg/backup/checks.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,19 @@ func CheckAWSClient() (string, error) {
4646
return strings.Replace(string(output), "\n", " ", -1), nil
4747
}
4848

49+
func CheckGpg() (string, error) {
50+
output, err := sh.Command("/bin/sh", "-c", "gpg --version").CombinedOutput()
51+
if err != nil {
52+
ex := ""
53+
if len(output) > 0 {
54+
ex = strings.Replace(string(output), "\n", " ", -1)
55+
}
56+
return "", errors.Wrapf(err, "gpg failed %v", ex)
57+
}
58+
59+
return strings.Replace(string(output), "\n", " ", -1), nil
60+
}
61+
4962
func CheckGCloudClient() (string, error) {
5063
output, err := sh.Command("/bin/sh", "-c", "gcloud --version").CombinedOutput()
5164
if err != nil {

pkg/backup/encrypt.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package backup
2+
3+
import (
4+
"fmt"
5+
"github.com/codeskyblue/go-sh"
6+
"github.com/pkg/errors"
7+
log "github.com/sirupsen/logrus"
8+
"github.com/stefanprodan/mgob/pkg/config"
9+
"os"
10+
"regexp"
11+
"strings"
12+
)
13+
14+
func encrypt(file string, encryptedFile string, plan config.Plan, conf *config.AppConfig) (string, error) {
15+
if plan.Encryption.Gpg != nil {
16+
if !conf.HasGpg {
17+
return "", errors.Errorf("GPG configuration is present, but no GPG binary is found! Uploading unencrypted backup.")
18+
}
19+
return gpgEncrypt(file, encryptedFile, plan)
20+
}
21+
22+
return "", errors.Errorf("Encryption config is not valid!")
23+
}
24+
25+
func removeUnencrypted(file string, encryptedFile string) {
26+
// Check if encrypted file exists and remove original
27+
stat, err := os.Stat(encryptedFile)
28+
if err == nil && stat.Size() > 0 {
29+
os.Remove(file)
30+
}
31+
}
32+
33+
func gpgEncrypt(file string, encryptedFile string, plan config.Plan) (string, error) {
34+
output := ""
35+
recipient := ""
36+
37+
recipients := plan.Encryption.Gpg.Recipients
38+
39+
keyFile := plan.Encryption.Gpg.KeyFile
40+
if keyFile != "" {
41+
keyFileStat, err := os.Stat(keyFile)
42+
if err == nil && !keyFileStat.IsDir() {
43+
// import key from file
44+
importCmd := fmt.Sprintf("gpg --batch --import %v", keyFile)
45+
46+
result, err := sh.Command("/bin/sh", "-c", importCmd).CombinedOutput()
47+
if len(result) > 0 {
48+
output += strings.Replace(string(result), "\n", " ", -1)
49+
}
50+
if err != nil {
51+
return "", errors.Wrapf(err, "Importing encryption key for plan %v failed %s", plan.Name, output)
52+
}
53+
if !strings.Contains(output, "imported: 1") && !strings.Contains(output, "unchanged: 1") {
54+
return "", errors.Errorf("Importing encryption key failed %v", output)
55+
}
56+
57+
re := regexp.MustCompile(`key ([0-9A-F]+):`)
58+
keyMatch := re.FindStringSubmatch(output)
59+
log.WithField("plan", plan.Name).Debugf("Import output: %v", output)
60+
log.WithField("plan", plan.Name).Debugf("Parsed key id: %v", keyMatch[1])
61+
if keyMatch != nil {
62+
recipients = append(recipients, keyMatch[1])
63+
}
64+
}
65+
}
66+
67+
recipient = strings.Join(recipients, " -r ")
68+
69+
if recipient == "" {
70+
return "", errors.Errorf("GPG configuration is present, but no encryption key is configured! %v", output)
71+
}
72+
73+
keyServer := plan.Encryption.Gpg.KeyServer
74+
if keyServer == "" {
75+
keyServer = "hkps://keys.openpgp.org"
76+
}
77+
78+
// encrypt file
79+
encryptCmd := fmt.Sprintf(
80+
"gpg -v --batch --yes --trust-model always --auto-key-locate local,%v -e -r %v -o %v %v",
81+
keyServer, recipient, encryptedFile, file)
82+
83+
result, err := sh.Command("/bin/sh", "-c", encryptCmd).CombinedOutput()
84+
if len(result) > 0 {
85+
output += strings.Replace(string(result), "\n", " ", -1)
86+
}
87+
if err != nil {
88+
return "", errors.Wrapf(err, "Encryption for plan %v failed %s", plan.Name, output)
89+
}
90+
91+
return output, nil
92+
}

pkg/backup/local.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ func logToFile(file string, data []byte) error {
6767
}
6868

6969
func applyRetention(path string, retention int) error {
70-
gz := fmt.Sprintf("cd %v && rm -f $(ls -1t *.gz | tail -n +%v)", path, retention+1)
70+
gz := fmt.Sprintf("cd %v && rm -f $(ls -1t *.gz *.gz.encrypted | tail -n +%v)", path, retention+1)
7171
err := sh.Command("/bin/sh", "-c", gz).Run()
7272
if err != nil {
7373
return errors.Wrapf(err, "removing old gz files from %v failed", path)

pkg/config/app.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ type AppConfig struct {
1010
DataPath string `json:"data_path"`
1111
Version string `json:"version"`
1212
UseAwsCli bool `json:"use_aws_cli"`
13+
HasGpg bool `json:"has_gpg"`
1314
}

pkg/config/plan.go

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,17 @@ import (
1111
)
1212

1313
type Plan struct {
14-
Name string `yaml:"name"`
15-
Target Target `yaml:"target"`
16-
Scheduler Scheduler `yaml:"scheduler"`
17-
S3 *S3 `yaml:"s3"`
18-
GCloud *GCloud `yaml:"gcloud"`
19-
Rclone *Rclone `yaml:"rclone"`
20-
Azure *Azure `yaml:"azure"`
21-
SFTP *SFTP `yaml:"sftp"`
22-
SMTP *SMTP `yaml:"smtp"`
23-
Slack *Slack `yaml:"slack"`
14+
Name string `yaml:"name"`
15+
Target Target `yaml:"target"`
16+
Scheduler Scheduler `yaml:"scheduler"`
17+
Encryption *Encryption `yaml:"encryption"`
18+
S3 *S3 `yaml:"s3"`
19+
GCloud *GCloud `yaml:"gcloud"`
20+
Rclone *Rclone `yaml:"rclone"`
21+
Azure *Azure `yaml:"azure"`
22+
SFTP *SFTP `yaml:"sftp"`
23+
SMTP *SMTP `yaml:"smtp"`
24+
Slack *Slack `yaml:"slack"`
2425
}
2526

2627
type Target struct {
@@ -39,6 +40,16 @@ type Scheduler struct {
3940
Timeout int `yaml:"timeout"`
4041
}
4142

43+
type Encryption struct {
44+
Gpg *Gpg `yaml:"gpg"`
45+
}
46+
47+
type Gpg struct {
48+
KeyServer string `yaml:"keyServer"`
49+
Recipients []string `yaml:"recipients"`
50+
KeyFile string `yaml:"keyFile"`
51+
}
52+
4253
type S3 struct {
4354
Bucket string `yaml:"bucket"`
4455
AccessKey string `yaml:"accessKey"`

0 commit comments

Comments
 (0)