A port/rewrite of my local git server's zfsvault project which is a heavy modification of the Archlinux zfs boot hook. Rewritten a bit more modular.
Designed to unlock the root dataset at boot time (but also others if seen) by reaching out to a Hashicorp Vault server.
At the current time this is only designed for mkinitcpio hook use
-
Clone the repository or grab the latest stable release
-
run zfsUnlocker/install.
This will copy itself into a new directory
/etc/zfsUnlocker
with a config file/etc/zfsUnlocker/zfsUnlocker.conf
.The script also places a hook and install file for mkinitcpio to reference.
-
Edit
/etc/mkinitcpio.conf
and addzfsUnlocker
to your hooks. I put it at the end. -
Optionally edit
/etc/zfsUnlocker/zfsUnlocker.conf
adding your vault server's fqdn and either a vault token or approle role_id/secret_id pair. These can also be passed in kernel arguments if desired. Among other features. -
Run
mkinitcpio -P
to build an initramfs with this hook, its modules and config inside.
If you're using a kv secret engine which is named something other than kv
you can modify the vaultKvEngineName variable to match.
If you also aren't storing the secrets directly under kvEngineName/zfs/zpoolNames/datasetNames you can also change vaultRootSearchPath
to match your secret paths.
If you also aren't using the key passphrase
to store the passphrase value there's vaultKeyName
too.
Configuring the conf file is optional for the most part but has some potentially nifty configuration options among other bits and pieces:
- Network bond creation
- Designed for servers which can't just DHCP directly on an interface
- iPhone tethering (Enabled by default)
- For Internet access via idevicepair, ipheth & usbmuxd
- variable
wirelessNetworks
- For defining one or more SSID:PSK combos for wpa_supplicant to try joining during the DHCP section of the script.
- variable
forceInterface
- to override the DHCP interface
- ssh passphrase generation and pubkey installation
- For rare scenarios where a token may have expired for unlocking the machine remotely
The script tries stored vault tokens first and if unsuccessful looks to the kernel arguments for another token or approle role_id/secret_id combo to try.
If kernel arguments are used the script attempts to protect them by changing the permissions on /proc/cmdline
.
Examples assuming a ZFS rootfs system consisting of a zpool named after its shortname and a natively encrypted dataset 'root' (computer-name/root)
-
vault secrets enable -version=2 kv
-
vault kv put kv/zfs/computer-name/root passphrase='testPa5sphraseH3re'
An example policy which allows reading all of a zpool's dataset keys, if defined.
echo 'path "kv/data/zfs/computer-name/*" {capabilities = ["read"]}
path "auth/token/lookup-self" {capabilities = ["read"]}' | \
vault policy write zfs/computer-name -
vault token create -policy=zfs/computer-name -ttl=30d -display-name=computer-name
Specify the resulting string as VAULT_TOKEN=xxx in either /etc/zfsUnlocker/zfsUnlocker.conf
or in your kernel arguments.
An example approle granting read access to the above strict single-zpool policy.
Successful auth for this example generates a token valid for one minute or two uses. Exactly enough for the hook to look its own token up (-1) and read exactly one kv secret afterwards (-1) before expiring - or 60 seconds. With the secret_id being reusable infinitely until it expires after 7 days.
In an example where there are more datasets to unlock in the early boot stage with unique passphrases, it may be a better idea to set token_num_uses=0
for infinite uses until the minute is up and the token expires.
Aptly named after the computer the approle's being made for.
vault auth enable approle
vault write auth/approle/role/zfs_computer-name secret_id_ttl=7d token_num_uses=2 token_ttl=60s token_max_ttl=60s secret_id_num_uses=0 policies=zfs/computer-name
vault read auth/approle/role/zfs_computer-name/role-id # Get the role_id
vault write -f auth/approle/role/zfs_computer-name/secret-id # Create a secret_id
Note down the returned role and secret IDs. They can be specified as role_id=xxx
and secret_id=xxx
in /etc/zfsUnlocker/zfsUnlocker.conf
or in your kernel arguments.
Once the script is installed and your VAULT_ADDR,and either a VAULT_TOKEN or role_id&secret_id are added to /etc/zfsUnlocker/zfsUnlocker.conf` the vault hook has a test function we can check with:
$ sudo /etc/zfsUnlocker/modules.d/20_vault/hook test computer-name/root
Entering test mode.
Testing config approle...
Got a vault token successfully...
Attempting unlock of: computer-name/root
Passphrase found for computer-name/root
cannot open 'computer-name/root': dataset does not exist
My machine here doesn't actually have a zpool named computer-name
with a dataset named root
in it. But the example read was a success and wanted to unlock something!
The script will also consider kernel arguments which can be activated and also tested with a reboot - Otherwise the Vault module of this script can be temporarily modified to include additional arguments on the fly.
Testing 'proposed' kernel args on the fly may be added in a later commit if I find myself testing them frequently enough without wanting to hack the script apart for each try or reboot for each test.