ZFS Remote Entschlüsselung via SSH
ZFS Remote Entschlüsselung via SSH
Ein zfs Dataset wird beim Start des Hosts automatisch entschlüsselt. Der Key wird dafür via SSH von einem anderen Host angefragt. Falls das System in einem fremden Netzwerk gebootet wird, kann das Dataset nicht entschlüsselt werden.
Vorbereitungen am empfangenden Host
Der Host, welche das verschlüsselte Saveset enthalten soll, muss zuerst ein SSH Schlüsselpaar erzeugen, welches danach für die Anfrage am bereitstellenden Host genutzt wird. Dafür nutze ich ed25519 als Keytype, da dies sicherer ist als RSA und unsere Gegenstelle sowieso ein modernes Debian / Proxmox sein wird.
ssh-keygen -t ed25519 -C "fetch-zfs-key@pve01-test" -f /etc/zfs/fetch-zfs-key_id
chmod 0600 /etc/zfs/fetch-zfs-key_id
Vorbereitungen am bereitstellenden Host
Auf dem Host, welcher den Key bereitstellen soll, muss der Key erzeugt werden und bei einem Aufruf eines bestimmten Kommandos ausgegeben werden.
Erzeugen des Keys
Den Key lege ich hier in diesem Beispiel unter /etc/zfs/remote-keys/<hostname>-<pool>.key
ab.
mkdir -p /etc/zfs/remote-keys
openssl rand -hex 32 | tr -d '\n' > /etc/zfs/remote-keys/pve01-test-encrypted.key
Ausgabekommando des Keys
Dieses Skript wird aufgerufen, wenn sich ein bestimmter SSH Key von einem bestimmten Host verbindet und stellt danach den Key bereit. Hierfür wird ein Bash-Skript unter /usr/local/sbin/provide-zfs-key
mit folgendem Inhalt erzeugt:
#!/bin/bash
cat /etc/zfs/remote-keys/$1.key
Diese Datei muss noch für den Owner ausführbar gemacht werden.
chmod 0700 /usr/local/sbin/provide-zfs-key
Um zu testen, ob dies funktioniert, kann bereits als root der Befehl provide-zfs-key pve01-encrypted
aufgerufen werden.
Anpassen der authorized_keys
Damit der Key an den anfragenden Host ausgegeben wird, muss folgende Zeile der Datei /root/.ssh/authorized_keys
hinzugefügt werden. Tauscht unbedingt den Platzhalter <pubkey> mit dem vorhin erzeugtem SSH Public-Key aus und passt die from-Adresse an. ;)
from="10.0.0.41",command="/usr/local/sbin/provide-zfs-key pve01-test-encrypted",no-port-forwarding,no-X11-forwarding,no-agent-forwarding <pubkey>
Erzeugen des verschlüsselten Datasets
Für das Erzeugen, Entsperren und Sperren des Datasets verwenden wir folgendes Hilfs-Skript welches manuell oder über eine systemd Unit beim Start des Hosts aufgerufen wird. Das Skript entsprechend euren Daten anpassen.
Ich habe es unter /usr/local/sbin/encrypted-zfs
abgelegt.
#!/bin/bash
set -euo pipefail
ZFS_DATASET="rpool/encrypted"
SSH_REMOTE_HOST="10.0.0.6"
SSH_REMOTE_USER="root"
SSH_KEYFILE="/etc/zfs/fetch-zfs-key_id"
TMPDIR="/tmp/zfs-ramdisk"
KEYFILE="rpool-encrypted.key"
create_tmp_dir() {
mkdir -p /tmp/zfs-ramdisk
echo "Create and mount temporary directory: $TMPDIR"
mount -t tmpfs -o size=1M tmpfs "$TMPDIR"
}
remove_tmp_dir() {
if [[ -n "$TMPDIR" && -d "$TMPDIR" ]]; then
echo "Shred temporary keyfile"
shred -u "$TMPDIR/$KEYFILE"
echo "Unmount the temporary directory"
umount "$TMPDIR"
rmdir "$TMPDIR"
fi
}
fetch_key_from_remote() {
create_tmp_dir
echo "Fetch the key from remote and store it in temporary file."
ssh -i "$SSH_KEYFILE" "$SSH_REMOTE_USER@$SSH_REMOTE_HOST" > "$TMPDIR/$KEYFILE"
}
create_zfs_dataset() {
echo "Check if a dataset named $ZFS_DATASET exists."
if ! zfs list -H -o name "$ZFS_DATASET" &>/dev/null; then
fetch_key_from_remote
zfs create \
-o encryption=on \
-o keyformat=hex \
-o mountpoint=none \
-o keylocation="file://$TMPDIR/$KEYFILE" \
"$ZFS_DATASET"
echo "Dataset $ZFS_DATASET created successfully."
remove_tmp_dir
else
echo "A dataset named $ZFS_DATASET already exists, exiting."
exit 1
fi
}
load_zfs_key() {
echo "Check if the key is already loaded..."
if ! zfs get -H -o value keystatus "$ZFS_DATASET" | grep -v "unavailable"; then
fetch_key_from_remote
echo "Load ZFS encryption key for $ZFS_DATASET"
zfs load-key -L "file://$TMPDIR/$KEYFILE" "$ZFS_DATASET"
remove_tmp_dir
else
echo "Key already loaded for $ZFS_DATASET."
fi
}
unload_zfs_key() {
echo "Attempting to unload ZFS encryption key for $ZFS_DATASET"
zfs unload-key "$ZFS_DATASET"
}
# Command dispatcher
case "${1:-}" in
create)
create_zfs_dataset
;;
load)
load_zfs_key
;;
unload)
unload_zfs_key
;;
*)
echo "Usage: $0 {create|load|unload}"
exit 1
;;
esac
Das Skript muss auch wieder für den Inhaber aufrufbar gemacht werden und danach kann man schon das Dataset erzeugen.
chmod 0700 /usr/local/sbin/encrypted-zfs
encrypted-zfs create
Erstellen einer systemd-Unit
Damit beim Boot des Systems automatisch das Dataset entschlüsselt wird, erstellen wir eine systemd-Unit unter /etc/systemd/system/zfs-loadkey.service
als oneshot-Service.
[Unit]
Description=ZFS Key Loader
DefaultDependencies=no
Before=pve-guests.service
Wants=network-online.target
After=network-online.target zfs-import.target
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/encrypted-zfs load
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
Danach enablen wir den Service mittels systemctl enable zfs-loadkey.service
und starten das System neu. Im journallog sieht man nun, dass das Saveset entschlüsselt wird:
root@pve01-test:~# journalctl -b0 -u zfs-loadkey
May 17 14:27:12 pve01-test systemd[1]: Starting zfs-loadkey.service - ZFS Key Loader...
May 17 14:27:12 pve01-test encrypted-zfs-pool[880]: Check if the key is already loaded...
May 17 14:27:13 pve01-test encrypted-zfs-pool[880]: Create and mount temporary directory: /tmp/zfs-ramdisk
May 17 14:27:13 pve01-test encrypted-zfs-pool[880]: Fetch the key from remote and store it in temporary file.
May 17 14:27:13 pve01-test encrypted-zfs-pool[896]: Pseudo-terminal will not be allocated because stdin is not a terminal.
May 17 14:27:13 pve01-test encrypted-zfs-pool[880]: Load ZFS encryption key for rpool/encrypted
May 17 14:27:13 pve01-test encrypted-zfs-pool[880]: Shred temporary keyfile
May 17 14:27:13 pve01-test encrypted-zfs-pool[880]: Unmount the temporary directory
May 17 14:27:13 pve01-test systemd[1]: Finished zfs-loadkey.service - ZFS Key Loader.
Sollte der Host nun nicht via SSH den Key holen können, so schlägt auch die Unit fehl und das Dataset bleibt verschlüsselt:
root@pve01-test:~# journalctl -b0 -u zfs-loadkey
May 17 14:34:31 pve01-test systemd[1]: Starting zfs-loadkey.service - ZFS Key Loader...
May 17 14:34:31 pve01-test encrypted-zfs-pool[881]: Check if the key is already loaded...
May 17 14:34:31 pve01-test encrypted-zfs-pool[881]: Create and mount temporary directory: /tmp/zfs-ramdisk
May 17 14:34:32 pve01-test encrypted-zfs-pool[881]: Fetch the key from remote and store it in temporary file.
May 17 14:34:32 pve01-test encrypted-zfs-pool[901]: Pseudo-terminal will not be allocated because stdin is not a terminal.
May 17 14:34:32 pve01-test encrypted-zfs-pool[901]: Permission denied, please try again.
May 17 14:34:32 pve01-test encrypted-zfs-pool[901]: Permission denied, please try again.
May 17 14:34:32 pve01-test encrypted-zfs-pool[901]: root@10.0.0.6: Permission denied (publickey,password).
May 17 14:34:32 pve01-test systemd[1]: zfs-loadkey.service: Main process exited, code=exited, status=255/EXCEPTION
May 17 14:34:32 pve01-test systemd[1]: zfs-loadkey.service: Failed with result 'exit-code'.
May 17 14:34:32 pve01-test systemd[1]: Failed to start zfs-loadkey.service - ZFS Key Loader.