Backup strategy on untrusted FTP

Update: Before implementing this method you might want to look at duply which uses gpg to implement the same functionality but does not need large image files.

We own a server which is used to keep e-mail and other personal data off the commercial cloud. Partly because of paranoia, partly because of the enjoyment of maintaining our own server.

We run the server ourselves so we have to backup all the software and configuration to get timely recovery after catastrophic failure. If you do the same, depending on the availability you want you will probably be running on a hardware RAID. But: Even that can fail, be it through software failure fucking your file system good or through some bigger hardware blackout. If the server going down is home to your e-mail, your dropbox-equivalent and your website you want to be able to get it back up as quickly as possible.

For this goal, our hosting provider allows for 100 GB of backups on one of their machines. It is vastly less than the 3 Tb of RAID5-storage on our server, but roughly covers the amount of data really vital to operating the server. We’re talking mysql databases, mail boxes, config files, websites hosted etc.

The big caveat is that the 100Gb of free backup space can only be accessed through FTP. This means:

  1. It’s not encrypted; everything can be read by our hoster. Not really desireable when you just spent hours on end making your system as safe as possible with encrypted disks.
  2. It does not preserve file permissions.
  3. It is too slow for large amounts of files. (We tried to run rsync on a curlftpfs-mounted ecryptfs-overlaid FTP share and it was just not usable).

Creation of the encrypted disk image

We decided to do things differently: As a backup target we use a disk image file (~95Gb), one copy of which lives on the local disk of our server. This disk image is encrypted using cryptsetup. I used the following commands to create the disk image (they are needed only once)

 # create 95Gig file of zeros (takes time depending on disk speed)
dd if=/dev/zero of=/backupimage.dat BS=1M COUNT=95000
# format it as an encrypted disk image
cryptsetup luksFormat /backupimage.dat
# open a loopback device for said image
cryptsetup luksOpen /backupimage.dat backupvolume
# format the volume
mkfs.ext4 /dev/mapper/backupvolume
# make a directory for the "disk"
mkdir /backupvolume
mount /dev/mapper/backupvolume /backupvolume

Now the encrypted image is mounted in /backupvolume. You can toy around with it, write some data to it etc. When you’re done, you should unmount the volume and close the loopback device:

umount /backupvolume
cryptsetup luksClose backupvolume

The backup process

For the backup process I needed to automatize the process. Only automatic backups will be run regularly and only regularly running backups are good backups. To automatize, I need a foolproof way of starting and stopping the loopback device and uploading the data to the FTP server. For the time being I’m using three bash scripts:

1. Mount the encrypted backup volume

This script opens the loopback device and mounts the encrypted disk image to /backup. Note that the password for the crypted image is clear text. This is necessary because you want the backup to run automatically. Additionally, the script can only be read by root and once someone has root access to your server he can access all the data anyway.

#!/bin/bash
if ! [ -a /dev/mapper/backupvolume ] # make sure loopback is closed
  then
  echo "password" | cryptsetup luksOpen /backupimage.dat backupvolume
  if mount /dev/mapper/backupvolume /backupvolume/
    then
      echo mount successful
      exit 0
    else # if we cannot mount we should close the loopback
      cryptsetup luksClose backupvolume
      echo "had problems mounting closed cryptloop device"
      exit 200
    fi
  else # if we cannot even open the loopback there was an error
  echo "error"
  exit 200
fi

1.1. Back up

Now is the time you want to backup stuff to /backupvolume. Make sure you leave the directory after, otherwise the following will throw an error.

2. Unmount the encrypted backup volume

After backup you need to properly close the backup volume:

#!/bin/bash
if [ -a /dev/mapper/backupvolume ] #make sure there is a cryptoloop
  then
  if ! umount /backupvolume #try to unmount it
    then # throw error if fail
      echo "unable to unmount"
      exit 200
    else # otherwise go on and unhook the loopback
      if ! cryptsetup luksClose backupvolume
        then
          echo "luksClose failed"
          exit 200
        else
          exit 0
      fi
  fi
fi

3. Upload the backup disk image to the FTP server

Once you have backed up to your image, the image file on your needs to be sent to the backup location. In our case that is an FTP server and if you want to use this method of backing up you are likely in a similar situation. For uploading data we use the following script:

#!/bin/bash
HOST=<ftp-host>
USER=<username>
PASS=<password>
MACHINE=<name of the machine>
ftp -inv $HOST <<END >ftp.log
user $USER $PASS
put /backupimage.dat backupimage-$MACHINE.dat
bye
END
if fgrep "226 Transfer complete" ftp.log
   then exit 0
else
   echo "FTP upload failed"
   exit 200
fi

In the next chapter: How to use these three scripts to backup your data automatically and be informed via e-mail whenever something goes wrong.