From c3e606a759ac144d29fa7391181140358943d0f1 Mon Sep 17 00:00:00 2001 From: Swarsel Date: Mon, 23 Dec 2024 17:07:47 +0100 Subject: [PATCH] feat: optional impermanence and encryption --- SwarselSystems.org | 64 +++++++++++++++++++++--------- flake.nix | 2 +- hosts/nixos/toto/default.nix | 4 +- hosts/nixos/toto/disk-config.nix | 68 +++++++++++++++++++++++++++----- scripts/bootstrap.sh | 58 +++++++++++++++++++-------- 5 files changed, 149 insertions(+), 47 deletions(-) diff --git a/SwarselSystems.org b/SwarselSystems.org index dda59c6..839c968 100644 --- a/SwarselSystems.org +++ b/SwarselSystems.org @@ -994,7 +994,7 @@ The interesting part is in the start: inputs.stylix.nixosModules.stylix inputs.lanzaboote.nixosModules.lanzaboote inputs.disko.nixosModules.disko - # inputs.impermanence.nixosModules.impermanence + inputs.impermanence.nixosModules.impermanence inputs.sops-nix.nixosModules.sops inputs.nswitch-rcm-nix.nixosModules.nswitch-rcm ./profiles/common/nixos @@ -1627,6 +1627,8 @@ I have removed most of the machines from this section. What remains are some hos { _module.args = { withSwap = false; + withImpermanence = true; + withEncryption = false; }; } ./hardware-configuration.nix @@ -1689,7 +1691,7 @@ I have removed most of the machines from this section. What remains are some hos swarselsystems = { wallpaper = self + /wallpaper/lenovowp.png; - impermanence = false; + impermanence = true; isBtrfs = false; initialSetup = true; }; @@ -2728,6 +2730,8 @@ This program sets up a new NixOS host. target_destination="" target_user="swarsel" ssh_port="22" + persist_dir="" + disk_encryption=0 temp=$(mktemp -d) function help_and_exit() { @@ -2746,6 +2750,7 @@ This program sets up a new NixOS host. echo " Default='${target_user}'." echo " --port specify the ssh port to use for remote access. Default=${ssh_port}." echo " --impermanence Use this flag if the target machine has impermanence enabled. WARNING: Assumes /persist path." + echo " --encryption Use this flag if the target machine has full disk encryption enabled." echo " --debug Enable debug mode." echo " -h | --help Print this help." exit 0 @@ -2800,14 +2805,14 @@ This program sets up a new NixOS host. SOPS_FILE=".sops.yaml" sed -i "{ - # Remove any * and & entries for this host - /[*&]$key_name/ d; - # Inject a new age: entry - # n matches the first line following age: and p prints it, then we transform it while reusing the spacing - /age:/{n; p; s/\(.*- \*\).*/\1$key_name/}; - # Inject a new hosts or user: entry - /&$key_type/{n; p; s/\(.*- &\).*/\1$key_name $key/} - }" $SOPS_FILE + # Remove any * and & entries for this host + /[*&]$key_name/ d; + # Inject a new age: entry + # n matches the first line following age: and p prints it, then we transform it while reusing the spacing + /age:/{n; p; s/\(.*- \*\).*/\1$key_name/}; + # Inject a new hosts or user: entry + /&$key_type/{n; p; s/\(.*- &\).*/\1$key_name $key/} + }" $SOPS_FILE green "Updating .sops.yaml" cd - } @@ -2834,6 +2839,14 @@ This program sets up a new NixOS host. shift temp=$1 ;; + --impermanence) + shift + persist_dir="/persist" + ;; + --encryption) + shift + disk_encryption=1 + ;; --debug) set -x ;; @@ -2869,24 +2882,28 @@ This program sets up a new NixOS host. # ------------------------ green "Preparing a new ssh_host_ed25519_key pair for $target_hostname." # Create the directory where sshd expects to find the host keys - install -d -m755 "$temp/etc/ssh" + install -d -m755 "$temp/$persist_dir/etc/ssh" # Generate host ssh key pair without a passphrase - ssh-keygen -t ed25519 -f "$temp/etc/ssh/ssh_host_ed25519_key" -C root@"$target_hostname" -N "" + ssh-keygen -t ed25519 -f "$temp/$persist_dir/etc/ssh/ssh_host_ed25519_key" -C root@"$target_hostname" -N "" # Set the correct permissions so sshd will accept the key - chmod 600 "$temp/etc/ssh/ssh_host_ed25519_key" + chmod 600 "$temp/$persist_dir/etc/ssh/ssh_host_ed25519_key" echo "Adding ssh host fingerprint at $target_destination to ~/.ssh/known_hosts" # This will fail if we already know the host, but that's fine ssh-keyscan -p "$ssh_port" "$target_destination" >> ~/.ssh/known_hosts || true # ------------------------ # when using luks, disko expects a passphrase on /tmp/disko-password, so we set it for now and will update the passphrase later # via the config - green "Preparing a temporary password for disko." - green "[Optional] Set disk encryption passphrase:" - read -rs luks_passphrase - if [ -n "$luks_passphrase" ]; then - $ssh_root_cmd "/bin/sh -c 'echo $luks_passphrase > /tmp/disko-password'" + if [ "$disk_encryption" -eq 1 ]; then + green "--encryption set: Preparing a temporary password for disko." + green "[Optional] Set disk encryption passphrase:" + read -rs luks_passphrase + if [ -n "$luks_passphrase" ]; then + $ssh_root_cmd "/bin/sh -c 'echo $luks_passphrase > /tmp/disko-password'" + else + $ssh_root_cmd "/bin/sh -c 'echo passphrase > /tmp/disko-password'" + fi else - $ssh_root_cmd "/bin/sh -c 'echo passphrase > /tmp/disko-password'" + green "--encryption not set: Not using disk encryption.." fi # ------------------------ green "Generating hardware-config.nix for $target_hostname and adding it to the nix-config." @@ -2911,6 +2928,11 @@ This program sets up a new NixOS host. fi done # ------------------------ + if [ -n "$persist_dir" ]; then + $ssh_root_cmd "cp /etc/machine-id $persist_dir/etc/machine-id || true" + $ssh_root_cmd "cp -R /etc/ssh/ $persist_dir/etc/ssh/ || true" + fi + # ------------------------ green "Generating an age key based on the new ssh_host_ed25519_key." target_key=$( ssh-keyscan -p "$ssh_port" -t ssh-ed25519 "$target_destination" 2>&1 | @@ -2960,6 +2982,10 @@ This program sets up a new NixOS host. cd "${git_root}" just sync "$target_user" "$target_destination" + if [ -n "$persist_dir" ]; then + $ssh_root_cmd "cp -r /home/$target_user/.dotfiles $persist_dir/.dotfiles || true" + fi + if yes_or_no "Do you want to rebuild immediately?"; then green "Rebuilding nix-config on $target_hostname" $ssh_root_cmd "mkdir -p /root/.local/share/nix/; printf '{\"extra-substituters\":{\"https://nix-community.cachix.org\":true,\"https://nix-community.cachix.org https://cache.ngi0.nixos.org/\":true},\"extra-trusted-public-keys\":{\"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=\":true,\"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs= cache.ngi0.nixos.org-1:KqH5CBLNSyX184S9BKZJo1LxrxJ9ltnY2uAs5c/f1MA=\":true}}' > /root/.local/share/nix/trusted-settings.json" diff --git a/flake.nix b/flake.nix index 925b8ce..6244c84 100644 --- a/flake.nix +++ b/flake.nix @@ -163,7 +163,7 @@ inputs.stylix.nixosModules.stylix inputs.lanzaboote.nixosModules.lanzaboote inputs.disko.nixosModules.disko - # inputs.impermanence.nixosModules.impermanence + inputs.impermanence.nixosModules.impermanence inputs.sops-nix.nixosModules.sops inputs.nswitch-rcm-nix.nixosModules.nswitch-rcm ./profiles/common/nixos diff --git a/hosts/nixos/toto/default.nix b/hosts/nixos/toto/default.nix index 742b06b..46f9fd9 100644 --- a/hosts/nixos/toto/default.nix +++ b/hosts/nixos/toto/default.nix @@ -10,6 +10,8 @@ in { _module.args = { withSwap = false; + withImpermanence = true; + withEncryption = false; }; } ./hardware-configuration.nix @@ -72,7 +74,7 @@ in swarselsystems = { wallpaper = self + /wallpaper/lenovowp.png; - impermanence = false; + impermanence = true; isBtrfs = false; initialSetup = true; }; diff --git a/hosts/nixos/toto/disk-config.nix b/hosts/nixos/toto/disk-config.nix index 7e61397..a073b16 100644 --- a/hosts/nixos/toto/disk-config.nix +++ b/hosts/nixos/toto/disk-config.nix @@ -1,8 +1,10 @@ # NOTE: ... is needed because dikso passes diskoFile { lib , pkgs -, withSwap ? false , swapSize +, withSwap ? true +, withEncryption ? true +, withImpermanence ? true , ... }: { @@ -26,7 +28,47 @@ mountOptions = [ "defaults" ]; }; }; - luks = { + root = lib.mkIf (!withEncryption) { + size = "100%"; + content = { + type = "btrfs"; + extraArgs = [ "-f" ]; # force overwrite + postCreateHook = lib.mkIf withImpermanence '' + MNTPOINT=$(mktemp -d) + mount "/dev/mapper/root" "$MNTPOINT" -o subvol=/ + trap 'umount $MNTPOINT; rm -rf $MNTPOINT' EXIT + btrfs subvolume snapshot -r $MNTPOINT/root $MNTPOINT/root-blank + ''; + subvolumes = { + "@root" = { + mountpoint = "/"; + mountOptions = [ + "compress=zstd" + "noatime" + ]; + }; + "@persist" = lib.mkIf withImpermanence { + mountpoint = "/persist"; + mountOptions = [ + "compress=zstd" + "noatime" + ]; + }; + "@nix" = { + mountpoint = "/nix"; + mountOptions = [ + "compress=zstd" + "noatime" + ]; + }; + "@swap" = lib.mkIf withSwap { + mountpoint = "/.swapvol"; + swap.swapfile.size = "${swapSize}G"; + }; + }; + }; + }; + luks = lib.mkIf withEncryption { size = "100%"; content = { type = "luks"; @@ -45,6 +87,12 @@ content = { type = "btrfs"; extraArgs = [ "-f" ]; # force overwrite + postCreateHook = lib.mkIf withImpermanence '' + MNTPOINT=$(mktemp -d) + mount "/dev/mapper/cryptroot" "$MNTPOINT" -o subvol=/ + trap 'umount $MNTPOINT; rm -rf $MNTPOINT' EXIT + btrfs subvolume snapshot -r $MNTPOINT/root $MNTPOINT/root-blank + ''; subvolumes = { "@root" = { mountpoint = "/"; @@ -53,13 +101,13 @@ "noatime" ]; }; - # "@persist" = { - # mountpoint = "${config.hostSpec.persistFolder}"; - # mountOptions = [ - # "compress=zstd" - # "noatime" - # ]; - # }; + "@persist" = lib.mkIf withImpermanence { + mountpoint = "/persist"; + mountOptions = [ + "compress=zstd" + "noatime" + ]; + }; "@nix" = { mountpoint = "/nix"; mountOptions = [ @@ -81,6 +129,8 @@ }; }; + fileSystems."/persist".neededForBoot = lib.mkIf withImpermanence true; + environment.systemPackages = [ pkgs.yubikey-manager # For luks fido2 enrollment before full install ]; diff --git a/scripts/bootstrap.sh b/scripts/bootstrap.sh index b2e01b4..68b68bb 100644 --- a/scripts/bootstrap.sh +++ b/scripts/bootstrap.sh @@ -5,6 +5,8 @@ target_hostname="" target_destination="" target_user="swarsel" ssh_port="22" +persist_dir="" +disk_encryption=0 temp=$(mktemp -d) function help_and_exit() { @@ -23,6 +25,7 @@ function help_and_exit() { echo " Default='${target_user}'." echo " --port specify the ssh port to use for remote access. Default=${ssh_port}." echo " --impermanence Use this flag if the target machine has impermanence enabled. WARNING: Assumes /persist path." + echo " --encryption Use this flag if the target machine has full disk encryption enabled." echo " --debug Enable debug mode." echo " -h | --help Print this help." exit 0 @@ -77,14 +80,14 @@ function update_sops_file() { SOPS_FILE=".sops.yaml" sed -i "{ - # Remove any * and & entries for this host - /[*&]$key_name/ d; - # Inject a new age: entry - # n matches the first line following age: and p prints it, then we transform it while reusing the spacing - /age:/{n; p; s/\(.*- \*\).*/\1$key_name/}; - # Inject a new hosts or user: entry - /&$key_type/{n; p; s/\(.*- &\).*/\1$key_name $key/} - }" $SOPS_FILE + # Remove any * and & entries for this host + /[*&]$key_name/ d; + # Inject a new age: entry + # n matches the first line following age: and p prints it, then we transform it while reusing the spacing + /age:/{n; p; s/\(.*- \*\).*/\1$key_name/}; + # Inject a new hosts or user: entry + /&$key_type/{n; p; s/\(.*- &\).*/\1$key_name $key/} + }" $SOPS_FILE green "Updating .sops.yaml" cd - } @@ -111,6 +114,14 @@ while [[ $# -gt 0 ]]; do shift temp=$1 ;; + --impermanence) + shift + persist_dir="/persist" + ;; + --encryption) + shift + disk_encryption=1 + ;; --debug) set -x ;; @@ -146,24 +157,28 @@ sed -i "/$target_hostname/d; /$target_destination/d" ~/.ssh/known_hosts # ------------------------ green "Preparing a new ssh_host_ed25519_key pair for $target_hostname." # Create the directory where sshd expects to find the host keys -install -d -m755 "$temp/etc/ssh" +install -d -m755 "$temp/$persist_dir/etc/ssh" # Generate host ssh key pair without a passphrase -ssh-keygen -t ed25519 -f "$temp/etc/ssh/ssh_host_ed25519_key" -C root@"$target_hostname" -N "" +ssh-keygen -t ed25519 -f "$temp/$persist_dir/etc/ssh/ssh_host_ed25519_key" -C root@"$target_hostname" -N "" # Set the correct permissions so sshd will accept the key -chmod 600 "$temp/etc/ssh/ssh_host_ed25519_key" +chmod 600 "$temp/$persist_dir/etc/ssh/ssh_host_ed25519_key" echo "Adding ssh host fingerprint at $target_destination to ~/.ssh/known_hosts" # This will fail if we already know the host, but that's fine ssh-keyscan -p "$ssh_port" "$target_destination" >> ~/.ssh/known_hosts || true # ------------------------ # when using luks, disko expects a passphrase on /tmp/disko-password, so we set it for now and will update the passphrase later # via the config -green "Preparing a temporary password for disko." -green "[Optional] Set disk encryption passphrase:" -read -rs luks_passphrase -if [ -n "$luks_passphrase" ]; then - $ssh_root_cmd "/bin/sh -c 'echo $luks_passphrase > /tmp/disko-password'" +if [ "$disk_encryption" -eq 1 ]; then + green "--encryption set: Preparing a temporary password for disko." + green "[Optional] Set disk encryption passphrase:" + read -rs luks_passphrase + if [ -n "$luks_passphrase" ]; then + $ssh_root_cmd "/bin/sh -c 'echo $luks_passphrase > /tmp/disko-password'" + else + $ssh_root_cmd "/bin/sh -c 'echo passphrase > /tmp/disko-password'" + fi else - $ssh_root_cmd "/bin/sh -c 'echo passphrase > /tmp/disko-password'" + green "--encryption not set: Not using disk encryption.." fi # ------------------------ green "Generating hardware-config.nix for $target_hostname and adding it to the nix-config." @@ -188,6 +203,11 @@ while true; do fi done # ------------------------ +if [ -n "$persist_dir" ]; then + $ssh_root_cmd "cp /etc/machine-id $persist_dir/etc/machine-id || true" + $ssh_root_cmd "cp -R /etc/ssh/ $persist_dir/etc/ssh/ || true" +fi +# ------------------------ green "Generating an age key based on the new ssh_host_ed25519_key." target_key=$( ssh-keyscan -p "$ssh_port" -t ssh-ed25519 "$target_destination" 2>&1 | @@ -237,6 +257,10 @@ if yes_or_no "Do you want to copy your full nix-config and nix-secrets to $targe cd "${git_root}" just sync "$target_user" "$target_destination" + if [ -n "$persist_dir" ]; then + $ssh_root_cmd "cp -r /home/$target_user/.dotfiles $persist_dir/.dotfiles || true" + fi + if yes_or_no "Do you want to rebuild immediately?"; then green "Rebuilding nix-config on $target_hostname" $ssh_root_cmd "mkdir -p /root/.local/share/nix/; printf '{\"extra-substituters\":{\"https://nix-community.cachix.org\":true,\"https://nix-community.cachix.org https://cache.ngi0.nixos.org/\":true},\"extra-trusted-public-keys\":{\"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=\":true,\"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs= cache.ngi0.nixos.org-1:KqH5CBLNSyX184S9BKZJo1LxrxJ9ltnY2uAs5c/f1MA=\":true}}' > /root/.local/share/nix/trusted-settings.json"