Compare commits

...

8 Commits

2 changed files with 111 additions and 13 deletions

View File

@@ -8,8 +8,37 @@ We expect minimal prep on your end. Please make sure that before execution the f
- Arch Linux live CD ISO image sees exactly one partition with partition type code `BF00` ("Solaris root")
- Arch Linux live CD ISO image sees exactly one partition with partition type code `EF00` ("EFI system partition")
- The `EF00` EFI partition is mountable, in practical terms this usually only means it has a file system.
- No ZFS zpool exists
### How to prep
On a blank example disk `/dev/sda` you can fulfill the requirements (One `EF00` partition with a file system plus one `BF00` partition) for example like so:
```
sgdisk --new '1::+100M' --new '2' --typecode '1:EF00' --typecode '2:BF00' /dev/sda
mkfs.vfat /dev/sda1
```
> `--new '1::+100M'`: Create partition number `1`. The field separator `:` separates the partition number from start sector. In this case start sector is unspecified so start sector sits at whatever the system's default is for this operation. On a blank disk on an Arch Linux live CD ISO image this will default to sector `2048`. Partition ends at whatever the beginning is `+100M` meaning plus 100 Mebibytes.
>
> `--new '2'`: Create partition number `2`. Both field number 2, the start sector, and field number 3, the end sector, are unspecified, there's no field separator `:`. Field number 2 will be the first free sector - in this case right after partition 1 - and field number 3 will be end of disk. Thus partition `2` will fill the remaining free disk space.
>
> `--typecode '1:EF00'`: Partition 1 gets partition type code `EF00`, an EFI system partition.
>
> `--typecode '2:BF00'`: Partition 2 gets partition type code `BF00`, an Solaris root partition.
The result will be something like this at which point you can start the `setup.sh` script, see [How to run this?](#how-to-run-this) below for more details.
```
# lsblk --paths
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
/dev/loop0 7:0 0 688.5M 1 loop /run/archiso/airootfs
/dev/sr0 11:0 1 810.3M 0 rom /run/archiso/bootmnt
/dev/sda 202:0 0 10G 0 disk
├─/dev/sda1 202:1 0 100M 0 part
└─/dev/sda2 202:2 0 9.9G 0 part
```
## ZFS dataset layout
The script will create a single ZFS zpool `zpool` on the `BF00` partition with dataset child `zpool/root` which itself has one child `zpool/root/archlinux`, that's where Arch Linux gets installed. Parallel to `zpool/root` it'll create `zpool/data` with a `zpool/data/home` child dataset that gets mounted at `/home`.
The script will use the `EF00` partition to install a ZFSBootMenu EFI executable if `efibootmgr` says that no such `ZFSBootMenu` entry exists. If ZFSBootMenu gets added to the EFI partition it'll become primary boot option.
@@ -24,6 +53,26 @@ The script will use the `EF00` partition to install a ZFSBootMenu EFI executable
```
During execution the script will call itself when it changes into its `chroot`, that's why we `export SCRIPT_URL`. Feel free to update `"${SCRIPT_URL}"` with whatever branch or revision you want to use from [quico.space/quico-os-setup/arch-zbm](https://quico.space/quico-os-setup/arch-zbm). Typically `.../branch/main/setup.sh` as shown above is what you want.
### Options
#### Compression
By default we create a zpool with ZFS properties `compression=on`. If the `lz4_compress` pool feature is active this will by default enable `compression=lz4`. See `man 7 zfsprops` for example in ZFS 2.19 for details. See `zpool get feature@lz4_compress <pool>` to check this feature's status on your `<pool>`.
To get zpool with uncompressed datasets export the shell variable `ARCHZBM_ZFSPROPS_NO_COMPRESSION` with any value prior to running this script. Literally any value works as long as you're not setting this to an empty string:
```
export ARCHZBM_ZFSPROPS_NO_COMPRESSION=yesplease
```
#### Encryption
By default we encrypt the zpool with ZFS properties `encryption=on`. In ZFS 2.1.9 this defaults to `encryption=aes-256-gcm`.
To get an unencrypted zpool export the shell variable `ARCHZBM_ZFSPROPS_NO_ENCRYPTION` with any value prior to running this script:
```
export ARCHZBM_ZFSPROPS_NO_ENCRYPTION=yup
```
## Steps
The scripts takes the following installation steps.
@@ -60,6 +109,29 @@ After installation you're going to want to at least touch these points in your n
- ZFS: The password for all datasets underneath `zpool` is `password`.
- Local `root` account: The local `root` account's password is `password`.
- Arch User Repository (AUR) helper: We installed [paru](https://github.com/Morganamilo/paru) as our AUR helper, we installed from GitHub via `makepkg -si`.
- In `/etc/systemd/network/50-wired.network` instead of a DHCP-based network config you can get a static one. The DHCP-based looks for reference looks like:
```
...
[Network]
DHCP=ipv4
IPForward=yes
Domains=~.
[DHCP]
UseDNS=yes
RouteMetric=10
```
Dynamic one does away with the `[DHCP]` section:
```
...
[Network]
Address=10.10.10.2/24
Gateway=10.10.10.1
DNS=10.10.10.1
IPForward=yes
```
# Password change

View File

@@ -111,7 +111,7 @@ function get_drive_id () {
printf -- '%s' "${drive_id_single}"
return 0
fi
>2 printf -- '%s\n' 'Not exactly one '"'${1:?}'"' partition entry in /dev/disk/by-id, exiting ...'
>2 printf -- '%s\n' 'No '"'${1:?}'"' partition entry in /dev/disk/by-partuuid, exiting ...'
exit 77
}
@@ -151,22 +151,33 @@ function set_zpool_password () {
}
function import_pool () {
zpool import -d '/dev/disk/by-id' -R '/mnt' "${zpool_name}" -N -f
zfs load-key "${zpool_name}"
zpool import -d '/dev/disk/by-partuuid' -R '/mnt' "${zpool_name}" -N -f
[[ ! "${ARCHZBM_ZFSPROPS_NO_ENCRYPTION}" ]] && zfs load-key "${zpool_name}"
}
function create_pool () {
# Create a temporary pool that is not cached
#
# Add zfsprops 'compression' unless environment variable
# ARCHZBM_ZFSPROPS_NO_COMPRESSION is set to any value.
#
# Add zfsprops 'encryption' along with 'keyformat' and a 'keylocation'
# unless environment variable ARCHZBM_ZFSPROPS_NO_ENCRYPTION is set to
# any value.
zpool create -f \
-o 'ashift=12' \
-o 'autotrim=on' \
-O 'acltype=posix' \
-O 'compression=on' \
$([[ ! "${ARCHZBM_ZFSPROPS_NO_COMPRESSION}" ]] && \
printf -- '%s ' \
'-O compression=on') \
-O 'relatime=on' \
-O 'xattr=sa' \
-O 'encryption=on' \
-O 'keyformat=passphrase' \
-O 'keylocation=file:///etc/zfs/'"${zpool_name}"'.key' \
$([[ ! "${ARCHZBM_ZFSPROPS_NO_ENCRYPTION}" ]] && \
printf -- '%s ' \
'-O encryption=on' \
'-O keyformat=passphrase' \
'-O keylocation=file:///etc/zfs/'"${zpool_name}"'.key') \
-O 'normalization=formD' \
-O 'mountpoint=none' \
-O 'canmount=off' \
@@ -202,7 +213,7 @@ function setup_zpool () {
zpool_drive="$(select_part 'zfs')"
drive_by_id="$(get_drive_id "${zpool_drive}")"
set_zpool_password
[[ ! "${ARCHZBM_ZFSPROPS_NO_ENCRYPTION}" ]] && set_zpool_password
if no_zpool_exists; then
create_pool "${drive_by_id}"
create_root_dataset
@@ -237,8 +248,20 @@ function pacman_dl_parallel () {
sed -ri -e 's'$'\x1''^.*?(ParallelDownloads)[^\r\n\f]*'$'\x1''\1 = 5'$'\x1''g' '/etc/pacman.conf'
}
function pacman_dont_check_space () {
# See pacman bug comment
# https://bugs.archlinux.org/task/45070#comment142712
#
# When we pacstrap onto ZFS pacman incorrectly calculates and
# overestimates required disk space. We instead assume an installation
# gets done with at least a 10 GiB drive which is plenty. Skip pacman's
# size check.
sed -ri -e 's'$'\x1''^.*?(CheckSpace)([^\r\n\f]*)'$'\x1''#\1\2'$'\x1''g' '/etc/pacman.conf'
}
function install_archlinux () {
pacman_dl_parallel
pacman_dont_check_space
pacstrap /mnt \
base \
base-devel \
@@ -293,14 +316,17 @@ function set_locale () {
}
function add_zfs_hook_to_initramfs () {
# Add zfs hook, remove fsck hook from initramfs. Also add plain text key
# file into initramfs since it's living inside an encrypted pool anyway.
# Add zfs hook, remove fsck hook from initramfs.
sed -ri \
-e 's'$'\x1''^(FILES=)[^\r\n\f]*'$'\x1''\1(/etc/zfs/'"${zpool_name}"'.key)'$'\x1''g' \
-e 's'$'\x1''(HOOKS=)(.*?[\(| ])(filesystems)([\)| ][^\r\n\f]*)'$'\x1''\1\2zfs \3\4'$'\x1''g' \
-e 's'$'\x1''((\()(fsck)(\)))'$'\x1''\2\4'$'\x1''g' \
-e 's'$'\x1''(([[:space:]]+)(fsck)|(fsck)([[:space:]]+))'$'\x1'''$'\x1''g' \
'/mnt/etc/mkinitcpio.conf'
# Also unless encryption's unwanted add plain text key file into
# initramfs since it's living inside an encrypted pool anyway.
[[ ! "${ARCHZBM_ZFSPROPS_NO_ENCRYPTION}" ]] && sed -ri \
-e 's'$'\x1''^(FILES=)[^\r\n\f]*'$'\x1''\1(/etc/zfs/'"${zpool_name}"'.key)'$'\x1''g' \
'/mnt/etc/mkinitcpio.conf'
}
function set_initramfs_build_list () {
@@ -317,7 +343,7 @@ function set_initramfs_build_list () {
}
function add_zfs_files_to_new_os () {
for zfs_file in '/etc/hostid' '/etc/zfs/zpool.cache' '/etc/zfs/'"${zpool_name}"'.key'; do
for zfs_file in '/etc/hostid' '/etc/zfs/zpool.cache' $([[ ! "${ARCHZBM_ZFSPROPS_NO_ENCRYPTION}" ]] && printf -- '%s' '/etc/zfs/'"${zpool_name}"'.key'); do
rsync -av --itemize-changes {'','/mnt'}"${zfs_file}"
done
}
@@ -586,7 +612,7 @@ function get_disks_with_one_efipart () {
}
function add_zbm_to_efi () {
if ! efibootmgr | grep -Pi -- 'ZFSBootMenu'; then
if ! efibootmgr | grep -Piq -- 'ZFSBootMenu'; then
local efi_disks_list
efi_disks_list="$(get_disks_with_one_efipart)"
if grep -Piq -- '^'"${efi_drive}"'$' <<<"${efi_disks_list}"; then