install arch linux with an encrypted boot partition.

bootstrap_arch_linux.sh
#! /bin/bash
set -eu

################################################################################
# prompt for info.
################################################################################

DISKS=($(lsblk | grep disk | cut -d" " -f1 | tr "\n" " "))
echo "install on which disk?"
select DEVICE in "${DISKS[@]}"; do
  echo "${DEVICE} selected"
  break
done

read -r -p "use device ${DEVICE}? (y/N) " REPLY
if [[ ! ${REPLY} =~ ^([Yy]$|[Yy]es) ]]; then
  echo "stopping script..."
  exit 1
fi


################################################################################
# connect to internet (usually automatic for wired connections).
################################################################################

# check if connection active
wget -q --tries=10 --timeout=20 --spider https://google.com
if ! [[ $? -eq 0 ]]; then
  # for wireless connections (connects to first interface available)
  if iw dev | grep Interface > /dev/null; then
    wifi-menu -o $(iw dev | grep -n 1 "Interface" | \
                   sed -n "s/^\s*Interface \(.*\)$/\1/p")
  else
    echo "no wireless interface available. check your wired connection."
  fi
fi


# for non-broadcasting wireless networks, wifi-menu will fail. manually define
# the netork via netctl. see examples available in /etc/netcl. once definined
# network <my_network> is in /etc/netctl, run:
#
#   netctl start <my_network>
#
# to automatically connect from now on, also run:
#
#   netctl enable <my_network>
#   systemctl enable netctl@<my_network>.service


################################################################################
# detect devices and configure storage options.
################################################################################

echo "start paritioning device: ${DEVICE}."

# create partition table
parted -s /dev/${DEVICE} mklabel msdos
parted -s /dev/${DEVICE} mkpart primary 2048s 100%

# create encrypted logical volumes
echo "\ninitialize luks partition."
cryptsetup luksFormat /dev/${DEVICE}1
echo "\nset password for opening luks volume."
cryptsetup luksOpen /dev/${DEVICE}1 lvm
echo "\nenter newly set password to mount luks volume."
pvcreate /dev/mapper/lvm
vgcreate vg /dev/mapper/lvm
lvcreate -L 4G vg -n swap
lvcreate -L 20G vg -n root
lvcreate -l +100%FREE vg -n home

# configure swap and filesystems
mkswap -L swap /dev/mapper/vg-swap
swapon /dev/mapper/vg-swap
mkfs.ext4 /dev/mapper/vg-root
mkfs.ext4 /dev/mapper/vg-home


################################################################################
# mount logical volumes and bootstrap base and base-devel packages.
################################################################################

# mount logical volumes
mount /dev/mapper/vg-root /mnt
mkdir /mnt/home
mount /dev/mapper/vg-home /mnt/home

# bootstrap
pacstrap -i /mnt base base-devel

# generate filesystem table
genfstab -U /mnt >> /mnt/etc/fstab

# if swap partition wasn't added to the fstab, add it manually
if test "$(grep '^UUID.*swap' /etc/fstab)"; then
  echo -e "# /dev/mapper/vg-swap LABEL=swap" >> /mnt/etc/fstab
  echo -e "UUID=$(lsblk -no UUID /dev/mapper/vg-swap)\tnone      \tswap      \tdefaults  \t0 0" >> /mnt/etc/fstab
  echo -e "" >> /mnt/etc/fstab
fi

# execute chroot script to install base system
if [ -f arch_chroot_script.sh ]; then
  cp -f arch_chroot_script.sh /mnt
  arch-chroot /mnt ./arch_chroot_script.sh
  rm -f /mnt/arch_chroot_script.sh
fi

# execute chroot script to build luks key into initcpio
if [ -f make_luks_key.sh ]; then
  cp -f make_luks_key.sh /mnt
  arch-chroot /mnt ./make_luks_key.sh
  rm -f /mnt/make_luks_key.sh
fi


################################################################################
# return from chroot, clean up, and reboot.
################################################################################

umount -R /mnt

