Installing Arch Linux with System Encryption Enabled

I still use Arch Linux because it is pretty much the only rolling-release distro that offers binary (pre-compiled) packages by default *and* only installs bare minimum packages required to get a working Linux system (no window manager, no desktop environment). I was re-installing Arch Linux on my desktop last week to set up system-wide encryption and decided to record all of the commands I used to get the bare minimum working. I am not aware of any source out there that actually tells you all of the console commands you need to run when you first install Arch Linux in plaintext format so I decided to write this post.

The system I am setting up is simple: it has only 1 hard drive. This drive has 1 boot partition 100MB big, and the rest is a single LVM partition that will eventually contain the / (aka “root”), /home, and swap partitions. This single LVM partition will be encrypted, so that when the system boots, it would require a password to unlock the /, /home, and swap partitions to actually load up the Kernel and userland.

If you are wondering why the /home partition is mounted on /mnt/home in some of the commands, this is because the Arch Linux live CD environment will load up a temporary Linux system onto your RAM, and we use the /mnt directory (again all files/folders are temporarily in your RAM) to mount the hard drive partitions. You can always use a live CD anywhere to load up a barebone Linux system to your RAM and mount the hard drives on your computer for inspection, and in such a case you will again use the /mnt directory to mount your hard drives here.

If you don’t know how to use vi (tsk, tsk), then just substitute that command with “nano” or something. Here are the commands, with comments, in plaintext format:

    # Put in the Arch Linux live CD and run the following commands...

    # Set up 2 partitions; the first one is the boot 100MB partition (be sure
    # to toggle the BOOT flag on it), and the second will be the LVM partition.
    # Be sure to select 8E for the LVM partition! You can use
    # /dev/disk/by-uuid/XXXX for more fine-grained control; use "blkid -o list"
    # to find out which drives are given which UUIDs.

    cfdisk /dev/sda

    # Now set up encryption on the LVM partition (the 2nd partition, so it's
    # /dev/sda2). It is recommended to use aes-xts-plain64 if the partition is
    # larger than 2 TB, I think.

    cryptsetup -c aes-xts-plain -y -s 512 luksFormat /dev/sda2

    # Decrypt the encrypted partition with LUKS. We need to decrypt it first
    # before setting up LVM volumes and filesystems on it. This will create
    # an entry under /dev/mapper/luks.

    cryptsetup luksOpen /dev/sda2 luks

    # Set up LVM. We call our volume group "vg0". To be honest I am not sure if
    # the "--contiguous y" option for swap matters because we are encrypting it
    # (doesn't block encryption encrypt data randomly on the disk?), but that's
    # what I found online and that's what I'll use here. The "+100%FREE" option
    # is a very handy option that tells LVM to fill up all remaining free space,
    # in this case, for the "home" logical volume.
    # Read more about LVM on your favorite online wiki.

    pvcreate /dev/mapper/luks
    vgcreate vg0 /dev/mapper/luks
    lvcreate --size 40G vg0 --name root
    lvcreate --size 2G --contiguous y vg0 --name swap
    lvcreate -l +100%FREE vg0 --name home

    # Initialize filesystems. Btrfs is still experimental, so I choose ext4
    # here. The vg0-root, vg0-home, and vg0-swap volumes should
    # appear under /dev/mapper as you create them with the lvcreate command.

    mkfs.ext4 /dev/mapper/vg0-root
    mkfs.ext4 /dev/mapper/vg0-home
    mkswap /dev/mapper/vg0-swap

    # Mount the logical volumes. Why? Because we need to install Arch Linux onto
    # it! You probably don't need to mount the "home" volume but it couldn't
    # hurt. Besides, it's nice to test that all volumes can be mounted OK anyway.

    mount /dev/mapper/vg0-root /mnt # /mnt is our system's "/" root   directory
    mkdir /mnt/home
    mount /dev/mapper/vg0-home /mnt/home
    swapon /dev/mapper/vg0-swap

    # Set up package download mirrors. The kernel mirror is generally a good one
    # if you live in the US. Google "arch linux mirror status" to find the
    # fastest mirrors.

    vi /etc/pacman.d/mirrorlist

    # Download and install the core minimum packages to get a working Linux
    # terminal (console) environment. Ah, simplicity!

    pacstrap -i /mnt base base-devel

    # Generate and inspect the filesystem tables needed when the system first
    # starts. The file should contain four "partitions" (I put them in quotes
    # because remember, we are using only 2 real partitions; 1st one is /boot and
    # the 2nd one is our LVM containing /, /home, and swap).

    genfstab -U -p /mnt >> /mnt/etc/fstab
    vi /mnt/etc/fstab

    # Make the system pretend that /mnt is /. This is called "chrooting". We need
    # to do this because some of the commands like locale-gen, systemctl, etc.
    # read configuration files from /etc, for example, and right now our /etc is
    # the /etc used by the Arch Linux live CD, not our newly-installed system's
    # root directory, /mnt.

    arch-chroot /mnt /bin/bash

    # Choose and generate locale; programs supporting internationalization (aka
    # "i18n") will use the language you pick to generate the proper text used in
    # menus, error messages, etc. Just choose "en_US.UTF-8 UTF-8" if you live in
    # the US.

    vi /etc/locale.gen
    echo LANG=en_US.UTF-8 > /etc/locale.conf

    # Choose default font used by the virtual console (the terminal screen you
    # will see when you first boot up your new Arch Linux system).

    setfont Lat2-Terminus16
    echo FONT=Lat2-Terminus16 > /etc/vconsole.conf

    # Choose timezone. Here, we tell hwclock that we want the hardware clock on
    # the motherboard to be set to UTC, which will then be interpreted as UTC
    # time by the kernel and re-adjusted to the timezone you chose when you
    # actually use the system. This is nice because UTC is the "master" time the
    # world uses anyway, so it makes sense to tell your motherboard to use it.

    ln -s /usr/share/zoneinfo/America/Los_Angeles /etc/localtime
    hwclock --systohc --utc

    # Choose your computer's name. Keep it simple, without spaces. I use "k0".

    echo MYHOSTNAME > /etc/hostname

    # Enable DHCPCD... aka "acquire dynamic ip address from your router/switch".
    # Be sure to choose the right eth0 or eth1 (eth2, if you have 3 ethernet
    # ports) port. If you choose the wrong "eth" number, just go to
    # /etc/systemd/system/ and rename the symlink; e.g.,
    # rename /etc/systemd/system/ to
    # /etc/systemd/system/

    systemctl enable dhcpcd@eth0.service

    # Disable unused package repos. You will want to disable the [testing] repo
    # unless you want to test unstable packages and engage in the package
    # debugging/development process. For Haskellers, be sure to add the
    # [haskell-core] repo (see

    vi /etc/pacman.conf

    # Set up the root user ("administrator" for you Windows people) password.


    # Install zsh, because you will like it better any other shell out there.

    pacman -S zsh

    # Add your regular user, and set up your password. This is your normal
    # username. We use the "-s /bin/zsh" option to tell useradd that we want to
    # log in with zsh.

    # useradd -m -g users -G wheel,storage,power -s /bin/zsh MYUSERNAME
    # passwd MYUSERNAME

    # VERY IMPORTANT: Add in the filesystem type that /root partition (LVM) is
    # using (for this tutorial, it is "ext4") into the MODULES variable and also
    # add 'encrypt' and 'lvm2' into HOOKS.

    vi /etc/mkinitcpio.conf

    # Re-generate the linux image to take into account the LVM and encrypt
    # flags we added into /etc/mkinitcpio.conf. I say "re-generate" because this
    # is our second time doing this (the first time was when we installed the
    # linux package with the pacstrap command above).

    mkinitcpio -p linux

    # Install boot loader. I like SYSLINUX because of the saner/easier-looking
    # boot configuration file, compared to GRUB 2. Be sure to add in
	#     "APPEND cryptdevice=/dev/disk/by-uuid/xxxxxxxxxx:luks root=/dev/mapper/vg0-root resume=/dev/mapper/vg0-swap ro"
    # under the "LABEL arch" entries, so that SYSLINUX tells the kernel to look
    # for the encrypted LVM partition. Otherwise, your system won't boot! Don't
    # forget to actually look up the UUID of your LVM partition (/dev/sda2 in
    # this tutorial). You don't need to tell the kernel about your vg0-home
    # volume because that will get mounted by /etc/fstab when it is read later in
    # the boot process.

    pacman -S syslinux
    syslinux-install_update -i -a -m
    vi /boot/syslinux/syslinux.cfg

    # Exit chroot environment.


    # Unmount volumes, and reboot. Remove your Arch Linux live CD when your
    # computer powers on again.

    umount /mnt/{boot,home}

