2014-05-14
Installing NixOS on a ThinkPad W540 with encrypted root
I recently got a ThinkPad W540 laptop and I'm trying out the NixOS Linux distribution:
NixOS is a GNU/Linux distribution that aims to improve the state of the art in system configuration management. In existing distributions, actions such as upgrades are dangerous: upgrading a package can cause other packages to break, upgrading an entire system is much less reliable than reinstalling from scratch, you can't safely test what the results of a configuration change will be, you cannot easily undo changes to the system, and so on.
I use the Nix package manager alongside other distributions and decided to try out the full operating system. This post outlines the steps I took to install NixOS with full disk encryption using LVM on LUKS.
Windows
The W540 comes with Windows 8.1 pre-installed and recovery partitions to enable rebuilding the system. I followed the install procedure to get Windows working and proceeding to make a recovery USB drive so I could get back to the starting state if things went wrong. Once this completed I went on with installing NixOS.
NixOS Live CD
I used the NixOS Graphical Live CD to install. I could have used the minimal CD but I went fo the graphical option to make sure the basic OS worked fine on the hardware. I installed the Live CD to a USB stick from another Linux machine using unetbootin.
To boot from this I had to change the W540 BIOS settings:
- Change the USB drive in the boot sequence so it was the first boot option.
- Disable Secure Boot.
- Change UEFI to be UEFI/Legacy Bios from the previous UEFI only setting.
Booting from the USB drive on the W540 worked fine and got me to a login prompt. Logging in with root
and no password gives a root shell. Installation can proceed from there or the GUI can be started with start display-manager
.
Networking
The installation process requires a connected network. I used a wireless network. This is configured in the Live CD using wpa_supplicant. This required editing /etc/wpa_supplicant.conf
to contain the settings for the network I was connecting to. For a public nework it was something like:
network={
ssid="My Network"
key_mgmt=NONE
}
The wpa_supplicant
service needs to be restarted after this:
# systemctl restart wpa_supplicant.service
It's important to get the syntax of the wpa_supplicant.conf
file correct otherwise it will fail to restart with no visible error.
Partition the disk
Partitioning is done manually using gdisk. Three partitions are needed:
- A small partition to hold GPT information and provide a place for GRUB to store data. I made this 1MB in size and it must have a partition type of
ef02
. This was/dev/sda1
. - An unencrypted boot partition used to start the initial boot, and load the encrypted partition. I made this 1GB in size (which is on the large side for what it needs to be) and left it at the partition type
8300
. This was/dev/sda2
. - The full disk encrypted partition. This was set to the size of the rest of the drive and partition type set to
8e00
for "Linux LVM". This was/dev/sda3
.
Create encrypted partitions
Once the disk is partitioned above we need to encrypt the main root partition and use LVM to create logical partitions within it for swap and root:
# cryptsetup luksFormat /dev/sda3
# cryptsetup luksOpen /dev/sda3 enc-pv
# pvcreate /dev/mapper/enc-pv
# vgcreate vg /dev/mapper/enc-pv
# lvcreate -L 40G -n swap vg
# lvcreate -l 111591 -n root vg
The lvcreate
commands create the logical partitions. The first is a 40GB swap drive. The laptop has 32GB of memory so I set this to be enough to store all of memory when hibernating plus extra. It could be made quite a bit smaller. The second creates the root partition. I use the -l
switch there to set the exact number of extents for the size. I got this number by trying a -L
with a larger size than the drive and used the number in the resulting error message.
Format partitions
The unencrypted boot partition is formatted with ext2
and the root partition with ext4
:
# mkfs.ext2 -L boot /dev/sda2
# mkfs.ext4 -O dir_index -j -L root /dev/vg/root
# mkswap -L swap /dev/vg/swap
These should be mounted for the install process as follows:
# mount /dev/bg/root /mnt
# mkdir /mnt/boot
# mount /dev/sda2 /mnt/boot
# swapon /dev/vg/swap
Configure NixOS
NixOS uses a declarative language for the configuration file that is used to install and configure the operating system. An initial file ready to be edited should be created with:
$ nixos-generate-config --root /mnt
This creates the following files:
/mnt/etc/nixos/configuration.nix
/mnt/etc/nixos/hardware-configuration.nix
The latter file is rewritten everytime this command is run. The first file can be edited and is never rewritten. For the initial boot I had to make one change to hardware-configuration.nix
. I commented out this line:
# services.xserver.videoDrivers = [ "nvidia" ];
I can re-add it later when configuring the X server if I want to use the nvidia
driver.
The changes that need to be made to configuration.nix
involve setting the GRUB partition, the Luks data and any additional packages to be installed. The Luks settings I added were:
boot.initrd.luks.devices = [
{
name = "root"; device = "/dev/sda3"; preLVM = true;
}
];
I changed the GRUB boot loader device to be:
boot.loader.grub.device = "/dev/sda";
To enable wireless I made sure I had:
networking.wireless.enable = true;
I added my preferred editor, vim, to the system packages:
environment.systemPackages = with pkgs; [
vim
];
Enable OpenSSH:
services.openssh.enable = true;
I've left configuring X and other things for later.
Install NixOS
To install based on the configuration made above:
# nixos-install
If that completes successfully the system can be rebooted into the newly installed NixOS:
# reboot
You'll need to enter the encryption password that was created during cryptsetup
when rebooting.
Completing installation
Once rebooted re-enable the network by performing the /etc/wpa_supplicant.conf
steps done during the install.
Installation of additional packages can continue following the NixOS manual. This mostly involves adding or changing settings in /etc/nixos/configuration.nix
and then running:
# nixos-rebuild switch
This is outlined in Changing the Configuration in the manual.
Troubleshooting
The most common errors I made were syntax errors in wpa_supplicant.conf
and configuration.nix
. The other issue I had was not creating the initial GPT partition. GRUB will give an error in this case explaining the issue. You can reboot the Live USB drive at any time and mount the encrypted drives to edit files if needed. The commands to mount the drives are:
# cryptsetup luksOpen /dev/sda3 enc-pv
# vgscan --mknodes
# vgchange -ay
# mount /dev/bg/root /mnt
# mkdir /mnt/boot
# mount /dev/sda2 /mnt/boot
# swapon /dev/vg/swap
Tips
environment.systemPackages
in /etc/configuration.nix
is where you add packages that are seen by all users. When this is changed you need to run the following for it to take effect:
# nixos-rebuild switch
To find the package name to use, run something like (for vim):
$ nix-env -qaP '*'|grep vim
A user can add their own packages using:
$ nix-env -i vim
And remove with:
$ nix-env -e vim
A useful GUI for connecting to wireless networks is wpa_gui
. To enable this add wpa_supplicant_gui
to environment.systemPackages
in /etc/nixos/configuration.nix
followed by a nixos-rebuild switch
. Add the following line to /etc/wpa_supplicant.conf
:
ctrl_interface=/var/run/wpa_supplicant
Restart wpa_supplicant
and run the gui:
$ systemctl restart wpa_supplicant.service
$ sudo wpa_gui
It's possible to make custom changes to Nix packages for each user. This is controlled by adding definitions to ~/.nixpkgs/config.nix
. The following config.nix
will provide Firefox with the official branding:
{
packageOverrides = pkgs : with pkgs; rec {
firefoxPkgs = pkgs.firefoxPkgs.override { enableOfficialBranding = true; };
};
}
Installing or re-installing for the user will use this version of Firefox:
$ nix-env -i firefox