# remove the live image when rebooting
reboot
#! /bin/bash set -eu ################################################################################ # prompt for info. ################################################################################ DISKS=($(lsblk | grep disk | cut -d" " -f1 | tr "\n" " ")) echo "install on which disk?" select DEVICE in "${DISKS[@]}"; do echo "${DEVICE} selected" break done read -r -p "use device ${DEVICE}? (y/N) " REPLY if [[ ! ${REPLY} =~ ^([Yy]$|[Yy]es) ]]; then echo "stopping script..." exit 1 fi ################################################################################ # connect to internet (usually automatic for wired connections). ################################################################################ # check if connection active wget -q --tries=10 --timeout=20 --spider https://google.com if ! [[ $? -eq 0 ]]; then # for wireless connections (connects to first interface available) if iw dev | grep Interface > /dev/null; then wifi-menu -o $(iw dev | grep -n 1 "Interface" | \ sed -n "s/^\s*Interface \(.*\)$/\1/p") else echo "no wireless interface available. check your wired connection." fi fi # for non-broadcasting wireless networks, wifi-menu will fail. manually define # the netork via netctl. see examples available in /etc/netcl. once definined # network is in /etc/netctl, run: # # netctl start # # to automatically connect from now on, also run: # # netctl enable # systemctl enable netctl@.service ################################################################################ # detect devices and configure storage options. ################################################################################ echo "start paritioning device: ${DEVICE}." # create partition table parted -s /dev/${DEVICE} mklabel msdos parted -s /dev/${DEVICE} mkpart primary 2048s 100% # create encrypted logical volumes echo "\ninitialize luks partition." cryptsetup luksFormat /dev/${DEVICE}1 echo "\nset password for opening luks volume." cryptsetup luksOpen /dev/${DEVICE}1 lvm echo "\nenter newly set password to mount luks volume." pvcreate /dev/mapper/lvm vgcreate vg /dev/mapper/lvm lvcreate -L 4G vg -n swap lvcreate -L 20G vg -n root lvcreate -l +100%FREE vg -n home # configure swap and filesystems mkswap -L swap /dev/mapper/vg-swap swapon /dev/mapper/vg-swap mkfs.ext4 /dev/mapper/vg-root mkfs.ext4 /dev/mapper/vg-home ################################################################################ # mount logical volumes and bootstrap base and base-devel packages. ################################################################################ # mount logical volumes mount /dev/mapper/vg-root /mnt mkdir /mnt/home mount /dev/mapper/vg-home /mnt/home # bootstrap pacstrap -i /mnt base base-devel # generate filesystem table genfstab -U /mnt >> /mnt/etc/fstab # if swap partition wasn't added to the fstab, add it manually if test "$(grep '^UUID.*swap' /etc/fstab)"; then echo -e "# /dev/mapper/vg-swap LABEL=swap" >> /mnt/etc/fstab echo -e "UUID=$(lsblk -no UUID /dev/mapper/vg-swap)\tnone \tswap \tdefaults \t0 0" >> /mnt/etc/fstab echo -e "" >> /mnt/etc/fstab fi # execute chroot script to install base system if [ -f arch_chroot_script.sh ]; then cp -f arch_chroot_script.sh /mnt arch-chroot /mnt ./arch_chroot_script.sh rm -f /mnt/arch_chroot_script.sh fi # execute chroot script to build luks key into initcpio if [ -f make_luks_key.sh ]; then cp -f make_luks_key.sh /mnt arch-chroot /mnt ./make_luks_key.sh rm -f /mnt/make_luks_key.sh fi ################################################################################ # return from chroot, clean up, and reboot. ################################################################################ umount -R /mnt # remove the live image when rebooting reboot
arch_chroot_script.sh
#! /bin/bash
set -eu

REGION=UTC
CITY=


################################################################################
# prompt for info.
################################################################################

DISKS=($(lsblk | grep disk | cut -d" " -f1 | tr "\n" " "))
echo "install on which disk?"
select DEVICE in "${DISKS[@]}"; do
  echo "${DEVICE} selected"
  break
done