PROTIP: Use the ALT+F2, ALT+F3, etc. shortcuts to log into and switch between different TTY screens; this is very useful when you are unsure about some of the commands used above and want to look at their manpages for more detailed explanations.

The process of installing Arch Linux is long, but arguably extremely clean and clutter-free. *EVERY* command counts, and matters. And you also get system-level encryption (everything except the /boot partition is encrypted). I would go ahead and install gvim, emacs, xmonad, and all the other little programs I use daily, but that can wait until the next reboot. And because it’s Linux, you don’t have to worry about restarting the whole system when you install new programs, so you can actually install 99% of everything you need within one session.

How to Encrypt Your USB Flash Drives

Did you know that your modern Linux kernel comes with a built-in encryption framework? I am talking about dm-crypt (device-mapper crypt) and the user-friendly layer on top of it, called LUKS (Linux Unified Key Setup). I just encrypted all of my USB flash drives two weeks ago using the dm-crypt + LUKS method and I am very happy with the results.

The process itself is very simple.

# 1. Find the correct device.


# 2. Wipe the device with random data. I prefer to target the disk by its UUID
# because using the /dev/sdX convention is not very reliable (the letters can
# change between boots/hotmounts). NOTE: You might be interested in
# if your device is over 16 GiB or so, because
# using /dev/urandom can be very slow. If using Arch Linux, you can get it from
# the AUR:

