ZFS encryption and notification service on OmniOS
679 words, 4 minutes
I have a Dell server in a colo. It (will) hosts some virtual machines and (private) data. And because trust does not exclude control (on your data), I do encrypt (at rest) some of the storage.
Read the zpool-features(7) and zfs(8) manpages to learn about the encryption feature. Read the smf(7) manpage to learn about Solaris service management facility.
Encrypted ZFS dataset
Create a ZFS pool and enable the encryption feature.
# diskinfo
TYPE DISK VID PID SIZE RMV SSD
SCSI c1t0d0 DELL PERC H710 136.12 GiB no no
SCSI c1t1d0 DELL PERC H710 1116.75 GiB no no
# zpool create -m /tank -o feature@encryption=enabled tank c1t1d0
Create an encrypted ZFS dataset.
# zfs create -o mountpoint=/zones \
-o encryption=on -o keyformat=passphrase \
tank/zones
Enter passphrase:
Re-enter passphrase:
Note on keylocation
: if unspecified, the default is prompt
. Which is
exactly what I want.
Note on pbkdf2iters
: although I tried to understand what a “good value”
would be, I couldn’t make up my mind. So I just used the default OmniOS
(Illumos?) value ; which is 350000.
Let’s have a look at what’s been done.
# zfs get -rp encryption,keystatus,pbkdf2iters,keylocation tank
NAME PROPERTY VALUE SOURCE
tank encryption off default
tank keystatus - -
tank pbkdf2iters 0 default
tank keylocation none default
tank/zones encryption aes-256-ccm -
tank/zones keystatus available -
tank/zones pbkdf2iters 350000 -
tank/zones keylocation prompt local
Notification service
As I used the passphrase
keyformat and the default keylocation is
prompt
, the ZFS dataset is not automatically mounted when the system
boots. It must be done manually ; which is exactly what I want. So I
wrote a script that runs at boot time and notifies me, using the
excellent Pushover notification system
, that I
have to log into the server and mount / unlock the dataset.
# vi /home/scripts/notif-boot
#!/usr/bin/env sh
PATH="/bin:/usr/bin:/sbin:/usr/sbin"
TOKEN='<changeme>'
USER='<changeme>'
TITLE="$(hostname -s) is ready for action"
MESSAGE="Connect and run /home/scripts/afterboot."
curl -s \
-F "token=$TOKEN" \
-F "user=$USER" \
-F "title=$TITLE" \
-F "message=$MESSAGE" \
https://api.pushover.net/1/messages.json \
2>&1 1>/dev/null
exit 0
# chmod 0740 /home/scripts/notif-boot
There does not seem to be any @reboot
special field available on
OmniOS’ crontab , so I created a service that runs only once at boot
time ; based on /lib/svc/manifest/system/zones.xml
and
/lib/svc/manifest/network/ssh.xml
.
# vi /root/notif-boot.xml
<?xml version="1.0"?>
<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
<service_bundle type='manifest' name='notif-boot'>
<!--
Run a script at system startup.
-->
<service
name='site/notif-boot'
type='service'
version='1'>
<create_default_instance enabled='false' />
<single_instance />
<dependency
name='multi-user-server'
type='service'
grouping='require_all'
restart_on='none'>
<service_fmri value='svc:/milestone/multi-user-server' />
</dependency>
<exec_method
type='method'
name='start'
exec='/home/scripts/notif-boot'
timeout_seconds='30'>
</exec_method>
<exec_method
type='method'
name='stop'
exec=':true'
timeout_seconds='30'>
</exec_method>
<property_group name='startd' type='framework'>
<propval name='duration' type='astring' value='transient' />
</property_group>
<stability value='Unstable' />
</service>
</service_bundle>
# svccfg validate /root/notif-boot.xml
# svccfg import /root/notif-boot.xml
# svcadm enable svc:/site/notif-boot
# svcs | grep notif
online 18:37:04 svc:/site/notif-boot:default
The validate
command works in the “No news is Good news” manner. In
case of errors, they will be described. In case of success, the command
ends silently.
Should the service fail, svcs -xv
can be used to get more information.
Should you want to stop or remove the service, those commands may help:
# svcadm clear svc:/site/notif-boot
# svcadm disable svc:/site/notif-boot
# svccfg delete svc:/site/notif-boot
From there, I get a Pushover notification each time my server boots and I should mount the encrypted ZFS dataset(s).
Mount encrypted ZFS
Mouting the encrypted dataset(s) can be done manually. But I have services that depends on those datasets. So I also have to run a bunch of commands. So I wrote a script that ask for passphrase(s) to mount the encrypted ZFS dataset(s) and start services that rely on data stored there.
The relevant parts of the script that will decrypt the ZFS dataset is:
# vi /home/scripts/afterboot
#!/usr/bin/env sh
PATH="/bin:/usr/bin:/sbin:/usr/sbin"
echo "Loading ZFS encryption key..."
zfs load-key tank/zones
echo "Mounting ZFS dataset(s)..."
zfs mount tank/zones
(...)
exit 0
# chmod 0750 /home/scripts/afterboot
Running /home/scripts/afterboot
loads the ZFS encryption key(s) and
mounts the encrypted dataset(s).