read -r -p "use device ${DEVICE}? (y/N): " REPLY
if [[ ! ${REPLY} =~ ^([Yy]$|[Yy]es) ]]; then
  echo "stopping script..."
  exit 1
fi

read -r -p "enter a new hostname: " HOSTNAME
read -r -p "enter a new username: " USER


################################################################################
# configure base system.
################################################################################

# set time
# rm /etc/localtime
if [ ${REGION} == "UTC" ]; then
  ln -sfn /usr/share/zoneinfo/${REGION} /etc/localtime
else
  ln -sfn /usr/share/zoneinfo/${REGION}/${CITY} /etc/localtime
fi
hwclock --systohc --utc

# set locale
LOCALE=$(locale | grep ^LANG | cut -d= -f2 | cut -d\. -f1)
sed -i "/^#${LOCALE}.UTF/s/^#//" /etc/locale.gen
locale-gen
echo LANG=${LOCALE}.UTF-8 > /etc/locale.conf

# set hostname and append hostname to /etc/hosts
echo ${HOSTNAME} > /etc/hostname
sed -i "/::1/a 127.0.1.1\t${HOSTNAME}.localdomain\t${HOSTNAME}" /etc/hosts

# install wireless tools
pacman -S --noconfirm iw wpa_supplicant dialog


################################################################################
# add encrypt and lvm2 modules to /etc/mkinitcpio.conf.
################################################################################

OLD_HOOKS="base udev autodetect modconf block filesystems keyboard fsck"
NEW_HOOKS="base udev autodetect modconf block encrypt lvm2 filesystems keyboard fsck"
sed -i "s/${OLD_HOOKS}/${NEW_HOOKS}/g" /etc/mkinitcpio.conf

# rebuild initial ramdisk with new modules
mkinitcpio -p linux


################################################################################
# install grub (assuing bios/mbr) and add encryption support.
################################################################################

pacman -S --noconfirm grub os-prober

FOR_SSD="cryptdevice=/dev/${DEVICE}1:lvm:allow-discards"
LVM_ROOT="root=/dev/mapper/vg-root"
GRUB_CMDLINE_LINUX="GRUB_CMDLINE_LINUX=\"${FOR_SSD} ${LVM_ROOT}\""
GRUB_ENABLE_CRYPTODISK="GRUB_ENABLE_CRYPTODISK=y"

sed -i "s|^GRUB_CMDLINE_LINUX=[\"\"]*|${GRUB_CMDLINE_LINUX}|g" /etc/default/grub
sed -i "/^GRUB_CMDLINE_LINUX=.*$/a ${GRUB_ENABLE_CRYPTODISK}" /etc/default/grub

grub-install --recheck /dev/${DEVICE}
grub-mkconfig -o /boot/grub/grub.cfg


################################################################################
# set root password, create new user, and exit.
################################################################################

# create new user with administrator rights
echo "creating user ${USER}."
useradd -m -c ${USER} ${USER} -s /bin/bash
usermod -aG wheel,${USER} ${USER}
echo "set a password for ${USER}."
passwd ${USER}

# enable wheel group
cat /etc/sudoers | sed -e "s/^# %wheel ALL=(ALL) ALL/%wheel ALL=(ALL) ALL/g" | \
  EDITOR=tee visudo >/dev/null

# disable root account
passwd -l root

# (optional) make_luks_key.sh
# if [ -f make_luks_key.sh ]; then
#   ./make_luks_key.sh
# fi

