1 Commits

Author SHA1 Message Date
Prokopenko Andrey
75cfc601d8 keys info 2021-09-15 17:48:23 +02:00
10 changed files with 290 additions and 2079 deletions

View File

@@ -1,11 +0,0 @@
name: shellcheck
on: push
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Run shellcheck
run: ci/run_shellcheck.sh

View File

@@ -1,8 +1,6 @@
# zfs-hetzner-vm
[![shellcheck](https://github.com/terem42/zfs-hetzner-vm/actions/workflows/shellcheck.yml/badge.svg)](https://github.com/terem42/zfs-hetzner-vm/actions/workflows/shellcheck.yml)
Scripts to install Debian 10, 11, 12 or Ubuntu 18 LTS, 20 LTS, 22 LTS with ZFS root on Hetzner root servers (virtual and dedicated).<br/>
Scripts to install Debian 11, 10 or Ubuntu 18 with ZFS root on Hetzner root servers (virtual and dedicated).<br/>
__WARNING:__ all data on the disk will be destroyed.
## How to use:
@@ -12,40 +10,28 @@ __WARNING:__ all data on the disk will be destroyed.
* Click "enable rescue and power cycle", add SSH key to the rescue console, set it OS to linux64, then press mount rescue and power cycle" button.
* connect via SSH to rescue console, and run the script from this repo.
Debian 10 minimal setup with SSH server
````bash
wget -qO- https://raw.githubusercontent.com/terem42/zfs-hetzner-vm/master/hetzner-debian10-zfs-setup.sh | bash -
````
Debian 11 minimal setup with SSH server
````bash
wget -qO- https://raw.githubusercontent.com/terem42/zfs-hetzner-vm/master/hetzner-debian11-zfs-setup.sh | bash -
````
Debian 12 minimal setup with SSH server
````bash
wget -qO- https://raw.githubusercontent.com/terem42/zfs-hetzner-vm/master/hetzner-debian12-zfs-setup.sh | bash -
````
Ubuntu 18.04 LTS minimal setup with SSH server
````bash
wget -qO- https://raw.githubusercontent.com/terem42/zfs-hetzner-vm/master/hetzner-ubuntu18-zfs-setup.sh | bash -
````
Ubuntu 20 LTS minimal setup with SSH server
````bash
wget -qO- https://raw.githubusercontent.com/terem42/zfs-hetzner-vm/master/hetzner-ubuntu20-zfs-setup.sh | bash -
````
Ubuntu 22 LTS minimal setup with SSH server
Debian 10 minimal setup with SSH server
````bash
wget -qO- https://raw.githubusercontent.com/terem42/zfs-hetzner-vm/master/hetzner-ubuntu22-zfs-setup.sh | bash -
wget -qO- https://raw.githubusercontent.com/terem42/zfs-hetzner-vm/master/hetzner-debian10-zfs-setup.sh | bash -
````
Ubuntu 18.04 LTS minimal setup with SSH server
````bash
wget -qO- https://raw.githubusercontent.com/terem42/zfs-hetzner-vm/master/hetzner-ubuntu18-zfs-setup.sh | bash -
````
Answer script questions about desired hostname and ZFS ARC cache size.
@@ -54,12 +40,10 @@ To cope with network failures its higly recommended to run the commands above in
Example of screen utility usage:
````bash
export LC_ALL=en_US.UTF-8 && screen -S zfs
screen -S zfs
````
To detach from screen console, hit Ctrl-d then a
To reattach, type `screen -r zfs`
Upon succesfull run, the script will reboot system, and you will be able to login into it, using the same SSH key you have used within rescue console
Please note that the drives you intend to format can not be in use,
you can execute `mdadm --stop --scan` before running the script to halt default software raid operations.

View File

@@ -1,25 +0,0 @@
#!/bin/bash
set -o pipefail
set -o errexit
set -o nounset
set -o errtrace
shopt -s inherit_errexit
# Always download the latest version:
#
# - it's fast and stable enough not to worry about it;
# - the workflow is basically single-person, so there's no risk of a new dev encountering an error found
# by a new shellcheck version.
mkdir -p /opt/shellcheck
wget -qO- https://github.com/koalaman/shellcheck/releases/download/stable/shellcheck-stable.linux.x86_64.tar.xz \
| tar xJv -O shellcheck-stable/shellcheck \
> /opt/shellcheck/shellcheck
chmod +x /opt/shellcheck/shellcheck
/opt/shellcheck/shellcheck --version
grep -lZP '^#!/bin/\w+sh' -R | xargs -0 /opt/shellcheck/shellcheck

View File

@@ -17,8 +17,6 @@ set -o errexit
set -o pipefail
set -o nounset
export TMPDIR=/tmp
# Variables
v_bpool_name=
v_bpool_tweaks=
@@ -42,7 +40,7 @@ c_deb_security_repo=http://mirror.hetzner.de/debian/security
c_default_zfs_arc_max_mb=250
c_default_bpool_tweaks="-o ashift=12 -O compression=lz4"
c_default_rpool_tweaks="-o ashift=12 -O acltype=posixacl -O compression=zstd-9 -O dnodesize=auto -O relatime=on -O xattr=sa -O normalization=formD"
c_default_rpool_tweaks="-o ashift=12 -O acltype=posixacl -O compression=lz4 -O dnodesize=auto -O relatime=on -O xattr=sa -O normalization=formD"
c_default_hostname=terem
c_zfs_mount_dir=/mnt
c_log_dir=$(dirname "$(mktemp)")/zfs-hetzner-vm
@@ -64,9 +62,7 @@ function print_step_info_header {
###############################################################################
# ${FUNCNAME[1]}"
if [[ "${1:-}" != "" ]]; then
echo -n " $1"
fi
[[ "${1:-}" != "" ]] && echo -n " $1" || true
echo "
###############################################################################
@@ -110,7 +106,7 @@ This script will prepare the ZFS pools, then install and configure minimal Debia
The script with minimal changes may be used on any other hosting provider supporting KVM virtualization and offering Debian-based rescue system.
In order to stop the procedure, hit Esc twice during dialogs (excluding yes/no ones), or Ctrl+C while any operation is running.
'
dialog --msgbox "$dialog_message" 30 100
dialog --ascii-lines --msgbox "$dialog_message" 30 100
}
function store_os_distro_information {
@@ -145,13 +141,13 @@ function initial_load_debian_zed_cache {
local success=0
if [[ ! -e /mnt/etc/zfs/zfs-list.cache/rpool ]] || [[ -e /mnt/etc/zfs/zfs-list.cache/rpool && (( $(find /mnt/etc/zfs/zfs-list.cache/rpool -type f -printf '%s' 2> /dev/null) == 0 )) ]]; then
if [[ ! -e /mnt/etc/zfs/zfs-list.cache/rpool ]] || [[ -e /mnt/etc/zfs/zfs-list.cache/rpool && (( $(ls -l /mnt/etc/zfs/zfs-list.cache/rpool 2> /dev/null | cut -d ' ' -f 5) == 0 )) ]]; then
chroot_execute "zfs set canmount=noauto rpool"
SECONDS=0
while (( SECONDS++ <= 300 )); do
if [[ -e /mnt/etc/zfs/zfs-list.cache/rpool ]] && (( $(find /mnt/etc/zfs/zfs-list.cache/rpool -type f -printf '%s' 2> /dev/null) > 0 )); then
while (( SECONDS++ <= 120 )); do
if [[ -e /mnt/etc/zfs/zfs-list.cache/rpool ]] && (( "$(ls -l /mnt/etc/zfs/zfs-list.cache/rpool | cut -d ' ' -f 5)" > 0 )); then
success=1
break
else
@@ -214,7 +210,7 @@ LOG
If you think this is a bug, please open an issue on https://github.com/terem42/zfs-hetzner-vm/issues, and attach the file `'"$c_disks_log"'`.
'
dialog --msgbox "$dialog_message" 30 100
dialog --ascii-lines --msgbox "$dialog_message" 30 100
exit 1
fi
@@ -243,7 +239,7 @@ function select_disks {
Devices with mounted partitions, cdroms, and removable devices are not displayed!
"
mapfile -t v_selected_disks < <(dialog --separate-output --checklist "$dialog_message" 30 100 $((${#menu_entries_option[@]} / 3)) "${menu_entries_option[@]}" 3>&1 1>&2 2>&3)
mapfile -t v_selected_disks < <(dialog --ascii-lines --separate-output --checklist "$dialog_message" 30 100 $((${#menu_entries_option[@]} / 3)) "${menu_entries_option[@]}" 3>&1 1>&2 2>&3)
if [[ ${#v_selected_disks[@]} -gt 0 ]]; then
break
@@ -260,7 +256,7 @@ function ask_swap_size {
local swap_size_invalid_message=
while [[ ! $v_swap_size =~ ^[0-9]+$ ]]; do
v_swap_size=$(dialog --inputbox "${swap_size_invalid_message}Enter the swap size in GiB (0 for no swap):" 30 100 2 3>&1 1>&2 2>&3)
v_swap_size=$(dialog --ascii-lines --inputbox "${swap_size_invalid_message}Enter the swap size in GiB (0 for no swap):" 30 100 2 3>&1 1>&2 2>&3)
swap_size_invalid_message="Invalid swap size! "
done
@@ -275,7 +271,7 @@ function ask_free_tail_space {
local tail_space_invalid_message=
while [[ ! $v_free_tail_space =~ ^[0-9]+$ ]]; do
v_free_tail_space=$(dialog --inputbox "${tail_space_invalid_message}Enter the space to leave at the end of each disk (0 for none):" 30 100 0 3>&1 1>&2 2>&3)
v_free_tail_space=$(dialog --ascii-lines --inputbox "${tail_space_invalid_message}Enter the space to leave at the end of each disk (0 for none):" 30 100 0 3>&1 1>&2 2>&3)
tail_space_invalid_message="Invalid size! "
done
@@ -290,7 +286,7 @@ function ask_zfs_arc_max_size {
local zfs_arc_max_invalid_message=
while [[ ! $v_zfs_arc_max_mb =~ ^[0-9]+$ ]]; do
v_zfs_arc_max_mb=$(dialog --inputbox "${zfs_arc_max_invalid_message}Enter ZFS ARC cache max size in Mb (minimum 64Mb, enter 0 for ZFS default value, the default will take up to 50% of memory):" 30 100 "$c_default_zfs_arc_max_mb" 3>&1 1>&2 2>&3)
v_zfs_arc_max_mb=$(dialog --ascii-lines --inputbox "${zfs_arc_max_invalid_message}Enter ZFS ARC cache max size in Mb (minimum 64Mb, enter 0 for ZFS default value, the default will take up to 50% of memory):" 30 100 "$c_default_zfs_arc_max_mb" 3>&1 1>&2 2>&3)
zfs_arc_max_invalid_message="Invalid size! "
done
@@ -306,14 +302,14 @@ function ask_pool_names {
local bpool_name_invalid_message=
while [[ ! $v_bpool_name =~ ^[a-z][a-zA-Z_:.-]+$ ]]; do
v_bpool_name=$(dialog --inputbox "${bpool_name_invalid_message}Insert the name for the boot pool" 30 100 bpool 3>&1 1>&2 2>&3)
v_bpool_name=$(dialog --ascii-lines --inputbox "${bpool_name_invalid_message}Insert the name for the boot pool" 30 100 bpool 3>&1 1>&2 2>&3)
bpool_name_invalid_message="Invalid pool name! "
done
local rpool_name_invalid_message=
while [[ ! $v_rpool_name =~ ^[a-z][a-zA-Z_:.-]+$ ]]; do
v_rpool_name=$(dialog --inputbox "${rpool_name_invalid_message}Insert the name for the root pool" 30 100 rpool 3>&1 1>&2 2>&3)
v_rpool_name=$(dialog --ascii-lines --inputbox "${rpool_name_invalid_message}Insert the name for the root pool" 30 100 rpool 3>&1 1>&2 2>&3)
rpool_name_invalid_message="Invalid pool name! "
done
@@ -325,8 +321,8 @@ function ask_pool_tweaks {
# shellcheck disable=SC2119
print_step_info_header
v_bpool_tweaks=$(dialog --inputbox "Insert the tweaks for the boot pool" 30 100 -- "$c_default_bpool_tweaks" 3>&1 1>&2 2>&3)
v_rpool_tweaks=$(dialog --inputbox "Insert the tweaks for the root pool" 30 100 -- "$c_default_rpool_tweaks" 3>&1 1>&2 2>&3)
v_bpool_tweaks=$(dialog --ascii-lines --inputbox "Insert the tweaks for the boot pool" 30 100 -- "$c_default_bpool_tweaks" 3>&1 1>&2 2>&3)
v_rpool_tweaks=$(dialog --ascii-lines --inputbox "Insert the tweaks for the root pool" 30 100 -- "$c_default_rpool_tweaks" 3>&1 1>&2 2>&3)
print_variables v_bpool_tweaks v_rpool_tweaks
}
@@ -341,8 +337,8 @@ function ask_root_password {
local password_repeat=-
while [[ "$v_root_password" != "$password_repeat" || "$v_root_password" == "" ]]; do
v_root_password=$(dialog --passwordbox "${password_invalid_message}Please enter the root account password (can't be empty):" 30 100 3>&1 1>&2 2>&3)
password_repeat=$(dialog --passwordbox "Please repeat the password:" 30 100 3>&1 1>&2 2>&3)
v_root_password=$(dialog --ascii-lines --passwordbox "${password_invalid_message}Please enter the root account password (can't be empty):" 30 100 3>&1 1>&2 2>&3)
password_repeat=$(dialog --ascii-lines --passwordbox "Please repeat the password:" 30 100 3>&1 1>&2 2>&3)
password_invalid_message="Passphrase empty, or not matching! "
done
@@ -352,7 +348,7 @@ function ask_root_password {
function ask_encryption {
print_step_info_header
if dialog --defaultno --yesno 'Do you want to encrypt the root pool?' 30 100; then
if dialog --ascii-lines --yesno 'Do you want to encrypt the root pool?' 30 100; then
v_encrypt_rpool=1
fi
set +x
@@ -360,8 +356,8 @@ function ask_encryption {
local passphrase_invalid_message=
local passphrase_repeat=-
while [[ "$v_passphrase" != "$passphrase_repeat" || ${#v_passphrase} -lt 8 ]]; do
v_passphrase=$(dialog --passwordbox "${passphrase_invalid_message}Please enter the passphrase for the root pool (8 chars min.):" 30 100 3>&1 1>&2 2>&3)
passphrase_repeat=$(dialog --passwordbox "Please repeat the passphrase:" 30 100 3>&1 1>&2 2>&3)
v_passphrase=$(dialog --ascii-lines --passwordbox "${passphrase_invalid_message}Please enter the passphrase for the root pool (8 chars min.):" 30 100 3>&1 1>&2 2>&3)
passphrase_repeat=$(dialog --ascii-lines --passwordbox "Please repeat the passphrase:" 30 100 3>&1 1>&2 2>&3)
passphrase_invalid_message="Passphrase too short, or not matching! "
done
@@ -372,7 +368,7 @@ function ask_encryption {
function ask_zfs_experimental {
print_step_info_header
if dialog --defaultno --yesno 'Do you want to use experimental zfs module build?' 30 100; then
if dialog --ascii-lines --yesno 'Do you want to use experimental zfs module build?' 30 100; then
v_zfs_experimental=1
fi
}
@@ -383,8 +379,8 @@ function ask_hostname {
local hostname_invalid_message=
while [[ ! $v_hostname =~ ^[a-z][a-zA-Z0-9_:.-]+$ ]]; do
v_hostname=$(dialog --inputbox "${hostname_invalid_message}Set the host name" 30 100 "$c_default_hostname" 3>&1 1>&2 2>&3)
while [[ ! $v_hostname =~ ^[a-z][a-zA-Z_:.-]+$ ]]; do
v_hostname=$(dialog --ascii-lines --inputbox "${hostname_invalid_message}Set the host name" 30 100 "$c_default_hostname" 3>&1 1>&2 2>&3)
hostname_invalid_message="Invalid host name! "
done
@@ -436,7 +432,8 @@ function unmount_and_export_fs {
echo "===========exporting zfs pools============="
set +e
while (( zpools_exported == 99 )) && (( SECONDS++ <= 60 )); do
if zpool export -a 2> /dev/null; then
zpool export -a 2> /dev/null
if [[ $? == 0 ]]; then
zpools_exported=1
echo "all zfs pools were succesfully exported"
break;
@@ -453,7 +450,6 @@ function unmount_and_export_fs {
#################### MAIN ################################
export LC_ALL=en_US.UTF-8
export NCURSES_NO_UTF8_ACS=1
check_prerequisites
@@ -495,19 +491,10 @@ done
echo "======= installing zfs on rescue system =========="
echo "zfs-dkms zfs-dkms/note-incompatible-licenses note true" | debconf-set-selections
# echo "y" | zfs
# linux-headers-generic linux-image-generic
apt install --yes software-properties-common dpkg-dev dkms
rm -f "$(which zfs)"
rm -f "$(which zpool)"
echo -e "deb http://deb.debian.org/debian/ testing main contrib non-free\ndeb http://deb.debian.org/debian/ testing main contrib non-free\n" >/etc/apt/sources.list.d/bookworm-testing.list
echo -e "Package: src:zfs-linux\nPin: release n=testing\nPin-Priority: 990\n" > /etc/apt/preferences.d/90_zfs
apt update
apt install -t testing --yes zfs-dkms zfsutils-linux
rm /etc/apt/sources.list.d/bookworm-testing.list
rm /etc/apt/preferences.d/90_zfs
apt update
export PATH=$PATH:/usr/sbin
apt install --yes -t buster-backports libelf-dev zfs-dkms
modprobe zfs
zfs --version
echo "======= partitioning the disk =========="
@@ -519,9 +506,9 @@ echo "======= partitioning the disk =========="
fi
for selected_disk in "${v_selected_disks[@]}"; do
wipefs --all --force "$selected_disk"
wipefs --all "$selected_disk"
sgdisk -a1 -n1:24K:+1000K -t1:EF02 "$selected_disk"
sgdisk -n2:0:+2G -t2:BF01 "$selected_disk" # Boot pool
sgdisk -n2:0:+512M -t2:BF01 "$selected_disk" # Boot pool
sgdisk -n3:0:"$tail_space_parameter" -t3:BF01 "$selected_disk" # Root pool
done
@@ -548,16 +535,12 @@ echo "======= create zfs pools and datasets =========="
pools_mirror_option=
fi
# shellcheck disable=SC2086
zpool create \
$v_bpool_tweaks -O canmount=off -O devices=off \
-o cachefile=/etc/zpool.cache \
-O mountpoint=/boot -R $c_zfs_mount_dir -f \
$v_bpool_name $pools_mirror_option "${bpool_disks_partitions[@]}"
# shellcheck disable=SC2086
echo -n "$v_passphrase" | zpool create \
-o cachefile=/etc/zpool.cache \
$v_rpool_tweaks \
"${encryption_options[@]}" \
-O mountpoint=/ -R $c_zfs_mount_dir -f \
@@ -573,7 +556,9 @@ zfs create -o canmount=noauto -o mountpoint=/boot "$v_bpool_name/BOOT/debian"
zfs mount "$v_bpool_name/BOOT/debian"
zfs create "$v_rpool_name/home"
zfs create -o mountpoint=/root "$v_rpool_name/home/root"
zfs create -o canmount=off "$v_rpool_name/var"
zfs create -o canmount=off "$v_rpool_name/var/lib"
zfs create "$v_rpool_name/var/log"
zfs create "$v_rpool_name/var/spool"
@@ -624,7 +609,7 @@ ff02::2 ip6-allrouters
ff02::3 ip6-allhosts
CONF
ip6addr_prefix=$(ip -6 a s | grep -E "inet6.+global" | sed -nE 's/.+inet6\s(([0-9a-z]{1,4}:){4,4}).+/\1/p' | head -n 1)
ip6addr_prefix=$(ip -6 a s | grep -E "inet6.+global" | sed -nE 's/.+inet6\s(([0-9a-z]{1,4}:){4,4}).+/\1/p')
cat <<CONF > /mnt/etc/systemd/network/10-eth0.network
[Match]
@@ -636,7 +621,9 @@ Address=${ip6addr_prefix}:1/64
Gateway=fe80::1
CONF
chroot_execute "systemctl enable systemd-networkd.service"
chroot_execute "systemctl enable systemd-resolved.service"
cp /etc/resolv.conf $c_zfs_mount_dir/etc/resolv.conf
echo "======= preparing the jail for chroot =========="
for virtual_fs_dir in proc sys dev; do
@@ -721,12 +708,6 @@ if [[ $v_zfs_experimental == "1" ]]; then
else
chroot_execute "apt install --yes -t buster-backports zfs-initramfs zfs-dkms zfsutils-linux"
fi
chroot_execute 'cat << DKMS > /etc/dkms/zfs.conf
# override for /usr/src/zfs-*/dkms.conf:
# always rebuild initrd when zfs module has been changed
# (either by a ZFS update or a new kernel version)
REMAKE_INITRD="yes"
DKMS'
echo "======= installing OpenSSH and network tooling =========="
chroot_execute "apt install --yes openssh-server net-tools"
@@ -742,8 +723,25 @@ chroot_execute "dpkg-reconfigure openssh-server -f noninteractive"
echo "======= set root password =========="
chroot_execute "echo root:$(printf "%q" "$v_root_password") | chpasswd"
echo "======= setting up zfs cache =========="
cp /etc/zpool.cache /mnt/etc/zfs/zpool.cache
echo "======= setting up zfs services =========="
chroot_execute "cat > /etc/systemd/system/zfs-import-bpool.service <<UNIT
[Unit]
DefaultDependencies=no
Before=zfs-import-scan.service
Before=zfs-import-cache.service
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStartPre=/bin/sh -c '[ -f /etc/zfs/zpool.cache ] && mv /etc/zfs/zpool.cache /etc/zfs/preboot_zpool.cache || true'
ExecStart=/sbin/zpool import -N -o cachefile=none -d /dev/disk/by-id $v_bpool_name
ExecStartPost=/bin/sh -c '[ -f /etc/zfs/preboot_zpool.cache ] && mv /etc/zfs/preboot_zpool.cache /etc/zfs/zpool.cache || true'
[Install]
WantedBy=zfs-import.target
UNIT"
chroot_execute "systemctl enable zfs-import-bpool.service"
echo "========setting up zfs module parameters========"
chroot_execute "echo options zfs zfs_arc_max=$((v_zfs_arc_max_mb * 1024 * 1024)) >> /etc/modprobe.d/zfs.conf"
@@ -770,7 +768,6 @@ if [[ $v_encrypt_rpool == "1" ]]; then
chroot_execute "apt install --yes dropbear-initramfs"
mkdir -p "$c_zfs_mount_dir/etc/dropbear-initramfs"
cp /root/.ssh/authorized_keys "$c_zfs_mount_dir/etc/dropbear-initramfs/authorized_keys"
cp "$c_zfs_mount_dir/etc/ssh/ssh_host_rsa_key" "$c_zfs_mount_dir/etc/ssh/ssh_host_rsa_key_temp"
@@ -787,6 +784,18 @@ if [[ $v_encrypt_rpool == "1" ]]; then
rm -rf "$c_zfs_mount_dir/etc/dropbear-initramfs/dropbear_dss_host_key"
fi
#cd "$c_zfs_mount_dir/root"
#wget http://ftp.de.debian.org/debian/pool/main/libt/libtommath/libtommath1_1.1.0-3_amd64.deb
#wget http://ftp.de.debian.org/debian/pool/main/d/dropbear/dropbear-bin_2018.76-5_amd64.deb
#wget http://ftp.de.debian.org/debian/pool/main/d/dropbear/dropbear-initramfs_2018.76-5_all.deb
#chroot_execute "dpkg -i /root/libtommath1_1.1.0-3_amd64.deb"
#chroot_execute "dpkg -i /root/dropbear-bin_2018.76-5_amd64.deb"
#chroot_execute "dpkg -i /root/dropbear-initramfs_2018.76-5_all.deb"
#rm $c_zfs_mount_dir/root/*.deb
#cd /root
echo "============setup root prompt============"
cat > "$c_zfs_mount_dir/root/.bashrc" <<CONF
export PS1='\[\033[01;31m\]\u\[\033[01;33m\]@\[\033[01;32m\]\h \[\033[01;33m\]\w \[\033[01;35m\]\$ \[\033[00m\]'
@@ -797,45 +806,9 @@ CONF
echo "========running packages upgrade==========="
chroot_execute "apt upgrade --yes"
chroot_execute "apt purge cryptsetup* --yes"
echo "===========add static route to initramfs via hook to add default routes for Hetzner due to Debian/Ubuntu initramfs DHCP bug ========="
mkdir -p "$c_zfs_mount_dir/usr/share/initramfs-tools/scripts/init-premount"
cat > "$c_zfs_mount_dir/usr/share/initramfs-tools/scripts/init-premount/static-route" <<'CONF'
#!/bin/sh
PREREQ=""
prereqs()
{
echo "$PREREQ"
}
case $1 in
prereqs)
prereqs
exit 0
;;
esac
. /scripts/functions
# Begin real processing below this line
configure_networking
ip route add 172.31.1.1/255.255.255.255 dev eth0
ip route add default via 172.31.1.1 dev eth0
CONF
cat > "$c_zfs_mount_dir/etc/network/interfaces" <<'CONF'
auto lo
iface lo inet loopback
iface lo inet6 loopback
allow-hotplug eth0
iface eth0 inet dhcp
iface eth0 inet6 dhcp
CONF
chmod 755 "$c_zfs_mount_dir/etc/network/interfaces"
#echo "===========add static route to initramfs via hook to add default routes due to initramfs DHCP bug ========="
# removed
echo "======= update initramfs =========="
chroot_execute "update-initramfs -u -k all"
@@ -852,7 +825,7 @@ fi
echo "======= setting mountpoints =========="
chroot_execute "zfs set mountpoint=legacy $v_bpool_name/BOOT/debian"
chroot_execute "echo $v_bpool_name/BOOT/debian /boot zfs nodev,relatime,x-systemd.requires=zfs-mount.service,x-systemd.device-timeout=10 0 0 > /etc/fstab"
chroot_execute "echo $v_bpool_name/BOOT/debian /boot zfs nodev,relatime,x-systemd.requires=zfs-import-bpool.service 0 0 > /etc/fstab"
chroot_execute "zfs set mountpoint=legacy $v_rpool_name/var/log"
chroot_execute "echo $v_rpool_name/var/log /var/log zfs nodev,relatime 0 0 >> /etc/fstab"
@@ -864,9 +837,7 @@ chroot_execute "zfs set mountpoint=legacy $v_rpool_name/tmp"
chroot_execute "echo $v_rpool_name/tmp /tmp zfs nodev,relatime 0 0 >> /etc/fstab"
echo "========= add swap, if defined"
if [[ $v_swap_size -gt 0 ]]; then
chroot_execute "echo /dev/zvol/$v_rpool_name/swap none swap discard 0 0 >> /etc/fstab"
fi
[[ $v_swap_size -gt 0 ]] && chroot_execute "echo /dev/zvol/$v_rpool_name/swap none swap discard 0 0 >> /etc/fstab" || true
chroot_execute "echo RESUME=none > /etc/initramfs-tools/conf.d/resume"
echo "======= unmounting filesystems and zfs pools =========="

View File

@@ -17,8 +17,6 @@ set -o errexit
set -o pipefail
set -o nounset
export TMPDIR=/tmp
# Variables
v_bpool_name=
v_bpool_tweaks=
@@ -64,10 +62,7 @@ function print_step_info_header {
###############################################################################
# ${FUNCNAME[1]}"
if [[ "${1:-}" != "" ]]; then
echo -n " $1"
fi
[[ "${1:-}" != "" ]] && echo -n " $1" || true
echo "
###############################################################################
@@ -146,13 +141,13 @@ function initial_load_debian_zed_cache {
local success=0
if [[ ! -e /mnt/etc/zfs/zfs-list.cache/rpool ]] || [[ -e /mnt/etc/zfs/zfs-list.cache/rpool && (( $(find /mnt/etc/zfs/zfs-list.cache/rpool -type f -printf '%s' 2> /dev/null) == 0 )) ]]; then
if [[ ! -e /mnt/etc/zfs/zfs-list.cache/rpool ]] || [[ -e /mnt/etc/zfs/zfs-list.cache/rpool && (( $(ls -l /mnt/etc/zfs/zfs-list.cache/rpool 2> /dev/null | cut -d ' ' -f 5) == 0 )) ]]; then
chroot_execute "zfs set canmount=noauto rpool"
SECONDS=0
while (( SECONDS++ <= 120 )); do
if [[ -e /mnt/etc/zfs/zfs-list.cache/rpool ]] && (( $(find /mnt/etc/zfs/zfs-list.cache/rpool -type f -printf '%s' 2> /dev/null) > 0 )); then
if [[ -e /mnt/etc/zfs/zfs-list.cache/rpool ]] && (( "$(ls -l /mnt/etc/zfs/zfs-list.cache/rpool | cut -d ' ' -f 5)" > 0 )); then
success=1
break
else
@@ -384,7 +379,7 @@ function ask_hostname {
local hostname_invalid_message=
while [[ ! $v_hostname =~ ^[a-z][a-zA-Z0-9_:.-]+$ ]]; do
while [[ ! $v_hostname =~ ^[a-z][a-zA-Z_:.-]+$ ]]; do
v_hostname=$(dialog --inputbox "${hostname_invalid_message}Set the host name" 30 100 "$c_default_hostname" 3>&1 1>&2 2>&3)
hostname_invalid_message="Invalid host name! "
@@ -437,7 +432,8 @@ function unmount_and_export_fs {
echo "===========exporting zfs pools============="
set +e
while (( zpools_exported == 99 )) && (( SECONDS++ <= 60 )); do
if zpool export -a 2> /dev/null; then
zpool export -a 2> /dev/null
if [[ $? == 0 ]]; then
zpools_exported=1
echo "all zfs pools were succesfully exported"
break;
@@ -496,19 +492,27 @@ done
echo "======= installing zfs on rescue system =========="
echo "zfs-dkms zfs-dkms/note-incompatible-licenses note true" | debconf-set-selections
# echo "y" | zfs
# linux-headers-generic linux-image-generic
apt install --yes software-properties-common dpkg-dev dkms
rm -f "$(which zfs)"
rm -f "$(which zpool)"
echo -e "deb http://deb.debian.org/debian/ testing main contrib non-free\ndeb http://deb.debian.org/debian/ testing main contrib non-free\n" >/etc/apt/sources.list.d/bookworm-testing.list
echo -e "Package: src:zfs-linux\nPin: release n=testing\nPin-Priority: 990\n" > /etc/apt/preferences.d/90_zfs
if [[ $v_zfs_experimental == "1" ]]; then
apt install --yes man wget curl software-properties-common nano htop gnupg
wget -O - https://terem42.github.io/zfs-debian/apt_pub.gpg | apt-key add -
add-apt-repository 'deb https://terem42.github.io/zfs-debian/public zfs-debian-experimental main'
apt update
apt install -t testing --yes zfs-dkms zfsutils-linux
rm /etc/apt/sources.list.d/bookworm-testing.list
rm /etc/apt/preferences.d/90_zfs
apt install --yes libelf-dev
apt install -t zfs-debian-experimental --yes zfs-dkms zfsutils-linux
else
cd "$(mktemp -d)"
wget "$(curl -Ls https://api.github.com/repos/openzfs/zfs/releases/latest| grep "browser_download_url.*tar.gz"|grep -E "tar.gz\"$"| cut -d '"' -f 4)"
apt update
export PATH=$PATH:/usr/sbin
apt install libssl-dev uuid-dev zlib1g-dev libblkid-dev -y
tar xfv zfs*.tar.gz
rm *.tar.gz
cd zfs*
./configure
make -j "$(nproc)"
make install
ldconfig
modprobe zfs
fi
zfs --version
echo "======= partitioning the disk =========="
@@ -522,7 +526,7 @@ echo "======= partitioning the disk =========="
for selected_disk in "${v_selected_disks[@]}"; do
wipefs --all --force "$selected_disk"
sgdisk -a1 -n1:24K:+1000K -t1:EF02 "$selected_disk"
sgdisk -n2:0:+2G -t2:BF01 "$selected_disk" # Boot pool
sgdisk -n2:0:+512M -t2:BF01 "$selected_disk" # Boot pool
sgdisk -n3:0:"$tail_space_parameter" -t3:BF01 "$selected_disk" # Root pool
done
@@ -549,17 +553,13 @@ echo "======= create zfs pools and datasets =========="
pools_mirror_option=
fi
# shellcheck disable=SC2086
zpool create \
$v_bpool_tweaks -O canmount=off -O devices=off \
-o cachefile=/etc/zpool.cache \
-O mountpoint=/boot -R $c_zfs_mount_dir -f \
$v_bpool_name $pools_mirror_option "${bpool_disks_partitions[@]}"
# shellcheck disable=SC2086
echo -n "$v_passphrase" | zpool create \
$v_rpool_tweaks \
-o cachefile=/etc/zpool.cache \
"${encryption_options[@]}" \
-O mountpoint=/ -R $c_zfs_mount_dir -f \
$v_rpool_name $pools_mirror_option "${rpool_disks_partitions[@]}"
@@ -574,7 +574,9 @@ zfs create -o canmount=noauto -o mountpoint=/boot "$v_bpool_name/BOOT/debian"
zfs mount "$v_bpool_name/BOOT/debian"
zfs create "$v_rpool_name/home"
zfs create -o mountpoint=/root "$v_rpool_name/home/root"
zfs create -o canmount=off "$v_rpool_name/var"
zfs create -o canmount=off "$v_rpool_name/var/lib"
zfs create "$v_rpool_name/var/log"
zfs create "$v_rpool_name/var/spool"
@@ -625,7 +627,7 @@ ff02::2 ip6-allrouters
ff02::3 ip6-allhosts
CONF
ip6addr_prefix=$(ip -6 a s | grep -E "inet6.+global" | sed -nE 's/.+inet6\s(([0-9a-z]{1,4}:){4,4}).+/\1/p' | head -n 1)
ip6addr_prefix=$(ip -6 a s | grep -E "inet6.+global" | sed -nE 's/.+inet6\s(([0-9a-z]{1,4}:){4,4}).+/\1/p')
cat <<CONF > /mnt/etc/systemd/network/10-eth0.network
[Match]
@@ -637,9 +639,9 @@ Address=${ip6addr_prefix}:1/64
Gateway=fe80::1
CONF
chroot_execute "systemctl enable systemd-networkd.service"
chroot_execute "systemctl enable systemd-resolved.service"
#cp /etc/resolv.conf $c_zfs_mount_dir/etc/resolv.conf
cp /etc/resolv.conf $c_zfs_mount_dir/etc/resolv.conf
echo "======= preparing the jail for chroot =========="
for virtual_fs_dir in proc sys dev; do
@@ -722,14 +724,8 @@ if [[ $v_zfs_experimental == "1" ]]; then
chroot_execute "apt update"
chroot_execute "apt install -t zfs-debian-experimental --yes zfs-initramfs zfs-dkms zfsutils-linux"
else
chroot_execute "apt install -t bullseye-backports --yes zfs-initramfs zfs-dkms zfsutils-linux"
chroot_execute "apt install --yes zfs-initramfs zfs-dkms zfsutils-linux"
fi
chroot_execute 'cat << DKMS > /etc/dkms/zfs.conf
# override for /usr/src/zfs-*/dkms.conf:
# always rebuild initrd when zfs module has been changed
# (either by a ZFS update or a new kernel version)
REMAKE_INITRD="yes"
DKMS'
echo "======= installing OpenSSH and network tooling =========="
chroot_execute "apt install --yes openssh-server net-tools"
@@ -745,9 +741,25 @@ chroot_execute "dpkg-reconfigure openssh-server -f noninteractive"
echo "======= set root password =========="
chroot_execute "echo root:$(printf "%q" "$v_root_password") | chpasswd"
echo "======= setting up zfs cache =========="
echo "======= setting up zfs services =========="
chroot_execute "cat > /etc/systemd/system/zfs-import-bpool.service <<UNIT
[Unit]
DefaultDependencies=no
Before=zfs-import-scan.service
Before=zfs-import-cache.service
cp /etc/zpool.cache /mnt/etc/zfs/zpool.cache
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStartPre=/bin/sh -c '[ -f /etc/zfs/zpool.cache ] && mv /etc/zfs/zpool.cache /etc/zfs/preboot_zpool.cache || true'
ExecStart=/sbin/zpool import -N -o cachefile=none -d /dev/disk/by-id $v_bpool_name
ExecStartPost=/bin/sh -c '[ -f /etc/zfs/preboot_zpool.cache ] && mv /etc/zfs/preboot_zpool.cache /etc/zfs/zpool.cache || true'
[Install]
WantedBy=zfs-import.target
UNIT"
chroot_execute "systemctl enable zfs-import-bpool.service"
echo "========setting up zfs module parameters========"
chroot_execute "echo options zfs zfs_arc_max=$((v_zfs_arc_max_mb * 1024 * 1024)) >> /etc/modprobe.d/zfs.conf"
@@ -775,21 +787,20 @@ if [[ $v_encrypt_rpool == "1" ]]; then
chroot_execute "apt install --yes dropbear-initramfs"
mkdir -p "$c_zfs_mount_dir/etc/dropbear/initramfs"
cp /root/.ssh/authorized_keys "$c_zfs_mount_dir/etc/dropbear/initramfs/authorized_keys"
cp /root/.ssh/authorized_keys "$c_zfs_mount_dir/etc/dropbear-initramfs/authorized_keys"
cp "$c_zfs_mount_dir/etc/ssh/ssh_host_rsa_key" "$c_zfs_mount_dir/etc/ssh/ssh_host_rsa_key_temp"
chroot_execute "ssh-keygen -p -i -m pem -N '' -f /etc/ssh/ssh_host_rsa_key_temp"
chroot_execute "/usr/lib/dropbear/dropbearconvert openssh dropbear /etc/ssh/ssh_host_rsa_key_temp /etc/dropbear/initramfs/dropbear_rsa_host_key"
chroot_execute "/usr/lib/dropbear/dropbearconvert openssh dropbear /etc/ssh/ssh_host_rsa_key_temp /etc/dropbear-initramfs/dropbear_rsa_host_key"
rm -rf "$c_zfs_mount_dir/etc/ssh/ssh_host_rsa_key_temp"
cp "$c_zfs_mount_dir/etc/ssh/ssh_host_ecdsa_key" "$c_zfs_mount_dir/etc/ssh/ssh_host_ecdsa_key_temp"
chroot_execute "ssh-keygen -p -i -m pem -N '' -f /etc/ssh/ssh_host_ecdsa_key_temp"
chroot_execute "/usr/lib/dropbear/dropbearconvert openssh dropbear /etc/ssh/ssh_host_ecdsa_key_temp /etc/dropbear/initramfs/dropbear_ecdsa_host_key"
chroot_execute "/usr/lib/dropbear/dropbearconvert openssh dropbear /etc/ssh/ssh_host_ecdsa_key_temp /etc/dropbear-initramfs/dropbear_ecdsa_host_key"
chroot_execute "rm -rf /etc/ssh/ssh_host_ecdsa_key_temp"
rm -rf "$c_zfs_mount_dir/etc/ssh/ssh_host_ecdsa_key_temp"
rm -rf "$c_zfs_mount_dir/etc/dropbear/initramfs/dropbear_dss_host_key"
rm -rf "$c_zfs_mount_dir/etc/dropbear-initramfs/dropbear_dss_host_key"
fi
echo "============setup root prompt============"
@@ -802,7 +813,6 @@ CONF
echo "========running packages upgrade==========="
chroot_execute "apt upgrade --yes"
chroot_execute "apt purge cryptsetup* --yes"
echo "===========add static route to initramfs via hook to add default routes for Hetzner due to Debian/Ubuntu initramfs DHCP bug ========="
mkdir -p "$c_zfs_mount_dir/usr/share/initramfs-tools/scripts/init-premount"
@@ -826,14 +836,12 @@ esac
configure_networking
ip route add 172.31.1.1/255.255.255.255 dev eth0
ip route add default via 172.31.1.1 dev eth0
ip route add 172.31.1.1/255.255.255.255 dev ens3
ip route add default via 172.31.1.1 dev ens3
CONF
chmod 755 "$c_zfs_mount_dir/usr/share/initramfs-tools/scripts/init-premount/static-route"
chmod 755 "$c_zfs_mount_dir/etc/network/interfaces"
echo "======= update initramfs =========="
chroot_execute "update-initramfs -u -k all"
@@ -849,7 +857,7 @@ fi
echo "======= setting mountpoints =========="
chroot_execute "zfs set mountpoint=legacy $v_bpool_name/BOOT/debian"
chroot_execute "echo $v_bpool_name/BOOT/debian /boot zfs nodev,relatime,x-systemd.requires=zfs-mount.service,x-systemd.device-timeout=10 0 0 > /etc/fstab"
chroot_execute "echo $v_bpool_name/BOOT/debian /boot zfs nodev,relatime,x-systemd.requires=zfs-import-bpool.service 0 0 > /etc/fstab"
chroot_execute "zfs set mountpoint=legacy $v_rpool_name/var/log"
chroot_execute "echo $v_rpool_name/var/log /var/log zfs nodev,relatime 0 0 >> /etc/fstab"
@@ -861,10 +869,7 @@ chroot_execute "zfs set mountpoint=legacy $v_rpool_name/tmp"
chroot_execute "echo $v_rpool_name/tmp /tmp zfs nodev,relatime 0 0 >> /etc/fstab"
echo "========= add swap, if defined"
if [[ $v_swap_size -gt 0 ]]; then
chroot_execute "echo /dev/zvol/$v_rpool_name/swap none swap discard 0 0 >> /etc/fstab"
fi
[[ $v_swap_size -gt 0 ]] && chroot_execute "echo /dev/zvol/$v_rpool_name/swap none swap discard 0 0 >> /etc/fstab" || true
chroot_execute "echo RESUME=none > /etc/initramfs-tools/conf.d/resume"
echo "======= unmounting filesystems and zfs pools =========="

View File

@@ -1,879 +0,0 @@
#!/bin/bash
: <<'end_header_info'
(c) Andrey Prokopenko job@terem.fr
fully automatic script to install Debian 11 with ZFS root on Hetzner VPS
WARNING: all data on the disk will be destroyed
How to use: add SSH key to the rescue console, set it OS to linux64, then press "mount rescue and power cycle" button
Next, connect via SSH to console, and run the script
Answer script questions about desired hostname, ZFS ARC cache size et cetera
To cope with network failures its higly recommended to run the script inside screen console
screen -dmS zfs
screen -r zfs
To detach from screen console, hit Ctrl-d then a
end_header_info
set -o errexit
set -o pipefail
set -o nounset
export TMPDIR=/tmp
# Variables
v_bpool_name=
v_bpool_tweaks=
v_rpool_name=
v_rpool_tweaks=
declare -a v_selected_disks
v_swap_size= # integer
v_free_tail_space= # integer
v_hostname=
v_kernel_variant=
v_zfs_arc_max_mb=
v_root_password=
v_encrypt_rpool= # 0=false, 1=true
v_passphrase=
v_zfs_experimental=
v_suitable_disks=()
# Constants
c_deb_packages_repo=https://deb.debian.org/debian
c_deb_security_repo=https://deb.debian.org/debian-security
c_default_zfs_arc_max_mb=250
c_default_bpool_tweaks="-o ashift=12 -O compression=lz4"
c_default_rpool_tweaks="-o ashift=12 -O acltype=posixacl -O compression=zstd-9 -O dnodesize=auto -O relatime=on -O xattr=sa -O normalization=formD"
c_default_hostname=terem
c_zfs_mount_dir=/mnt
c_log_dir=$(dirname "$(mktemp)")/zfs-hetzner-vm
c_install_log=$c_log_dir/install.log
c_lsb_release_log=$c_log_dir/lsb_release.log
c_disks_log=$c_log_dir/disks.log
function activate_debug {
mkdir -p "$c_log_dir"
exec 5> "$c_install_log"
BASH_XTRACEFD="5"
set -x
}
# shellcheck disable=SC2120
function print_step_info_header {
echo -n "
###############################################################################
# ${FUNCNAME[1]}"
if [[ "${1:-}" != "" ]]; then
echo -n " $1"
fi
echo "
###############################################################################
"
}
function print_variables {
for variable_name in "$@"; do
declare -n variable_reference="$variable_name"
echo -n "$variable_name:"
case "$(declare -p "$variable_name")" in
"declare -a"* )
for entry in "${variable_reference[@]}"; do
echo -n " \"$entry\""
done
;;
"declare -A"* )
for key in "${!variable_reference[@]}"; do
echo -n " $key=\"${variable_reference[$key]}\""
done
;;
* )
echo -n " $variable_reference"
;;
esac
echo
done
echo
}
function display_intro_banner {
# shellcheck disable=SC2119
print_step_info_header
local dialog_message='Hello!
This script will prepare the ZFS pools, then install and configure minimal Debian 11 with ZFS root on Hetzner hosting VPS instance
The script with minimal changes may be used on any other hosting provider supporting KVM virtualization and offering Debian-based rescue system.
In order to stop the procedure, hit Esc twice during dialogs (excluding yes/no ones), or Ctrl+C while any operation is running.
'
dialog --msgbox "$dialog_message" 30 100
}
function store_os_distro_information {
# shellcheck disable=SC2119
print_step_info_header
lsb_release --all > "$c_lsb_release_log"
}
function check_prerequisites {
# shellcheck disable=SC2119
print_step_info_header
if [[ $(id -u) -ne 0 ]]; then
echo 'This script must be run with administrative privileges!'
exit 1
fi
if [[ ! -r /root/.ssh/authorized_keys ]]; then
echo "SSH pubkey file is absent, please add it to the rescue system setting, then reboot into rescue system and run the script"
exit 1
fi
if ! dpkg-query --showformat="\${Status}" -W dialog 2> /dev/null | grep -q "install ok installed"; then
apt install --yes dialog
fi
}
function initial_load_debian_zed_cache {
chroot_execute "mkdir /etc/zfs/zfs-list.cache"
chroot_execute "touch /etc/zfs/zfs-list.cache/rpool"
chroot_execute "ln -sf /usr/lib/zfs-linux/zed.d/history_event-zfs-list-cacher.sh /etc/zfs/zed.d/"
chroot_execute "zed -F &"
local success=0
if [[ ! -e /mnt/etc/zfs/zfs-list.cache/rpool ]] || [[ -e /mnt/etc/zfs/zfs-list.cache/rpool && (( $(find /mnt/etc/zfs/zfs-list.cache/rpool -type f -printf '%s' 2> /dev/null) == 0 )) ]]; then
chroot_execute "zfs set canmount=noauto rpool"
SECONDS=0
while (( SECONDS++ <= 120 )); do
if [[ -e /mnt/etc/zfs/zfs-list.cache/rpool ]] && (( $(find /mnt/etc/zfs/zfs-list.cache/rpool -type f -printf '%s' 2> /dev/null) > 0 )); then
success=1
break
else
sleep 1
fi
done
else
success=1
fi
if (( success != 1 )); then
echo "Fatal zed daemon error: the ZFS cache hasn't been updated by ZED!"
exit 1
fi
chroot_execute "pkill zed"
sed -Ei 's|/mnt/?|/|g' /mnt/etc/zfs/zfs-list.cache/rpool
}
function find_suitable_disks {
# shellcheck disable=SC2119
print_step_info_header
udevadm trigger
# shellcheck disable=SC2012
ls -l /dev/disk/by-id | tail -n +2 | perl -lane 'print "@F[8..10]"' > "$c_disks_log"
local candidate_disk_ids
local mounted_devices
candidate_disk_ids=$(find /dev/disk/by-id -regextype awk -regex '.+/(ata|nvme|scsi)-.+' -not -regex '.+-part[0-9]+$' | sort)
mounted_devices="$(df | awk 'BEGIN {getline} {print $1}' | xargs -n 1 lsblk -no pkname 2> /dev/null | sort -u || true)"
while read -r disk_id || [[ -n "$disk_id" ]]; do
local device_info
device_info="$(udevadm info --query=property "$(readlink -f "$disk_id")")"
block_device_basename="$(basename "$(readlink -f "$disk_id")")"
if ! grep -q '^ID_TYPE=cd$' <<< "$device_info"; then
if ! grep -q "^$block_device_basename\$" <<< "$mounted_devices"; then
v_suitable_disks+=("$disk_id")
fi
fi
cat >> "$c_disks_log" << LOG
## DEVICE: $disk_id ################################
$(udevadm info --query=property "$(readlink -f "$disk_id")")
LOG
done < <(echo -n "$candidate_disk_ids")
if [[ ${#v_suitable_disks[@]} -eq 0 ]]; then
local dialog_message='No suitable disks have been found!
If you think this is a bug, please open an issue on https://github.com/terem42/zfs-hetzner-vm/issues, and attach the file `'"$c_disks_log"'`.
'
dialog --msgbox "$dialog_message" 30 100
exit 1
fi
print_variables v_suitable_disks
}
function select_disks {
# shellcheck disable=SC2119
print_step_info_header
while true; do
local menu_entries_option=()
if [[ ${#v_suitable_disks[@]} -eq 1 ]]; then
local disk_selection_status=ON
else
local disk_selection_status=OFF
fi
for disk_id in "${v_suitable_disks[@]}"; do
menu_entries_option+=("$disk_id" "($block_device_basename)" "$disk_selection_status")
done
local dialog_message="Select the ZFS devices (multiple selections can be in mirror or strip).
Devices with mounted partitions, cdroms, and removable devices are not displayed!
"
mapfile -t v_selected_disks < <(dialog --separate-output --checklist "$dialog_message" 30 100 $((${#menu_entries_option[@]} / 3)) "${menu_entries_option[@]}" 3>&1 1>&2 2>&3)
if [[ ${#v_selected_disks[@]} -gt 0 ]]; then
break
fi
done
print_variables v_selected_disks
}
function ask_swap_size {
# shellcheck disable=SC2119
print_step_info_header
local swap_size_invalid_message=
while [[ ! $v_swap_size =~ ^[0-9]+$ ]]; do
v_swap_size=$(dialog --inputbox "${swap_size_invalid_message}Enter the swap size in GiB (0 for no swap):" 30 100 2 3>&1 1>&2 2>&3)
swap_size_invalid_message="Invalid swap size! "
done
print_variables v_swap_size
}
function ask_free_tail_space {
# shellcheck disable=SC2119
print_step_info_header
local tail_space_invalid_message=
while [[ ! $v_free_tail_space =~ ^[0-9]+$ ]]; do
v_free_tail_space=$(dialog --inputbox "${tail_space_invalid_message}Enter the space to leave at the end of each disk (0 for none):" 30 100 0 3>&1 1>&2 2>&3)
tail_space_invalid_message="Invalid size! "
done
print_variables v_free_tail_space
}
function ask_zfs_arc_max_size {
# shellcheck disable=SC2119
print_step_info_header
local zfs_arc_max_invalid_message=
while [[ ! $v_zfs_arc_max_mb =~ ^[0-9]+$ ]]; do
v_zfs_arc_max_mb=$(dialog --inputbox "${zfs_arc_max_invalid_message}Enter ZFS ARC cache max size in Mb (minimum 64Mb, enter 0 for ZFS default value, the default will take up to 50% of memory):" 30 100 "$c_default_zfs_arc_max_mb" 3>&1 1>&2 2>&3)
zfs_arc_max_invalid_message="Invalid size! "
done
print_variables v_zfs_arc_max_mb
}
function ask_pool_names {
# shellcheck disable=SC2119
print_step_info_header
local bpool_name_invalid_message=
while [[ ! $v_bpool_name =~ ^[a-z][a-zA-Z_:.-]+$ ]]; do
v_bpool_name=$(dialog --inputbox "${bpool_name_invalid_message}Insert the name for the boot pool" 30 100 bpool 3>&1 1>&2 2>&3)
bpool_name_invalid_message="Invalid pool name! "
done
local rpool_name_invalid_message=
while [[ ! $v_rpool_name =~ ^[a-z][a-zA-Z_:.-]+$ ]]; do
v_rpool_name=$(dialog --inputbox "${rpool_name_invalid_message}Insert the name for the root pool" 30 100 rpool 3>&1 1>&2 2>&3)
rpool_name_invalid_message="Invalid pool name! "
done
print_variables v_bpool_name v_rpool_name
}
function ask_pool_tweaks {
# shellcheck disable=SC2119
print_step_info_header
v_bpool_tweaks=$(dialog --inputbox "Insert the tweaks for the boot pool" 30 100 -- "$c_default_bpool_tweaks" 3>&1 1>&2 2>&3)
v_rpool_tweaks=$(dialog --inputbox "Insert the tweaks for the root pool" 30 100 -- "$c_default_rpool_tweaks" 3>&1 1>&2 2>&3)
print_variables v_bpool_tweaks v_rpool_tweaks
}
function ask_root_password {
# shellcheck disable=SC2119
print_step_info_header
set +x
local password_invalid_message=
local password_repeat=-
while [[ "$v_root_password" != "$password_repeat" || "$v_root_password" == "" ]]; do
v_root_password=$(dialog --passwordbox "${password_invalid_message}Please enter the root account password (can't be empty):" 30 100 3>&1 1>&2 2>&3)
password_repeat=$(dialog --passwordbox "Please repeat the password:" 30 100 3>&1 1>&2 2>&3)
password_invalid_message="Passphrase empty, or not matching! "
done
set -x
}
function ask_encryption {
print_step_info_header
if dialog --defaultno --yesno 'Do you want to encrypt the root pool?' 30 100; then
v_encrypt_rpool=1
fi
set +x
if [[ $v_encrypt_rpool == "1" ]]; then
local passphrase_invalid_message=
local passphrase_repeat=-
while [[ "$v_passphrase" != "$passphrase_repeat" || ${#v_passphrase} -lt 8 ]]; do
v_passphrase=$(dialog --passwordbox "${passphrase_invalid_message}Please enter the passphrase for the root pool (8 chars min.):" 30 100 3>&1 1>&2 2>&3)
passphrase_repeat=$(dialog --passwordbox "Please repeat the passphrase:" 30 100 3>&1 1>&2 2>&3)
passphrase_invalid_message="Passphrase too short, or not matching! "
done
fi
set -x
}
function ask_zfs_experimental {
print_step_info_header
if dialog --defaultno --yesno 'Do you want to use experimental zfs module build?' 30 100; then
v_zfs_experimental=1
fi
}
function ask_hostname {
# shellcheck disable=SC2119
print_step_info_header
local hostname_invalid_message=
while [[ ! $v_hostname =~ ^[a-z][a-zA-Z0-9_:.-]+$ ]]; do
v_hostname=$(dialog --inputbox "${hostname_invalid_message}Set the host name" 30 100 "$c_default_hostname" 3>&1 1>&2 2>&3)
hostname_invalid_message="Invalid host name! "
done
print_variables v_hostname
}
function determine_kernel_variant {
if dmidecode | grep -q vServer; then
v_kernel_variant="-cloud"
fi
}
function chroot_execute {
chroot $c_zfs_mount_dir bash -c "$1"
}
function unmount_and_export_fs {
# shellcheck disable=SC2119
print_step_info_header
for virtual_fs_dir in dev sys proc; do
umount --recursive --force --lazy "$c_zfs_mount_dir/$virtual_fs_dir"
done
local max_unmount_wait=5
echo -n "Waiting for virtual filesystems to unmount "
SECONDS=0
for virtual_fs_dir in dev sys proc; do
while mountpoint -q "$c_zfs_mount_dir/$virtual_fs_dir" && [[ $SECONDS -lt $max_unmount_wait ]]; do
sleep 0.5
echo -n .
done
done
echo
for virtual_fs_dir in dev sys proc; do
if mountpoint -q "$c_zfs_mount_dir/$virtual_fs_dir"; then
echo "Re-issuing umount for $c_zfs_mount_dir/$virtual_fs_dir"
umount --recursive --force --lazy "$c_zfs_mount_dir/$virtual_fs_dir"
fi
done
SECONDS=0
zpools_exported=99
echo "===========exporting zfs pools============="
set +e
while (( zpools_exported == 99 )) && (( SECONDS++ <= 60 )); do
if zpool export -a 2> /dev/null; then
zpools_exported=1
echo "all zfs pools were succesfully exported"
break;
else
sleep 1
fi
done
set -e
if (( zpools_exported != 1 )); then
echo "failed to export zfs pools"
exit 1
fi
}
#################### MAIN ################################
export LC_ALL=en_US.UTF-8
export NCURSES_NO_UTF8_ACS=1
check_prerequisites
activate_debug
display_intro_banner
find_suitable_disks
select_disks
ask_swap_size
ask_free_tail_space
ask_pool_names
ask_pool_tweaks
ask_encryption
ask_zfs_arc_max_size
ask_zfs_experimental
ask_root_password
ask_hostname
determine_kernel_variant
clear
echo "===========remove unused kernels in rescue system========="
for kver in $(find /lib/modules/* -maxdepth 0 -type d | grep -v "$(uname -r)" | cut -s -d "/" -f 4); do
apt purge --yes "linux-headers-$kver"
apt purge --yes "linux-image-$kver"
done
echo "======= installing zfs on rescue system =========="
echo "zfs-dkms zfs-dkms/note-incompatible-licenses note true" | debconf-set-selections
# echo "y" | zfs
# linux-headers-generic linux-image-generic
apt install --yes software-properties-common dpkg-dev dkms
rm -f "$(which zfs)"
rm -f "$(which zpool)"
echo -e "deb http://deb.debian.org/debian/ testing main contrib non-free\ndeb http://deb.debian.org/debian/ testing main contrib non-free\n" >/etc/apt/sources.list.d/bookworm-testing.list
echo -e "Package: src:zfs-linux\nPin: release n=testing\nPin-Priority: 990\n" > /etc/apt/preferences.d/90_zfs
apt update
apt install -t testing --yes zfs-dkms zfsutils-linux
rm /etc/apt/sources.list.d/bookworm-testing.list
rm /etc/apt/preferences.d/90_zfs
apt update
export PATH=$PATH:/usr/sbin
zfs --version
echo "======= partitioning the disk =========="
if [[ $v_free_tail_space -eq 0 ]]; then
tail_space_parameter=0
else
tail_space_parameter="-${v_free_tail_space}G"
fi
for selected_disk in "${v_selected_disks[@]}"; do
wipefs --all --force "$selected_disk"
sgdisk -a1 -n1:24K:+1000K -t1:EF02 "$selected_disk"
sgdisk -n2:0:+2G -t2:BF01 "$selected_disk" # Boot pool
sgdisk -n3:0:"$tail_space_parameter" -t3:BF01 "$selected_disk" # Root pool
done
udevadm settle
echo "======= create zfs pools and datasets =========="
encryption_options=()
rpool_disks_partitions=()
bpool_disks_partitions=()
if [[ $v_encrypt_rpool == "1" ]]; then
encryption_options=(-O "encryption=aes-256-gcm" -O "keylocation=prompt" -O "keyformat=passphrase")
fi
for selected_disk in "${v_selected_disks[@]}"; do
rpool_disks_partitions+=("${selected_disk}-part3")
bpool_disks_partitions+=("${selected_disk}-part2")
done
pools_mirror_option=
if [[ ${#v_selected_disks[@]} -gt 1 ]]; then
if dialog --defaultno --yesno "Do you want to use mirror mode for ${v_selected_disks[*]}?" 30 100; then
pools_mirror_option=mirror
fi
fi
# shellcheck disable=SC2086
zpool create \
$v_bpool_tweaks -O canmount=off -O devices=off \
-o cachefile=/etc/zpool.cache \
-O mountpoint=/boot -R $c_zfs_mount_dir -f \
$v_bpool_name $pools_mirror_option "${bpool_disks_partitions[@]}"
# shellcheck disable=SC2086
echo -n "$v_passphrase" | zpool create \
$v_rpool_tweaks \
-o cachefile=/etc/zpool.cache \
"${encryption_options[@]}" \
-O mountpoint=/ -R $c_zfs_mount_dir -f \
$v_rpool_name $pools_mirror_option "${rpool_disks_partitions[@]}"
zfs create -o canmount=off -o mountpoint=none "$v_rpool_name/ROOT"
zfs create -o canmount=off -o mountpoint=none "$v_bpool_name/BOOT"
zfs create -o canmount=noauto -o mountpoint=/ "$v_rpool_name/ROOT/debian"
zfs mount "$v_rpool_name/ROOT/debian"
zfs create -o canmount=noauto -o mountpoint=/boot "$v_bpool_name/BOOT/debian"
zfs mount "$v_bpool_name/BOOT/debian"
zfs create "$v_rpool_name/home"
#zfs create -o mountpoint=/root "$v_rpool_name/home/root"
zfs create -o canmount=off "$v_rpool_name/var"
zfs create "$v_rpool_name/var/log"
zfs create "$v_rpool_name/var/spool"
zfs create -o com.sun:auto-snapshot=false "$v_rpool_name/var/cache"
zfs create -o com.sun:auto-snapshot=false "$v_rpool_name/var/tmp"
chmod 1777 "$c_zfs_mount_dir/var/tmp"
zfs create "$v_rpool_name/srv"
zfs create -o canmount=off "$v_rpool_name/usr"
zfs create "$v_rpool_name/usr/local"
zfs create "$v_rpool_name/var/mail"
zfs create -o com.sun:auto-snapshot=false -o canmount=on -o mountpoint=/tmp "$v_rpool_name/tmp"
chmod 1777 "$c_zfs_mount_dir/tmp"
if [[ $v_swap_size -gt 0 ]]; then
zfs create \
-V "${v_swap_size}G" -b "$(getconf PAGESIZE)" \
-o compression=zle -o logbias=throughput -o sync=always -o primarycache=metadata -o secondarycache=none -o com.sun:auto-snapshot=false \
"$v_rpool_name/swap"
udevadm settle
mkswap -f "/dev/zvol/$v_rpool_name/swap"
fi
echo "======= setting up initial system packages =========="
debootstrap --arch=amd64 bookworm "$c_zfs_mount_dir" "$c_deb_packages_repo"
zfs set devices=off "$v_rpool_name"
echo "======= setting up the network =========="
echo "$v_hostname" > $c_zfs_mount_dir/etc/hostname
cat > "$c_zfs_mount_dir/etc/hosts" <<CONF
127.0.1.1 ${v_hostname}
127.0.0.1 localhost
# The following lines are desirable for IPv6 capable hosts
::1 ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
ff02::3 ip6-allhosts
CONF
ip6addr_prefix=$(ip -6 a s | grep -E "inet6.+global" | sed -nE 's/.+inet6\s(([0-9a-z]{1,4}:){4,4}).+/\1/p' | head -n 1)
cat <<CONF > /mnt/etc/systemd/network/10-eth0.network
[Match]
Name=eth0
[Network]
DHCP=ipv4
Address=${ip6addr_prefix}:1/64
Gateway=fe80::1
CONF
chroot_execute "systemctl enable systemd-networkd.service"
echo "======= preparing the jail for chroot =========="
for virtual_fs_dir in proc sys dev; do
mount --rbind "/$virtual_fs_dir" "$c_zfs_mount_dir/$virtual_fs_dir"
done
echo "======= setting apt repos =========="
cat > "$c_zfs_mount_dir/etc/apt/sources.list" <<CONF
deb $c_deb_packages_repo bookworm main contrib non-free non-free-firmware
deb $c_deb_packages_repo bookworm-updates main contrib non-free non-free-firmware
deb $c_deb_security_repo bookworm-security main contrib non-free non-free-firmware
deb $c_deb_packages_repo bookworm-backports main contrib non-free non-free-firmware
CONF
chroot_execute "apt update"
echo "======= setting locale, console and language =========="
chroot_execute "apt install --yes -qq locales debconf-i18n apt-utils"
sed -i 's/# en_US.UTF-8/en_US.UTF-8/' "$c_zfs_mount_dir/etc/locale.gen"
sed -i 's/# fr_FR.UTF-8/fr_FR.UTF-8/' "$c_zfs_mount_dir/etc/locale.gen"
sed -i 's/# fr_FR.UTF-8/fr_FR.UTF-8/' "$c_zfs_mount_dir/etc/locale.gen"
sed -i 's/# de_AT.UTF-8/de_AT.UTF-8/' "$c_zfs_mount_dir/etc/locale.gen"
sed -i 's/# de_DE.UTF-8/de_DE.UTF-8/' "$c_zfs_mount_dir/etc/locale.gen"
chroot_execute 'cat <<CONF | debconf-set-selections
locales locales/default_environment_locale select en_US.UTF-8
keyboard-configuration keyboard-configuration/store_defaults_in_debconf_db boolean true
keyboard-configuration keyboard-configuration/variant select German
keyboard-configuration keyboard-configuration/unsupported_layout boolean true
keyboard-configuration keyboard-configuration/modelcode string pc105
keyboard-configuration keyboard-configuration/unsupported_config_layout boolean true
keyboard-configuration keyboard-configuration/layout select German
keyboard-configuration keyboard-configuration/layoutcode string de
keyboard-configuration keyboard-configuration/optionscode string
keyboard-configuration keyboard-configuration/toggle select No toggling
keyboard-configuration keyboard-configuration/xkb-keymap select de
keyboard-configuration keyboard-configuration/switch select No temporary switch
keyboard-configuration keyboard-configuration/unsupported_config_options boolean true
keyboard-configuration keyboard-configuration/ctrl_alt_bksp boolean false
keyboard-configuration keyboard-configuration/variantcode string
keyboard-configuration keyboard-configuration/model select Generic 105-key PC (intl.)
keyboard-configuration keyboard-configuration/altgr select The default for the keyboard layout
keyboard-configuration keyboard-configuration/compose select No compose key
keyboard-configuration keyboard-configuration/unsupported_options boolean true
console-setup console-setup/fontsize-fb47 select 8x16
console-setup console-setup/store_defaults_in_debconf_db boolean true
console-setup console-setup/codeset47 select # Latin1 and Latin5 - western Europe and Turkic languages
console-setup console-setup/fontface47 select Fixed
console-setup console-setup/fontsize string 8x16
console-setup console-setup/charmap47 select UTF-8
console-setup console-setup/fontsize-text47 select 8x16
console-setup console-setup/codesetcode string Lat15
tzdata tzdata/Areas select Europe
tzdata tzdata/Zones/Europe select Vienna
grub-pc grub-pc/install_devices_empty boolean true
CONF'
chroot_execute "dpkg-reconfigure locales -f noninteractive"
echo -e "LC_ALL=en_US.UTF-8\nLANG=en_US.UTF-8\n" >> "$c_zfs_mount_dir/etc/environment"
chroot_execute "apt install -qq --yes keyboard-configuration console-setup"
chroot_execute "dpkg-reconfigure keyboard-configuration -f noninteractive"
chroot_execute "dpkg-reconfigure console-setup -f noninteractive"
chroot_execute "setupcon"
chroot_execute "rm -f /etc/localtime /etc/timezone"
chroot_execute "dpkg-reconfigure tzdata -f noninteractive"
echo "======= installing latest kernel============="
# linux-headers-generic linux-image-generic
chroot_execute "apt install --yes linux-image${v_kernel_variant}-amd64 linux-headers${v_kernel_variant}-amd64 dpkg-dev"
echo "======= installing aux packages =========="
chroot_execute "apt install --yes man wget curl software-properties-common nano htop gnupg"
echo "======= installing zfs packages =========="
chroot_execute 'echo "zfs-dkms zfs-dkms/note-incompatible-licenses note true" | debconf-set-selections'
if [[ $v_zfs_experimental == "1" ]]; then
chroot_execute "wget -O - https://terem42.github.io/zfs-debian/apt_pub.gpg | apt-key add -"
chroot_execute "add-apt-repository 'deb https://terem42.github.io/zfs-debian/public zfs-debian-experimental main'"
chroot_execute "apt update"
chroot_execute "apt install -t zfs-debian-experimental --yes zfs-initramfs zfs-dkms zfsutils-linux"
else
chroot_execute "apt install -t bookworm-backports --yes zfs-initramfs zfs-dkms zfsutils-linux"
fi
chroot_execute 'cat << DKMS > /etc/dkms/zfs.conf
# override for /usr/src/zfs-*/dkms.conf:
# always rebuild initrd when zfs module has been changed
# (either by a ZFS update or a new kernel version)
REMAKE_INITRD="yes"
DKMS'
echo "======= installing OpenSSH and network tooling =========="
chroot_execute "apt install --yes openssh-server net-tools"
echo "======= setup OpenSSH =========="
sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/g' "$c_zfs_mount_dir/etc/ssh/sshd_config"
sed -i 's/#PubkeyAuthentication yes/PubkeyAuthentication yes/g' "$c_zfs_mount_dir/etc/ssh/sshd_config"
chroot_execute "rm /etc/ssh/ssh_host_*"
chroot_execute "dpkg-reconfigure openssh-server -f noninteractive"
echo "======= set root password =========="
chroot_execute "echo root:$(printf "%q" "$v_root_password") | chpasswd"
echo "======= setting up zfs cache =========="
cp /etc/zpool.cache /mnt/etc/zfs/zpool.cache
echo "========setting up zfs module parameters========"
chroot_execute "echo options zfs zfs_arc_max=$((v_zfs_arc_max_mb * 1024 * 1024)) >> /etc/modprobe.d/zfs.conf"
echo "======= setting up grub =========="
chroot_execute "echo 'grub-pc grub-pc/install_devices_empty boolean true' | debconf-set-selections"
chroot_execute "DEBIAN_FRONTEND=noninteractive apt install --yes grub-legacy"
chroot_execute "DEBIAN_FRONTEND=noninteractive apt install --yes grub-pc"
chroot_execute "grub-install --recheck ${v_selected_disks[0]}"
chroot_execute "sed -i 's/#GRUB_TERMINAL=console/GRUB_TERMINAL=console/g' /etc/default/grub"
chroot_execute "sed -i 's|GRUB_CMDLINE_LINUX_DEFAULT=.*|GRUB_CMDLINE_LINUX_DEFAULT=\"net.ifnames=0\"|' /etc/default/grub"
chroot_execute "sed -i 's|GRUB_CMDLINE_LINUX=\"\"|GRUB_CMDLINE_LINUX=\"root=ZFS=rpool/ROOT/debian\"|g' /etc/default/grub"
chroot_execute "sed -i 's/quiet//g' /etc/default/grub"
chroot_execute "sed -i 's/splash//g' /etc/default/grub"
chroot_execute "echo 'GRUB_DISABLE_OS_PROBER=true' >> /etc/default/grub"
for ((i = 1; i < ${#v_selected_disks[@]}; i++)); do
dd if="${v_selected_disks[0]}-part1" of="${v_selected_disks[i]}-part1"
done
if [[ $v_encrypt_rpool == "1" ]]; then
echo "=========set up dropbear=============="
chroot_execute "apt install --yes dropbear-initramfs"
mkdir -p "$c_zfs_mount_dir/etc/dropbear/initramfs"
cp /root/.ssh/authorized_keys "$c_zfs_mount_dir/etc/dropbear/initramfs/authorized_keys"
cp "$c_zfs_mount_dir/etc/ssh/ssh_host_rsa_key" "$c_zfs_mount_dir/etc/ssh/ssh_host_rsa_key_temp"
chroot_execute "ssh-keygen -p -i -m pem -N '' -f /etc/ssh/ssh_host_rsa_key_temp"
chroot_execute "/usr/lib/dropbear/dropbearconvert openssh dropbear /etc/ssh/ssh_host_rsa_key_temp /etc/dropbear/initramfs/dropbear_rsa_host_key"
rm -rf "$c_zfs_mount_dir/etc/ssh/ssh_host_rsa_key_temp"
cp "$c_zfs_mount_dir/etc/ssh/ssh_host_ecdsa_key" "$c_zfs_mount_dir/etc/ssh/ssh_host_ecdsa_key_temp"
chroot_execute "ssh-keygen -p -i -m pem -N '' -f /etc/ssh/ssh_host_ecdsa_key_temp"
chroot_execute "/usr/lib/dropbear/dropbearconvert openssh dropbear /etc/ssh/ssh_host_ecdsa_key_temp /etc/dropbear/initramfs/dropbear_ecdsa_host_key"
chroot_execute "rm -rf /etc/ssh/ssh_host_ecdsa_key_temp"
rm -rf "$c_zfs_mount_dir/etc/ssh/ssh_host_ecdsa_key_temp"
rm -rf "$c_zfs_mount_dir/etc/dropbear/initramfs/dropbear_dss_host_key"
fi
echo "============setup root prompt============"
cat > "$c_zfs_mount_dir/root/.bashrc" <<CONF
export PS1='\[\033[01;31m\]\u\[\033[01;33m\]@\[\033[01;32m\]\h \[\033[01;33m\]\w \[\033[01;35m\]\$ \[\033[00m\]'
umask 022
export LS_OPTIONS='--color=auto -h'
eval "\$(dircolors)"
CONF
echo "========= add root pubkey for login via SSH"
mkdir -p "$c_zfs_mount_dir/root/.ssh/"
cp /root/.ssh/authorized_keys "$c_zfs_mount_dir/root/.ssh/authorized_keys"
echo "========running packages upgrade and autoremove==========="
chroot_execute "apt upgrade --yes"
chroot_execute "apt purge cryptsetup* --yes"
echo "===========add static route to initramfs via hook to add default routes for Hetzner due to Debian/Ubuntu initramfs DHCP bug ========="
mkdir -p "$c_zfs_mount_dir/usr/share/initramfs-tools/scripts/init-premount"
cat > "$c_zfs_mount_dir/usr/share/initramfs-tools/scripts/init-premount/static-route" <<'CONF'
#!/bin/sh
PREREQ=""
prereqs()
{
echo "$PREREQ"
}
case $1 in
prereqs)
prereqs
exit 0
;;
esac
. /scripts/functions
# Begin real processing below this line
configure_networking
ip route add 172.31.1.1/255.255.255.255 dev eth0
ip route add default via 172.31.1.1 dev eth0
CONF
chmod 755 "$c_zfs_mount_dir/usr/share/initramfs-tools/scripts/init-premount/static-route"
chmod 755 "$c_zfs_mount_dir/etc/network/interfaces"
echo "======= update initramfs =========="
chroot_execute "update-initramfs -u -k all"
chroot_execute "apt remove cryptsetup* --yes"
echo "======= update grub =========="
chroot_execute "update-grub"
echo "======= setting up zed =========="
if [[ $v_zfs_experimental == "1" ]]; then
chroot_execute "zfs set canmount=noauto rpool"
else
initial_load_debian_zed_cache
fi
echo "======= setting mountpoints =========="
chroot_execute "zfs set mountpoint=legacy $v_bpool_name/BOOT/debian"
chroot_execute "echo $v_bpool_name/BOOT/debian /boot zfs nodev,relatime,x-systemd.requires=zfs-mount.service,x-systemd.device-timeout=10 0 0 > /etc/fstab"
chroot_execute "zfs set mountpoint=legacy $v_rpool_name/var/log"
chroot_execute "echo $v_rpool_name/var/log /var/log zfs nodev,relatime 0 0 >> /etc/fstab"
chroot_execute "zfs set mountpoint=legacy $v_rpool_name/var/spool"
chroot_execute "echo $v_rpool_name/var/spool /var/spool zfs nodev,relatime 0 0 >> /etc/fstab"
chroot_execute "zfs set mountpoint=legacy $v_rpool_name/var/tmp"
chroot_execute "echo $v_rpool_name/var/tmp /var/tmp zfs nodev,relatime 0 0 >> /etc/fstab"
chroot_execute "zfs set mountpoint=legacy $v_rpool_name/tmp"
chroot_execute "echo $v_rpool_name/tmp /tmp zfs nodev,relatime 0 0 >> /etc/fstab"
echo "========= add swap, if defined"
if [[ $v_swap_size -gt 0 ]]; then
chroot_execute "echo /dev/zvol/$v_rpool_name/swap none swap discard 0 0 >> /etc/fstab"
fi
chroot_execute "echo RESUME=none > /etc/initramfs-tools/conf.d/resume"
echo "======= unmounting filesystems and zfs pools =========="
unmount_and_export_fs
echo "======== setup complete, rebooting ==============="
reboot

View File

@@ -17,8 +17,6 @@ set -o errexit
set -o pipefail
set -o nounset
export TMPDIR=/tmp
# Variables
v_bpool_name=
v_bpool_tweaks=
@@ -42,7 +40,7 @@ c_deb_security_repo=http://mirror.hetzner.de/ubuntu/security
c_default_zfs_arc_max_mb=250
c_default_bpool_tweaks="-o ashift=12 -O compression=lz4"
c_default_rpool_tweaks="-o ashift=12 -O acltype=posixacl -O compression=zstd-9 -O dnodesize=auto -O relatime=on -O xattr=sa -O normalization=formD"
c_default_rpool_tweaks="-o ashift=12 -O acltype=posixacl -O compression=lz4 -O dnodesize=auto -O relatime=on -O xattr=sa -O normalization=formD"
c_default_hostname=terem
c_zfs_mount_dir=/mnt
c_log_dir=$(dirname "$(mktemp)")/zfs-hetzner-vm
@@ -64,9 +62,7 @@ function print_step_info_header {
###############################################################################
# ${FUNCNAME[1]}"
if [[ "${1:-}" != "" ]]; then
echo -n " $1"
fi
[[ "${1:-}" != "" ]] && echo -n " $1" || true
echo "
###############################################################################
@@ -110,7 +106,7 @@ This script will prepare the ZFS pools, then install and configure minimal Ubunt
The script with minimal changes may be used on any other hosting provider supporting KVM virtualization and offering Debian-based rescue system.
In order to stop the procedure, hit Esc twice during dialogs (excluding yes/no ones), or Ctrl+C while any operation is running.
'
dialog --msgbox "$dialog_message" 30 100
dialog --ascii-lines --msgbox "$dialog_message" 30 100
}
function store_os_distro_information {
@@ -179,7 +175,7 @@ LOG
If you think this is a bug, please open an issue on https://github.com/terem42/zfs-hetzner-vm/issues, and attach the file `'"$c_disks_log"'`.
'
dialog --msgbox "$dialog_message" 30 100
dialog --ascii-lines --msgbox "$dialog_message" 30 100
exit 1
fi
@@ -208,7 +204,7 @@ function select_disks {
Devices with mounted partitions, cdroms, and removable devices are not displayed!
"
mapfile -t v_selected_disks < <(dialog --separate-output --checklist "$dialog_message" 30 100 $((${#menu_entries_option[@]} / 3)) "${menu_entries_option[@]}" 3>&1 1>&2 2>&3)
mapfile -t v_selected_disks < <(dialog --ascii-lines --separate-output --checklist "$dialog_message" 30 100 $((${#menu_entries_option[@]} / 3)) "${menu_entries_option[@]}" 3>&1 1>&2 2>&3)
if [[ ${#v_selected_disks[@]} -gt 0 ]]; then
break
@@ -225,7 +221,7 @@ function ask_swap_size {
local swap_size_invalid_message=
while [[ ! $v_swap_size =~ ^[0-9]+$ ]]; do
v_swap_size=$(dialog --inputbox "${swap_size_invalid_message}Enter the swap size in GiB (0 for no swap):" 30 100 2 3>&1 1>&2 2>&3)
v_swap_size=$(dialog --ascii-lines --inputbox "${swap_size_invalid_message}Enter the swap size in GiB (0 for no swap):" 30 100 2 3>&1 1>&2 2>&3)
swap_size_invalid_message="Invalid swap size! "
done
@@ -240,7 +236,7 @@ function ask_free_tail_space {
local tail_space_invalid_message=
while [[ ! $v_free_tail_space =~ ^[0-9]+$ ]]; do
v_free_tail_space=$(dialog --inputbox "${tail_space_invalid_message}Enter the space to leave at the end of each disk (0 for none):" 30 100 0 3>&1 1>&2 2>&3)
v_free_tail_space=$(dialog --ascii-lines --inputbox "${tail_space_invalid_message}Enter the space to leave at the end of each disk (0 for none):" 30 100 0 3>&1 1>&2 2>&3)
tail_space_invalid_message="Invalid size! "
done
@@ -255,7 +251,7 @@ function ask_zfs_arc_max_size {
local zfs_arc_max_invalid_message=
while [[ ! $v_zfs_arc_max_mb =~ ^[0-9]+$ ]]; do
v_zfs_arc_max_mb=$(dialog --inputbox "${zfs_arc_max_invalid_message}Enter ZFS ARC cache max size in Mb (minimum 64Mb, enter 0 for ZFS default value, the default will take up to 50% of memory):" 30 100 "$c_default_zfs_arc_max_mb" 3>&1 1>&2 2>&3)
v_zfs_arc_max_mb=$(dialog --ascii-lines --inputbox "${zfs_arc_max_invalid_message}Enter ZFS ARC cache max size in Mb (minimum 64Mb, enter 0 for ZFS default value, the default will take up to 50% of memory):" 30 100 "$c_default_zfs_arc_max_mb" 3>&1 1>&2 2>&3)
zfs_arc_max_invalid_message="Invalid size! "
done
@@ -271,14 +267,14 @@ function ask_pool_names {
local bpool_name_invalid_message=
while [[ ! $v_bpool_name =~ ^[a-z][a-zA-Z_:.-]+$ ]]; do
v_bpool_name=$(dialog --inputbox "${bpool_name_invalid_message}Insert the name for the boot pool" 30 100 bpool 3>&1 1>&2 2>&3)
v_bpool_name=$(dialog --ascii-lines --inputbox "${bpool_name_invalid_message}Insert the name for the boot pool" 30 100 bpool 3>&1 1>&2 2>&3)
bpool_name_invalid_message="Invalid pool name! "
done
local rpool_name_invalid_message=
while [[ ! $v_rpool_name =~ ^[a-z][a-zA-Z_:.-]+$ ]]; do
v_rpool_name=$(dialog --inputbox "${rpool_name_invalid_message}Insert the name for the root pool" 30 100 rpool 3>&1 1>&2 2>&3)
v_rpool_name=$(dialog --ascii-lines --inputbox "${rpool_name_invalid_message}Insert the name for the root pool" 30 100 rpool 3>&1 1>&2 2>&3)
rpool_name_invalid_message="Invalid pool name! "
done
@@ -290,8 +286,8 @@ function ask_pool_tweaks {
# shellcheck disable=SC2119
print_step_info_header
v_bpool_tweaks=$(dialog --inputbox "Insert the tweaks for the boot pool" 30 100 -- "$c_default_bpool_tweaks" 3>&1 1>&2 2>&3)
v_rpool_tweaks=$(dialog --inputbox "Insert the tweaks for the root pool" 30 100 -- "$c_default_rpool_tweaks" 3>&1 1>&2 2>&3)
v_bpool_tweaks=$(dialog --ascii-lines --inputbox "Insert the tweaks for the boot pool" 30 100 -- "$c_default_bpool_tweaks" 3>&1 1>&2 2>&3)
v_rpool_tweaks=$(dialog --ascii-lines --inputbox "Insert the tweaks for the root pool" 30 100 -- "$c_default_rpool_tweaks" 3>&1 1>&2 2>&3)
print_variables v_bpool_tweaks v_rpool_tweaks
}
@@ -306,8 +302,8 @@ function ask_root_password {
local password_repeat=-
while [[ "$v_root_password" != "$password_repeat" || "$v_root_password" == "" ]]; do
v_root_password=$(dialog --passwordbox "${password_invalid_message}Please enter the root account password (can't be empty):" 30 100 3>&1 1>&2 2>&3)
password_repeat=$(dialog --passwordbox "Please repeat the password:" 30 100 3>&1 1>&2 2>&3)
v_root_password=$(dialog --ascii-lines --passwordbox "${password_invalid_message}Please enter the root account password (can't be empty):" 30 100 3>&1 1>&2 2>&3)
password_repeat=$(dialog --ascii-lines --passwordbox "Please repeat the password:" 30 100 3>&1 1>&2 2>&3)
password_invalid_message="Passphrase empty, or not matching! "
done
@@ -317,7 +313,7 @@ function ask_root_password {
function ask_encryption {
print_step_info_header
if dialog --defaultno --yesno 'Do you want to encrypt the root pool?' 30 100; then
if dialog --ascii-lines --yesno 'Do you want to encrypt the root pool?' 30 100; then
v_encrypt_rpool=1
fi
set +x
@@ -325,8 +321,8 @@ function ask_encryption {
local passphrase_invalid_message=
local passphrase_repeat=-
while [[ "$v_passphrase" != "$passphrase_repeat" || ${#v_passphrase} -lt 8 ]]; do
v_passphrase=$(dialog --passwordbox "${passphrase_invalid_message}Please enter the passphrase for the root pool (8 chars min.):" 30 100 3>&1 1>&2 2>&3)
passphrase_repeat=$(dialog --passwordbox "Please repeat the passphrase:" 30 100 3>&1 1>&2 2>&3)
v_passphrase=$(dialog --ascii-lines --passwordbox "${passphrase_invalid_message}Please enter the passphrase for the root pool (8 chars min.):" 30 100 3>&1 1>&2 2>&3)
passphrase_repeat=$(dialog --ascii-lines --passwordbox "Please repeat the passphrase:" 30 100 3>&1 1>&2 2>&3)
passphrase_invalid_message="Passphrase too short, or not matching! "
done
@@ -337,7 +333,7 @@ function ask_encryption {
function ask_zfs_experimental {
print_step_info_header
if dialog --defaultno --yesno 'Do you want to use experimental zfs module build?' 30 100; then
if dialog --ascii-lines --yesno 'Do you want to use experimental zfs module build?' 30 100; then
v_zfs_experimental=1
fi
}
@@ -348,8 +344,8 @@ function ask_hostname {
local hostname_invalid_message=
while [[ ! $v_hostname =~ ^[a-z][a-zA-Z0-9_:.-]+$ ]]; do
v_hostname=$(dialog --inputbox "${hostname_invalid_message}Set the host name" 30 100 "$c_default_hostname" 3>&1 1>&2 2>&3)
while [[ ! $v_hostname =~ ^[a-z][a-zA-Z_:.-]+$ ]]; do
v_hostname=$(dialog --ascii-lines --inputbox "${hostname_invalid_message}Set the host name" 30 100 "$c_default_hostname" 3>&1 1>&2 2>&3)
hostname_invalid_message="Invalid host name! "
done
@@ -403,8 +399,8 @@ function unmount_and_export_fs {
echo "===========exporting zfs pools============="
set +e
while (( zpools_exported == 99 )) && (( SECONDS++ <= 60 )); do
if zpool export -a 2> /dev/null; then
zpool export -a 2> /dev/null
if [[ $? == 0 ]]; then
zpools_exported=1
echo "all zfs pools were succesfully exported"
break;
@@ -463,19 +459,10 @@ done
echo "======= installing zfs on rescue system =========="
echo "zfs-dkms zfs-dkms/note-incompatible-licenses note true" | debconf-set-selections
# echo "y" | zfs
# linux-headers-generic linux-image-generic
apt install --yes software-properties-common dpkg-dev dkms
rm -f "$(which zfs)"
rm -f "$(which zpool)"
echo -e "deb http://deb.debian.org/debian/ testing main contrib non-free\ndeb http://deb.debian.org/debian/ testing main contrib non-free\n" >/etc/apt/sources.list.d/bookworm-testing.list
echo -e "Package: src:zfs-linux\nPin: release n=testing\nPin-Priority: 990\n" > /etc/apt/preferences.d/90_zfs
apt update
apt install -t testing --yes zfs-dkms zfsutils-linux
rm /etc/apt/sources.list.d/bookworm-testing.list
rm /etc/apt/preferences.d/90_zfs
apt update
export PATH=$PATH:/usr/sbin
apt install --yes -t buster-backports libelf-dev zfs-dkms
modprobe zfs
zfs --version
echo "======= partitioning the disk =========="
@@ -487,9 +474,9 @@ echo "======= partitioning the disk =========="
fi
for selected_disk in "${v_selected_disks[@]}"; do
wipefs --all --force "$selected_disk"
wipefs --all "$selected_disk"
sgdisk -a1 -n1:24K:+1000K -t1:EF02 "$selected_disk"
sgdisk -n2:0:+2G -t2:BF01 "$selected_disk" # Boot pool
sgdisk -n2:0:+512M -t2:BF01 "$selected_disk" # Boot pool
sgdisk -n3:0:"$tail_space_parameter" -t3:BF01 "$selected_disk" # Root pool
done
@@ -516,17 +503,13 @@ echo "======= create zfs pools and datasets =========="
pools_mirror_option=
fi
# shellcheck disable=SC2086
zpool create \
$v_bpool_tweaks -O canmount=off -O devices=off \
-o cachefile=/etc/zpool.cache \
-O mountpoint=/boot -R $c_zfs_mount_dir -f \
$v_bpool_name $pools_mirror_option "${bpool_disks_partitions[@]}"
# shellcheck disable=SC2086
echo -n "$v_passphrase" | zpool create \
$v_rpool_tweaks \
-o cachefile=/etc/zpool.cache \
"${encryption_options[@]}" \
-O mountpoint=/ -R $c_zfs_mount_dir -f \
$v_rpool_name $pools_mirror_option "${rpool_disks_partitions[@]}"
@@ -541,7 +524,9 @@ zfs create -o canmount=noauto -o mountpoint=/boot "$v_bpool_name/BOOT/ubuntu"
zfs mount "$v_bpool_name/BOOT/ubuntu"
zfs create "$v_rpool_name/home"
zfs create -o mountpoint=/root "$v_rpool_name/home/root"
zfs create -o canmount=off "$v_rpool_name/var"
zfs create -o canmount=off "$v_rpool_name/var/lib"
zfs create "$v_rpool_name/var/log"
zfs create "$v_rpool_name/var/spool"
@@ -592,7 +577,7 @@ ff02::2 ip6-allrouters
ff02::3 ip6-allhosts
CONF
ip6addr_prefix=$(ip -6 a s | grep -E "inet6.+global" | sed -nE 's/.+inet6\s(([0-9a-z]{1,4}:){4,4}).+/\1/p' | head -n 1)
ip6addr_prefix=$(ip -6 a s | grep -E "inet6.+global" | sed -nE 's/.+inet6\s(([0-9a-z]{1,4}:){4,4}).+/\1/p')
cat <<CONF > /mnt/etc/systemd/network/10-eth0.network
[Match]
@@ -605,9 +590,7 @@ Gateway=fe80::1
CONF
chroot_execute "systemctl enable systemd-networkd.service"
chroot_execute "systemctl enable systemd-resolved.service"
#cp /etc/resolv.conf $c_zfs_mount_dir/etc/resolv.conf
mkdir -p "$c_zfs_mount_dir/etc/cloud/cloud.cfg.d/"
cat > "$c_zfs_mount_dir/etc/cloud/cloud.cfg.d/99-disable-network-config.cfg" <<CONF
@@ -617,6 +600,7 @@ CONF
rm -rf $c_zfs_mount_dir/etc/network/interfaces.d/50-cloud-init.cfg
cp /etc/resolv.conf $c_zfs_mount_dir/etc/resolv.conf
echo "======= preparing the jail for chroot =========="
for virtual_fs_dir in proc sys dev; do
@@ -691,29 +675,26 @@ if [[ $v_kernel_variant == "-virtual" ]]; then
chroot_execute "DEBIAN_FRONTEND=noninteractive apt install --yes linux-image-extra-virtual-hwe-18.04"
fi
echo "======= installing aux packages =========="
chroot_execute "apt install --yes man wget curl software-properties-common nano htop gnupg"
chroot_execute "systemctl disable thermald"
echo "======= installing zfs packages =========="
chroot_execute 'echo "zfs-dkms zfs-dkms/note-incompatible-licenses note true" | debconf-set-selections'
if [[ $v_zfs_experimental == "1" ]]; then
chroot_execute "wget -O - https://terem42.github.io/zfs-debian/apt_pub.gpg | apt-key add -"
chroot_execute "add-apt-repository 'deb https://terem42.github.io/zfs-debian/public zfs-debian-experimental main'"
chroot_execute "apt update"
chroot_execute "apt install -t zfs-debian-experimental --yes zfs-initramfs zfs-dkms zfsutils-linux"
else
chroot_execute "add-apt-repository --yes ppa:jonathonf/zfs"
fi
chroot_execute 'echo "zfs-dkms zfs-dkms/note-incompatible-licenses note true" | debconf-set-selections'
if [[ $v_zfs_experimental == "1" ]]; then
chroot_execute "apt install -t zfs-debian-experimental --yes zfs-initramfs zfs-dkms zfsutils-linux"
else
chroot_execute "apt install --yes zfs-initramfs zfs-dkms zfsutils-linux"
fi
chroot_execute 'cat << DKMS > /etc/dkms/zfs.conf
# override for /usr/src/zfs-*/dkms.conf:
# always rebuild initrd when zfs module has been changed
# (either by a ZFS update or a new kernel version)
REMAKE_INITRD="yes"
DKMS'
echo "======= installing OpenSSH and network tooling =========="
chroot_execute "apt install --yes openssh-server net-tools"
@@ -728,8 +709,25 @@ chroot_execute "dpkg-reconfigure openssh-server -f noninteractive"
echo "======= set root password =========="
chroot_execute "echo root:$(printf "%q" "$v_root_password") | chpasswd"
echo "======= setting up zfs cache =========="
cp /etc/zpool.cache /mnt/etc/zfs/zpool.cache
echo "======= setting up zfs services =========="
chroot_execute "cat > /etc/systemd/system/zfs-import-bpool.service <<UNIT
[Unit]
DefaultDependencies=no
Before=zfs-import-scan.service
Before=zfs-import-cache.service
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStartPre=/bin/sh -c '[ -f /etc/zfs/zpool.cache ] && mv /etc/zfs/zpool.cache /etc/zfs/preboot_zpool.cache || true'
ExecStart=/sbin/zpool import -N -o cachefile=none $v_bpool_name
ExecStartPost=/bin/sh -c '[ -f /etc/zfs/preboot_zpool.cache ] && mv /etc/zfs/preboot_zpool.cache /etc/zfs/zpool.cache || true'
[Install]
WantedBy=zfs-import.target
UNIT"
chroot_execute "systemctl enable zfs-import-bpool.service"
echo "========setting up zfs module parameters========"
chroot_execute "echo options zfs zfs_arc_max=$((v_zfs_arc_max_mb * 1024 * 1024)) >> /etc/modprobe.d/zfs.conf"
@@ -769,6 +767,18 @@ if [[ $v_encrypt_rpool == "1" ]]; then
rm -rf "$c_zfs_mount_dir/etc/ssh/ssh_host_ecdsa_key_temp"
rm -rf "$c_zfs_mount_dir/etc/dropbear-initramfs/dropbear_dss_host_key"
cd "$c_zfs_mount_dir/root"
wget http://ftp.de.debian.org/debian/pool/main/libt/libtommath/libtommath1_1.1.0-3_amd64.deb
wget http://ftp.de.debian.org/debian/pool/main/d/dropbear/dropbear-bin_2018.76-5_amd64.deb
wget http://ftp.de.debian.org/debian/pool/main/d/dropbear/dropbear-initramfs_2018.76-5_all.deb
chroot_execute "dpkg -i /root/libtommath1_1.1.0-3_amd64.deb"
chroot_execute "dpkg -i /root/dropbear-bin_2018.76-5_amd64.deb"
chroot_execute "dpkg -i /root/dropbear-initramfs_2018.76-5_all.deb"
rm $c_zfs_mount_dir/root/*.deb
cd /root
fi
echo "============setup root prompt============"
@@ -781,7 +791,6 @@ CONF
echo "========running packages upgrade==========="
chroot_execute "apt upgrade --yes"
chroot_execute "apt purge cryptsetup* --yes"
echo "===========add static route to initramfs via hook to add default routes due to Ubuntu initramfs DHCP bug ========="
mkdir -p "$c_zfs_mount_dir/usr/share/initramfs-tools/scripts/init-premount"
@@ -805,8 +814,8 @@ esac
configure_networking
ip route add 172.31.1.1/255.255.255.255 dev eth0
ip route add default via 172.31.1.1 dev eth0
ip route add 172.31.1.1/255.255.255.255 dev ens3
ip route add default via 172.31.1.1 dev ens3
CONF
chmod 755 "$c_zfs_mount_dir/usr/share/initramfs-tools/scripts/init-premount/static-route"
@@ -823,7 +832,7 @@ chroot_execute "zfs set canmount=noauto rpool"
echo "======= setting mountpoints =========="
chroot_execute "zfs set mountpoint=legacy $v_bpool_name/BOOT/ubuntu"
chroot_execute "echo $v_bpool_name/BOOT/ubuntu /boot zfs nodev,relatime,x-systemd.requires=zfs-mount.service,x-systemd.device-timeout=10 0 0 > /etc/fstab"
chroot_execute "echo $v_bpool_name/BOOT/ubuntu /boot zfs nodev,relatime,x-systemd.requires=zfs-import-bpool.service 0 0 > /etc/fstab"
chroot_execute "zfs set mountpoint=legacy $v_rpool_name/var/log"
chroot_execute "echo $v_rpool_name/var/log /var/log zfs nodev,relatime 0 0 >> /etc/fstab"
@@ -835,10 +844,7 @@ chroot_execute "zfs set mountpoint=legacy $v_rpool_name/tmp"
chroot_execute "echo $v_rpool_name/tmp /tmp zfs nodev,relatime 0 0 >> /etc/fstab"
echo "========= add swap, if defined"
if [[ $v_swap_size -gt 0 ]]; then
chroot_execute "echo /dev/zvol/$v_rpool_name/swap none swap discard 0 0 >> /etc/fstab"
fi
[[ $v_swap_size -gt 0 ]] && chroot_execute "echo /dev/zvol/$v_rpool_name/swap none swap discard 0 0 >> /etc/fstab" || true
chroot_execute "echo RESUME=none > /etc/initramfs-tools/conf.d/resume"
echo "======= unmounting filesystems and zfs pools =========="

View File

@@ -17,8 +17,6 @@ set -o errexit
set -o pipefail
set -o nounset
export TMPDIR=/tmp
# Variables
v_bpool_name=
v_bpool_tweaks=
@@ -42,7 +40,7 @@ c_deb_security_repo=http://mirror.hetzner.de/ubuntu/security
c_default_zfs_arc_max_mb=250
c_default_bpool_tweaks="-o ashift=12 -O compression=lz4"
c_default_rpool_tweaks="-o ashift=12 -O acltype=posixacl -O compression=zstd-9 -O dnodesize=auto -O relatime=on -O xattr=sa -O normalization=formD"
c_default_rpool_tweaks="-o ashift=12 -O acltype=posixacl -O compression=lz4 -O dnodesize=auto -O relatime=on -O xattr=sa -O normalization=formD"
c_default_hostname=terem
c_zfs_mount_dir=/mnt
c_log_dir=$(dirname "$(mktemp)")/zfs-hetzner-vm
@@ -64,9 +62,7 @@ function print_step_info_header {
###############################################################################
# ${FUNCNAME[1]}"
if [[ "${1:-}" != "" ]]; then
echo -n " $1"
fi
[[ "${1:-}" != "" ]] && echo -n " $1" || true
echo "
###############################################################################
@@ -317,7 +313,7 @@ function ask_root_password {
function ask_encryption {
print_step_info_header
if dialog --defaultno --yesno 'Do you want to encrypt the root pool?' 30 100; then
if dialog --yesno 'Do you want to encrypt the root pool?' 30 100; then
v_encrypt_rpool=1
fi
set +x
@@ -337,7 +333,7 @@ function ask_encryption {
function ask_zfs_experimental {
print_step_info_header
if dialog --defaultno --yesno 'Do you want to use experimental zfs module build?' 30 100; then
if dialog --yesno 'Do you want to use experimental zfs module build?' 30 100; then
v_zfs_experimental=1
fi
}
@@ -348,7 +344,7 @@ function ask_hostname {
local hostname_invalid_message=
while [[ ! $v_hostname =~ ^[a-z][a-zA-Z0-9_:.-]+$ ]]; do
while [[ ! $v_hostname =~ ^[a-z][a-zA-Z_:.-]+$ ]]; do
v_hostname=$(dialog --inputbox "${hostname_invalid_message}Set the host name" 30 100 "$c_default_hostname" 3>&1 1>&2 2>&3)
hostname_invalid_message="Invalid host name! "
@@ -403,8 +399,8 @@ function unmount_and_export_fs {
echo "===========exporting zfs pools============="
set +e
while (( zpools_exported == 99 )) && (( SECONDS++ <= 60 )); do
if zpool export -a 2> /dev/null; then
zpool export -a 2> /dev/null
if [[ $? == 0 ]]; then
zpools_exported=1
echo "all zfs pools were succesfully exported"
break;
@@ -463,19 +459,20 @@ done
echo "======= installing zfs on rescue system =========="
echo "zfs-dkms zfs-dkms/note-incompatible-licenses note true" | debconf-set-selections
# echo "y" | zfs
# linux-headers-generic linux-image-generic
apt install --yes software-properties-common dpkg-dev dkms
rm -f "$(which zfs)"
rm -f "$(which zpool)"
echo -e "deb http://deb.debian.org/debian/ testing main contrib non-free\ndeb http://deb.debian.org/debian/ testing main contrib non-free\n" >/etc/apt/sources.list.d/bookworm-testing.list
echo -e "Package: src:zfs-linux\nPin: release n=testing\nPin-Priority: 990\n" > /etc/apt/preferences.d/90_zfs
cd "$(mktemp -d)"
wget "$(curl -Ls https://api.github.com/repos/openzfs/zfs/releases/latest| grep "browser_download_url.*tar.gz"|grep -E "tar.gz\"$"| cut -d '"' -f 4)"
apt update
apt install -t testing --yes zfs-dkms zfsutils-linux
rm /etc/apt/sources.list.d/bookworm-testing.list
rm /etc/apt/preferences.d/90_zfs
apt update
export PATH=$PATH:/usr/sbin
apt install libssl-dev uuid-dev zlib1g-dev libblkid-dev -y
tar xfv zfs*.tar.gz
rm *.tar.gz
cd zfs*
./configure
make -j "$(nproc)"
make install
ldconfig
modprobe zfs
zfs --version
echo "======= partitioning the disk =========="
@@ -489,7 +486,7 @@ echo "======= partitioning the disk =========="
for selected_disk in "${v_selected_disks[@]}"; do
wipefs --all --force "$selected_disk"
sgdisk -a1 -n1:24K:+1000K -t1:EF02 "$selected_disk"
sgdisk -n2:0:+2G -t2:BF01 "$selected_disk" # Boot pool
sgdisk -n2:0:+512M -t2:BF01 "$selected_disk" # Boot pool
sgdisk -n3:0:"$tail_space_parameter" -t3:BF01 "$selected_disk" # Root pool
done
@@ -516,17 +513,13 @@ echo "======= create zfs pools and datasets =========="
pools_mirror_option=
fi
# shellcheck disable=SC2086
zpool create \
$v_bpool_tweaks -O canmount=off -O devices=off \
-o cachefile=/etc/zpool.cache \
-O mountpoint=/boot -R $c_zfs_mount_dir -f \
$v_bpool_name $pools_mirror_option "${bpool_disks_partitions[@]}"
# shellcheck disable=SC2086
echo -n "$v_passphrase" | zpool create \
$v_rpool_tweaks \
-o cachefile=/etc/zpool.cache \
"${encryption_options[@]}" \
-O mountpoint=/ -R $c_zfs_mount_dir -f \
$v_rpool_name $pools_mirror_option "${rpool_disks_partitions[@]}"
@@ -541,7 +534,9 @@ zfs create -o canmount=noauto -o mountpoint=/boot "$v_bpool_name/BOOT/ubuntu"
zfs mount "$v_bpool_name/BOOT/ubuntu"
zfs create "$v_rpool_name/home"
zfs create -o mountpoint=/root "$v_rpool_name/home/root"
zfs create -o canmount=off "$v_rpool_name/var"
zfs create -o canmount=off "$v_rpool_name/var/lib"
zfs create "$v_rpool_name/var/log"
zfs create "$v_rpool_name/var/spool"
@@ -592,7 +587,7 @@ ff02::2 ip6-allrouters
ff02::3 ip6-allhosts
CONF
ip6addr_prefix=$(ip -6 a s | grep -E "inet6.+global" | sed -nE 's/.+inet6\s(([0-9a-z]{1,4}:){4,4}).+/\1/p' | head -n 1)
ip6addr_prefix=$(ip -6 a s | grep -E "inet6.+global" | sed -nE 's/.+inet6\s(([0-9a-z]{1,4}:){4,4}).+/\1/p')
cat <<CONF > /mnt/etc/systemd/network/10-eth0.network
[Match]
@@ -605,7 +600,6 @@ Gateway=fe80::1
CONF
chroot_execute "systemctl enable systemd-networkd.service"
chroot_execute "systemctl enable systemd-resolved.service"
mkdir -p "$c_zfs_mount_dir/etc/cloud/cloud.cfg.d/"
@@ -616,7 +610,7 @@ CONF
rm -rf $c_zfs_mount_dir/etc/network/interfaces.d/50-cloud-init.cfg
#cp /etc/resolv.conf $c_zfs_mount_dir/etc/resolv.conf
cp /etc/resolv.conf $c_zfs_mount_dir/etc/resolv.conf
echo "======= preparing the jail for chroot =========="
for virtual_fs_dir in proc sys dev; do
@@ -685,10 +679,10 @@ chroot_execute "rm -f /etc/localtime /etc/timezone"
chroot_execute "dpkg-reconfigure tzdata -f noninteractive "
echo "======= installing latest kernel============="
chroot_execute "DEBIAN_FRONTEND=noninteractive apt install --yes linux-headers${v_kernel_variant} linux-image${v_kernel_variant}"
chroot_execute "DEBIAN_FRONTEND=noninteractive apt install --yes linux-headers${v_kernel_variant}-hwe-18.04 linux-image${v_kernel_variant}-hwe-18.04"
if [[ $v_kernel_variant == "-virtual" ]]; then
# linux-image-extra is only available for virtual hosts
chroot_execute "DEBIAN_FRONTEND=noninteractive apt install --yes linux-image-extra-virtual"
chroot_execute "DEBIAN_FRONTEND=noninteractive apt install --yes linux-image-extra-virtual-hwe-20.04"
fi
@@ -697,24 +691,25 @@ chroot_execute "apt install --yes man-db wget curl software-properties-common na
chroot_execute "systemctl disable thermald"
echo "======= installing zfs packages =========="
chroot_execute 'echo "zfs-dkms zfs-dkms/note-incompatible-licenses note true" | debconf-set-selections'
if [[ $v_zfs_experimental == "1" ]]; then
chroot_execute "wget -O - https://terem42.github.io/zfs-debian/apt_pub.gpg | apt-key add -"
chroot_execute "add-apt-repository 'deb https://terem42.github.io/zfs-debian/public zfs-debian-experimental main'"
chroot_execute "apt update"
else
echo "======= installing OpenZFS 2.0 stable package from Debian 10 backports zfs packages =========="
chroot_execute "apt-key adv --recv-key --keyserver keyserver.ubuntu.com 648ACFD622F3D138"
chroot_execute "sudo apt-key adv --recv-key --keyserver keyserver.ubuntu.com 0E98404D386FA1D9"
chroot_execute "add-apt-repository 'deb http://deb.debian.org/debian buster-backports main contrib non-free'"
chroot_execute "apt install -t buster-backports --yes zfs-dkms zfsutils-linux zfs-initramfs"
chroot_execute "add-apt-repository -r 'deb http://deb.debian.org/debian buster-backports main contrib non-free'"
fi
chroot_execute 'echo "zfs-dkms zfs-dkms/note-incompatible-licenses note true" | debconf-set-selections'
if [[ $v_zfs_experimental == "1" ]]; then
chroot_execute "apt install -t zfs-debian-experimental --yes zfs-initramfs zfs-dkms zfsutils-linux"
else
chroot_execute "add-apt-repository --yes ppa:jonathonf/zfs"
chroot_execute "apt install --yes zfs-initramfs zfs-dkms zfsutils-linux"
fi
chroot_execute 'cat << DKMS > /etc/dkms/zfs.conf
# override for /usr/src/zfs-*/dkms.conf:
# always rebuild initrd when zfs module has been changed
# (either by a ZFS update or a new kernel version)
REMAKE_INITRD="yes"
DKMS'
echo "======= installing OpenSSH and network tooling =========="
chroot_execute "apt install --yes openssh-server net-tools"
@@ -729,8 +724,25 @@ chroot_execute "dpkg-reconfigure openssh-server -f noninteractive"
echo "======= set root password =========="
chroot_execute "echo root:$(printf "%q" "$v_root_password") | chpasswd"
echo "======= setting up zfs cache =========="
cp /etc/zpool.cache /mnt/etc/zfs/zpool.cache
echo "======= setting up zfs services =========="
chroot_execute "cat > /etc/systemd/system/zfs-import-bpool.service <<UNIT
[Unit]
DefaultDependencies=no
Before=zfs-import-scan.service
Before=zfs-import-cache.service
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStartPre=/bin/sh -c '[ -f /etc/zfs/zpool.cache ] && mv /etc/zfs/zpool.cache /etc/zfs/preboot_zpool.cache || true'
ExecStart=/sbin/zpool import -N -o cachefile=none $v_bpool_name
ExecStartPost=/bin/sh -c '[ -f /etc/zfs/preboot_zpool.cache ] && mv /etc/zfs/preboot_zpool.cache /etc/zfs/zpool.cache || true'
[Install]
WantedBy=zfs-import.target
UNIT"
chroot_execute "systemctl enable zfs-import-bpool.service"
echo "========setting up zfs module parameters========"
chroot_execute "echo options zfs zfs_arc_max=$((v_zfs_arc_max_mb * 1024 * 1024)) >> /etc/modprobe.d/zfs.conf"
@@ -772,13 +784,13 @@ if [[ $v_encrypt_rpool == "1" ]]; then
rm -rf "$c_zfs_mount_dir/etc/dropbear-initramfs/dropbear_dss_host_key"
cd "$c_zfs_mount_dir/root"
wget http://ftp.de.debian.org/debian/pool/main/libt/libtommath/libtommath1_1.2.0-6_amd64.deb
wget http://ftp.de.debian.org/debian/pool/main/d/dropbear/dropbear-bin_2020.81-3_amd64.deb
wget http://ftp.de.debian.org/debian/pool/main/d/dropbear/dropbear-initramfs_2020.81-3_all.deb
wget http://ftp.de.debian.org/debian/pool/main/libt/libtommath/libtommath1_1.1.0-3_amd64.deb
wget http://ftp.de.debian.org/debian/pool/main/d/dropbear/dropbear-bin_2018.76-5_amd64.deb
wget http://ftp.de.debian.org/debian/pool/main/d/dropbear/dropbear-initramfs_2018.76-5_all.deb
chroot_execute "dpkg -i /root/libtommath1_1.2.0-6_amd64.deb"
chroot_execute "dpkg -i /root/dropbear-bin_2020.81-3_amd64.deb"
chroot_execute "dpkg -i /root/dropbear-initramfs_2020.81-3_all.deb"
chroot_execute "dpkg -i /root/libtommath1_1.1.0-3_amd64.deb"
chroot_execute "dpkg -i /root/dropbear-bin_2018.76-5_amd64.deb"
chroot_execute "dpkg -i /root/dropbear-initramfs_2018.76-5_all.deb"
rm $c_zfs_mount_dir/root/*.deb
cd /root
@@ -794,7 +806,6 @@ CONF
echo "========running packages upgrade==========="
chroot_execute "apt upgrade --yes"
chroot_execute "apt purge cryptsetup* --yes"
echo "===========add static route to initramfs via hook to add default routes due to Ubuntu initramfs DHCP bug ========="
mkdir -p "$c_zfs_mount_dir/usr/share/initramfs-tools/scripts/init-premount"
@@ -836,7 +847,7 @@ chroot_execute "zfs set canmount=noauto rpool"
echo "======= setting mountpoints =========="
chroot_execute "zfs set mountpoint=legacy $v_bpool_name/BOOT/ubuntu"
chroot_execute "echo $v_bpool_name/BOOT/ubuntu /boot zfs nodev,relatime,x-systemd.requires=zfs-mount.service,x-systemd.device-timeout=10 0 0 > /etc/fstab"
chroot_execute "echo $v_bpool_name/BOOT/ubuntu /boot zfs nodev,relatime,x-systemd.requires=zfs-import-bpool.service 0 0 > /etc/fstab"
chroot_execute "zfs set mountpoint=legacy $v_rpool_name/var/log"
chroot_execute "echo $v_rpool_name/var/log /var/log zfs nodev,relatime 0 0 >> /etc/fstab"
@@ -848,10 +859,7 @@ chroot_execute "zfs set mountpoint=legacy $v_rpool_name/tmp"
chroot_execute "echo $v_rpool_name/tmp /tmp zfs nodev,relatime 0 0 >> /etc/fstab"
echo "========= add swap, if defined"
if [[ $v_swap_size -gt 0 ]]; then
chroot_execute "echo /dev/zvol/$v_rpool_name/swap none swap discard 0 0 >> /etc/fstab"
fi
[[ $v_swap_size -gt 0 ]] && chroot_execute "echo /dev/zvol/$v_rpool_name/swap none swap discard 0 0 >> /etc/fstab" || true
chroot_execute "echo RESUME=none > /etc/initramfs-tools/conf.d/resume"
echo "======= unmounting filesystems and zfs pools =========="

View File

@@ -1,850 +0,0 @@
#!/bin/bash
: <<'end_header_info'
(c) Andrey Prokopenko job@terem.fr
fully automatic script to install Ubuntu 20 LTS with ZFS root on Hetzner VPS
WARNING: all data on the disk will be destroyed
How to use: add SSH key to the rescue console, set it OS to linux64, then press "mount rescue and power cycle" button
Next, connect via SSH to console, and run the script
Answer script questions about desired hostname and ZFS ARC cache size
To cope with network failures its higly recommended to run the script inside screen console
screen -dmS zfs
screen -r zfs
To detach from screen console, hit Ctrl-d then a
end_header_info
set -o errexit
set -o pipefail
set -o nounset
export TMPDIR=/tmp
# Variables
v_bpool_name=
v_bpool_tweaks=
v_rpool_name=
v_rpool_tweaks=
declare -a v_selected_disks
v_swap_size= # integer
v_free_tail_space= # integer
v_hostname=
v_kernel_variant=
v_zfs_arc_max_mb=
v_root_password=
v_encrypt_rpool= # 0=false, 1=true
v_passphrase=
v_zfs_experimental=
v_suitable_disks=()
# Constants
c_deb_packages_repo=http://mirror.hetzner.de/ubuntu/packages
c_deb_security_repo=http://mirror.hetzner.de/ubuntu/security
c_default_zfs_arc_max_mb=250
c_default_bpool_tweaks="-o ashift=12 -O compression=lz4"
c_default_rpool_tweaks="-o ashift=12 -O acltype=posixacl -O compression=zstd-9 -O dnodesize=auto -O relatime=on -O xattr=sa -O normalization=formD"
c_default_hostname=terem
c_zfs_mount_dir=/mnt
c_log_dir=$(dirname "$(mktemp)")/zfs-hetzner-vm
c_install_log=$c_log_dir/install.log
c_lsb_release_log=$c_log_dir/lsb_release.log
c_disks_log=$c_log_dir/disks.log
function activate_debug {
mkdir -p "$c_log_dir"
exec 5> "$c_install_log"
BASH_XTRACEFD="5"
set -x
}
# shellcheck disable=SC2120
function print_step_info_header {
echo -n "
###############################################################################
# ${FUNCNAME[1]}"
if [[ "${1:-}" != "" ]]; then
echo -n " $1"
fi
echo "
###############################################################################
"
}
function print_variables {
for variable_name in "$@"; do
declare -n variable_reference="$variable_name"
echo -n "$variable_name:"
case "$(declare -p "$variable_name")" in
"declare -a"* )
for entry in "${variable_reference[@]}"; do
echo -n " \"$entry\""
done
;;
"declare -A"* )
for key in "${!variable_reference[@]}"; do
echo -n " $key=\"${variable_reference[$key]}\""
done
;;
* )
echo -n " $variable_reference"
;;
esac
echo
done
echo
}
function display_intro_banner {
# shellcheck disable=SC2119
print_step_info_header
local dialog_message='Hello!
This script will prepare the ZFS pools, then install and configure minimal Ubuntu 20 LTS with ZFS root on Hetzner hosting VPS instance
The script with minimal changes may be used on any other hosting provider supporting KVM virtualization and offering Debian-based rescue system.
In order to stop the procedure, hit Esc twice during dialogs (excluding yes/no ones), or Ctrl+C while any operation is running.
'
dialog --msgbox "$dialog_message" 30 100
}
function store_os_distro_information {
# shellcheck disable=SC2119
print_step_info_header
lsb_release --all > "$c_lsb_release_log"
}
function check_prerequisites {
# shellcheck disable=SC2119
print_step_info_header
if [[ $(id -u) -ne 0 ]]; then
echo 'This script must be run with administrative privileges!'
exit 1
fi
if [[ ! -r /root/.ssh/authorized_keys ]]; then
echo "SSH pubkey file is absent, please add it to the rescue system setting, then reboot into rescue system and run the script"
exit 1
fi
if ! dpkg-query --showformat="\${Status}" -W dialog 2> /dev/null | grep -q "install ok installed"; then
apt install --yes dialog
fi
}
function find_suitable_disks {
# shellcheck disable=SC2119
print_step_info_header
udevadm trigger
# shellcheck disable=SC2012
ls -l /dev/disk/by-id | tail -n +2 | perl -lane 'print "@F[8..10]"' > "$c_disks_log"
local candidate_disk_ids
local mounted_devices
candidate_disk_ids=$(find /dev/disk/by-id -regextype awk -regex '.+/(ata|nvme|scsi)-.+' -not -regex '.+-part[0-9]+$' | sort)
mounted_devices="$(df | awk 'BEGIN {getline} {print $1}' | xargs -n 1 lsblk -no pkname 2> /dev/null | sort -u || true)"
while read -r disk_id || [[ -n "$disk_id" ]]; do
local device_info
device_info="$(udevadm info --query=property "$(readlink -f "$disk_id")")"
block_device_basename="$(basename "$(readlink -f "$disk_id")")"
if ! grep -q '^ID_TYPE=cd$' <<< "$device_info"; then
if ! grep -q "^$block_device_basename\$" <<< "$mounted_devices"; then
v_suitable_disks+=("$disk_id")
fi
fi
cat >> "$c_disks_log" << LOG
## DEVICE: $disk_id ################################
$(udevadm info --query=property "$(readlink -f "$disk_id")")
LOG
done < <(echo -n "$candidate_disk_ids")
if [[ ${#v_suitable_disks[@]} -eq 0 ]]; then
local dialog_message='No suitable disks have been found!
If you think this is a bug, please open an issue on https://github.com/terem42/zfs-hetzner-vm/issues, and attach the file `'"$c_disks_log"'`.
'
dialog --msgbox "$dialog_message" 30 100
exit 1
fi
print_variables v_suitable_disks
}
function select_disks {
# shellcheck disable=SC2119
print_step_info_header
while true; do
local menu_entries_option=()
if [[ ${#v_suitable_disks[@]} -eq 1 ]]; then
local disk_selection_status=ON
else
local disk_selection_status=OFF
fi
for disk_id in "${v_suitable_disks[@]}"; do
menu_entries_option+=("$disk_id" "($block_device_basename)" "$disk_selection_status")
done
local dialog_message="Select the ZFS devices (multiple selections will be in mirror).
Devices with mounted partitions, cdroms, and removable devices are not displayed!
"
mapfile -t v_selected_disks < <(dialog --separate-output --checklist "$dialog_message" 30 100 $((${#menu_entries_option[@]} / 3)) "${menu_entries_option[@]}" 3>&1 1>&2 2>&3)
if [[ ${#v_selected_disks[@]} -gt 0 ]]; then
break
fi
done
print_variables v_selected_disks
}
function ask_swap_size {
# shellcheck disable=SC2119
print_step_info_header
local swap_size_invalid_message=
while [[ ! $v_swap_size =~ ^[0-9]+$ ]]; do
v_swap_size=$(dialog --inputbox "${swap_size_invalid_message}Enter the swap size in GiB (0 for no swap):" 30 100 2 3>&1 1>&2 2>&3)
swap_size_invalid_message="Invalid swap size! "
done
print_variables v_swap_size
}
function ask_free_tail_space {
# shellcheck disable=SC2119
print_step_info_header
local tail_space_invalid_message=
while [[ ! $v_free_tail_space =~ ^[0-9]+$ ]]; do
v_free_tail_space=$(dialog --inputbox "${tail_space_invalid_message}Enter the space to leave at the end of each disk (0 for none):" 30 100 0 3>&1 1>&2 2>&3)
tail_space_invalid_message="Invalid size! "
done
print_variables v_free_tail_space
}
function ask_zfs_arc_max_size {
# shellcheck disable=SC2119
print_step_info_header
local zfs_arc_max_invalid_message=
while [[ ! $v_zfs_arc_max_mb =~ ^[0-9]+$ ]]; do
v_zfs_arc_max_mb=$(dialog --inputbox "${zfs_arc_max_invalid_message}Enter ZFS ARC cache max size in Mb (minimum 64Mb, enter 0 for ZFS default value, the default will take up to 50% of memory):" 30 100 "$c_default_zfs_arc_max_mb" 3>&1 1>&2 2>&3)
zfs_arc_max_invalid_message="Invalid size! "
done
print_variables v_zfs_arc_max_mb
}
function ask_pool_names {
# shellcheck disable=SC2119
print_step_info_header
local bpool_name_invalid_message=
while [[ ! $v_bpool_name =~ ^[a-z][a-zA-Z_:.-]+$ ]]; do
v_bpool_name=$(dialog --inputbox "${bpool_name_invalid_message}Insert the name for the boot pool" 30 100 bpool 3>&1 1>&2 2>&3)
bpool_name_invalid_message="Invalid pool name! "
done
local rpool_name_invalid_message=
while [[ ! $v_rpool_name =~ ^[a-z][a-zA-Z_:.-]+$ ]]; do
v_rpool_name=$(dialog --inputbox "${rpool_name_invalid_message}Insert the name for the root pool" 30 100 rpool 3>&1 1>&2 2>&3)
rpool_name_invalid_message="Invalid pool name! "
done
print_variables v_bpool_name v_rpool_name
}
function ask_pool_tweaks {
# shellcheck disable=SC2119
print_step_info_header
v_bpool_tweaks=$(dialog --inputbox "Insert the tweaks for the boot pool" 30 100 -- "$c_default_bpool_tweaks" 3>&1 1>&2 2>&3)
v_rpool_tweaks=$(dialog --inputbox "Insert the tweaks for the root pool" 30 100 -- "$c_default_rpool_tweaks" 3>&1 1>&2 2>&3)
print_variables v_bpool_tweaks v_rpool_tweaks
}
function ask_root_password {
# shellcheck disable=SC2119
print_step_info_header
set +x
local password_invalid_message=
local password_repeat=-
while [[ "$v_root_password" != "$password_repeat" || "$v_root_password" == "" ]]; do
v_root_password=$(dialog --passwordbox "${password_invalid_message}Please enter the root account password (can't be empty):" 30 100 3>&1 1>&2 2>&3)
password_repeat=$(dialog --passwordbox "Please repeat the password:" 30 100 3>&1 1>&2 2>&3)
password_invalid_message="Passphrase empty, or not matching! "
done
set -x
}
function ask_encryption {
print_step_info_header
if dialog --defaultno --yesno 'Do you want to encrypt the root pool?' 30 100; then
v_encrypt_rpool=1
fi
set +x
if [[ $v_encrypt_rpool == "1" ]]; then
local passphrase_invalid_message=
local passphrase_repeat=-
while [[ "$v_passphrase" != "$passphrase_repeat" || ${#v_passphrase} -lt 8 ]]; do
v_passphrase=$(dialog --passwordbox "${passphrase_invalid_message}Please enter the passphrase for the root pool (8 chars min.):" 30 100 3>&1 1>&2 2>&3)
passphrase_repeat=$(dialog --passwordbox "Please repeat the passphrase:" 30 100 3>&1 1>&2 2>&3)
passphrase_invalid_message="Passphrase too short, or not matching! "
done
fi
set -x
}
function ask_zfs_experimental {
print_step_info_header
if dialog --defaultno --yesno 'Do you want to use experimental zfs module build?' 30 100; then
v_zfs_experimental=1
fi
}
function ask_hostname {
# shellcheck disable=SC2119
print_step_info_header
local hostname_invalid_message=
while [[ ! $v_hostname =~ ^[a-z][a-zA-Z0-9_:.-]+$ ]]; do
v_hostname=$(dialog --inputbox "${hostname_invalid_message}Set the host name" 30 100 "$c_default_hostname" 3>&1 1>&2 2>&3)
hostname_invalid_message="Invalid host name! "
done
print_variables v_hostname
}
function determine_kernel_variant {
if dmidecode | grep -q vServer; then
v_kernel_variant="-virtual"
else
v_kernel_variant="-generic"
fi
}
function chroot_execute {
chroot $c_zfs_mount_dir bash -c "$1"
}
function unmount_and_export_fs {
# shellcheck disable=SC2119
print_step_info_header
for virtual_fs_dir in dev sys proc; do
umount --recursive --force --lazy "$c_zfs_mount_dir/$virtual_fs_dir"
done
local max_unmount_wait=5
echo -n "Waiting for virtual filesystems to unmount "
SECONDS=0
for virtual_fs_dir in dev sys proc; do
while mountpoint -q "$c_zfs_mount_dir/$virtual_fs_dir" && [[ $SECONDS -lt $max_unmount_wait ]]; do
sleep 0.5
echo -n .
done
done
echo
for virtual_fs_dir in dev sys proc; do
if mountpoint -q "$c_zfs_mount_dir/$virtual_fs_dir"; then
echo "Re-issuing umount for $c_zfs_mount_dir/$virtual_fs_dir"
umount --recursive --force --lazy "$c_zfs_mount_dir/$virtual_fs_dir"
fi
done
SECONDS=0
zpools_exported=99
echo "===========exporting zfs pools============="
set +e
while (( zpools_exported == 99 )) && (( SECONDS++ <= 60 )); do
if zpool export -a 2> /dev/null; then
zpools_exported=1
echo "all zfs pools were succesfully exported"
break;
else
sleep 1
fi
done
set -e
if (( zpools_exported != 1 )); then
echo "failed to export zfs pools"
exit 1
fi
}
#################### MAIN ################################
export LC_ALL=en_US.UTF-8
export NCURSES_NO_UTF8_ACS=1
check_prerequisites
display_intro_banner
activate_debug
find_suitable_disks
select_disks
ask_swap_size
ask_free_tail_space
ask_pool_names
ask_pool_tweaks
ask_encryption
ask_zfs_arc_max_size
ask_zfs_experimental
ask_root_password
ask_hostname
determine_kernel_variant
clear
echo "===========remove unused kernels in rescue system========="
for kver in $(find /lib/modules/* -maxdepth 0 -type d | grep -v "$(uname -r)" | cut -s -d "/" -f 4); do
apt purge --yes "linux-headers-$kver"
apt purge --yes "linux-image-$kver"
done
echo "======= installing zfs on rescue system =========="
echo "zfs-dkms zfs-dkms/note-incompatible-licenses note true" | debconf-set-selections
# echo "y" | zfs
# linux-headers-generic linux-image-generic
apt install --yes software-properties-common dpkg-dev dkms
rm -f "$(which zfs)"
rm -f "$(which zpool)"
echo -e "deb http://deb.debian.org/debian/ testing main contrib non-free\ndeb http://deb.debian.org/debian/ testing main contrib non-free\n" >/etc/apt/sources.list.d/bookworm-testing.list
echo -e "Package: src:zfs-linux\nPin: release n=testing\nPin-Priority: 990\n" > /etc/apt/preferences.d/90_zfs
apt update
apt install -t testing --yes zfs-dkms zfsutils-linux
rm /etc/apt/sources.list.d/bookworm-testing.list
rm /etc/apt/preferences.d/90_zfs
apt update
export PATH=$PATH:/usr/sbin
zfs --version
echo "======= partitioning the disk =========="
if [[ $v_free_tail_space -eq 0 ]]; then
tail_space_parameter=0
else
tail_space_parameter="-${v_free_tail_space}G"
fi
for selected_disk in "${v_selected_disks[@]}"; do
wipefs --all --force "$selected_disk"
sgdisk -a1 -n1:24K:+1000K -t1:EF02 "$selected_disk"
sgdisk -n2:0:+2G -t2:BF01 "$selected_disk" # Boot pool
sgdisk -n3:0:"$tail_space_parameter" -t3:BF01 "$selected_disk" # Root pool
done
udevadm settle
echo "======= create zfs pools and datasets =========="
encryption_options=()
rpool_disks_partitions=()
bpool_disks_partitions=()
if [[ $v_encrypt_rpool == "1" ]]; then
encryption_options=(-O "encryption=aes-256-gcm" -O "keylocation=prompt" -O "keyformat=passphrase")
fi
for selected_disk in "${v_selected_disks[@]}"; do
rpool_disks_partitions+=("${selected_disk}-part3")
bpool_disks_partitions+=("${selected_disk}-part2")
done
if [[ ${#v_selected_disks[@]} -gt 1 ]]; then
pools_mirror_option=mirror
else
pools_mirror_option=
fi
# shellcheck disable=SC2086
zpool create \
$v_bpool_tweaks -O canmount=off -O devices=off \
-o cachefile=/etc/zpool.cache \
-O mountpoint=/boot -R $c_zfs_mount_dir -f \
$v_bpool_name $pools_mirror_option "${bpool_disks_partitions[@]}"
# shellcheck disable=SC2086
echo -n "$v_passphrase" | zpool create \
$v_rpool_tweaks \
-o cachefile=/etc/zpool.cache \
"${encryption_options[@]}" \
-O mountpoint=/ -R $c_zfs_mount_dir -f \
$v_rpool_name $pools_mirror_option "${rpool_disks_partitions[@]}"
zfs create -o canmount=off -o mountpoint=none "$v_rpool_name/ROOT"
zfs create -o canmount=off -o mountpoint=none "$v_bpool_name/BOOT"
zfs create -o canmount=noauto -o mountpoint=/ "$v_rpool_name/ROOT/ubuntu"
zfs mount "$v_rpool_name/ROOT/ubuntu"
zfs create -o canmount=noauto -o mountpoint=/boot "$v_bpool_name/BOOT/ubuntu"
zfs mount "$v_bpool_name/BOOT/ubuntu"
zfs create "$v_rpool_name/home"
#zfs create -o mountpoint=/root "$v_rpool_name/home/root"
zfs create -o canmount=off "$v_rpool_name/var"
zfs create "$v_rpool_name/var/log"
zfs create "$v_rpool_name/var/spool"
zfs create -o com.sun:auto-snapshot=false "$v_rpool_name/var/cache"
zfs create -o com.sun:auto-snapshot=false "$v_rpool_name/var/tmp"
chmod 1777 "$c_zfs_mount_dir/var/tmp"
zfs create "$v_rpool_name/srv"
zfs create -o canmount=off "$v_rpool_name/usr"
zfs create "$v_rpool_name/usr/local"
zfs create "$v_rpool_name/var/mail"
zfs create -o com.sun:auto-snapshot=false -o canmount=on -o mountpoint=/tmp "$v_rpool_name/tmp"
chmod 1777 "$c_zfs_mount_dir/tmp"
if [[ $v_swap_size -gt 0 ]]; then
zfs create \
-V "${v_swap_size}G" -b "$(getconf PAGESIZE)" \
-o compression=zle -o logbias=throughput -o sync=always -o primarycache=metadata -o secondarycache=none -o com.sun:auto-snapshot=false \
"$v_rpool_name/swap"
udevadm settle
mkswap -f "/dev/zvol/$v_rpool_name/swap"
fi
echo "======= setting up initial system packages =========="
debootstrap --arch=amd64 jammy "$c_zfs_mount_dir" "$c_deb_packages_repo"
zfs set devices=off "$v_rpool_name"
echo "======= setting up the network =========="
echo "$v_hostname" > $c_zfs_mount_dir/etc/hostname
cat > "$c_zfs_mount_dir/etc/hosts" <<CONF
127.0.1.1 ${v_hostname}
127.0.0.1 localhost
# The following lines are desirable for IPv6 capable hosts
::1 ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
ff02::3 ip6-allhosts
CONF
ip6addr_prefix=$(ip -6 a s | grep -E "inet6.+global" | sed -nE 's/.+inet6\s(([0-9a-z]{1,4}:){4,4}).+/\1/p' | head -n 1)
cat <<CONF > /mnt/etc/systemd/network/10-eth0.network
[Match]
Name=eth0
[Network]
DHCP=ipv4
Address=${ip6addr_prefix}:1/64
Gateway=fe80::1
CONF
chroot_execute "systemctl enable systemd-networkd.service"
chroot_execute "systemctl enable systemd-resolved.service"
mkdir -p "$c_zfs_mount_dir/etc/cloud/cloud.cfg.d/"
cat > "$c_zfs_mount_dir/etc/cloud/cloud.cfg.d/99-disable-network-config.cfg" <<CONF
network:
config: disabled
CONF
rm -rf $c_zfs_mount_dir/etc/network/interfaces.d/50-cloud-init.cfg
echo "======= preparing the jail for chroot =========="
for virtual_fs_dir in proc sys dev; do
mount --rbind "/$virtual_fs_dir" "$c_zfs_mount_dir/$virtual_fs_dir"
done
echo "======= setting apt repos =========="
cat > "$c_zfs_mount_dir/etc/apt/sources.list" <<CONF
deb [arch=i386,amd64] $c_deb_packages_repo jammy main restricted
deb [arch=i386,amd64] $c_deb_packages_repo jammy-updates main restricted
deb [arch=i386,amd64] $c_deb_packages_repo jammy-backports main restricted
deb [arch=i386,amd64] $c_deb_packages_repo jammy universe
deb [arch=i386,amd64] $c_deb_security_repo jammy-security main restricted
CONF
chroot_execute "apt update"
echo "======= setting locale, console and language =========="
chroot_execute "apt --yes --fix-broken install"
chroot_execute "apt install --yes -qq locales debconf-i18n apt-utils keyboard-configuration console-setup"
sed -i 's/# en_US.UTF-8/en_US.UTF-8/' "$c_zfs_mount_dir/etc/locale.gen"
sed -i 's/# fr_FR.UTF-8/fr_FR.UTF-8/' "$c_zfs_mount_dir/etc/locale.gen"
sed -i 's/# fr_FR.UTF-8/fr_FR.UTF-8/' "$c_zfs_mount_dir/etc/locale.gen"
sed -i 's/# de_AT.UTF-8/de_AT.UTF-8/' "$c_zfs_mount_dir/etc/locale.gen"
sed -i 's/# de_DE.UTF-8/de_DE.UTF-8/' "$c_zfs_mount_dir/etc/locale.gen"
chroot_execute 'cat <<CONF | debconf-set-selections
locales locales/default_environment_locale select en_US.UTF-8
keyboard-configuration keyboard-configuration/store_defaults_in_debconf_db boolean true
keyboard-configuration keyboard-configuration/variant select German
keyboard-configuration keyboard-configuration/unsupported_layout boolean true
keyboard-configuration keyboard-configuration/modelcode string pc105
keyboard-configuration keyboard-configuration/unsupported_config_layout boolean true
keyboard-configuration keyboard-configuration/layout select German
keyboard-configuration keyboard-configuration/layoutcode string de
keyboard-configuration keyboard-configuration/optionscode string
keyboard-configuration keyboard-configuration/toggle select No toggling
keyboard-configuration keyboard-configuration/xkb-keymap select de
keyboard-configuration keyboard-configuration/switch select No temporary switch
keyboard-configuration keyboard-configuration/unsupported_config_options boolean true
keyboard-configuration keyboard-configuration/ctrl_alt_bksp boolean false
keyboard-configuration keyboard-configuration/variantcode string
keyboard-configuration keyboard-configuration/model select Generic 105-key PC (intl.)
keyboard-configuration keyboard-configuration/altgr select The default for the keyboard layout
keyboard-configuration keyboard-configuration/compose select No compose key
keyboard-configuration keyboard-configuration/unsupported_options boolean true
console-setup console-setup/fontsize-fb47 select 8x16
console-setup console-setup/store_defaults_in_debconf_db boolean true
console-setup console-setup/codeset47 select # Latin1 and Latin5 - western Europe and Turkic languages
console-setup console-setup/fontface47 select Fixed
console-setup console-setup/fontsize string 8x16
console-setup console-setup/charmap47 select UTF-8
console-setup console-setup/fontsize-text47 select 8x16
console-setup console-setup/codesetcode string Lat15
tzdata tzdata/Areas select Europe
tzdata tzdata/Zones/Europe select Vienna
grub-pc grub-pc/install_devices_empty boolean true
CONF'
chroot_execute "dpkg-reconfigure locales -f noninteractive"
echo -e "LC_ALL=en_US.UTF-8\nLANG=en_US.UTF-8\n" >> "$c_zfs_mount_dir/etc/environment"
chroot_execute "dpkg-reconfigure keyboard-configuration -f noninteractive"
chroot_execute "dpkg-reconfigure console-setup -f noninteractive"
chroot_execute "setupcon"
chroot_execute "rm -f /etc/localtime /etc/timezone"
chroot_execute "dpkg-reconfigure tzdata -f noninteractive "
echo "======= installing latest kernel============="
chroot_execute "DEBIAN_FRONTEND=noninteractive apt install --yes linux-headers${v_kernel_variant} linux-image${v_kernel_variant}"
if [[ $v_kernel_variant == "-virtual" ]]; then
# linux-image-extra is only available for virtual hosts
chroot_execute "DEBIAN_FRONTEND=noninteractive apt install --yes linux-image-extra-virtual"
fi
echo "======= installing aux packages =========="
chroot_execute "apt install --yes man-db wget curl software-properties-common nano htop gnupg"
chroot_execute "systemctl disable thermald"
echo "======= installing zfs packages =========="
chroot_execute 'echo "zfs-dkms zfs-dkms/note-incompatible-licenses note true" | debconf-set-selections'
if [[ $v_zfs_experimental == "1" ]]; then
chroot_execute "wget -O - https://terem42.github.io/zfs-debian/apt_pub.gpg | apt-key add -"
chroot_execute "add-apt-repository 'deb https://terem42.github.io/zfs-debian/public zfs-debian-experimental main'"
chroot_execute "apt update"
chroot_execute "apt install -t zfs-debian-experimental --yes zfs-initramfs zfs-dkms zfsutils-linux"
else
chroot_execute "add-apt-repository --yes ppa:jonathonf/zfs"
chroot_execute "apt install --yes zfs-initramfs zfs-dkms zfsutils-linux"
fi
chroot_execute 'cat << DKMS > /etc/dkms/zfs.conf
# override for /usr/src/zfs-*/dkms.conf:
# always rebuild initrd when zfs module has been changed
# (either by a ZFS update or a new kernel version)
REMAKE_INITRD="yes"
DKMS'
echo "======= installing OpenSSH and network tooling =========="
chroot_execute "apt install --yes openssh-server net-tools"
echo "======= setup OpenSSH =========="
mkdir -p "$c_zfs_mount_dir/root/.ssh/"
cp /root/.ssh/authorized_keys "$c_zfs_mount_dir/root/.ssh/authorized_keys"
sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/g' "$c_zfs_mount_dir/etc/ssh/sshd_config"
sed -i 's/#PubkeyAuthentication yes/PubkeyAuthentication yes/g' "$c_zfs_mount_dir/etc/ssh/sshd_config"
chroot_execute "rm /etc/ssh/ssh_host_*"
chroot_execute "dpkg-reconfigure openssh-server -f noninteractive"
echo "======= set root password =========="
chroot_execute "echo root:$(printf "%q" "$v_root_password") | chpasswd"
echo "======= setting up zfs cache =========="
cp /etc/zpool.cache /mnt/etc/zfs/zpool.cache
echo "========setting up zfs module parameters========"
chroot_execute "echo options zfs zfs_arc_max=$((v_zfs_arc_max_mb * 1024 * 1024)) >> /etc/modprobe.d/zfs.conf"
echo "======= setting up grub =========="
chroot_execute "echo 'grub-pc grub-pc/install_devices_empty boolean true' | debconf-set-selections"
chroot_execute "DEBIAN_FRONTEND=noninteractive apt install --yes grub-pc"
chroot_execute "grub-install ${v_selected_disks[0]}"
chroot_execute "sed -i 's/#GRUB_TERMINAL=console/GRUB_TERMINAL=console/g' /etc/default/grub"
chroot_execute "sed -i 's|GRUB_CMDLINE_LINUX_DEFAULT=.*|GRUB_CMDLINE_LINUX_DEFAULT=\"net.ifnames=0\"|' /etc/default/grub"
chroot_execute "sed -i 's|GRUB_CMDLINE_LINUX=\"\"|GRUB_CMDLINE_LINUX=\"root=ZFS=rpool/ROOT/ubuntu\"|g' /etc/default/grub"
chroot_execute "sed -i 's/quiet//g' /etc/default/grub"
chroot_execute "sed -i 's/splash//g' /etc/default/grub"
chroot_execute "echo 'GRUB_DISABLE_OS_PROBER=true' >> /etc/default/grub"
for ((i = 1; i < ${#v_selected_disks[@]}; i++)); do
dd if="${v_selected_disks[0]}-part1" of="${v_selected_disks[i]}-part1"
done
if [[ $v_encrypt_rpool == "1" ]]; then
echo "=========set up dropbear=============="
chroot_execute "apt install --yes dropbear-initramfs"
mkdir -p "$c_zfs_mount_dir/etc/dropbear/initramfs"
cp /root/.ssh/authorized_keys "$c_zfs_mount_dir/etc/dropbear/initramfs/authorized_keys"
cp "$c_zfs_mount_dir/etc/ssh/ssh_host_rsa_key" "$c_zfs_mount_dir/etc/ssh/ssh_host_rsa_key_temp"
chroot_execute "ssh-keygen -p -i -m pem -N '' -f /etc/ssh/ssh_host_rsa_key_temp"
chroot_execute "/usr/lib/dropbear/dropbearconvert openssh dropbear /etc/ssh/ssh_host_rsa_key_temp /etc/dropbear/initramfs/dropbear_rsa_host_key"
rm -rf "$c_zfs_mount_dir/etc/ssh/ssh_host_rsa_key_temp"
cp "$c_zfs_mount_dir/etc/ssh/ssh_host_ecdsa_key" "$c_zfs_mount_dir/etc/ssh/ssh_host_ecdsa_key_temp"
chroot_execute "ssh-keygen -p -i -m pem -N '' -f /etc/ssh/ssh_host_ecdsa_key_temp"
chroot_execute "/usr/lib/dropbear/dropbearconvert openssh dropbear /etc/ssh/ssh_host_ecdsa_key_temp /etc/dropbear/initramfs/dropbear_ecdsa_host_key"
chroot_execute "rm -rf /etc/ssh/ssh_host_ecdsa_key_temp"
rm -rf "$c_zfs_mount_dir/etc/ssh/ssh_host_ecdsa_key_temp"
rm -rf "$c_zfs_mount_dir/etc/dropbear/initramfs/dropbear_dss_host_key"
fi
echo "============setup root prompt============"
cat > "$c_zfs_mount_dir/root/.bashrc" <<CONF
export PS1='\[\033[01;31m\]\u\[\033[01;33m\]@\[\033[01;32m\]\h \[\033[01;33m\]\w \[\033[01;35m\]\$ \[\033[00m\]'
umask 022
export LS_OPTIONS='--color=auto -h'
eval "\$(dircolors)"
CONF
echo "========running packages upgrade==========="
chroot_execute "apt upgrade --yes"
chroot_execute "apt purge cryptsetup* --yes"
echo "===========add static route to initramfs via hook to add default routes due to Ubuntu initramfs DHCP bug ========="
mkdir -p "$c_zfs_mount_dir/usr/share/initramfs-tools/scripts/init-premount"
cat > "$c_zfs_mount_dir/usr/share/initramfs-tools/scripts/init-premount/static-route" <<'CONF'
#!/bin/sh
PREREQ=""
prereqs()
{
echo "$PREREQ"
}
case $1 in
prereqs)
prereqs
exit 0
;;
esac
. /scripts/functions
# Begin real processing below this line
configure_networking
ip route add 172.31.1.1/255.255.255.255 dev ens3
ip route add default via 172.31.1.1 dev ens3
CONF
chmod 755 "$c_zfs_mount_dir/usr/share/initramfs-tools/scripts/init-premount/static-route"
echo "======= update initramfs =========="
chroot_execute "update-initramfs -u -k all"
echo "======= update grub =========="
chroot_execute "update-grub"
echo "======= setting up zed =========="
chroot_execute "zfs set canmount=noauto rpool"
echo "======= setting mountpoints =========="
chroot_execute "zfs set mountpoint=legacy $v_bpool_name/BOOT/ubuntu"
chroot_execute "echo $v_bpool_name/BOOT/ubuntu /boot zfs nodev,relatime,x-systemd.requires=zfs-mount.service,x-systemd.device-timeout=10 0 0 > /etc/fstab"
chroot_execute "zfs set mountpoint=legacy $v_rpool_name/var/log"
chroot_execute "echo $v_rpool_name/var/log /var/log zfs nodev,relatime 0 0 >> /etc/fstab"
chroot_execute "zfs set mountpoint=legacy $v_rpool_name/var/spool"
chroot_execute "echo $v_rpool_name/var/spool /var/spool zfs nodev,relatime 0 0 >> /etc/fstab"
chroot_execute "zfs set mountpoint=legacy $v_rpool_name/var/tmp"
chroot_execute "echo $v_rpool_name/var/tmp /var/tmp zfs nodev,relatime 0 0 >> /etc/fstab"
chroot_execute "zfs set mountpoint=legacy $v_rpool_name/tmp"
chroot_execute "echo $v_rpool_name/tmp /tmp zfs nodev,relatime 0 0 >> /etc/fstab"
echo "========= add swap, if defined"
if [[ $v_swap_size -gt 0 ]]; then
chroot_execute "echo /dev/zvol/$v_rpool_name/swap none swap discard 0 0 >> /etc/fstab"
fi
chroot_execute "echo RESUME=none > /etc/initramfs-tools/conf.d/resume"
echo "======= unmounting filesystems and zfs pools =========="
unmount_and_export_fs
echo "======== setup complete, rebooting ==============="
reboot

2
ubuntu_keys_add.sh Normal file
View File

@@ -0,0 +1,2 @@
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 3B4FE6ACC0B21F32
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 871920D1991BC9