NixOs install UEFI Luks-on-LVM

Posted on December 24, 2018 by ngyj

Just documenting my installation. There’s nothing really special, and the manual is fairly exhaustive, but maybe it can help out someone who never did any (semi-)manual OS installation. It is actually based of: https://wiki.archlinux.org/index.php/Dm-crypt/Encrypting_an_entire_system#LUKS_on_LVM .

To be honest, if there’s something you don’t understand you really should try to look at the --help and/or the manpages: things start to slowly make sense when you stare at them long enough and these are valuable sources of information. Also don’t hesitate to try out things yourself, the worst that can happen is restarting the entire installation. (I’m saying this because I remember my first time installing arch, I had this fear that if I entered some incorrect command everything would explode…) Just don’t forget to backup your personal data, or even better, don’t keep it near the installation.

Boot from installation media

liveusb

Making the installation media comes down to:

$ dd if=nix.iso of=/dev/usb bs=1M status=progress

use lsblk or df to identify devices.

post-boot

Check for internet connection (see here, here, or even here and well try to look for examples on google I guess). Personnally I’m wired and it worked out of the box.

Decide on a partition scheme, for me it will be this:

NAME            SIZE TYPE  MOUNTPOINT
sda           232.9G disk
├─sda1          512M part  /boot
└─sda2        232.4G part
  ├─vg0-swap      8G lvm   [SWAP]
  ├─vg0-nixos    50G lvm
  │ └─nixos      50G crypt /
  └─vg0-home  174.4G lvm
    └─home    174.4G crypt /home

I’m using Luks-on-LVM. If you’re reading this it’s probably the same for you. But if that’s not the case, the reason for this choice is so I can encrypt my root and home parition separately. That way I can boot from my other OS and mount the /home alone (without the root partition) and if I ever want to install another distro but keep my /home I can also do that very easily.

prepare Disks

paritionning

I’ll use gdisk here.

$ gdisk /dev/sda

-- create new partition table
o > y
-- efi boot partition
n > default(ENTER) > 512M > ef00 > p
-- lvm parition (everything else)
n > default(ENTER) > default > 8300 > p
-- write changes
w > y

We only need 2 partitions, the rest will be done with LVM.

prepare LVMs

$ pvcreate /dev/sda2
$ vgcreate vg0 /dev/sda2
$ lvcreate -L 8G vg0 -n swap
$ lvcreate -L 50G vg0 -n nixos
$ lvcreate -l +100%FREE vg0 -n home

Nothing special here, we just init /dev/sda2, make a volume group called vg0 on it and add:

  • a 8G volume named swap
  • a 50G volume named nixos
  • a volume named home with the remaining space

crypt, format & mount the root-parition

Here we encrypt the root partition (vg0-nixos) with a passphrase, and open (decrypt) it, we name the decrypted volume nixos. Since it’s still a blank volume, we need to format it with our filesystem of choice. Personnally I decided to try out xfs. The default on linux tends to be ext4 though, if that’s what you’d prefer use mkfs.ext4.

$ cryptsetup luksFromat -c aes-xts-plain64 -y -s 512 /dev/mapper/vg0-nixos
$ cryptsetup open /dev/mapper/vg0-nixos nixos
$ mkfs.xfs -L NIXROOT /dev/mapper/nixos # or mkfs.ext4
$ mount /dev/mapper/nixos /mnt

btw if your install failed and you need to reboot from a liveusb, instead of redoing the entire installation process you can just mount the disks again by doing :

$ cryptsetup open /dev/mapper/vg0-nixos nixos
$ mount /dev/mapper/nixos /mnt

that way you don’t have to repartition and reformat everything, you can directly mount/open the volumes. Depending on your problem, it also saves you from reinstalling everything (because repartition/formatting wipe all your data, you would have to do that step everytime you had a problem too).

boot-parition

We start by erasing everything on the boot partition. Just in case.

$ dd if=/dev/zero of=/dev/sda1 bs=1M status=progress

Since we’re on UEFI, we need to format the parition with fat32. We then mount it on /mnt/boot.

$ mkfs.fat -F 32 -n boot /dev/sda1
$ mkdir /mnt/boot
$ mount /dev/sda1 /mnt/boot

