Update installation guide

This commit is contained in:
ItsDrike 2024-04-08 03:36:03 +02:00
parent a6cf555b00
commit 718a55f595
Signed by: ItsDrike
GPG key ID: FA2745890B7048C0

View file

@ -62,18 +62,6 @@ btrfs subvolume create /mnt/nix
btrfs subvolume create /mnt/log btrfs subvolume create /mnt/log
btrfs subvolume create /mnt/persist btrfs subvolume create /mnt/persist
btrfs subvolume create /mnt/data btrfs subvolume create /mnt/data
```
We will now take a read-only snapshot of the root subvolume.
This snapshot will be eventually used for rolling back to on every boot (impermanence).
```shell
btrfs subvolume snapshot -r /mnt/root /mnt/root-blank
```
And finally, we can unmount the btrfs root.
```shell
umount /mnt umount /mnt
``` ```
@ -226,7 +214,11 @@ fileSystems."/" =
### Subvolumes needed for boot ### Subvolumes needed for boot
In order to correctly persist `/var/log`, the respective subvolume need to be mounted early enough in In order to correctly persist `/var/log`, the respective subvolume need to be mounted early enough in
the boot process. To do this, we will want to add `neededForBoot = true;`, so the entry will look like this: the boot process. To do this, we will want to add `neededForBoot = true;`. Additionally, we will also
need to add this parameter for our `/persisr` subvolume. This is because we will be storing the user
password (including root password) in a password file there (mentioned later on).
So the entries will look like this:
```nix ```nix
fileSystems."/var/log" = fileSystems."/var/log" =
@ -235,10 +227,14 @@ fileSystems."/var/log" =
options = [ "subvol=log" "noatime" "compress=zstd:3" ]; options = [ "subvol=log" "noatime" "compress=zstd:3" ];
neededForBoot = true; neededForBoot = true;
}; };
```
Additionally, we will also need to add `neededForBoot = true;` to our `/persist` subvolume. This is because fileSystems."/persist" =
we will be storing the root users password file in there. { device = "/dev/disk/by-label/NIXFS";
fsType = "btrfs";
options = [ "subvol=persist" "noatime" "compress=zstd:3" ];
neededForBoot = true;
};
```
## Minimal config ## Minimal config
@ -310,12 +306,11 @@ screen. Log in as root, set your password (`passwd itsdrike`), log out and re-lo
This is an optional step, if you don't want your root partition to get auto-reset on each boot, you can simply skip this. This is an optional step, if you don't want your root partition to get auto-reset on each boot, you can simply skip this.
### Auto-restore root-blank snapshot ### Auto-wipe root partition
Remember how we create the empty snapshot of our root subvolume? Well now comes the time when we put it to use. We will To reset the root subvolume on every boot, we can simply delete it and create a new one in its place. We will be doing
restore this snapshot from initrd, which runs in a temporary file-system, before our actual file-system is even mounted. this from initrd, which runs in a temporary file-system, before the actual file-system is properly mounted (following
This makes it a perfect place to run a script which will restore our root subvolume to the blank snapshot before each fstab). This makes it a perfect place to run a script, which will wipe the root subvolume before each boot.
boot.
I will set this up using a systemd-based initrd, because I will need systemd for TPM unlocking later on. If you don't I will set this up using a systemd-based initrd, because I will need systemd for TPM unlocking later on. If you don't
care about that, it is also possible to do this without systemd. You can a guide for such setup care about that, it is also possible to do this without systemd. You can a guide for such setup
@ -338,41 +333,37 @@ boot.initrd.systemd = {
unitConfig.DefaultDependencies = "no"; unitConfig.DefaultDependencies = "no";
serviceConfig.Type = "oneshot"; serviceConfig.Type = "oneshot";
script = '' script = ''
mkdir -p /mnt # Mount the BTRFS root to /mnt so we can manipulate btrfs subvolumes
mount --mkdir /dev/mapper/cryptfs /mnt
# We first mount the btrfs root to /mnt # Simply deleting a subvolume with btrfs subvolume delete will not work,
# so we can manipulate btrfs subvolumes. # if that subvolume contains other btrfs subvolumes. Because of that, we
mount /dev/mapper/cryptfs /mnt # instead use this function to delete subvolumes, whihc will first perform
# a recursive deletion of any nested subvolumes.
# While we're tempted to just delete /root and create
# a new snapshot from /root-blank, /root is already
# populated at this point with a number of subvolumes,
# which makes `btrfs subvolume delete` fail.
# So, we remove them first.
# #
# /root contains subvolumes: # This is necessary, because the root subvolume will actually usually contain
# - /root/var/lib/portables # other subvolumes, even if the user haven't created those explicitly. It seems
# - /root/var/lib/machines # that NixOS creates these automatically. Namely, I observed these in root subvol:
# # - root/srv
# These are probably related to systemd-nspawn, but # - root/var/lib/portables
# since I don't use it, I'm not 100% sure. # - root/var/lib/machines
# Anyhow, deleting these subvolumes hasn't resulted in # - root/var/tmp
# any issues so far, except for fairly benign-looking delete_subvolume_recursively() {
# errors from systemd-tmpfiles. IFS=$'\n'
btrfs subvolume list -o /mnt/root | for x in $(btrfs subvolume list -o "$1" | cut -f 9- -d ' '); do
cut -f9 -d' ' | delete_subvolume_recursively "/mnt/$x"
while read subvolume; do done
echo "deleting /$subvolume subvolume..."
btrfs subvolume delete "/mnt/$subvolume"
done &&
echo "deleting /root subvolume..." &&
btrfs subvolume delete /mnt/root
echo "restoring blank /root subvolume..." echo "Deleting subvolume $1"
btrfs subvolume snapshot /mnt/root-blank /mnt/root btrfs subvolume delete "$1"
}
# Once we're done rolling back to a blank snapshot, # Recreate the root subvolume
# we can unmount /mnt and continue on the boot process. delete_subvolume_recursively "/mnt/root"
echo "Re-creating root subvolume"
btrfs subvolume create "/mnt/root"
# we can now unmount /mnt and continue on the boot process.
umount /mnt umount /mnt
''; '';
}; };
@ -407,12 +398,6 @@ in
]; ];
files = [ files = [
"/etc/machine-id" "/etc/machine-id"
# ssh stuff
"/etc/ssh/ssh_host_ed25519_key"
"/etc/ssh/ssh_host_ed25519_key.pub"
"/etc/ssh/ssh_host_rsa_key"
"/etc/ssh/ssh_host_rsa_key.pub"
]; ];
}; };
@ -422,6 +407,21 @@ in
"L /var/lib/NetworkManager/seen-bssids - - - - /persist/system/var/lib/NetworkManager/seen-bssids" "L /var/lib/NetworkManager/seen-bssids - - - - /persist/system/var/lib/NetworkManager/seen-bssids"
"L /var/lib/NetworkManager/timestamps - - - - /persist/system/var/lib/NetworkManager/timestamps" "L /var/lib/NetworkManager/timestamps - - - - /persist/system/var/lib/NetworkManager/timestamps"
]; ];
# Define host key paths in the persistent mount point instead of using impermanence for these.
# This works better, because these keys also get auto-created if they don't already exist.
services.openssh.hostKeys = mkForce [
{
bits = 4096;
path = "/persist/system/etc/ssh/ssh_host_rsa_key";
type = "rsa";
}
{
bits = 4096;
path = "/persist/system/etc/ssh/ssh_host_ed25519_key";
type = "ed25519";
}
];
} }
``` ```
@ -436,23 +436,25 @@ Note that with impermanence, your user passwords will get erased too (with the `
you can create password files, which will contain the password hashes for each user: you can create password files, which will contain the password hashes for each user:
```shell ```shell
mkpasswd -m sha-512 > /persist/system/passwords/root mkdir -p /persist/passwords
mkpasswd -m sha-512 > /persist/system/passwords/itsdrike chmod 700 /persist/passwords
mkpasswd -m sha-512 > /persist/passwords/root
mkpasswd -m sha-512 > /persist/passwords/itsdrike
chmod 600 /persist/passwords/*
``` ```
And declare these in our `configuration.nix` or `impermanence.nix` And declare these in our `configuration.nix` or `impermanence.nix`
```nix ```nix
users = { users = {
# This option makes it that users are not mutable outside our configuration # This option makes it that users are not mutable outside of our configuration.
# If you are using impermanence, this will actually be the case regardless of this setting, # If you're using root impermanence, this will actually be the case regardless
# however, setting this explicitly is a good idea, because nix will warn us if # of this setting, however, setting this explicitly is a good idea, because nix
# our users don't have passwords set # will warn us if our users don't have passwords set, preventing lock outs.
mutableUsers = false; mutableUsers = false;
# Each existing user needs to have a password file defined here # Each existing user needs to have a password file defined here, otherwise
# otherwise, they will not be available to login. # they will not be available to login. These password files can be generated with:
# These password files can be generated using the following command:
# mkpasswd -m sha-512 > /persist/passwords/myuser # mkpasswd -m sha-512 > /persist/passwords/myuser
users = { users = {
root = { root = {
@ -601,7 +603,6 @@ The resulting file should then look something like this:
{ {
imports = [ imports = [
./hardware-configuration.nix ./hardware-configuration.nix
./impermanence.nix
]; ];
boot.supportedFilesystems = [ "btrfs" ]; boot.supportedFilesystems = [ "btrfs" ];
@ -625,6 +626,21 @@ The resulting file should then look something like this:
system = { system = {
hostname = "anduril"; hostname = "anduril";
username = "itsdrike"; username = "itsdrike";
impermanence = {
root = {
enable = true;
# Some people use /nix/persist/system for this, leaving persistent files in /nix subvolume
# I much prefer using a standalone subvolume for this though.
persistentMountPoint = "/persist";
};
# Configure automatic root subvolume wiping on boot from initrd
autoWipeBtrfs = {
enable = true;
devices."/dev/disk/by-label/NIXROOT".subvolumes = [ "root" ];
};
};
}; };
device = { device = {
virtual-machine = false; virtual-machine = false;
@ -646,12 +662,14 @@ The resulting file should then look something like this:
} }
``` ```
> [!WARNING] > [!NOTE]
> I'm currently working on making impermanence config in my flake directly. This will mean you will eventually be > You may notice that this configuration also includes custom options for impermanence,
> expected to just enable impermanence through myOptions. Right now, the config above includes `impermanence.nix` > and that the impermanence.nix is no longer declared in imports. This is because my
> that we have enabled earlier. This will work, however note that flakes are a bit stricter with fetchTarball, and > flake already contains a fully custom support (mostly similar to what I've shown here)
> require a sha256 hash to be specified. You can specify it, or use the `--impure` flag for now. Once impermanence > to handle impermanence. This allows me to re-use this impermanence across multiple
> will be integrated into my flake, it will be handled as an input, and you won't have to worry about anything. > machines very easily.
>
> You can now therefore delete the `impermanence.nix` file.
### Commit and switch ### Commit and switch