eCryptfs and $HOME

Following the short introduction to eCryptfs and file-system encryption solutions; this article describes how I used eCryptfs to encrypt my entire $HOME, and dm-crypt to protect swap space (without breaking hibernation).


eCryptfs basics

eCryptfs is a kernel-native stacked cryptographic file-system for GNU/Linux, that is used on top of existing mounted file-systems. It is a part of Linux since version 2.6.19 (the ecryptfs module), but to work with it you need the userspace tools: ecryptfs-utils, which in turn require keyutils (tools for the kernel key management system). Once you install these two packages you can modprobe ecryptfs and continue with the setup. If you are interested in implementation details I highly recommend the Linux Journal article by Michael Halcrow, the architect and former developer of eCryptfs. Today Michael works at Microsoft on BitLocker - their own disk encryption solution.

Encrypting a directory

I co-authored another article on eCryptfs, that guides you trough the process of creating a secure and private directory within your $HOME. You can read it on the Arch Linux wiki.

Encrypting $HOME

First you need to backup your current $HOME, what I did was:
$ su -
# mv /home/user /home/user.old
# mkdir -m 700 /home/user
# chown user:user /home/user
# usermod -d /home/user.old user
After your new $HOME becomes /home/user.old re-login to the system. Then prepare the rest of the eCryptfs directory structure:
$ su -
# mkdir -p /home/.ecryptfs/user/.Private
# chmod 755 /home/.ecryptfs
# chmod -R 700 /home/.ecryptfs/user
# chown -R user:user /home/.ecryptfs/user
# ln -s /home/.ecryptfs/user/.Private /home/user/.Private
# chmod 500 /home/user
Directory /home/.ecryptfs is owned by root and it is a central place for everything related to eCryptfs and user accounts. Everything under /home/.ecryptfs/user is owned by you and the actual encrypted data will be stored in /home/.ecryptfs/user/.Private. That directory will be mounted on top of /home/user (for convenience I will use the symlink /home/user/.Private when mounting). While /home/user is not mounted we made sure that nothing can be written there with that last chmod command. This will prevent cronjobs and other software from causing problems.

Note: Previously this article explained a much simpler setup, using /home/user as both lower and upper directory. This new approach is better and safer. Among other things it avoids problems with cronjobs (as explained), and it is much easier to access your encrypted data (i.e. for backup).

We can now mount eCryptfs over /home/user - notice that while mounted it will have the same permissions as the lower .Private directory:
# mount -t ecryptfs /home/user/.Private /home/user
Since this is the first time you are mounting this directory eCryptfs will ask you some questions about how you want it set up. Summary of my own options:
Key type: passphrase
Passphrase: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Cypher: twofish
Key byte: 32
Plaintext passtrough: yes
Filename encryption: yes
Add signature to cache: yes
Filename encryption is available from Linux 2.6.29, and I enabled passtrough because I needed it for auto-mounting later (it allows un-encrypted files to be stored in the lower directory). When asked you can also generate an additional key that will be used for filename encryption, or you can use the same key you generated for encrypting file contents. As for your passphrase, I don't need to tell you that it should be strong, you can generate a batch of strong pseudo-random passphrases with a command like:
# < /dev/urandom tr -cd \[:graph:\] | fold -w 32 | head -n 5
Remember it or print it on paper for safe keeping, this will be your mount passphrase which will be used to mount this directory. Without it your data is lost.

Note: The relation between a passphrase and key(s) could be confusing, so I will explain it in more detail; The passphrase is your mount passphrase which will be salted, hashed and loaded into the kernel keyring. In eCryptfs terms, this salted, hashed passphrase is your "file encryption key, encryption key", or fekek (I will refer to it as just "key" later in the text). Also, in eCryptfs terms the key used to protect filenames is known as "filename encryption key", or fnek. Usually it is automatically generated from the same passphrase, which is salted and hashed a little differently then it was for generating the fekek.

