2022-11-10 16:42:06 +03:00
|
|
|
#!/usr/bin/env bash
|
|
|
|
set -eufo pipefail
|
|
|
|
|
|
|
|
showUsage() {
|
|
|
|
cat <<USAGE
|
2022-12-09 17:37:41 +03:00
|
|
|
Usage: nixos-remote [options] ssh-host
|
2022-11-10 16:42:06 +03:00
|
|
|
|
|
|
|
Options:
|
|
|
|
|
|
|
|
* -f, --flake flake
|
|
|
|
set the flake to install the system from
|
2022-12-07 18:06:02 +03:00
|
|
|
* -s, --store-paths
|
|
|
|
set the store paths to the disko-script and nixos-system directly
|
|
|
|
if this is give, flake is not needed
|
2022-12-07 18:06:20 +03:00
|
|
|
* --no-ssh-copy
|
|
|
|
skip copying ssh-keys to target system
|
2022-12-29 22:11:06 +03:00
|
|
|
* --no-reboot
|
|
|
|
do not reboot after installation, allowing further customization of the target installation.
|
2022-11-10 17:30:50 +03:00
|
|
|
* --kexec url
|
|
|
|
use another kexec tarball to bootstrap NixOS
|
2022-12-27 21:43:41 +03:00
|
|
|
* --stop-after-disko
|
|
|
|
exit after disko formating, you can then proceed to install manually or some other way
|
2022-12-23 20:22:25 +03:00
|
|
|
* --extra-files files
|
|
|
|
files to copy into the new nixos installation
|
2022-12-28 17:19:23 +03:00
|
|
|
* --disk-encryption-keys files
|
|
|
|
files to copy into the installer environment, after kexec but before installation. Can be
|
|
|
|
used for things like disk encryption keys
|
2022-12-09 17:26:44 +03:00
|
|
|
* --debug
|
|
|
|
enable debug output
|
2022-11-10 16:42:06 +03:00
|
|
|
USAGE
|
|
|
|
}
|
|
|
|
|
|
|
|
abort() {
|
|
|
|
echo "aborted: $*" >&2
|
|
|
|
exit 1
|
|
|
|
}
|
|
|
|
|
2022-12-14 15:25:31 +03:00
|
|
|
kexec_url=https://github.com/nix-community/nixos-images/releases/download/nixos-22.11/nixos-kexec-installer-x86_64-linux.tar.gz
|
2022-12-30 15:16:00 +03:00
|
|
|
enable_debug=""
|
|
|
|
maybereboot="reboot"
|
2022-11-10 16:42:06 +03:00
|
|
|
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
|
|
case "$1" in
|
|
|
|
-f | --flake)
|
|
|
|
flake=$2
|
|
|
|
shift
|
|
|
|
;;
|
2022-12-07 18:06:02 +03:00
|
|
|
-s | --store-paths)
|
2022-12-30 20:26:02 +03:00
|
|
|
disko_script=$(readlink -f "$2")
|
|
|
|
nixos_system=$(readlink -f "$3")
|
2022-12-07 18:06:02 +03:00
|
|
|
shift
|
|
|
|
shift
|
|
|
|
;;
|
2022-11-10 16:42:06 +03:00
|
|
|
--help)
|
|
|
|
showUsage
|
|
|
|
exit 0
|
|
|
|
;;
|
2022-11-10 17:30:50 +03:00
|
|
|
--kexec)
|
|
|
|
kexec_url=$2
|
|
|
|
shift
|
|
|
|
;;
|
2022-11-24 20:28:47 +03:00
|
|
|
--no-ssh-copy-id)
|
|
|
|
no_ssh_copy=y
|
|
|
|
;;
|
2022-12-09 17:26:44 +03:00
|
|
|
--debug)
|
2022-12-30 15:16:00 +03:00
|
|
|
enable_debug="-x"
|
2022-12-09 17:26:44 +03:00
|
|
|
set -x
|
|
|
|
;;
|
2022-12-23 20:22:25 +03:00
|
|
|
--extra-files)
|
|
|
|
extra_files=$2
|
|
|
|
shift
|
|
|
|
;;
|
2022-12-28 17:19:23 +03:00
|
|
|
--disk-encryption-keys)
|
|
|
|
disk_encryption_keys=$2
|
|
|
|
shift
|
|
|
|
;;
|
2022-12-27 21:43:41 +03:00
|
|
|
--stop-after-disko)
|
|
|
|
stop_after_disko=y
|
|
|
|
;;
|
2022-12-30 13:24:26 +03:00
|
|
|
--no-reboot)
|
2022-12-30 15:16:00 +03:00
|
|
|
maybereboot=""
|
2022-12-30 13:24:26 +03:00
|
|
|
;;
|
2022-11-10 16:42:06 +03:00
|
|
|
*)
|
2022-12-21 21:06:58 +03:00
|
|
|
if [[ -z ${ssh_connection:-} ]]; then
|
2022-12-29 19:18:46 +03:00
|
|
|
ssh_connection="$1"
|
2022-11-10 16:42:06 +03:00
|
|
|
else
|
|
|
|
showUsage
|
|
|
|
exit 1
|
|
|
|
fi
|
|
|
|
;;
|
|
|
|
esac
|
|
|
|
shift
|
|
|
|
done
|
|
|
|
|
2022-12-09 18:20:48 +03:00
|
|
|
|
2022-11-10 16:42:06 +03:00
|
|
|
# ssh wrapper
|
2022-11-10 18:45:14 +03:00
|
|
|
timeout_ssh_() {
|
2022-11-10 17:31:43 +03:00
|
|
|
timeout 10 ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "$ssh_connection" "$@"
|
2022-11-10 16:42:06 +03:00
|
|
|
}
|
2022-11-10 18:45:14 +03:00
|
|
|
ssh_() {
|
|
|
|
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "$ssh_connection" "$@"
|
|
|
|
}
|
2022-11-23 20:32:36 +03:00
|
|
|
nixCopy() {
|
|
|
|
NIX_SSHOPTS='-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no' nix copy --extra-experimental-features nix-command "$@"
|
|
|
|
}
|
|
|
|
nix_build() {
|
|
|
|
nix \
|
|
|
|
--experimental-features flakes build \
|
|
|
|
--extra-experimental-features nix-command \
|
|
|
|
--no-write-lock-file \
|
|
|
|
--print-out-paths \
|
|
|
|
"$@"
|
|
|
|
}
|
|
|
|
|
2022-12-21 21:06:58 +03:00
|
|
|
if [[ -z ${ssh_connection:-} ]]; then
|
2022-12-09 18:20:48 +03:00
|
|
|
abort "ssh-host must be set"
|
|
|
|
fi
|
|
|
|
|
2022-11-23 20:32:36 +03:00
|
|
|
# parse flake nixos-install style syntax, get the system attr
|
2022-12-21 21:06:58 +03:00
|
|
|
if [[ -n "${flake:-}" ]]; then
|
2022-11-23 20:32:36 +03:00
|
|
|
if [[ $flake =~ ^(.*)\#([^\#\"]*)$ ]]; then
|
|
|
|
flake="${BASH_REMATCH[1]}"
|
|
|
|
flakeAttr="${BASH_REMATCH[2]}"
|
|
|
|
fi
|
2022-12-21 21:06:58 +03:00
|
|
|
if [[ -z "${flakeAttr:-}" ]]; then
|
2022-11-23 20:32:36 +03:00
|
|
|
echo "Please specify the name of the NixOS configuration to be installed, as a URI fragment in the flake-uri."
|
|
|
|
echo "For example, to use the output nixosConfigurations.foo from the flake.nix, append \"#foo\" to the flake-uri."
|
|
|
|
exit 1
|
|
|
|
fi
|
|
|
|
disko_script=$(nix_build "${flake}#nixosConfigurations.${flakeAttr}.config.system.build.disko")
|
|
|
|
nixos_system=$(nix_build "${flake}#nixosConfigurations.${flakeAttr}.config.system.build.toplevel")
|
2022-12-21 21:06:58 +03:00
|
|
|
elif [[ -n "${disko_script:-}" ]] && [[ -n "${nixos_system:-}" ]]; then
|
2022-12-07 18:06:02 +03:00
|
|
|
if [[ ! -e "${disko_script}" ]] || [[ ! -e "${nixos_system}" ]]; then
|
|
|
|
echo "${disko_script} and ${nixos_system} must be existing store-paths"
|
|
|
|
exit 1
|
|
|
|
fi
|
|
|
|
:
|
2022-11-23 20:32:36 +03:00
|
|
|
else
|
|
|
|
abort "flake must be set"
|
|
|
|
fi
|
|
|
|
|
|
|
|
# wait for machine to become reachable (possibly forever)
|
2022-12-30 15:16:00 +03:00
|
|
|
# TODO we probably need an architecture detection here
|
2022-12-30 20:51:46 +03:00
|
|
|
# TODO if we have specified a user here but we are already booted into the
|
|
|
|
# installer, than the user might not work anymore
|
2022-12-30 15:16:00 +03:00
|
|
|
until facts=$(ssh_ -o ConnectTimeout=10 -- <<SSH
|
|
|
|
set -efu ${enable_debug}
|
|
|
|
has(){
|
|
|
|
command -v tar >/dev/null && echo "y" || echo "n"
|
|
|
|
}
|
|
|
|
cat <<FACTS
|
|
|
|
is_os=\$(uname)
|
|
|
|
is_kexec=\$(if test -f /etc/is_kexec; then echo "y"; else echo "n"; fi)
|
|
|
|
has_tar=\$(has tar)
|
|
|
|
has_sudo=\$(has sudo)
|
|
|
|
has_wget=\$(has wget)
|
|
|
|
has_curl=\$(has curl)
|
|
|
|
FACTS
|
|
|
|
SSH
|
|
|
|
); do
|
|
|
|
sleep 5
|
|
|
|
done
|
|
|
|
# make facts available in script
|
|
|
|
# shellcheck disable=SC2046
|
|
|
|
export $(echo "$facts" | xargs | grep -E '^(has|is)_[a-z0-9_]+=\S+')
|
2022-11-10 16:42:06 +03:00
|
|
|
|
2022-12-30 15:16:00 +03:00
|
|
|
if [[ ${has_tar-n} == "n" ]]; then
|
|
|
|
abort "no tar command found, but required to unpack kexec tarball"
|
2022-12-14 15:34:23 +03:00
|
|
|
fi
|
2022-12-30 15:16:00 +03:00
|
|
|
maybesudo=""
|
|
|
|
if [[ ${has_sudo-n} == "y" ]]; then
|
|
|
|
maybesudo="sudo"
|
2022-11-10 19:15:00 +03:00
|
|
|
fi
|
2022-12-30 15:16:00 +03:00
|
|
|
if [[ ${is_os-n} != "Linux" ]]; then
|
|
|
|
abort "This script requires Linux as the operating system, but got $is_os"
|
2022-11-24 20:28:47 +03:00
|
|
|
fi
|
|
|
|
|
2022-12-30 15:16:00 +03:00
|
|
|
if [[ ${is_kexec-n} != "y" ]] && [[ ${no_ssh_copy-n} != "y" ]]; then
|
|
|
|
ssh-copy-id -o ConnectTimeout=10 -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "$ssh_connection"
|
|
|
|
fi
|
2022-11-24 20:28:47 +03:00
|
|
|
|
2022-12-30 15:16:00 +03:00
|
|
|
if [[ ${is_kexec-n} == "n" ]]; then
|
2022-11-24 20:28:47 +03:00
|
|
|
ssh_ << SSH
|
2022-12-30 15:16:00 +03:00
|
|
|
set -efu ${enable_debug}
|
|
|
|
"${maybesudo}" rm -rf /root/kexec
|
|
|
|
"${maybesudo}" mkdir -p /root/kexec
|
|
|
|
SSH
|
|
|
|
|
|
|
|
if [[ -f "$kexec_url" ]]; then
|
2022-12-30 20:51:46 +03:00
|
|
|
ssh_ "${maybesudo} tar -C /root/kexec -xvzf-" < "$kexec_url"
|
2022-12-30 15:16:00 +03:00
|
|
|
elif [[ ${has_curl-n} == "y" ]]; then
|
|
|
|
ssh_ "curl --fail -Ss -L '${kexec_url}' | ${maybesudo} tar -C /root/kexec -xvzf-"
|
|
|
|
elif [[ ${has_wget-n} == "y" ]]; then
|
|
|
|
ssh_ "wget '${kexec_url}' -O- | ${maybesudo} tar -C /root/kexec -xvzf-"
|
2022-11-10 16:42:06 +03:00
|
|
|
else
|
2022-12-30 15:16:00 +03:00
|
|
|
curl --fail -Ss -L "${kexec_url}" | ssh_ "${maybesudo} tar -C /root/kexec -xvzf-"
|
2022-11-10 16:42:06 +03:00
|
|
|
fi
|
2022-11-12 13:47:17 +03:00
|
|
|
|
2022-12-30 15:16:00 +03:00
|
|
|
ssh_ << SSH
|
|
|
|
TMPDIR=/root/kexec setsid ${maybesudo} /root/kexec/kexec/run
|
2022-11-10 16:42:06 +03:00
|
|
|
SSH
|
2022-11-24 20:28:47 +03:00
|
|
|
|
2022-11-10 16:42:06 +03:00
|
|
|
# wait for machine to become unreachable
|
2022-11-10 18:45:14 +03:00
|
|
|
while timeout_ssh_ -- exit 0; do sleep 1; done
|
2022-11-10 16:42:06 +03:00
|
|
|
|
2022-12-30 20:51:46 +03:00
|
|
|
# After kexec we explicitly set the user to root@
|
|
|
|
ssh_connection="root@${ssh_connection#*@}"
|
|
|
|
|
2022-11-10 16:42:06 +03:00
|
|
|
# watiting for machine to become available again
|
2022-11-10 17:31:43 +03:00
|
|
|
until ssh_ -o ConnectTimeout=10 -- exit 0; do sleep 5; done
|
2022-11-10 16:42:06 +03:00
|
|
|
fi
|
|
|
|
|
2022-12-28 17:19:23 +03:00
|
|
|
if [[ -n ${disk_encryption_keys:-} ]]; then
|
|
|
|
if [[ -d "$disk_encryption_keys" ]]; then
|
|
|
|
disk_encryption_keys="$disk_encryption_keys/"
|
|
|
|
fi
|
2022-12-29 13:31:51 +03:00
|
|
|
rsync -vrlF -e "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no" "$disk_encryption_keys" "${ssh_connection#ssh://}:/tmp/"
|
2022-12-28 17:19:23 +03:00
|
|
|
fi
|
|
|
|
|
2022-12-29 19:18:46 +03:00
|
|
|
nixCopy --to "ssh://$ssh_connection" "$disko_script"
|
2022-12-21 15:57:39 +03:00
|
|
|
ssh_ "$disko_script"
|
2022-11-10 16:42:06 +03:00
|
|
|
|
2022-12-27 21:43:41 +03:00
|
|
|
if [[ ${stop_after_disko-n} == "y" ]]; then
|
|
|
|
exit 0
|
|
|
|
fi
|
|
|
|
|
2022-12-29 19:18:46 +03:00
|
|
|
nixCopy --to "ssh://$ssh_connection?remote-store=local?root=/mnt" "$nixos_system"
|
2022-12-23 20:22:25 +03:00
|
|
|
if [[ -n ${extra_files:-} ]]; then
|
|
|
|
if [[ -d "$extra_files" ]]; then
|
|
|
|
extra_files="$extra_files/"
|
|
|
|
fi
|
2022-12-29 13:33:47 +03:00
|
|
|
rsync -vrlF -e "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no" "$extra_files" "${ssh_connection}:/mnt/"
|
2022-12-23 20:22:25 +03:00
|
|
|
fi
|
2022-12-30 13:24:26 +03:00
|
|
|
|
2022-12-30 15:16:00 +03:00
|
|
|
ssh_ <<SSH
|
|
|
|
set -efu ${enable_debug}
|
2022-12-28 17:19:23 +03:00
|
|
|
# needed for installation if initrd-secrets are used
|
2022-12-30 03:09:07 +03:00
|
|
|
mkdir -m777 -p /mnt/tmp
|
2022-12-30 15:16:00 +03:00
|
|
|
nixos-install --no-root-passwd --no-channel-copy --system "$nixos_system"
|
|
|
|
${maybereboot}
|
|
|
|
SSH
|