|
|
@@ -0,0 +1,201 @@ |
|
|
#!/usr/bin/env bash |
|
|
|
|
|
# |
|
|
# NixOS install script synthesized from: |
|
|
# |
|
|
# - Erase Your Darlings (https://grahamc.com/blog/erase-your-darlings) |
|
|
# - ZFS Datasets for NixOS (https://grahamc.com/blog/nixos-on-zfs) |
|
|
# - NixOS Manual (https://nixos.org/nixos/manual/) |
|
|
# |
|
|
# It expects the name of the block device (e.g. 'sda') to partition |
|
|
# and install NixOS on and an authorized public ssh key to log in as |
|
|
# 'root' remotely. The script must also be executed as root. |
|
|
# |
|
|
# Example: `sudo ./install.sh sde "ssh-rsa AAAAB..."` |
|
|
# |
|
|
|
|
|
set -euo pipefail |
|
|
|
|
|
################################################################################ |
|
|
|
|
|
export COLOR_RESET="\033[0m" |
|
|
export RED_BG="\033[41m" |
|
|
export BLUE_BG="\033[44m" |
|
|
|
|
|
function err { |
|
|
echo -e "${RED_BG}$1${COLOR_RESET}" |
|
|
} |
|
|
|
|
|
function info { |
|
|
echo -e "${BLUE_BG}$1${COLOR_RESET}" |
|
|
} |
|
|
|
|
|
################################################################################ |
|
|
|
|
|
export DISK=$1 |
|
|
export AUTHORIZED_SSH_KEY=$2 |
|
|
|
|
|
if ! [[ -v DISK ]]; then |
|
|
err "Missing argument. Expected block device name, e.g. 'sda'" |
|
|
exit 1 |
|
|
fi |
|
|
|
|
|
export DISK_PATH="/dev/${DISK}" |
|
|
|
|
|
if ! [[ -b "$DISK_PATH" ]]; then |
|
|
err "Invalid argument: '${DISK_PATH}' is not a block special file" |
|
|
exit 1 |
|
|
fi |
|
|
|
|
|
if ! [[ -v AUTHORIZED_SSH_KEY ]]; then |
|
|
err "Missing argument. Expected public SSH key, e.g. 'ssh-rsa AAAAB...'" |
|
|
exit 1 |
|
|
fi |
|
|
|
|
|
if [[ "$EUID" > 0 ]]; then |
|
|
err "Must run as root" |
|
|
exit 1 |
|
|
fi |
|
|
|
|
|
export ZFS_POOL="rpool" |
|
|
|
|
|
# ephemeral datasets |
|
|
export ZFS_LOCAL="${ZFS_POOL}/local" |
|
|
export ZFS_DS_ROOT="${ZFS_LOCAL}/root" |
|
|
export ZFS_DS_NIX="${ZFS_LOCAL}/nix" |
|
|
|
|
|
# persistent datasets |
|
|
export ZFS_SAFE="${ZFS_POOL}/safe" |
|
|
export ZFS_DS_HOME="${ZFS_SAFE}/home" |
|
|
export ZFS_DS_PERSIST="${ZFS_SAFE}/persist" |
|
|
|
|
|
export ZFS_BLANK_SNAPSHOT="${ZFS_DS_ROOT}@blank" |
|
|
|
|
|
################################################################################ |
|
|
|
|
|
info "Running the UEFI (GPT) partitioning and formatting directions from the NixOS manual ..." |
|
|
parted "$DISK_PATH" -- mklabel gpt |
|
|
parted "$DISK_PATH" -- mkpart primary 512MiB 100% |
|
|
parted "$DISK_PATH" -- mkpart ESP fat32 1MiB 512MiB |
|
|
parted "$DISK_PATH" -- set 2 boot on |
|
|
export DISK_PART_ROOT="${DISK_PATH}1" |
|
|
export DISK_PART_BOOT="${DISK_PATH}2" |
|
|
|
|
|
info "Formatting boot partition ..." |
|
|
mkfs.fat -F 32 -n boot "$DISK_PART_BOOT" |
|
|
|
|
|
info "Creating '$ZFS_POOL' ZFS pool for '$DISK_PART_ROOT' ..." |
|
|
zpool create -f "$ZFS_POOL" "$DISK_PART_ROOT" |
|
|
|
|
|
info "Enabling compression for '$ZFS_POOL' ZFS pool ..." |
|
|
zfs set compression=on "$ZFS_POOL" |
|
|
|
|
|
info "Creating '$ZFS_DS_ROOT' ZFS dataset ..." |
|
|
zfs create -p -o mountpoint=legacy "$ZFS_DS_ROOT" |
|
|
|
|
|
info "Configuring extended attributes setting for '$ZFS_DS_ROOT' ZFS dataset ..." |
|
|
zfs set xattr=sa "$ZFS_DS_ROOT" |
|
|
|
|
|
info "Configuring access control list setting for '$ZFS_DS_ROOT' ZFS dataset ..." |
|
|
zfs set acltype=posixacl "$ZFS_DS_ROOT" |
|
|
|
|
|
info "Creating '$ZFS_BLANK_SNAPSHOT' ZFS snapshot ..." |
|
|
zfs snapshot "$ZFS_BLANK_SNAPSHOT" |
|
|
|
|
|
info "Mounting '$ZFS_DS_ROOT' to /mnt ..." |
|
|
mount -t zfs "$ZFS_DS_ROOT" /mnt |
|
|
|
|
|
info "Mounting '$DISK_PART_BOOT' to /mnt/boot ..." |
|
|
mkdir /mnt/boot |
|
|
mount "$DISK_PART_BOOT" /mnt/boot |
|
|
|
|
|
info "Creating '$ZFS_DS_NIX' ZFS dataset ..." |
|
|
zfs create -p -o mountpoint=legacy "$ZFS_DS_NIX" |
|
|
|
|
|
info "Disabling access time setting for '$ZFS_DS_NIX' ZFS dataset ..." |
|
|
zfs set atime=off "$ZFS_DS_NIX" |
|
|
|
|
|
info "Mounting '$ZFS_DS_NIX' to /mnt/nix ..." |
|
|
mkdir /mnt/nix |
|
|
mount -t zfs "$ZFS_DS_NIX" /mnt/nix |
|
|
|
|
|
info "Creating '$ZFS_DS_HOME' ZFS dataset ..." |
|
|
zfs create -p -o mountpoint=legacy "$ZFS_DS_HOME" |
|
|
|
|
|
info "Mounting '$ZFS_DS_HOME' to /mnt/home ..." |
|
|
mkdir /mnt/home |
|
|
mount -t zfs "$ZFS_DS_HOME" /mnt/home |
|
|
|
|
|
info "Creating '$ZFS_DS_PERSIST' ZFS dataset ..." |
|
|
zfs create -p -o mountpoint=legacy "$ZFS_DS_PERSIST" |
|
|
|
|
|
info "Mounting '$ZFS_DS_PERSIST' to /mnt/persist ..." |
|
|
mkdir /mnt/persist |
|
|
mount -t zfs "$ZFS_DS_PERSIST" /mnt/persist |
|
|
|
|
|
info "Creating persistent directory for host SSH keys ..." |
|
|
mkdir -p /mnt/persist/etc/ssh |
|
|
|
|
|
info "Generating NixOS configuration (/mnt/etc/nixos/*.nix) ..." |
|
|
nixos-generate-config --root /mnt |
|
|
|
|
|
info "Enter password for the root user ..." |
|
|
ROOT_PASSWORD_HASH="$(mkpasswd -m sha-512 | sed 's/\$/\\$/g')" |
|
|
|
|
|
export NIXOS_CONFIG_PATH=/mnt/etc/nixos/configuration.nix |
|
|
export NIXOS_CONFIG_BACKUP_PATH="${NIXOS_CONFIG_PATH}.original" |
|
|
|
|
|
if [[ -f "$NIXOS_CONFIG_BACKUP_PATH" ]]; then |
|
|
info "Restoring copy of the original NixOS config to '${NIXOS_CONFIG_PATH}' ..." |
|
|
cp "$NIXOS_CONFIG_BACKUP_PATH" "$NIXOS_CONFIG_PATH" |
|
|
else |
|
|
info "Saving a copy of the original '${NIXOS_CONFIG_PATH}' to '${NIXOS_CONFIG_BACKUP_PATH}' ..." |
|
|
cp "$NIXOS_CONFIG_PATH" "$NIXOS_CONFIG_BACKUP_PATH" |
|
|
fi |
|
|
|
|
|
info "Injecting extra configuration into /mnt/etc/nixos/configuration.nix ..." |
|
|
EXTRA_NIXOS_CONFIG=" |
|
|
# source: https://grahamc.com/blog/erase-your-darlings |
|
|
boot.initrd.postDeviceCommands = lib.mkAfter '' |
|
|
zfs rollback -r ${ZFS_BLANK_SNAPSHOT} |
|
|
''; |
|
|
|
|
|
# source: https://grahamc.com/blog/nixos-on-zfs |
|
|
boot.kernelParams = [ \"elevator=none\" ]; |
|
|
|
|
|
networking.hostId = \"$(head -c 8 /etc/machine-id)\"; |
|
|
|
|
|
users = { |
|
|
mutableUsers = false; |
|
|
users.root.initialHashedPassword = \"${ROOT_PASSWORD_HASH}\"; |
|
|
users.root.openssh.authorizedKeys.keys = [ |
|
|
\"${AUTHORIZED_SSH_KEY}\" |
|
|
]; |
|
|
}; |
|
|
|
|
|
services.openssh = { |
|
|
enable = true; |
|
|
hostKeys = [ |
|
|
{ |
|
|
path = \"/persist/etc/ssh/ssh_host_ed25519_key\"; |
|
|
type = \"ed25519\"; |
|
|
} |
|
|
{ |
|
|
path = \"/persist/etc/ssh/ssh_host_rsa_key\"; |
|
|
type = \"rsa\"; |
|
|
bits = 4096; |
|
|
} |
|
|
]; |
|
|
}; |
|
|
" |
|
|
|
|
|
nix-shell -p ed --command "ed \"${NIXOS_CONFIG_PATH}\" <<EOF |
|
|
16i${EXTRA_NIXOS_CONFIG}. |
|
|
wq |
|
|
EOF |
|
|
" |
|
|
|
|
|
sed -i "s/{ config, pkgs, ... }:/{ config, pkgs, lib, ... }:/" "$NIXOS_CONFIG_PATH" |
|
|
|
|
|
info "Installing NixOS to /mnt ..." |
|
|
nixos-install --no-root-passwd # already prompted for and configured password |