The directory is now mounted and your key is in the kernel keyring. We now have to edit a few files to allow easier mounts in the future. First inspect your /etc/mtab file, it contains an entry for encrypted /home/user, which you need to replicate in /etc/fstab. An example entry in fstab might then look like (XYZ represents the key signature):
# Encrypted volumes
/home/user/.Private /home/user ecryptfs rw,user,noauto,exec,ecryptfs_sig=XYZ,ecryptfs_cipher=twofish,ecryptfs_key_bytes=32,ecryptfs_passthrough,ecryptfs_fnek_sig=XYZ,ecryptfs_unlink_sigs 0 0
When mounting your new $HOME as a user you will use mount -i so you don't invoke the mount helper again. Using -i by default mounts with noexec, nosuid, nodev - this being your $HOME you will probably want to have executable files, so I added exec to the above fstab line.

When you mounted, a /root/.ecryptfs directory was automatically created - that's where the signature cache was stored. Let's create a few other files in there (later we will move this configuration directory, and transfer the ownership to you):
# touch /root/.ecryptfs/auto-mount
# ecryptfs-wrap-passphrase /root/.ecryptfs/wrapped-passphrase
Passphrase to wrap: [enter mount passphrase]
Wrapping passphrase: [enter user password]
Let's explain the second command. We are using a passphrase for our mount point and the key needs to be added to the kernel keyring every time we want to mount /home/user. Our later goal is to have auto-mounting on login, so to make that easier and automatic we encrypted our mount passphrase with our login password. The resulting hash is stored in the wrapped-passphrase file, which can be unwraped by the eCryptfs PAM module on login (more about this below). This also has some security implications, which we will discuss later on.

Our initial setup is done, let's un-mount the directory:
# umount /home/user

Auto mounting on login

To be able to auto-mount when you login to the system your eCryptfs configuration directory must be readable, it can't be encrypted. We will now move it to /home/.ecryptfs/user:
# mv /root/.ecryptfs /home/.ecryptfs/user
# chown -R user:user /home/.ecryptfs/user/.ecryptfs
# ln -s /home/.ecryptfs/user/.ecryptfs /home/user/.ecryptfs
Now we have to write the actual auto-mounting code. For me the simplest solution was to employ shell initialization files. In my case and ZSH those being ~/.zprofile and ~/.zshrc. In case of BASH ~/.profile and ~/.bashrc can be used. The way your shell reads init files is important, the profile file is read first and in case it's an interactive shell the rc file is read next. You could add the auto-mount code to your existing profile or rc file and leave it un-encrypted. But let's not expose any more information than it is absolutely needed. I setup my ~/.zprofile to mount on login and left ~/.zshrc encrypted. Let's see the example code:
# -*- shell-script -*-
if [ -r "${HOME}/.ecryptfs/auto-mount" ]; then
  grep -qs "${HOME} ecryptfs" /proc/mounts
  if [ $? -ne 0 ]; then
    mount -i "${HOME}" ; cd "${HOME}"
In order for this code to be executed the file must be readable. That's why we enabled passtrough before. Save it as /home/ for now.

Note: An alternative to mounting from your shell profile is to use the pam-mount package. You should also know that eCryptfs PAM module has some auto-mounting functionality, but most of it was written with Ubuntu in mind. To make use of it you would have to setup everything exactly the way Ubuntu does. In fact, if you followed this guide then you already did so. However it's not possible to change default options and you would have to use AES and a 16 byte key. How does it work? If the PAM module finds the auto-mount file it unwraps the passphrase and proceeds to call /sbin/mount.ecryptfs_private which can successfully mount only if all above requirements are met. Same thing applies to un-mounting when session is closed, provided the PAM stack is configured correctly.

Now we need to configure PAM to automatically unwrap the passphrase on login (using the password the user entered to login to the system). My current workstation is running Arch Linux so I modified /etc/pam.d/system-auth - on other distributions /etc/pam.d/login or common-auth could be used (also consider the run-level and possibly login manager you are using). The important part being to add the first setting immediately below the line referencing the module in the auth context and the second immediately above the line referencing the module in the password context. Let's see an example from Arch Linux:
auth      required     try_first_pass nullok
auth      required unwrap
password  required
password  required     try_first_pass nullok sha512 shadow
# eCryptfs optional in session context
#session  optional

