Index » Community Contributions » System encryption using LUKS and GPG encrypted keys for arch linux


Using arch linux mkinitcpio's encrypt hook, one can easily use encrypted root partitions with LUKS. It's also possible to use key files stored on an external drive, like an usb stick. However, if someone steals your usb stick, he can just copy the key and potentially access the system. I wanted to have a little extra security by additionally encrypting the key file with gpg using a symmetric cipher and a passphrase.
Since the encrypt hook doesn't support this scenario, I created a modifed hook called “etwo” (silly name I know, it was the first thing that came to my mind). It will simply look if the key file has the extension .gpg and, if yes, use gpg to decrypt it, then pipe the result into cryptsetup.


In this short guide, I use the following disk/partition names:
/dev/sda: is the hard disk that will contain an encrypted swap (/dev/sda1), /var (/dev/sda2) and root (/dev/sda3) partition.
/dev/sdb is the usb stick that will contain the gpg encrypted luks keys, the kernel and grub. It will have one partition /dev/sdb1 formatted with ext2.
/dev/mapper/root, /dev/mapper/swap and /dev/mapper/var will be the encrypted devices.


Thanks to the authors of SECURITY_System_Encryption_DM-Crypt_with_LUKS (gentoo wiki), System Encryption with LUKS (arch wiki), mkinitcpio (arch wiki) and Early Userspace in Arch Linux (/dev/brain0 blog)!


1. Boot the arch live cd
I had to use a newer testing version, because the 2010.05 cd came with a broken gpg. You can download one here: I chose the “core“ version. Go ahead and boot the live cd, but don't start the setup yet.

2. Set keymap
Use km to set your keymap. This is important for non-qwerty keyboards to avoid suprises with passphrases...

3. Wipe your discs
ATTENTION: this will DELETE everything on /dev/sda and /dev/sdb forever! Do not blame me for any lost data!
Before encrypting the hard disc, it has to be completely wiped and overwritten with random data. I used shred for this. Others use badblocks or dd with /dev/urandom. Either way, this will take a long time, depending on the size of your disc. I also wiped my usb stick just to be sure.

shred -v /dev/sda
shred -v /dev/sdb

4. Partitioning
Fire up fdisk and create the following partitions:

  • /dev/sda1, type linux swap.

  • /dev/sda2: type linux

  • /dev/sda3: type linux

  • /dev/sdb1, type linux

Of course you can choose a different layout, this is just how I did it. Keep in mind that only the root filesystem will be decrypted by the initcpio. The rest will be decypted during normal init boot using /etc/crypttab, the keys being somewhere on the root filesystem.

5. Format  and mount the usb stick
Create an ext2 filesystem on /dev/sdb1:

mkfs.ext2 /dev/sdb1
mkdir /root/usb
mount /dev/sdb1 /root/usb
cd /root/usb # this will be our working directory for now.

Do not mount anything to /mnt, because the arch installer will use that directory later to mount the encrypted root filesystem.

6. Configure the network (if not already done automatically)

ifconfig eth0 netmask
route add default gw
echo "nameserver" >> /etc/resolv.conf

(this is just an example, your mileage may vary)

7. Install gnupg

pacman -Sy
pacman -S gnupg

Verify that gnupg works by launching gpg.

8. Create the keys
Just to be sure, make sure swap is off:

cat /proc/swaps

should return no entries.

