mirror of
https://github.com/terem42/zfs-hetzner-vm.git
synced 2025-12-24 07:48:33 +00:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7adfb085a3 | ||
|
|
5bef08a173 | ||
|
|
8991c37e74 | ||
|
|
d5608e25d2 | ||
|
|
6521aad489 | ||
|
|
59b4f9860c | ||
|
|
7fd16b8e69 | ||
|
|
639f10c2c9 | ||
|
|
0b8ed0971f | ||
|
|
2cc49006ac | ||
|
|
9c98dad360 | ||
|
|
08aeddca0f | ||
|
|
4efef6b5a4 | ||
|
|
b67f314955 |
11
.github/workflows/shellcheck.yml
vendored
11
.github/workflows/shellcheck.yml
vendored
@@ -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
|
|
||||||
51
README.md
51
README.md
@@ -1,8 +1,6 @@
|
|||||||
# zfs-hetzner-vm
|
# zfs-hetzner-vm
|
||||||
|
|
||||||
[](https://github.com/terem42/zfs-hetzner-vm/actions/workflows/shellcheck.yml)
|
Scripts to install Debian 10 or Ubuntu 18 with ZFS root on Hetzner root servers (virtual and dedicated).<br/>
|
||||||
|
|
||||||
Scripts to install Debian 10, 11, 12, 13 or Ubuntu 18 LTS, 20 LTS, 22 LTS, 24 LTS with ZFS root on Hetzner root servers (virtual and dedicated).
|
|
||||||
__WARNING:__ all data on the disk will be destroyed.
|
__WARNING:__ all data on the disk will be destroyed.
|
||||||
|
|
||||||
## How to use:
|
## How to use:
|
||||||
@@ -14,64 +12,25 @@ __WARNING:__ all data on the disk will be destroyed.
|
|||||||
|
|
||||||
Debian 10 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-debian10-zfs-setup.sh | 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 -
|
|
||||||
````
|
|
||||||
|
|
||||||
Debian 13 minimal setup with SSH server
|
|
||||||
|
|
||||||
````bash
|
|
||||||
wget -qO- https://raw.githubusercontent.com/terem42/zfs-hetzner-vm/master/hetzner-debian13-zfs-setup.sh | bash -
|
|
||||||
````
|
|
||||||
|
|
||||||
Ubuntu 18.04 LTS minimal setup with SSH server
|
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 -
|
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
|
|
||||||
|
|
||||||
````bash
|
|
||||||
wget -qO- https://raw.githubusercontent.com/terem42/zfs-hetzner-vm/master/hetzner-ubuntu22-zfs-setup.sh | bash -
|
|
||||||
````
|
|
||||||
|
|
||||||
Ubuntu 24 LTS minimal setup with SSH server
|
|
||||||
|
|
||||||
````bash
|
|
||||||
wget -qO- https://raw.githubusercontent.com/terem42/zfs-hetzner-vm/master/hetzner-ubuntu24-zfs-setup.sh | bash -
|
|
||||||
````
|
|
||||||
|
|
||||||
Answer script questions about desired hostname and ZFS ARC cache size.
|
Answer script questions about desired hostname and ZFS ARC cache size.
|
||||||
|
|
||||||
To cope with network failures its higly recommended to run the commands above inside screen console, type `man screen` for more info.
|
To cope with network failures its higly recommended to run the commands above inside screen console, type `man screen` for more info.
|
||||||
Example of screen utility usage:
|
Example of screen utility usage:
|
||||||
|
````
|
||||||
````bash
|
screen -S zfs
|
||||||
export LC_ALL=en_US.UTF-8 && screen -S zfs
|
|
||||||
````
|
````
|
||||||
To detach from screen console, hit Ctrl-d then a
|
To detach from screen console, hit Ctrl-d then a
|
||||||
To reattach, type `screen -r zfs`
|
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
|
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.
|
|
||||||
|
|||||||
@@ -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
|
|
||||||
@@ -17,8 +17,6 @@ set -o errexit
|
|||||||
set -o pipefail
|
set -o pipefail
|
||||||
set -o nounset
|
set -o nounset
|
||||||
|
|
||||||
export TMPDIR=/tmp
|
|
||||||
|
|
||||||
# Variables
|
# Variables
|
||||||
v_bpool_name=
|
v_bpool_name=
|
||||||
v_bpool_tweaks=
|
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_zfs_arc_max_mb=250
|
||||||
c_default_bpool_tweaks="-o ashift=12 -O compression=lz4"
|
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_default_hostname=terem
|
||||||
c_zfs_mount_dir=/mnt
|
c_zfs_mount_dir=/mnt
|
||||||
c_log_dir=$(dirname "$(mktemp)")/zfs-hetzner-vm
|
c_log_dir=$(dirname "$(mktemp)")/zfs-hetzner-vm
|
||||||
@@ -64,9 +62,7 @@ function print_step_info_header {
|
|||||||
###############################################################################
|
###############################################################################
|
||||||
# ${FUNCNAME[1]}"
|
# ${FUNCNAME[1]}"
|
||||||
|
|
||||||
if [[ "${1:-}" != "" ]]; then
|
[[ "${1:-}" != "" ]] && echo -n " $1" || true
|
||||||
echo -n " $1"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "
|
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.
|
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.
|
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 {
|
function store_os_distro_information {
|
||||||
@@ -138,20 +134,20 @@ function check_prerequisites {
|
|||||||
|
|
||||||
function initial_load_debian_zed_cache {
|
function initial_load_debian_zed_cache {
|
||||||
chroot_execute "mkdir /etc/zfs/zfs-list.cache"
|
chroot_execute "mkdir /etc/zfs/zfs-list.cache"
|
||||||
chroot_execute "touch /etc/zfs/zfs-list.cache/$v_rpool_name"
|
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 "ln -sf /usr/lib/zfs-linux/zed.d/history_event-zfs-list-cacher.sh /etc/zfs/zed.d/"
|
||||||
|
|
||||||
chroot_execute "zed -F &"
|
chroot_execute "zed -F &"
|
||||||
|
|
||||||
local success=0
|
local success=0
|
||||||
|
|
||||||
if [[ ! -e "$c_zfs_mount_dir/etc/zfs/zfs-list.cache/$v_rpool_name" ]] || [[ -e "$c_zfs_mount_dir/etc/zfs/zfs-list.cache/$v_rpool_name" && (( $(find "$c_zfs_mount_dir/etc/zfs/zfs-list.cache/$v_rpool_name" -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 $v_rpool_name"
|
chroot_execute "zfs set canmount=noauto rpool"
|
||||||
|
|
||||||
SECONDS=0
|
SECONDS=0
|
||||||
|
|
||||||
while (( SECONDS++ <= 120 )); do
|
while (( SECONDS++ <= 120 )); do
|
||||||
if [[ -e "$c_zfs_mount_dir/etc/zfs/zfs-list.cache/$v_rpool_name" ]] && (( $(find "$c_zfs_mount_dir/etc/zfs/zfs-list.cache/$v_rpool_name" -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
|
success=1
|
||||||
break
|
break
|
||||||
else
|
else
|
||||||
@@ -169,7 +165,7 @@ function initial_load_debian_zed_cache {
|
|||||||
|
|
||||||
chroot_execute "pkill zed"
|
chroot_execute "pkill zed"
|
||||||
|
|
||||||
sed -Ei "s|/$c_zfs_mount_dir/?|/|g" "$c_zfs_mount_dir/etc/zfs/zfs-list.cache/$v_rpool_name"
|
sed -Ei 's|/mnt/?|/|g' /mnt/etc/zfs/zfs-list.cache/rpool
|
||||||
}
|
}
|
||||||
|
|
||||||
function find_suitable_disks {
|
function find_suitable_disks {
|
||||||
@@ -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"'`.
|
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
|
exit 1
|
||||||
fi
|
fi
|
||||||
@@ -243,7 +239,7 @@ function select_disks {
|
|||||||
|
|
||||||
Devices with mounted partitions, cdroms, and removable devices are not displayed!
|
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
|
if [[ ${#v_selected_disks[@]} -gt 0 ]]; then
|
||||||
break
|
break
|
||||||
@@ -260,7 +256,7 @@ function ask_swap_size {
|
|||||||
local swap_size_invalid_message=
|
local swap_size_invalid_message=
|
||||||
|
|
||||||
while [[ ! $v_swap_size =~ ^[0-9]+$ ]]; do
|
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! "
|
swap_size_invalid_message="Invalid swap size! "
|
||||||
done
|
done
|
||||||
@@ -275,7 +271,7 @@ function ask_free_tail_space {
|
|||||||
local tail_space_invalid_message=
|
local tail_space_invalid_message=
|
||||||
|
|
||||||
while [[ ! $v_free_tail_space =~ ^[0-9]+$ ]]; do
|
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! "
|
tail_space_invalid_message="Invalid size! "
|
||||||
done
|
done
|
||||||
@@ -290,7 +286,7 @@ function ask_zfs_arc_max_size {
|
|||||||
local zfs_arc_max_invalid_message=
|
local zfs_arc_max_invalid_message=
|
||||||
|
|
||||||
while [[ ! $v_zfs_arc_max_mb =~ ^[0-9]+$ ]]; do
|
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! "
|
zfs_arc_max_invalid_message="Invalid size! "
|
||||||
done
|
done
|
||||||
@@ -306,14 +302,14 @@ function ask_pool_names {
|
|||||||
local bpool_name_invalid_message=
|
local bpool_name_invalid_message=
|
||||||
|
|
||||||
while [[ ! $v_bpool_name =~ ^[a-z][a-zA-Z_:.-]+$ ]]; do
|
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! "
|
bpool_name_invalid_message="Invalid pool name! "
|
||||||
done
|
done
|
||||||
local rpool_name_invalid_message=
|
local rpool_name_invalid_message=
|
||||||
|
|
||||||
while [[ ! $v_rpool_name =~ ^[a-z][a-zA-Z_:.-]+$ ]]; do
|
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! "
|
rpool_name_invalid_message="Invalid pool name! "
|
||||||
done
|
done
|
||||||
@@ -325,8 +321,8 @@ function ask_pool_tweaks {
|
|||||||
# shellcheck disable=SC2119
|
# shellcheck disable=SC2119
|
||||||
print_step_info_header
|
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_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 --inputbox "Insert the tweaks for the root pool" 30 100 -- "$c_default_rpool_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
|
print_variables v_bpool_tweaks v_rpool_tweaks
|
||||||
}
|
}
|
||||||
@@ -341,8 +337,8 @@ function ask_root_password {
|
|||||||
local password_repeat=-
|
local password_repeat=-
|
||||||
|
|
||||||
while [[ "$v_root_password" != "$password_repeat" || "$v_root_password" == "" ]]; do
|
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)
|
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 --passwordbox "Please repeat the password:" 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! "
|
password_invalid_message="Passphrase empty, or not matching! "
|
||||||
done
|
done
|
||||||
@@ -352,7 +348,7 @@ function ask_root_password {
|
|||||||
function ask_encryption {
|
function ask_encryption {
|
||||||
print_step_info_header
|
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
|
v_encrypt_rpool=1
|
||||||
fi
|
fi
|
||||||
set +x
|
set +x
|
||||||
@@ -360,8 +356,8 @@ function ask_encryption {
|
|||||||
local passphrase_invalid_message=
|
local passphrase_invalid_message=
|
||||||
local passphrase_repeat=-
|
local passphrase_repeat=-
|
||||||
while [[ "$v_passphrase" != "$passphrase_repeat" || ${#v_passphrase} -lt 8 ]]; do
|
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)
|
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 --passwordbox "Please repeat the passphrase:" 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! "
|
passphrase_invalid_message="Passphrase too short, or not matching! "
|
||||||
done
|
done
|
||||||
@@ -372,7 +368,7 @@ function ask_encryption {
|
|||||||
function ask_zfs_experimental {
|
function ask_zfs_experimental {
|
||||||
print_step_info_header
|
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
|
v_zfs_experimental=1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
@@ -383,8 +379,8 @@ function ask_hostname {
|
|||||||
|
|
||||||
local hostname_invalid_message=
|
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)
|
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! "
|
hostname_invalid_message="Invalid host name! "
|
||||||
done
|
done
|
||||||
@@ -399,7 +395,7 @@ function determine_kernel_variant {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function chroot_execute {
|
function chroot_execute {
|
||||||
chroot $c_zfs_mount_dir bash -c "DEBIAN_FRONTEND=noninteractive $1"
|
chroot $c_zfs_mount_dir bash -c "$1"
|
||||||
}
|
}
|
||||||
|
|
||||||
function unmount_and_export_fs {
|
function unmount_and_export_fs {
|
||||||
@@ -435,8 +431,9 @@ function unmount_and_export_fs {
|
|||||||
zpools_exported=99
|
zpools_exported=99
|
||||||
echo "===========exporting zfs pools============="
|
echo "===========exporting zfs pools============="
|
||||||
set +e
|
set +e
|
||||||
while (( zpools_exported == 99 )) && (( SECONDS++ <= 60 )); do
|
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
|
zpools_exported=1
|
||||||
echo "all zfs pools were succesfully exported"
|
echo "all zfs pools were succesfully exported"
|
||||||
break;
|
break;
|
||||||
@@ -453,7 +450,6 @@ function unmount_and_export_fs {
|
|||||||
|
|
||||||
#################### MAIN ################################
|
#################### MAIN ################################
|
||||||
export LC_ALL=en_US.UTF-8
|
export LC_ALL=en_US.UTF-8
|
||||||
export NCURSES_NO_UTF8_ACS=1
|
|
||||||
|
|
||||||
check_prerequisites
|
check_prerequisites
|
||||||
|
|
||||||
@@ -494,20 +490,11 @@ for kver in $(find /lib/modules/* -maxdepth 0 -type d | grep -v "$(uname -r)" |
|
|||||||
done
|
done
|
||||||
|
|
||||||
echo "======= installing zfs on rescue system =========="
|
echo "======= installing zfs on rescue system =========="
|
||||||
echo "zfs-dkms zfs-dkms/note-incompatible-licenses note true" | debconf-set-selections
|
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
|
apt update
|
||||||
export PATH=$PATH:/usr/sbin
|
apt install --yes -t buster-backports libelf-dev zfs-dkms
|
||||||
|
modprobe zfs
|
||||||
zfs --version
|
zfs --version
|
||||||
|
|
||||||
echo "======= partitioning the disk =========="
|
echo "======= partitioning the disk =========="
|
||||||
@@ -519,9 +506,9 @@ echo "======= partitioning the disk =========="
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
for selected_disk in "${v_selected_disks[@]}"; do
|
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 -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
|
sgdisk -n3:0:"$tail_space_parameter" -t3:BF01 "$selected_disk" # Root pool
|
||||||
done
|
done
|
||||||
|
|
||||||
@@ -548,16 +535,12 @@ echo "======= create zfs pools and datasets =========="
|
|||||||
pools_mirror_option=
|
pools_mirror_option=
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# shellcheck disable=SC2086
|
|
||||||
zpool create \
|
zpool create \
|
||||||
$v_bpool_tweaks -O canmount=off -O devices=off \
|
$v_bpool_tweaks -O canmount=off -O devices=off \
|
||||||
-o cachefile=/etc/zpool.cache \
|
|
||||||
-O mountpoint=/boot -R $c_zfs_mount_dir -f \
|
-O mountpoint=/boot -R $c_zfs_mount_dir -f \
|
||||||
$v_bpool_name $pools_mirror_option "${bpool_disks_partitions[@]}"
|
$v_bpool_name $pools_mirror_option "${bpool_disks_partitions[@]}"
|
||||||
|
|
||||||
# shellcheck disable=SC2086
|
|
||||||
echo -n "$v_passphrase" | zpool create \
|
echo -n "$v_passphrase" | zpool create \
|
||||||
-o cachefile=/etc/zpool.cache \
|
|
||||||
$v_rpool_tweaks \
|
$v_rpool_tweaks \
|
||||||
"${encryption_options[@]}" \
|
"${encryption_options[@]}" \
|
||||||
-O mountpoint=/ -R $c_zfs_mount_dir -f \
|
-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 mount "$v_bpool_name/BOOT/debian"
|
||||||
|
|
||||||
zfs create "$v_rpool_name/home"
|
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"
|
||||||
|
zfs create -o canmount=off "$v_rpool_name/var/lib"
|
||||||
zfs create "$v_rpool_name/var/log"
|
zfs create "$v_rpool_name/var/log"
|
||||||
zfs create "$v_rpool_name/var/spool"
|
zfs create "$v_rpool_name/var/spool"
|
||||||
|
|
||||||
@@ -624,7 +609,7 @@ ff02::2 ip6-allrouters
|
|||||||
ff02::3 ip6-allhosts
|
ff02::3 ip6-allhosts
|
||||||
CONF
|
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
|
cat <<CONF > /mnt/etc/systemd/network/10-eth0.network
|
||||||
[Match]
|
[Match]
|
||||||
@@ -636,7 +621,9 @@ Address=${ip6addr_prefix}:1/64
|
|||||||
Gateway=fe80::1
|
Gateway=fe80::1
|
||||||
CONF
|
CONF
|
||||||
chroot_execute "systemctl enable systemd-networkd.service"
|
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 =========="
|
echo "======= preparing the jail for chroot =========="
|
||||||
for virtual_fs_dir in proc sys dev; do
|
for virtual_fs_dir in proc sys dev; do
|
||||||
@@ -721,12 +708,6 @@ if [[ $v_zfs_experimental == "1" ]]; then
|
|||||||
else
|
else
|
||||||
chroot_execute "apt install --yes -t buster-backports zfs-initramfs zfs-dkms zfsutils-linux"
|
chroot_execute "apt install --yes -t buster-backports zfs-initramfs zfs-dkms zfsutils-linux"
|
||||||
fi
|
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 =========="
|
echo "======= installing OpenSSH and network tooling =========="
|
||||||
chroot_execute "apt install --yes openssh-server net-tools"
|
chroot_execute "apt install --yes openssh-server net-tools"
|
||||||
@@ -742,22 +723,37 @@ chroot_execute "dpkg-reconfigure openssh-server -f noninteractive"
|
|||||||
echo "======= set root password =========="
|
echo "======= set root password =========="
|
||||||
chroot_execute "echo root:$(printf "%q" "$v_root_password") | chpasswd"
|
chroot_execute "echo root:$(printf "%q" "$v_root_password") | chpasswd"
|
||||||
|
|
||||||
echo "======= setting up zfs cache =========="
|
echo "======= setting up zfs services =========="
|
||||||
cp /etc/zpool.cache /mnt/etc/zfs/zpool.cache
|
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========"
|
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"
|
chroot_execute "echo options zfs zfs_arc_max=$((v_zfs_arc_max_mb * 1024 * 1024)) >> /etc/modprobe.d/zfs.conf"
|
||||||
|
|
||||||
echo "======= setting up grub =========="
|
echo "======= setting up grub =========="
|
||||||
chroot_execute "echo 'grub-pc grub-pc/install_devices_empty boolean true' | debconf-set-selections"
|
chroot_execute "echo 'grub-pc grub-pc/install_devices_empty boolean true' | debconf-set-selections"
|
||||||
chroot_execute "apt install --yes grub-pc"
|
chroot_execute "DEBIAN_FRONTEND=noninteractive apt install --yes grub-pc"
|
||||||
for disk in ${v_selected_disks[@]}; do
|
chroot_execute "grub-install ${v_selected_disks[0]}"
|
||||||
chroot_execute "grub-install $disk"
|
|
||||||
done
|
|
||||||
|
|
||||||
chroot_execute "sed -i 's/#GRUB_TERMINAL=console/GRUB_TERMINAL=console/g' /etc/default/grub"
|
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_DEFAULT=.*|GRUB_CMDLINE_LINUX_DEFAULT=\"net.ifnames=0\"|' /etc/default/grub"
|
||||||
chroot_execute "sed -i 's|GRUB_CMDLINE_LINUX=\"\"|GRUB_CMDLINE_LINUX=\"root=ZFS=$v_rpool_name/ROOT/debian\"|g' /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/quiet//g' /etc/default/grub"
|
||||||
chroot_execute "sed -i 's/splash//g' /etc/default/grub"
|
chroot_execute "sed -i 's/splash//g' /etc/default/grub"
|
||||||
@@ -771,8 +767,7 @@ if [[ $v_encrypt_rpool == "1" ]]; then
|
|||||||
echo "=========set up dropbear=============="
|
echo "=========set up dropbear=============="
|
||||||
|
|
||||||
chroot_execute "apt install --yes dropbear-initramfs"
|
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"
|
cp "$c_zfs_mount_dir/etc/ssh/ssh_host_rsa_key" "$c_zfs_mount_dir/etc/ssh/ssh_host_rsa_key_temp"
|
||||||
@@ -789,6 +784,18 @@ if [[ $v_encrypt_rpool == "1" ]]; then
|
|||||||
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
|
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============"
|
echo "============setup root prompt============"
|
||||||
cat > "$c_zfs_mount_dir/root/.bashrc" <<CONF
|
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\]'
|
export PS1='\[\033[01;31m\]\u\[\033[01;33m\]@\[\033[01;32m\]\h \[\033[01;33m\]\w \[\033[01;35m\]\$ \[\033[00m\]'
|
||||||
@@ -799,45 +806,9 @@ CONF
|
|||||||
|
|
||||||
echo "========running packages upgrade==========="
|
echo "========running packages upgrade==========="
|
||||||
chroot_execute "apt upgrade --yes"
|
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 ========="
|
#echo "===========add static route to initramfs via hook to add default routes due to initramfs DHCP bug ========="
|
||||||
mkdir -p "$c_zfs_mount_dir/usr/share/initramfs-tools/scripts/init-premount"
|
# removed
|
||||||
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 "======= update initramfs =========="
|
echo "======= update initramfs =========="
|
||||||
chroot_execute "update-initramfs -u -k all"
|
chroot_execute "update-initramfs -u -k all"
|
||||||
@@ -847,14 +818,14 @@ chroot_execute "update-grub"
|
|||||||
|
|
||||||
echo "======= setting up zed =========="
|
echo "======= setting up zed =========="
|
||||||
if [[ $v_zfs_experimental == "1" ]]; then
|
if [[ $v_zfs_experimental == "1" ]]; then
|
||||||
chroot_execute "zfs set canmount=noauto $v_rpool_name"
|
chroot_execute "zfs set canmount=noauto rpool"
|
||||||
else
|
else
|
||||||
initial_load_debian_zed_cache
|
initial_load_debian_zed_cache
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "======= setting mountpoints =========="
|
echo "======= setting mountpoints =========="
|
||||||
chroot_execute "zfs set mountpoint=legacy $v_bpool_name/BOOT/debian"
|
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 "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 "echo $v_rpool_name/var/log /var/log zfs nodev,relatime 0 0 >> /etc/fstab"
|
||||||
@@ -866,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"
|
chroot_execute "echo $v_rpool_name/tmp /tmp zfs nodev,relatime 0 0 >> /etc/fstab"
|
||||||
|
|
||||||
echo "========= add swap, if defined"
|
echo "========= add swap, if defined"
|
||||||
if [[ $v_swap_size -gt 0 ]]; then
|
[[ $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 /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"
|
chroot_execute "echo RESUME=none > /etc/initramfs-tools/conf.d/resume"
|
||||||
|
|
||||||
echo "======= unmounting filesystems and zfs pools =========="
|
echo "======= unmounting filesystems and zfs pools =========="
|
||||||
|
|||||||
@@ -17,8 +17,6 @@ set -o errexit
|
|||||||
set -o pipefail
|
set -o pipefail
|
||||||
set -o nounset
|
set -o nounset
|
||||||
|
|
||||||
export TMPDIR=/tmp
|
|
||||||
|
|
||||||
# Variables
|
# Variables
|
||||||
v_bpool_name=
|
v_bpool_name=
|
||||||
v_bpool_tweaks=
|
v_bpool_tweaks=
|
||||||
@@ -64,10 +62,7 @@ function print_step_info_header {
|
|||||||
###############################################################################
|
###############################################################################
|
||||||
# ${FUNCNAME[1]}"
|
# ${FUNCNAME[1]}"
|
||||||
|
|
||||||
if [[ "${1:-}" != "" ]]; then
|
[[ "${1:-}" != "" ]] && echo -n " $1" || true
|
||||||
echo -n " $1"
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
echo "
|
echo "
|
||||||
###############################################################################
|
###############################################################################
|
||||||
@@ -111,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.
|
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.
|
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 {
|
function store_os_distro_information {
|
||||||
@@ -139,20 +134,20 @@ function check_prerequisites {
|
|||||||
|
|
||||||
function initial_load_debian_zed_cache {
|
function initial_load_debian_zed_cache {
|
||||||
chroot_execute "mkdir /etc/zfs/zfs-list.cache"
|
chroot_execute "mkdir /etc/zfs/zfs-list.cache"
|
||||||
chroot_execute "touch /etc/zfs/zfs-list.cache/$v_rpool_name"
|
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 "ln -sf /usr/lib/zfs-linux/zed.d/history_event-zfs-list-cacher.sh /etc/zfs/zed.d/"
|
||||||
|
|
||||||
chroot_execute "zed -F &"
|
chroot_execute "zed -F &"
|
||||||
|
|
||||||
local success=0
|
local success=0
|
||||||
|
|
||||||
if [[ ! -e "$c_zfs_mount_dir/etc/zfs/zfs-list.cache/$v_rpool_name" ]] || [[ -e "$c_zfs_mount_dir/etc/zfs/zfs-list.cache/$v_rpool_name" && (( $(find "$c_zfs_mount_dir/etc/zfs/zfs-list.cache/$v_rpool_name" -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 $v_rpool_name"
|
chroot_execute "zfs set canmount=noauto rpool"
|
||||||
|
|
||||||
SECONDS=0
|
SECONDS=0
|
||||||
|
|
||||||
while (( SECONDS++ <= 120 )); do
|
while (( SECONDS++ <= 120 )); do
|
||||||
if [[ -e "$c_zfs_mount_dir/etc/zfs/zfs-list.cache/$v_rpool_name" ]] && (( $(find "$c_zfs_mount_dir/etc/zfs/zfs-list.cache/$v_rpool_name" -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
|
success=1
|
||||||
break
|
break
|
||||||
else
|
else
|
||||||
@@ -170,7 +165,7 @@ function initial_load_debian_zed_cache {
|
|||||||
|
|
||||||
chroot_execute "pkill zed"
|
chroot_execute "pkill zed"
|
||||||
|
|
||||||
sed -Ei "s|/$c_zfs_mount_dir/?|/|g" "$c_zfs_mount_dir/etc/zfs/zfs-list.cache/$v_rpool_name"
|
sed -Ei 's|/mnt/?|/|g' /mnt/etc/zfs/zfs-list.cache/rpool
|
||||||
}
|
}
|
||||||
|
|
||||||
function find_suitable_disks {
|
function find_suitable_disks {
|
||||||
@@ -215,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"'`.
|
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
|
exit 1
|
||||||
fi
|
fi
|
||||||
@@ -244,7 +239,7 @@ function select_disks {
|
|||||||
|
|
||||||
Devices with mounted partitions, cdroms, and removable devices are not displayed!
|
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
|
if [[ ${#v_selected_disks[@]} -gt 0 ]]; then
|
||||||
break
|
break
|
||||||
@@ -261,7 +256,7 @@ function ask_swap_size {
|
|||||||
local swap_size_invalid_message=
|
local swap_size_invalid_message=
|
||||||
|
|
||||||
while [[ ! $v_swap_size =~ ^[0-9]+$ ]]; do
|
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! "
|
swap_size_invalid_message="Invalid swap size! "
|
||||||
done
|
done
|
||||||
@@ -276,7 +271,7 @@ function ask_free_tail_space {
|
|||||||
local tail_space_invalid_message=
|
local tail_space_invalid_message=
|
||||||
|
|
||||||
while [[ ! $v_free_tail_space =~ ^[0-9]+$ ]]; do
|
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! "
|
tail_space_invalid_message="Invalid size! "
|
||||||
done
|
done
|
||||||
@@ -291,7 +286,7 @@ function ask_zfs_arc_max_size {
|
|||||||
local zfs_arc_max_invalid_message=
|
local zfs_arc_max_invalid_message=
|
||||||
|
|
||||||
while [[ ! $v_zfs_arc_max_mb =~ ^[0-9]+$ ]]; do
|
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! "
|
zfs_arc_max_invalid_message="Invalid size! "
|
||||||
done
|
done
|
||||||
@@ -307,14 +302,14 @@ function ask_pool_names {
|
|||||||
local bpool_name_invalid_message=
|
local bpool_name_invalid_message=
|
||||||
|
|
||||||
while [[ ! $v_bpool_name =~ ^[a-z][a-zA-Z_:.-]+$ ]]; do
|
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! "
|
bpool_name_invalid_message="Invalid pool name! "
|
||||||
done
|
done
|
||||||
local rpool_name_invalid_message=
|
local rpool_name_invalid_message=
|
||||||
|
|
||||||
while [[ ! $v_rpool_name =~ ^[a-z][a-zA-Z_:.-]+$ ]]; do
|
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! "
|
rpool_name_invalid_message="Invalid pool name! "
|
||||||
done
|
done
|
||||||
@@ -326,8 +321,8 @@ function ask_pool_tweaks {
|
|||||||
# shellcheck disable=SC2119
|
# shellcheck disable=SC2119
|
||||||
print_step_info_header
|
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_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 --inputbox "Insert the tweaks for the root pool" 30 100 -- "$c_default_rpool_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
|
print_variables v_bpool_tweaks v_rpool_tweaks
|
||||||
}
|
}
|
||||||
@@ -342,8 +337,8 @@ function ask_root_password {
|
|||||||
local password_repeat=-
|
local password_repeat=-
|
||||||
|
|
||||||
while [[ "$v_root_password" != "$password_repeat" || "$v_root_password" == "" ]]; do
|
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)
|
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 --passwordbox "Please repeat the password:" 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! "
|
password_invalid_message="Passphrase empty, or not matching! "
|
||||||
done
|
done
|
||||||
@@ -353,7 +348,7 @@ function ask_root_password {
|
|||||||
function ask_encryption {
|
function ask_encryption {
|
||||||
print_step_info_header
|
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
|
v_encrypt_rpool=1
|
||||||
fi
|
fi
|
||||||
set +x
|
set +x
|
||||||
@@ -361,8 +356,8 @@ function ask_encryption {
|
|||||||
local passphrase_invalid_message=
|
local passphrase_invalid_message=
|
||||||
local passphrase_repeat=-
|
local passphrase_repeat=-
|
||||||
while [[ "$v_passphrase" != "$passphrase_repeat" || ${#v_passphrase} -lt 8 ]]; do
|
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)
|
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 --passwordbox "Please repeat the passphrase:" 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! "
|
passphrase_invalid_message="Passphrase too short, or not matching! "
|
||||||
done
|
done
|
||||||
@@ -373,7 +368,7 @@ function ask_encryption {
|
|||||||
function ask_zfs_experimental {
|
function ask_zfs_experimental {
|
||||||
print_step_info_header
|
print_step_info_header
|
||||||
|
|
||||||
if dialog --defaultno --yesno 'Do you want to use experimental zfs module build?' 30 100; then
|
if dialog --defaultno --ascii-lines --yesno 'Do you want to use experimental zfs module build?' 30 100; then
|
||||||
v_zfs_experimental=1
|
v_zfs_experimental=1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
@@ -384,8 +379,8 @@ function ask_hostname {
|
|||||||
|
|
||||||
local hostname_invalid_message=
|
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)
|
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! "
|
hostname_invalid_message="Invalid host name! "
|
||||||
done
|
done
|
||||||
@@ -400,7 +395,7 @@ function determine_kernel_variant {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function chroot_execute {
|
function chroot_execute {
|
||||||
chroot $c_zfs_mount_dir bash -c "DEBIAN_FRONTEND=noninteractive $1"
|
chroot $c_zfs_mount_dir bash -c "$1"
|
||||||
}
|
}
|
||||||
|
|
||||||
function unmount_and_export_fs {
|
function unmount_and_export_fs {
|
||||||
@@ -436,8 +431,9 @@ function unmount_and_export_fs {
|
|||||||
zpools_exported=99
|
zpools_exported=99
|
||||||
echo "===========exporting zfs pools============="
|
echo "===========exporting zfs pools============="
|
||||||
set +e
|
set +e
|
||||||
while (( zpools_exported == 99 )) && (( SECONDS++ <= 60 )); do
|
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
|
zpools_exported=1
|
||||||
echo "all zfs pools were succesfully exported"
|
echo "all zfs pools were succesfully exported"
|
||||||
break;
|
break;
|
||||||
@@ -454,7 +450,6 @@ function unmount_and_export_fs {
|
|||||||
|
|
||||||
#################### MAIN ################################
|
#################### MAIN ################################
|
||||||
export LC_ALL=en_US.UTF-8
|
export LC_ALL=en_US.UTF-8
|
||||||
export NCURSES_NO_UTF8_ACS=1
|
|
||||||
|
|
||||||
check_prerequisites
|
check_prerequisites
|
||||||
|
|
||||||
@@ -495,20 +490,28 @@ for kver in $(find /lib/modules/* -maxdepth 0 -type d | grep -v "$(uname -r)" |
|
|||||||
done
|
done
|
||||||
|
|
||||||
echo "======= installing zfs on rescue system =========="
|
echo "======= installing zfs on rescue system =========="
|
||||||
echo "zfs-dkms zfs-dkms/note-incompatible-licenses note true" | debconf-set-selections
|
echo "zfs-dkms zfs-dkms/note-incompatible-licenses note true" | debconf-set-selections
|
||||||
# echo "y" | zfs
|
if [[ $v_zfs_experimental == "1" ]]; then
|
||||||
# linux-headers-generic linux-image-generic
|
apt install --yes man wget curl software-properties-common nano htop gnupg
|
||||||
apt install --yes software-properties-common dpkg-dev dkms
|
wget -O - https://terem42.github.io/zfs-debian/apt_pub.gpg | apt-key add -
|
||||||
rm -f "$(which zfs)"
|
add-apt-repository 'deb https://terem42.github.io/zfs-debian/public zfs-debian-experimental main'
|
||||||
rm -f "$(which zpool)"
|
apt update
|
||||||
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
|
apt install --yes libelf-dev
|
||||||
echo -e "Package: src:zfs-linux\nPin: release n=testing\nPin-Priority: 990\n" > /etc/apt/preferences.d/90_zfs
|
apt install -t zfs-debian-experimental --yes zfs-dkms zfsutils-linux
|
||||||
apt update
|
else
|
||||||
apt install -t testing --yes zfs-dkms zfsutils-linux
|
cd "$(mktemp -d)"
|
||||||
rm /etc/apt/sources.list.d/bookworm-testing.list
|
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)"
|
||||||
rm /etc/apt/preferences.d/90_zfs
|
apt update
|
||||||
apt update
|
apt install libssl-dev uuid-dev zlib1g-dev libblkid-dev -y
|
||||||
export PATH=$PATH:/usr/sbin
|
tar xfv zfs*.tar.gz
|
||||||
|
rm *.tar.gz
|
||||||
|
cd zfs*
|
||||||
|
./configure
|
||||||
|
make -j "$(nproc)"
|
||||||
|
make install
|
||||||
|
ldconfig
|
||||||
|
modprobe zfs
|
||||||
|
fi
|
||||||
zfs --version
|
zfs --version
|
||||||
|
|
||||||
echo "======= partitioning the disk =========="
|
echo "======= partitioning the disk =========="
|
||||||
@@ -522,7 +525,7 @@ echo "======= partitioning the disk =========="
|
|||||||
for selected_disk in "${v_selected_disks[@]}"; do
|
for selected_disk in "${v_selected_disks[@]}"; do
|
||||||
wipefs --all --force "$selected_disk"
|
wipefs --all --force "$selected_disk"
|
||||||
sgdisk -a1 -n1:24K:+1000K -t1:EF02 "$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
|
sgdisk -n3:0:"$tail_space_parameter" -t3:BF01 "$selected_disk" # Root pool
|
||||||
done
|
done
|
||||||
|
|
||||||
@@ -549,17 +552,13 @@ echo "======= create zfs pools and datasets =========="
|
|||||||
pools_mirror_option=
|
pools_mirror_option=
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# shellcheck disable=SC2086
|
|
||||||
zpool create \
|
zpool create \
|
||||||
$v_bpool_tweaks -O canmount=off -O devices=off \
|
$v_bpool_tweaks -O canmount=off -O devices=off \
|
||||||
-o cachefile=/etc/zpool.cache \
|
|
||||||
-O mountpoint=/boot -R $c_zfs_mount_dir -f \
|
-O mountpoint=/boot -R $c_zfs_mount_dir -f \
|
||||||
$v_bpool_name $pools_mirror_option "${bpool_disks_partitions[@]}"
|
$v_bpool_name $pools_mirror_option "${bpool_disks_partitions[@]}"
|
||||||
|
|
||||||
# shellcheck disable=SC2086
|
|
||||||
echo -n "$v_passphrase" | zpool create \
|
echo -n "$v_passphrase" | zpool create \
|
||||||
$v_rpool_tweaks \
|
$v_rpool_tweaks \
|
||||||
-o cachefile=/etc/zpool.cache \
|
|
||||||
"${encryption_options[@]}" \
|
"${encryption_options[@]}" \
|
||||||
-O mountpoint=/ -R $c_zfs_mount_dir -f \
|
-O mountpoint=/ -R $c_zfs_mount_dir -f \
|
||||||
$v_rpool_name $pools_mirror_option "${rpool_disks_partitions[@]}"
|
$v_rpool_name $pools_mirror_option "${rpool_disks_partitions[@]}"
|
||||||
@@ -574,7 +573,9 @@ zfs create -o canmount=noauto -o mountpoint=/boot "$v_bpool_name/BOOT/debian"
|
|||||||
zfs mount "$v_bpool_name/BOOT/debian"
|
zfs mount "$v_bpool_name/BOOT/debian"
|
||||||
|
|
||||||
zfs create "$v_rpool_name/home"
|
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"
|
||||||
|
zfs create -o canmount=off "$v_rpool_name/var/lib"
|
||||||
zfs create "$v_rpool_name/var/log"
|
zfs create "$v_rpool_name/var/log"
|
||||||
zfs create "$v_rpool_name/var/spool"
|
zfs create "$v_rpool_name/var/spool"
|
||||||
|
|
||||||
@@ -625,7 +626,7 @@ ff02::2 ip6-allrouters
|
|||||||
ff02::3 ip6-allhosts
|
ff02::3 ip6-allhosts
|
||||||
CONF
|
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
|
cat <<CONF > /mnt/etc/systemd/network/10-eth0.network
|
||||||
[Match]
|
[Match]
|
||||||
@@ -637,9 +638,9 @@ Address=${ip6addr_prefix}:1/64
|
|||||||
Gateway=fe80::1
|
Gateway=fe80::1
|
||||||
CONF
|
CONF
|
||||||
chroot_execute "systemctl enable systemd-networkd.service"
|
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 =========="
|
echo "======= preparing the jail for chroot =========="
|
||||||
for virtual_fs_dir in proc sys dev; do
|
for virtual_fs_dir in proc sys dev; do
|
||||||
@@ -722,14 +723,8 @@ if [[ $v_zfs_experimental == "1" ]]; then
|
|||||||
chroot_execute "apt update"
|
chroot_execute "apt update"
|
||||||
chroot_execute "apt install -t zfs-debian-experimental --yes zfs-initramfs zfs-dkms zfsutils-linux"
|
chroot_execute "apt install -t zfs-debian-experimental --yes zfs-initramfs zfs-dkms zfsutils-linux"
|
||||||
else
|
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
|
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 =========="
|
echo "======= installing OpenSSH and network tooling =========="
|
||||||
chroot_execute "apt install --yes openssh-server net-tools"
|
chroot_execute "apt install --yes openssh-server net-tools"
|
||||||
@@ -745,24 +740,38 @@ chroot_execute "dpkg-reconfigure openssh-server -f noninteractive"
|
|||||||
echo "======= set root password =========="
|
echo "======= set root password =========="
|
||||||
chroot_execute "echo root:$(printf "%q" "$v_root_password") | chpasswd"
|
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========"
|
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"
|
chroot_execute "echo options zfs zfs_arc_max=$((v_zfs_arc_max_mb * 1024 * 1024)) >> /etc/modprobe.d/zfs.conf"
|
||||||
|
|
||||||
echo "======= setting up grub =========="
|
echo "======= setting up grub =========="
|
||||||
chroot_execute "echo 'grub-pc grub-pc/install_devices_empty boolean true' | debconf-set-selections"
|
chroot_execute "echo 'grub-pc grub-pc/install_devices_empty boolean true' | debconf-set-selections"
|
||||||
chroot_execute "apt install --yes grub-legacy"
|
chroot_execute "DEBIAN_FRONTEND=noninteractive apt install --yes grub-legacy"
|
||||||
chroot_execute "apt install --yes grub-pc"
|
chroot_execute "DEBIAN_FRONTEND=noninteractive apt install --yes grub-pc"
|
||||||
for disk in ${v_selected_disks[@]}; do
|
chroot_execute "grub-install --recheck ${v_selected_disks[0]}"
|
||||||
chroot_execute "grub-install --recheck $disk"
|
|
||||||
done
|
|
||||||
|
|
||||||
chroot_execute "sed -i 's/#GRUB_TERMINAL=console/GRUB_TERMINAL=console/g' /etc/default/grub"
|
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_DEFAULT=.*|GRUB_CMDLINE_LINUX_DEFAULT=\"net.ifnames=0\"|' /etc/default/grub"
|
||||||
chroot_execute "sed -i 's|GRUB_CMDLINE_LINUX=\"\"|GRUB_CMDLINE_LINUX=\"root=ZFS=$v_rpool_name/ROOT/debian\"|g' /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/quiet//g' /etc/default/grub"
|
||||||
chroot_execute "sed -i 's/splash//g' /etc/default/grub"
|
chroot_execute "sed -i 's/splash//g' /etc/default/grub"
|
||||||
@@ -776,22 +785,21 @@ if [[ $v_encrypt_rpool == "1" ]]; then
|
|||||||
echo "=========set up dropbear=============="
|
echo "=========set up dropbear=============="
|
||||||
|
|
||||||
chroot_execute "apt install --yes dropbear-initramfs"
|
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"
|
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 "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"
|
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"
|
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 "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"
|
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/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
|
fi
|
||||||
|
|
||||||
echo "============setup root prompt============"
|
echo "============setup root prompt============"
|
||||||
@@ -804,37 +812,6 @@ CONF
|
|||||||
|
|
||||||
echo "========running packages upgrade==========="
|
echo "========running packages upgrade==========="
|
||||||
chroot_execute "apt upgrade --yes"
|
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 =========="
|
echo "======= update initramfs =========="
|
||||||
chroot_execute "update-initramfs -u -k all"
|
chroot_execute "update-initramfs -u -k all"
|
||||||
@@ -844,14 +821,14 @@ chroot_execute "update-grub"
|
|||||||
|
|
||||||
echo "======= setting up zed =========="
|
echo "======= setting up zed =========="
|
||||||
if [[ $v_zfs_experimental == "1" ]]; then
|
if [[ $v_zfs_experimental == "1" ]]; then
|
||||||
chroot_execute "zfs set canmount=noauto $v_rpool_name"
|
chroot_execute "zfs set canmount=noauto rpool"
|
||||||
else
|
else
|
||||||
initial_load_debian_zed_cache
|
initial_load_debian_zed_cache
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "======= setting mountpoints =========="
|
echo "======= setting mountpoints =========="
|
||||||
chroot_execute "zfs set mountpoint=legacy $v_bpool_name/BOOT/debian"
|
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 "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 "echo $v_rpool_name/var/log /var/log zfs nodev,relatime 0 0 >> /etc/fstab"
|
||||||
@@ -863,10 +840,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"
|
chroot_execute "echo $v_rpool_name/tmp /tmp zfs nodev,relatime 0 0 >> /etc/fstab"
|
||||||
|
|
||||||
echo "========= add swap, if defined"
|
echo "========= add swap, if defined"
|
||||||
if [[ $v_swap_size -gt 0 ]]; then
|
[[ $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 /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"
|
chroot_execute "echo RESUME=none > /etc/initramfs-tools/conf.d/resume"
|
||||||
|
|
||||||
echo "======= unmounting filesystems and zfs pools =========="
|
echo "======= unmounting filesystems and zfs pools =========="
|
||||||
|
|||||||
@@ -1,916 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
: <<'end_header_info'
|
|
||||||
(c) Andrey Prokopenko job@terem.fr
|
|
||||||
fully automatic script to install Debian 12 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
|
|
||||||
export DEBIAN_FRONTEND=noninteractive
|
|
||||||
|
|
||||||
# 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
|
|
||||||
c_efimode_enabled="$(if [[ -d /sys/firmware/efi/efivars ]]; then echo 1; else echo 0; fi)"
|
|
||||||
|
|
||||||
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 12 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/$v_rpool_name"
|
|
||||||
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 "$c_zfs_mount_dir/etc/zfs/zfs-list.cache/$v_rpool_name" ]] || [[ -e "$c_zfs_mount_dir/etc/zfs/zfs-list.cache/$v_rpool_name" && (( $(find "$c_zfs_mount_dir/etc/zfs/zfs-list.cache/$v_rpool_name" -type f -printf '%s' 2> /dev/null) == 0 )) ]]; then
|
|
||||||
chroot_execute "zfs set canmount=noauto $v_rpool_name"
|
|
||||||
|
|
||||||
SECONDS=0
|
|
||||||
|
|
||||||
while (( SECONDS++ <= 120 )); do
|
|
||||||
if [[ -e "$c_zfs_mount_dir/etc/zfs/zfs-list.cache/$v_rpool_name" ]] && (( $(find "$c_zfs_mount_dir/etc/zfs/zfs-list.cache/$v_rpool_name" -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|/$c_zfs_mount_dir/?|/|g" "$c_zfs_mount_dir/etc/zfs/zfs-list.cache/$v_rpool_name"
|
|
||||||
}
|
|
||||||
|
|
||||||
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 "DEBIAN_FRONTEND=noninteractive $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
|
|
||||||
if dpkg -l "linux-headers-$kver" 2>/dev/null | grep -q "^ii"; then
|
|
||||||
apt purge --yes "linux-headers-$kver"
|
|
||||||
fi
|
|
||||||
if dpkg -l "linux-image-$kver" 2>/dev/null | grep -q "^ii"; then
|
|
||||||
apt purge --yes "linux-image-$kver"
|
|
||||||
fi
|
|
||||||
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 -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" 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"
|
|
||||||
if (( c_efimode_enabled == 1 )); then
|
|
||||||
sgdisk -a1 -n1:24K:+1G -t1:EF00 "$selected_disk" # EFI partition
|
|
||||||
else
|
|
||||||
sgdisk -a1 -n1:24K:+1000K -t1:EF02 "$selected_disk"
|
|
||||||
fi
|
|
||||||
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 \
|
|
||||||
-m none \
|
|
||||||
-o cachefile=/etc/zpool.cache \
|
|
||||||
-o compatibility=grub2 \
|
|
||||||
-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 \
|
|
||||||
-m none \
|
|
||||||
$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
|
|
||||||
|
|
||||||
if (( c_efimode_enabled == 1 )); then
|
|
||||||
echo "======= create filesystem on EFI partition(s) =========="
|
|
||||||
|
|
||||||
for selected_disk in "${v_selected_disks[@]}"; do
|
|
||||||
mkfs.fat -F32 "${selected_disk}-part1"
|
|
||||||
done
|
|
||||||
mkdir -p "$c_zfs_mount_dir/boot/efi"
|
|
||||||
mount "${v_selected_disks[0]}-part1" "$c_zfs_mount_dir/boot/efi"
|
|
||||||
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 > "$c_zfs_mount_dir/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
|
|
||||||
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 "$c_zfs_mount_dir/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 =========="
|
|
||||||
if (( c_efimode_enabled == 1 )); then
|
|
||||||
chroot_execute "apt install --yes grub-efi-amd64"
|
|
||||||
else
|
|
||||||
chroot_execute "echo 'grub-pc grub-pc/install_devices_empty boolean true' | debconf-set-selections"
|
|
||||||
chroot_execute "apt install --yes grub-legacy"
|
|
||||||
chroot_execute "apt install --yes grub-pc"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if (( c_efimode_enabled == 1 )); then
|
|
||||||
#chroot_execute grub-probe /boot
|
|
||||||
chroot_execute grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=debian --recheck
|
|
||||||
else
|
|
||||||
for disk in ${v_selected_disks[@]}; do
|
|
||||||
chroot_execute "grub-install --recheck $disk"
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
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=$v_rpool_name/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 $v_rpool_name"
|
|
||||||
else
|
|
||||||
initial_load_debian_zed_cache
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "======= setting mountpoints =========="
|
|
||||||
if (( c_efimode_enabled == 1 )); then
|
|
||||||
umount "$c_zfs_mount_dir/boot/efi"
|
|
||||||
fi
|
|
||||||
|
|
||||||
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
|
|
||||||
@@ -1,951 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
: <<'end_header_info'
|
|
||||||
(c) Andrey Prokopenko job@terem.fr
|
|
||||||
fully automatic script to install Debian 13 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, 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 -euo pipefail
|
|
||||||
|
|
||||||
# ---- Configuration ----
|
|
||||||
SYSTEM_HOSTNAME=""
|
|
||||||
ROOT_PASSWORD=""
|
|
||||||
ZFS_POOL=""
|
|
||||||
DEBIAN_CODENAME="trixie" # Debian 13
|
|
||||||
TARGET="/mnt/debian"
|
|
||||||
|
|
||||||
ZBM_BIOS_URL="https://github.com/zbm-dev/zfsbootmenu/releases/download/v3.0.1/zfsbootmenu-release-x86_64-v3.0.1-linux6.1.tar.gz"
|
|
||||||
ZBM_EFI_URL="https://github.com/zbm-dev/zfsbootmenu/releases/download/v3.0.1/zfsbootmenu-release-x86_64-v3.0.1-linux6.1.EFI"
|
|
||||||
|
|
||||||
MAIN_BOOT="/main_boot"
|
|
||||||
|
|
||||||
# Hetzner mirrors for Debian
|
|
||||||
MIRROR_SITE="https://mirror.hetzner.com"
|
|
||||||
MIRROR_MAIN="deb ${MIRROR_SITE}/debian/packages ${DEBIAN_CODENAME} main contrib non-free non-free-firmware"
|
|
||||||
MIRROR_UPDATES="deb ${MIRROR_SITE}/debian/packages ${DEBIAN_CODENAME}-updates main contrib non-free non-free-firmware"
|
|
||||||
MIRROR_SECURITY="deb ${MIRROR_SITE}/debian/security ${DEBIAN_CODENAME}-security main contrib non-free non-free-firmware"
|
|
||||||
|
|
||||||
# Global variables
|
|
||||||
INSTALL_DISK=""
|
|
||||||
EFI_MODE=false
|
|
||||||
BOOT_LABEL=""
|
|
||||||
BOOT_TYPE=""
|
|
||||||
BOOT_PART=""
|
|
||||||
ZFS_PART=""
|
|
||||||
|
|
||||||
# ---- User Input Functions ----
|
|
||||||
function setup_whiptail_colors {
|
|
||||||
# Green text on black background - classic terminal theme
|
|
||||||
export NEWT_COLORS='
|
|
||||||
root=green,black
|
|
||||||
window=green,black
|
|
||||||
shadow=green,black
|
|
||||||
border=green,black
|
|
||||||
title=green,black
|
|
||||||
textbox=green,black
|
|
||||||
button=black,green
|
|
||||||
listbox=green,black
|
|
||||||
actlistbox=black,green
|
|
||||||
actsellistbox=black,green
|
|
||||||
checkbox=green,black
|
|
||||||
actcheckbox=black,green
|
|
||||||
entry=green,black
|
|
||||||
label=green,black
|
|
||||||
'
|
|
||||||
}
|
|
||||||
|
|
||||||
function check_whiptail {
|
|
||||||
if ! command -v whiptail &> /dev/null; then
|
|
||||||
echo "Installing whiptail..."
|
|
||||||
apt update
|
|
||||||
apt install -y whiptail
|
|
||||||
fi
|
|
||||||
setup_whiptail_colors
|
|
||||||
}
|
|
||||||
|
|
||||||
function get_hostname {
|
|
||||||
while true; do
|
|
||||||
SYSTEM_HOSTNAME=$(whiptail \
|
|
||||||
--title " System Hostname " \
|
|
||||||
--inputbox "\nEnter the hostname for the new system:" \
|
|
||||||
10 60 "zfs-debian" \
|
|
||||||
3>&1 1>&2 2>&3)
|
|
||||||
|
|
||||||
local exit_status=$?
|
|
||||||
if [ $exit_status -ne 0 ]; then
|
|
||||||
echo "Installation cancelled by user."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Validate hostname
|
|
||||||
if [[ "$SYSTEM_HOSTNAME" =~ ^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]$ ]] && [[ ${#SYSTEM_HOSTNAME} -le 63 ]]; then
|
|
||||||
break
|
|
||||||
else
|
|
||||||
whiptail \
|
|
||||||
--title " Invalid Hostname " \
|
|
||||||
--msgbox "Invalid hostname. Please use only letters, numbers, and hyphens. Must start and end with alphanumeric character. Maximum 63 characters." \
|
|
||||||
12 60
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
function get_zfs_pool_name {
|
|
||||||
while true; do
|
|
||||||
ZFS_POOL=$(whiptail \
|
|
||||||
--title " ZFS Pool Name " \
|
|
||||||
--inputbox "\nEnter the name for the ZFS pool:" \
|
|
||||||
10 60 "rpool" \
|
|
||||||
3>&1 1>&2 2>&3)
|
|
||||||
|
|
||||||
local exit_status=$?
|
|
||||||
if [ $exit_status -ne 0 ]; then
|
|
||||||
echo "Installation cancelled by user."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Validate ZFS pool name
|
|
||||||
if [[ "$ZFS_POOL" =~ ^[a-zA-Z][a-zA-Z0-9_-]*$ ]] && [[ ${#ZFS_POOL} -le 255 ]]; then
|
|
||||||
break
|
|
||||||
else
|
|
||||||
whiptail \
|
|
||||||
--title " Invalid Pool Name " \
|
|
||||||
--msgbox "Invalid ZFS pool name. Must start with a letter and contain only letters, numbers, hyphens, and underscores. Maximum 255 characters." \
|
|
||||||
12 60
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
function get_root_password {
|
|
||||||
while true; do
|
|
||||||
# Get first password input
|
|
||||||
local password1
|
|
||||||
local password2
|
|
||||||
|
|
||||||
password1=$(whiptail \
|
|
||||||
--title " Root Password " \
|
|
||||||
--passwordbox "\nEnter root password (input hidden):" \
|
|
||||||
10 60 \
|
|
||||||
3>&1 1>&2 2>&3)
|
|
||||||
|
|
||||||
local exit_status=$?
|
|
||||||
if [ $exit_status -ne 0 ]; then
|
|
||||||
echo "Installation cancelled by user."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Get password confirmation
|
|
||||||
password2=$(whiptail \
|
|
||||||
--title " Confirm Root Password " \
|
|
||||||
--passwordbox "\nConfirm root password (input hidden):" \
|
|
||||||
10 60 \
|
|
||||||
3>&1 1>&2 2>&3)
|
|
||||||
|
|
||||||
exit_status=$?
|
|
||||||
if [ $exit_status -ne 0 ]; then
|
|
||||||
echo "Installation cancelled by user."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check if passwords match
|
|
||||||
if [ "$password1" = "$password2" ]; then
|
|
||||||
if [ -n "$password1" ]; then
|
|
||||||
ROOT_PASSWORD="$password1"
|
|
||||||
break
|
|
||||||
else
|
|
||||||
whiptail \
|
|
||||||
--title " Empty Password " \
|
|
||||||
--msgbox "Password cannot be empty. Please enter a password." \
|
|
||||||
10 50
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
whiptail \
|
|
||||||
--title " Password Mismatch " \
|
|
||||||
--msgbox "Passwords do not match. Please try again." \
|
|
||||||
10 50
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
function show_summary_and_confirm {
|
|
||||||
local summary="Please review the installation settings:
|
|
||||||
|
|
||||||
Hostname: $SYSTEM_HOSTNAME
|
|
||||||
ZFS Pool: $ZFS_POOL
|
|
||||||
Debian Version: $DEBIAN_CODENAME (13)
|
|
||||||
Target: $TARGET
|
|
||||||
Boot Mode: $([ "$EFI_MODE" = true ] && echo "EFI" || echo "BIOS")
|
|
||||||
Install Disk: $INSTALL_DISK
|
|
||||||
|
|
||||||
*** WARNING: This will DESTROY ALL DATA on $INSTALL_DISK! ***
|
|
||||||
|
|
||||||
Do you want to continue with the installation?"
|
|
||||||
|
|
||||||
if whiptail \
|
|
||||||
--title " Installation Summary " \
|
|
||||||
--yesno "$summary" \
|
|
||||||
18 60; then
|
|
||||||
# User confirmed - just continue silently
|
|
||||||
echo "User confirmed installation. Starting now..."
|
|
||||||
else
|
|
||||||
echo "Installation cancelled by user."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function get_user_input {
|
|
||||||
echo "======= Gathering Installation Parameters =========="
|
|
||||||
check_whiptail
|
|
||||||
|
|
||||||
# Show welcome message
|
|
||||||
whiptail \
|
|
||||||
--title " ZFS Debian Installer " \
|
|
||||||
--msgbox "Welcome to the ZFS Debian Installer for Hetzner Cloud.\n\nThis script will install Debian 13 with ZFS root on your server." \
|
|
||||||
12 60
|
|
||||||
|
|
||||||
# Get user inputs
|
|
||||||
get_hostname
|
|
||||||
get_zfs_pool_name
|
|
||||||
get_root_password
|
|
||||||
}
|
|
||||||
|
|
||||||
# ---- System Detection Functions ----
|
|
||||||
function detect_efi {
|
|
||||||
echo "======= Detecting EFI support =========="
|
|
||||||
|
|
||||||
if [ -d /sys/firmware/efi ]; then
|
|
||||||
echo "✓ EFI firmware detected"
|
|
||||||
EFI_MODE=true
|
|
||||||
BOOT_LABEL="EFI"
|
|
||||||
BOOT_TYPE="ef00"
|
|
||||||
else
|
|
||||||
echo "✓ Legacy BIOS mode detected"
|
|
||||||
EFI_MODE=false
|
|
||||||
BOOT_LABEL="boot"
|
|
||||||
BOOT_TYPE="8300"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function find_install_disk {
|
|
||||||
echo "======= Finding install disk =========="
|
|
||||||
|
|
||||||
local candidate_disks=()
|
|
||||||
|
|
||||||
# Use lsblk to find all unmounted, writable disks
|
|
||||||
while IFS= read -r disk; do
|
|
||||||
[[ -n "$disk" ]] && candidate_disks+=("$disk")
|
|
||||||
done < <(lsblk -npo NAME,TYPE,RO,MOUNTPOINT | awk '
|
|
||||||
$2 == "disk" && $3 == "0" && $4 == "" {print $1}
|
|
||||||
')
|
|
||||||
|
|
||||||
if [[ ${#candidate_disks[@]} -eq 0 ]]; then
|
|
||||||
echo "No suitable installation disks found" >&2
|
|
||||||
echo "Looking for: unmounted, writable disks without partitions in use" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
INSTALL_DISK="${candidate_disks[0]}"
|
|
||||||
echo "Using installation disk: $INSTALL_DISK"
|
|
||||||
|
|
||||||
# Show all available disks for verification
|
|
||||||
echo "All available disks:"
|
|
||||||
lsblk -o NAME,SIZE,TYPE,MOUNTPOINT,RO | grep -v loop
|
|
||||||
}
|
|
||||||
|
|
||||||
# ---- Rescue System Preparation Functions ----
|
|
||||||
function remove_unused_kernels {
|
|
||||||
echo "=========== Removing 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
|
|
||||||
|
|
||||||
for pkg in "linux-headers-$kver" "linux-image-$kver"; do
|
|
||||||
if dpkg -l "$pkg" 2>/dev/null | grep -q '^ii'; then
|
|
||||||
echo "Purging $pkg ..."
|
|
||||||
apt purge --yes "$pkg"
|
|
||||||
else
|
|
||||||
echo "Package $pkg not installed, skipping."
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
function install_zfs_on_rescue_system {
|
|
||||||
echo "======= Installing ZFS on rescue system =========="
|
|
||||||
echo "zfs-dkms zfs-dkms/note-incompatible-licenses note true" | debconf-set-selections
|
|
||||||
# Enable backports for newer ZFS version
|
|
||||||
echo "deb http://mirror.hetzner.com/debian/packages bookworm-backports main contrib" > /etc/apt/sources.list.d/backports.list
|
|
||||||
apt update
|
|
||||||
apt -t bookworm-backports install -y zfsutils-linux
|
|
||||||
}
|
|
||||||
|
|
||||||
# ---- Disk Partitioning Functions ----
|
|
||||||
function partition_disk {
|
|
||||||
echo "======= Partitioning disk =========="
|
|
||||||
sgdisk -Z "$INSTALL_DISK"
|
|
||||||
|
|
||||||
if [ "$EFI_MODE" = true ]; then
|
|
||||||
echo "Creating EFI partition layout"
|
|
||||||
# EFI System Partition (ESP) - 64MB is plenty for ZFSBootMenu
|
|
||||||
sgdisk -n1:1M:+128M -t1:ef00 -c1:"EFI" "$INSTALL_DISK"
|
|
||||||
# ZFS partition
|
|
||||||
sgdisk -n2:0:0 -t2:bf00 -c2:"zfs" "$INSTALL_DISK"
|
|
||||||
else
|
|
||||||
echo "Creating BIOS partition layout"
|
|
||||||
# /boot partition - 64MB is also sufficient for BIOS ZFSBootMenu
|
|
||||||
sgdisk -n1:1M:+128M -t1:8300 -c1:"boot" "$INSTALL_DISK"
|
|
||||||
# ZFS partition
|
|
||||||
sgdisk -n2:0:0 -t2:bf00 -c2:"zfs" "$INSTALL_DISK"
|
|
||||||
# Set legacy BIOS bootable flag
|
|
||||||
sgdisk -A 1:set:2 "$INSTALL_DISK"
|
|
||||||
fi
|
|
||||||
|
|
||||||
partprobe "$INSTALL_DISK" || true
|
|
||||||
udevadm settle
|
|
||||||
|
|
||||||
# Set partition variables based on mode
|
|
||||||
if [ "$EFI_MODE" = true ]; then
|
|
||||||
BOOT_PART="$(blkid -t PARTLABEL='EFI' -o device)"
|
|
||||||
ZFS_PART="$(blkid -t PARTLABEL='zfs' -o device)"
|
|
||||||
# Format ESP as FAT32
|
|
||||||
mkfs.fat -F 32 -n EFI "$BOOT_PART"
|
|
||||||
else
|
|
||||||
BOOT_PART="$(blkid -t PARTLABEL='boot' -o device)"
|
|
||||||
ZFS_PART="$(blkid -t PARTLABEL='zfs' -o device)"
|
|
||||||
mkfs.ext4 -F -L boot "$BOOT_PART"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# ---- ZFS Pool and Dataset Functions ----
|
|
||||||
function create_zfs_pool {
|
|
||||||
echo "======= Creating ZFS pool =========="
|
|
||||||
# Clean up any existing ZFS binaries in PATH
|
|
||||||
rm -f "$(which zfs)" 2>/dev/null || true
|
|
||||||
rm -f "$(which zpool)" 2>/dev/null || true
|
|
||||||
|
|
||||||
export PATH=/usr/sbin:$PATH
|
|
||||||
modprobe zfs
|
|
||||||
|
|
||||||
zpool create -f -o ashift=12 \
|
|
||||||
-o cachefile="/etc/zfs/zpool.cache" \
|
|
||||||
-O compression=lz4 \
|
|
||||||
-O acltype=posixacl \
|
|
||||||
-O xattr=sa \
|
|
||||||
-O mountpoint=none \
|
|
||||||
"$ZFS_POOL" "$ZFS_PART"
|
|
||||||
|
|
||||||
zfs create -o mountpoint=none "$ZFS_POOL/ROOT"
|
|
||||||
zfs create -o mountpoint=legacy "$ZFS_POOL/ROOT/debian"
|
|
||||||
|
|
||||||
echo "======= Assigning $ZFS_POOL/ROOT/debian dataset as bootable =========="
|
|
||||||
zpool set bootfs="$ZFS_POOL/ROOT/debian" "$ZFS_POOL"
|
|
||||||
zpool set cachefile="/etc/zfs/zpool.cache" "$ZFS_POOL"
|
|
||||||
}
|
|
||||||
|
|
||||||
function create_additional_zfs_datasets {
|
|
||||||
echo "======= Creating additional ZFS datasets with TEMPORARY mountpoints =========="
|
|
||||||
|
|
||||||
# Ensure parent datasets are created first
|
|
||||||
zfs create -o mountpoint=none "$ZFS_POOL/ROOT/debian/var"
|
|
||||||
zfs create -o mountpoint=none "$ZFS_POOL/ROOT/debian/var/cache"
|
|
||||||
|
|
||||||
# Create leaf datasets with temporary mountpoints under $TARGET
|
|
||||||
zfs create -o com.sun:auto-snapshot=false -o mountpoint="$TARGET/tmp" "$ZFS_POOL/ROOT/debian/tmp"
|
|
||||||
zfs set devices=off "$ZFS_POOL/ROOT/debian/tmp"
|
|
||||||
|
|
||||||
zfs create -o com.sun:auto-snapshot=false -o mountpoint="$TARGET/var/tmp" "$ZFS_POOL/ROOT/debian/var/tmp"
|
|
||||||
zfs set devices=off "$ZFS_POOL/ROOT/debian/var/tmp"
|
|
||||||
|
|
||||||
zfs create -o mountpoint="$TARGET/var/log" "$ZFS_POOL/ROOT/debian/var/log"
|
|
||||||
zfs set atime=off "$ZFS_POOL/ROOT/debian/var/log"
|
|
||||||
|
|
||||||
zfs create -o com.sun:auto-snapshot=false -o mountpoint="$TARGET/var/cache/apt" "$ZFS_POOL/ROOT/debian/var/cache/apt"
|
|
||||||
zfs set atime=off "$ZFS_POOL/ROOT/debian/var/cache/apt"
|
|
||||||
|
|
||||||
# Create home dataset separately
|
|
||||||
zfs create -o mountpoint="$TARGET/home" "$ZFS_POOL/home"
|
|
||||||
|
|
||||||
# Mount all datasets
|
|
||||||
zfs mount -a
|
|
||||||
|
|
||||||
# Set permissions on the actual ZFS datasets
|
|
||||||
echo "Setting permissions on ZFS datasets..."
|
|
||||||
chmod 1777 "$TARGET/tmp"
|
|
||||||
chmod 1777 "$TARGET/var/tmp"
|
|
||||||
echo "✓ Temp directory permissions set (1777)"
|
|
||||||
}
|
|
||||||
|
|
||||||
function set_final_mountpoints {
|
|
||||||
echo "======= Setting final mountpoints =========="
|
|
||||||
|
|
||||||
# Leaf datasets - actual system mountpoints
|
|
||||||
zfs set mountpoint=/tmp "$ZFS_POOL/ROOT/debian/tmp"
|
|
||||||
zfs set mountpoint=/var/tmp "$ZFS_POOL/ROOT/debian/var/tmp"
|
|
||||||
zfs set mountpoint=/var/log "$ZFS_POOL/ROOT/debian/var/log"
|
|
||||||
zfs set mountpoint=/var/cache/apt "$ZFS_POOL/ROOT/debian/var/cache/apt"
|
|
||||||
|
|
||||||
# Home dataset - separate from OS
|
|
||||||
zfs set mountpoint=/home "$ZFS_POOL/home"
|
|
||||||
echo ""
|
|
||||||
echo "Detailed dataset listing:"
|
|
||||||
zfs list -o name,mountpoint -r "$ZFS_POOL"
|
|
||||||
}
|
|
||||||
|
|
||||||
# ---- System Bootstrap Functions ----
|
|
||||||
function bootstrap_debian_system {
|
|
||||||
echo "======= Bootstrapping Debian to temporary directory =========="
|
|
||||||
|
|
||||||
# Install debootstrap if not available
|
|
||||||
if ! command -v debootstrap &> /dev/null; then
|
|
||||||
echo "Installing debootstrap..."
|
|
||||||
apt update
|
|
||||||
apt install -y debootstrap
|
|
||||||
fi
|
|
||||||
|
|
||||||
#echo "======= Copying staged system to ZFS datasets =========="
|
|
||||||
# Mount root dataset for copying
|
|
||||||
mkdir -p "$TARGET"
|
|
||||||
mount -t zfs "$ZFS_POOL/ROOT/debian" "$TARGET"
|
|
||||||
|
|
||||||
create_additional_zfs_datasets
|
|
||||||
|
|
||||||
# Bootstrap Debian 13 (Trixie) - include dbus to satisfy systemd-resolved dependency
|
|
||||||
debootstrap \
|
|
||||||
--components=main,contrib,non-free,non-free-firmware \
|
|
||||||
--include=initramfs-tools,dbus,locales,debconf-i18n,apt-utils,keyboard-configuration,console-setup,kbd,zstd,systemd-resolved,systemd-timesyncd \
|
|
||||||
"$DEBIAN_CODENAME" \
|
|
||||||
"$TARGET" \
|
|
||||||
"$MIRROR_SITE/debian/packages"
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function setup_chroot_environment {
|
|
||||||
echo "======= Mounting virtual filesystems for chroot =========="
|
|
||||||
mount -t proc proc "$TARGET/proc"
|
|
||||||
mount -t sysfs sysfs "$TARGET/sys"
|
|
||||||
|
|
||||||
# Only mount specific tmpfs directories, not the entire /run
|
|
||||||
mkdir -p "$TARGET/run/lock" "$TARGET/run/shm"
|
|
||||||
mount -t tmpfs tmpfs "$TARGET/run/lock"
|
|
||||||
mount -t tmpfs tmpfs "$TARGET/run/shm"
|
|
||||||
mount -t tmpfs tmpfs "$TARGET/tmp"
|
|
||||||
|
|
||||||
mount --bind /dev "$TARGET/dev"
|
|
||||||
mount --bind /dev/pts "$TARGET/dev/pts"
|
|
||||||
}
|
|
||||||
|
|
||||||
# ---- System Configuration Functions ----
|
|
||||||
function configure_basic_system {
|
|
||||||
echo "======= Configuring basic system settings =========="
|
|
||||||
chroot "$TARGET" /bin/bash <<EOF
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
# Set hostname from variable
|
|
||||||
echo "$SYSTEM_HOSTNAME" > /etc/hostname
|
|
||||||
|
|
||||||
# Configure timezone (Vienna)
|
|
||||||
echo "Europe/Vienna" > /etc/timezone
|
|
||||||
ln -sf /usr/share/zoneinfo/Europe/Vienna /etc/localtime
|
|
||||||
|
|
||||||
# Generate locales
|
|
||||||
cat > /etc/locale.gen <<'LOCALES'
|
|
||||||
en_US.UTF-8 UTF-8
|
|
||||||
de_AT.UTF-8 UTF-8
|
|
||||||
fr_FR.UTF-8 UTF-8
|
|
||||||
ru_RU.UTF-8 UTF-8
|
|
||||||
LOCALES
|
|
||||||
|
|
||||||
locale-gen
|
|
||||||
|
|
||||||
# Set default locale
|
|
||||||
update-locale LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8
|
|
||||||
|
|
||||||
# Configure keyboard for German and US with Alt+Shift toggle
|
|
||||||
cat > /etc/default/keyboard <<'KEYBOARD'
|
|
||||||
# KEYBOARD CONFIGURATION FILE
|
|
||||||
|
|
||||||
# Consult the keyboard(5) manual page.
|
|
||||||
|
|
||||||
XKBMODEL="pc105"
|
|
||||||
XKBLAYOUT="de,ru"
|
|
||||||
XKBVARIANT=","
|
|
||||||
XKBOPTIONS="grp:ctrl_shift_toggle"
|
|
||||||
|
|
||||||
BACKSPACE="guess"
|
|
||||||
KEYBOARD
|
|
||||||
|
|
||||||
# Apply keyboard configuration to console
|
|
||||||
setupcon --force
|
|
||||||
|
|
||||||
# Update /etc/hosts with the hostname
|
|
||||||
echo "127.0.0.1 localhost" > /etc/hosts
|
|
||||||
echo "127.0.1.1 $SYSTEM_HOSTNAME" >> /etc/hosts
|
|
||||||
echo "::1 localhost ip6-localhost ip6-loopback" >> /etc/hosts
|
|
||||||
echo "ff02::1 ip6-allnodes" >> /etc/hosts
|
|
||||||
echo "ff02::2 ip6-allrouters" >> /etc/hosts
|
|
||||||
|
|
||||||
# Set proper permissions for ZFS datasets
|
|
||||||
chmod 1777 /tmp
|
|
||||||
chmod 1777 /var/tmp
|
|
||||||
EOF
|
|
||||||
|
|
||||||
echo "======= Configuration Summary ======="
|
|
||||||
chroot "$TARGET" /bin/bash <<'EOF'
|
|
||||||
echo "Hostname: $(cat /etc/hostname)"
|
|
||||||
echo "Timezone: $(cat /etc/timezone)"
|
|
||||||
echo "Current time: $(date)"
|
|
||||||
echo "Default locale: $(grep LANG /etc/default/locale)"
|
|
||||||
echo "Available locales:"
|
|
||||||
locale -a | grep -E "(en_US|de_AT|fr_FR|ru_RU)"
|
|
||||||
echo "Keyboard layout: $(grep XKBLAYOUT /etc/default/keyboard)"
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
function install_system_packages {
|
|
||||||
echo "======= Installing ZFS and essential packages in chroot =========="
|
|
||||||
chroot "$TARGET" /bin/bash <<'EOF'
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
# Configure sources.list for Debian 13
|
|
||||||
cat > /etc/apt/sources.list <<'SOURCES'
|
|
||||||
deb https://mirror.hetzner.com/debian/packages trixie main contrib non-free non-free-firmware
|
|
||||||
deb https://mirror.hetzner.com/debian/packages trixie-updates main contrib non-free non-free-firmware
|
|
||||||
deb https://mirror.hetzner.com/debian/security trixie-security main contrib non-free non-free-firmware
|
|
||||||
deb https://mirror.hetzner.com/debian/packages trixie-backports main contrib non-free non-free-firmware
|
|
||||||
SOURCES
|
|
||||||
|
|
||||||
# Update package lists
|
|
||||||
apt update
|
|
||||||
|
|
||||||
# Install kernel
|
|
||||||
apt install -y --no-install-recommends linux-image-cloud-amd64 linux-headers-cloud-amd64
|
|
||||||
|
|
||||||
# Install essential packages
|
|
||||||
apt install -y curl nano htop net-tools ssh \
|
|
||||||
apt-transport-https ca-certificates gnupg dirmngr \
|
|
||||||
firmware-linux-free apparmor
|
|
||||||
|
|
||||||
echo "zfs-dkms zfs-dkms/note-incompatible-licenses note true" | debconf-set-selections
|
|
||||||
|
|
||||||
apt install -y -t trixie-backports zfsutils-linux zfs-initramfs zfs-dkms
|
|
||||||
|
|
||||||
# Get the actual kernel version installed in the chroot
|
|
||||||
KERNEL_VERSION=$(ls /lib/modules/ | head -n1)
|
|
||||||
echo "Detected kernel version: $KERNEL_VERSION"
|
|
||||||
|
|
||||||
# Verify ZFS module is available in the chroot filesystem
|
|
||||||
echo "=== Verifying ZFS module in chroot ==="
|
|
||||||
if find "/lib/modules/$KERNEL_VERSION" -name "*zfs*" -type f | grep -q .; then
|
|
||||||
echo "✓ ZFS module files found in /lib/modules/$KERNEL_VERSION/"
|
|
||||||
find "/lib/modules/$KERNEL_VERSION" -name "*zfs*" -type f
|
|
||||||
else
|
|
||||||
echo "✗ ZFS module files not found - attempting DKMS rebuild"
|
|
||||||
dkms autoinstall -k "$KERNEL_VERSION" || true
|
|
||||||
depmod -a "$KERNEL_VERSION"
|
|
||||||
|
|
||||||
# Check again after DKMS rebuild
|
|
||||||
if find "/lib/modules/$KERNEL_VERSION" -name "*zfs*" -type f | grep -q .; then
|
|
||||||
echo "✓ ZFS module files found after DKMS rebuild"
|
|
||||||
else
|
|
||||||
echo "✗ ZFS module files still not found - this may cause boot issues"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Ensure ZFS module is included in initramfs
|
|
||||||
echo "zfs" >> /etc/initramfs-tools/modules
|
|
||||||
|
|
||||||
# Generate initramfs with ZFS support
|
|
||||||
update-initramfs -u -k all
|
|
||||||
|
|
||||||
# Verify kernel installation
|
|
||||||
echo "Installed kernel packages:"
|
|
||||||
dpkg -l | grep linux-image
|
|
||||||
echo "Kernel version:"
|
|
||||||
ls /lib/modules/
|
|
||||||
echo "Kernel files in ZFS dataset:"
|
|
||||||
ls -la /boot/vmlinuz* /boot/initrd.img* 2>/dev/null || echo "No kernel files found"
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
function verify_initramfs {
|
|
||||||
echo "======= Verifying initramfs contents =========="
|
|
||||||
chroot "$TARGET" /bin/bash <<'EOF'
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
echo "=== Checking initramfs for ZFS components ==="
|
|
||||||
for initrd in /boot/initrd.img-*; do
|
|
||||||
if [ -f "$initrd" ]; then
|
|
||||||
echo "Checking: $initrd"
|
|
||||||
lsinitramfs "$initrd" | grep -E "(zfs|pool|dataset|spl)" | head -10 || echo "No ZFS components found (this might be normal for first check)"
|
|
||||||
echo "---"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "=== Checking ZFS module files on disk ==="
|
|
||||||
KERNEL_VERSION=$(ls /lib/modules/ | head -n1)
|
|
||||||
find "/lib/modules/$KERNEL_VERSION" -name "*zfs*" -type f
|
|
||||||
|
|
||||||
echo "=== Testing ZFS commands ==="
|
|
||||||
which zpool && zpool --version || echo "zpool not found"
|
|
||||||
which zfs && zfs --version || echo "zfs not found"
|
|
||||||
|
|
||||||
echo "=== Checking DKMS status ==="
|
|
||||||
dkms status || echo "DKMS not available"
|
|
||||||
|
|
||||||
echo "=== Checking if ZFS tools are properly installed ==="
|
|
||||||
dpkg -l | grep -E "(zfs|spl)"
|
|
||||||
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
function configure_ssh {
|
|
||||||
echo "======= Setting up OpenSSH =========="
|
|
||||||
mkdir -p "$TARGET/root/.ssh/"
|
|
||||||
cp /root/.ssh/authorized_keys "$TARGET/root/.ssh/authorized_keys"
|
|
||||||
sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/g' "$TARGET/etc/ssh/sshd_config"
|
|
||||||
sed -i 's/#PubkeyAuthentication yes/PubkeyAuthentication yes/g' "$TARGET/etc/ssh/sshd_config"
|
|
||||||
|
|
||||||
chroot "$TARGET" /bin/bash <<'EOF'
|
|
||||||
rm /etc/ssh/ssh_host_*
|
|
||||||
dpkg-reconfigure openssh-server -f noninteractive
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
function set_root_credentials {
|
|
||||||
echo "======= Setting root password =========="
|
|
||||||
chroot "$TARGET" /bin/bash -c "echo root:$(printf "%q" "$ROOT_PASSWORD") | chpasswd"
|
|
||||||
|
|
||||||
echo "============ Setting up root prompt ============"
|
|
||||||
cat > "$TARGET/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
|
|
||||||
}
|
|
||||||
|
|
||||||
# ---- Bootloader Functions ----
|
|
||||||
function setup_efi_boot {
|
|
||||||
echo "======= Setting up EFI boot =========="
|
|
||||||
|
|
||||||
# Mount EFI System Partition
|
|
||||||
mkdir -p "$MAIN_BOOT"
|
|
||||||
mount "$BOOT_PART" "$MAIN_BOOT"
|
|
||||||
|
|
||||||
# Create EFI directory structure
|
|
||||||
mkdir -p "$MAIN_BOOT/EFI/Boot"
|
|
||||||
|
|
||||||
# Download ZFSBootMenu EFI binary
|
|
||||||
echo "Downloading ZFSBootMenu EFI binary from: $ZBM_EFI_URL"
|
|
||||||
curl -L "$ZBM_EFI_URL" -o "$MAIN_BOOT/EFI/Boot/bootx64.efi"
|
|
||||||
}
|
|
||||||
|
|
||||||
function setup_bios_boot {
|
|
||||||
echo "======= Setting up BIOS boot =========="
|
|
||||||
|
|
||||||
# Mount boot partition
|
|
||||||
mkdir -p "$MAIN_BOOT"
|
|
||||||
mount "$BOOT_PART" "$MAIN_BOOT"
|
|
||||||
|
|
||||||
# Install extlinux in rescue system if needed
|
|
||||||
if ! command -v extlinux &> /dev/null; then
|
|
||||||
echo "Installing extlinux in rescue system..."
|
|
||||||
apt update
|
|
||||||
apt install -y extlinux
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Install extlinux
|
|
||||||
extlinux --install "$MAIN_BOOT"
|
|
||||||
|
|
||||||
# Create extlinux configuration
|
|
||||||
cat > "$MAIN_BOOT/extlinux.conf" << 'EOF'
|
|
||||||
DEFAULT zfsbootmenu
|
|
||||||
PROMPT 0
|
|
||||||
TIMEOUT 0
|
|
||||||
|
|
||||||
LABEL zfsbootmenu
|
|
||||||
LINUX /zfsbootmenu/vmlinuz-bootmenu
|
|
||||||
INITRD /zfsbootmenu/initramfs-bootmenu.img
|
|
||||||
APPEND ro quiet
|
|
||||||
EOF
|
|
||||||
|
|
||||||
echo "Generated extlinux.conf:"
|
|
||||||
cat "$MAIN_BOOT/extlinux.conf"
|
|
||||||
|
|
||||||
# Download and install ZFSBootMenu for BIOS
|
|
||||||
local TEMP_ZBM=$(mktemp -d)
|
|
||||||
echo "Downloading ZFSBootMenu for BIOS from: $ZBM_BIOS_URL"
|
|
||||||
curl -L "$ZBM_BIOS_URL" -o "$TEMP_ZBM/zbm.tar.gz"
|
|
||||||
tar -xz -C "$TEMP_ZBM" -f "$TEMP_ZBM/zbm.tar.gz" --strip-components=1
|
|
||||||
|
|
||||||
# Copy ZFSBootMenu to boot partition
|
|
||||||
mkdir -p "$MAIN_BOOT/zfsbootmenu"
|
|
||||||
cp "$TEMP_ZBM"/vmlinuz* "$MAIN_BOOT/zfsbootmenu/"
|
|
||||||
cp "$TEMP_ZBM"/initramfs* "$MAIN_BOOT/zfsbootmenu/"
|
|
||||||
|
|
||||||
# Clean up
|
|
||||||
rm -rf "$TEMP_ZBM"
|
|
||||||
|
|
||||||
echo "ZFSBootMenu files copied to boot partition:"
|
|
||||||
ls -la "$MAIN_BOOT/zfsbootmenu/"
|
|
||||||
|
|
||||||
# Install MBR and set boot flag
|
|
||||||
dd bs=440 conv=notrunc count=1 if="/usr/lib/EXTLINUX/gptmbr.bin" of="$INSTALL_DISK"
|
|
||||||
parted "$INSTALL_DISK" set 1 boot on
|
|
||||||
|
|
||||||
echo "BIOS boot setup complete"
|
|
||||||
}
|
|
||||||
|
|
||||||
function configure_bootloader {
|
|
||||||
echo "======= Setting up boot based on firmware type =========="
|
|
||||||
if [ "$EFI_MODE" = true ]; then
|
|
||||||
setup_efi_boot
|
|
||||||
else
|
|
||||||
setup_bios_boot
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "======= Configuring ZFSBootMenu for auto-detection =========="
|
|
||||||
zfs set org.zfsbootmenu:commandline="ro quiet" "$ZFS_POOL/ROOT/debian"
|
|
||||||
|
|
||||||
echo "Boot configuration:"
|
|
||||||
zfs get org.zfsbootmenu:commandline "$ZFS_POOL/ROOT/debian"
|
|
||||||
}
|
|
||||||
|
|
||||||
# ---- System Services Functions ----
|
|
||||||
function configure_system_services {
|
|
||||||
echo "======= Configuring ZFS cachefile in chrooted system =========="
|
|
||||||
mkdir -p "$TARGET/etc/zfs"
|
|
||||||
cp /etc/zfs/zpool.cache "$TARGET/etc/zfs/zpool.cache"
|
|
||||||
|
|
||||||
echo "Cachefile status:"
|
|
||||||
zpool get cachefile "$ZFS_POOL"
|
|
||||||
ls -la "$TARGET/etc/zfs/zpool.cache" && echo "✓ Cachefile ready" || echo "✗ Cachefile failed"
|
|
||||||
|
|
||||||
echo "======= Enabling essential system services =========="
|
|
||||||
chroot "$TARGET" /bin/bash <<'EOF'
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
systemctl enable systemd-resolved
|
|
||||||
systemctl enable systemd-timesyncd
|
|
||||||
systemctl enable systemd-networkd
|
|
||||||
|
|
||||||
systemctl enable zfs-import-cache
|
|
||||||
systemctl enable zfs-mount
|
|
||||||
|
|
||||||
systemctl enable ssh
|
|
||||||
systemctl enable apt-daily.timer
|
|
||||||
|
|
||||||
echo "Enabled services:"
|
|
||||||
systemctl list-unit-files | grep enabled
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
function configure_networking {
|
|
||||||
echo "======= Configuring systemd-networkd for Hetzner Cloud =========="
|
|
||||||
|
|
||||||
# Create systemd-networkd configuration for all ethernet interfaces
|
|
||||||
mkdir -p "$TARGET/etc/systemd/network"
|
|
||||||
|
|
||||||
cat > "$TARGET/etc/systemd/network/10-hetzner.network" <<'EOF'
|
|
||||||
[Match]
|
|
||||||
Name=ens* enp* eth*
|
|
||||||
|
|
||||||
[Network]
|
|
||||||
DHCP=yes
|
|
||||||
IPv6PrivacyExtensions=yes
|
|
||||||
|
|
||||||
[DHCP]
|
|
||||||
RouteMetric=100
|
|
||||||
UseDNS=yes
|
|
||||||
UseDomains=yes
|
|
||||||
|
|
||||||
[DHCPv4]
|
|
||||||
RouteMetric=100
|
|
||||||
UseDNS=yes
|
|
||||||
UseDomains=yes
|
|
||||||
|
|
||||||
[IPv6AcceptRA]
|
|
||||||
RouteMetric=100
|
|
||||||
EOF
|
|
||||||
|
|
||||||
echo "systemd-networkd configuration:"
|
|
||||||
cat "$TARGET/etc/systemd/network/10-hetzner.network"
|
|
||||||
echo ""
|
|
||||||
}
|
|
||||||
|
|
||||||
# ---- Cleanup and Finalization Functions ----
|
|
||||||
function unmount_all_datasets_and_partitions {
|
|
||||||
echo "======= Unmounting all datasets =========="
|
|
||||||
|
|
||||||
# First, unmount virtual filesystems that might be using the datasets
|
|
||||||
echo "Unmounting virtual filesystems..."
|
|
||||||
for dir in dev/pts dev tmp run/lock run/shm run sys proc; do
|
|
||||||
if mountpoint -q "$TARGET/$dir"; then
|
|
||||||
echo "Unmounting $TARGET/$dir"
|
|
||||||
umount "$TARGET/$dir" 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# Give it a moment
|
|
||||||
sleep 2
|
|
||||||
|
|
||||||
# Try to unmount boot partition first
|
|
||||||
if mountpoint -q "$MAIN_BOOT"; then
|
|
||||||
echo "Unmounting boot partition from $MAIN_BOOT"
|
|
||||||
umount "$MAIN_BOOT" 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Unmount ZFS datasets
|
|
||||||
echo "Unmounting ZFS datasets..."
|
|
||||||
zfs umount -a 2>/dev/null || true
|
|
||||||
|
|
||||||
# Wait for unmounts to complete
|
|
||||||
sleep 2
|
|
||||||
|
|
||||||
# If root dataset is still mounted, try lazy unmount
|
|
||||||
if mountpoint -q "$TARGET"; then
|
|
||||||
echo "Attempting lazy unmount of $TARGET"
|
|
||||||
umount -l "$TARGET" 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Force unmount any stubborn ZFS datasets
|
|
||||||
if zfs get mounted -r "$ZFS_POOL" 2>/dev/null | grep -q "yes"; then
|
|
||||||
echo "Forcing unmount of remaining ZFS datasets..."
|
|
||||||
zfs umount -a -f 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Final verification and force unmount if still mounted
|
|
||||||
if mountpoint -q "$TARGET"; then
|
|
||||||
echo "WARNING: $TARGET is still mounted! Attempting final cleanup..."
|
|
||||||
# Use fuser to find what's using the mount
|
|
||||||
if command -v fuser &> /dev/null; then
|
|
||||||
fuser -mv "$TARGET" 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
# Force lazy unmount as last resort
|
|
||||||
umount -l "$TARGET" 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Final ZFS unmount check
|
|
||||||
local mounted_count=0
|
|
||||||
mounted_count=$(zfs get mounted -r "$ZFS_POOL" 2>/dev/null | grep -c "yes" || true)
|
|
||||||
|
|
||||||
if [ "$mounted_count" -gt 0 ]; then
|
|
||||||
echo "WARNING: $mounted_count dataset(s) still mounted:"
|
|
||||||
zfs get mounted -r "$ZFS_POOL" 2>/dev/null | grep "yes" || true
|
|
||||||
else
|
|
||||||
echo "✓ All ZFS datasets successfully unmounted"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Verify $TARGET is unmounted
|
|
||||||
if mountpoint -q "$TARGET"; then
|
|
||||||
echo "WARNING: $TARGET is still mounted but continuing..."
|
|
||||||
else
|
|
||||||
echo "✓ $TARGET successfully unmounted"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Verify $MAIN_BOOT is unmounted
|
|
||||||
if mountpoint -q "$MAIN_BOOT"; then
|
|
||||||
echo "WARNING: $MAIN_BOOT is still mounted!"
|
|
||||||
else
|
|
||||||
echo "✓ $MAIN_BOOT successfully unmounted"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function unmount_chroot_environment {
|
|
||||||
echo "======= Unmounting virtual filesystems =========="
|
|
||||||
# Unmount virtual filesystems first
|
|
||||||
for dir in dev/pts dev tmp run sys proc; do
|
|
||||||
if mountpoint -q "$TARGET/$dir"; then
|
|
||||||
echo "Unmounting $TARGET/$dir"
|
|
||||||
umount "$TARGET/$dir" 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
function export_zfs_pool {
|
|
||||||
echo "======= Exporting ZFS pool =========="
|
|
||||||
# Export the pool to ensure clean state
|
|
||||||
zpool export "$ZFS_POOL"
|
|
||||||
echo "✓ ZFS pool '$ZFS_POOL' exported successfully"
|
|
||||||
}
|
|
||||||
|
|
||||||
function show_final_instructions {
|
|
||||||
echo ""
|
|
||||||
echo "=========================================="
|
|
||||||
echo " INSTALLATION COMPLETE! "
|
|
||||||
echo "=========================================="
|
|
||||||
echo ""
|
|
||||||
echo "System Information:"
|
|
||||||
echo " Hostname: $SYSTEM_HOSTNAME"
|
|
||||||
echo " ZFS Pool: $ZFS_POOL"
|
|
||||||
echo " Boot Mode: $([ "$EFI_MODE" = true ] && echo "EFI" || echo "BIOS")"
|
|
||||||
echo " Debian Version: $DEBIAN_CODENAME"
|
|
||||||
echo " Networking: systemd-networkd + systemd-resolved"
|
|
||||||
echo ""
|
|
||||||
echo "=========================================="
|
|
||||||
echo "Rebooting..."
|
|
||||||
}
|
|
||||||
|
|
||||||
# ---- Main Installation Flow ----
|
|
||||||
function main {
|
|
||||||
echo "Starting ZFS Debian 13 installation on Hetzner..."
|
|
||||||
|
|
||||||
# Get user input first
|
|
||||||
get_user_input
|
|
||||||
|
|
||||||
# System detection
|
|
||||||
detect_efi
|
|
||||||
find_install_disk
|
|
||||||
|
|
||||||
# Show summary and get confirmation
|
|
||||||
show_summary_and_confirm
|
|
||||||
|
|
||||||
# Rescue system preparation
|
|
||||||
remove_unused_kernels
|
|
||||||
install_zfs_on_rescue_system
|
|
||||||
|
|
||||||
# Disk partitioning
|
|
||||||
partition_disk
|
|
||||||
|
|
||||||
# ZFS setup
|
|
||||||
create_zfs_pool
|
|
||||||
|
|
||||||
# System installation
|
|
||||||
bootstrap_debian_system
|
|
||||||
setup_chroot_environment
|
|
||||||
configure_basic_system
|
|
||||||
install_system_packages
|
|
||||||
|
|
||||||
verify_initramfs
|
|
||||||
configure_ssh
|
|
||||||
set_root_credentials
|
|
||||||
|
|
||||||
# System configuration
|
|
||||||
configure_system_services
|
|
||||||
configure_networking
|
|
||||||
|
|
||||||
# Bootloader configuration
|
|
||||||
configure_bootloader
|
|
||||||
|
|
||||||
# Finalization
|
|
||||||
unmount_chroot_environment
|
|
||||||
|
|
||||||
unmount_all_datasets_and_partitions
|
|
||||||
set_final_mountpoints
|
|
||||||
export_zfs_pool
|
|
||||||
|
|
||||||
# Completion
|
|
||||||
show_final_instructions
|
|
||||||
|
|
||||||
reboot
|
|
||||||
}
|
|
||||||
|
|
||||||
# Run main function
|
|
||||||
main
|
|
||||||
@@ -17,8 +17,6 @@ set -o errexit
|
|||||||
set -o pipefail
|
set -o pipefail
|
||||||
set -o nounset
|
set -o nounset
|
||||||
|
|
||||||
export TMPDIR=/tmp
|
|
||||||
|
|
||||||
# Variables
|
# Variables
|
||||||
v_bpool_name=
|
v_bpool_name=
|
||||||
v_bpool_tweaks=
|
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_zfs_arc_max_mb=250
|
||||||
c_default_bpool_tweaks="-o ashift=12 -O compression=lz4"
|
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_default_hostname=terem
|
||||||
c_zfs_mount_dir=/mnt
|
c_zfs_mount_dir=/mnt
|
||||||
c_log_dir=$(dirname "$(mktemp)")/zfs-hetzner-vm
|
c_log_dir=$(dirname "$(mktemp)")/zfs-hetzner-vm
|
||||||
@@ -64,9 +62,7 @@ function print_step_info_header {
|
|||||||
###############################################################################
|
###############################################################################
|
||||||
# ${FUNCNAME[1]}"
|
# ${FUNCNAME[1]}"
|
||||||
|
|
||||||
if [[ "${1:-}" != "" ]]; then
|
[[ "${1:-}" != "" ]] && echo -n " $1" || true
|
||||||
echo -n " $1"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "
|
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.
|
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.
|
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 {
|
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"'`.
|
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
|
exit 1
|
||||||
fi
|
fi
|
||||||
@@ -208,7 +204,7 @@ function select_disks {
|
|||||||
|
|
||||||
Devices with mounted partitions, cdroms, and removable devices are not displayed!
|
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
|
if [[ ${#v_selected_disks[@]} -gt 0 ]]; then
|
||||||
break
|
break
|
||||||
@@ -225,7 +221,7 @@ function ask_swap_size {
|
|||||||
local swap_size_invalid_message=
|
local swap_size_invalid_message=
|
||||||
|
|
||||||
while [[ ! $v_swap_size =~ ^[0-9]+$ ]]; do
|
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! "
|
swap_size_invalid_message="Invalid swap size! "
|
||||||
done
|
done
|
||||||
@@ -240,7 +236,7 @@ function ask_free_tail_space {
|
|||||||
local tail_space_invalid_message=
|
local tail_space_invalid_message=
|
||||||
|
|
||||||
while [[ ! $v_free_tail_space =~ ^[0-9]+$ ]]; do
|
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! "
|
tail_space_invalid_message="Invalid size! "
|
||||||
done
|
done
|
||||||
@@ -255,7 +251,7 @@ function ask_zfs_arc_max_size {
|
|||||||
local zfs_arc_max_invalid_message=
|
local zfs_arc_max_invalid_message=
|
||||||
|
|
||||||
while [[ ! $v_zfs_arc_max_mb =~ ^[0-9]+$ ]]; do
|
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! "
|
zfs_arc_max_invalid_message="Invalid size! "
|
||||||
done
|
done
|
||||||
@@ -271,14 +267,14 @@ function ask_pool_names {
|
|||||||
local bpool_name_invalid_message=
|
local bpool_name_invalid_message=
|
||||||
|
|
||||||
while [[ ! $v_bpool_name =~ ^[a-z][a-zA-Z_:.-]+$ ]]; do
|
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! "
|
bpool_name_invalid_message="Invalid pool name! "
|
||||||
done
|
done
|
||||||
local rpool_name_invalid_message=
|
local rpool_name_invalid_message=
|
||||||
|
|
||||||
while [[ ! $v_rpool_name =~ ^[a-z][a-zA-Z_:.-]+$ ]]; do
|
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! "
|
rpool_name_invalid_message="Invalid pool name! "
|
||||||
done
|
done
|
||||||
@@ -290,8 +286,8 @@ function ask_pool_tweaks {
|
|||||||
# shellcheck disable=SC2119
|
# shellcheck disable=SC2119
|
||||||
print_step_info_header
|
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_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 --inputbox "Insert the tweaks for the root pool" 30 100 -- "$c_default_rpool_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
|
print_variables v_bpool_tweaks v_rpool_tweaks
|
||||||
}
|
}
|
||||||
@@ -306,8 +302,8 @@ function ask_root_password {
|
|||||||
local password_repeat=-
|
local password_repeat=-
|
||||||
|
|
||||||
while [[ "$v_root_password" != "$password_repeat" || "$v_root_password" == "" ]]; do
|
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)
|
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 --passwordbox "Please repeat the password:" 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! "
|
password_invalid_message="Passphrase empty, or not matching! "
|
||||||
done
|
done
|
||||||
@@ -317,7 +313,7 @@ function ask_root_password {
|
|||||||
function ask_encryption {
|
function ask_encryption {
|
||||||
print_step_info_header
|
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
|
v_encrypt_rpool=1
|
||||||
fi
|
fi
|
||||||
set +x
|
set +x
|
||||||
@@ -325,8 +321,8 @@ function ask_encryption {
|
|||||||
local passphrase_invalid_message=
|
local passphrase_invalid_message=
|
||||||
local passphrase_repeat=-
|
local passphrase_repeat=-
|
||||||
while [[ "$v_passphrase" != "$passphrase_repeat" || ${#v_passphrase} -lt 8 ]]; do
|
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)
|
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 --passwordbox "Please repeat the passphrase:" 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! "
|
passphrase_invalid_message="Passphrase too short, or not matching! "
|
||||||
done
|
done
|
||||||
@@ -337,7 +333,7 @@ function ask_encryption {
|
|||||||
function ask_zfs_experimental {
|
function ask_zfs_experimental {
|
||||||
print_step_info_header
|
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
|
v_zfs_experimental=1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
@@ -348,8 +344,8 @@ function ask_hostname {
|
|||||||
|
|
||||||
local hostname_invalid_message=
|
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)
|
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! "
|
hostname_invalid_message="Invalid host name! "
|
||||||
done
|
done
|
||||||
@@ -366,7 +362,7 @@ function determine_kernel_variant {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function chroot_execute {
|
function chroot_execute {
|
||||||
chroot $c_zfs_mount_dir bash -c "DEBIAN_FRONTEND=noninteractive $1"
|
chroot $c_zfs_mount_dir bash -c "$1"
|
||||||
}
|
}
|
||||||
|
|
||||||
function unmount_and_export_fs {
|
function unmount_and_export_fs {
|
||||||
@@ -403,8 +399,8 @@ function unmount_and_export_fs {
|
|||||||
echo "===========exporting zfs pools============="
|
echo "===========exporting zfs pools============="
|
||||||
set +e
|
set +e
|
||||||
while (( zpools_exported == 99 )) && (( SECONDS++ <= 60 )); do
|
while (( zpools_exported == 99 )) && (( SECONDS++ <= 60 )); do
|
||||||
|
zpool export -a 2> /dev/null
|
||||||
if zpool export -a 2> /dev/null; then
|
if [[ $? == 0 ]]; then
|
||||||
zpools_exported=1
|
zpools_exported=1
|
||||||
echo "all zfs pools were succesfully exported"
|
echo "all zfs pools were succesfully exported"
|
||||||
break;
|
break;
|
||||||
@@ -421,7 +417,6 @@ function unmount_and_export_fs {
|
|||||||
|
|
||||||
#################### MAIN ################################
|
#################### MAIN ################################
|
||||||
export LC_ALL=en_US.UTF-8
|
export LC_ALL=en_US.UTF-8
|
||||||
export NCURSES_NO_UTF8_ACS=1
|
|
||||||
|
|
||||||
check_prerequisites
|
check_prerequisites
|
||||||
|
|
||||||
@@ -462,20 +457,11 @@ for kver in $(find /lib/modules/* -maxdepth 0 -type d | grep -v "$(uname -r)" |
|
|||||||
done
|
done
|
||||||
|
|
||||||
echo "======= installing zfs on rescue system =========="
|
echo "======= installing zfs on rescue system =========="
|
||||||
echo "zfs-dkms zfs-dkms/note-incompatible-licenses note true" | debconf-set-selections
|
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
|
apt update
|
||||||
export PATH=$PATH:/usr/sbin
|
apt install --yes -t buster-backports libelf-dev zfs-dkms
|
||||||
|
modprobe zfs
|
||||||
zfs --version
|
zfs --version
|
||||||
|
|
||||||
echo "======= partitioning the disk =========="
|
echo "======= partitioning the disk =========="
|
||||||
@@ -487,9 +473,9 @@ echo "======= partitioning the disk =========="
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
for selected_disk in "${v_selected_disks[@]}"; do
|
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 -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
|
sgdisk -n3:0:"$tail_space_parameter" -t3:BF01 "$selected_disk" # Root pool
|
||||||
done
|
done
|
||||||
|
|
||||||
@@ -516,17 +502,13 @@ echo "======= create zfs pools and datasets =========="
|
|||||||
pools_mirror_option=
|
pools_mirror_option=
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# shellcheck disable=SC2086
|
|
||||||
zpool create \
|
zpool create \
|
||||||
$v_bpool_tweaks -O canmount=off -O devices=off \
|
$v_bpool_tweaks -O canmount=off -O devices=off \
|
||||||
-o cachefile=/etc/zpool.cache \
|
|
||||||
-O mountpoint=/boot -R $c_zfs_mount_dir -f \
|
-O mountpoint=/boot -R $c_zfs_mount_dir -f \
|
||||||
$v_bpool_name $pools_mirror_option "${bpool_disks_partitions[@]}"
|
$v_bpool_name $pools_mirror_option "${bpool_disks_partitions[@]}"
|
||||||
|
|
||||||
# shellcheck disable=SC2086
|
|
||||||
echo -n "$v_passphrase" | zpool create \
|
echo -n "$v_passphrase" | zpool create \
|
||||||
$v_rpool_tweaks \
|
$v_rpool_tweaks \
|
||||||
-o cachefile=/etc/zpool.cache \
|
|
||||||
"${encryption_options[@]}" \
|
"${encryption_options[@]}" \
|
||||||
-O mountpoint=/ -R $c_zfs_mount_dir -f \
|
-O mountpoint=/ -R $c_zfs_mount_dir -f \
|
||||||
$v_rpool_name $pools_mirror_option "${rpool_disks_partitions[@]}"
|
$v_rpool_name $pools_mirror_option "${rpool_disks_partitions[@]}"
|
||||||
@@ -541,7 +523,9 @@ zfs create -o canmount=noauto -o mountpoint=/boot "$v_bpool_name/BOOT/ubuntu"
|
|||||||
zfs mount "$v_bpool_name/BOOT/ubuntu"
|
zfs mount "$v_bpool_name/BOOT/ubuntu"
|
||||||
|
|
||||||
zfs create "$v_rpool_name/home"
|
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"
|
||||||
|
zfs create -o canmount=off "$v_rpool_name/var/lib"
|
||||||
zfs create "$v_rpool_name/var/log"
|
zfs create "$v_rpool_name/var/log"
|
||||||
zfs create "$v_rpool_name/var/spool"
|
zfs create "$v_rpool_name/var/spool"
|
||||||
|
|
||||||
@@ -592,7 +576,7 @@ ff02::2 ip6-allrouters
|
|||||||
ff02::3 ip6-allhosts
|
ff02::3 ip6-allhosts
|
||||||
CONF
|
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
|
cat <<CONF > /mnt/etc/systemd/network/10-eth0.network
|
||||||
[Match]
|
[Match]
|
||||||
@@ -605,9 +589,7 @@ Gateway=fe80::1
|
|||||||
CONF
|
CONF
|
||||||
|
|
||||||
chroot_execute "systemctl enable systemd-networkd.service"
|
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/"
|
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
|
cat > "$c_zfs_mount_dir/etc/cloud/cloud.cfg.d/99-disable-network-config.cfg" <<CONF
|
||||||
@@ -617,6 +599,7 @@ CONF
|
|||||||
|
|
||||||
rm -rf $c_zfs_mount_dir/etc/network/interfaces.d/50-cloud-init.cfg
|
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 =========="
|
echo "======= preparing the jail for chroot =========="
|
||||||
for virtual_fs_dir in proc sys dev; do
|
for virtual_fs_dir in proc sys dev; do
|
||||||
@@ -685,35 +668,32 @@ chroot_execute "rm -f /etc/localtime /etc/timezone"
|
|||||||
chroot_execute "dpkg-reconfigure tzdata -f noninteractive "
|
chroot_execute "dpkg-reconfigure tzdata -f noninteractive "
|
||||||
|
|
||||||
echo "======= installing latest kernel============="
|
echo "======= installing latest kernel============="
|
||||||
chroot_execute "apt install --yes linux-headers${v_kernel_variant}-hwe-18.04 linux-image${v_kernel_variant}-hwe-18.04"
|
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
|
if [[ $v_kernel_variant == "-virtual" ]]; then
|
||||||
# linux-image-extra is only available for virtual hosts
|
# linux-image-extra is only available for virtual hosts
|
||||||
chroot_execute "apt install --yes linux-image-extra-virtual-hwe-18.04"
|
chroot_execute "DEBIAN_FRONTEND=noninteractive apt install --yes linux-image-extra-virtual-hwe-18.04"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
echo "======= installing aux packages =========="
|
echo "======= installing aux packages =========="
|
||||||
chroot_execute "apt install --yes man wget curl software-properties-common nano htop gnupg"
|
chroot_execute "apt install --yes man wget curl software-properties-common nano htop gnupg"
|
||||||
chroot_execute "systemctl disable thermald"
|
chroot_execute "systemctl disable thermald"
|
||||||
|
|
||||||
echo "======= installing zfs packages =========="
|
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
|
if [[ $v_zfs_experimental == "1" ]]; then
|
||||||
chroot_execute "wget -O - https://terem42.github.io/zfs-debian/apt_pub.gpg | apt-key add -"
|
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 "add-apt-repository 'deb https://terem42.github.io/zfs-debian/public zfs-debian-experimental main'"
|
||||||
chroot_execute "apt update"
|
chroot_execute "apt update"
|
||||||
chroot_execute "apt install -t zfs-debian-experimental --yes zfs-initramfs zfs-dkms zfsutils-linux"
|
|
||||||
else
|
else
|
||||||
chroot_execute "add-apt-repository --yes ppa:jonathonf/zfs"
|
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"
|
chroot_execute "apt install --yes zfs-initramfs zfs-dkms zfsutils-linux"
|
||||||
fi
|
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 =========="
|
echo "======= installing OpenSSH and network tooling =========="
|
||||||
chroot_execute "apt install --yes openssh-server net-tools"
|
chroot_execute "apt install --yes openssh-server net-tools"
|
||||||
|
|
||||||
@@ -728,22 +708,37 @@ chroot_execute "dpkg-reconfigure openssh-server -f noninteractive"
|
|||||||
echo "======= set root password =========="
|
echo "======= set root password =========="
|
||||||
chroot_execute "echo root:$(printf "%q" "$v_root_password") | chpasswd"
|
chroot_execute "echo root:$(printf "%q" "$v_root_password") | chpasswd"
|
||||||
|
|
||||||
echo "======= setting up zfs cache =========="
|
echo "======= setting up zfs services =========="
|
||||||
cp /etc/zpool.cache /mnt/etc/zfs/zpool.cache
|
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========"
|
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"
|
chroot_execute "echo options zfs zfs_arc_max=$((v_zfs_arc_max_mb * 1024 * 1024)) >> /etc/modprobe.d/zfs.conf"
|
||||||
|
|
||||||
echo "======= setting up grub =========="
|
echo "======= setting up grub =========="
|
||||||
chroot_execute "echo 'grub-pc grub-pc/install_devices_empty boolean true' | debconf-set-selections"
|
chroot_execute "echo 'grub-pc grub-pc/install_devices_empty boolean true' | debconf-set-selections"
|
||||||
chroot_execute "apt install --yes grub-pc"
|
chroot_execute "DEBIAN_FRONTEND=noninteractive apt install --yes grub-pc"
|
||||||
for disk in ${v_selected_disks[@]}; do
|
chroot_execute "grub-install ${v_selected_disks[0]}"
|
||||||
chroot_execute "grub-install $disk"
|
|
||||||
done
|
|
||||||
|
|
||||||
chroot_execute "sed -i 's/#GRUB_TERMINAL=console/GRUB_TERMINAL=console/g' /etc/default/grub"
|
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_DEFAULT=.*|GRUB_CMDLINE_LINUX_DEFAULT=\"net.ifnames=0\"|' /etc/default/grub"
|
||||||
chroot_execute "sed -i 's|GRUB_CMDLINE_LINUX=\"\"|GRUB_CMDLINE_LINUX=\"root=ZFS=$v_rpool_name/ROOT/ubuntu\"|g' /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/quiet//g' /etc/default/grub"
|
||||||
chroot_execute "sed -i 's/splash//g' /etc/default/grub"
|
chroot_execute "sed -i 's/splash//g' /etc/default/grub"
|
||||||
@@ -771,6 +766,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/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"
|
||||||
|
|
||||||
|
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
|
fi
|
||||||
|
|
||||||
echo "============setup root prompt============"
|
echo "============setup root prompt============"
|
||||||
@@ -783,7 +790,6 @@ CONF
|
|||||||
|
|
||||||
echo "========running packages upgrade==========="
|
echo "========running packages upgrade==========="
|
||||||
chroot_execute "apt upgrade --yes"
|
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 ========="
|
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"
|
mkdir -p "$c_zfs_mount_dir/usr/share/initramfs-tools/scripts/init-premount"
|
||||||
@@ -807,8 +813,8 @@ esac
|
|||||||
|
|
||||||
configure_networking
|
configure_networking
|
||||||
|
|
||||||
ip route add 172.31.1.1/255.255.255.255 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 eth0
|
ip route add default via 172.31.1.1 dev ens3
|
||||||
CONF
|
CONF
|
||||||
|
|
||||||
chmod 755 "$c_zfs_mount_dir/usr/share/initramfs-tools/scripts/init-premount/static-route"
|
chmod 755 "$c_zfs_mount_dir/usr/share/initramfs-tools/scripts/init-premount/static-route"
|
||||||
@@ -821,11 +827,11 @@ chroot_execute "update-grub"
|
|||||||
|
|
||||||
echo "======= setting up zed =========="
|
echo "======= setting up zed =========="
|
||||||
|
|
||||||
chroot_execute "zfs set canmount=noauto $v_rpool_name"
|
chroot_execute "zfs set canmount=noauto rpool"
|
||||||
|
|
||||||
echo "======= setting mountpoints =========="
|
echo "======= setting mountpoints =========="
|
||||||
chroot_execute "zfs set mountpoint=legacy $v_bpool_name/BOOT/ubuntu"
|
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 "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 "echo $v_rpool_name/var/log /var/log zfs nodev,relatime 0 0 >> /etc/fstab"
|
||||||
@@ -837,10 +843,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"
|
chroot_execute "echo $v_rpool_name/tmp /tmp zfs nodev,relatime 0 0 >> /etc/fstab"
|
||||||
|
|
||||||
echo "========= add swap, if defined"
|
echo "========= add swap, if defined"
|
||||||
if [[ $v_swap_size -gt 0 ]]; then
|
[[ $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 /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"
|
chroot_execute "echo RESUME=none > /etc/initramfs-tools/conf.d/resume"
|
||||||
|
|
||||||
echo "======= unmounting filesystems and zfs pools =========="
|
echo "======= unmounting filesystems and zfs pools =========="
|
||||||
|
|||||||
@@ -1,863 +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 "DEBIAN_FRONTEND=noninteractive $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 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 focal "$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
|
|
||||||
|
|
||||||
#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
|
|
||||||
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 focal main restricted
|
|
||||||
deb [arch=i386,amd64] $c_deb_packages_repo focal-updates main restricted
|
|
||||||
deb [arch=i386,amd64] $c_deb_packages_repo focal-backports main restricted
|
|
||||||
deb [arch=i386,amd64] $c_deb_packages_repo focal universe
|
|
||||||
deb [arch=i386,amd64] $c_deb_security_repo focal-security main restricted
|
|
||||||
CONF
|
|
||||||
|
|
||||||
chroot_execute "apt update"
|
|
||||||
|
|
||||||
echo "======= setting locale, console and language =========="
|
|
||||||
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 "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 "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 "apt install --yes grub-pc"
|
|
||||||
for disk in ${v_selected_disks[@]}; do
|
|
||||||
chroot_execute "grub-install $disk"
|
|
||||||
done
|
|
||||||
|
|
||||||
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=$v_rpool_name/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"
|
|
||||||
|
|
||||||
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"
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
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"
|
|
||||||
|
|
||||||
rm $c_zfs_mount_dir/root/*.deb
|
|
||||||
cd /root
|
|
||||||
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 $v_rpool_name"
|
|
||||||
|
|
||||||
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
|
|
||||||
@@ -1,852 +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 "DEBIAN_FRONTEND=noninteractive $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 "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 "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 "apt install --yes grub-pc"
|
|
||||||
for disk in ${v_selected_disks[@]}; do
|
|
||||||
chroot_execute "grub-install $disk"
|
|
||||||
done
|
|
||||||
|
|
||||||
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=$v_rpool_name/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 $v_rpool_name"
|
|
||||||
|
|
||||||
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
|
|
||||||
@@ -1,973 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
: <<'end_header_info'
|
|
||||||
(c) Andrey Prokopenko job@terem.fr
|
|
||||||
fully automatic script to install Ubuntu 24 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, 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 -euo pipefail
|
|
||||||
|
|
||||||
# ---- Configuration ----
|
|
||||||
# These will be set by user input
|
|
||||||
SYSTEM_HOSTNAME=""
|
|
||||||
ROOT_PASSWORD=""
|
|
||||||
ZFS_POOL=""
|
|
||||||
UBUNTU_CODENAME="noble" # Ubuntu 24.04
|
|
||||||
TARGET="/mnt/ubuntu"
|
|
||||||
|
|
||||||
ZBM_BIOS_URL="https://github.com/zbm-dev/zfsbootmenu/releases/download/v3.0.1/zfsbootmenu-release-x86_64-v3.0.1-linux6.1.tar.gz"
|
|
||||||
ZBM_EFI_URL="https://github.com/zbm-dev/zfsbootmenu/releases/download/v3.0.1/zfsbootmenu-release-x86_64-v3.0.1-linux6.1.EFI"
|
|
||||||
|
|
||||||
MAIN_BOOT="/main_boot"
|
|
||||||
|
|
||||||
# Hetzner mirrors
|
|
||||||
MIRROR_SITE="https://mirror.hetzner.com"
|
|
||||||
MIRROR_MAIN="deb ${MIRROR_SITE}/ubuntu/packages ${UBUNTU_CODENAME} main restricted universe multiverse"
|
|
||||||
MIRROR_UPDATES="deb ${MIRROR_SITE}/ubuntu/packages ${UBUNTU_CODENAME}-updates main restricted universe multiverse"
|
|
||||||
MIRROR_BACKPORTS="deb ${MIRROR_SITE}/ubuntu/packages ${UBUNTU_CODENAME}-backports main restricted universe multiverse"
|
|
||||||
MIRROR_SECURITY="deb ${MIRROR_SITE}/ubuntu/security ${UBUNTU_CODENAME}-security main restricted universe multiverse"
|
|
||||||
|
|
||||||
# Global variables
|
|
||||||
INSTALL_DISK=""
|
|
||||||
EFI_MODE=false
|
|
||||||
BOOT_LABEL=""
|
|
||||||
BOOT_TYPE=""
|
|
||||||
BOOT_PART=""
|
|
||||||
ZFS_PART=""
|
|
||||||
|
|
||||||
# ---- User Input Functions ----
|
|
||||||
function setup_whiptail_colors {
|
|
||||||
# Green text on black background - classic terminal theme
|
|
||||||
export NEWT_COLORS='
|
|
||||||
root=green,black
|
|
||||||
window=green,black
|
|
||||||
shadow=green,black
|
|
||||||
border=green,black
|
|
||||||
title=green,black
|
|
||||||
textbox=green,black
|
|
||||||
button=black,green
|
|
||||||
listbox=green,black
|
|
||||||
actlistbox=black,green
|
|
||||||
actsellistbox=black,green
|
|
||||||
checkbox=green,black
|
|
||||||
actcheckbox=black,green
|
|
||||||
entry=green,black
|
|
||||||
label=green,black
|
|
||||||
'
|
|
||||||
}
|
|
||||||
|
|
||||||
function check_whiptail {
|
|
||||||
if ! command -v whiptail &> /dev/null; then
|
|
||||||
echo "Installing whiptail..."
|
|
||||||
apt update
|
|
||||||
apt install -y whiptail
|
|
||||||
fi
|
|
||||||
setup_whiptail_colors
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function get_hostname {
|
|
||||||
while true; do
|
|
||||||
SYSTEM_HOSTNAME=$(whiptail \
|
|
||||||
--title "System Hostname" \
|
|
||||||
--inputbox "Enter the hostname for the new system:" \
|
|
||||||
10 60 "zfs-ubuntu" \
|
|
||||||
3>&1 1>&2 2>&3)
|
|
||||||
|
|
||||||
local exit_status=$?
|
|
||||||
if [ $exit_status -ne 0 ]; then
|
|
||||||
whiptail --title "Cancelled" --msgbox "Installation cancelled by user." 10 50
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Validate hostname
|
|
||||||
if [[ "$SYSTEM_HOSTNAME" =~ ^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]$ ]] && [[ ${#SYSTEM_HOSTNAME} -le 63 ]]; then
|
|
||||||
break
|
|
||||||
else
|
|
||||||
whiptail \
|
|
||||||
--title "Invalid Hostname" \
|
|
||||||
--msgbox "Invalid hostname. Please use only letters, numbers, and hyphens. Must start and end with alphanumeric character. Maximum 63 characters." \
|
|
||||||
12 60
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
function get_zfs_pool_name {
|
|
||||||
while true; do
|
|
||||||
ZFS_POOL=$(whiptail \
|
|
||||||
--title "ZFS Pool Name" \
|
|
||||||
--inputbox "Enter the name for the ZFS pool:" \
|
|
||||||
10 60 "rpool" \
|
|
||||||
3>&1 1>&2 2>&3)
|
|
||||||
|
|
||||||
local exit_status=$?
|
|
||||||
if [ $exit_status -ne 0 ]; then
|
|
||||||
whiptail --title "Cancelled" --msgbox "Installation cancelled by user." 10 50
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Validate ZFS pool name
|
|
||||||
if [[ "$ZFS_POOL" =~ ^[a-zA-Z][a-zA-Z0-9_-]*$ ]] && [[ ${#ZFS_POOL} -le 255 ]]; then
|
|
||||||
break
|
|
||||||
else
|
|
||||||
whiptail \
|
|
||||||
--title "Invalid Pool Name" \
|
|
||||||
--msgbox "Invalid ZFS pool name. Must start with a letter and contain only letters, numbers, hyphens, and underscores. Maximum 255 characters." \
|
|
||||||
12 60
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
function get_root_password {
|
|
||||||
while true; do
|
|
||||||
# Get first password input
|
|
||||||
local password1
|
|
||||||
local password2
|
|
||||||
|
|
||||||
password1=$(whiptail \
|
|
||||||
--title "Root Password" \
|
|
||||||
--passwordbox "Enter root password (input hidden):" \
|
|
||||||
10 60 \
|
|
||||||
3>&1 1>&2 2>&3)
|
|
||||||
|
|
||||||
local exit_status=$?
|
|
||||||
if [ $exit_status -ne 0 ]; then
|
|
||||||
whiptail --title "Cancelled" --msgbox "Installation cancelled by user." 10 50
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Get password confirmation
|
|
||||||
password2=$(whiptail \
|
|
||||||
--title "Confirm Root Password" \
|
|
||||||
--passwordbox "Confirm root password (input hidden):" \
|
|
||||||
10 60 \
|
|
||||||
3>&1 1>&2 2>&3)
|
|
||||||
|
|
||||||
exit_status=$?
|
|
||||||
if [ $exit_status -ne 0 ]; then
|
|
||||||
whiptail --title "Cancelled" --msgbox "Installation cancelled by user." 10 50
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check if passwords match
|
|
||||||
if [ "$password1" = "$password2" ]; then
|
|
||||||
if [ -n "$password1" ]; then
|
|
||||||
ROOT_PASSWORD="$password1"
|
|
||||||
break
|
|
||||||
else
|
|
||||||
whiptail \
|
|
||||||
--title "Empty Password" \
|
|
||||||
--msgbox "Password cannot be empty. Please enter a password." \
|
|
||||||
10 50
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
whiptail \
|
|
||||||
--title "Password Mismatch" \
|
|
||||||
--msgbox "Passwords do not match. Please try again." \
|
|
||||||
10 50
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
function show_summary_and_confirm {
|
|
||||||
local summary="Please review the installation settings:
|
|
||||||
|
|
||||||
Hostname: $SYSTEM_HOSTNAME
|
|
||||||
ZFS Pool: $ZFS_POOL
|
|
||||||
Ubuntu Version: $UBUNTU_CODENAME (24.04)
|
|
||||||
Target: $TARGET
|
|
||||||
Boot Mode: $([ "$EFI_MODE" = true ] && echo "EFI" || echo "BIOS")
|
|
||||||
Install Disk: $INSTALL_DISK
|
|
||||||
|
|
||||||
*** WARNING: This will DESTROY ALL DATA on $INSTALL_DISK! ***
|
|
||||||
|
|
||||||
Do you want to continue with the installation?"
|
|
||||||
|
|
||||||
if whiptail \
|
|
||||||
--title " Installation Summary " \
|
|
||||||
--yesno "$summary" \
|
|
||||||
18 60; then
|
|
||||||
# User confirmed - just continue silently
|
|
||||||
echo "User confirmed installation. Starting now..."
|
|
||||||
else
|
|
||||||
echo "Installation cancelled by user."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function get_user_input {
|
|
||||||
echo "======= Gathering Installation Parameters =========="
|
|
||||||
check_whiptail
|
|
||||||
|
|
||||||
# Show welcome message
|
|
||||||
whiptail \
|
|
||||||
--title "ZFS Ubuntu Installer" \
|
|
||||||
--msgbox "Welcome to the ZFS Ubuntu Installer for Hetzner Cloud.\n\nThis script will install Ubuntu 24.04 with ZFS root on your server." \
|
|
||||||
12 60
|
|
||||||
|
|
||||||
# Get user inputs
|
|
||||||
get_hostname
|
|
||||||
get_zfs_pool_name
|
|
||||||
get_root_password
|
|
||||||
}
|
|
||||||
|
|
||||||
# ---- System Detection Functions ----
|
|
||||||
function detect_efi {
|
|
||||||
echo "======= Detecting EFI support =========="
|
|
||||||
|
|
||||||
if [ -d /sys/firmware/efi ]; then
|
|
||||||
echo "✓ EFI firmware detected"
|
|
||||||
EFI_MODE=true
|
|
||||||
BOOT_LABEL="EFI"
|
|
||||||
BOOT_TYPE="ef00"
|
|
||||||
else
|
|
||||||
echo "✓ Legacy BIOS mode detected"
|
|
||||||
EFI_MODE=false
|
|
||||||
BOOT_LABEL="boot"
|
|
||||||
BOOT_TYPE="8300"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function find_install_disk {
|
|
||||||
echo "======= Finding install disk =========="
|
|
||||||
|
|
||||||
local candidate_disks=()
|
|
||||||
|
|
||||||
# Use lsblk to find all unmounted, writable disks
|
|
||||||
while IFS= read -r disk; do
|
|
||||||
[[ -n "$disk" ]] && candidate_disks+=("$disk")
|
|
||||||
done < <(lsblk -npo NAME,TYPE,RO,MOUNTPOINT | awk '
|
|
||||||
$2 == "disk" && $3 == "0" && $4 == "" {print $1}
|
|
||||||
')
|
|
||||||
|
|
||||||
if [[ ${#candidate_disks[@]} -eq 0 ]]; then
|
|
||||||
echo "No suitable installation disks found" >&2
|
|
||||||
echo "Looking for: unmounted, writable disks without partitions in use" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
INSTALL_DISK="${candidate_disks[0]}"
|
|
||||||
echo "Using installation disk: $INSTALL_DISK"
|
|
||||||
|
|
||||||
# Show all available disks for verification
|
|
||||||
echo "All available disks:"
|
|
||||||
lsblk -o NAME,SIZE,TYPE,MOUNTPOINT,RO | grep -v loop
|
|
||||||
}
|
|
||||||
|
|
||||||
# ---- Rescue System Preparation Functions ----
|
|
||||||
function remove_unused_kernels {
|
|
||||||
echo "=========== Removing 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
|
|
||||||
|
|
||||||
for pkg in "linux-headers-$kver" "linux-image-$kver"; do
|
|
||||||
if dpkg -l "$pkg" 2>/dev/null | grep -q '^ii'; then
|
|
||||||
echo "Purging $pkg ..."
|
|
||||||
apt purge --yes "$pkg"
|
|
||||||
else
|
|
||||||
echo "Package $pkg not installed, skipping."
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
function install_zfs_on_rescue_system {
|
|
||||||
echo "======= Installing ZFS on rescue system =========="
|
|
||||||
echo "zfs-dkms zfs-dkms/note-incompatible-licenses note true" | debconf-set-selections
|
|
||||||
# Enable Hetzner bookworm-backports
|
|
||||||
sed -i 's/^# deb http:\/\/mirror.hetzner.com\/debian\/packages bookworm-backports/deb http:\/\/mirror.hetzner.com\/debian\/packages bookworm-backports/' /etc/apt/sources.list
|
|
||||||
apt update
|
|
||||||
apt -t bookworm-backports install -y zfsutils-linux
|
|
||||||
}
|
|
||||||
|
|
||||||
# ---- Disk Partitioning Functions ----
|
|
||||||
function partition_disk {
|
|
||||||
echo "======= Partitioning disk =========="
|
|
||||||
sgdisk -Z "$INSTALL_DISK"
|
|
||||||
|
|
||||||
if [ "$EFI_MODE" = true ]; then
|
|
||||||
echo "Creating EFI partition layout"
|
|
||||||
# EFI System Partition (ESP) - 64MB is plenty for ZFSBootMenu
|
|
||||||
sgdisk -n1:1M:+128M -t1:ef00 -c1:"EFI" "$INSTALL_DISK"
|
|
||||||
# ZFS partition
|
|
||||||
sgdisk -n2:0:0 -t2:bf00 -c2:"zfs" "$INSTALL_DISK"
|
|
||||||
else
|
|
||||||
echo "Creating BIOS partition layout"
|
|
||||||
# /boot partition - 64MB is also sufficient for BIOS ZFSBootMenu
|
|
||||||
sgdisk -n1:1M:+128M -t1:8300 -c1:"boot" "$INSTALL_DISK"
|
|
||||||
# ZFS partition
|
|
||||||
sgdisk -n2:0:0 -t2:bf00 -c2:"zfs" "$INSTALL_DISK"
|
|
||||||
# Set legacy BIOS bootable flag
|
|
||||||
sgdisk -A 1:set:2 "$INSTALL_DISK"
|
|
||||||
fi
|
|
||||||
|
|
||||||
partprobe "$INSTALL_DISK" || true
|
|
||||||
udevadm settle
|
|
||||||
|
|
||||||
# Set partition variables based on mode
|
|
||||||
if [ "$EFI_MODE" = true ]; then
|
|
||||||
BOOT_PART="$(blkid -t PARTLABEL='EFI' -o device)"
|
|
||||||
ZFS_PART="$(blkid -t PARTLABEL='zfs' -o device)"
|
|
||||||
# Format ESP as FAT32
|
|
||||||
mkfs.fat -F 32 -n EFI "$BOOT_PART"
|
|
||||||
else
|
|
||||||
BOOT_PART="$(blkid -t PARTLABEL='boot' -o device)"
|
|
||||||
ZFS_PART="$(blkid -t PARTLABEL='zfs' -o device)"
|
|
||||||
mkfs.ext4 -F -L boot "$BOOT_PART"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# ---- ZFS Pool and Dataset Functions ----
|
|
||||||
function create_zfs_pool {
|
|
||||||
echo "======= Creating ZFS pool =========="
|
|
||||||
# Clean up any existing ZFS binaries in PATH
|
|
||||||
rm -f "$(which zfs)" 2>/dev/null || true
|
|
||||||
rm -f "$(which zpool)" 2>/dev/null || true
|
|
||||||
|
|
||||||
export PATH=/usr/sbin:$PATH
|
|
||||||
modprobe zfs
|
|
||||||
|
|
||||||
zpool create -f -o ashift=12 \
|
|
||||||
-o cachefile="/etc/zfs/zpool.cache" \
|
|
||||||
-O compression=lz4 \
|
|
||||||
-O acltype=posixacl \
|
|
||||||
-O xattr=sa \
|
|
||||||
-O mountpoint=none \
|
|
||||||
"$ZFS_POOL" "$ZFS_PART"
|
|
||||||
|
|
||||||
zfs create -o mountpoint=none "$ZFS_POOL/ROOT"
|
|
||||||
zfs create -o mountpoint=legacy "$ZFS_POOL/ROOT/ubuntu"
|
|
||||||
|
|
||||||
echo "======= Assigning $ZFS_POOL/ROOT/ubuntu dataset as bootable =========="
|
|
||||||
zpool set bootfs="$ZFS_POOL/ROOT/ubuntu" "$ZFS_POOL"
|
|
||||||
zpool set cachefile="/etc/zfs/zpool.cache" "$ZFS_POOL"
|
|
||||||
}
|
|
||||||
|
|
||||||
function create_additional_zfs_datasets {
|
|
||||||
echo "======= Creating additional ZFS datasets with TEMPORARY mountpoints =========="
|
|
||||||
|
|
||||||
# Ensure parent datasets are created first
|
|
||||||
zfs create -o mountpoint=none "$ZFS_POOL/ROOT/ubuntu/var"
|
|
||||||
zfs create -o mountpoint=none "$ZFS_POOL/ROOT/ubuntu/var/cache"
|
|
||||||
|
|
||||||
# Create leaf datasets with temporary mountpoints under $TARGET
|
|
||||||
zfs create -o com.sun:auto-snapshot=false -o mountpoint="$TARGET/tmp" "$ZFS_POOL/ROOT/ubuntu/tmp"
|
|
||||||
zfs set devices=off "$ZFS_POOL/ROOT/ubuntu/tmp"
|
|
||||||
|
|
||||||
zfs create -o com.sun:auto-snapshot=false -o mountpoint="$TARGET/var/tmp" "$ZFS_POOL/ROOT/ubuntu/var/tmp"
|
|
||||||
zfs set devices=off "$ZFS_POOL/ROOT/ubuntu/var/tmp"
|
|
||||||
|
|
||||||
zfs create -o mountpoint="$TARGET/var/log" "$ZFS_POOL/ROOT/ubuntu/var/log"
|
|
||||||
zfs set atime=off "$ZFS_POOL/ROOT/ubuntu/var/log"
|
|
||||||
|
|
||||||
zfs create -o com.sun:auto-snapshot=false -o mountpoint="$TARGET/var/cache/apt" "$ZFS_POOL/ROOT/ubuntu/var/cache/apt"
|
|
||||||
zfs set atime=off "$ZFS_POOL/ROOT/ubuntu/var/cache/apt"
|
|
||||||
|
|
||||||
# Create home dataset separately
|
|
||||||
zfs create -o mountpoint="$TARGET/home" "$ZFS_POOL/home"
|
|
||||||
|
|
||||||
# Mount all datasets
|
|
||||||
zfs mount -a
|
|
||||||
|
|
||||||
# Set permissions on the actual ZFS datasets
|
|
||||||
echo "Setting permissions on ZFS datasets..."
|
|
||||||
chmod 1777 "$TARGET/tmp"
|
|
||||||
chmod 1777 "$TARGET/var/tmp"
|
|
||||||
echo "✓ Temp directory permissions set (1777)"
|
|
||||||
}
|
|
||||||
|
|
||||||
function set_final_mountpoints {
|
|
||||||
echo "======= Setting final mountpoints =========="
|
|
||||||
|
|
||||||
# Leaf datasets - actual system mountpoints
|
|
||||||
zfs set mountpoint=/tmp "$ZFS_POOL/ROOT/ubuntu/tmp"
|
|
||||||
zfs set mountpoint=/var/tmp "$ZFS_POOL/ROOT/ubuntu/var/tmp"
|
|
||||||
zfs set mountpoint=/var/log "$ZFS_POOL/ROOT/ubuntu/var/log"
|
|
||||||
zfs set mountpoint=/var/cache/apt "$ZFS_POOL/ROOT/ubuntu/var/cache/apt"
|
|
||||||
|
|
||||||
# Home dataset - separate from OS
|
|
||||||
zfs set mountpoint=/home "$ZFS_POOL/home"
|
|
||||||
echo ""
|
|
||||||
echo "Detailed dataset listing:"
|
|
||||||
zfs list -o name,mountpoint -r "$ZFS_POOL"
|
|
||||||
}
|
|
||||||
|
|
||||||
# ---- System Bootstrap Functions ----
|
|
||||||
function bootstrap_ubuntu_system {
|
|
||||||
echo "======= Bootstrapping Ubuntu to temporary directory =========="
|
|
||||||
local TEMP_STAGE=$(mktemp -d)
|
|
||||||
echo "Created temporary staging directory: $TEMP_STAGE"
|
|
||||||
|
|
||||||
# Cleanup function for temp directory
|
|
||||||
cleanup_temp_stage() {
|
|
||||||
if [ -d "$TEMP_STAGE" ]; then
|
|
||||||
echo "Cleaning up temporary staging directory..."
|
|
||||||
rm -rf "$TEMP_STAGE"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Add trap to ensure cleanup on script exit
|
|
||||||
trap cleanup_temp_stage EXIT
|
|
||||||
|
|
||||||
# Add Hetzner Ubuntu mirror as trusted
|
|
||||||
echo "deb [trusted=yes] http://mirror.hetzner.com/ubuntu/packages noble main" > /etc/apt/sources.list.d/ubuntu-temp.list
|
|
||||||
|
|
||||||
# Update and download ubuntu-keyring without sandbox warning
|
|
||||||
apt-get update
|
|
||||||
apt-get -o APT::Sandbox::User=root download ubuntu-keyring
|
|
||||||
|
|
||||||
# Verify download was successful
|
|
||||||
if [ ! -f ubuntu-keyring*.deb ]; then
|
|
||||||
echo "ERROR: Failed to download ubuntu-keyring package"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Extract the keyring
|
|
||||||
dpkg-deb -x ubuntu-keyring*.deb /tmp/ubuntu-keyring-extract/
|
|
||||||
mkdir -p /usr/share/keyrings
|
|
||||||
cp /tmp/ubuntu-keyring-extract/usr/share/keyrings/ubuntu-archive-keyring.gpg /usr/share/keyrings/
|
|
||||||
|
|
||||||
echo "✓ Downloaded and extracted ubuntu-keyring package"
|
|
||||||
|
|
||||||
# Clean up - remove temporary repository and restore apt state
|
|
||||||
rm -f /etc/apt/sources.list.d/ubuntu-temp.list
|
|
||||||
apt update
|
|
||||||
|
|
||||||
# Clean up downloaded package
|
|
||||||
rm -f ubuntu-keyring*.deb
|
|
||||||
|
|
||||||
apt install -y mmdebstrap
|
|
||||||
|
|
||||||
mmdebstrap --variant=debootstrap \
|
|
||||||
--keyring=/usr/share/keyrings/ubuntu-archive-keyring.gpg \
|
|
||||||
--include=systemd-resolved,locales,debconf-i18n,apt-utils,keyboard-configuration,console-setup,kbd,extlinux,initramfs-tools,zstd \
|
|
||||||
"$UBUNTU_CODENAME" "$TEMP_STAGE" \
|
|
||||||
"$MIRROR_MAIN" "$MIRROR_UPDATES" "$MIRROR_BACKPORTS" "$MIRROR_SECURITY"
|
|
||||||
|
|
||||||
echo "======= Copying staged system to ZFS datasets =========="
|
|
||||||
# Mount root dataset for copying
|
|
||||||
mkdir -p "$TARGET"
|
|
||||||
mount -t zfs "$ZFS_POOL/ROOT/ubuntu" "$TARGET"
|
|
||||||
|
|
||||||
create_additional_zfs_datasets
|
|
||||||
|
|
||||||
# Use rsync to copy the entire system (this will populate all datasets)
|
|
||||||
echo "Copying staged system to ZFS datasets..."
|
|
||||||
rsync -aAX "$TEMP_STAGE/" "$TARGET/"
|
|
||||||
|
|
||||||
echo "Staged system copied successfully"
|
|
||||||
echo "Source size: $(du -sh "$TEMP_STAGE")"
|
|
||||||
echo "Target size: $(du -sh "$TARGET")"
|
|
||||||
|
|
||||||
# Clean up temp directory
|
|
||||||
cleanup_temp_stage
|
|
||||||
trap - EXIT
|
|
||||||
}
|
|
||||||
|
|
||||||
function setup_chroot_environment {
|
|
||||||
echo "======= Mounting virtual filesystems for chroot =========="
|
|
||||||
mount -t proc proc "$TARGET/proc"
|
|
||||||
mount -t sysfs sysfs "$TARGET/sys"
|
|
||||||
mount -t tmpfs tmpfs "$TARGET/run"
|
|
||||||
mount -t tmpfs tmpfs "$TARGET/tmp"
|
|
||||||
mount --bind /dev "$TARGET/dev"
|
|
||||||
mount --bind /dev/pts "$TARGET/dev/pts"
|
|
||||||
|
|
||||||
configure_dns_resolution
|
|
||||||
}
|
|
||||||
|
|
||||||
function configure_dns_resolution {
|
|
||||||
echo "======= Configuring DNS resolution =========="
|
|
||||||
mkdir -p "$TARGET/run/systemd/resolve"
|
|
||||||
|
|
||||||
if command -v resolvectl >/dev/null 2>&1; then
|
|
||||||
echo "Getting DNS from resolvectl..."
|
|
||||||
|
|
||||||
# First try Global DNS servers
|
|
||||||
local DNS_SERVERS=$(resolvectl dns | awk '
|
|
||||||
/^Global:/ {
|
|
||||||
for(i=2; i<=NF; i++) print $i
|
|
||||||
}
|
|
||||||
' | head -3)
|
|
||||||
|
|
||||||
# If Global is empty, find first non-empty link
|
|
||||||
if [ -z "$DNS_SERVERS" ]; then
|
|
||||||
echo "No global DNS servers found, searching for first non-empty link..."
|
|
||||||
DNS_SERVERS=$(resolvectl dns | awk '
|
|
||||||
/^Link [0-9]+ / && NF > 3 {
|
|
||||||
for(i=4; i<=NF; i++) print $i # Start from field 4 to skip the interface name
|
|
||||||
exit # Stop after first non-empty link
|
|
||||||
}
|
|
||||||
')
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "$DNS_SERVERS" ]; then
|
|
||||||
# Create resolv.conf with the DNS servers
|
|
||||||
echo "$DNS_SERVERS" | while read -r dns; do
|
|
||||||
echo "nameserver $dns"
|
|
||||||
done > "$TARGET/run/systemd/resolve/stub-resolv.conf"
|
|
||||||
echo "Using DNS servers: $(echo "$DNS_SERVERS" | tr '\n' ' ')"
|
|
||||||
else
|
|
||||||
echo "ERROR: No DNS servers found in resolvectl output"
|
|
||||||
echo "resolvectl dns output:"
|
|
||||||
resolvectl dns
|
|
||||||
echo "Cannot continue without DNS configuration"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "ERROR: resolvectl command not found"
|
|
||||||
echo "Cannot configure DNS without resolvectl"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# ---- System Configuration Functions ----
|
|
||||||
function configure_basic_system {
|
|
||||||
echo "======= Configuring basic system settings =========="
|
|
||||||
chroot "$TARGET" /bin/bash <<EOF
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
# Set hostname from variable
|
|
||||||
echo "$SYSTEM_HOSTNAME" > /etc/hostname
|
|
||||||
|
|
||||||
# Configure timezone (Vienna)
|
|
||||||
echo "Europe/Vienna" > /etc/timezone
|
|
||||||
ln -sf /usr/share/zoneinfo/Europe/Vienna /etc/localtime
|
|
||||||
|
|
||||||
# Generate locales
|
|
||||||
cat > /etc/locale.gen <<'LOCALES'
|
|
||||||
en_US.UTF-8 UTF-8
|
|
||||||
de_AT.UTF-8 UTF-8
|
|
||||||
fr_FR.UTF-8 UTF-8
|
|
||||||
ru_RU.UTF-8 UTF-8
|
|
||||||
LOCALES
|
|
||||||
|
|
||||||
locale-gen
|
|
||||||
|
|
||||||
# Set default locale
|
|
||||||
update-locale LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8
|
|
||||||
|
|
||||||
# Configure keyboard for German and US with Alt+Shift toggle
|
|
||||||
cat > /etc/default/keyboard <<'KEYBOARD'
|
|
||||||
# KEYBOARD CONFIGURATION FILE
|
|
||||||
|
|
||||||
# Consult the keyboard(5) manual page.
|
|
||||||
|
|
||||||
XKBMODEL="pc105"
|
|
||||||
XKBLAYOUT="de,ru"
|
|
||||||
XKBVARIANT=","
|
|
||||||
XKBOPTIONS="grp:ctrl_shift_toggle"
|
|
||||||
|
|
||||||
BACKSPACE="guess"
|
|
||||||
KEYBOARD
|
|
||||||
|
|
||||||
# Apply keyboard configuration to console
|
|
||||||
setupcon --force
|
|
||||||
|
|
||||||
# Update /etc/hosts with the hostname
|
|
||||||
echo "127.0.0.1 localhost" > /etc/hosts
|
|
||||||
echo "127.0.1.1 $SYSTEM_HOSTNAME" >> /etc/hosts
|
|
||||||
echo "::1 localhost ip6-localhost ip6-loopback" >> /etc/hosts
|
|
||||||
echo "ff02::1 ip6-allnodes" >> /etc/hosts
|
|
||||||
echo "ff02::2 ip6-allrouters" >> /etc/hosts
|
|
||||||
|
|
||||||
# Set proper permissions for ZFS datasets
|
|
||||||
chmod 1777 /tmp
|
|
||||||
chmod 1777 /var/tmp
|
|
||||||
EOF
|
|
||||||
|
|
||||||
echo "======= Configuration Summary ======="
|
|
||||||
chroot "$TARGET" /bin/bash <<'EOF'
|
|
||||||
echo "Hostname: $(cat /etc/hostname)"
|
|
||||||
echo "Timezone: $(cat /etc/timezone)"
|
|
||||||
echo "Current time: $(date)"
|
|
||||||
echo "Default locale: $(grep LANG /etc/default/locale)"
|
|
||||||
echo "Available locales:"
|
|
||||||
locale -a | grep -E "(en_US|de_AT|fr_FR|ru_RU)"
|
|
||||||
echo "Keyboard layout: $(grep XKBLAYOUT /etc/default/keyboard)"
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
function install_system_packages {
|
|
||||||
echo "======= Installing ZFS and essential packages in chroot =========="
|
|
||||||
chroot "$TARGET" /bin/bash <<'EOF'
|
|
||||||
set -euo pipefail
|
|
||||||
# Update package lists
|
|
||||||
apt update
|
|
||||||
|
|
||||||
# Install generic kernel (creates files in ZFS dataset /boot)
|
|
||||||
apt install -y --no-install-recommends linux-image-generic linux-headers-generic
|
|
||||||
|
|
||||||
# Install ZFS utilities and aux packages
|
|
||||||
|
|
||||||
echo "zfs-dkms zfs-dkms/note-incompatible-licenses note true" | debconf-set-selections
|
|
||||||
|
|
||||||
apt install -y zfs-dkms zfsutils-linux zfs-initramfs software-properties-common bash curl nano htop net-tools ssh
|
|
||||||
|
|
||||||
# Ensure ZFS module is included in initramfs
|
|
||||||
echo "zfs" >> /etc/initramfs-tools/modules
|
|
||||||
|
|
||||||
# Generate initramfs with ZFS support
|
|
||||||
update-initramfs -u -k all
|
|
||||||
|
|
||||||
# Verify kernel installation
|
|
||||||
echo "Installed kernel packages:"
|
|
||||||
dpkg -l | grep linux-image
|
|
||||||
echo "Kernel version:"
|
|
||||||
ls /lib/modules/
|
|
||||||
echo "Kernel files in ZFS dataset:"
|
|
||||||
ls -la /boot/vmlinuz* /boot/initrd.img* 2>/dev/null || echo "No kernel files found"
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
function configure_ssh {
|
|
||||||
echo "======= Setting up OpenSSH =========="
|
|
||||||
mkdir -p "$TARGET/root/.ssh/"
|
|
||||||
cp /root/.ssh/authorized_keys "$TARGET/root/.ssh/authorized_keys"
|
|
||||||
sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/g' "$TARGET/etc/ssh/sshd_config"
|
|
||||||
sed -i 's/#PubkeyAuthentication yes/PubkeyAuthentication yes/g' "$TARGET/etc/ssh/sshd_config"
|
|
||||||
|
|
||||||
chroot "$TARGET" /bin/bash <<'EOF'
|
|
||||||
rm /etc/ssh/ssh_host_*
|
|
||||||
dpkg-reconfigure openssh-server -f noninteractive
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
function set_root_credentials {
|
|
||||||
echo "======= Setting root password =========="
|
|
||||||
chroot "$TARGET" /bin/bash -c "echo root:$(printf "%q" "$ROOT_PASSWORD") | chpasswd"
|
|
||||||
|
|
||||||
echo "============ Setting up root prompt ============"
|
|
||||||
cat > "$TARGET/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
|
|
||||||
}
|
|
||||||
|
|
||||||
# ---- Bootloader Functions ----
|
|
||||||
function setup_efi_boot {
|
|
||||||
echo "======= Setting up EFI boot =========="
|
|
||||||
|
|
||||||
# Mount EFI System Partition
|
|
||||||
mkdir -p "$MAIN_BOOT"
|
|
||||||
mount "$BOOT_PART" "$MAIN_BOOT"
|
|
||||||
|
|
||||||
# Create EFI directory structure
|
|
||||||
mkdir -p "$MAIN_BOOT/EFI/Boot"
|
|
||||||
|
|
||||||
# Download ZFSBootMenu EFI binary
|
|
||||||
echo "Downloading ZFSBootMenu EFI binary from: $ZBM_EFI_URL"
|
|
||||||
curl -L "$ZBM_EFI_URL" -o "$MAIN_BOOT/EFI/Boot/bootx64.efi"
|
|
||||||
}
|
|
||||||
|
|
||||||
function setup_bios_boot {
|
|
||||||
echo "======= Setting up BIOS boot =========="
|
|
||||||
|
|
||||||
# Mount boot partition
|
|
||||||
mkdir -p "$MAIN_BOOT"
|
|
||||||
mount "$BOOT_PART" "$MAIN_BOOT"
|
|
||||||
|
|
||||||
# Install extlinux in rescue system if needed
|
|
||||||
if ! command -v extlinux &> /dev/null; then
|
|
||||||
echo "Installing extlinux in rescue system..."
|
|
||||||
apt update
|
|
||||||
apt install -y extlinux
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Install extlinux
|
|
||||||
extlinux --install "$MAIN_BOOT"
|
|
||||||
|
|
||||||
# Create extlinux configuration
|
|
||||||
cat > "$MAIN_BOOT/extlinux.conf" << 'EOF'
|
|
||||||
DEFAULT zfsbootmenu
|
|
||||||
PROMPT 0
|
|
||||||
TIMEOUT 0
|
|
||||||
|
|
||||||
LABEL zfsbootmenu
|
|
||||||
LINUX /zfsbootmenu/vmlinuz-bootmenu
|
|
||||||
INITRD /zfsbootmenu/initramfs-bootmenu.img
|
|
||||||
APPEND ro quiet
|
|
||||||
EOF
|
|
||||||
|
|
||||||
echo "Generated extlinux.conf:"
|
|
||||||
cat "$MAIN_BOOT/extlinux.conf"
|
|
||||||
|
|
||||||
# Download and install ZFSBootMenu for BIOS
|
|
||||||
local TEMP_ZBM=$(mktemp -d)
|
|
||||||
echo "Downloading ZFSBootMenu for BIOS from: $ZBM_BIOS_URL"
|
|
||||||
curl -L "$ZBM_BIOS_URL" -o "$TEMP_ZBM/zbm.tar.gz"
|
|
||||||
tar -xz -C "$TEMP_ZBM" -f "$TEMP_ZBM/zbm.tar.gz" --strip-components=1
|
|
||||||
|
|
||||||
# Copy ZFSBootMenu to boot partition
|
|
||||||
mkdir -p "$MAIN_BOOT/zfsbootmenu"
|
|
||||||
cp "$TEMP_ZBM"/vmlinuz* "$MAIN_BOOT/zfsbootmenu/"
|
|
||||||
cp "$TEMP_ZBM"/initramfs* "$MAIN_BOOT/zfsbootmenu/"
|
|
||||||
|
|
||||||
# Clean up
|
|
||||||
rm -rf "$TEMP_ZBM"
|
|
||||||
|
|
||||||
echo "ZFSBootMenu files copied to boot partition:"
|
|
||||||
ls -la "$MAIN_BOOT/zfsbootmenu/"
|
|
||||||
|
|
||||||
# Install MBR and set boot flag
|
|
||||||
dd bs=440 conv=notrunc count=1 if="$TARGET/usr/lib/EXTLINUX/gptmbr.bin" of="$INSTALL_DISK"
|
|
||||||
parted "$INSTALL_DISK" set 1 boot on
|
|
||||||
|
|
||||||
echo "BIOS boot setup complete"
|
|
||||||
}
|
|
||||||
|
|
||||||
function configure_bootloader {
|
|
||||||
echo "======= Setting up boot based on firmware type =========="
|
|
||||||
if [ "$EFI_MODE" = true ]; then
|
|
||||||
setup_efi_boot
|
|
||||||
else
|
|
||||||
setup_bios_boot
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "======= Configuring ZFSBootMenu for auto-detection =========="
|
|
||||||
zfs set org.zfsbootmenu:commandline="ro quiet" "$ZFS_POOL/ROOT/ubuntu"
|
|
||||||
|
|
||||||
echo "Boot configuration:"
|
|
||||||
zfs get org.zfsbootmenu:commandline "$ZFS_POOL/ROOT/ubuntu"
|
|
||||||
}
|
|
||||||
|
|
||||||
# ---- System Services Functions ----
|
|
||||||
function configure_system_services {
|
|
||||||
echo "======= Configuring ZFS cachefile in chrooted system =========="
|
|
||||||
mkdir -p "$TARGET/etc/zfs"
|
|
||||||
cp /etc/zfs/zpool.cache "$TARGET/etc/zfs/zpool.cache"
|
|
||||||
|
|
||||||
echo "Cachefile status:"
|
|
||||||
zpool get cachefile "$ZFS_POOL"
|
|
||||||
ls -la "$TARGET/etc/zfs/zpool.cache" && echo "✓ Cachefile ready" || echo "✗ Cachefile failed"
|
|
||||||
|
|
||||||
echo "======= Enabling essential system services =========="
|
|
||||||
chroot "$TARGET" /bin/bash <<'EOF'
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
systemctl enable systemd-resolved
|
|
||||||
systemctl enable systemd-timesyncd
|
|
||||||
|
|
||||||
systemctl enable zfs-import-cache
|
|
||||||
systemctl enable zfs-mount
|
|
||||||
|
|
||||||
systemctl enable ssh
|
|
||||||
systemctl enable apt-daily.timer
|
|
||||||
|
|
||||||
echo "Enabled services:"
|
|
||||||
systemctl list-unit-files | grep enabled
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
function configure_networking {
|
|
||||||
echo "======= Configuring Netplan for Hetzner Cloud =========="
|
|
||||||
chroot "$TARGET" /bin/bash <<'EOF'
|
|
||||||
set -euo pipefail
|
|
||||||
# Create Netplan configuration that matches all non-loopback interfaces
|
|
||||||
cat > /etc/netplan/01-hetzner.yaml <<'EOL'
|
|
||||||
network:
|
|
||||||
version: 2
|
|
||||||
renderer: networkd
|
|
||||||
ethernets:
|
|
||||||
all-interfaces:
|
|
||||||
match:
|
|
||||||
name: "!lo"
|
|
||||||
dhcp4: true
|
|
||||||
dhcp6: true
|
|
||||||
dhcp4-overrides:
|
|
||||||
use-dns: true
|
|
||||||
use-hostname: true
|
|
||||||
use-domains: true
|
|
||||||
route-metric: 100
|
|
||||||
dhcp6-overrides:
|
|
||||||
use-dns: true
|
|
||||||
use-hostname: true
|
|
||||||
use-domains: true
|
|
||||||
route-metric: 100
|
|
||||||
critical: true
|
|
||||||
EOL
|
|
||||||
|
|
||||||
# Set proper permissions - Netplan requires strict permissions (600)
|
|
||||||
chmod 600 /etc/netplan/01-hetzner.yaml
|
|
||||||
chown root:root /etc/netplan/01-hetzner.yaml
|
|
||||||
|
|
||||||
# Apply the Netplan configuration
|
|
||||||
netplan generate
|
|
||||||
echo "Netplan configuration created for all interfaces"
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
# ---- Cleanup and Finalization Functions ----
|
|
||||||
function unmount_all_datasets_and_partitions {
|
|
||||||
echo "======= Unmounting all datasets =========="
|
|
||||||
|
|
||||||
# First, unmount all auto-mounted ZFS datasets (tmp, var/tmp, var/log, etc.)
|
|
||||||
echo "Unmounting auto-mounted ZFS datasets..."
|
|
||||||
zfs umount -a 2>/dev/null || true
|
|
||||||
|
|
||||||
# Manually unmount the root legacy dataset from $TARGET
|
|
||||||
if mountpoint -q "$TARGET"; then
|
|
||||||
echo "Unmounting root dataset from $TARGET"
|
|
||||||
umount "$TARGET" 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Manually unmount boot partition if mounted
|
|
||||||
if mountpoint -q "$MAIN_BOOT"; then
|
|
||||||
echo "Unmounting boot partition from $MAIN_BOOT"
|
|
||||||
umount "$MAIN_BOOT" 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Wait for unmounts to complete
|
|
||||||
sleep 1
|
|
||||||
|
|
||||||
# Force unmount any stubborn datasets
|
|
||||||
if zfs get mounted -r "$ZFS_POOL" 2>/dev/null | grep -q "yes"; then
|
|
||||||
echo "Forcing unmount of remaining ZFS datasets..."
|
|
||||||
zfs umount -a -f 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Final verification
|
|
||||||
local mounted_count=0
|
|
||||||
mounted_count=$(zfs get mounted -r "$ZFS_POOL" 2>/dev/null | grep -c "yes" || true)
|
|
||||||
|
|
||||||
if [ "$mounted_count" -gt 0 ]; then
|
|
||||||
echo "WARNING: $mounted_count dataset(s) still mounted after unmount attempt:"
|
|
||||||
zfs get mounted -r "$ZFS_POOL" 2>/dev/null | grep "yes" || true
|
|
||||||
else
|
|
||||||
echo "✓ All ZFS datasets successfully unmounted"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Verify $TARGET is unmounted
|
|
||||||
if mountpoint -q "$TARGET"; then
|
|
||||||
echo "WARNING: $TARGET is still mounted!"
|
|
||||||
mount | grep "$TARGET" || true
|
|
||||||
else
|
|
||||||
echo "✓ $TARGET successfully unmounted"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Verify $MAIN_BOOT is unmounted
|
|
||||||
if mountpoint -q "$MAIN_BOOT"; then
|
|
||||||
echo "WARNING: $MAIN_BOOT is still mounted!"
|
|
||||||
mount | grep "$MAIN_BOOT" || true
|
|
||||||
else
|
|
||||||
echo "✓ $MAIN_BOOT successfully unmounted"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function unmount_chroot_environment {
|
|
||||||
echo "======= Unmounting virtual filesystems =========="
|
|
||||||
# Unmount virtual filesystems first
|
|
||||||
for dir in dev/pts dev tmp run sys proc; do
|
|
||||||
if mountpoint -q "$TARGET/$dir"; then
|
|
||||||
echo "Unmounting $TARGET/$dir"
|
|
||||||
umount "$TARGET/$dir" 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
function finalize_system_resolved {
|
|
||||||
echo "======= Setting systemd-resolved configuration for final boot =========="
|
|
||||||
# This must be done while $TARGET is still mounted
|
|
||||||
mkdir -p "$TARGET/run/systemd/resolve"
|
|
||||||
cat > "$TARGET/run/systemd/resolve/stub-resolv.conf" << 'EOF'
|
|
||||||
nameserver 127.0.0.53
|
|
||||||
options edns0 trust-ad
|
|
||||||
search .
|
|
||||||
EOF
|
|
||||||
echo "✓ systemd-resolved configuration set"
|
|
||||||
}
|
|
||||||
|
|
||||||
function export_zfs_pool {
|
|
||||||
echo "======= Exporting ZFS pool =========="
|
|
||||||
zpool export "$ZFS_POOL" 2>/dev/null || true
|
|
||||||
|
|
||||||
# Verify everything is unmounted
|
|
||||||
if mountpoint -q "$TARGET"; then
|
|
||||||
echo "WARNING: $TARGET is still mounted!"
|
|
||||||
mount | grep "$TARGET"
|
|
||||||
else
|
|
||||||
echo "✓ All filesystems successfully unmounted"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function show_final_instructions {
|
|
||||||
echo ""
|
|
||||||
echo "=========================================="
|
|
||||||
echo " INSTALLATION COMPLETE! "
|
|
||||||
echo "=========================================="
|
|
||||||
echo ""
|
|
||||||
echo "System Information:"
|
|
||||||
echo " Hostname: $SYSTEM_HOSTNAME"
|
|
||||||
echo " ZFS Pool: $ZFS_POOL"
|
|
||||||
echo " Boot Mode: $([ "$EFI_MODE" = true ] && echo "EFI" || echo "BIOS")"
|
|
||||||
echo " Ubuntu Version: $UBUNTU_CODENAME"
|
|
||||||
echo " Networking: systemd-networkd + systemd-resolved"
|
|
||||||
echo ""
|
|
||||||
echo "=========================================="
|
|
||||||
echo "Rebooting..."
|
|
||||||
}
|
|
||||||
|
|
||||||
# ---- Main Execution Function ----
|
|
||||||
function main {
|
|
||||||
echo "Starting ZFS Ubuntu installation on Hetzner Cloud..."
|
|
||||||
|
|
||||||
# Phase 0: User input
|
|
||||||
get_user_input
|
|
||||||
|
|
||||||
# Phase 1: System detection and preparation
|
|
||||||
detect_efi
|
|
||||||
find_install_disk
|
|
||||||
|
|
||||||
# Show summary and get final confirmation
|
|
||||||
show_summary_and_confirm
|
|
||||||
|
|
||||||
remove_unused_kernels
|
|
||||||
install_zfs_on_rescue_system
|
|
||||||
|
|
||||||
# Phase 2: Disk partitioning and ZFS setup
|
|
||||||
partition_disk
|
|
||||||
create_zfs_pool
|
|
||||||
|
|
||||||
# Phase 3: System bootstrap
|
|
||||||
bootstrap_ubuntu_system
|
|
||||||
setup_chroot_environment
|
|
||||||
|
|
||||||
# Phase 4: System configuration
|
|
||||||
configure_basic_system
|
|
||||||
install_system_packages
|
|
||||||
configure_ssh
|
|
||||||
set_root_credentials
|
|
||||||
configure_system_services
|
|
||||||
configure_networking
|
|
||||||
|
|
||||||
# Phase 5: Bootloader setup
|
|
||||||
configure_bootloader
|
|
||||||
|
|
||||||
# Phase 6: Cleanup and finalization
|
|
||||||
unmount_chroot_environment
|
|
||||||
finalize_system_resolved
|
|
||||||
unmount_all_datasets_and_partitions
|
|
||||||
|
|
||||||
# Phase 7: Final mountpoints and export
|
|
||||||
set_final_mountpoints
|
|
||||||
|
|
||||||
export_zfs_pool
|
|
||||||
|
|
||||||
show_final_instructions
|
|
||||||
|
|
||||||
reboot
|
|
||||||
}
|
|
||||||
|
|
||||||
# Execute main function
|
|
||||||
main "$@"
|
|
||||||
Reference in New Issue
Block a user