swap

Nothing magic here, just use the normal commands for making swap.

$ mkswap -L swap /dev/mapper/vg0-swap
$ swapon /dev/mapper/vg0-swap

Voilà, disks are ready. We’ll do /home later.

Install NixOS

Now we’ll let nix do some work for us:

$ nixos-generate-config --root /mnt
$ vim /mnt/etc/nixos/configuration.nix

This is a very minimal configuration, we just want to install nixos, reboot and see if everything’s okay so far (easier to troubleshoot one thing at a time). We’ll have to re-edit the file for our /home, and to customize our install later anyway.

{ config, pkgs, ... }:
{
  imports = [ ./hardware-confguration.nix ];
  networking.hostName = "foo";

  # these might be defaults
  boot.loader.grub.enable = true;
  boot.loader.grub.version = 2;
  boot.loader.grub.efiSupport = true;
  boot.loader.systemd-boot.enable = true;
  boot.loader.efi.canTouchEfiVariables = true;

  # might use a decent editor for our next edit
  environment.systemPackages = with pkgs; [
    vim
  ];
}

Now’s the time to configure your internet connection, and make some offerings to the Lord. If this hasn’t been done already!

$ nixos-install
$ reboot

Configure

Now the reason we delayed the creation of the /home is so we could troubleshoot it on its own if we messed up. But the whole NixOs generations makes it a breeze though… The plan is to encrypt /home but without actually having to give a passphrase for it. We decrypt our /home with a keyfile instead, stored in the root partition. Remember we need a passphrase to decrypt the root parition. So once we decrypted root, the system can use the keyfile to decrypt our home. Luks devices can have multiple keys, so if you share the home partition with multiple distros, each one of them can have a separate keyfile, so you don’t necessarily have to copy your keyfile around. And you can add a normal passphrase, just in case you lose the keyfile. That way we only have one passphrase to enter on boot, but we can’t decrypt /home without giving a passphrase.

First, make the key:

$ mkdir -m 700 /luks-keys
$ dd if=/dev/urandom of=/luks-keys/home bs=1 count=256 status=progress

then we create, format and mount the volume:

$ cryptsetup luksFormat -v /dev/mapper/vg0-home /luks-keys/home
$ cryptsetup -d /luks-keys/home -y /dev/mapper/vg0-home # also make a passphrase
$ cryptsetup -d /luks-keys/home open /dev/mapper/vh0-home home
$ mkfs.xfs -L HOME /dev/mapper/home
$ mount /dev/mapper/home home

The second command is optional, I made a passphrase just in case I decide to install a new system but forget to backup the keyfile, or make a new one. So I can always access my /home without the root-partitions and the keyfile on them. Or in case my root gets nuked and I still want to save the home, idk pick your disaster I guess.

Another run of:

$ nixos-generate-config

Now we need to comment out the following line in the /etc/nixos/hardware-configuration.nix

# boot.initrd.luks.devices."home".device = "/dev/disk/by-uuid/<UUID>"

and instead decrypt the disk via crypttab, in the /etc/nixos/configuration.nix:

systemd.generator-packages = [ pkgs.systemd-cryptsetup-generator ];
environment.etc = {
  "crypttab" = {
    enable = true;
    text = ''
home UUID=<UUID> /luks-keys/home luks
    '';
  };
}

From what I understand all the boot.initrd.luks.devices are decrypted before the root’s mounted. Which is not what we want, our keyfile is on the root. That’s why we use crypttab instead. If you know of another solution, please tell me, I’d be happy to hear it.

tip: If you want to reencrypt your swap at each reboot, you can also do that here:

swap /dev/mapper/vg0-swap /dev/urandom swap,cipher=aes-xts-plain64,size=256

Again:

$ nixos-rebuild switch
$ reboot

pray and check if your /home is correctly mounted with lsblk after reboot. Check the uuids and make sure you only gave your passphrase for the root partition.

Post-install

Voilà that’s the basic Luks-on-LVM UEFi installation. From here on it’s just modifying the configuration.nix to your needs. Plenty of stuff covers that already. Some places you can start: