Featured image of post End to End Encrypted Backups with Rsync

End to End Encrypted Backups with Rsync

5 minute read

In the age of the ransomware attacks having a current backup is paramount. But can you have an easy backup procedure and be sure that, even in the event of the full backup server compromise your data is not lost? While there are many backup tools and vendors out there that promise it (e.g. velero/restic/duplicity, see the referecence section belo), they are complicated and/or proprietary, requiring you to trust whoever is providing it.

I prefer simple, time-tested backup solutions. Here is the fully end-to-end encrypted backups using nothing but bash, rsync and ssh. It supports:

  • untrusted remote storage
  • multiple clients backing up to one server
  • backup partitions that grow and shrink as needed
  • fully auditable storage

The way it works is like this:

  1. The remote server has a plain partition that stores one or more sparse files. The files contain LUKS encrypted partitions inside with the backups
  2. Before each backup the client mounts the plain partition over SSH, then opens the encrypted file and mounts the partition from inside of it
  3. It backs up the data and performs unmounting in the reverse order
  4. The remote server never sees the unencrypted data since SSH is providing encryption in transit and LUKS is providing encryption at rest.

Implementing E2EE backups

There are three steps in this process:

  • Create an SSH connection to the server
  • Create an encrypted file for the backups on the server
  • Mount the encrypted file over SSH into the local file system

If you’d like to see it fully implemented and automated check my SARDELKA project.

  • Create SSH keys on the local system: ssh-keygen -t ed25519 -f ~/.ssh/backupuser_ed25519

  • Create a backup user on the remote system

sudo useradd -m -s /bin/bash backupuser
sudo mkdir ~backupuser/.ssh/
sudo touch ~backupuser/.ssh/authorized_keys
sudo chown -R backupuser:backupuser ~backupuser/.ssh/
sudo chmod 700 ~backupuser/.ssh/
sudo chmod 600 ~backupuser/.ssh/authorized_keys

Copy the contents of the ~/.ssh/backupuser_ed25519.pub from the local system to ~backupuser/.ssh/authorized_keys of the remote system Test that you can login as backupuser to the remote system. To make it easier in the future add the following to your .ssh/config:

Host remotebackupserver
    HostName backupserver
    User backupusedr
    PubkeyAuthentication yes
    IdentityFile ~/.ssh/backupio_ed25519
  • Configure FUSE to allow root to operate on user mounts. This is needed because cryptsetup has to run as root to create /dev/mapper devices. To do this uncomment user_allow_other in /etc/fuse.conf

  • Mount the remote file system over SSHFS sudo sshfs -o allow_other remotebackupserver:/path/to/backup/ /mnt/backup-host

  • Create a sparse file that will hold your backups, indicating the max size you’ll want it to grow to (which could also be increased later). truncate -s 5T /mnt/backup-host/backups-system-a.luks

  • Setup LUKS encryption inside of this file with a new key-file

sudo dd if=/dev/random of=/root/.keyfiles/backupstore.key bs=4096 count=1
sudo chmod 600 /root/.keyfiles/backupstore
sudo cryptsetup luksFormat /mnt/backup-host/backups-system-a.luks --key-file /root/luks/backupstore.key
  • Create normal partition inside of the encrypted file. Initialize the whole FS right now to avoid performance issues later with the lazy init.
sudo cryptsetup luksOpen /mnt/backup-host/backup-file.luks --key-file /root/luks/backupstore.key backup-partition
sudo mkfs.ext4 -m0 -E lazy_itable_init=0,lazy_journal_init=0 /dev/mapper/backup-partition
sudo mount /dev/mapper/backup-partition /mnt/remote-backup

Backup

Now you can start backing up your system to /mnt/remote-backup as if it was a local trusted file system. For example if you want to run something like a synthetic full backup run

sudo rsync -vaH --numeric-ids --delete --delete-excluded --stats --log-file system.log --log-file-format="%i %10l %n %L" --progress --prune-empty-dirs --link-dest=/mnt/remote-backup/home --exclude-from system.exclude /home/ /mnt/remote-backup/home.<date>

Teardown

After the backup is done use these steps:

sudo umount /mnt/remote-backup
sudo cryptsetup luksClose /dev/mapper/backup-partition
sudo fusermount -u /mnt/backup-host

Maintenance

Moving backups

If you ever need to move your backup sparse file use cp --sparse=always or sudo dd if=backups-system-a.luks of=otherfolder/backups-system-a.luks bs=64K conv=sparse. 64k is chosen to limit ‘bloat’ during dd copying (file size increase from the original).

Increasing backup file size

Use truncate and resize2fs

sudo sshfs -o allow_other remotebackupserver:/path/to/backup/ /mnt/backup-host
truncate -s 8T /mnt/backup-host/backup-file.luks
sudo cryptsetup luksOpen /mnt/backup-host/backup-file.luks --key-file /root/luks/backupstore.key backup-partition
sudo resize2fs /dev/mapper/backup-partition

Decreasing backup file size

The size of the sparse filed does not automatically decrease when you are deleting files from the backups. To do this you need a way to run the filesystem TRIM/Discard commands. However, SSHFS/SFTP does support TRIM, so you’ll need to temporarily mount it over NBD. Note that although NBD supports TLS setting it up is a chore for this purpose, so here is the plain-text example that does not provide is not encrypted in transit. Make sure to use it ONLY for the trim command and nothing else.

  • Get the NBD server and client apt-get install nbd-server nbd-client
  • Enable trim in the config file:
[default]
exportname = /mnt/backup-host/backup-file.luks
trim = true
  • Start the NBD server sudo nbd-server -d -C /path/to/config
  • Connect to it from the client nbd-client -N default -n backup-server 10809 /dev/nbd0
  • Open the LUKS file with the --allow-discards option: sudo cryptsetup luksOpen --allow-discards /dev/nbd0 backup-mapper
  • Mount it sudo mount /dev/mapper/backup-mapper /mnt/remote-backup
  • Trim the deleted space: fstrim -v /mnt/remote-backup
  • Tear everything down

Notes

There is still a chance for the local computer to corrupt/delete the remote backups if the local computer is compromized. Here are some ideas on how to fix that:

  • Use encrypted rsync server/client connection over with full synthetic backups (see SARDELKA ) as you will not be able to delete/corrupt already existing backups that way.
  • Use append-only/write-only backups with restic and rclone.
  • Use an append-only FS in the backup file

References

Licensed under CC BY-NC-SA 4.0
Based off the Stack theme.