Motivation
Beside the reliability and integrity of a backup I’d want to ensure that all backed up data are not acessible by third parties.
Requirements
- Backblaze B2 Cloud Storage account
- Ubuntu 16.04 LTS
Setup
Preparation
I presume that there’s a running minimal Ubuntu Xenial installation with a directory /mnt/backup
which should be backed up to Backblaze.
Installation
Install necessary software:
apt-get install software-properties-common gnupg2 gnupg-agent python-pip
add-apt-repository ppa:duplicity-team/ppa
apt-get install duplicity
pip install b2
Then create a new GPG key which is dedicated to backup encryption. For details about key generation check the Ubuntu GPG HowTo out.
Create a backup script which is triggered regularly by cron or something else:
!/usr/bin/env bash
APPLICATION_KEY="000000000000000000000000000000000000000000"
APPLICATION_SECRET="000000000000000000000000000000000000000000"
KEYID="FFFFFFFFFFFFFFFF"
FULL_AGE="90D"
FULL_BACKUPS="2"
LOCKFILE="/tmp/duplicity.lock"
GPGBINARY="/usr/bin/gpg"
OPTIONS="-v0 --no-print-statistics" # because there is no --quiet
## ^ config ends here ^ ##
OPTIONS="${OPTIONS} --gpg-binary=${GPGBINARY} --tempdir=/mnt/backup/tmp --archive-dir=/mnt/backup/.duplicity --use-agent"
if [ "$1" = "unlock" ]; then
gpg-connect-agent /bye
${GPGBINARY} -sanu ${KEYID} "$0"
exit $?
fi
if [ -f ${LOCKFILE:?} ]; then
echo >&2 "Other instance active. Abort"
exit 1
fi
function finish {
rm -rf ${LOCKFILE:?}
}
trap finish EXIT
echo $$ > ${LOCKFILE:?}
## Transfer to b2
## There's a bug in duplicity which prevents us from adding more then one backup into one bucket.
## Therefore each backup has it's own bucket. See https://bugs.launchpad.net/duplicity/+bug/1523498
cd /mnt/backup/
for BACKUPPATH in dom0 www static vms/*; do
BUCKET=your-prefix-${BACKUPPATH#vms/}
BUCKET=$(tr . - <<< ${BUCKET})
duplicity ${OPTIONS} --name=${BUCKET:?} --encrypt-key=${KEYID} --asynchronous-upload --full-if-older-than=${FULL_AGE:?} --num-retries=10 ${BACKUPPATH:?} b2://${APPLICATION_KEY}:${APPLICATION_KEY_SECRET}@${BUCKET} &&
duplicity ${OPTIONS} --name=${BUCKET:?} --force remove-all-but-n-full ${FULL_BACKUPS:?} b2://${APPLICATION_KEY}:${APPLICATION_KEY_SECRET}@${BUCKET}
done
Duplicity uses your GPG key to en- and decrypt all data, even the meta data. But keep in mind, that it keeps a copy of the decrypted meta data on the local disk (defaults to ~/.cache/duplicity
) and utilizes a temporary
directory. If just a part of your disk is encrypted (instead of FDE) adapt the script to point those locations (option --tempdir
and --archive-dir
) to the encrypted part.
Duplicity needs access to your private key to decrypt those metadata, therefore the script utilizes gpg-agent
to unlock the key and keep it in an agent session. So after rebooting the machine or killing the agent ensure that you call
the script with unlock
as parameter and provide the GPG key passphrase to load it into the agent session. Afterwards duplicity uses the agent to access the key for all operations which depend on it.
The gpg-agent
won’t talk with duplicity until loopback pin entry is allowed. Also the agent caches each entry just for 600s per default.
The documentation tells that the timeout is reset each time the entry is accessed, my personal experience doesn’t confirm this. Therefore I’ve set the tiemout to 315360000 seconds (10 years).
Execute the following command to add the necessary setting to your local gpg-agent.conf: echo -e 'allow-loopback-pinentry\ndefault-cache-ttl 315360000 #10y\nmax-cache-ttl 315360000' >> ~/.gnupg/gpg-agent.conf
I’d like to separate the different logical parts of my backup into different sets. Therefore the script above creates a new backup (and bucket) for each folder in /mnt/backup
.
All my VMs create automatic backups in subfolders of /mnt/backup/vms
and the script applies some “Voodoo” to map the VM names to valid B2 bucket names. You can ignore this part if you don’t need it.
If the target bucket is not created yet, it will be created automatically. New B2 buckets are per default private, that’s fine but they are also configured to keep all versions of all files in the bucket. I’m pretty sure you don’t want to keep all backup files even if they are already deleted by duplicity. So after adding new folders (aka creating new buckets) login to yout Backblaze account and update the settings of the new bucket.
Configuration
ACCOUNTID
andAPPLICATION_KEY
must be changed to your personal B2 account ID and application key:KEYID
must be changed to the ID of your generated GPG keyFULL_AGE
defines after which time a new full backup will be triggered. For valid values check the duplicity man page (sectionTIME FORMATS
)FULL_BACKUPS
defines how many full backups should be keept before deleting old ones
Per default duplicity is quite noisy and logs some stuff like statistics, etc. It has no --quiet
option but can be silenced with -v0 --no-print-statistics
(see OPTIONS
variable)
Restore a backup
To restore a previously created backup just adapt the following command to your needs and keep your GPG key passphrase on hand:
duplicity --tempdir=/mnt/backup/tmp --archive-dir=/mnt/backup/.duplicity restore b2://${APPLICATION_KEY}:${APPLICATION_KEY_SECRET}@${BUCKET} /mnt/backup/restore
If you just need some files from the backup add the option --file-to-restore <RELATIVE_PATH>
to the restore command above.