Saturday, June 2, 2012

Mount WebDAV resources with davfs2 and secure it with encfs

Number of online resources offering free remote storage facilities has seriously increased in the recent time and it continue to grow taking a good share from the Dropbox's marketspace. Dropbox has been on the cloud storage market since the early days but for many people a proprietary client offered by Dropbox is not acceptable. There are of course attempts to overcome such a drawback by implementing an open source client that is configurable to use different types of cloud storage facilities transparently to the user. An example for such a client is syncany.org. I've been waiting for the syncany.org project to reach a mature state for months but it seems like they are not making it in the near future. So I started to look for other possibilities to utilize cloud storage facilities using only standard means available on the operating system's level that would require no dedicated client to be installed. The rest of the article is devoted mainly to users of Unix-like OS's but there is a section in the end explaining how to mount and encrypt WebDAV resources on Windows platform.

 

Introduction


Number of available cloud storage facilities (CSF for short) makes it no easy task to choose the one (or some) to use. There is a comparison table available on this wiki page which can be used to make the decision easier but it lacks information if a CSF supports WebDAV extension or not. I know at least two CSF supporting WebDAV: Box.com and Yandex.Disk. The box.com is known for its promo actions in the past that allowed a regular user to allocate 50Gb storage for free. The negative side of this CSF is the maximum file size of 250Mb. Yandex.Disk on the other hand gives 10Gb for free and the maximum file size is 3Gb. Register on any service you like that supports WebDAV extension. Generally one should choose a CSF that is closer geographically to reduce latency and to increase the throughput.

 

Part 1: Mounting WebDAV resources


Standard means of mounting WebDAV resources on Unix-like OS's is davfs2 module. This is the FUSE based module and it works in userspace. After installation of davfs2 it should be reconfigured to set SUID bit to make it possible for a regular user to mount davfs resources. On a Debian-like distribution just call 'sudo dpkg-reconfigure davfs2' and follow the instructions. After reconfiguring users that are allowed to mount davfs resources should be added into the davfs2 group:
sudo adduser alex davfs2
To facilitate mounting process the following entries could be added to the /etc/fstab:
https://dav.box.com/dav/  /home/alex/box.com  davfs noauto,user 0 0
https://webdav.yandex.ru/  /home/alex/yandex.disk  davfs noauto,user 0 0
To avoid entering CSF credentials each time when it is mounted the following should be performed:
mkdir ~/.davfs2
cp /etc/davfs2/davfs2.conf ~/.davfs2
echo "use_locks 0" >> ~/.davfs2/davfs2.conf
echo "/home/alex/box.com BoxUserName BoxPassword" > ~/.davfs2/secrets
echo "/home/alex/yandex.disk YDUserName YDPassword" >> ~/.davfs2/secrets
chmod 600 ~/.davfs2/secrets
Please refer to the man pages of the davfs2.conf (man davfs2.conf) to find out more about available options like use_locks or proxy (if a proxy is required). At least it is worth reading about caching options that could increase the overall performance.

To test if everything is configured correctly one could use the following:
mount /home/alex/box.com # this directory shall exist
echo "Some message to test mounted resource" > ~/box.com/testfile.txt
Now check with the box.com's web frontend if testfile.txt exists on the server.

 

Part two: Encrypting remote files locally.


Sensitive information should not be stored in plain text on a server. That's why there should be a way to encrypt files locally before they are sent to the server. One could create a TrueCrypt container on the server but:
  • container size cannot be bigger as 250M on the box.com server.
  • it would be necessary to transfer the whole container from the server and back even if only couple of bytes are changed.
One possible solution is to use encfs. Encfs works transparently as an additional layer above existing file system and allows to access separate files without the need to sync the whole encrypted directory. All files are encrypted locally and the user has full control of the cypher algorithm to be used (encfs uses algorithms available on the system). Additionally by creating an encfs file system it is possible to specify the files block size (default is 1024 bytes) which could improve the performance as only changed blocks would be reencrypted and transferred. Unfortunately this feature cannot be utilized by the davfs file system because it doesn't support partial updates.
After installation of the encfs it is necessary to add users to the fuse group:
sudo adduser alex fuse
Now it's time to create an encrypted directory. I'm using the following pattern for all CSFs:
  • create a directory on the server named crypt
  • create a local directory named ${resourceName}.crypt
