Root on ZFS with native encryption: Difference between revisions

From Alpine Linux
(fix link)
 
(124 intermediate revisions by 7 users not shown)
Line 1: Line 1:
= Introduction =
This is a guide for installing Alpine Linux with its root partition on an encrypted ZFS volume, using ZFS's own encryption capabilities. The system will be encrypted when powered off and will need to be unlocked by typing a passphrase at boot. To be able to boot the system, the `/boot` partition remains unencrypted.


This documentation describes how to set up Alpine Linux using ZFS with a pool that uses ZFS' native encryption capabilities, which have been recently introduced in ZFS on Linux (ZoL) 0.8.0.
= OpenZFS Guide =


Note that you must install the <code>/boot/</code> directory on an unencrypted partition (either an unencrypted ZFS pool or any other FS of your choosing, if it's compatible with your bootloader) to boot correctly.
A guide on OpenZFS website supports native encryption, UEFI boot and legacy boot, and multi-disk.  See [https://openzfs.github.io/openzfs-docs/Getting%20Started/Alpine%20Linux/Root%20on%20ZFS.html here].


== Requirements ==
= Downloading Alpine =


You'll need a medium to put a live image on. You can use any live medium that supports ZoL >=0.8.x, but as of writing this it's easiest to use [https://ubuntu.com/download/desktop Ubuntu 19.10], which comes with ZFS pre-installed.
Download the '''extended''' release from https://www.alpinelinux.org/downloads/ as only it contains the zfs kernel modules at the time of writing (2022-02-12).


== Hard Disk Device Name ==
Write it to a USB device and boot from it.


The following documentation uses the <code>/dev/sda</code> device as installation destination. If your environment uses a different device name for your hard disk, use the corresponding device names in the examples. It also uses <code>rpool</code> as name of the root pool, you can change this at will, but be sure to change it everywhere it's mentioned.
= Initial Setup =


= Setting up Alpine Linux Using ZFS with native encryption =
Run the following to start the installation procedure:


To install Alpine Linux in a ZFS pool with encryption enable, you cannot use the [[Installation|official installation]] procedure, so follow along this guide.
{{cmd|setup-alpine}}


== Creating the Partition Layout ==
Answer all the questions, and hit {{Key|Ctrl}}+{{Key|C}} when prompted for which disk you'd like to use.


Linux requires an unencrypted <code>/boot/</code> partition to boot. You can assign the remaining space for the encrypted ZFS pool.
== Optional: SSH access ==


* Start the <code>fdisk</code> utility to set up partitions:
This section is optional and it assumes internet connectivity. You may enable sshd so you can ssh into the box and copy and paste the rest of the commands from these instructions into a terminal window.


# fdisk /dev/sda
Edit {{path|/etc/ssh/sshd_config}} and search for `Permit`. Change the value after `PermitRootLogin` to read `yes`


:* Create the <code>/boot/</code> partition:
save and exit to shell. Run {{codeline|service sshd restart}}
::* Enter <code>n</code> &rarr; <code>p</code> &rarr; <code>1</code> &rarr; <code>1</code> &rarr; <code>100m</code> to create a new 100 MB primary partition.


:* Set the <code>/boot/</code> partition active:
Now you can ssh in as root. Do not forget to go back and comment this line out when you're done since it will be enabled on the resulting machine. You will be reminded again at the end of this doc.
::* Enter <code>a</code> &rarr; <code>1</code>.


:* Create the ZFS partition:
= Add required packages  =
::* Enter <code>n</code> &rarr; <code>p</code> &rarr; <code>2</code> to start creating the next partition. Press <code>Enter</code> to select the default start cylinder. Enter the size of partition. For example, <code>512m</code> for 512 MB or <code>5g</code> for 5 GB. Alternatively press <code>Enter</code> to set the maximum available size.


:* To verify the settings, press <code>p</code>. The output shows, for example:
{{cmd|apk add {{pkg|zfs}} {{pkg|sfdisk}} {{pkg|e2fsprogs}} {{pkg|syslinux}}}}
<pre>
Device    Boot  Start      End  Sectors  Size Id Type
/dev/sda1  *      2048  206847  204800  100M 83 Linux
/dev/sda2      206848 41943039 41736192 19.9G 83 Linux
</pre>
* Press <code>w</code> to save the changes.


== Setting up the root pool ==
= Partition setup =


You can create your rootpool with the following command:
We're assuming that {{path|/dev/sda}} is the target storage device here and in the rest of the document, but the name of the storage device you wish to install to may be different. To see a list of storage devices and determine the correct one, type {{codeline|sfdisk -l}}.


# zpool create -o ashift=12 \
{{cmd|echo -e "/dev/sda1: start{{=}}1M,size{{=}}100M,bootable\n/dev/sda2: start{{=}}101M" &#124; sfdisk --quiet --label dos /dev/sda}}
      -O acltype=posixacl -O canmount=off -O compression=lz4 \
      -O dnodesize=auto -O normalization=formD -O relatime=on -O xattr=sa \
      -O encryption=aes-256-gcm -O keylocation=prompt -O keyformat=passphrase \
      -O mountpoint=/ -R /mnt \
      rpool /dev/sda2


You will have to enter your passphrase at this point. Choose wisely, as your passphrase is most likely [https://gitlab.com/cryptsetup/cryptsetup/wikis/FrequentlyAskedQuestions#5-security-aspects the weakest link in this setup].
== Create device nodes ==
 
{{cmd|mdev -s}}
 
== Create the {{path|/boot}} filesystem ==


A few notes on the options supplied to zpool:
{{cmd|mkfs.ext4 /dev/sda1}}


* <code>ashift=12</code> is recommended here because many drives today have 4KiB (or larger) physical sectors, even though they present 512B logical sectors
= ZFS setup =
== Create the root zpool ==


* <code>acltype=posixacl</code> enables POSIX ACLs globally
{{cmd|modprobe zfs
zpool create -f -o ashift{{=}}12 \
-O acltype{{=}}posixacl -O canmount{{=}}off -O compression{{=}}lz4 \
-O dnodesize{{=}}auto -O normalization{{=}}formD -O relatime{{=}}on -O xattr{{=}}sa \
-O encryption{{=}}aes-256-gcm -O keylocation{{=}}prompt -O keyformat{{=}}passphrase \
-O mountpoint{{=}}/ -R /mnt \
rpool /dev/sda2}}


* <code>normalization=formD</code> eliminates some corner cases relating to UTF-8 filename normalization. It also enables <code>utf8only=on</code>, meaning that only files with valid UTF-8 filenames will be accepted.
You will have to enter your passphrase at this point. Choose wisely, as your passphrase is most likely [https://gitlab.com/cryptsetup/cryptsetup/wikis/FrequentlyAskedQuestions#5-security-aspects the weakest link in this setup].


* <code>xattr=sa</code> vastly improves the performance of extended attributes, but is Linux-only. If you care about using this pool on other OpenZFS implementation don't specify this option.
A few notes on the options supplied to zpool:
<ul>
<li>{{codeline|ashift{{=}}12}} is recommended here because many drives today have 4KiB (or larger) physical sectors, even though they present 512B logical sectors</li>
<li>{{codeline|acltype{{=}}posixacl}} enables POSIX ACLs globally</li>
<li>{{codeline|normalization{{=}}formD}} eliminates some corner cases relating to UTF-8 filename normalization. It also enables `utf8only=on`, meaning that only files with valid UTF-8 filenames will be accepted.</li>
<li>{{codeline|xattr{{=}}sa}} vastly improves the performance of extended attributes, but is Linux-only. If you care about using this pool on other OpenZFS implementation don't specify this option.</li></ul>


After completing this, confirm that the pool has been created:
After completing this, confirm that the pool has been created:


# zpool status
{{cmd|# zpool status}}


Should return something like:
Should return something like:
<pre>
  pool: rpool
state: ONLINE
  scan: none requested
config:
NAME        STATE    READ WRITE CKSUM
rpool      ONLINE      0    0    0
  sda2  ONLINE      0    0    0
errors: No known data errors
</pre>
=== Creating the required datasets ===
# zfs create -o mountpoint=none -o canmount=off rpool/ROOT
# zfs create -o mountpoint=legacy rpool/ROOT/alpine
# mount -t zfs rpool/ROOT/alpine /mnt/
=== Creating optional datasets (feel free to add your own) ===
# zfs create -o mountpoint=/home rpool/HOME
# zfs create -o mountpoint=/var/log rpool/LOG
== Creating the <code>/boot</code> filesystem ==
# mkfs.ext4 /dev/sda1
== Mounting the <code>/boot</code> filesystem ==
* Create the <code>/mnt/boot/</code> directory and mount the <code>/dev/sda1</code> partition in this directory:
# mkdir /mnt/boot/
# mount -t ext4 /dev/sda1 /mnt/boot/
== Installing Alpine Linux ==
Please follow [[Installing_Alpine_Linux_in_a_chroot|Installing Alpine Linux in a chroot]] to setup a base install of Alpine Linux.
After you've followed that guide, you still have to do some additional setup for ZFS:
* As of the time of writing this ZFS 0.8.x is only available in [[Edge]], so you'll have to enable it in <code>/etc/apk/repositories</code>. Check [https://pkgs.alpinelinux.org/packages?name=zfs pkgs.alpinelinux.org] to see the status of this.
* Install the ZoL and linux-vanilla package: <code>apk add linux-vanilla zfs</code>
* Enable ZFS' services:
# rc-update add zfs-import sysinit
# rc-update add zfs-mount sysinit
* Edit the <code>/etc/mkinitfs/mkinitfs.conf</code> file and append <code>zfs</code> module to the <code>features</code> parameter:
features="ata base ide scsi usb virtio ext4 lvm <u>zfs</u>"
Be mindful to also include other modules which may be required for your setup, such as the <code>nvme</code> module.
* Rebuild the initial RAM disk:
# mkinitfs $(ls /lib/modules/)
* Edit the <code>/etc/update-extlinux.conf</code> file, set the root ZFS dataset and append the following kernel options to the <code>default_kernel_opts</code> parameter:
root=rpool/ROOT/alpine
default_kernel_opts="... <u>rootfstype=zfs</u>"
* Update extlinux's config (if you're not using a different bootloader)
# update-extlinux
# exit
: Ignore the errors the <code>update-extlinux</code> utility displays.
* Write the MBR to the <code>/dev/sda</code> device:
# dd bs=440 count=1 conv=notrunc if=/mnt/usr/share/syslinux/mbr.bin of=/dev/sda
== Unmounting the filesystems ==
* Unmount <code>/mnt/boot/</code>:
# umount /mnt/boot/
* Unmount all zfs filesystems:
# zfs unmount -a
* Reboot the system:
# reboot
== Booting the system ==
Right now mkinitfs doesn't support ZFS asking for passwords during boot, so it'll throw you into a rescue shell for you to enter the password during boot. You have to do the following things after pressing enter:


# zfs load-key -a
      pool: rpool
# mount -t zfs rpool/ROOT/alpine /sysroot
    state: ONLINE
# exit
      scan: none requested
    config:
   
        NAME        STATE    READ WRITE CKSUM
        rpool       ONLINE      0    0    0
          sda2      ONLINE      0    0    0
   
    errors: No known data errors


And your system should continue booting! :)
== Create the required datasets and mount root ==


= Troubleshooting =
{{cmd|zfs create -o mountpoint{{=}}none -o canmount{{=}}off rpool/ROOT
zfs create -o mountpoint{{=}}legacy rpool/ROOT/alpine
mount -t zfs rpool/ROOT/alpine /mnt/}}


== General Procedure ==
== Mount the {{path|/boot}} filesystem ==


In case your system fails to boot, you can verify the settings and fix incorrect configurations:
{{cmd|mkdir /mnt/boot/
mount -t ext4 /dev/sda1 /mnt/boot/}}


* [[#Preparing_the_Installation_Environment|Preparing the Installation Environment]]
== Enable ZFS' services ==


* Load the ZFS kernel module:
{{cmd|rc-update add zfs-import sysinit
rc-update add zfs-mount sysinit}}


# modprobe zfs
= Install Alpine Linux =


* [[#Mounting_the_File_Systems|Mount the file systems]]
{{cmd|setup-disk /mnt
dd if{{=}}/usr/share/syslinux/mbr.bin of{{=}}/dev/sda # write mbr so we can boot}}


# zpool import -R /mnt rpool
= Reboot and enjoy! =
# mount -t ext4 /dev/sda1 /mnt/boot


* Verify that you run the steps described in the [[#Installing_Alpine_Linux|Installing Alpine Linux]] section correctly. Update the configuration if necessary.
😉


[[Category:Storage]]
'''NOTE:'''
[[Category:Security]]
If you went with the optional step, be sure to disable root login after you reboot.

Latest revision as of 17:18, 21 August 2023

This is a guide for installing Alpine Linux with its root partition on an encrypted ZFS volume, using ZFS's own encryption capabilities. The system will be encrypted when powered off and will need to be unlocked by typing a passphrase at boot. To be able to boot the system, the `/boot` partition remains unencrypted.

OpenZFS Guide

A guide on OpenZFS website supports native encryption, UEFI boot and legacy boot, and multi-disk. See here.

Downloading Alpine

Download the extended release from https://www.alpinelinux.org/downloads/ as only it contains the zfs kernel modules at the time of writing (2022-02-12).

Write it to a USB device and boot from it.

Initial Setup

Run the following to start the installation procedure:

setup-alpine

Answer all the questions, and hit Ctrl+C when prompted for which disk you'd like to use.

Optional: SSH access

This section is optional and it assumes internet connectivity. You may enable sshd so you can ssh into the box and copy and paste the rest of the commands from these instructions into a terminal window.

Edit /etc/ssh/sshd_config and search for `Permit`. Change the value after `PermitRootLogin` to read `yes`

save and exit to shell. Run service sshd restart

Now you can ssh in as root. Do not forget to go back and comment this line out when you're done since it will be enabled on the resulting machine. You will be reminded again at the end of this doc.

Add required packages

apk add zfs sfdisk e2fsprogs syslinux

Partition setup

We're assuming that /dev/sda is the target storage device here and in the rest of the document, but the name of the storage device you wish to install to may be different. To see a list of storage devices and determine the correct one, type sfdisk -l.

echo -e "/dev/sda1: start=1M,size=100M,bootable\n/dev/sda2: start=101M" | sfdisk --quiet --label dos /dev/sda

Create device nodes

mdev -s

Create the /boot filesystem

mkfs.ext4 /dev/sda1

ZFS setup

Create the root zpool

modprobe zfs zpool create -f -o ashift=12 \ -O acltype=posixacl -O canmount=off -O compression=lz4 \ -O dnodesize=auto -O normalization=formD -O relatime=on -O xattr=sa \ -O encryption=aes-256-gcm -O keylocation=prompt -O keyformat=passphrase \ -O mountpoint=/ -R /mnt \ rpool /dev/sda2

You will have to enter your passphrase at this point. Choose wisely, as your passphrase is most likely the weakest link in this setup.

A few notes on the options supplied to zpool:

  • ashift=12 is recommended here because many drives today have 4KiB (or larger) physical sectors, even though they present 512B logical sectors
  • acltype=posixacl enables POSIX ACLs globally
  • normalization=formD eliminates some corner cases relating to UTF-8 filename normalization. It also enables `utf8only=on`, meaning that only files with valid UTF-8 filenames will be accepted.
  • xattr=sa vastly improves the performance of extended attributes, but is Linux-only. If you care about using this pool on other OpenZFS implementation don't specify this option.

After completing this, confirm that the pool has been created:

# zpool status

Should return something like:

     pool: rpool
    state: ONLINE
     scan: none requested
   config:
   
       NAME        STATE     READ WRITE CKSUM
       rpool       ONLINE       0     0     0
         sda2      ONLINE       0     0     0
   
   errors: No known data errors

Create the required datasets and mount root

zfs create -o mountpoint=none -o canmount=off rpool/ROOT zfs create -o mountpoint=legacy rpool/ROOT/alpine mount -t zfs rpool/ROOT/alpine /mnt/

Mount the /boot filesystem

mkdir /mnt/boot/ mount -t ext4 /dev/sda1 /mnt/boot/

Enable ZFS' services

rc-update add zfs-import sysinit rc-update add zfs-mount sysinit

Install Alpine Linux

setup-disk /mnt dd if=/usr/share/syslinux/mbr.bin of=/dev/sda # write mbr so we can boot

Reboot and enjoy!

😉

NOTE: If you went with the optional step, be sure to disable root login after you reboot.