From 92acd8b535416901884f5d0669399254afb2f55c Mon Sep 17 00:00:00 2001 From: christophhagemann <123030593+christophhagemann@users.noreply.github.com> Date: Mon, 30 Jan 2023 12:34:42 +0100 Subject: [PATCH 01/10] Add encryption option homedir; preliminaries --- .../Debian/Debian Bullseye Root on ZFS.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/Getting Started/Debian/Debian Bullseye Root on ZFS.rst b/docs/Getting Started/Debian/Debian Bullseye Root on ZFS.rst index 4fb7e3603..600396a94 100644 --- a/docs/Getting Started/Debian/Debian Bullseye Root on ZFS.rst +++ b/docs/Getting Started/Debian/Debian Bullseye Root on ZFS.rst @@ -71,9 +71,9 @@ Contributing Encryption ~~~~~~~~~~ -This guide supports three different encryption options: unencrypted, ZFS -native encryption, and LUKS. With any option, all ZFS features are fully -available. +This guide supports four different encryption options: unencrypted, ZFS +native encryption, LUKS and per-user encrypted homedirs. With any option, +all ZFS features are fully available. Unencrypted does not encrypt anything, of course. With no encryption happening, this option naturally has the best performance. @@ -303,7 +303,7 @@ Step 2: Disk Formatting Choose one of the following options: - - Unencrypted:: + - Unencrypted (choose this if you want per-user encyprtion later on):: zpool create \ -o ashift=12 \ From 3a61abcd8fff3e801857699eb4224a3f89b756b8 Mon Sep 17 00:00:00 2001 From: christophhagemann <123030593+christophhagemann@users.noreply.github.com> Date: Mon, 30 Jan 2023 12:42:14 +0100 Subject: [PATCH 02/10] creation of homedir --- .../Debian/Debian Bullseye Root on ZFS.rst | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/docs/Getting Started/Debian/Debian Bullseye Root on ZFS.rst b/docs/Getting Started/Debian/Debian Bullseye Root on ZFS.rst index 600396a94..00b9fdf3c 100644 --- a/docs/Getting Started/Debian/Debian Bullseye Root on ZFS.rst +++ b/docs/Getting Started/Debian/Debian Bullseye Root on ZFS.rst @@ -951,10 +951,28 @@ Step 6: First Boot Replace ``YOUR_USERNAME`` with your desired username:: username=YOUR_USERNAME - - zfs create rpool/home/$username - adduser $username - + + Choose one of the following: + + - Unencrypted homedir or whole-disk encryption + + :: + + zfs create rpool/home/$username + adduser $username + + - Encrypted homdir per user, automatic decryption on login + + :: + + zfs create rpool/home/$username -o encryption=on -o keyformat=passphrase -o keylocation=prompt -o canmount=noauto + zfs mount rpool/home/$username + adduser $username + + **Note**: Use the same strong password for ZFS encryption and user password. Please note: After a breach of your password changing the ZFS password does not restore protection. + + In either case, continue:: + cp -a /etc/skel/. /home/$username chown -R $username:$username /home/$username usermod -a -G audio,cdrom,dip,floppy,netdev,plugdev,sudo,video $username From c7b524a21d76098990af250e72b113e71c5d4ca8 Mon Sep 17 00:00:00 2001 From: christophhagemann <123030593+christophhagemann@users.noreply.github.com> Date: Mon, 30 Jan 2023 12:54:18 +0100 Subject: [PATCH 03/10] PAM --- .../Debian/Debian Bullseye Root on ZFS.rst | 46 +++++++++++++++++-- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/docs/Getting Started/Debian/Debian Bullseye Root on ZFS.rst b/docs/Getting Started/Debian/Debian Bullseye Root on ZFS.rst index 00b9fdf3c..dcb059559 100644 --- a/docs/Getting Started/Debian/Debian Bullseye Root on ZFS.rst +++ b/docs/Getting Started/Debian/Debian Bullseye Root on ZFS.rst @@ -952,16 +952,16 @@ Step 6: First Boot username=YOUR_USERNAME - Choose one of the following: + Choose one of the following: - - Unencrypted homedir or whole-disk encryption + - Unencrypted homedir or whole-disk encryption :: zfs create rpool/home/$username adduser $username - - Encrypted homdir per user, automatic decryption on login + - Encrypted homdir per user, automatic decryption on login :: @@ -970,6 +970,46 @@ Step 6: First Boot adduser $username **Note**: Use the same strong password for ZFS encryption and user password. Please note: After a breach of your password changing the ZFS password does not restore protection. + + Tell PAM to unlock the dataset key on login:: + + printf "auth [success=1 default=ignore] pam_succeed_if.so service = sudo quiet\n\ + auth optional pam_exec.so expose_authtok /usr/local/sbin/unlock-key-zfs-homedir\n"\ + >> /etc/pam.d/common-auth + + The second line calls a new unlock-script, the first line disables it if we just sudo. + + Create the unlock-script:: + + touch /usr/local/sbin/unlock-key-zfs-homedir + chmod a+x /usr/local/sbin/unlock-key-zfs-homedir + vi /usr/local/sbin/unlock-key-zfs-homedir + + and put into it:: + + #!/bin/bash + + set -e + + # called from PAM common_auth to unlock + # we get the login password (must be the same as ZFS password) on stdin + + # exit if root + [ "$PAM_USER" == "root" ] && exit 0 + + # do nothing if no dataset exists + zfs list rpool/home/$PAM_USER || exit 0 + + # exit if our dataset is not encrypted + [ `zfs list rpool/home/$PAM_USER -o encryption -H` == off ] && exit 0 + + # exit if already mounted for some reason + findmnt "/home/$PAM_USER" && exit 0 + + # still here? unlock now + zfs load-key "rpool/home/$PAM_USER" < /dev/stdin + + In either case, continue:: From e88a41d728001b392910ed05138d156278c68310 Mon Sep 17 00:00:00 2001 From: christophhagemann <123030593+christophhagemann@users.noreply.github.com> Date: Mon, 30 Jan 2023 13:12:43 +0100 Subject: [PATCH 04/10] systemd-part --- .../Debian/Debian Bullseye Root on ZFS.rst | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/docs/Getting Started/Debian/Debian Bullseye Root on ZFS.rst b/docs/Getting Started/Debian/Debian Bullseye Root on ZFS.rst index dcb059559..50bd884de 100644 --- a/docs/Getting Started/Debian/Debian Bullseye Root on ZFS.rst +++ b/docs/Getting Started/Debian/Debian Bullseye Root on ZFS.rst @@ -1008,6 +1008,77 @@ Step 6: First Boot # still here? unlock now zfs load-key "rpool/home/$PAM_USER" < /dev/stdin + + Now create a systemd service to mount our unlocked dataset:: + + mkdir -p /etc/systemd/system/user@.service.d/ + printf "[Unit]\n\ + Requires=user-zfs-mount@%%i.service\n"\ + > local-mount-zfs.conf + + Write the service definition:: + + vi /etc/systemd/system/user-zfs-mount@.service + + with this content:: + + # local service to mount encrypted homdirs + + [Unit] + Description=User ZFS mount /home/ for UID %i + After=dbus.service + StopWhenUnneeded=yes + IgnoreOnIsolate=yes + + [Service] + ExecStart=/usr/local/sbin/mount-zfs-homedir start %i + ExecStop=/usr/local/sbin/mount-zfs-homedir stop %i + Type=oneshot + RemainAfterExit=yes + Slice=user-%i.slice + + Finally, create the helper script:: + + touch /usr/local/sbin/mount-zfs-homedir + chmod a+x /usr/local/sbin/mount-zfs-homedir + vi /usr/local/sbin/mount-zfs-homedir + + And put into it:: + + #!/bin/bash + + set -e + + # called from systemd via /etc/systemd/system/user-zfs-mount@.service + # to mount/unmount + # we get: $1 - start/stop, $2 - UID + + # get username from UID passed to us by systemd + USERNAME=$(id -nu $2) + + # gracefully exit if no such dataset exists + zfs list rpool/home/$USERNAME || exit 0 + + case $1 in + start) + # exit if already mounted + findmnt "/home/$USERNAME" && exit 0 + + # Mount homedir of user we are logging in as + zfs mount "rpool/home/$USERNAME" + ;; + + stop) + # if the dataset of the user logging out is not encrypted, leave it alone + [ `zfs list rpool/home/$PAM_USER -o encryption -H` == off ] && exit 0 + + zfs umount "rpool/home/$USERNAME" + zfs unload-key "rpool/home/$USERNAME" + ;; + + esac + + From e13ef00b5444072583630c6e1857b77cd343daa2 Mon Sep 17 00:00:00 2001 From: christophhagemann <123030593+christophhagemann@users.noreply.github.com> Date: Mon, 30 Jan 2023 13:23:28 +0100 Subject: [PATCH 05/10] Cleanup --- docs/Getting Started/Debian/Debian Bullseye Root on ZFS.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/Getting Started/Debian/Debian Bullseye Root on ZFS.rst b/docs/Getting Started/Debian/Debian Bullseye Root on ZFS.rst index 50bd884de..e402f8749 100644 --- a/docs/Getting Started/Debian/Debian Bullseye Root on ZFS.rst +++ b/docs/Getting Started/Debian/Debian Bullseye Root on ZFS.rst @@ -93,6 +93,11 @@ entered at the console. Performance is good, but LUKS sits underneath ZFS, so if multiple disks (mirror or raidz topologies) are used, the data has to be encrypted once per disk. +Per-user encrypted homdirs encrypts only the datasets for each user. The system +boots without the need to enter a passphrase. No user-data is accessible until +the user logs in. The homdirs-datasets are automatically unlocked on login and +locked on last logout. Mixing encrypted and non-encrypted homdirs is supported. + Step 1: Prepare The Install Environment --------------------------------------- From 34a1f1023225f691f16b3b5e43ce749986d93edc Mon Sep 17 00:00:00 2001 From: christophhagemann <123030593+christophhagemann@users.noreply.github.com> Date: Mon, 30 Jan 2023 16:29:21 +0100 Subject: [PATCH 06/10] Added an option to remove an error message by zfs-mount-generator --- docs/Getting Started/Debian/Debian Bullseye Root on ZFS.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Getting Started/Debian/Debian Bullseye Root on ZFS.rst b/docs/Getting Started/Debian/Debian Bullseye Root on ZFS.rst index e402f8749..46926a53f 100644 --- a/docs/Getting Started/Debian/Debian Bullseye Root on ZFS.rst +++ b/docs/Getting Started/Debian/Debian Bullseye Root on ZFS.rst @@ -970,7 +970,7 @@ Step 6: First Boot :: - zfs create rpool/home/$username -o encryption=on -o keyformat=passphrase -o keylocation=prompt -o canmount=noauto + zfs create rpool/home/$username -o encryption=on -o keyformat=passphrase -o keylocation=prompt -o canmount=noauto -o org.openzfs.systemd:ignore=on zfs mount rpool/home/$username adduser $username From 6e285022e3ccfba5d54315d2dbaad19217c1576c Mon Sep 17 00:00:00 2001 From: christophhagemann <123030593+christophhagemann@users.noreply.github.com> Date: Tue, 31 Jan 2023 09:15:14 +0100 Subject: [PATCH 07/10] Cosmetic changes As requested by @rlaager - Thanks for the suggestions! --- .../Debian/Debian Bullseye Root on ZFS.rst | 46 +++++++++++-------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/docs/Getting Started/Debian/Debian Bullseye Root on ZFS.rst b/docs/Getting Started/Debian/Debian Bullseye Root on ZFS.rst index 46926a53f..472b69a07 100644 --- a/docs/Getting Started/Debian/Debian Bullseye Root on ZFS.rst +++ b/docs/Getting Started/Debian/Debian Bullseye Root on ZFS.rst @@ -72,13 +72,13 @@ Encryption ~~~~~~~~~~ This guide supports four different encryption options: unencrypted, ZFS -native encryption, LUKS and per-user encrypted homedirs. With any option, +native encryption, LUKS, and per-user encrypted home directories. With any option, all ZFS features are fully available. -Unencrypted does not encrypt anything, of course. With no encryption +**Unencrypted** does not encrypt anything, of course. With no encryption happening, this option naturally has the best performance. -ZFS native encryption encrypts the data and most metadata in the root +**ZFS native encryption** encrypts the data and most metadata in the root pool. It does not encrypt dataset or snapshot names or properties. The boot pool is not encrypted at all, but it only contains the bootloader, kernel, and initrd. (Unless you put a password in ``/etc/fstab``, the @@ -87,16 +87,16 @@ without the passphrase being entered at the console. Performance is good. As the encryption happens in ZFS, even if multiple disks (mirror or raidz topologies) are used, the data only has to be encrypted once. -LUKS encrypts almost everything. The only unencrypted data is the bootloader, +**LUKS** encrypts almost everything. The only unencrypted data is the bootloader, kernel, and initrd. The system cannot boot without the passphrase being entered at the console. Performance is good, but LUKS sits underneath ZFS, so if multiple disks (mirror or raidz topologies) are used, the data has to be encrypted once per disk. -Per-user encrypted homdirs encrypts only the datasets for each user. The system -boots without the need to enter a passphrase. No user-data is accessible until -the user logs in. The homdirs-datasets are automatically unlocked on login and -locked on last logout. Mixing encrypted and non-encrypted homdirs is supported. +**Per-user encrypted home directory** encrypts only the datasets for each user. The system +boots without the need to enter a passphrase. No userdata is accessible until +the user logs in. The home directory dataset is automatically unlocked on login and +locked on last logout. Mixing encrypted and non-encrypted home directories is supported. Step 1: Prepare The Install Environment --------------------------------------- @@ -308,7 +308,7 @@ Step 2: Disk Formatting Choose one of the following options: - - Unencrypted (choose this if you want per-user encyprtion later on):: + - Unencrypted or Per-User Encrypted Home Directories:: zpool create \ -o ashift=12 \ @@ -959,14 +959,14 @@ Step 6: First Boot Choose one of the following: - - Unencrypted homedir or whole-disk encryption + - Unencrypted, ZFS native encryption, or LUKS: :: zfs create rpool/home/$username adduser $username - - Encrypted homdir per user, automatic decryption on login + - Per-User Encrypted Home Directories: :: @@ -974,13 +974,16 @@ Step 6: First Boot zfs mount rpool/home/$username adduser $username - **Note**: Use the same strong password for ZFS encryption and user password. Please note: After a breach of your password changing the ZFS password does not restore protection. + **Note**: Use the same strong password for ZFS encryption and user password. Please note: After a breach of your password, changing the ZFS password does not restore protection. Tell PAM to unlock the dataset key on login:: - printf "auth [success=1 default=ignore] pam_succeed_if.so service = sudo quiet\n\ - auth optional pam_exec.so expose_authtok /usr/local/sbin/unlock-key-zfs-homedir\n"\ - >> /etc/pam.d/common-auth + vi /etc/pam.d/common-auth + + Append after the last line:: + + auth [success=1 default=ignore] pam_succeed_if.so service = sudo quiet + auth optional pam_exec.so expose_authtok /usr/local/sbin/unlock-key-zfs-homedir The second line calls a new unlock-script, the first line disables it if we just sudo. @@ -1017,10 +1020,13 @@ Step 6: First Boot Now create a systemd service to mount our unlocked dataset:: mkdir -p /etc/systemd/system/user@.service.d/ - printf "[Unit]\n\ - Requires=user-zfs-mount@%%i.service\n"\ - > local-mount-zfs.conf - + vi /etc/systemd/system/user@.service.d/local-mount-zfs.conf + + In the file, request our new service:: + + [Unit] + Requires=user-zfs-mount@%i.service + Write the service definition:: vi /etc/systemd/system/user-zfs-mount@.service @@ -1069,7 +1075,7 @@ Step 6: First Boot # exit if already mounted findmnt "/home/$USERNAME" && exit 0 - # Mount homedir of user we are logging in as + # Mount home directory of user we are logging in as zfs mount "rpool/home/$USERNAME" ;; From 2e9b1e890d58afc6da930299945fdcb5ac758894 Mon Sep 17 00:00:00 2001 From: christophhagemann <123030593+christophhagemann@users.noreply.github.com> Date: Tue, 31 Jan 2023 09:50:39 +0100 Subject: [PATCH 08/10] Wrong copy&paste corrected --- docs/Getting Started/Debian/Debian Bullseye Root on ZFS.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Getting Started/Debian/Debian Bullseye Root on ZFS.rst b/docs/Getting Started/Debian/Debian Bullseye Root on ZFS.rst index 472b69a07..4f76b59cb 100644 --- a/docs/Getting Started/Debian/Debian Bullseye Root on ZFS.rst +++ b/docs/Getting Started/Debian/Debian Bullseye Root on ZFS.rst @@ -1081,7 +1081,7 @@ Step 6: First Boot stop) # if the dataset of the user logging out is not encrypted, leave it alone - [ `zfs list rpool/home/$PAM_USER -o encryption -H` == off ] && exit 0 + [ `zfs list rpool/home/$USERNAME -o encryption -H` == off ] && exit 0 zfs umount "rpool/home/$USERNAME" zfs unload-key "rpool/home/$USERNAME" From e9b2900c6373af856ef7d6b3f774bc829385940f Mon Sep 17 00:00:00 2001 From: christophhagemann <123030593+christophhagemann@users.noreply.github.com> Date: Tue, 31 Jan 2023 10:32:17 +0100 Subject: [PATCH 09/10] shellcheck and update shellscripts Thanks for the hints! --- .../Debian/Debian Bullseye Root on ZFS.rst | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/Getting Started/Debian/Debian Bullseye Root on ZFS.rst b/docs/Getting Started/Debian/Debian Bullseye Root on ZFS.rst index 4f76b59cb..acb11afd8 100644 --- a/docs/Getting Started/Debian/Debian Bullseye Root on ZFS.rst +++ b/docs/Getting Started/Debian/Debian Bullseye Root on ZFS.rst @@ -989,13 +989,11 @@ Step 6: First Boot Create the unlock-script:: - touch /usr/local/sbin/unlock-key-zfs-homedir - chmod a+x /usr/local/sbin/unlock-key-zfs-homedir vi /usr/local/sbin/unlock-key-zfs-homedir and put into it:: - #!/bin/bash + #!/bin/sh set -e @@ -1003,19 +1001,19 @@ Step 6: First Boot # we get the login password (must be the same as ZFS password) on stdin # exit if root - [ "$PAM_USER" == "root" ] && exit 0 + [ "$PAM_USER" = "root" ] && exit 0 # do nothing if no dataset exists - zfs list rpool/home/$PAM_USER || exit 0 + zfs list "rpool/home/$PAM_USER" || exit 0 # exit if our dataset is not encrypted - [ `zfs list rpool/home/$PAM_USER -o encryption -H` == off ] && exit 0 + [ "$(zfs list "rpool/home/$PAM_USER" -o encryption -H)" = off ] && exit 0 # exit if already mounted for some reason findmnt "/home/$PAM_USER" && exit 0 - # still here? unlock now - zfs load-key "rpool/home/$PAM_USER" < /dev/stdin + # still here? unlock now, zfs reads the passwd from STDIN + zfs load-key "rpool/home/$PAM_USER" Now create a systemd service to mount our unlocked dataset:: @@ -1050,13 +1048,11 @@ Step 6: First Boot Finally, create the helper script:: - touch /usr/local/sbin/mount-zfs-homedir - chmod a+x /usr/local/sbin/mount-zfs-homedir vi /usr/local/sbin/mount-zfs-homedir And put into it:: - #!/bin/bash + #!/bin/sh set -e @@ -1065,10 +1061,10 @@ Step 6: First Boot # we get: $1 - start/stop, $2 - UID # get username from UID passed to us by systemd - USERNAME=$(id -nu $2) + USERNAME=$(id -nu "$2") # gracefully exit if no such dataset exists - zfs list rpool/home/$USERNAME || exit 0 + zfs list "rpool/home/$USERNAME" || exit 0 case $1 in start) @@ -1081,7 +1077,7 @@ Step 6: First Boot stop) # if the dataset of the user logging out is not encrypted, leave it alone - [ `zfs list rpool/home/$USERNAME -o encryption -H` == off ] && exit 0 + [ "$(zfs list "rpool/home/$USERNAME" -o encryption -H)" = off ] && exit 0 zfs umount "rpool/home/$USERNAME" zfs unload-key "rpool/home/$USERNAME" @@ -1089,7 +1085,11 @@ Step 6: First Boot esac - + Don't forget to make the new scripts executable:: + + chmod a+x /usr/local/sbin/unlock-key-zfs-homedir + chmod a+x /usr/local/sbin/mount-zfs-homedir + From bb9cc0738ca563f7424f385f4fb341b9d9b17355 Mon Sep 17 00:00:00 2001 From: christophhagemann <123030593+christophhagemann@users.noreply.github.com> Date: Sat, 11 Feb 2023 12:34:16 +0100 Subject: [PATCH 10/10] Updated scripts in per-user-encrypted home directory section - Prevent two race conditions: between unlock (PAM) and mount (systemd) and between mount and the remaining login process - Minor cosmetic changes - Added the option to take a snapshot upon logout --- .../Debian/Debian Bullseye Root on ZFS.rst | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/docs/Getting Started/Debian/Debian Bullseye Root on ZFS.rst b/docs/Getting Started/Debian/Debian Bullseye Root on ZFS.rst index acb11afd8..bbf7c1e04 100644 --- a/docs/Getting Started/Debian/Debian Bullseye Root on ZFS.rst +++ b/docs/Getting Started/Debian/Debian Bullseye Root on ZFS.rst @@ -1020,10 +1020,11 @@ Step 6: First Boot mkdir -p /etc/systemd/system/user@.service.d/ vi /etc/systemd/system/user@.service.d/local-mount-zfs.conf - In the file, request our new service:: + In the file, request our new service (Requires) and instruct systemd to wait for completion before continuing (After):: [Unit] Requires=user-zfs-mount@%i.service + After=user-zfs-mount@%i.service Write the service definition:: @@ -1070,17 +1071,25 @@ Step 6: First Boot start) # exit if already mounted findmnt "/home/$USERNAME" && exit 0 + + # prevent race condition with PAM unlock: If key is not yet available, wait for it + [ "$(zfs get keystatus -H -o value rpool/home/"$USERNAME")" != available ] && sleep 1 # Mount home directory of user we are logging in as zfs mount "rpool/home/$USERNAME" + ;; stop) # if the dataset of the user logging out is not encrypted, leave it alone [ "$(zfs list "rpool/home/$USERNAME" -o encryption -H)" = off ] && exit 0 - - zfs umount "rpool/home/$USERNAME" - zfs unload-key "rpool/home/$USERNAME" + + # unmount and unload key (-u) + zfs umount -u "rpool/home/$USERNAME" + + # optional: uncomment the next two lines to take a snapshot upon logout + # zfs destroy "rpool/home/$USERNAME@last-logout" || true + # zfs snapshot "rpool/home/$USERNAME@last-logout" ;; esac