Secure remote backup with OpenVPN, rsync and encfs
One of the reasons I purchased a dedicated box on OVH low-cost solution kimsufi is to have my own backup server, a la Mozy. There are a bunch of existing solutions, but having my own allows to keep full control (and ownership) on the data (paranoid mode ON).
My requirements
- the data on the remote host must be encrypted and as secure as possible
- even if the server is on a 100Mbps connection, the data inside my home is still on DSL with a slow upload, so the backup protocol must be optimized to cope with this low bandwidth and high latency (i.e. block-level backup)
- communication between the backup clients and server must be secure
- I should be able occasionally to read the encrypted backup data on the backup server or on another host
My solution
1. the data on the remote host must be encrypted and as secure as possible
I am a long-time user of truecrypt, but I failed to find a workable use case in this situation, so turned toward the internet for some other options. After some research, I decided to use EncFS. The main reason is that encFS allows a --reverse option, which provides an encrypted view of clear data, as opposed to many other solutions I found. You’ll see in the next sections why this is useful.
2. the backup protocol must be optimized to cope with low bandwidth and high latency
No real breakthrough here : this lead straight to the infamous and beloved rsync.
3. communication between the backup clients and server should be secure
While it is possible to use rsync over ssh, I decided instead to leverage the OpenVPN VPN I had setup between the machines already. This made the rsync commands and firewall configurations easier.
4. I should be able occasionally to read the encrypted backup data on the backup server or on another host
This is something that encFS allows provided that the you share the corresponding .encfs6.xml file.
How it works
On the client machine, I will use encFS in reverse mode to create a virtual encrypted view of the data I want to backup and I will backup this data onto the server via rsync. This way, the server will never even see the data in clear, neither the password for the encrypted volume : all it will know is the encrypted data. To some extent, I do not even need to have encFS installed on the server.
encFS requires two things to encrypt/decrypt a volume :
- the volume configuration file :
.encfs6.xml - the volume password
In my case, I will use a single password and config file for all different folders, so this will make it easier to manage.
As I want to occasionally be able to see the decrypted data on the server, I will however need to install encFS there, and have a copy of .encfs6.xml available as well (which is somewhat of a security risk). IMPORTANT NOTE : you MUST in any case backup your .encfs6.xml somewhere as well (preferably in encrypted format). Remembering your password is not enough to decrypt your data, you need this file!
Putting it all together
I will take the scenario where I want to backup the following folders on my machine called workstation onto a backup server called server :
~/Pictures
~/Documents
/data/Music
Both machines are linked together through OpenVPN. On workstation let’s start by installing encFS :
jc@workstation:~$ sudo apt-get install encfs
Then we select the location of the .encfs6.xml by setting the ENCFS6_CONFIG environment variable :
jc@workstation:~$ ENCFS6_CONFIG=/home/jc/.encfs6.xml
We then create the virtual encrypted view and the corresponding config file (here I chose AES 256 bits, 1024 byte file system and encryption of file names) :
jc@workstation:~$ mkdir /tmp/encrypted-view jc@workstation $ encfs --reverse ~/Pictures /tmp/encrypted-view Création d'un nouveau volume chiffré. Veuillez choisir au moins une des options suivantes : entrez "x" pour le mode de configuration expert, entrez "p" pour un mode pré-configuré paranoïaque, n'importe quoi d'autre ou une ligne vide sélectionnera le mode standard. ?> x Mode de configuration manuel sélectionné. Les algorithmes de chiffrement suivants sont disponibles : 1. AES : 16 byte block cipher -- Supporte des longueurs de clé de 128 à 256 bits -- Supporte des tailles de bloc de 64 à 4096 octets 2. Blowfish : chiffrement par blocs de 8 octets -- Supporte des longueurs de clé de 128 à 256 bits -- Supporte des tailles de bloc de 64 à 4096 octets Entrez le nombre correspondant à votre choix : 1 Algorithme sélectionné "AES" Veuillez sélectionner une taille de clé en bits. Le chiffrement que vous avez choisi supporte des tailles de 128 à 256 bits par incréments de 64 bits. Par exemple : 128, 192, 256 Taille de clé sélectionnée : 256 Utilisation d'une taille de clé de 256 bits Sélectionnez une taille de bloc en octets. L'algorithme choisi supporte des tailles de 64 à 4096 octets par incréments de 16. Ou pressez juste Entrée pour la valeur par défaut (1024 octets) taille de bloc du système de fichier : 1024 Utilisation d'une taille de bloc du système de fichier de 1024 octets Les algorithmes d'encodage de noms de fichiers suivants sont disponibles : 1. Block : Encodage par bloc, dissimule quelque peu la longueur des noms de fichier 2. Null : No encryption of filenames 3. Stream : Encodage de flux, conserve les noms de fichiers aussi courts que possible Entrez le nombre correspondant à votre choix : 1 Algorithme sélectionné "Block"" --reverse specified, not using unique/chained IV Configuration terminée. Le système de fichiers sur le point d'être créé a les caractéristiques suivantes : Chiffrement de système de fichiers "ssl/aes", version 3:0:2 Encodage du nom de fichier : "nameio/block", version 3:0:1 Taille de clé : 256 bits Taille de bloc : 1024 octets File holes passed through to ciphertext. Vous allez maintenant devoir entrer un mot de passe pour votre système de fichiers. Vous allez devoir absolument vous souvenir de ce mot de passe, car il n'y a aucun mécanisme de secours. Cependant, le mot de passe pourra être changé plus tard en utilisant encfsctl. Nouveau mot de passe EncFS : mypassword Vérifiez le mot de passe EncFS : mypassword
Note that the --reverse option has disabled some options (unique/chained IV) automatically.
At this point, we can see that a virtual crypted view of the ~/Pictures folder :
jc@workstation:~$ ls -l ~/Pictures/ -rw-r----- 1 jc jc 125551 sept. 2 00:01 jc2.jpg -rw-r----- 1 jc jc 99674 sept. 2 00:01 jc3.jpg -rw-r----- 1 jc jc 79136 sept. 2 00:01 jc.jpg jc@workstation:~$ ls -l /tmp/encrypted-view/ -rw-r----- 1 jc jc 125551 sept. 2 00:01 5JKReeIql4n9EjyzSfVDA0Ht -rw-r----- 1 jc jc 99674 sept. 2 00:01 EumzT639e2iL0ITbSGtG8BL5 -rw-r----- 1 jc jc 79136 sept. 2 00:01 gOF85RoOfybQ2yqAF2ruCPOc
Pretty nice, isn’t it? There’s not extra disk space taken by this virtual crypted folder, it’s just a on-the-fly, dynamic view of the original folder.
Now on the backup server, we need to enable rsync as a daemon in /etc/default/rsync:
RSYNC_ENABLE=true
Then we configure the /etc/rsyncd.conf file:
log file = /var/log/rsync.log timeout = 300 read only = yes [backup] comment = Backup folder path = /home/backup read only = no list = yes uid = root gid = root host allow = 10.8.0.* host deny = *
Note that uid/gid is set as root to preserve all permissions. I only allow connections from the OpenVPN IPs (10.8.0.*). For extra security, I have denied the access to the rsync port in iptables on the eth0 interface, but allowed it on the OpenVPN (tun0) one:
jc@server:~$ sudo iptables -vL Chain INPUT (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destination 98528 36M ACCEPT all -- lo any anywhere anywhere 62M 77G ACCEPT all -- tun0 any anywhere anywhere [...] 6438K 3946M ACCEPT all -- eth0 any anywhere anywhere state RELATED,ESTABLISHED [...] 35073 9155K DROP all -- eth0 any anywhere anywhere
Now we can run our backup command with the following rsync command from the workstation:
jc@workstation:~$ rsync --archive --progress --numeric-ids --bwlimit=90 -T /.TEMP /tmp/encrypted-view/ rsync://server/backup/Pictures
sending incremental file list
created directory /Pictures
./
5JKReeIql4n9EjyzSfVDA0Ht
125551 100% 59.38kB/s 0:00:01 (xfer#1, to-check=2/4)
EumzT639e2iL0ITbSGtG8BL5
99674 100% 41.97kB/s 0:00:02 (xfer#2, to-check=1/4)
gOF85RoOfybQ2yqAF2ruCPOc
79136 100% 34.43kB/s 0:00:01 (xfer#3, to-check=0/4)
sent 304644 bytes received 68 bytes 35848.47 bytes/sec
total size is 304361 speedup is 1.00
I have used the following options on the rsync command:
--numeric-ids: in order to preserve file ownership as much as possible (see rsync man page)--bwlimit=90: to limit the bandwidth used by rsync and not kill my internet connection when the backup runs-T /.TEMP: based on this encFS page. The.TEMPfolder must be created on the server and is relative to the rsync mountpoint, so in our case/home/backup/.TEMP
Now, if we have a look on the server, the encrypted files are there:
jc@server:~$ ls -l /home/backup/Pictures -rw-r----- 1 jc jc 125551 sept. 2 00:01 5JKReeIql4n9EjyzSfVDA0Ht -rw-r----- 1 jc jc 99674 sept. 2 00:01 EumzT639e2iL0ITbSGtG8BL5 -rw-r----- 1 jc jc 79136 sept. 2 00:01 gOF85RoOfybQ2yqAF2ruCPOc
If I want to be able to decrypt them on the server, I need the .encfs6.xml there as well. Assuming you have put it in /tmp on the server, you can now read the data there :
jc@server:~$ mkdir /tmp/decrypted jc@server:~$ ENCFS6_CONFIG=/tmp/.encfs6.xml jc@server:~$ encfs /home/backup/Pictures /tmp/decrypted/ jc@server:~$ ls -l /tmp/decrypted/ -rw-r----- 1 jc jc 125551 sept. 2 00:01 jc2.jpg -rw-r----- 1 jc jc 99674 sept. 2 00:01 jc3.jpg -rw-r----- 1 jc jc 79136 sept. 2 00:01 jc.jpg
Automating the backup
In order to automate the backup, I am running it as a cron task on the client machine. Here is my script:
#!/bin/bash
ROOT_UID=0 # Only users with $UID 0 have root privileges.
ENCFS6_CONFIG=/home/jc/.encfs6.xml
export ENCFS6_CONFIG
PASSWORD_FILE=/home/jc/.password
VIRTUAL_ENCRYPTED_FOLDER=/tmp/encrypted
function delete_empty_folder {
if [ "$(ls -A $1)" ]; then
echo "Encrypted folder ($1) is not empty, something is wrong"
exit 1
else
echo "Encrypted folder ($1) is empty, removing it..."
rm -fr $VIRTUAL_ENCRYPTED_FOLDER
echo "Encrypted folder ($1) is now deleted"
exit 0
fi
}
function mount_encrypted_folder {
cat $PASSWORD_FILE | encfs -S --reverse $1 $VIRTUAL_ENCRYPTED_FOLDER
if [[ $? -ne 0 ]]; then
echo "Error while mounting the encrypted folder (($VIRTUAL_ENCRYPTED_FOLDER) on $1, exiting."
exit 1
fi
}
function unmount_encrypted_folder {
mount | grep $VIRTUAL_ENCRYPTED_FOLDER
if [[ $? -eq 0 ]]; then
echo "The encrypted folder ($VIRTUAL_ENCRYPTED_FOLDER) is currently mounted, unmounting..."
umount $VIRTUAL_ENCRYPTED_FOLDER
if [[ $? -ne 0 ]]; then
echo "Error while unmounting the encrypted folder ($VIRTUAL_ENCRYPTED_FOLDER), aborting."
exit 1
fi
echo "Encrypted folder ($VIRTUAL_ENCRYPTED_FOLDER) is now unmounted."
else
echo "Encrypted folder ($VIRTUAL_ENCRYPTED_FOLDER) was not mounted, no action taken"
fi
}
function rsync_folder {
#The rsync .TEMP folder must exist on the destination!
rsync --archive --progress --numeric-ids --bwlimit=90 -T /.TEMP $VIRTUAL_ENCRYPTED_FOLDER/ rsync://server/backup$1
}
# Run as root, of course.
if [ "$UID" -ne "$ROOT_UID" ]
then
echo "Must be root to run this script."
exit 1
fi
#purge directory if it exists
if [ -d "$VIRTUAL_ENCRYPTED_FOLDER" ]; then
unmount_encrypted_folder
else
mkdir $VIRTUAL_ENCRYPTED_FOLDER
fi
mount_encrypted_folder ~/Pictures
rsync_folder /workstation/home/jc/Pictures
unmount_encrypted_folder
mount_encrypted_folder ~/Documents
rsync_folder /workstation/home/jc/Documents
unmount_encrypted_folder
mount_encrypted_folder /data/Music
rsync_folder /data/Music
unmount_encrypted_folder
delete_empty_folder $VIRTUAL_ENCRYPTED_FOLDER
I was considering using a backup scheme like yours, but I see two problems:
1. You cannot see what files are being updated,
because you just can read the encrypted filenames.
2. The transmission is slow,
because rsync is working over the encrypted files, and then
any small delta in the plain file generates a completely different encrypted file.
There is another tool, rsyncrypto, that aims to solve the second problem.
It uses a weaker encryptor that generates similar encrypted files from similar plain files,
but I cannot say if it is sufficiently strong.
Regards,
Ramón
Hi Ramón
One way to circumvent #1 is to use encfs without encrypting the file names. If that does not work for you, maybe there is a way to parse log files and send the file names to
encfsctl, not sure if that would be feasible or not.Regarding #2, you should not worry about this : encfs does not change the whole encrypted file if the original file changes just a little. You made me wonder, actually, so I tested it. See this post : http://jc.coynel.net/?p=135
JC
Yes. For #1 I think the way to go is to use encfsctl, because I prefer encrypting the file names. Nevertheless, I did not do the exercise, because #2 was a deal breaker for me, and I’m still not completely convinced on #2. What will happen if you just simply add one character at the very beggining of the 5 MB file?
Best regards,
Ramón
I actually did not realize that
rsyncwas so powerful and could deal with this case. It actually does! I have summarized my experiment in the article previously mentioned. As you suspected, when encryption is used this use case does not work.[…] I have written a bunch of scripts to automate some tasks, like for example to automate dynamic IP registration in a DNS or more commonly for backup purposes. […]
Thanks a lot, this got me started on using encfs to create an encrypted backup of my Nextcloud on a remote server. I wrote an article on how to do it, in case someone’s interested: https://gitlab.com/wolframroesler/back-up-nextcloud/blob/master/local-to-remote.md