exit 0
#! /bin/bash set -eu REGION=UTC CITY= ################################################################################ # prompt for info. ################################################################################ DISKS=($(lsblk | grep disk | cut -d" " -f1 | tr "\n" " ")) echo "install on which disk?" select DEVICE in "${DISKS[@]}"; do echo "${DEVICE} selected" break done read -r -p "use device ${DEVICE}? (y/N): " REPLY if [[ ! ${REPLY} =~ ^([Yy]$|[Yy]es) ]]; then echo "stopping script..." exit 1 fi read -r -p "enter a new hostname: " HOSTNAME read -r -p "enter a new username: " USER ################################################################################ # configure base system. ################################################################################ # set time # rm /etc/localtime if [ ${REGION} == "UTC" ]; then ln -sfn /usr/share/zoneinfo/${REGION} /etc/localtime else ln -sfn /usr/share/zoneinfo/${REGION}/${CITY} /etc/localtime fi hwclock --systohc --utc # set locale LOCALE=$(locale | grep ^LANG | cut -d= -f2 | cut -d\. -f1) sed -i "/^#${LOCALE}.UTF/s/^#//" /etc/locale.gen locale-gen echo LANG=${LOCALE}.UTF-8 > /etc/locale.conf # set hostname and append hostname to /etc/hosts echo ${HOSTNAME} > /etc/hostname sed -i "/::1/a 127.0.1.1\t${HOSTNAME}.localdomain\t${HOSTNAME}" /etc/hosts # install wireless tools pacman -S --noconfirm iw wpa_supplicant dialog ################################################################################ # add encrypt and lvm2 modules to /etc/mkinitcpio.conf. ################################################################################ OLD_HOOKS="base udev autodetect modconf block filesystems keyboard fsck" NEW_HOOKS="base udev autodetect modconf block encrypt lvm2 filesystems keyboard fsck" sed -i "s/${OLD_HOOKS}/${NEW_HOOKS}/g" /etc/mkinitcpio.conf # rebuild initial ramdisk with new modules mkinitcpio -p linux ################################################################################ # install grub (assuing bios/mbr) and add encryption support. ################################################################################ pacman -S --noconfirm grub os-prober FOR_SSD="cryptdevice=/dev/${DEVICE}1:lvm:allow-discards" LVM_ROOT="root=/dev/mapper/vg-root" GRUB_CMDLINE_LINUX="GRUB_CMDLINE_LINUX=\"${FOR_SSD} ${LVM_ROOT}\"" GRUB_ENABLE_CRYPTODISK="GRUB_ENABLE_CRYPTODISK=y" sed -i "s|^GRUB_CMDLINE_LINUX=[\"\"]*|${GRUB_CMDLINE_LINUX}|g" /etc/default/grub sed -i "/^GRUB_CMDLINE_LINUX=.*$/a ${GRUB_ENABLE_CRYPTODISK}" /etc/default/grub grub-install --recheck /dev/${DEVICE} grub-mkconfig -o /boot/grub/grub.cfg ################################################################################ # set root password, create new user, and exit. ################################################################################ # create new user with administrator rights echo "creating user ${USER}." useradd -m -c ${USER} ${USER} -s /bin/bash usermod -aG wheel,${USER} ${USER} echo "set a password for ${USER}." passwd ${USER} # enable wheel group cat /etc/sudoers | sed -e "s/^# %wheel ALL=(ALL) ALL/%wheel ALL=(ALL) ALL/g" | \ EDITOR=tee visudo >/dev/null # disable root account passwd -l root # (optional) make_luks_key.sh # if [ -f make_luks_key.sh ]; then # ./make_luks_key.sh # fi exit 0
make_luks_key.sh
#! /bin/bash
set -eu

################################################################################
# prompt for info.
################################################################################

DISKS=($(lsblk | grep disk | cut -d" " -f1 | tr "\n" " "))
echo "install on which disk?"
select DEVICE in "${DISKS[@]}"; do
  echo "${DEVICE} selected"
  break
done

read -r -p "use device ${DEVICE}? (y/N): " REPLY
if [[ ! ${REPLY} =~ ^([Yy]$|[Yy]es) ]]; then
  echo "stopping script..."
  exit 1
fi


################################################################################
# (optional) create a binary luks key to store in the initial ramdisk.
################################################################################

# generate keyfile and prevent non-root from reading it.
CRYPTOKEY="/etc/crypto_keyfile.bin"
dd bs=512 count=4 if=/dev/urandom of=${CRYPTOKEY}
chmod 400 ${CRYPTOKEY}
cryptsetup luksAddKey /dev/${DEVICE}1 ${CRYPTOKEY}