In other words all files in the local ~/${resourceName}.crypt directory will be transparently encrypted and stored on the server in the crypt directory (which is mapped locally to the ~/${resourceName}/crypt). 
For example to create an encrypted directory on the box.com I'd need to do the following:
mkdir /home/alex/box.com/crypt # directory is created on the server
mkdir /home/alex/box.com.crypt # local directory that is mapped into the directory created above
encfs /home/alex/box.com/crypt /home/alex/box.com.crypt # full path shall be used
By creating new encrypted directory encfs asks several questions and also requests a new password. This new password should not be equal of course to the password given by registering on the box.com.

To test if everything is properly configured one could do the following:
cat /etc/passwd > ~/box.com.crypt/mypasswd
ls -la ~/box.com/crypt
On the server side files and their names are encrypted:
alex@xubuntu:~$ ls -la ~/box.com/crypt/
total 2.8K
-rw-rw-r-- 1 alex alex 1.7K May 13 15:25 FPcfOjww7ZzucMGSMgncWXEt
drwxr-xr-x 2 alex alex  368 May 13 15:25 .
drwxr-xr-x 5 alex alex  136 May 13 15:25 ..
-rw-r--r-- 1 alex alex 1.1K Apr 14 23:24 .encfs6.xml
By using this approach it will be possible to store non encrypted files too simply putting them anywhere but not in the crypt directory.

 

Part 3: Mounting everything automatically.


Now I need to find a way to automatically mount encrypted partition after mounting davfs resource. One possible solution to get notified when a volume is mounted or unmounted is to use dbus notifications. This solution was a bit too complex so I've written a simple script to automatically mount/umount both davfs and encfs for a specified CSF:
alex@xubuntu:~$ cat ~/mountscf 
#!/bin/sh

service=$1
userdir=$HOME # adjust it if needed
enc_config=".encfs6.$1.xml"

[ -z "$service" ] && {
  notify-send -u low -i error "Service parameter is missing" "Please, specify a service name" 
  exit 1
}

isdavmounted=$(mount | grep $service | grep davfs)
isencmounted=$(mount | grep $service | grep encfs)

if [ -n "$isencmounted" -o -n "$isdavmounted" ]; then 
  #unmount
  [ -n "$isencmounted" ] && {
    fusermount -u "${userdir}/${service}.crypt" && msg="Encfs,"
  }
  [ -n "$isdavmounted" ] && {
    umount "${userdir}/${service}" && msg="${msg}Davfs "
  }
  if [ -n "$msg" ]; then
    notify-send -u low -i info "$service unmounting" "$msg unmouned successfuly!" 
  else
    notify-send -u low -i error "$service unmounting" "Failed to unmount $service!" 
  fi
else
  #mount
  mount ${userdir}/${service} && {
    msg="Davfs,"
    export ENCFS6_CONFIG="$HOME/.encfs/$enc_config"
    encfs --extpass="cat $HOME/.encfs/$service" ${userdir}/${service}/crypt ${userdir}/${service}.crypt/ && {
      msg="${msg}Encfs "
    }
  }
  if [ -n "$msg" ]; then
    notify-send -u low -i info "$service mounting" "$msg mounted successfuly!" 
  else
    notify-send -u low -i error "$service mounting" "Failed to mount $service!" 
  fi
fi
Script input parameter is the CSF name to be mounted. This name is actually the service directory name that exists in the directory specified by the $userdir variable. The trick here is the encfs's --extpass parameter. This parameter specifies an external command that prints CSF's password into stdout. In the script passwords are simply printed by the cat command. Passwords are created and stored the following way:
mkdir ~/.encfs
echo "EncfBoxComPass" > ~/.encfs/box.com
echo "EncfYandexDiskPass" > ~/.encfs/yandex.disk
chmod 600 ~/.encfs/box.com
chmod 600 ~/.encfs/yandex.disk

NOTE: The script has been adapted to reflect recent policy changes on the box.com. Now, dot files (files that starts with the period) aren't allowed to be stored. As the result encfs cannot create a config file called '.encfs6.xml' in the '.../crypt' directory on the server. The workaround is to store these config files locally. I store them in the ~/.encfs directory and export ENCFS6_CONFIG variable before calling encfs. Using this trick it is also possible to mount different CSFs at the same time. Storing config files locally also improves security and reduces mount time a bit. Thanks to Hans Comps for pointing this out in the comments.