Create gpg encrypted keys (remember, we're still in our working dir /root/usb):

dd if=/dev/urandom bs=512 count=4 | gpg -v --cipher-algo aes256 --digest-algo sha512 -c -a  > root.gpg
dd if=/dev/urandom bs=512 count=4 | gpg -v --cipher-algo aes256 --digest-algo sha512 -c -a > var.gpg

Choose a strong password!!

Don't do this in two steps, e.g don't do dd to a file and then gpg on that file. The key should never be stored in plain text on an unencrypted device, except if that device is wiped on system restart (ramfs)!

Note that the default cipher for gpg is cast5, I just chose to use a different one.

9. Create the encrypted devices with cryptsetup
Create encrypted swap:

cryptsetup -c aes-cbc-essiv:sha256 -s 256 -h whirlpool -d /dev/urandom create swap /dev/sda1

You should see /dev/mapper/swap now. Don't format nor turn it on for now. This will be done by the arch installer.

Important: From the Cryptsetup 1.1.2 Release notes:

Cryptsetup can accept passphrase on stdin (standard input). Handling of new line (\n) character is defined by input specification:
    if keyfile is specified as "-" (using --key-file=- or by positional argument in luksFormat and luksAddKey, like cat file | cryptsetup --key-file=- <action> ), input is processed
      as normal binary file and no new line is interpreted.
    if there is no key file specification (with default input from stdin pipe like echo passphrase | cryptsetup <action> ) input is processed as input from terminal, reading will
      stop after new line is detected.

If I understand this correctly, since the randomly generated key can contain a newline early on, piping the key into cryptsetup without specifying --key-file=- could result in a big part of the key to be ignored by cryptsetup. Example: if the random key was "foo\nandsomemorebaratheendofthekey", piping it directly into cryptsetup without --key-file=- would result in cryptsetup using only "foo" as key which would have big security implications. We should therefor ALWAYS pipe the key into cryptsetup using --key-file=- which ignores newlines.

gpg -q -d root.gpg 2>/dev/null | cryptsetup -v -–key-file=- -c aes-cbc-essiv:sha256 -s 256 -h whirlpool luksFormat /dev/sda3
gpg -q -d var.gpg 2>/dev/null | cryptsetup -v –-key-file=- -c aes-cbc-essiv:sha256 -s 256 -h whirlpool -v luksFormat /dev/sda2

Check for any errors.

10. Open the luks devices

gpg -d root.gpg 2>/dev/null | cryptsetup -v –-key-file=- luksOpen /dev/sda3 root
gpg -d var.gpg 2>/dev/null | cryptsetup -v –-key-file=- luksOpen /dev/sda2 var

If you see /dev/mapper/root and /dev/mapper/var now, everything is ok.

11. Start the installer /arch/setup
Follow steps 1 to 3.
At step 4 (Prepare hard drive(s), select “3 – Manually Configure block devices, filesystems and mountpoints. Choose /dev/sdb1 (the usb stick) as /boot, /dev/mapper/swap for swap, /dev/mapper/root for / and /dev/mapper/var for /var.
Format all drives (choose “yes” when asked “do you want to have this filesystem (re)created”) EXCEPT for /dev/sdb1, choose “no”. Choose the correct filesystem for /dev/sdb1, ext2 in my case. Use swap for /dev/mapper/swap. For the rest, I chose ext4.

Select DONE to start formatting.

At step 5 (Select packages), select grub as boot loader. Select the base group. Add mkinitcpio.

Start step 6 (Install packages).

Go to step 7 (Configure System).
By sure to set the correct KEYMAP, LOCALE and TIMEZONE in /etc/rc.conf.

Edit /etc/fstab:

/dev/mapper/root / ext4 defaults 0 1
/dev/mapper/swap swap swap defaults 0 0
/dev/mapper/var /var ext4 defaults 0 1
# /dev/sdb1 /boot ext2 defaults 0 1

Configure the rest normally. When you're done, setup will launch mkinitcpio. We'll manually launch this again later.

Go to step 8 (install boot loader).
Be sure to change the kernel line in menu.lst:

kernel /vmlinuz26 root=/dev/mapper/root cryptdevice=/dev/sda3:root cryptkey=/dev/sdb1:ext2:/root.gpg

Don't forget the :root suffix in cryptdevice!

Also, my root line was set to (hd1,0). Had to change that to

root (hd0,0)

Install grub to /dev/sdb (the usb stick).

Now, we can exit the installer.

12. Install mkinitcpio with the etwo hook.
Create /mnt/lib/initcpio/hooks/etwo:


run_hook() {
    /sbin/modprobe -a -q dm-crypt >/dev/null 2>&1
    if [ -e "/sys/class/misc/device-mapper" ]; then
        if [ ! -e "/dev/mapper/control" ]; then
            /bin/mknod "/dev/mapper/control" c $(cat /sys/class/misc/device-mapper/dev | sed 's|:| |')
        [ "${quiet}" = "y" ] && CSQUIET=">/dev/null"

        # Get keyfile if specified
        if [ "x${cryptkey}" != "x" ]; then
            ckdev="$(echo "${cryptkey}" | cut -d: -f1)"
            ckarg1="$(echo "${cryptkey}" | cut -d: -f2)"
            ckarg2="$(echo "${cryptkey}" | cut -d: -f3)"
            if poll_device "${ckdev}" ${rootdelay}; then
                case ${ckarg1} in
                        # Use a file on the device
                        # ckarg1 is not numeric: ckarg1=filesystem, ckarg2=path
                        if [ "${ckarg2#*.}" = "gpg" ]; then
                        mkdir /ckey
                        mount -r -t ${ckarg1} ${ckdev} /ckey
                        dd if=/ckey/${ckarg2} of=${ckeyfile} >/dev/null 2>&1
                        umount /ckey
                        # Read raw data from the block device
                        # ckarg1 is numeric: ckarg1=offset, ckarg2=length
                        dd if=${ckdev} of=${ckeyfile} bs=1 skip=${ckarg1} count=${ckarg2} >/dev/null 2>&1
            [ ! -f ${ckeyfile} ] && echo "Keyfile could not be opened. Reverting to passphrase."
        if [ -n "${cryptdevice}" ]; then
            cryptdev="$(echo "${cryptdevice}" | cut -d: -f1)"
            cryptname="$(echo "${cryptdevice}" | cut -d: -f2)"

        warn_deprecated() {
            echo "The syntax 'root=${root}' where '${root}' is an encrypted volume is deprecated"
            echo "Use 'cryptdevice=${root}:root root=/dev/mapper/root' instead."

        if  poll_device "${cryptdev}" ${rootdelay}; then
            if /sbin/cryptsetup isLuks ${cryptdev} >/dev/null 2>&1; then
                [ ${DEPRECATED_CRYPT} -eq 1 ] && warn_deprecated
                # If keyfile exists, try to use that
                if [ -f ${ckeyfile} ]; then
                    if [ "${usegpg}" = "y" ]; then
                        # gpg tty fixup
                        if [ -e /dev/tty ]; then mv /dev/tty /dev/tty.backup; fi
                        cp -a /dev/console /dev/tty
                        while [ ! -e /dev/mapper/${cryptname} ];
                            sleep 2
                            /usr/bin/gpg -d "${ckeyfile}" 2>/dev/null | cryptsetup --key-file=- luksOpen ${cryptdev} ${cryptname} ${CSQUIET}
                        rm /dev/tty
                        if [ -e /dev/tty.backup ]; then mv /dev/tty.backup /dev/tty; fi
                        if eval /sbin/cryptsetup --key-file ${ckeyfile} luksOpen ${cryptdev} ${cryptname} ${CSQUIET}; then
                            echo "Invalid keyfile. Reverting to passphrase."
                # Ask for a passphrase
                if [ ${dopassphrase} -gt 0 ]; then
                    echo ""
                    echo "A password is required to access the ${cryptname} volume:"

                    #loop until we get a real password
                    while ! eval /sbin/cryptsetup luksOpen ${cryptdev} ${cryptname} ${CSQUIET}; do
                        sleep 2;
                if [ -e "/dev/mapper/${cryptname}" ]; then
                    if [ ${DEPRECATED_CRYPT} -eq 1 ]; then
                        export root="/dev/mapper/root"
                    err "Password succeeded, but ${cryptname} creation failed, aborting..."
                    exit 1
            elif [ -n "${crypto}" ]; then
                [ ${DEPRECATED_CRYPT} -eq 1 ] && warn_deprecated
                msg "Non-LUKS encrypted device found..."
                if [ $# -ne 5 ]; then
                    err "Verify parameter format: crypto=hash:cipher:keysize:offset:skip"
                    err "Non-LUKS decryption not attempted..."
                    return 1
                exe="/sbin/cryptsetup create ${cryptname} ${cryptdev}"
                tmp=$(echo "${crypto}" | cut -d: -f1)
                [ -n "${tmp}" ] && exe="${exe} --hash \"${tmp}\""
                tmp=$(echo "${crypto}" | cut -d: -f2)
                [ -n "${tmp}" ] && exe="${exe} --cipher \"${tmp}\""
                tmp=$(echo "${crypto}" | cut -d: -f3)
                [ -n "${tmp}" ] && exe="${exe} --key-size \"${tmp}\""
                tmp=$(echo "${crypto}" | cut -d: -f4)
                [ -n "${tmp}" ] && exe="${exe} --offset \"${tmp}\""
                tmp=$(echo "${crypto}" | cut -d: -f5)
                [ -n "${tmp}" ] && exe="${exe} --skip \"${tmp}\""
                if [ -f ${ckeyfile} ]; then
                    exe="${exe} --key-file ${ckeyfile}"
                    exe="${exe} --verify-passphrase"
                    echo ""
                    echo "A password is required to access the ${cryptname} volume:"
                eval "${exe} ${CSQUIET}"

                if [ $? -ne 0 ]; then
                    err "Non-LUKS device decryption failed. verify format: "
                    err "      crypto=hash:cipher:keysize:offset:skip"
                    exit 1
                if [ -e "/dev/mapper/${cryptname}" ]; then
                    if [ ${DEPRECATED_CRYPT} -eq 1 ]; then
                        export root="/dev/mapper/root"
                    err "Password succeeded, but ${cryptname} creation failed, aborting..."
                    exit 1
                err "Failed to open encryption mapping: The device ${cryptdev} is not a LUKS volume and the crypto= paramater was not specified."
        rm -f ${ckeyfile}

Create /mnt/lib/initcpio/install/etwo:


build() {
    local mod

    add_module dm-crypt
    if [[ $CRYPTO_MODULES ]]; then
        for mod in $CRYPTO_MODULES; do
            add_module "$mod"
        add_all_modules '/crypto/'
    add_dir "/dev/mapper"
    add_binary "cryptsetup"
    add_binary "dmsetup"
    add_binary "/usr/bin/gpg"
    add_file "/usr/lib/udev/rules.d/10-dm.rules"
    add_file "/usr/lib/udev/rules.d/13-dm-disk.rules"
    add_file "/usr/lib/udev/rules.d/95-dm-notify.rules"
    add_file "/usr/lib/initcpio/udev/11-dm-initramfs.rules" "/usr/lib/udev/rules.d/11-dm-initramfs.rules"

help ()
  This hook allows for an encrypted root device with support for gpg encrypted key files.
  To use gpg, the key file must have the extension .gpg and you have to install gpg and add /usr/bin/gpg
  to your BINARIES var in /etc/mkinitcpio.conf.

Edit /mnt/etc/mkinitcpio.conf (only relevant sections displayed):

MODULES=”ext2 ext4” # not sure if this is really nessecary.
BINARIES=”/usr/bin/gpg” # this could probably be done in install/etwo...
HOOKS=”base udev usbinput keymap autodetect pata scsi sata usb etwo filesystems” # (usbinput is only needed if you have an usb keyboard)

Copy the initcpio stuff over to the live cd:

cp /mnt/lib/initcpio/hooks/etwo /lib/initcpio/hooks/
cp /mnt/lib/initcpio/install/etwo /lib/initcpio/install/
cp /mnt/etc/mkinitcpio.conf /etc/

Verify your LOCALE, KEYMAP and TIMEZONE in /etc/rc.conf!

Now reinstall the initcpio:

mkinitcpio -g /mnt/boot/kernel26.img

Make sure there were no errors and that all hooks were included.

13. Decrypt the "var" key to the encrypted root

mkdir /mnt/keys
chmod 500 /mnt/keys
gpg –output /mnt/keys/var -d /mnt/boot/var.gpg
chmod 400 /mnt/keys/var

14. Setup crypttab
Edit /mnt/etc/crypttab:

swap    /dev/sda1    SWAP    -c aes-cbc-essiv:sha256 -s 256 -h whirlpool
var /dev/sda2    /keys/var

15. Reboot
We're done, you may reboot. Make sure you select the usb stick as the boot device in your bios and hope for the best. big_smile. If it didn't work, play with grub's settings or boot from the live cd, mount your encrypted devices and check all settings. You might also have less trouble by using uuid's instead of device names.  I chose device names to keep things as simple as possible, even though it's not the optimal way to do it.

Make backups of your data and your usb stick and do not forget your password(s)! Or you can say goodbye to your data forever...

0 (0)
Article Rating (No Votes)
Rate this article
There are no attachments for this article.
There are no comments for this article. Be the first to post a comment.
Full Name
Email Address
Security Code Security Code
Related Articles RSS Feed
How to enable automatic security updates on CentOS 7 with yum-cron
Viewed 1395 times since Fri, Oct 26, 2018
Nagrywanie sesji SSH do pliku
Viewed 1862 times since Thu, May 24, 2018
Linux - How to monitor memory usage
Viewed 1650 times since Fri, Jun 8, 2018
Linux / UNIX: Run Command a Number of Times In a Row
Viewed 13647 times since Tue, Aug 6, 2019
List of 10 Must Know Oracle Database Parameters for Database Administrator
Viewed 28562 times since Thu, Jun 21, 2018
10 Linux nslookup Command Examples for DNS Lookup
Viewed 9147 times since Sun, Sep 30, 2018
linux manual tools
Viewed 1399 times since Fri, Sep 28, 2018
ZPOOL: Detach a submirror from a mirrored zpool
Viewed 1583 times since Sun, Jun 3, 2018
Linux Kernel /etc/sysctl.conf Security Hardening
Viewed 21902 times since Fri, Aug 3, 2018
Monitoring bezpieczeństwa Linux: integracja auditd + OSSEC cz. I
Viewed 1373 times since Fri, Apr 5, 2019