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.TEMP
folder 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
rsync
was 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