Install Debian 11 on OpenZFS via live session

This commit is contained in:
Kris Lamoureux 2022-10-04 03:10:43 -04:00
commit 3fffb1c169
Signed by: kris
GPG Key ID: 3EDA9C3441EDA925
7 changed files with 373 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
packer_cache
output
*.log

5
LICENSE Normal file
View File

@ -0,0 +1,5 @@
Copyright (C) 2022 by Kris Lamoureux <kris@lamoureux.io>
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

8
Makefile Normal file
View File

@ -0,0 +1,8 @@
all: debianzfs.qcow2
debianzfs.qcow2: export PKR_VAR_password = "$(pwgen -s 8 1)"
debianzfs.qcow2:
packer build -color=false . | tee debianzfs.log
clean:
rm -rf output debianzfs.log

49
README.md Normal file
View File

@ -0,0 +1,49 @@
# DebianZFS
DebianZFS is a bash script that automates a UEFI Debian installation on an encrypted ZFS pool. The script installs `zfsutils-linux` on Debian Live, then proceeds to partition, create the boot and root zpools, and [debootstraps](https://wiki.debian.org/Debootstrap) a minimal system.
Due to [licensing concerns with OpenZFS and Linux](https://openzfs.github.io/openzfs-docs/License.html), distributing the resulting binaries together in a disk image may be a copyright violation. However, nothing keeps you from legally building and keeping your own disk images.
- Requires packer and qemu to build images
### Quick Start
1. Clone the repository and navigate into the directory
```
git clone https://git.krislamo.org/kris/debianzfs
cd debianzfs
```
2. Build image
```
make
```
3. Copy qcow2 image to libvirt/images
```
sudo cp output/debianzfs.qcow2 /var/lib/libvirt/images/
```
4. Grab auto-generated passwords from the log
```
grep PW= debianzfs.log
```
5. Make a Libvirt VM and start
```
sudo virt-install --name debianzfs \
--description 'Debian ZFS' \
--ram 2048 \
--vcpus 2 \
--disk /var/lib/libvirt/images/debianzfs.qcow2 \
--os-type generic \
--network bridge=virbr0 \
--graphics vnc,listen=127.0.0.1,port=5901 \
--boot uefi,loader=/usr/shar/OVMF/OVMF_CODE.fd
```
6. If dropped into initramfs
```
zpool import -f rpool
exit
```
7. Enter rpool password
8. Login with root's password
### License
- DebianZFS is licensed under 0BSD, a public domain equivalent license; see the `LICENSE` file for more information

51
debianzfs.pkr.hcl Normal file
View File

@ -0,0 +1,51 @@
# Set 'password' using shell var: PKR_VAR_password=$(pwgen -s 8 1)
variable "password" {}
source "qemu" "bullseye-live" {
iso_url = "https://cdimage.debian.org/debian-cd/current-live/amd64/iso-hybrid/debian-live-11.5.0-amd64-standard.iso"
iso_checksum = "sha256:8172b188061d098080bb315972becbe9bd387c856866746cee018102cd00fc9b"
output_directory = "output"
shutdown_command = "echo 'packer' | sudo -S shutdown -P now"
disk_size = "5000M"
memory = 2048
format = "qcow2"
accelerator = "kvm"
http_directory = "."
ssh_username = "user"
ssh_password = var.password
ssh_timeout = "5m"
vm_name = "debianzfs.qcow2"
net_device = "virtio-net"
disk_interface = "virtio"
boot_wait = "5s"
boot_command = [
"<enter><wait10>",
"<enter><wait>",
"sudo -i<enter><wait>",
"read -s userpw<enter><wait>",
"${var.password}<enter><wait>",
"PASSWORD=$(echo $userpw | openssl passwd -1 -stdin)<enter><wait>",
"usermod -p \"$PASSWORD\" user<enter><wait>",
"apt-get update && apt-get install -y ssh && \\<enter>",
"systemctl start ssh && exit<enter>"
]
}
build {
name = "zfs"
sources = ["source.qemu.bullseye-live"]
provisioner "file" {
source = "debianzfs.sh"
destination = "/tmp/debianzfs.sh"
}
provisioner "shell" {
scripts = ["scripts/setup.sh"]
}
provisioner "shell" {
inline = ["sudo /tmp/debianzfs.sh /dev/vda"]
}
}

254
debianzfs.sh Normal file
View File

@ -0,0 +1,254 @@
#!/bin/bash
DISK=$1
ZFSHOST=$2
[ -z "$ZFSHOST" ] && ZFSHOST="debianzfs"
# Settings
export DEBIAN_FRONTEND=noninteractive
CODENAME="bullseye"
ZFSROOT="/mnt"
# Display commands
set -x
# Is the DISK path a block device?
DISK_TYPE=$(file "${DISK}" | awk '{ print $2$3 }')
if [ "$DISK_TYPE" != "blockspecial" ]; then
echo "ERROR: Disk '${DISK}' is not a block device"
exit 1;
fi
# Update sources list
SOURCES_LIST="/etc/apt/sources.list"
[ -f "$SOURCES_LIST" ] && mv "$SOURCES_LIST" "$SOURCES_LIST.$(date +%s).bak"
echo "deb http://deb.debian.org/debian/ ${CODENAME} main contrib" > "$SOURCES_LIST"
apt-get update
# Install tools and ZFS
apt-get install -y debootstrap gdisk pwgen zfsutils-linux
# Ensure swap isn't in use
swapoff --all
# Partition
sgdisk -n2:1M:+512M -t2:EF00 "$DISK"
sgdisk -n3:0:+1G -t3:BF01 "$DISK"
sgdisk -n4:0:0 -t4:BF00 "$DISK"
# Create boot pool
zpool create -f \
-o ashift=12 \
-o autotrim=on -d \
-o cachefile=/etc/zfs/zpool.cache \
-o feature@async_destroy=enabled \
-o feature@bookmarks=enabled \
-o feature@embedded_data=enabled \
-o feature@empty_bpobj=enabled \
-o feature@enabled_txg=enabled \
-o feature@extensible_dataset=enabled \
-o feature@filesystem_limits=enabled \
-o feature@hole_birth=enabled \
-o feature@large_blocks=enabled \
-o feature@livelist=enabled \
-o feature@lz4_compress=enabled \
-o feature@spacemap_histogram=enabled \
-o feature@zpool_checkpoint=enabled \
-O devices=off \
-O acltype=posixacl -O xattr=sa \
-O compression=lz4 \
-O normalization=formD \
-O relatime=on \
-O canmount=off -O mountpoint=/boot -R "$ZFSROOT" \
bpool "${DISK}3"
# Create root pool with random 16 character password
RPOOLPW="$(pwgen -s 16 1)"
echo "$RPOOLPW" | \
zpool create -f \
-o ashift=12 \
-o autotrim=on \
-O encryption=on -O keylocation=prompt -O keyformat=passphrase \
-O acltype=posixacl -O xattr=sa -O dnodesize=auto \
-O compression=lz4 \
-O normalization=formD \
-O relatime=on \
-O canmount=off -O mountpoint=/ -R "$ZFSROOT" \
rpool "${DISK}4"
unset RPOOLPW
# Create filesystem datasets to act as containers
zfs create -o canmount=off -o mountpoint=none rpool/ROOT
zfs create -o canmount=off -o mountpoint=none bpool/BOOT
# Create filesystem datasets for the root and boot filesystems
zfs create -o canmount=noauto -o mountpoint=/ rpool/ROOT/debian
zfs mount rpool/ROOT/debian
zfs create -o mountpoint=/boot bpool/BOOT/debian
# Create datasets
zfs create rpool/home
zfs create -o mountpoint=/root rpool/home/root
chmod 700 "$ZFSROOT/root"
zfs create -o canmount=off rpool/var
zfs create -o canmount=off rpool/var/lib
zfs create rpool/var/log
zfs create rpool/var/spool
zfs create -o com.sun:auto-snapshot=false rpool/var/cache
zfs create -o com.sun:auto-snapshot=false rpool/var/lib/nfs
zfs create -o com.sun:auto-snapshot=false rpool/var/tmp
chmod 1777 "$ZFSROOT/var/tmp"
zfs create -o canmount=off rpool/usr
zfs create rpool/usr/local
zfs create rpool/var/lib/AccountsService
zfs create rpool/var/lib/NetworkManager
zfs create -o com.sun:auto-snapshot=false rpool/var/lib/docker
# Mount a tmpfs at /run
mkdir "$ZFSROOT/run"
mount -t tmpfs tmpfs /mnt/run
mkdir "$ZFSROOT/run/lock"
# Install minimal system
debootstrap "$CODENAME" /mnt
# Copy in zpool.cache
mkdir "$ZFSROOT/etc/zfs"
cp /etc/zfs/zpool.cache "$ZFSROOT/etc/zfs/"
# Configure hostname
hostname "$ZFSHOST"
hostname > "$ZFSROOT/etc/hostname"
sed "/^127.0.0.1.*localhost/a 127.0.1.1\\t$ZFSHOST" "$ZFSROOT/etc/hosts" | tee "$ZFSROOT/etc/hosts"
# Configure network devices
NETWORK_DEVICES=$(ip a | awk '$1 ~ /^[0-9][:]/ {print substr($2, 0, length($2)-1)}')
while read -r INTER; do
if [ ! "$INTER" = "lo" ]; then
cat <<-EOF > "$ZFSROOT/etc/network/interfaces.d/$INTER"
auto ${INTER}
iface ${INTER} inet dhcp
EOF
fi
done <<< "$NETWORK_DEVICES"
# Update sources list in ZFSROOT
ZFS_SOURCES_LIST="$ZFSROOT/etc/apt/sources.list"
[ -f "$ZFS_SOURCES_LIST" ] && mv "$ZFS_SOURCES_LIST" "$ZFS_SOURCES_LIST.$(date +%s).bak"
cat <<-EOF > "$ZFS_SOURCES_LIST"
deb http://deb.debian.org/debian ${CODENAME} main contrib
deb-src http://deb.debian.org/debian ${CODENAME} main contrib
deb http://deb.debian.org/debian-security ${CODENAME}-security main contrib
deb-src http://deb.debian.org/debian-security ${CODENAME}-security main contrib
deb http://deb.debian.org/debian ${CODENAME}-updates main contrib
deb-src http://deb.debian.org/debian ${CODENAME}-updates main contrib
EOF
# Copy DISK var under ZFSROOT
echo "DISK=${DISK}" > "$ZFSROOT/var/tmp/zfsenv"
# Bind the virtual filesystems from the LiveCD environment to the new system
mount --make-private --rbind /dev /mnt/dev
mount --make-private --rbind /proc /mnt/proc
mount --make-private --rbind /sys /mnt/sys
# Chroot
cat << CHROOT | chroot /mnt bash --login
# Setup
set -ex
. /var/tmp/zfsenv
unset CDPATH
cd
# Configure a basic system environment
export DEBIAN_FRONTEND=noninteractive
export LC_CTYPE=en_US.UTF-8
export LC_ALL=en_US.UTF-8
ln -s /proc/self/mounts /etc/mtab
apt-get update
apt-get upgrade -y
apt-get install -y console-setup locales
dpkg-reconfigure locales tzdata keyboard-configuration console-setup
apt-get install -y dpkg-dev linux-headers-generic linux-image-generic zfs-initramfs
echo REMAKE_INITRD=yes > /etc/dkms/zfs.conf
# Install Grub for UEFI
apt-get install -y dosfstools
echo REMAKE_INITRD=yes > /etc/dkms/zfs.conf
mkdosfs -F 32 -s 1 -n EFI "${DISK}2"
mkdir /boot/efi
echo "${DISK}2" /boot/efi vfat defaults 0 0 >> /etc/fstab
mount /boot/efi
apt-get install -y grub-efi-amd64 shim-signed
apt-get purge -y os-prober
ROOTPW=$(pwgen 8 1)
echo "root:\$ROOTPW" | chpasswd
unset ROOTPW
# Add bpool import service
cat <<- BPOOL > /etc/systemd/system/zfs-import-bpool.service
[Unit]
DefaultDependencies=no
Before=zfs-import-scan.service
Before=zfs-import-cache.service
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/sbin/zpool import -N -o cachefile=none bpool
# Work-around to preserve zpool cache:
ExecStartPre=-/bin/mv /etc/zfs/zpool.cache /etc/zfs/preboot_zpool.cache
ExecStartPost=-/bin/mv /etc/zfs/preboot_zpool.cache /etc/zfs/zpool.cache
[Install]
WantedBy=zfs-import.target
BPOOL
# Enable importing bpool service
systemctl enable zfs-import-bpool.service
# Mount a tmpfs to /tmp
cp /usr/share/systemd/tmp.mount /etc/systemd/system/
systemctl enable tmp.mount
# Verify that the ZFS boot filesystem is recognized
grub-probe /boot
# Refresh the initrd files
update-initramfs -c -k all
# Workaround GRUB's missing zpool-features support
sed -i "s/^\(GRUB_CMDLINE_LINUX=\).*/\1\"root=ZFS=rpool\/ROOT\/debian\"/" /etc/default/grub
sed -i "s/^\(GRUB_CMDLINE_LINUX_DEFAULT=\).*/\1\"\"/" /etc/default/grub
sed -i '/GRUB_TERMINAL/s/^#//g' /etc/default/grub
cat /etc/default/grub
# Update the boot configuration
update-grub
# Install GRUB to the ESP
grub-install --target=x86_64-efi --efi-directory=/boot/efi \
--bootloader-id=debian --recheck --no-floppy
# Fix filesystem mount ordering
mkdir /etc/zfs/zfs-list.cache
touch /etc/zfs/zfs-list.cache/bpool
touch /etc/zfs/zfs-list.cache/rpool
timeout 10 zed -F || \
test -s /etc/zfs/zfs-list.cache/bpool &&
test -s /etc/zfs/zfs-list.cache/rpool
# Fix the paths to eliminate /mnt
sed -Ei "s|/mnt/?|/|" /etc/zfs/zfs-list.cache/*
# Snapshot the initial installation
zfs snapshot bpool/BOOT/debian@install
zfs snapshot rpool/ROOT/debian@install
exit
CHROOT
mount | grep -v zfs | tac | awk '/\/mnt/ {print $3}' | \
xargs -I{} umount -lf {}
zpool export -a || exit 0
exit 0

3
scripts/setup.sh Normal file
View File

@ -0,0 +1,3 @@
#!/bin/bash
sudo passwd -d user
sudo chmod u+x /tmp/debianzfs.sh