# add key to /etc/mkinitcpio.conf
OLD_MKINITCPIO_FILES=$(sed -n "s/^FILES=\"\(.*\)\"/\1/p" /etc/mkinitcpio.conf)
NEW_MKINITCPIO_FILES=$(echo ${OLD_MKINITCPIO_FILES} ${CRYPTOKEY})
sed -i "s|^FILES=\"${OLD_MKINITCPIO_FILES}\"|FILES=\"${NEW_MKINITCPIO_FILES}\"|g" /etc/mkinitcpio.conf

# rebuild the kernel
mkinitcpio -p linux

# prevent non-privileged users from reading any ramdisk
sudo chmod -R 700 /boot/

# add key to grub config file
CRYPTO_KEY="cryptkey=rootfs:${CRYPTOKEY}"
OLD_GRUB_CMDLINE_LINUX=$(sed -n "s/^GRUB_CMDLINE_LINUX=\"\(.*\)\"/\1/p" /etc/default/grub)
NEW_GRUB_CMDLINE_LINUX="${OLD_GRUB_CMDLINE_LINUX} ${CRYPTO_KEY}"
sed -i "s|${OLD_GRUB_CMDLINE_LINUX}|${NEW_GRUB_CMDLINE_LINUX}|g" /etc/default/grub

# rebuild grub
grub-mkconfig -o /boot/grub/grub.cfg
#! /bin/bash set -eu ################################################################################ # prompt for info. ################################################################################ DISKS=($(lsblk | grep disk | cut -d" " -f1 | tr "\n" " ")) echo "install on which disk?" select DEVICE in "${DISKS[@]}"; do echo "${DEVICE} selected" break done read -r -p "use device ${DEVICE}? (y/N): " REPLY if [[ ! ${REPLY} =~ ^([Yy]$|[Yy]es) ]]; then echo "stopping script..." exit 1 fi ################################################################################ # (optional) create a binary luks key to store in the initial ramdisk. ################################################################################ # generate keyfile and prevent non-root from reading it. CRYPTOKEY="/etc/crypto_keyfile.bin" dd bs=512 count=4 if=/dev/urandom of=${CRYPTOKEY} chmod 400 ${CRYPTOKEY} cryptsetup luksAddKey /dev/${DEVICE}1 ${CRYPTOKEY} # add key to /etc/mkinitcpio.conf OLD_MKINITCPIO_FILES=$(sed -n "s/^FILES=\"\(.*\)\"/\1/p" /etc/mkinitcpio.conf) NEW_MKINITCPIO_FILES=$(echo ${OLD_MKINITCPIO_FILES} ${CRYPTOKEY}) sed -i "s|^FILES=\"${OLD_MKINITCPIO_FILES}\"|FILES=\"${NEW_MKINITCPIO_FILES}\"|g" /etc/mkinitcpio.conf # rebuild the kernel mkinitcpio -p linux # prevent non-privileged users from reading any ramdisk sudo chmod -R 700 /boot/ # add key to grub config file CRYPTO_KEY="cryptkey=rootfs:${CRYPTOKEY}" OLD_GRUB_CMDLINE_LINUX=$(sed -n "s/^GRUB_CMDLINE_LINUX=\"\(.*\)\"/\1/p" /etc/default/grub) NEW_GRUB_CMDLINE_LINUX="${OLD_GRUB_CMDLINE_LINUX} ${CRYPTO_KEY}" sed -i "s|${OLD_GRUB_CMDLINE_LINUX}|${NEW_GRUB_CMDLINE_LINUX}|g" /etc/default/grub # rebuild grub grub-mkconfig -o /boot/grub/grub.cfg

The Arch Wiki will tell you how to install Arch with a separate, unencrypted boot partition, but it won’t help if you want to encrypt your boot partition and mount it along with your other ones. The following is an explanation of how to create them using GRUB modules, LVM, and LUKS.

1. Boot your Arch installation image.

Download the latest image here and be sure to verify the image after it is downloaded. After all, encryption is pointless if the installation image itself is compromised.

