Linux Mint encryption

In my previous post on full disk encryption I described how to avoid having to enter your passphrase twice. That method, however, only works on Arch. Here’s how to do it on Linux Mint1.

The initial setup process (with LVM, LUKS and GRUB) is the same as on Arch, but instead of editing /etc/mkinitcpio.conf, which doesn’t exist on Mint, create /etc/crypttab:

lvm /dev/sda1 none luks

So far, so good. The crypttab man page even talks about how you can point to a keyfile in the third column (“none” above). Unfortunately, if you do so, it doesn’t actually work. If you read the cryptroot script2, you find that the keyfile is only ever used as an argument to a keyscript.

Oddly enough, although there are several provided scripts, each doing various exotic things, none of them seem to handle the simple case of using the key to just decrypt the drive.

What these scripts have in common is that they take the “keyfile” as an argument, and their output is used as the key. So, all we need to do is provide a “script” that will take a filename and output the file’s contents:

lvm /dev/sda1 /crypto_keyfile.bin luks,keyscript=/bin/cat

Now, the cryptroot hook3 will copy the cat executable into the ramdisk, and during boot cat will send the keyfile’s contents to cryptsetup.

All that’s left is to ensure the keyfile is available before the drive is decrypted by copying it into the ramdisk too. There’s no convenient FILES option like in Arch, so you’ll have to make a custom hook. Luckily, it’s trivial:

#!/bin/sh
cp /crypto_keyfile.bin "${DESTDIR}"

Put it in /etc/initramfs-tools/hooks/ and make it executable:

chmod +x /etc/initramfs-tools/hooks/crypto_keyfile

Recreate the ramdisk:

update-initramfs -u

Check that everything is where it should be with lsinitramfs and reboot.

Update (02/08/15): Don’t forget that since the keyfile is stored on the ramdisk, you should make it only accessible by root, as well:

