From 33c2361ab6f0d7bfc802d2fc10d2f29aef44867b Mon Sep 17 00:00:00 2001 From: Till Höppner Date: Wed, 31 May 2017 18:00:15 +0200 Subject: Add backup image --- images/backup/.gitignore | 1 + images/backup/backup.service | 11 ++++ images/backup/backup.timer | 9 +++ images/backup/backup.yml | 21 +++++++ images/backup/duplicity/Dockerfile | 16 +++++ images/backup/duplicity/backup.sh | 39 ++++++++++++ images/backup/duplicity/gpg-agent.conf | 1 + images/backup/duplicity/restore.sh | 26 ++++++++ images/backup/gpg_keys/backups.pub.key | 108 +++++++++++++++++++++++++++++++++ images/backup/restore.yml | 16 +++++ 10 files changed, 248 insertions(+) create mode 100644 images/backup/.gitignore create mode 100644 images/backup/backup.service create mode 100644 images/backup/backup.timer create mode 100644 images/backup/backup.yml create mode 100644 images/backup/duplicity/Dockerfile create mode 100755 images/backup/duplicity/backup.sh create mode 100644 images/backup/duplicity/gpg-agent.conf create mode 100755 images/backup/duplicity/restore.sh create mode 100644 images/backup/gpg_keys/backups.pub.key create mode 100644 images/backup/restore.yml (limited to 'images') diff --git a/images/backup/.gitignore b/images/backup/.gitignore new file mode 100644 index 0000000..2549b3d --- /dev/null +++ b/images/backup/.gitignore @@ -0,0 +1 @@ +config.env diff --git a/images/backup/backup.service b/images/backup/backup.service new file mode 100644 index 0000000..f4cce9b --- /dev/null +++ b/images/backup/backup.service @@ -0,0 +1,11 @@ +[Unit] +Description=Duplicity backups +Requires=docker.service + +[Service] +Restart=no +ExecStart=/bin/sh -c 'docker-compose -f /storage/disk0/server15_backup/backup.yml up --no-recreate' +ExecStop=/bin/sh -c 'docker-compose -f /storage/disk0/server15_backup/restore.yml stop' + +[Install] +WantedBy=local.target diff --git a/images/backup/backup.timer b/images/backup/backup.timer new file mode 100644 index 0000000..35bf358 --- /dev/null +++ b/images/backup/backup.timer @@ -0,0 +1,9 @@ +[Unit] +Description=Run backups daily + +[Timer] +Persistent=true +OnCalendar=04:04 + +[Install] +WantedBy=timers.target diff --git a/images/backup/backup.yml b/images/backup/backup.yml new file mode 100644 index 0000000..c182b8d --- /dev/null +++ b/images/backup/backup.yml @@ -0,0 +1,21 @@ +version: '3' + +services: + backup: + env_file: + - config.env + build: ./duplicity + command: /bin/sh /backup.sh + hostname: duplicity + restart: "no" + volumes: + - cache:/cache + - ./gpg_keys:/gpg_keys:ro + + - git_data:/data/git + +volumes: + cache: + + # server_git_data: + #external: true diff --git a/images/backup/duplicity/Dockerfile b/images/backup/duplicity/Dockerfile new file mode 100644 index 0000000..9ce6d6b --- /dev/null +++ b/images/backup/duplicity/Dockerfile @@ -0,0 +1,16 @@ +FROM alpine:3.5 + +RUN apk add --update duplicity ca-certificates gnupg openssh-client py-paramiko py2-pip + +RUN pip install --upgrade setuptools +RUN pip install --upgrade distribute + +ADD backup.sh /backup.sh +ADD restore.sh /restore.sh + +RUN mkdir /root/.gnupg/ +ADD gpg-agent.conf /root/.gnupg/gpg-agent.conf +RUN chmod 600 /root/.gnupg + +RUN mkdir /root/.ssh +RUN chmod 600 /root/.ssh diff --git a/images/backup/duplicity/backup.sh b/images/backup/duplicity/backup.sh new file mode 100755 index 0000000..38223e3 --- /dev/null +++ b/images/backup/duplicity/backup.sh @@ -0,0 +1,39 @@ +#!/bin/sh -xe +set -x +set -e + +ls -lah /data + +CONNECTION_OPTIONS="--use-agent \ + --verbosity $LOG_LEVEL \ + --num-retries 3 \ + --encrypt-key $FINGERPRINT \ + --log-file /dev/stdout \ + --archive-dir /cache/archive \ + --ssh-options=-oIdentityFile=/ssh_keys/backup \ + --gpg-options --passphrase=$PASSPHRASE \ + --gpg-options --no-tty \ + --gpg-options --batch \ + --gpg-options --pinentry-mode=loopback" + +# Import and trust the GPG Keys +gpg --passphrase $PASSPHRASE --no-tty --batch --import /gpg_keys/*.priv.asc +echo "$FINGERPRINT:6:" | gpg --import-ownertrust + +mkdir -p /cache/archive + +# Make the actual backup +duplicity --asynchronous-upload \ + --volsize 250 \ + --full-if-older-than 1M \ + $CONNECTION_OPTIONS \ + $EXCLUDE_DIRS \ + /data/ "$BACKUP_STORAGE" + +# Clean up broken backups +duplicity cleanup $CONNECTION_OPTIONS \ + "$BACKUP_STORAGE" + +# Clean up old backups +duplicity remove-all-but-n-full 1 $CONNECTION_OPTIONS \ + "$BACKUP_STORAGE" diff --git a/images/backup/duplicity/gpg-agent.conf b/images/backup/duplicity/gpg-agent.conf new file mode 100644 index 0000000..d1b6ae3 --- /dev/null +++ b/images/backup/duplicity/gpg-agent.conf @@ -0,0 +1 @@ +allow-loopback-pinentry diff --git a/images/backup/duplicity/restore.sh b/images/backup/duplicity/restore.sh new file mode 100755 index 0000000..df3f9c9 --- /dev/null +++ b/images/backup/duplicity/restore.sh @@ -0,0 +1,26 @@ +#!/bin/sh -xe +set -x +set -e + +CONNECTION_OPTIONS="--use-agent \ + --verbosity $LOG_LEVEL \ + --num-retries 3 \ + --encrypt-key $FINGERPRINT \ + --log-file /dev/stdout \ + --archive-dir /cache/archive \ + --ssh-options=-oIdentityFile=/ssh_keys/backup \ + --gpg-options --passphrase=$PASSPHRASE \ + --gpg-options --no-tty \ + --gpg-options --batch \ + --gpg-options --pinentry-mode=loopback" + +# Import and trust the GPG Keys +gpg --passphrase $PASSPHRASE --no-tty --batch --import /gpg_keys/*.priv.asc +echo "$FINGERPRINT:6:" | gpg --import-ownertrust + +mkdir -p /cache/archive + +# Restore the Backup +duplicity restore \ + $CONNECTION_OPTIONS \ + "$BACKUP_STORAGE" /data/ diff --git a/images/backup/gpg_keys/backups.pub.key b/images/backup/gpg_keys/backups.pub.key new file mode 100644 index 0000000..b7377e3 --- /dev/null +++ b/images/backup/gpg_keys/backups.pub.key @@ -0,0 +1,108 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +=ZkCc +-----END PGP PUBLIC KEY BLOCK----- diff --git a/images/backup/restore.yml b/images/backup/restore.yml new file mode 100644 index 0000000..6640b26 --- /dev/null +++ b/images/backup/restore.yml @@ -0,0 +1,16 @@ +version: '3' + +services: + backup: + env_file: + - config.env + build: ./duplicity + command: /bin/sh /restore.sh + hostname: duplicity + restart: "no" + volumes: + - ./restored:/data + - ./gpg_keys:/gpg_keys + +volumes: + cache: -- cgit v1.2.3