Burn the image to disk or copy it to a USB. Then, boot from it.

2. Create your partitions.

You’ll first need to do the steps already outlined on the Arch Wiki installation guide up to partition creation. Basically, set up your network interface an identify the devices within the filesystem.

The following will create three partitions for swap, home. and root. Replace the X of /dev/sdX with the letter corresponding to the hard drive you are installing on. Modify the code as suits your needs.

parted -s /dev/sdX mklabel msdos
parted -s /dev/sdX mkpart primary 2048s 100%
cryptsetup luksFormat /dev/sdX1
cryptsetup luksOpen /dev/sdX1 lvm
pvcreate /dev/mapper/lvm
vgcreate vg /dev/mapper/lvm
lvcreate -L 4G vg -n swap
lvcreate -L 20G 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

These instructions are copied basically verbatim from the blog mentioned at the beginning.

3. Install Arch Linux.

Now, follow the generic install instructions. Don’t forget to generate the fstab.

When you install the kernel, you’ll need to add some modules to the initramfs image. Open /etc/mkinitcpio.conf and encrypt and lvm2 to the kernel build hooks. The order shouldn’t matter, but just in case, edit the line so that it reads as follows:

HOOKS="base udev autodetect modconf block encrypt lvm2 filesystems keyboard fsck"

NOTE: If there are other modules not shown in the above example, leave them alone.

The modules added to the build hooks are necessary for the initramfs to be able to decrypt partitions and mount the filesystem they live on. Save your changes to mkinitcpio.conf and rebuild the kernel image. Run:

mkinitcpio -p linux

4. Install GRUB.

Continue with the install instructions until you set to the part that tells you how to install a bootloader. Install GRUB as you normally would. Then, open /etc/default/grub and add the line:

GRUB_ENABLE_CRYPTODISK=y

Then, also in /etc/default/grub, edit GRUB_CMDLINE_LINUX to pass extra boot parameters to the kernel:

GRUB_CMDLINE_LINUX="cryptdevice=/dev/sdX1:lvm root=/dev/mapper/vg-root"

Note that if you have an SSD and don’t mind the security implications of allowing discards, do this instead:

GRUB_CMDLINE_LINUX="cryptdevice=/dev/sdX1:lvm:allow-discards root=/dev/mapper/vg-root"

Then, regenerate the GRUB config file:

grub-mkconfig -o /boot/grub.cfg

5. (Optional) Create LUKS key.

If you booted your system already, you’ll notice that you have to enter your LUKS password each time you try to decrypt a block device. It gets annoying, but you can get around it by supplying a keyfile to the kernel that’ll boot the decrypt partitions after you decrypt the bootloader. This doesn’t carry security implications because until you enter the password in GRUB, the keyfile is encrypted on the hard drive.

The only security hazards the key poses is when the system is booted and the key resides in the ramfs, unencrypted. At this point, so does the LUKS master key, so if attackers can get hold of your keyfile in this state, they might as well get your master key. If an attacker is that determined and pernicious, you’ll need to do a lot more to secure your system, something well beyond the scope of this post.

Generate the keyfile by doing the following:

1. Fill a file with random bits.

dd bs=512 count=4 if=/dev/urandom of=/crypto_keyfile.bin
cryptsetup luksAddkey /dev/sdX1 /crypto_keyfile.bin

You can stick crypto_keyfile.bin anywhere in the root partition if you’d like. Also, adding the key will prompt you for a password. Enter any existing LUKS password.

2. Build the key into the initramfs.

Add the key to the FILES parameter in /etc/mkinitcpio.conf. Then regenerate the initramfs image by:

mkinitcpio -p linux

3. Add the key to GRUB.

Open /etc/default/grub and append GRUB_CMDLINE_LINUX with cryptkey=rootfs:/crypto_keyfile.bin. Do not delete the cryptdevice parts added earlier!!!

Then, regenerate the grub.cfg file with grub-mkconfig.

Now, the key will be loaded into the boot image and decrypt your partitions once you select a kernel from the GRUB boot menu.