The script can be started by a launcher on the desktop:

[Image]


After completion of the mount/umount operation a notification is shown:
[Image]

 

Windows Client


An encrypted directory created by the encfs can also be accessed on Windows platform using BoxCryptor.  BoxCryptor uses the same mechanisms internally that encfs uses. The only problem here is that BoxCryptor askes for a drive letter which means that WebDAV resources shall be mapped onto a drive letter first. I haven't found a way to do that on Windows XP platform using only operating system means. WebDAV resources could be added as network resources on Windows XP but cannot be mapped to a drive letter. Windows 7 on the other hand maps WebDAV resources using 'net use' command but I do not own a copy of Windows 7 to test. Alternatively one could use the NetDrive free utility to map WebDAV resources to a drive letter to make BoxCryptor work.

 

Conclusion


Using the aforementioned technique is straightforward. Both encfs and davfs2 are available in the Ubuntu Software Center and are easy to install. Still it shall be noted that dedicated clients could perform better if they support partial updates.

12 comments:

Hans Comps said...

Hi,
Just started using my Box.com account.
Successfully mounted webdav box.com to local mount point. Working fine.
Issuing encfs to mount box.com/crypt to local box.com.crypt point does not work. No error, no verbose messages, just no mount happening?

alex said...

I do not know what's wrong with your setup. Well, try to encrypt a local folder first to see if it works. If it works then it should also work with a webdav mapped folder.

Hans Comps said...

Tried to create hidden (.dot) files and it failed.
From the Box.com FAQ:
------------------
The following file types are blocked by Sync:
Temporary files and folders (.tmp and files/folders starting with ~ character)
System and hidden files
Windows shortcuts
Box WebDocs
Outlook PST files
QuickBook files
Google Docs/Spreadsheets
------------------

So I guess the hidden encfs config file create fails?

Hans Comps said...

Just successfully created via the web, so I guess it is a Webdav issue?

alex said...

Ha, indeed. This must be something new on the box.com! All dot files created through the webdav are deleted! I have created an encfs folder long time ago and the .encfs6.xml is not deleted! Seems like they have some kind of a trigger for dot files created after specific date. Well I prefere Yandex anyway. Thanks for pointing that out

Hans Comps said...

I can upload a local <.encfs6.xml> via the web interface, but it is not "readable" from the webdav mount.
Any tips on keeping the .encfs6.xml file local, while having the data in the webdav mount?
I tried modifying the ENCFS6_CONFIG variable, but can't seem to have it redirect the xml file.

alex said...

Well ENCFS6_CONFIG trick works for me. Just make sure that you use the full path to the new file location and don't forget to export ENCFS6_CONFIG before calling encfs. I'll update the article to reflect the changes needed to store .encfs6.xml files locally

Hans Comps said...

Finger trouble with the export.
Thanks, works fine with keeping the xml local.
Not sure, but I think you can assign any filename to ENCFS6_CONFIG.
Will check; if so I will just have it link to encfs6.xml (not hidden) and move the file again to box.com.

Hans Comps said...

Last update.
Works fine with encfs6.xml file (not hidden) on box.com, mounted with webdav. Just point ENCFS6_CONFIG to it.

Anonymous said...

Thanks for this tutorial, but the final script does not work for me. Everything works fine until calling ./mountcfs box.com. All it delivers is "21:49:14 (FileUtils.cpp:360) Konfigurationsdatei /home/ichbins/.encfs/.encfs6.box.com.xml wurde gefunden, konnte aber nicht geladen werden", then I am again at the point after calling "encfs /home/ichbins/box.com/crypt /home/ichbins/box.com.crypt". Any idea whats going wrong?

Anonymous said...

Works like charm! The error only occured during the first mount process because it had to create the local xml file. After the first mount everything works fine!

Anonymous said...

Some enhancement of script, when exporting encfs xml file path, check file existence to avoid script break at first run:

...
export ENCFS6_CONFIG="$HOME/.encfs/$enc_config"
if [ -f "${ENCFS6_CONFIG}" ]; then
encfs --extpass="cat $HOME/.encfs/$service" ${userdir}/${service}/crypt ${userdir}/${service}.crypt/ && {
msg="${msg}Encfs "
}

else
encfs ${userdir}/${service}/crypt ${userdir}/${service}.crypt/ && {
msg="${msg}Encfs "
}

fi
...