Now that you prepared everything for automatic mounting you can once again mount /home/user to transfer your files back - and encrypt them in the process. When you un-mounted the directory the key was deleted from the kernel keyring so you will have to insert it once more. This would also be a good place to mention that keyctl utility can be used for key management or for some simpler operations you can use the ecryptfs-manager:
$ ecryptfs-insert-wrapped-passphrase-into-keyring /home/user/.ecryptfs/wrapped-passphrase
Passphrase: [enter user password]

$ mount -i /home/user
Now move or copy your files from /home/user.old to /home/user. If you have enough space you should definitely copy so you have a backup. Files will be encrypted as they are placed in their new home:
$ rsync -aP /home/user.old/ /home/user/
When it's done you should exit and login as root, un-mount the encrypted directory and change your $HOME once more. Last step is to setup the shell profile you prepared earlier:
# umount /home/user
# usermod -d /home/user user

# chmod 600 /home/
# chown user:user /home/
# mv /home/ /home/.ecryptfs/user/.Private/.profile
# ln -s /home/.ecryptfs/user/.Private/.profile /home/user/.profile

# exit
You can now login as a user to test if auto-mounting works. PAM will unwrap your passphrase and insert the key into the kernel keyring. Your auto-mount code from ~/.profile will be run next. You are now using an encrypted $HOME directory.

It is a good idea to create another set of links (this time encrypted) to your eCryptfs configuration directories, so that both your encrypted and un-encrypted $HOME points to them:
$ ln -s /home/.ecryptfs/user/.ecryptfs /home/user/.ecryptfs
$ ln -s /home/.ecryptfs/user/.Private /home/user/.Private

Encrypting swap space

Various data is leaked to your swap space. If we are encrypting our $HOME we have to encrypt swap too. Since eCryptfs is not ideal for encrypting entire partitions we will use dm-crypt for this. Since nothing permanent is stored in swap you can setup dm-crypt to use a random generated key every boot and re-write the swap space. But in order for suspend to disk to work the hibernation image must not be changed/deleted. I have read all kinds of miss-information about this, many claimed it's impossible to use encrypted swap with hibernation. It's not true. Important thing being to use a static key for encryption - so your swap stays unchanged on next boot and hibernation image intact.

Note: My swap partition is /dev/sda3 and I use uswsusp for suspend/hibernate. Examples are written accordingly.

First we will setup an encrypted swap space, with dm-crypt and LUKS. Luks is needed because of the static key, in Arch Linux package is called cryptsetup:
$ su -

Disable swap space
# swapoff /dev/sda3

Encrypt /dev/sda3 and set a passphrase (no connection to eCryptfs one)
# cryptsetup -h sha256 -c twofish-xts-essiv:sha256 -s 256 luksFormat /dev/sda3

Map /dev/sda3 to /dev/mapper/swap (using the passphrase)
# cryptsetup luksOpen /dev/sda3 swap

Make /dev/mapper/swap a swap device
# mkswap /dev/mapper/swap

Enable the encrypted swap
# swapon -p 1 /dev/mapper/swap
Swap is configured, now edit a few files to tell your system to use this new swap. Everything that will be written to it will be encrypted on the fly and actually written to your real swap partition. First comment out your current swap entry in /etc/fstab and create a new one pointing to your new swap space:
#/dev/sda3   swap         swap      defaults                0 0

# Encrypted volumes
/dev/mapper/swap          swap      swap     defaults       0 0
Uswsusp (or whatever method you are using - kernel, TuxOnIce) also needs to know that the new resume device is /dev/mapper/swap. Edit /etc/suspend.conf and replace your entry:
#resume device = /dev/sda3
resume device = /dev/mapper/swap
Note: You can also setup encryption with uswsusp it self. More information can be found in /usr/share/doc/suspend/README.encryption. Basically you generate a key file /etc/suspend.key, enable encryption in suspend.conf and point "RSA key file" entry to the key you generated. Your hibernate image would already be encrypted once when written to /dev/mapper/swap, but if you really want two layers of encryption you could do it.

