Compare commits
12 Commits
7d5075a543
...
5cf4b2c325
Author | SHA1 | Date | |
---|---|---|---|
5cf4b2c325 | |||
a2cb784cb3 | |||
119be2b876 | |||
9560677352 | |||
52c09fc93f | |||
1181432add | |||
e3025883fa | |||
79feaed5ac | |||
7338924c82 | |||
924925e08e | |||
3030eb0f2d | |||
305e4191f1 |
94
README.md
94
README.md
@@ -99,6 +99,14 @@ The script will create a single ZFS zpool `zpool` on the zpool partition with da
|
||||
|
||||
## Options
|
||||
|
||||
The following options can be given either by exporting them as shell variables prior to script execution or in a file named `archzbm_settings.env` that lives in your current working directory where you're about to execute the script. File format is identical to shell variable assignments of the form `VAR=value` or `VAR='value'`.
|
||||
|
||||
If `./archzbm_settings.env` exists the script will `source` its content and `export` all variables for use in future steps.
|
||||
|
||||
In cases where a variable is both exported prior to script execution and specified in `archzbm_settings.env` the latter will override the former.
|
||||
|
||||
Known options are as follows.
|
||||
|
||||
### 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>`.
|
||||
@@ -119,24 +127,95 @@ export ARCHZBM_ZFSPROPS_NO_ENCRYPTION=yup
|
||||
|
||||
### Passwords
|
||||
|
||||
By default both the zpool password and the account password for `root` are literally `password`. While you can certainly change these after initial system setup you can also optionally set these passwords in a settings file named `archzbm_settings.env` that lives in your current working directory where you're about to execute the script. File format is identical to shell variable assignments of the form `VAR=value` or `VAR='value'`.
|
||||
|
||||
If `./archzbm_settings.env` exists the script will `source` its content and `export` all variables for use in future steps. Only known variables are:
|
||||
By default both the zpool password and the account password for `root` are literally `password`. While you can certainly change these after initial system setup (see [Password change](#password-change)) you can also optionally set passwords as follows:
|
||||
|
||||
```
|
||||
ARCHZBM_ZPOOL_PASSWORD='a fancy password'
|
||||
ARCHZBM_ROOT_PASSWORD='t0psecr3t!'
|
||||
```
|
||||
|
||||
> While the `root` password may be weak and `chpasswd` won't care do make sure to set a zpool password that meets ZFS' complexity rules. Per `man 7 zfsprops` section `keyformat` the only requirement is a length "between 8 and 512 bytes" (as in minimum 8 characters). If you pick a password that's too weak ZFS will reject zpool creation and very ungracefully derail the rest of this script. The script doesn't check what you're setting.
|
||||
> While the `root` password is allowed to be weak and `chpasswd` won't care do make sure to set a zpool password that meets ZFS' complexity rules. Per `man 7 zfsprops` section `keyformat` the only requirement is a length "between 8 and 512 bytes" (as in minimum 8 characters). If you pick a password that's too weak ZFS will reject zpool creation and very ungracefully derail the rest of this script. The script doesn't check what you're setting.
|
||||
|
||||
The script does create a second user named `build` but doesn't set a password on account creation. As such no password variable can be set for it in `./archzbm_settings.env`. It's intended as a helper for system setup tasks such as `sudo -u build paru -S <package>` where an account password is irrelevant since `root` can always `sudo` whatever it wants. You will not be able to log in to the `build` account yourself although you certainly could set a password for it. Instead we suggest you create a proper user account for yourself. Your newly installed Arch Linux comes with an `/etc/motd` greeting that summarizes this as:
|
||||
The script does create a second user named `build` but doesn't set a password on account creation. It's intended as a helper for system setup tasks such as `sudo -u build paru -S <package>` where an account password is irrelevant since `root` can always `sudo` whatever it wants. You will not be able to log in to the `build` account yourself although you certainly could set a password for it. Instead we suggest you create a proper user account for yourself. Your newly installed Arch Linux comes with an `/etc/motd` greeting that summarizes this as:
|
||||
|
||||
```
|
||||
useradd --create-home --shell /bin/bash --user-group --groups wheel <user>
|
||||
passwd <user>
|
||||
```
|
||||
|
||||
### Networking
|
||||
|
||||
By default the script configures plain ZFSBootMenu without networking nor an SSH server. If you're interested in SSH-ing into your ZFSBootMenu boot loader you're going to want to specify some of the following variables.
|
||||
|
||||
#### IP address
|
||||
|
||||
```
|
||||
ARCHZBM_NET_CLIENT_IP=''
|
||||
ARCHZBM_NET_SERVER_IP=''
|
||||
ARCHZBM_NET_GATEWAY_IP=''
|
||||
ARCHZBM_NET_NETMASK=''
|
||||
ARCHZBM_NET_HOSTNAME=''
|
||||
ARCHZBM_NET_DEVICE=''
|
||||
ARCHZBM_NET_AUTOCONF=''
|
||||
```
|
||||
|
||||
By default none of the variables are set to any value and no networking will be available in ZFSBootMenu. If you want networking as in an IP address bound to a network interace set at least one of these variables or one of the [SSH](#ssh) variables listed further down. Setting one or more `ARCHZBM_NET_*` variables to an empty string is valid. If at least one variable is given either from this paragraph or from [SSH](#ssh) we're assuming that you want networking. Unspecified values and values set to the empty string `''` use defaults.
|
||||
|
||||
For networking we rely on the [mkinitcpio-nfs-utils](https://archlinux.org/packages/core/x86_64/mkinitcpio-nfs-utils/) package with its `net` hook. Please refer to its [initcpio-install-net](https://gitlab.archlinux.org/archlinux/packaging/packages/mkinitcpio-nfs-utils/-/blob/main/initcpio-install-net) script file for usage hints on above variables. The hook implements a subset of the [ip Kernel Command Line argument](https://docs.kernel.org/admin-guide/nfs/nfsroot.html).
|
||||
|
||||
Mapping between `net` hook field names and our shell variables is straightforward. Fields 8, 9 and 10 (DNS and NTP server addresses) from the official `ip` docs are unsupported in `net` hook. As such our hook has a total of 7 fields available for you to configure.
|
||||
|
||||
```
|
||||
+-------------+------------------------+
|
||||
| net hook | This script |
|
||||
+-------------+------------------------+
|
||||
| <client-ip> | ARCHZBM_NET_CLIENT_IP |
|
||||
| <server-ip> | ARCHZBM_NET_SERVER_IP |
|
||||
| <gw-ip> | ARCHZBM_NET_GATEWAY_IP |
|
||||
| <netmask> | ARCHZBM_NET_NETMASK |
|
||||
| <hostname> | ARCHZBM_NET_HOSTNAME |
|
||||
| <device> | ARCHZBM_NET_DEVICE |
|
||||
| <autoconf> | ARCHZBM_NET_AUTOCONF |
|
||||
+-------------+------------------------+
|
||||
```
|
||||
|
||||
A valid example with a few fields populated may look like so:
|
||||
|
||||
```
|
||||
ARCHZBM_NET_CLIENT_IP='10.10.10.2'
|
||||
ARCHZBM_NET_GATEWAY_IP='10.10.10.1'
|
||||
ARCHZBM_NET_NETMASK='255.255.255.0'
|
||||
ARCHZBM_NET_DEVICE='eth0'
|
||||
ARCHZBM_NET_AUTOCONF='none'
|
||||
```
|
||||
|
||||
Note that in this example `ARCHZBM_NET_SERVER_IP` and `ARCHZBM_NET_HOSTNAME` are left unassigned.
|
||||
|
||||
This add the following `ip=` instruction to your Kernel Command Line:
|
||||
```
|
||||
ip=10.10.10.2::10.10.10.1:255.255.255.0::eth0:none
|
||||
```
|
||||
|
||||
#### SSH
|
||||
|
||||
If you want networking indicated by the fact that at least one of the `ARCHZBM_NET_*` variables is set or one of the `ARCHZBM_SSH_*` vars we assume that you want an SSH daemon as well. This comes in the form of a `dropbear` daemon with minimal configurability. Use the following variables to define Dropbear's behavior.
|
||||
|
||||
```
|
||||
ARCHZBM_SSH_PORT='22'
|
||||
ARCHZBM_SSH_KEEPALIVE_INTVL='1'
|
||||
ARCHZBM_SSH_AUTH_KEYS=''
|
||||
```
|
||||
|
||||
In `ARCHZBM_SSH_PORT` you specify Dropbear's listening port, this defaults to `22` if unconfigured or set to an empty string. With `ARCHZBM_SSH_KEEPALIVE_INTVL` you define at which interval Dropbear will send keepalive messages to an SSH client through the SSH connection. This defaults to `1` as in every `1` second a keepalive message is sent.
|
||||
|
||||
Dropbear in this setup only supports key-based authentication, no password-based authentication. The value from `ARCHZBM_SSH_AUTH_KEYS` will be converted to a list of public SSH keys allowed to SSH into Dropbear as its default `root` user while ZFSBootMenu is running. The format of `ARCHZBM_SSH_AUTH_KEYS` is a single line where `authorized_keys` entries are split with double-commas:
|
||||
|
||||
```
|
||||
ssh-rsa Eahajei8,,ssh-ed25519 kaeD0mas ...
|
||||
```
|
||||
|
||||
This syntax crutch allows you to use the full range of Dropbear-supported `authorized_keys` stanzas, see [man 8 dropbear](https://man.archlinux.org/man/extra/dropbear/dropbear.8.en) for what's available. Whether or not this is useful to you is another topic :) At least the functionality for stanzas is there by separating values in `ARCHZBM_SSH_AUTH_KEYS` with double-commas.
|
||||
|
||||
# Steps
|
||||
|
||||
The script takes the following installation steps.
|
||||
@@ -498,7 +577,12 @@ arch-chroot /mnt /bin/bash
|
||||
When done exit `chroot` and cleanly remove your work:
|
||||
|
||||
```
|
||||
# UEFI system ...
|
||||
umount /mnt/efi
|
||||
|
||||
# ... or legacy BIOS system
|
||||
umount /mnt/boot/syslinux
|
||||
|
||||
zfs umount -a
|
||||
zpool export zpool
|
||||
```
|
||||
|
156
setup.sh
156
setup.sh
@@ -468,6 +468,29 @@ function in_file_in_array_insert_n_before_m () {
|
||||
arg_array="${2:?}"
|
||||
arg_string="${3:?}"
|
||||
arg_precede="${4:?}"
|
||||
|
||||
# Look for a line that contains in this order
|
||||
# - String "${arg_array}" and a equals sign (=), assign capture group \1
|
||||
# - Followed by as few characters as possible followed by either an
|
||||
# opening parenthesis or a space character, assign capture group \2
|
||||
# - String "${arg_precede}", capture as capture group \3
|
||||
# - Followed by either a closing parenthesis or a space which are then
|
||||
# followed by as many non-line break characters as possible, capture
|
||||
# as capture group \2
|
||||
#
|
||||
# For following example text we're assuming that:
|
||||
# - "${arg_array}" equals 'HOOKS'
|
||||
# - "${arg_precede}" equals 'filesystems'
|
||||
# - "${arg_string}" equals 'zfs'
|
||||
#
|
||||
# This finds a 'HOOKS=' array definition that contains the string
|
||||
# 'filesystems' either at the beginning of the 'HOOKS=(...)' opening
|
||||
# parenthesis, at the very end or somewhere in the middle where it may
|
||||
# be preceded or followed by one or more space characters. It saves
|
||||
# 'HOOKS=', it saves whatever precedes 'filesystems' and 'filesystems'
|
||||
# itself plus whatever comes after 'filesystems' until end of line. It
|
||||
# lastly inserts 'zfs' and a space character right in front of
|
||||
# 'filesystems'.
|
||||
sed -ri \
|
||||
-e 's'$'\x1''('"${arg_array}"'=)(.*?[( ])('"${arg_precede}"')([) ][^\r\n\f]*)'$'\x1''\1\2'"${arg_string}"' \3\4'$'\x1''g' \
|
||||
"${arg_file}"
|
||||
@@ -478,6 +501,21 @@ function in_file_in_array_insert_n_at_the_end () {
|
||||
arg_file="${1:?}"
|
||||
arg_array="${2:?}"
|
||||
arg_string="${3:?}"
|
||||
|
||||
# Look for end of array, insert "${arg_string}" right before
|
||||
#
|
||||
# For following example text we're assuming that:
|
||||
# - "${arg_array}" equals 'HOOKS'
|
||||
# - "${arg_string}" equals 'net'
|
||||
#
|
||||
# This:
|
||||
# - Finds a 'HOOKS=' array definition, saves it as \1
|
||||
# - Finds as many non-closing parenthesis characters as possible, so all
|
||||
# characters until just before the final ')' character of the line and
|
||||
# saves this as \2
|
||||
# - Finds the closing parenthesis character plus all non-line break
|
||||
# characters until end of line that come after it and saves this as \3
|
||||
# - Adds a space character and 'net' after \2
|
||||
sed -ri \
|
||||
-e 's'$'\x1''('"${arg_array}"'=)([^)]*)(\)[^\r\n\f]*)'$'\x1''\1\2 '"${arg_string}"'\3'$'\x1''g' \
|
||||
"${arg_file}"
|
||||
@@ -488,6 +526,23 @@ function in_file_in_array_remove_n () {
|
||||
arg_file="${1:?}"
|
||||
arg_array="${2:?}"
|
||||
arg_string="${3:?}"
|
||||
|
||||
# Look for any line that contains "${arg_string}", delete that string
|
||||
#
|
||||
# For following example text we're assuming that:
|
||||
# - "${arg_array}" equals 'HOOKS'
|
||||
# - "${arg_string}" equals 'fsck'
|
||||
#
|
||||
# First -e expression removes 'fsck' wherever it is defined as the one
|
||||
# and only element of any HOOKS=(fsck) should such a line exist.
|
||||
#
|
||||
# Second -e expression finds string 'fsck' where it's preceded by space
|
||||
# character(s) and followed by either space character(s) or a closing
|
||||
# parenthesis.
|
||||
#
|
||||
# Third -e expression finds string 'fsck' where it's preceded by space
|
||||
# character(s) or an opening parenthesis and followed space
|
||||
# character(s).
|
||||
sed -ri \
|
||||
-e 's'$'\x1''((\()('"${arg_string}"')(\)))'$'\x1''\2\4'$'\x1''g' \
|
||||
-e 's'$'\x1''('"${arg_array}"'=.*?)([[:space:]]+'"${arg_string}"')([[:space:]]+|\))'$'\x1''\1\3'$'\x1''g' \
|
||||
@@ -741,7 +796,11 @@ EOF
|
||||
fi
|
||||
|
||||
# Up here maybe 'ro quiet' instead of 'ro'. This is ZFSBootMenu's kernel
|
||||
# command line.
|
||||
# command line. In MBR/Syslinux/extlinux mode /it/ must pass arguments to
|
||||
# ZFSBootMenu's kernel so check /boot/syslinux/syslinux.cfg for how we start
|
||||
# ZFSBootMenu in this mode. The .Kernel.CommandLine in
|
||||
# /etc/zfsbootmenu/config.yaml is irrelevant for us in MBR/Syslinux/extlinux
|
||||
# mode.
|
||||
|
||||
# Assign cmdline for final kernel start. This is our Arch Linx kernel
|
||||
# command line.
|
||||
@@ -751,6 +810,8 @@ EOF
|
||||
function get_dropbear_hooks () {
|
||||
mkdir -p '/opt/git/quico.space/quico-os-setup/mkinitcpio-dropbear-pacman-hook/branches/main'
|
||||
git -C '/opt/git/quico.space/quico-os-setup/mkinitcpio-dropbear-pacman-hook/branches/main' clone 'https://quico.space/quico-os-setup/mkinitcpio-dropbear-pacman-hook.git' .
|
||||
ln -s '/opt/git/quico.space/quico-os-setup/mkinitcpio-dropbear-pacman-hook/branches/main/pacman-mkinitcpio-dropbear-hook.sh' '/usr/local/bin/pacman-mkinitcpio-dropbear-hook'
|
||||
ln -s '/opt/git/quico.space/quico-os-setup/mkinitcpio-dropbear-pacman-hook/branches/main/pacman-mkinitcpio-dropbear-install.sh' '/usr/local/bin/pacman-mkinitcpio-dropbear-install'
|
||||
ln -s '/opt/git/quico.space/quico-os-setup/mkinitcpio-dropbear-pacman-hook/branches/main/pacman-mkinitcpio-dropbear-hook.hook' '/usr/share/libalpm/hooks/pacman-mkinitcpio-dropbear-hook.hook'
|
||||
ln -s '/opt/git/quico.space/quico-os-setup/mkinitcpio-dropbear-pacman-hook/branches/main/pacman-mkinitcpio-dropbear-install.hook' '/usr/share/libalpm/hooks/pacman-mkinitcpio-dropbear-install.hook'
|
||||
}
|
||||
@@ -771,43 +832,70 @@ function customize_dropbear_hooks () {
|
||||
fi
|
||||
}
|
||||
|
||||
function set_unique_ip_in_syslinux_kcl () {
|
||||
local zbm_config default_ip
|
||||
zbm_config="${1:?}"
|
||||
default_ip="${2:?}"
|
||||
|
||||
# First -e expression removes first looks for lines that contain
|
||||
# 'APPEND' plus a space character and only in those lines removes all
|
||||
# occurrences of ' ip=' followed by as many non-space characters as
|
||||
# possible. This removes whatever 'ip=' definition already was present.
|
||||
#
|
||||
# Second -e expression similarly looks for lines that contain 'APPEND'
|
||||
# plus a space character then at their end inserts a space plus our
|
||||
# desired new 'ip=' definition. This puts in place the 'ip=' we want.
|
||||
sed -ri \
|
||||
-e \\$'\x1''APPEND '$'\x1 s'$'\x1'' ip=([^[:space:]]*)'$'\x1'''$'\x1''gi' \
|
||||
-e \\$'\x1''APPEND '$'\x1 s'$'\x1''$'$'\x1'' '"${default_ip}"''$'\x1''gi' \
|
||||
"${zbm_config}"
|
||||
}
|
||||
|
||||
function ensure_ip_in_kcl () {
|
||||
local zbm_config kcl_length kcl_string default_ip ip_addr_found new_kcl first_kcl_elem
|
||||
local -a kcl
|
||||
paru_install 'go-yq'
|
||||
|
||||
zbm_config='/etc/zfsbootmenu/config.yaml'
|
||||
kcl_length="$(yq '.Kernel.CommandLine | length' "${zbm_config}")"
|
||||
if [[ "${kcl_length}" -eq '0' ]]; then
|
||||
>&3 printf -- '%s\n' \
|
||||
'No .Kernel.CommandLine YAML element with content found in '"${zbm_config}"'. Exiting ...'
|
||||
exit 77
|
||||
else
|
||||
kcl_string="$(yq '.Kernel.CommandLine' "${zbm_config}")"
|
||||
fi
|
||||
|
||||
local default_ip
|
||||
default_ip='ip='"${ARCHZBM_NET_CLIENT_IP}"':'"${ARCHZBM_NET_SERVER_IP}"':'"${ARCHZBM_NET_GATEWAY_IP}"':'"${ARCHZBM_NET_NETMASK}"':'"${ARCHZBM_NET_HOSTNAME}"':'"${ARCHZBM_NET_DEVICE}"':'"${ARCHZBM_NET_AUTOCONF}"
|
||||
mapfile -t kcl < <(<<<"${kcl_string}" tr ' ' '\n' | sed '/^$/d')
|
||||
for kcl_elem in "${!kcl[@]}"; do
|
||||
if grep -Piq -- 'ip=' <<<"${kcl[$kcl_elem]}"; then
|
||||
ip_addr_found='true'
|
||||
kcl["${kcl_elem}"]="${default_ip}"
|
||||
fi
|
||||
done
|
||||
if [[ ! "${ip_addr_found}" ]]; then
|
||||
kcl+=("${default_ip}")
|
||||
fi
|
||||
new_kcl=''
|
||||
first_kcl_elem='true'
|
||||
for kcl_elem in "${kcl[@]}"; do
|
||||
if [[ ! "${first_kcl_elem}" ]]; then
|
||||
new_kcl+=' '"${kcl_elem}"
|
||||
|
||||
if [[ "${part_schema}" = 'gpt' ]]; then
|
||||
local zbm_config kcl_length kcl_string ip_addr_found new_kcl first_kcl_elem
|
||||
local -a kcl
|
||||
paru_install 'go-yq'
|
||||
|
||||
zbm_config='/etc/zfsbootmenu/config.yaml'
|
||||
kcl_length="$(yq '.Kernel.CommandLine | length' "${zbm_config}")"
|
||||
if [[ "${kcl_length}" -eq '0' ]]; then
|
||||
>&3 printf -- '%s\n' \
|
||||
'No .Kernel.CommandLine YAML element with content found in '"${zbm_config}"'. Exiting ...'
|
||||
exit 77
|
||||
else
|
||||
new_kcl+="${kcl_elem}"
|
||||
unset -v first_kcl_elem
|
||||
kcl_string="$(yq '.Kernel.CommandLine' "${zbm_config}")"
|
||||
fi
|
||||
done
|
||||
yq -i '.Kernel.CommandLine = "'"${new_kcl}"'"' "${zbm_config}"
|
||||
|
||||
mapfile -t kcl < <(<<<"${kcl_string}" tr ' ' '\n' | sed '/^$/d')
|
||||
for kcl_elem in "${!kcl[@]}"; do
|
||||
if grep -Piq -- 'ip=' <<<"${kcl[$kcl_elem]}"; then
|
||||
ip_addr_found='true'
|
||||
kcl["${kcl_elem}"]="${default_ip}"
|
||||
fi
|
||||
done
|
||||
if [[ ! "${ip_addr_found}" ]]; then
|
||||
kcl+=("${default_ip}")
|
||||
fi
|
||||
new_kcl=''
|
||||
first_kcl_elem='true'
|
||||
for kcl_elem in "${kcl[@]}"; do
|
||||
if [[ ! "${first_kcl_elem}" ]]; then
|
||||
new_kcl+=' '"${kcl_elem}"
|
||||
else
|
||||
new_kcl+="${kcl_elem}"
|
||||
unset -v first_kcl_elem
|
||||
fi
|
||||
done
|
||||
yq -i '.Kernel.CommandLine = "'"${new_kcl}"'"' "${zbm_config}"
|
||||
else
|
||||
local zbm_config
|
||||
zbm_config='/boot/syslinux/syslinux.cfg'
|
||||
set_unique_ip_in_syslinux_kcl "${zbm_config}" "${default_ip}"
|
||||
fi
|
||||
}
|
||||
|
||||
function set_pub_keys () {
|
||||
|
Reference in New Issue
Block a user