I want my computer that connects to TrueNAS to be able to unlock the volumes in the same script that I use to connect to the mounted volumes.
Solved
See my own comment below.
Original
I tried using the following script, but the problem is that unlocking the volume doesn’t trigger the mechanisms that make it available as a network share.
So how do I run a process that triggers the unlock, and triggers it to be available as a network share?
Only Unlocks, Does NOT Make Available on Network
#!/bin/sh
set -e
set -u
# USAGE
# cat ./passphrase.txt | ssh '~/bin/zfs-unlock-all-encrypted-volumes'
echo "Enter the unlock phrase:"
read -r b_phrase
zfs get -t 'filesystem' encryption |
grep -v -E 'NAME|off' |
cut -d' ' -f1 |
while read -r b_pool; do
echo "Unlocking ${b_pool}..."
echo "${b_phrase}" | zfs load-key -L file:///dev/stdin "${b_pool}" || true
done
Those are vanilla zfs commands in your script. It’s not recommended to pursue that route, since you’ll also need to closely follow additional steps, such as mounting, mount location, etc.
The TrueNAS middleware API is different.
You need to use the CLI command (which is a terribly redundant name, by the way).
When you “unlock” a dataset using the GUI, it does the following (and possibly more):
Tests the key / passphrase in a dry-run
Unlocks the dataset(s) if the key / passphrase is correct
Mounts the dataset(s) under ALTROOT/<complete-dataset-name>)
The middleware / GUI is immediately aware of everything
This will read TRUENAS_BASE_URL and TRUENAS_API_KEY from ~/.config/truenas/env, and passphrases from ~/.config/truenas/zfs-passphrases.conf and successfully unlock all encrypted volumes, triggering all relevant middleware.
#!/bin/sh
set -e
set -u
if ! test -s ~/.config/truenas/env; then
mkdir -p ~/.config/truenas/
{
echo '# Example'
echo '# export TRUENAS_BASE_URL=https://truenas.local'
echo '# export TRUENAS_API_KEY=abc123 # from https://<truenas>/ui/apikeys'
} > ~/.config/truenas/env
fi
if ! grep -q -v -E '^\s*(#.*)?$' ~/.config/truenas/env; then
{
echo ""
echo "ERROR"
echo " Missing ~/.config/truenas/env"
echo ""
echo "SOLUTION"
echo " Create and save an API key from https://truenas.local/ui/apikeys"
echo ""
} >&2
exit 1
fi
# shellcheck disable=SC1090
. ~/.config/truenas/env
if test -z "${TRUENAS_BASE_URL:-}" || test -z "${TRUENAS_API_KEY:-}"; then
{
echo ""
echo "ERROR"
echo " Missing config from ~/.config/truenas/env"
echo ""
echo "SOLUTION"
echo " Set the config in this format:"
echo " export TRUENAS_BASE_URL=https://truenas.local # no trailing slash"
echo " export TRUENAS_API_KEY=abc123"
echo ""
} >&2
exit 1
fi
if ! test -s ~/.config/truenas/zfs-passphrases.conf; then
{
echo ""
echo "ERROR"
echo " Missing ~/.config/truenas/zfs-passphrases.conf"
echo ""
echo "SOLUTION"
echo " Set the passphrases in this format:"
echo " tank1/Data:foo bar baz"
echo " tankN/VolumeName:pass phrase goes here"
echo ""
} >&2
exit 1
fi
fn_list() { (
b_dataset_url="${TRUENAS_BASE_URL}/api/v2.0/pool/dataset"
echo " GET ${b_dataset_url} (listing dataset ids)..." >&2
curl --fail-with-body -sS -k "${b_dataset_url}" \
-H "Authorization: Bearer ${TRUENAS_API_KEY}" |
jq -r '.[] | select(.encrypted == true) | .id'
); }
fn_unlock() { (
b_dataset_id="${1}"
b_dataset_phrase="${2}"
b_unlock_url="${TRUENAS_BASE_URL}/api/v2.0/pool/dataset/unlock"
printf " POST %s (unlocking %s)..." "${b_unlock_url}" "${b_dataset_id}" >&2
curl --fail-with-body -sS -k "${b_unlock_url}" \
-H "Authorization: Bearer ${TRUENAS_API_KEY}" \
-H "Content-Type: application/json" \
-d '{ "id": "'"${b_dataset_id}"'"
, "unlock_options": {
"recursive": true,
"datasets": [
{ "name": "'"${b_dataset_id}"'"
, "passphrase": "'"${b_dataset_phrase}"'"}
]
}
}'
echo " unlocked" >&2
); }
main() { (
b_truenas_zfs_phrases="$(grep -v -E '^\s*(#.*)?$' ~/.config/truenas/zfs-passphrases.conf)"
echo "Unlocking TrueNAS..." >&2
fn_list | while read -r b_dataset_id; do
b_dataset_phrase="$(
echo "${b_truenas_zfs_phrases}" |
grep -F "${b_dataset_id}:" |
cut -d':' -f2
)"
if test -z "${b_dataset_phrase}"; then
echo " SKIP '${b_dataset_id}': no passphrase" >&2
continue
fi
fn_unlock "${b_dataset_id}" "${b_dataset_phrase}"
done
echo "Done"
); }
main