Now comes the really tricky part. In order to resume from hibernation your (encrypted)swap must be mounted early on. How hibernation is usually done is with a resume hook in your initrd. You will need another hook in there that will setup swap before resume hook is run. I will explain my setup on Arch Linux, for Debian I found one guide on how to do it there.

Arch Linux already has an encrypt hook that is used to mount the root (and only root) file-system in case it's encrypted. All other encrypted partitions are mounted later on from /etc/crypttab - not of much use for hibernation, because by then it would be too late. I modified the hook /lib/initcpio/hooks/encrypt and changed cryptdev to "/dev/sda3" and cryptname to "swap". In recent versions the syntax has changed and cryptdevice is used like so: cryptdevice="/dev/sda3:swap swap=/dev/mapper/swap". This also means that you can append "cryptdevice=/dev/sda3:swap" to the kernel in your boot loader configuration and avoid editing the hook all together.

The basic thing here is that a command like the following example must be run on boot (it will ask for the passphrase and then unlock the swap partition):
/bin/cryptsetup luksOpen /dev/sda3 swap
Add the encrypt hook to /etc/mkinitcpio.conf before the uresume/resume and filesystems hooks. You should then re-create the initrd:
# mkinitcpio -p kernel26
All done. You can reboot and test it with a clean hibernation cycle.

Note: If you have concerns about using a static key, consider using a static key only when you hibernate, while using a random key otherwise. It can be done and you will find examples of automation on the web. If nothing else you can re-mount the swap by hand, using a passphrase, before hibernating.

Encrypting other directories

Some other directories to consider for encryption are: /tmp and /var/tmp. You could use dm-crypt or eCryptfs with random keys for /tmp since nothing permanent is stored there. But because of this, you could also just setup /tmp as tmpfs and have it in memory. The /var/tmp directory is trickier, it holds some data that should be preserved and it should be mounted very early in the boot process (before anything else is written there). Doing it would be an interesting exercise.

An example fstab entry for the tmpfs:
# Pseudo
none    /tmp    tmpfs    defaults,size=512M,mode=1777    0 0

Notes on security

If you decided to use auto-mounting eCryptfs setup explained here employs a strong encryption passphrase for your files, but actually protects it with your user login password (which in most cases doesn't even have 10 characters). First think about making your login password more complex, using eCryptfs or not. The Diceware method provides strong passwords that are easy to remember. I also recommend this document on password recovery speeds.

Simple method for drastically improving the security of your eCryptfs setup is to move your wrapped-passphrase file to a removable storage device, like a memory card. Symlink it to your eCryptfs configuration directory, and in this scenario you get two-factor authentication, something you know (your password) and something only you have (your wrapped-passphrase file):
$ ls -al ~/.ecryptfs/wrapped-passphrase
lrwxrwxrwx 1 user user 18 2009-03-14 21:45 /home/user/.ecryptfs/wrapped-passphrase -> /mnt/usb/.ecryptfs/key
Note: If you ever change your login password you can use the ecryptfs-rewrap-passphrase utility to update and re-encrypt your wrapped-passphrase file.

Using this method requires your removable storage to be mounted before you login. Modern distributions allow you to auto-mount through udev but there are also legacy solutions like autofs which still work good. Using it your media would be mounted the moment PAM tries to read your wrapped-passphrase. Here are some mount options to consider:
usb  -fstype=vfat,utf8,shortname=mixed,noexec,umask=077,fmask=0177,dmask=0077,uid=1000,gid=1000 :/dev/sdc1

Consider using additional layers of encryption. You can encrypt individual files (i.e. that hold passwords) or tarball-ed directories with GPG. If needed, tools like Elettra provide plausible deniability.

Last topic I will discuss are backups. Backup is mandatory, but if you went through all this to secure your data you can't just dump it on a file-server. The data must remain encrypted. One approach is to encrypt the destination it self, the other to backup the source in encrypted form (or both, yes). For removable drives both options are feasible, but in case of a network file-system eCryptfs is still not fully functional over NFS. My personal backup solution, rybackup, has eCryptfs support and could give you some ideas.

Copyright Adrian C. Licensed under the terms of the CC Att-SA license.