25 Jan 2015
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 Mint.
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
script, 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
hook 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
13 Jul 2014
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.
09 Jul 2014
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
:
- Replace
sha256 = "0";
with src = "./.";
-
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.
-
Prepend with haskellPackages;
to the expression body, i.e.
with haskellPackages; cabal.mkDerivation (self: {
...
-
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 it, 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 is, 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:
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 elsewhere. 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.
23 May 2014
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 were: 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
With /boot
on an encrypted device, grub-mkconfig
should have GRUB load the necessary modules to decrypt and mount it.
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 parameter
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.