chmod -R g-rwx,o-rwx /boot

  1. It should also work with Debian/Ubuntu (or any distro that uses initramfs-tools

  2. in /usr/share/initramfs-tools/scripts/local-top/ 

  3. in /usr/share/initramfs-tools/hooks/ 

nix-shell on Linux Mint

After spending hours tearing out my hair trying to figure out why nix-shell wasn’t working on Linux Mint, which included digging through the Perl source code nix-shell is written in (I hate Perl), I find that it’s caused by a call to mint-fortune in /etc/bash.bashrc.

Fixed by commenting out one line. Damn it.

Haskell development with Nix

Haskell development with cabal was greatly improved by the addition of cabal sandboxes, but cabal hell and issues with non-Haskell dependencies persist. For example, building the gtk bindings is impossible with the latest version of cabal; I was forced to work around this by installing them with the system package manager. When the necessary packages at the right version are unavailable in the package manager though, an alternative solution needs to be found. Enter Nix.

I actually first encountered Nix a few years ago, trying out NixOS for a while before returning to better supported distributions. Even now, the downsides seem to outweigh the advantages, at least for a single-user system, but with recent attention to using Nix for Haskell development, I decided to try using it solely for that, without dedicating my entire system to NixOS.

nix-shell

The major draw of developing with Nix over cabal alone is being able to easily switch out which dependencies are used, without having to delete, rebuild, wait, worry about whether the builds will succeed with the new versions, and so on. The way this is done is by having nix-shell set up an environment where all the dependencies are available. Fortunately, enumerating the dependencies doesn’t have to be done by hand; the cabal2nix tool can be used to create Nix expressions from your project’s cabal file:

cabal2nix projectname.cabal --sha256=0 > shell.nix

The dummy sha256 hash is needed to prevent cabal2nix from trying to download the package from Hackage.

cabal2nix generated Nix expressions are intended to be added to nixpkgs, so a few edits are needed to have it work automatically with nix-shell:

  1. Replace sha256 = "0"; with src = "./.";
  2. Replace the list of function parameters ({ cabal, mtl, ... }:) with

    { haskellPackages ? (import <nixpkgs> {}).haskellPackages }:
    

    If any of the parameters are not Haskell packages, keep them in the list, but add a default value, as above.

  3. Prepend with haskellPackages; to the expression body, i.e.

    with haskellPackages; cabal.mkDerivation (self: {
    ...
    
  4. Optionally, if you don’t have cabal installed globally, add cabalInstall to buildTools. So, if no other build tools are used:

    buildTools = [ cabalInstall ];
    

A potential issue can arise if the name of a non-Haskell package you need conflicts with the name of a Haskell package. In that case, you need to move the with haskellPackages; inside the expression body, like so:

haskellPackages.cabal.mkDerivation (self: {
  ...
  buildTools = with haskellPackages; [ cabalInstall ];
  buildDepends = with haskellPackages; [ ... ];
  ...

Alternatively, add the package to extraLibraries:

extraLibraries = with import <nixpkgs> {}; [ pkgname ];

Now, calling nix-shell in the project directory will drop you into a shell with all the necessary packages in your $PATH, or you can hook up something like nix-shell --command='cabal build' to your build system.

Missing dependencies

Not all packages on Hackage are in nixpkgs. If your project depends on one of those, it’s possible to add it yourself. There are two ways to do this; you can clone the nixpkgs repository and modify it1, or you can just add it locally. I will describe the latter.

To add Nix expressions to the local nixpkgs collection, you need to create a ~/.nixpkgs/config.nix file. Now, the Nix wiki gives an outline of what this file should look like, but with the Nix documentation being as it is2, most of the functions and attributes used are undocumented, so I wasn’t able to tell how it’s supposed to work. Not being comfortable with copy-pasting an opaque block of code, I wrote a clearer version, which probably works differently, but does the job nonetheless:

{
  packageOverrides = pkgs: rec {
    haskellPackages = with pkgs.haskellPackages; pkgs.haskellPackages // rec {
      packageName = callPackage ./haskell/package-name.nix {};
      ...
    };
  };
}

With this, packages can be added much the same way they would be to the main nixpkgs collection:

  • Add the callPackage line to config.nix for the package, as above.
  • Use cabal2nix to generate the Nix expression:

    cabal2nix cabal://package-name > ~/.nixpkgs/haskell/package-name.nix
    

Simple.

Local package dependencies

If you want to have your project depend on a local package not on Hackage, that’s just as easy; all it takes is replacing the sha256 attribute with a src attribute pointing to the directory containing the package (as with shell.nix for nix-shell project support).

Conclusion

Using Nix expressions to set up your development environment can provide a lot of flexibility, allowing you to do things like switch between libraries with or without profiling, or even between compiler versions, which has been described elsewhere3. With this post I’ve shown, I hope, how a basic Nix setup can be created with very little up-front effort, streamlining workflow and providing a base to build on should that flexibility become needed.


  1. https://nixos.org/wiki/Contributing_to_nixpkgs 

  2. That is to say, not very good. 

  3. My use of Nix for Haskell development is heavily inspired by “How I Develop with Nix” and “My experience with NixOS”. 

Full disk encryption with LUKS (including /boot)

Update (25/01/15): I wrote a new post about how to achieve the same thing with Linux Mint.

While looking for information about how to encrypt my laptop’s hard drive, among the repeated claims that the partition on which /boot resides must remain unencrypted, I found the suggestion that GRUB should be able to handle cryptography since it can be set up with a hashed password.

Being too lazy to want to deal with a separate boot partition, I went looking to see what modules GRUB can load, and there they were1: crypto.mod, cryptodisk.mod and even luks.mod!

Since there don’t appear to be any instructions on how to fully encrypt a system including /boot, I’ve decided to make a short guide on how to do it.

This guide will describe setting up an encrypted Arch Linux system. The procedure is mostly distribution-agnostic, but note that anything involving mkinitcpio is Arch Linux specific and must be replaced if another distribution is used.

Set up partitions (LVM on LUKS)

This is well-documented elsewhere, so I won’t be explaining it. If something is unfamiliar to you, I recommend reading the ArchWiki page on the subject before proceeding.

parted -s /dev/sda mklabel msdos
parted -s /dev/sda mkpart primary 2048s 100%
cryptsetup luksFormat /dev/sda1
cryptsetup luksOpen /dev/sda1 lvm
pvcreate /dev/mapper/lvm
vgcreate vg /dev/mapper/lvm
lvcreate -L 4G vg -n swap
lvcreate -L 15G vg -n root
lvcreate -l +100%FREE vg -n home
mkswap -L swap /dev/mapper/vg-swap
mkfs.ext4 /dev/mapper/vg-root
mkfs.ext4 /dev/mapper/vg-home
mount /dev/mapper/vg-root /mnt
mkdir /mnt/home
mount /dev/mapper/vg-home /mnt/home

Install Linux

At this point you should be in a live system with all partitions mounted, so you can go ahead and run the install. Just be sure not to reboot once it’s done.

Don’t forget to add the lvm2 and encrypt hooks to /etc/mkinitcpio.conf and run

mkinitcpio -p linux

Configure GRUB

With /boot on an encrypted device, grub-mkconfig should have GRUB load the necessary modules to decrypt and mount it2.

grub-install, on the other hand, will refuse to work, complain about /boot being encrypted, and demand that GRUB_ENABLE_CRYPTODISK=1 be added to the config. This is a bug (in the error message). Instead, add

GRUB_ENABLE_CRYPTODISK=y

to /etc/default/grub. Now, before trying to find and load the initial ramdisk, GRUB will ask for a passphrase to decrypt /dev/sda1.

Finally, add the cryptdevice kernel parameter3

GRUB_CMDLINE_LINUX="cryptdevice=/dev/sda1:lvm"

and run

grub-mkconfig -o /boot/grub/grub.cfg
grub-install /dev/sda

Reboot, and that’s it. You now have a fully encrypted system.

Bonus: Login once

You’ve probably noticed that there remains the minor annoyance of having to decrypt your drive twice: once for GRUB and once for the kernel. Evidently, when GRUB passes control to the kernel, the encrypted drive is dismounted.

There is, however, a way to open a LUKS device without entering a passphrase: with a keyfile. The encrypt hook can take the file specified in the cryptkey kernel parameter (default: /crypto_keyfile.bin) and use it to unlock the cryptdevice.

dd bs=512 count=4 if=/dev/urandom of=/crypto_keyfile.bin
cryptsetup luksAddKey /dev/sda1 /crypto_keyfile.bin

I tried various methods to get GRUB to load the keyfile into memory and pass it to the kernel, without success. Then, I realised that the initrd image is itself something GRUB loads into memory, and mkinitcpio.conf has a very convenient FILES option…

FILES=/crypto_keyfile.bin

Run mkinitcpio again, and when you reboot, you’ll only need to enter your password once.

Security considerations

While the computer is off, the keyfile is stored inside the encrypted drive, so it is secure. When the computer is on, however, the keyfile is unencrypted, with a copy on the ramdisk. So, you should probably make sure only root can access these files:

chmod 000 /crypto_keyfile.bin  # actually, even root doesn't need to access this
chmod -R g-rwx,o-rwx /boot     # just to be safe

The keyfile will probably also be retained in memory, but so is the LUKS master key. If the attacker has enough access to your system for that to be a problem, encryption is moot. So long as there are no copies of the keyfile anywhere else, you should be fine. In other words, don’t back it up or reuse it elsewhere.


  1. in /boot/grub/i386-pc/ 

  2. If it doesn’t—check /boot/grub/grub.cfg—then you can add cryptodisk and luks to GRUB_PRELOAD_MODULES

  3. https://wiki.archlinux.org/index.php/GRUB#Root_encryption