220 lines
8.0 KiB
Bash
220 lines
8.0 KiB
Bash
#!/usr/bin/env bash
|
|
|
|
# === Motivation ===
|
|
# Ensure the system can boot with encrypted root after installation.
|
|
|
|
# === Problem Statement ===
|
|
# Post-install steps must align initramfs, bootloader, and encryption metadata.
|
|
|
|
# === Scope ===
|
|
# In scope: required configuration updates inside the installed system.
|
|
# Out of scope: package selection and user account management.
|
|
|
|
# === Concepts ===
|
|
# Initramfs: early boot image that unlocks encrypted storage.
|
|
# Bootloader config: entries that point to the encrypted root.
|
|
# Dracut modules: crypt (LUKS unlock), resume (hibernation support if swap file used).
|
|
# Crypttab: /etc/crypttab maps LUKS UUIDs to device names for initramfs.
|
|
# GRUB cryptodisk: GRUB_ENABLE_CRYPTODISK=y in /etc/default/grub enables LUKS unlock.
|
|
# Kernel parameters: rd.luks.uuid=<UUID> tells dracut which LUKS container to unlock.
|
|
|
|
# === Decisions ===
|
|
# Keep post-install steps explicit and minimal, focused on boot viability.
|
|
# Default to GRUB because it is widely supported on Void without systemd dependencies.
|
|
# Regenerate initramfs for all installed kernels to avoid boot drift.
|
|
# Use UUID-based references in /etc/crypttab for stability across device naming changes.
|
|
# Enable dracut crypt module explicitly in /etc/dracut.conf.d/10-crypt.conf.
|
|
# Set GRUB_ENABLE_CRYPTODISK=y in /etc/default/grub to allow GRUB to unlock LUKS.
|
|
# Add rd.luks.uuid=<UUID> to GRUB_CMDLINE_LINUX for initramfs LUKS unlock.
|
|
# Run grub-install to embed cryptodisk support, then grub-mkconfig to update menu entries.
|
|
|
|
# === Alternatives Considered ===
|
|
# Skipping post-install updates rejected because it risks unbootable systems.
|
|
# systemd-boot and rEFInd rejected as defaults due to availability and scope constraints.
|
|
# EFISTUB rejected as default because it increases manual UEFI entry management.
|
|
|
|
# === Constraints ===
|
|
# Steps must run in the target system context.
|
|
|
|
# === Open Questions ===
|
|
# Should we generate a rescue initramfs in addition to the default one?
|
|
# Should we verify GRUB can unlock LUKS2 before rebooting, or trust the configuration?
|
|
# How do we handle future kernel updates - should we document the dracut reconfiguration process?
|
|
# Should swap file activation be configured in this phase, or deferred to first boot?
|
|
|
|
# === Success Criteria ===
|
|
# After reboot, the system prompts for decryption and boots successfully.
|
|
|
|
postinstall_bind_mounts() {
|
|
mkdir -p "$MOUNT_ROOT/dev" "$MOUNT_ROOT/proc" "$MOUNT_ROOT/sys" "$MOUNT_ROOT/run"
|
|
mount --rbind /dev "$MOUNT_ROOT/dev"
|
|
mount --make-rslave "$MOUNT_ROOT/dev"
|
|
mount -t proc /proc "$MOUNT_ROOT/proc"
|
|
mount --rbind /sys "$MOUNT_ROOT/sys"
|
|
mount --make-rslave "$MOUNT_ROOT/sys"
|
|
mount --rbind /run "$MOUNT_ROOT/run"
|
|
mount --make-rslave "$MOUNT_ROOT/run"
|
|
}
|
|
|
|
postinstall_unbind_mounts() {
|
|
umount -R "$MOUNT_ROOT/dev" 2>/dev/null || true
|
|
umount -R "$MOUNT_ROOT/proc" 2>/dev/null || true
|
|
umount -R "$MOUNT_ROOT/sys" 2>/dev/null || true
|
|
umount -R "$MOUNT_ROOT/run" 2>/dev/null || true
|
|
}
|
|
|
|
postinstall_run() {
|
|
: "${ROOT_PART:?Root partition is required}"
|
|
: "${ESP_PART:?ESP partition is required}"
|
|
: "${CRYPT_NAME:?Crypt mapping name is required}"
|
|
: "${MOUNT_ROOT:?Mount root is required}"
|
|
: "${FS_TYPE:?Filesystem type is required}"
|
|
: "${SWAP_SIZE:?Swap size is required}"
|
|
: "${HOSTNAME:?Hostname is required}"
|
|
local luks_uuid
|
|
local root_uuid
|
|
local esp_uuid
|
|
|
|
luks_uuid="$(cryptsetup luksUUID "$ROOT_PART")"
|
|
root_uuid="$(blkid -s UUID -o value "/dev/mapper/${CRYPT_NAME}")"
|
|
esp_uuid="$(blkid -s UUID -o value "$ESP_PART")"
|
|
|
|
log_info "Post-install: configuring target system in chroot"
|
|
postinstall_bind_mounts
|
|
trap postinstall_unbind_mounts EXIT
|
|
|
|
LUKS_UUID="$luks_uuid" ROOT_UUID="$root_uuid" ESP_UUID="$esp_uuid" FS_TYPE="$FS_TYPE" SWAP_SIZE="$SWAP_SIZE" HOSTNAME="$HOSTNAME" \
|
|
chroot "$MOUNT_ROOT" /bin/bash -s <<'EOS'
|
|
set -euo pipefail
|
|
|
|
# Ensure required tools are available inside the target system.
|
|
for cmd in grub-install grub-mkconfig xbps-reconfigure fallocate mkswap; do
|
|
if ! command -v "$cmd" >/dev/null 2>&1; then
|
|
echo "Missing required command inside chroot: $cmd" >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
# Write the hostname explicitly to match wrapper configuration.
|
|
printf '%s\n' "$HOSTNAME" > /etc/hostname
|
|
|
|
# Ensure crypttab exists with the correct UUID mapping.
|
|
mkdir -p /etc
|
|
printf 'cryptroot UUID=%s none luks\n' "$LUKS_UUID" > /etc/crypttab
|
|
|
|
# Ensure dracut loads crypt and optional resume modules.
|
|
mkdir -p /etc/dracut.conf.d
|
|
if [[ "$SWAP_SIZE" != "0" ]]; then
|
|
printf 'add_dracutmodules+=" crypt resume "\n' > /etc/dracut.conf.d/10-crypt.conf
|
|
else
|
|
printf 'add_dracutmodules+=" crypt "\n' > /etc/dracut.conf.d/10-crypt.conf
|
|
fi
|
|
|
|
update_fstab_entry() {
|
|
local mount_point="$1"
|
|
local fs_type="$2"
|
|
local options="$3"
|
|
local uuid="$4"
|
|
if [[ -z "$uuid" ]]; then
|
|
echo "Missing UUID for $mount_point" >&2
|
|
exit 1
|
|
fi
|
|
local line="UUID=$uuid $mount_point $fs_type $options 0 0"
|
|
|
|
if awk -v mp="$mount_point" '$2==mp {found=1} END {exit found?0:1}' /etc/fstab; then
|
|
awk -v mp="$mount_point" -v line="$line" 'BEGIN{OFS=" "} $2==mp {$0=line} {print}' /etc/fstab > /etc/fstab.tmp
|
|
mv /etc/fstab.tmp /etc/fstab
|
|
else
|
|
printf '%s\n' "$line" >> /etc/fstab
|
|
fi
|
|
}
|
|
|
|
create_swapfile() {
|
|
local swap_size="$1"
|
|
|
|
# Try fallocate first, fall back to dd if it fails
|
|
if ! fallocate -l "$swap_size" /swap/swapfile 2>/dev/null; then
|
|
echo "fallocate failed, using dd as fallback..."
|
|
local size_num
|
|
local size_unit
|
|
local bs="1M"
|
|
local count
|
|
|
|
size_num="${swap_size%%[^0-9.]*}"
|
|
size_unit="${swap_size##*[0-9.]}"
|
|
|
|
case "$size_unit" in
|
|
GiB|G|GB) count=$(awk "BEGIN {print int($size_num * 1024)}") ;;
|
|
MiB|M|MB|"") count=$(awk "BEGIN {print int($size_num)}") ;;
|
|
*) echo "Warning: unknown size unit, assuming MiB"; count=$(awk "BEGIN {print int($size_num)}") ;;
|
|
esac
|
|
|
|
dd if=/dev/zero of=/swap/swapfile bs="$bs" count="$count" status=progress
|
|
fi
|
|
}
|
|
|
|
touch /etc/fstab
|
|
update_fstab_entry /boot/efi vfat "defaults" "$ESP_UUID"
|
|
|
|
if [[ "$FS_TYPE" == "btrfs" ]]; then
|
|
update_fstab_entry / "btrfs" "defaults,subvol=@" "$ROOT_UUID"
|
|
update_fstab_entry /home "btrfs" "defaults,subvol=@home" "$ROOT_UUID"
|
|
update_fstab_entry /var "btrfs" "defaults,subvol=@var" "$ROOT_UUID"
|
|
update_fstab_entry /var/log "btrfs" "defaults,subvol=@log" "$ROOT_UUID"
|
|
update_fstab_entry /.snapshots "btrfs" "defaults,subvol=@snapshots" "$ROOT_UUID"
|
|
update_fstab_entry /swap "btrfs" "defaults,subvol=@swap" "$ROOT_UUID"
|
|
else
|
|
update_fstab_entry / "ext4" "defaults" "$ROOT_UUID"
|
|
fi
|
|
|
|
# Prepare a swap file inside the encrypted root if enabled.
|
|
if [[ "$SWAP_SIZE" != "0" ]]; then
|
|
mkdir -p /swap
|
|
if [[ "$FS_TYPE" == "btrfs" ]]; then
|
|
if command -v chattr >/dev/null 2>&1; then
|
|
chattr +C /swap
|
|
fi
|
|
if command -v btrfs >/dev/null 2>&1; then
|
|
btrfs property set /swap compression none || true
|
|
fi
|
|
fi
|
|
|
|
create_swapfile "$SWAP_SIZE"
|
|
chmod 600 /swap/swapfile
|
|
mkswap /swap/swapfile
|
|
if ! grep -q '^/swap/swapfile' /etc/fstab; then
|
|
printf '/swap/swapfile none swap defaults 0 0\n' >> /etc/fstab
|
|
fi
|
|
fi
|
|
|
|
# Ensure GRUB unlocks the encrypted root and passes the LUKS UUID to initramfs.
|
|
mkdir -p /etc/default
|
|
touch /etc/default/grub
|
|
if grep -q '^GRUB_ENABLE_CRYPTODISK=' /etc/default/grub; then
|
|
sed -i 's/^GRUB_ENABLE_CRYPTODISK=.*/GRUB_ENABLE_CRYPTODISK=y/' /etc/default/grub
|
|
else
|
|
printf 'GRUB_ENABLE_CRYPTODISK=y\n' >> /etc/default/grub
|
|
fi
|
|
|
|
if grep -q '^GRUB_CMDLINE_LINUX=' /etc/default/grub; then
|
|
if ! grep -q "rd.luks.uuid=$LUKS_UUID" /etc/default/grub; then
|
|
sed -i "s/^GRUB_CMDLINE_LINUX=\"/GRUB_CMDLINE_LINUX=\"rd.luks.uuid=$LUKS_UUID /" /etc/default/grub
|
|
fi
|
|
else
|
|
printf 'GRUB_CMDLINE_LINUX="rd.luks.uuid=%s"\n' "$LUKS_UUID" >> /etc/default/grub
|
|
fi
|
|
|
|
# Regenerate initramfs and GRUB configuration.
|
|
echo "Regenerating initramfs for all kernels (this may take a while)..."
|
|
xbps-reconfigure -fa
|
|
|
|
echo "Installing GRUB bootloader..."
|
|
grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=Void --recheck
|
|
|
|
echo "Generating GRUB configuration..."
|
|
grub-mkconfig -o /boot/grub/grub.cfg
|
|
EOS
|
|
|
|
log_info "Post-install complete."
|
|
}
|