mirror of
https://github.com/sgillespie/nixos-yubikey-luks.git
synced 2024-09-11 09:55:51 +03:00
207 lines
7.6 KiB
Markdown
207 lines
7.6 KiB
Markdown
# LUKS-Encrypted Filesystem with Yubikey PBA
|
|
In this guide, we describe how to set up an encrypted filesystem with Yubikey pre-boot authentication (PBA) on NixOS. While the focus is on NixOS, the same techniques should be able to be used on any Linux system where Linux Unified Key Setup (LUKS) is available.
|
|
|
|
This guide is inspired by and based on [Yubikey based Full Disk Encryption (FDE) on NixOS](https://nixos.wiki/wiki/Yubikey_based_Full_Disk_Encryption_(FDE)_on_NixOS).
|
|
|
|
Other methods exist for other Linux distributions:
|
|
|
|
* ArchLinux: [Yubikey Full Disk Encryption](https://github.com/agherzan/yubikey-full-disk-encryption)
|
|
* Debian: [Yubikey for Luks](https://github.com/cornelinux/yubikey-luks)
|
|
|
|
## Design
|
|
We have the option of using either one (1FA) or two (2FA) factors for authentication. Using 1FA, the Yubikey must be inserted to open the LUKS device, but no extra passphrase is required. With 2FA, once the Yubikey is inserted, we'll be asked to enter a passphrase in order to open the LUKS device.
|
|
|
|
We'll program the Yubikey in Challenge-Response (HMAC-SHA1) mode in an alternate slot. Then we'll calculare the `salt` and `iterations` and store them on an unencrypted partition. These values will be used to calculate the challenge for the Yubikey. The response, along with a user-entered passphrase in 2FA, will be used to calculate the LUKS key.
|
|
|
|
At boot time, NixOSs Yubikey PBA will read the `salt` and `iterations`, which is again used to calculate the challenge. The Yubikey's response will be used to calculate the LUKS key. If we're using 2FA, we'll enter a passphrase which will be combined with the challenge-response key. If the key is successfully unlocked, NixOS will recalculate the `salt` and `iterations` values, and the expected Yubikey response. It will use the response to update the LUKS key so the passphrase is different at each time the machine is booted.
|
|
|
|
## Requirements
|
|
Before beginning the process, it's assumed that you have
|
|
|
|
* An unencrypted partition (Here we use ESP, but any partition is fine)
|
|
* A Yubikey with a free configuration slot
|
|
* A running NixOS system
|
|
|
|
### Setup
|
|
For convenience, I've created a Nix expression that includes all dependencies.
|
|
|
|
{ nixpkgs ? import <nixpkgs> {} }:
|
|
|
|
let
|
|
inherit (nixpkgs) fetchurl pkgs stdenv;
|
|
|
|
pbkdf2Sha512 = stdenv.mkDerivation {
|
|
name = "pbkdf2-sha512";
|
|
version = "latest";
|
|
buildInputs = [pkgs.openssl];
|
|
|
|
src = fetchurl {
|
|
url = "https://raw.githubusercontent.com/NixOS/nixpkgs/master/nixos/modules/system/boot/pbkdf2-sha512.c";
|
|
sha256 = "0pn5hh78pyh4q6qjp3abipivkgd8l39sqg5jnawz66bdzicag4l7";
|
|
};
|
|
|
|
unpackPhase = ":";
|
|
buildPhase = "cc -O3 -I${pkgs.openssl.dev}/include -L${pkgs.openssl.out}/lib ${./pbkdf2-sha512.c} -o pbkdf2-sha512 -lcrypto";
|
|
installPhase = ''
|
|
mkdir -p $out/bin
|
|
install -m755 ${./pbkdf2-sha512} $out/bin/pbkdf2-sha512
|
|
'';
|
|
};
|
|
in
|
|
stdenv.mkDerivation {
|
|
name = "yubikey-luks-setup";
|
|
buildInputs = with pkgs; [
|
|
cryptsetup
|
|
openssl
|
|
pbkdf2Sha512
|
|
yubikey-personalization
|
|
];
|
|
|
|
shellHook = ''
|
|
rbtohex() {
|
|
( od -An -vtx1 | tr -d ' \n' )
|
|
}
|
|
|
|
hextorb() {
|
|
( tr '[:lower:]' '[:upper:]' | sed -e 's/\([0-9A-F]\{2\}\)/\\\\\\x\1/gI'| xargs printf )
|
|
}
|
|
'';
|
|
|
|
|
|
inherit (pkgs) cryptsetup openssl yubikey-personalization;
|
|
}
|
|
|
|
Add the above snippet to a new file `yubikey-luks-setup.nix` and enter a nix shell
|
|
|
|
nix-shell yubikey-luks-setup.nix
|
|
|
|
## Setup - Manual
|
|
If you don't want to use the nix expression, we can set up the same environment manually.
|
|
|
|
You'll need the following software dependencies:
|
|
|
|
* cryptsetup
|
|
* gcc
|
|
* openssl
|
|
* pbkdf2-sha512
|
|
* yubikey-personalization
|
|
|
|
`pkdf2-sha512` is a simple program included in nixpkgs that exposes OpenSSLs PKDF2 implementation. Grap the source file at https://raw.githubusercontent.com/NixOS/nixpkgs/master/nixos/modules/system/boot/pbkdf2-sha512.c and compile it.
|
|
|
|
Finally, we need a couple of bash helper functions.
|
|
|
|
rbtohex() {
|
|
( od -An -vtx1 | tr -d ' \n' )
|
|
}
|
|
|
|
hextorb() {
|
|
( tr '[:lower:]' '[:upper:]' | sed -e 's/\([0-9A-F]\{2\}\)/\\\\\\x\1/gI'| xargs printf )
|
|
}
|
|
|
|
Add these to a shell file and source it.
|
|
|
|
## Procedure
|
|
### Step 1 - Program the Yubikey Free Slot
|
|
Program the Yubikey's free slot challenge-response mode (HMAC-SHA1). We let the Yubikey generate a key for us.
|
|
|
|
ykpersonalize -2 -ochal-resp -ochal-hmac
|
|
|
|
### Step 2 - Create a New Partition
|
|
Create a new partition. As an example, we'll create a new 100G partition on sdb starting at 208G.
|
|
|
|
parted /dev/sdb -- mkpart primary 208G 308G
|
|
|
|
### Step 3 - Calculate the LUKS Passphrase
|
|
Generate the initial salt. It can be any integer value between 16 and 64.
|
|
|
|
SALT_LENGTH=16
|
|
SALT="$(dd if=/dev/random bs=1 count=$SALT_LENGTH 2>/dev/null | rbtohex)"
|
|
|
|
Enter the 2FA passphrase, if desired
|
|
|
|
read -s USER_PASSPHRASE
|
|
|
|
Calculate the initial challenge and response
|
|
|
|
CHALLENGE="$(echo -n $SALT | openssl dgst -binary -sha512 | rbtohex)"
|
|
RESPONSE=$(ykchalresp -2 -x $CHALLENGE 2>/dev/null)
|
|
|
|
Calculate the LUKS slot key from the desired factors
|
|
|
|
KEY_LENGTH=512
|
|
ITERATIONS=1000000
|
|
|
|
If you want to use 2FA
|
|
|
|
LUKS_KEY="$(echo -n $USER_PASSPHRASE | pbkdf2-sha512 $(($KEY_LENGTH / 8)) $ITERATIONS $RESPONSE | rbtohex)"
|
|
|
|
Otherwise
|
|
|
|
LUKS_KEY="$(echo | pbkdf2-sha512 $(($KEY_LENGTH / 8)) $ITERATIONS $RESPONSE | rbtohex)"
|
|
|
|
### Step 4 - Create the LUKS device
|
|
Create the LUKS device.
|
|
|
|
CIPHER=aes-xts-plain64
|
|
HASH=sha512
|
|
|
|
As an example, we'll use the partition we created in Step 1: `/dev/sdb5`.
|
|
|
|
echo -n "LUKS_KEY" | hextorb | cryptsetup luksFormat --cipher="$CIPHER" \
|
|
--key-size="$KEY_LENGTH" --hash="$HASH" --key-file=- /dev/sdb5
|
|
|
|
### Step 5 - Store Salt and Iterations
|
|
Store the salt and iterations on an unencrypted partition. Here, we use the `ESP` partition mounted on `/boot`
|
|
|
|
mkdir -p /boot/crypt-storage
|
|
echo -ne "$SALT\n$ITERATIONS" > /boot/crypt-storage/default
|
|
|
|
### Step 6 - Open the LUKS device
|
|
Open the LUKS device. As an example, we again use /dev/sdb5.
|
|
|
|
cryptsetup open --type=luks /dev/sdb5 encrypted
|
|
|
|
We can now access the volume at `/dev/mapper/encrypted`. For example, to format it as ext4
|
|
|
|
mkfs.ext4 /dev/mapper/encrypted
|
|
|
|
### Step 7 - Update NixOS Configuration
|
|
Open up your hardware configuration at `/etc/nixos/hardware-configuration.nix` and set up the new LUKS-encrypted disk
|
|
|
|
boot.initrd = {
|
|
# Required to open the EFI partition and Yubikey
|
|
kernelModules = ["vfat" "nls_cp437" "nls_iso8859-1" "usbhid"];
|
|
|
|
luks = {
|
|
# Update if necessary
|
|
cryptoModules = ["aes" "xts" "sha512"];
|
|
|
|
# Support for Yubikey PBA
|
|
yubikeySupport = true;
|
|
|
|
devices."encrypted" = {
|
|
device = "/dev/sdb5"; # Be sure to update this to the correct volume
|
|
|
|
yubikey = {
|
|
slot = 2;
|
|
twoFactor = true; # Set to false for 1FA
|
|
gracePeriod = 30; # Time in seconds to wait for Yubikey to be inserted
|
|
keyLength = 16; # Set to $KEY_LENGTH/8
|
|
saltLength = 16; # Set to $SALT_LENGTH
|
|
|
|
storage = {
|
|
device = "/dev/sdb1"; # Be sure to update this to the correct volume
|
|
fsType = "vfat";
|
|
path = "/crypt-storage/default";
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
### Step 8 - Reboot
|
|
Rebuild your NixOS configuration and reboot
|
|
|
|
nixos-rebuild boot # Rebuild NixOS configs and set as the default for next boot
|
|
reboot
|