19-define-os-ip #24

Merged
hygienic-books merged 23 commits from 19-define-os-ip into main 2023-11-12 03:26:37 +00:00
2 changed files with 317 additions and 66 deletions

View File

@ -109,6 +109,30 @@ In cases where a variable is both exported prior to script execution and specifi
Known options are as follows.
### Kernel downgrade
By default we install newest `linux` and `linux-headers` packages into a `chroot`. Once we're in that `chroot` we then install newest [AUR zfs-dkms package](https://aur.archlinux.org/packages/zfs-dkms). You may want to override `linux` and `linux-headers` versions to ensure you end up with a compatible mix between them and `zfs-dkms`.
For example:
```
export ARCHZBM_KERNEL_VER=6.5.9.arch2
```
In our `chroot` this will trigger execution of:
```
downgrade --ala-only 'linux=6.5.9.arch2' 'linux-headers=6.5.9.arch2' --ignore always
```
Where `downgrade` is the [AUR downgrade package](https://aur.archlinux.org/packages/downgrade). This will downgrade `linux` and `linux-headers` and will add a setting to your `/etc/pacman.conf`:
```
[options]
IgnorePkg = linux linux-headers
```
Setting `ARCHZBM_KERNEL_VER` to an empty string `''` or keeping it undefined are both valid and will retain newest versions instead of downgrading.
Also read [Kernel selection](#kernel-selection) for details.
### Compression
By default we create a zpool with ZFS property `compression=on`. If the `lz4_compress` pool feature is active this will by default enable `compression=lz4`. See `man 7 zfsprops` for example in ZFS 2.1.9 for details. See `zpool get feature@lz4_compress <pool>` to check this feature's status on your `<pool>`.
@ -243,7 +267,7 @@ This syntax crutch allows you to use the full range of Dropbear-supported `autho
An interactive questionnaire can guide you through settings and goes like this:
![Command line setup questionnaire](https://i.imgur.com/RhCStdu.gif)
![Command line setup questionnaire](https://i.imgur.com/OXG75GH.gif)
To do the questionnaire yourself start this script with the `setup` argument:
@ -296,6 +320,24 @@ When it comes to the point that your SSH client sends an are-your-still-there me
This effectively configures your SSH client to remain connected even through somewhat lossy hops to the Dropbear daemon; and to cleanly disconnect 3 seconds and some change after you've executed whatever you needed to do in ZFSBootMenu.
# Kernel selection
This script compiles ZFS via Arch Linux' [Dynamic Kernel Module Support](https://wiki.archlinux.org/title/Dynamic_Kernel_Module_Support) (DKMS). Not all kernels allow for successful compilation, in some instances a particularly recent kernel version may change APIs to such a degree that ZFS compilation simply fails.
We strongly suggest to that you:
- Firstly, refer to a resource such as the [Arch Linux Archive package version list](https://archive.archlinux.org/packages/l/linux/) to find out what newest kernel version this script will install.
- Secondly, research if newest [AUR zfs-dkms package](https://aur.archlinux.org/packages/zfs-dkms) is compatible with that kernel. Two reasonable points of contact are AUR and its comments section for `zfs-dkms` where users quickly report issues; and the [github.com/openzfs/zfs issues list](https://github.com/openzfs/zfs/issues).
An example for this is that `linux-6.6.1.arch1-1-x86_64` came out on Wednesday, November 8, 2023 at a time when newest `zfs-dkms` package version [was 2.2.0](https://aur.archlinux.org/cgit/aur.git/commit/?h=zfs-dkms&id=da1b6372c57b16f2781a7fda2b95971bb392c5ee) which did not compile against `linux` 6.6.x.
You'd then set for example:
```
export ARCHZBM_KERNEL_VER=6.5.9.arch2
```
Where any 6.5.x version is known to work well with `zfs-dkms`. See also [Kernel downgrade](#kernel-downgrade) for details on how to configure this.
# Flavor choices
We make the following opinionated flavor choices. Feel free to change them to your liking.
@ -418,7 +460,7 @@ In order to generate a new master key after you've changed your user key as ment
--large-block \
--compressed \
'zpool/root/archlinux-sxu@rekey' | \
\
zfs receive \
-Fvu \
-o 'encryption=on' \

291
setup.sh
View File

@ -36,6 +36,17 @@ trap '[ "$?" -ne 77 ] || exit 77' ERR
declare zpool_drive efi_drive boot_drive part_schema
function calculate_prefix_from_netmask () {
# https://stackoverflow.com/a/50419919
c='0'
x='0'"$(printf -- '%o' ${1//./ })"
while [ "${x}" -gt '0' ]; do
# Modulo then bitwise-shift x and store as new x
let c+="$(( x % 2 ))" 'x>>=1'
done
printf -- '%s' '/'"${c}";
}
function setup_env_vars () {
printf -- '%s\n' \
'We will go over a series of questions to create an answer file with' \
@ -57,26 +68,29 @@ function setup_env_vars () {
echo '----------------------------------------'
echo
echo "Do you want compressed datasets?"
select arg_compressed in "Compressed" "Uncompressed"; do
read -u3 -p 'Please type kernel version to use, leave empty for latest, confirm with <Enter>: ' ARCHZBM_KERNEL_VER
echo
echo 'Do you want compressed datasets?'
select arg_compressed in 'Compressed' 'Uncompressed'; do
case "${arg_compressed}" in
Compressed)
'Compressed')
break
;;
Uncompressed)
'Uncompressed')
ARCHZBM_ZFSPROPS_NO_COMPRESSION='true'
break
;;
esac
done <&3 && echo
echo "Do you want encrypted datasets?"
select arg_encrypted in "Encrypted" "Unencrypted"; do
echo 'Do you want encrypted datasets?'
select arg_encrypted in 'Encrypted' 'Unencrypted'; do
case "${arg_encrypted}" in
Encrypted)
'Encrypted')
break
;;
Unencrypted)
'Unencrypted')
ARCHZBM_ZFSPROPS_NO_ENCRYPTION='true'
break
;;
@ -84,14 +98,14 @@ function setup_env_vars () {
done <&3 && echo
if [[ "${arg_encrypted}" = 'Encrypted' ]]; then
echo "Do you want a custom dataset decryption password?"
select arg_custom_dataset_pw in "Yes" "No"; do
echo 'Do you want a custom dataset decryption password?'
select arg_custom_dataset_pw in 'Yes' 'No (use '"'"'password'"'"')'; do
case "${arg_custom_dataset_pw}" in
Yes)
'Yes')
want_custom_dataset_pw='true'
break
;;
No)
'No (use '"'"'password'"'"')')
break
;;
esac
@ -104,14 +118,14 @@ function setup_env_vars () {
fi
fi
echo "Do you want a custom 'root' user password?"
select arg_custom_root_pw in "Yes" "No"; do
echo 'Do you want a custom '"'"'root'"'"' user password?'
select arg_custom_root_pw in 'Yes' 'No (use '"'"'password'"'"')'; do
case "${arg_custom_root_pw}" in
Yes)
'Yes')
want_custom_root_pw='true'
break
;;
No)
'No (use '"'"'password'"'"')')
break
;;
esac
@ -123,7 +137,7 @@ function setup_env_vars () {
echo
fi
echo "Do you want an SSH daemon in ZFSBootMenu?"
echo 'Do you want an SSH daemon in ZFSBootMenu?'
select arg_ssh_in_zbm in "Yes" "No"; do
case "${arg_ssh_in_zbm}" in
Yes)
@ -137,8 +151,8 @@ function setup_env_vars () {
done <&3 && echo
if [[ "${want_ssh_in_zbm}" ]]; then
echo "How do you want to assign an IP address in ZFSBootMenu?"
select arg_ip_autoconf_method in "Statically" "Dynamically, DHCP" "Dynamically, BOOTP" "Dynamically, RARP"; do
echo 'How do you want to assign an IP address in ZFSBootMenu?'
select arg_ip_autoconf_method in 'Statically' 'Dynamically, DHCP' 'Dynamically, BOOTP' 'Dynamically, RARP'; do
case "${arg_ip_autoconf_method}" in
'Statically')
ARCHZBM_NET_AUTOCONF='none'
@ -173,8 +187,8 @@ function setup_env_vars () {
echo
fi
echo "Do you want a custom SSH listening port?"
select arg_custom_ssh_port in "Yes (let me specify)" "No (keep port 22)"; do
echo 'Do you want a custom SSH listening port?'
select arg_custom_ssh_port in 'Yes (let me specify)' 'No (keep port 22)'; do
case "${arg_custom_ssh_port}" in
'Yes (let me specify)')
want_custom_ssh_port='true'
@ -191,8 +205,8 @@ function setup_env_vars () {
echo
fi
echo "Do you want the SSH daemon to use a custom keepalive send interval?"
select arg_custom_ssh_keepalive_intvl in "Yes (let me specify)" "No (keep 1)"; do
echo 'Do you want the SSH daemon to use a custom keepalive send interval?'
select arg_custom_ssh_keepalive_intvl in 'Yes (let me specify)' 'No (keep 1)'; do
case "${arg_custom_ssh_keepalive_intvl}" in
'Yes (let me specify)')
want_custom_keepalive_intvl='true'
@ -213,7 +227,126 @@ function setup_env_vars () {
echo
fi
for env_var in 'ARCHZBM_ZFSPROPS_NO_COMPRESSION' 'ARCHZBM_ZFSPROPS_NO_ENCRYPTION' 'ARCHZBM_ZPOOL_PASSWORD' 'ARCHZBM_ROOT_PASSWORD' 'ARCHZBM_NET_AUTOCONF' 'ARCHZBM_NET_DEVICE' 'ARCHZBM_NET_CLIENT_IP' 'ARCHZBM_NET_NETMASK' 'ARCHZBM_NET_GATEWAY_IP' 'ARCHZBM_SSH_PORT' 'ARCHZBM_SSH_KEEPALIVE_INTVL' 'ARCHZBM_SSH_AUTH_KEYS'; do
if [[ "${want_ssh_in_zbm}" ]]; then
echo 'Do you want to define operating system'"'"'s IP address?'
select arg_os_ip in 'Yes (let me specify)' 'Yes (use ZBM addresses)' 'No (DHCP is fine)'; do
case "${arg_os_ip}" in
'Yes (let me specify)')
want_custom_ip_in_os='true'
want_dns_and_ntp='true'
break
;;
'Yes (use ZBM addresses)')
ARCHZBM_OS_CLIENT_IP="${ARCHZBM_NET_CLIENT_IP}"
ARCHZBM_NET_CLIENT_IP_PREFIX="$(calculate_prefix_from_netmask "${ARCHZBM_NET_NETMASK}")"
ARCHZBM_OS_CLIENT_IP+="${ARCHZBM_NET_CLIENT_IP_PREFIX}"
ARCHZBM_OS_GATEWAY_IP="${ARCHZBM_NET_GATEWAY_IP}"
want_dns_and_ntp='true'
break
;;
'No (DHCP is fine)')
break
;;
esac
done <&3 && echo
if [[ "${want_custom_ip_in_os}" ]]; then
read -u3 -p 'Interface IP address with CIDR prefix (a.b.c.d/nn): ' ARCHZBM_OS_CLIENT_IP
echo
read -u3 -p 'Gateway IP address: ' ARCHZBM_OS_GATEWAY_IP
echo
fi
else
echo 'Do you want to define operating system'"'"'s IP address?'
select arg_os_ip in 'Yes (let me specify)' 'No (DHCP is fine)'; do
case "${arg_os_ip}" in
'Yes (let me specify)')
want_own_ip_in_os='true'
want_dns_and_ntp='true'
break
;;
'No (DHCP is fine)')
break
;;
esac
done <&3 && echo
if [[ "${want_own_ip_in_os}" ]]; then
read -u3 -p 'Interface IP address with CIDR prefix (a.b.c.d/nn): ' ARCHZBM_OS_CLIENT_IP
echo
read -u3 -p 'Gateway IP address: ' ARCHZBM_OS_GATEWAY_IP
echo
fi
fi
if [[ "${want_ssh_in_zbm}" ]]; then
echo 'Do you want to define OS '"'"'root'"'"' user'"'"'s SSH pub key?'
select arg_root_pub_keys in 'Yes (let me specify)' 'Yes (use ZBM pub keys)' 'No (don'"'"'t enable sshd.service)'; do
case "${arg_root_pub_keys}" in
'Yes (let me specify)')
want_custom_pub_keys_in_os='true'
break
;;
'Yes (use ZBM pub keys)')
ARCHZBM_OS_SSH_AUTH_KEYS="${ARCHZBM_SSH_AUTH_KEYS}"
break
;;
'No (don'"'"'t enable sshd.service)')
break
;;
esac
done <&3 && echo
if [[ "${want_custom_pub_keys_in_os}" ]]; then
read -u3 -p 'Please type SSH pub keys on one line separated by double-commas (,,) and confirm with <Enter>: ' ARCHZBM_OS_SSH_AUTH_KEYS
echo
fi
else
echo 'Do you want to define OS root user'"'"'s SSH pub key?'
select arg_root_pub_keys in 'Yes (let me specify)' 'No (don'"'"'t enable sshd.service)'; do
case "${arg_root_pub_keys}" in
'Yes (let me specify)')
want_own_pub_key_in_os='true'
break
;;
'No (don'"'"'t enable sshd.service)')
break
;;
esac
done <&3 && echo
if [[ "${want_own_pub_key_in_os}" ]]; then
read -u3 -p 'Please type SSH pub keys on one line separated by double-commas (,,) and confirm with <Enter>: ' ARCHZBM_OS_SSH_AUTH_KEYS
echo
fi
fi
if [[ "${want_dns_and_ntp}" ]]; then
read -u3 -p 'Specify one or more comma-separated DNS IPs: ' ARCHZBM_OS_DNS_IP
echo
echo 'Do you want to override Arch Linux'"'"' NTP servers?'
select arg_custom_ntp in 'Yes' 'No'; do
case "${arg_custom_ntp}" in
'Yes')
want_own_ntp='true'
break
;;
'No')
break
;;
esac
done <&3 && echo
if [[ "${want_own_ntp}" ]]; then
read -u3 -p 'Specify one or more comma-separated NTP hostnames or IPs: ' ARCHZBM_OS_NTP_IP
echo
fi
fi
for env_var in 'ARCHZBM_KERNEL_VER' 'ARCHZBM_ZFSPROPS_NO_COMPRESSION' 'ARCHZBM_ZFSPROPS_NO_ENCRYPTION' 'ARCHZBM_ZPOOL_PASSWORD' 'ARCHZBM_ROOT_PASSWORD' 'ARCHZBM_NET_AUTOCONF' 'ARCHZBM_NET_DEVICE' 'ARCHZBM_NET_CLIENT_IP' 'ARCHZBM_NET_NETMASK' 'ARCHZBM_NET_GATEWAY_IP' 'ARCHZBM_SSH_PORT' 'ARCHZBM_SSH_KEEPALIVE_INTVL' 'ARCHZBM_SSH_AUTH_KEYS' 'ARCHZBM_OS_CLIENT_IP' 'ARCHZBM_OS_GATEWAY_IP' 'ARCHZBM_OS_SSH_AUTH_KEYS' 'ARCHZBM_OS_DNS_IP' 'ARCHZBM_OS_NTP_IP'; do
if [[ "${!env_var}" ]]; then
printf -- '%s='"'"'%s'"'"'\n' \
"${env_var}" "${!env_var}" \
@ -224,7 +357,7 @@ function setup_env_vars () {
printf -- '%s\n' \
'Done, please rerun script now with just' \
'... | bash' \
'so without the '"'"'setup'"'"' argument's
'so without the '"'"'setup'"'"' argument'
exit 77
}
@ -959,7 +1092,6 @@ EFI:
ImageDir: /efi/EFI/ZBM
Versions: false
Enabled: true
Stub: /usr/share/zfsbootmenu/stubs/linuxx64.efi.stub/linuxx64.efi.stub # workaround: https://github.com/zbm-dev/zfsbootmenu/discussions/501
Kernel:
CommandLine: ro loglevel=0 zbm.import_policy=hostid
Prefix: vmlinuz
@ -1173,6 +1305,13 @@ function install_os_in_chroot () {
unleash_makepkg #2.5
add_motd_getting_started_msg #2.6
get_aur_helper #2.7
if [[ "${ARCHZBM_KERNEL_VER}" ]]; then
paru_install 'downgrade'
yes | downgrade --ala-only \
'linux='"${ARCHZBM_KERNEL_VER}" \
'linux-headers='"${ARCHZBM_KERNEL_VER}" \
--ignore always
fi
paru_install 'zfs-dkms' 'zfs-utils' 'jq'
hwclock --systohc
mkinitcpio -P
@ -1207,11 +1346,54 @@ function set_root_pw () {
function configure_networking () {
#3.3
local -a dns_addresses ntp_addresses
# Begin network unit file with a default top section
cat > '/mnt/etc/systemd/network/50-wired.network' <<"EOF"
[Match]
Name=en*
[Network]
EOF
# Decide on what comes next in network unit file
if [[ "${ARCHZBM_OS_CLIENT_IP}" ]] || \
[[ "${ARCHZBM_OS_GATEWAY_IP}" ]] || \
[[ "${ARCHZBM_OS_DNS_IP}" ]] || \
[[ "${ARCHZBM_OS_NTP_IP}" ]]; then
cat >> '/mnt/etc/systemd/network/50-wired.network' <<EOF
Address=${ARCHZBM_OS_CLIENT_IP}
Gateway=${ARCHZBM_OS_GATEWAY_IP}
EOF
if [[ "${ARCHZBM_OS_DNS_IP}" ]]; then
mapfile -t dns_addresses < <(<<<"${ARCHZBM_OS_DNS_IP}" tr ',' '\n' | sed '/^$/d')
else
dns_addresses+=('8.8.8.8')
dns_addresses+=('8.8.4.4')
fi
for dns_addr in "${dns_addresses[@]}"; do
cat >> '/mnt/etc/systemd/network/50-wired.network' <<EOF
DNS=${dns_addr}
EOF
done
if [[ "${ARCHZBM_OS_NTP_IP}" ]]; then
mapfile -t ntp_addresses < <(<<<"${ARCHZBM_OS_NTP_IP}" tr ',' '\n' | sed '/^$/d')
for ntp_addr in "${ntp_addresses[@]}"; do
cat >> '/mnt/etc/systemd/network/50-wired.network' <<EOF
NTP=${ntp_addr}
EOF
done
fi
cat >> '/mnt/etc/systemd/network/50-wired.network' <<"EOF"
IPForward=yes
Domains=~.
EOF
else
cat >> '/mnt/etc/systemd/network/50-wired.network' <<"EOF"
DHCP=ipv4
IPForward=yes
@ -1219,33 +1401,56 @@ IPForward=yes
UseDNS=yes
RouteMetric=10
EOF
fi
systemctl enable 'systemd-networkd' --root='/mnt'
systemctl disable 'systemd-networkd-wait-online' --root='/mnt'
}
function configure_dns () {
function configure_sshd () {
#3.4
local pub_key_line
cat >> '/mnt/etc/ssh/sshd_config.d/40-defaults.conf' <<"EOF"
PasswordAuthentication no
PermitRootLogin yes
EOF
while IFS= read -r pub_key_line; do
printf -- '%s\n' "${pub_key_line}" >> '/mnt/root/.ssh/authorized_keys'
done < <(<<<"${ARCHZBM_OS_SSH_AUTH_KEYS}" sed -r -e 's/,,/\n/g')
systemctl enable 'sshd.service' --root='/mnt'
}
function configure_dns () {
#3.5
rm '/mnt/etc/resolv.conf'
ln -s '/run/systemd/resolve/stub-resolv.conf' '/mnt/etc/resolv.conf'
# Optionally you may want /etc/systemd/network/50-wired.network to use
# UseDNS=no and hardcode DNS server(s) here:
# sed -i 's/^#DNS=.*/DNS=1.1.1.1/' /mnt/etc/systemd/resolved.conf
systemctl enable 'systemd-resolved' --root='/mnt'
systemctl enable 'systemd-resolved.service' --root='/mnt'
}
function configure_ntp () {
#3.6
systemctl enable 'systemd-timesyncd.service' --root='/mnt'
}
function configure_reflector () {
#3.5
#3.7
systemctl enable 'reflector.service' 'reflector.timer' --root='/mnt'
}
function configure_zfs () {
#3.6
systemctl enable 'zfs-import-cache' 'zfs-mount' 'zfs-import.target' 'zfs.target' --root='/mnt'
#3.8
systemctl enable 'zfs-import-cache.service' 'zfs-mount.service' 'zfs-import.target' 'zfs.target' --root='/mnt'
}
function configure_zfs_mount_gen () {
#3.7
#3.9
mkdir -p '/mnt/etc/zfs/zfs-list.cache'
touch '/mnt/etc/zfs/zfs-list.cache/'"${zpool_name}"
zfs list -H -o name,mountpoint,canmount,atime,relatime,devices,exec,readonly,setuid,nbmand | sed 's/\/mnt//' > '/mnt/etc/zfs/zfs-list.cache/'"${zpool_name}"
@ -1253,7 +1458,7 @@ function configure_zfs_mount_gen () {
}
function set_new_uefi_boot_entries () {
#3.8
#3.10
declare -a uefi_images
mapfile -t uefi_images < \
<(find '/mnt/efi/EFI/ZBM' -type f -iname '*.efi' -print0 | \
@ -1296,7 +1501,7 @@ function set_new_uefi_boot_entries () {
}
function umount_all () {
#3.9
#3.11
if [[ "${part_schema}" = 'mbr' ]]; then
umount '/mnt/boot/syslinux'
else
@ -1310,14 +1515,18 @@ function finalize_os_setup () {
#3.1
set_root_pw #3.2
configure_networking #3.3
configure_dns #3.4
configure_reflector #3.5
configure_zfs #3.6
configure_zfs_mount_gen #3.7
if [[ "${part_schema}" = 'gpt' ]]; then
set_new_uefi_boot_entries #3.8
if [[ "${ARCHZBM_OS_SSH_AUTH_KEYS}" ]]; then
configure_sshd #3.4
fi
umount_all #3.9
configure_dns #3.5
configure_ntp #3.6
configure_reflector #3.7
configure_zfs #3.8
configure_zfs_mount_gen #3.9
if [[ "${part_schema}" = 'gpt' ]]; then
set_new_uefi_boot_entries #3.10
fi
umount_all #3.11
}
function main () {