dd if=/dev/urandom of=/dev/disk/by-uuid/XXX bs=4096

# 3. Create the partition on the device.

cfdisk /dev/disk/by-uuid/XXX

# 4. Encrypt the partition and make it LUKS-compatible. See the manpage for
# cryptsetup(8).
#   -c: cipher type to use
#   -y: LUKS will ask you to input the passphrase; using -y will ask you twice
#       and complain if the two do not match.
#   -s: Key size in bits; the larget the merrier, but limited by the cipher/mode used.

cryptsetup -c aes-xts-plain -y -s 512 luksFormat /dev/disk/by-uuid/XXX

# 5. Open the partition with LUKS.

cryptsetup luksOpen /dev/disk/by-uuid/XXX mycrypteddev

# The partition is now available from /dev/mapper/mycrypteddev as a "regular"
# partition, since LUKS is now handling all block device encryption between the
# user and the device.

# 6. Set up a filesystem on the partition.

mkfs.ext4 /dev/mapper/mycrypteddev

# 7. Close the partition with LUKS.

cryptsetup luksClose /dev/mapper/mycrypteddev

# Encryption setup complete! Now every time you want to access the partition,
# you must first open it with LUKS and then mount it. Then when you're done, do
# the reverse: unmount and close it with LUKS.

# To mount and open with LUKS:

cryptsetup luksOpen /dev/disk/by-uuid/XXX mycrypteddev
mount -t ext4 /dev/mapper/mycrypteddev /mnt/mount_point

# To unmount and close with LUKS:

umount /mnt/mount_point
cryptsetup luksClose mycrypteddev

The mount/open and unmount/close steps necessary for using the device is laborious. That’s why you should write a bash script to run them. I’ve written the following bash script called to access my 3 USB drives this way:

# mount/unmount encrypted flash drives


case $2 in
        uuid="11e102cd-dea1-46a8-ae9b-b3f74b536e64" # my red USB drive
        uuid="cf169437-b937-4a39-86cb-7ca82bd9fe94" # my green one
        uuid="57a0b7d5-d2a6-47e0-a0e3-adf69501d0cd" # my blue one

if [[ $uuid == "" ]]; then
    echo "No predefined device specified."
    exit 0

case $1 in
        echo "Authorizing encrypted partition /dev/mapper/$mp..."
        sudo cryptsetup luksOpen /dev/disk/by-uuid/$uuid $mp
        echo -n "Mounting partition on /mnt/$mp..."
        sudo mount -t ext4 /dev/mapper/$mp /mnt/$mp && echo "done."
        echo -n "Unmounting /mnt/$mp..."
        sudo umount /mnt/$mp && echo "done."
        echo -n "Closing encrypted partition /dev/mapper/$mp..."
        sudo cryptsetup luksClose $mp && echo "done."

To mount the green USB to /mnt/ef0 (“ef0” is just an arbitrary folder name):

./ m 1 ef0

Then to unmount:

./ u 1 ef0

Simple, eh? Go forth and encrypt all of your USB drives, so that when they get lost, they can’t be read by curious strangers. You can use the above steps to create and encrypt multiple partitions in the same device, or to only encrypt one partition while leaving other partitions unencrypted (i.e., steps 4 through 7 are partition-specific). The choice is yours. I prefer partition-level (aka “block device”) encryption over file/folder encryption because I don’t have to mentally think every time “hey, do I want to encrypt this?” for every file/folder I create.

If you want to look into encrypting your hard drives and swap partitions, read through this disk encryption page, and particularly, this section. There are many “levels” of encryption, and you should consider the many options available to you.