Rust CLI · 2021 completed

Shavee

Automatically decrypt and mount ZFS datasets using Yubikey HMAC as 2FA, or any file on USB/SFTP/HTTPS. PAM module for auto-unlocking encrypted home directories on login.

Shavee

Shavee is a program to automatically decrypt and mount ZFS datasets using Yubikey HMAC as 2FA or any File on USB/SFTP/HTTPS drive, with support for PAM to auto mount home directories on login.

Supported Methods

1. Yubikey

Yubikeys are secure authentication USB devices we can use as a strong second factor. Yubikeys come pre-programmed with a HMAC key on Slot 2 which can be used to derive the final encryption key along with your password.

Important: The programmed HMAC secret in the Yubikey CANNOT be extracted once programmed in. If you want to use multiple keys on the same dataset (e.g. backup keys), you must program the same fresh HMAC secrets on all those keys.

Yubikey mode is set with the -y flag. In this mode the program looks for a Yubikey on login and uses its HMAC mode along with your password to derive the final encryption key. The Yubikey HMAC Slot can be set with the -s flag (defaults to Slot 2).

2. File / HTTP(S) / SFTP

In this mode the program looks for a file (any file) and uses it along with your password to derive the final encryption key. File mode is set using the -f <path> option. The file can be local, HTTPS, or SFTP:

# HTTPS
shavee -f https://foo.org/secret.png

# SFTP (custom port)
shavee -f sftp://user@foo.org/mnt/secretfile -P 4242

# Local file (e.g. USB drive)
shavee -f /mnt/usb/secret.png

The -P option sets the port for both HTTP and SFTP. The idea is to keep the file on a USB storage device or a network location you control, present during login to derive the final encryption key. You can use any pre-existing file, or create one:

dd if=/dev/urandom of=./secretfile bs=4096 count=4096

Note: Since the file becomes part of your encryption key and its security cannot be guaranteed as with a Yubikey, you are responsible for keeping it secure.

3. Password Only

If no second factor is specified, the program uses only the password as a single factor.

Build and Install

  1. Install Rust
  2. Clone the repo:
git clone https://github.com/ashuio/shavee.git
cd shavee
  1. Build:
cargo build --release
  1. Install the binary:
sudo cp target/release/shavee /usr/bin
  1. Install the PAM module:
sudo cp target/release/libshavee_pam.so /usr/lib/security/

Modes

Flags / Options

FlagDescription
-yUse Yubikey for 2FA
-fUse any file as 2FA (takes filepath, HTTP(S), or SFTP location)
-PSet port for HTTP and SFTP requests (uppercase P)
-sSet Yubikey HMAC Slot (1 or 2)
-cCreate/Change key of ZFS dataset with derived encryption key
-zUnlock and mount the given dataset with the derived key (takes ZFS dataset path; auto-appends username in PAM mode)

The -y (Yubikey mode) and -f (File mode) flags are interchangeable. It is recommended to re-run the key change command for your datasets after version updates.

Configure ZFS Datasets

If using with PAM: your dataset password should be the same as your user account password for it to work automatically. Remember to update your encryption key if you update your password.

Change/Update encryption key

Encryption must already be enabled and the key loaded to change the key of an existing dataset:

shavee -c <zfs dataset path>
# e.g. shavee -y -c zroot/data/home/hunter

Create a new dataset

sudo shavee -c <Desired dataset>
# e.g. sudo shavee -f /mnt/usb/secretfile -c zroot/data/home/hunter

Unlock and mount any ZFS partition

shavee -y -z zroot/data/home/hunter/secrets

Use in Scripts

Pipe the password directly into shavee:

echo "hunter2" | shavee -y -z zroot/data/home/hunter/secrets

Use a USB Drive for Second Factor

Use the -f option to substitute a Yubikey with any USB drive. To auto-mount the USB at boot so shavee can find the required keyfile on login, use udev. Create /etc/udev/rules.d/99-usb-automount.rules:

ACTION=="add", SUBSYSTEMS=="usb", SUBSYSTEM=="block", ENV{ID_FS_UUID}=="<UUID of partition>", RUN{program}+="/usr/bin/systemd-mount --no-block --automount=yes --collect $devnode <Desired Mount point>"

Example:

ACTION=="add", SUBSYSTEMS=="usb", SUBSYSTEM=="block", ENV{ID_FS_UUID}=="ADB0-DA9C", RUN{program}+="/usr/bin/systemd-mount --no-block --automount=yes --collect $devnode /media/usb"

Get the UUID with:

udevadm info --query=all --name=<Target disk> | grep ID_FS_UUID=
# e.g. udevadm info --query=all --name=/dev/sdb1 | grep ID_FS_UUID=

Reload udev rules:

udevadm control --reload-rules

PAM Auto-Unlock Encrypted Home on Login

Shavee comes with a PAM module to execute during the login process. Add the following line to your desired PAM login method file (e.g. /etc/pam.d/sddm for graphical logins, /etc/pam.d/login for CLI logins):

auth optional libshavee_pam.so -y -z <base home dir>

Example:

auth optional libshavee_pam.so -y -z zroot/data/home

Where zroot/data/home mounts to /home.

Dual Home Directories in ZFS

Since ZFS mounts datasets over pre-existing directories and we defined our module in PAM as optional, we still get authenticated with just the password even if the dataset is NOT decrypted (e.g. because the Yubikey was not inserted).

We can use this to essentially have two home directories:

Let me know if interested — I could write up a more detailed guide.

← All projects