I built myself a NAS of my own design lately for my old one did not perform to the tasks I have in mind for those sorts of dedicated machines. Two of the many desires I wanted to tackle were the utilisation of a modern file system and properly encrypted disks. I had my eyes on FreeBSD and ZFS for quite a while and so I decided the time was right.
The problem was on how to achive the full disk encryption. ZFS does have this feature, but only as of Zpool version 30. FreeBSD 9.1 on the other hand only supports version 28 at this point. On my old maschine I used the Linux dm-crypt tool for encryption. Though I was now very happy with it, it did perform the task, so I looked into similar methods available on FreeBSD.
The FreeBSD Handbook (great
resource as it always is) has a section on
Encrypting Disk Partitions
and points out a solution using geli.
This does work nicely, but the example uses keyfiles - something I did not want.
Also, one has to know the device name (or number, like da2) of each disk.
Every disk of mine has a name trivial name. I tend to mount them into a folder
with this name, and I would like it if I could reference the device with this
name too, so there will not be some name mixup in case the device number changes
due to a changing disk setup. There is a tool called
glabel that does exactly
perform to the task. It assigns a labelname to a device, making it available
under /dev/label/$labelname.
Disk setup
Well now, lets go through the steps needed to get an encrypted ZFS disk:
First of all, we destroy any previous partitioning on the disk. This will wipe the device clean, therefore only proceed when you are absolutely certain!
dd if=/dev/zero of=/dev/$device bs=512 count=1
Now we create a new partitioning layout using the GPT (GUID Partition Table).
gpart create -s GPT $device
Next, we assign the label to the device.
glabel label -v $labelname /dev/$device
Until now you had to know the $device of the disk. From now on, we will only
reference it via the $labelname we just assigned.
geli init -s 4096 -e AES-XTS -J - /dev/label/$labelname
And this is where it gets interesting. We just used geli to initialise a
password protected GEOM container on the device. The -j option will make
geli prompt the password from the command line.
From now on, the device /dev/label/$labelname holds encrypted data. In order
to get a decrypted device we need to attach it.
geli attach -d -j - /dev/label/$labelname
As in the previous step -j will make geli prompt the password from the
command line. The result of this step is a new device called
/dev/label/$labelname.eli. This one is decrypted and we can use it as any
ordinary device. At this point it is still empty though, lacking even a file
system. So lets give it one.
zpool create -m /$mountpoint/$labelname $labelname \
/dev/label/$labelname.eli
This will create a Zpool on the disk with the poolname equal to the $labelname
we gave the disk. Additionally, it will set the mountpoint. This can be anywhere
within your file system. I have it in a special folder within the file system
root called /frachtraum (German for cargo bay), but you may choose any name
and place.
And finally we do some tweaks.
zfs set compression=lz4 $labelname
UPDATE: As of FreeBSD 10 the lz4 compression algorithm is supported, and it
is quite a good choice. Previously I used lzjb which was fine too.
And of course, we do need permissions.
chmod -R 775 /$mountpoint/$labelname
And that’s it! There is our new full disk encrypted ZFS drive. The reason why I
use bash variables like $device, $labelname and $mountpoint in all the
steps is because I do not tend to do this by hand, but within a script of
course. One does not want to make a tiny error and screw up the setup, you know.
All the variables are therefore provided as command line arguments.
Mounting procedure
Most likely one has more than one disk these days and running a decryption and mounting procedure by hand after every restart (a home NAS does not run 24/7 in general) tends to be quite dull, so some automation is in order. Basically, the only thing you need is a list of you labelnames stored somewhere in you file system. This - in opposition to an encryption keyfile - does not pose as a security risk.
echo -n "enter password: ";
stty -echo
read password
stty echo
LABELLIST=( $( < labellist.txt ) )
for labelname in "${LABELLIST[@]}" ; do
echo $password | geli attach -d -j - /dev/label/$labelname
if [ $? -eq 0 ]
then
zfs mount $labelname
fi
done
This is the very short version of my mounting script. I do have much more error handling and output eye candy involved, but this makes it a bit confusing to read.
Basically it prompts for the password and attaches the crypted device the same way used in the setup procedure. On success, it only performes a zfs mount. We told ZFS where the Zpool should be mounted during setup, so no need to do it again.
Some final notes
-
As you will have already noticed at this point, every disk gets its own Zpool, every Zpool containing exactly one ZFS. A lot of the great features of ZFS like mirroring or RaidZ are not used, therefore there is no redundancy involved in this setup. Many of you may not want this (and I myself am not entirely happy with it either)
-
Also, you will find that there are two ways for
glabelto write the label: automatic and manual. On the first glance automatic seems like a good idea. But if you read the man page carefully, you will find that this method stores the label name in the last sector of the GEOM provider (that is the disk). This sector is later overwritten by the geli encryption process - and therefore the label destroyed! -
If you do use a disk as an automatic backup target (lets say for OSXs time machine), I found it effective to use a stronger compression algorithm. ZFS also supports the
gzipalgorithm family from compression level 1 to 9, the later being the maximum. The results are quite effektive, though initial backups may take a little while longer. Yet it is not a good idea on disks storing a lot of already compressed data (like multimedia files).gziptries to compress it nevertheless, but fails of course, not without having spent a lot of time in the process, making write performance quite bad.lz4on the other hand is quite quick in realising a lost cause and will not effekt performance noticeably. -
The encryption key used for disk encryption is 4096 bits. At the time I wrote this script it was a good practice and there was no indication to worry. Yet this was before the Snowden leaks in Mid 2013 of course. And it seems that to Mid 2014 this still is a save keylength to stick to.