It took me quite a while to get to this setup so I thought I would write a guide to help others and also to discuss feedback and other desktop setups.
I’m currently running NixOS on my Tuxedo Pulse 14, a Surface Pro 6, an 8-9 years old custom gaming tower PC with intel + nvidia, and a Hetzner VPS. Also had it on a ROG Ally which I ended up returning when a joystick stopped working on it.
For those who want to jump ahead and just look at the setup, here it is. Everything is open, except for one git-crypt encrypted file where I keep sensitive info. Feel welcome to fork the repo.
The setup is LUKS2 encrypted Btrfs root with a swapfile, auto unlock with TPM2, secureboot and a BIOS password.
It would be nice if someone could help me set up a disko partition layout, but for now I just do manual partitioning with a 1024 MB FAT32 boot partition and the rest as an encrypted BtrFS root partition. Other root partitions will also work but my personal choice is BtrFS right now.
If you already have a system installed with ext4, here’s an easy to follow guide to convert to btrfs.
During the installation you should select disk encryption which will set it up for you. The new installer uses LUKS2 by default, however if you already have a LUKS1 encypted volume, which doesn’t work with TPM unlock, this simple guide shows you how to upgrade it to LUKS2 and switch to a more secure algorithm after the installation while you’re still on the live USB.
After rebooting into the installed system, I would start a nix-shell with git and git-crypt, clone my repo to my home directory, and unlock git-crypt with a key that I exported from another machine. The repo has a top level flake.nix which contains configurations for all of my computers based on the hostname, my home manager setup, and even the shell setup for the repo, note that using nix-shell to clone works just as well.
Assuming that this is the first time I’m bringing up a new device, I would just create a folder for it within “devices” and add a conf.nix and a hw.nix. I would copy the contents of the auto generated /etc/nixos/hardware-configuration.nix into the local hw.nix. For the conf.nix, something like this:
{ config, pkgs, pconf, nixos-hardware, ... }: {
imports = [
./hw.nix
nixos-hardware.nixosModules.my-device
../../modules/personal.nix
];
#
networking.hostName = "<device-name>";
# Swap
swapDevices = [ { device = "/var/swapfile"; size = 10*1024; } ]; # 10 = 8 GB RAM + 2 for padding
#
system.stateVersion = "24.11"; # Do not change
}
The swapfile will be automatically created based on the config.
Then add a new machine config in the flake which imports this conf.nix, like I did for the other 2 devices. If the device already had a working config and needs to be brought up after a fresh clean reinstall, then there is no need to recreate these 2 config files or to re-add it to the flake.
Then I would remove both files from /etc/nixos/ and create a symlink of my flake to /etc/nixos/flake.nix. There is also a symlink for my home manager configuration. The commands for both are in my README.
At this point we’re still missing the secure boot signing keys, so the setup won’t build. Here’s a short guide to set up secureboot. First I would open a nix shell with “sbctl” and run the key generation command, then the build should already work. If you set up the symlinks, using the standard sudo nixos-rebuild switch
works.
Enrolling the secureboot key was a different process for all my machines. On the surface I just ran the enroll command and it just worked after that. On the tower I had to turn ON the Windows 10 WHQL settings in BIOS, switch a newly appeared setting from CSM to UEFI and then put secureboot in a setup state, then reboot and run the enroll command. On the Tuxedo I had to put secureboot into “custom” mode, disable auto enroll of factory keys, and then put it in setup mode. After enabling secureboot I put a BIOS password on both machines, but I’m not sure if that significantly improves security.
After enrolling the keys and enabling secureboot, make sure to reboot and verify that it works according to the guide.
Finally I enrolled the disk decryption key in the TPM chip. To be secure, make sure to also add PCR15 with value 0 and add tpm2-measure-pcr=yes to crypttabExtraOpts.
systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs="0+2+3+5+7+8+12+13+14+15:sha256=0000000000000000000000000000000000000000000000000000000000000000" /dev/<root-partition>
# Modify HW config
boot.initrd.luks.devices."luks-<...>" = {
device = "/dev/disk/by-uuid/<...>";
crypttabExtraOpts = [ "tpm2-device=auto" "tpm2-measure-pcr=yes" ];
};
The cryptenroll command and another command to find the name of your encrypted partition are both in my README.
At this point, you should be able to reboot with secureboot enabled and only have to enter your login password, not encryption password. Depending on your hardware, this setup is fairly secure. As long as your nix configuration doesn’t install malicious software. If an attacker tampers with your unencrypted boot, secureboot will fail, and if they disable secureboot or load a live USB the TPM won’t decrypt your root partition, so they won’t be able to access your data. If they backup and replace your encrypted root partition with an unencrypted malicious setup and then try to unlock the backup with the TPM, it will fail the PCR 15 check thanks to binding it to value 0 and measuring the unlocked system in it after unlocking it.
If they steal your laptop, modify your boot partition, get past your BIOS password, disable secure boot, and return your laptop without you noticing, then you unknowingly turn it on, not notice that secure boot is off, then enter the encryption key to unlock your drive, then they could gain access to your data. Or using the cold boot attack…
After you confirmed that everything works, nix-collect-garbage -d
can free up some space and make your boot menu cleaner by erasing old boot generations and unused packages. If you added programs.nh
then nh clean all
is more effective.
Note, the declarative email/calendar in the config doesn’t work, hopefully in the future it will. I would also like to add impermanence to this setup (open to suggestions), but I’m not there yet.