Ransomware resistant backups

This post documents my backup system. It’s a fairly convoluted one that is a little more than most people want or need.

You’ll require a unix-y system, familiarity with the command line, and patience if you wish to replicate it.

Requirements

I am a bit of a stickler for my backups. I’m a private person, but also more than a little paranoid about ransomware attacks. I wanted backups that were:

  1. Encrypted: No one but me should be able to read my backups.
  2. Incremental: Minimise the network and disk overhead where possible.
  3. Cheap: I am from Yorkshire.
  4. Ransomware-resistant: Good backups are the best line of defence against ransomware.
  5. Set-and-forget: I should be able to configure these backups once and not worry about them until I get a new laptop. Previous backup systems (which I had to take manual action) ended up less than great.

High-level

This system relies on Duplicity, AWS S3, and AWS Lambda.

Architecture overview

Cron periodically launches Duplicity to create encrypted incremental backups and store them in S3. Lambda removes the backup system’s ability to overwrite it’s own files after they have been uploaded. Finally, S3’s object expiration keeps costs from growing without limit.

The build

Note: Keep your AWS root account very safe. Use two-factor authentication if possible.

AWS S3

Outline:

  1. choose an AWS region. eu-west-1 (Frankfurt) is probably a good choice for most Europeans.
  2. Setup an IAM user
  3. Create S3 bucket, including expiration policy
  4. Give IAM user access to that bucket
  5. Save IAM access keys

Duplicity

Duplicity is a free command line tool that sends encrypted incremental backups to any number of storage locations. It can send backups to AWS S3, FTP, SSH, Dropbox, and many more.

This is managed with a short(ish) shell script which sets up the environment, including the AWS IAM access keys.

#!/bin/sh
# Fail fast on errors
set -euf
# Set the AWS access keys.
export AWS_ACCESS_KEY_ID="$(cat ${HOME}/.aws/backup/access_id)"
export AWS_SECRET_ACCESS_KEY="$(cat ${HOME}/.aws/backup/access_key)"
# Set up the encryption passphrase.
export PASSPHRASE="$(cat ${HOME}/.aws/backup/passphrase)"
# Hide credentials when the script exits.
function on_exit {
unset AWS_ACCESS_KEY_ID
unset AWS_SECRET_ACCESS_KEY
unset PASSPHRASE
}
trap on_exit EXIT
# Set the S3 bucket name from above
DEST="BUCKET_NAME"
# Create a complete backup every 3 months. Tune this to your needs.
MAX_REBACKUP_AGE="3M"
# Run duplicity, excluding a number of useless directories
duplicity --full-if-older-than "${MAX_REBACKUP_AGE}" \
--exclude "${HOME}/Downloads" \
--exclude "${HOME}/.vagrant.d" \
--exclude "${HOME}/.m2" \
--exclude "${HOME}/.android" \
--exclude "${HOME}/.cache" \
--exclude "${HOME}/.npm" \
--exclude "${HOME}/.cinnamon" \
--exclude "${HOME}/.config" \
--exclude "${HOME}/.gnome" \
--exclude "${HOME}/.gnome2" \
--exclude "${HOME}/.gnupg" \
--exclude "${HOME}/.mozilla" \
--exclude "${HOME}/.local" \
--s3-use-new-style \
--s3-use-ia \
"${HOME}" \
"${DEST}"

Save this in ~/.bin/s3-backup

Encryption keys

You’ll need to create an encryption key for your backups. It’s be best if this is random and long. You can use:

openssl rand -base64 32 > ~/.aws/backup/passphrase

You should keep a copy of ~/.aws/backup/passphrase somewhere very safe. A password manager, like LastPass or 1Password, is a good idea.

Cron

The script should be run every day or more frequently. We’re on *nix, so we can do this with cron. Edit your crontab with your favourite editor:

$> crontab -e

To run the script every day at 22:35, add:

35 22 * * * ${HOME}/.bin/s3-backup

AWS Lambda

Results

Alternatives

Laptop backups go to a directory on a private server (e.g. home server, VPS) which has a process monitoring a directory for completed uploads and max age.