Skip to content

Instantly share code, notes, and snippets.

@iesplin
Last active February 26, 2020 05:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save iesplin/d321326d744499efd2273a3680b1dff5 to your computer and use it in GitHub Desktop.
Save iesplin/d321326d744499efd2273a3680b1dff5 to your computer and use it in GitHub Desktop.
Zymkey script for migrate existing SD card root fs to external LUKS storage device. Added support for Ubuntu 18.04.4
#!/bin/bash
#
# This script will make an encrypted root file system on a USB drive with the
# following steps:
# 1) create a LUKS key that is locked up by zymkey
# 2) create a LUKS dm-crypt partition on an external drive
# 3) create an ext4 volume on the dm-crypt partition
# 5) copy the existing root file system on /dev/mmcblkXpY (X=0, Y=2 by default)
# to the dm-crypt volume
# 6) create an initramfs which will be used to boot to the dm-crypt volume
# 7) reboot to transfer control to the new root file system
# 8) the new root file system starts a service which scrubs and removes the
# old root file system and then removes itself
# Ensure running as root or exit
if [ "$(id -u)" != "0" ]
then
echo "run this as root or use sudo" 2>&1 && exit 1
fi
usage() { echo "Usage: $0 [-x <path to external storage device (e.g. /dev/sdX>] [-s <max size of new root part>] [-p <ext part num>] [-m <src part num>]" 1>&2; exit 1; }
source /var/lib/zymbit/zkenv.conf >/dev/null 2>&1
export ZK_GPIO_WAKE_PIN
MMC_DEV_NUM="0"
RFS_DST_PART_NUM="1"
RFS_SRC_PART_NUM="2"
while getopts ":x:s:p:m:d:" o; do
case "${o}" in
x)
EXT_DEV=${OPTARG}
if [ ! -e "${EXT_DEV}" ]
then
echo "specified external device ${EXT_DEV} does not exist"; usage
fi
;;
s)
MAX_RFS_SIZE="+${OPTARG}"
;;
p)
RFS_DST_PART_NUM="${OPTARG}"
;;
m)
RFS_SRC_PART_NUM="${OPTARG}"
;;
d)
MMC_DEV_NUM="${OPTARG}"
;;
*)
usage
;;
esac
done
shift $((OPTIND-1))
if [ -z ${EXT_DEV} ]
then
echo "No volume name (/dev/...) specified. Defaulting to /dev/sda..."
EXT_DEV="/dev/sda"
fi
MMC_DEV="/dev/mmcblk${MMC_DEV_NUM}"
EXT_RFS_PART="${EXT_DEV}${RFS_DST_PART_NUM}"
# Install rsync and the zymkey standalone apps
echo "Installing necessary packages..."
apt-get update -y || exit
apt-get install -y rsync zksaapps || exit
echo "done."
# Unmount the temporary boot device
umount ${EXT_RFS_PART} &>/dev/null
# Format the external mass media
echo -n "Formatting external storage media on ${EXT_DEV}..."
dd if=/dev/zero of=${EXT_DEV} bs=512 count=1 conv=notrunc >/dev/null || exit
sync
echo -e "n\np\n${RFS_DST_PART_NUM}\n\n${MAX_RFS_SIZE}\nw\n" | fdisk ${EXT_DEV} >/dev/null || exit
# Stop the zymkey interface connector
echo -n "Stopping zkifc..."
systemctl stop zkifc >/dev/null || exit
sleep 10
echo "done."
# Make a zymkey-locked LUKS key
echo -n "Creating LUKS key..."
zkgrifs 512 > /run/key.bin || exit
zklockifs /run/key.bin > /var/lib/zymbit/key.bin.lock || exit
echo "done."
# Write a self-destruct script to erase the old root file system after the
# first boot on the new root
cat > /usr/local/bin/encr_fs_cleanup.sh <<EOF
#!/bin/bash
systemctl stop zkifc
echo "Erasing and removing old root fs..."
dd if=/dev/zero of=${MMC_DEV}p${RFS_SRC_PART_NUM} bs=8M
echo -e "d\n${RFS_SRC_PART_NUM}\nw\n" | fdisk ${MMC_DEV}
sync
systemctl disable encr_fs_cleanup
rm /etc/systemd/system/encr_fs_cleanup.service
systemctl daemon-reload
# Enable zkifc
systemctl start zkifc
rm -rf /mnt/cryptrfs
rm -- $0
reboot
exit 0
EOF
chmod +x /usr/local/bin/encr_fs_cleanup.sh
# Write a service for executing the script above
cat > /etc/systemd/system/encr_fs_cleanup.service <<EOF
[Unit]
Description=First time boot encrypted filesystem cleanup service
[Service]
Type=simple
ExecStart=/usr/local/bin/encr_fs_cleanup.sh
[Install]
WantedBy=multi-user.target
EOF
systemctl enable encr_fs_cleanup
# Copy /var/lib/zymbit and all standalone zymkey utilities to initramfs
cat > /etc/initramfs-tools/hooks/zymkey_cryptfs_cfg <<"EOF"
#!/bin/sh
PREREQ=""
prereqs() {
echo "$PREREQ"
}
case "$1" in
prereqs)
prereqs
exit 0
;;
esac
. /usr/share/initramfs-tools/hook-functions
mkdir -p ${DESTDIR}/var/lib/zymbit
cp -prf /var/lib/zymbit/* ${DESTDIR}/var/lib/zymbit
copy_exec /sbin/zkunlockifs /sbin
EOF
chmod +x /etc/initramfs-tools/hooks/zymkey_cryptfs_cfg
cat >> /usr/sbin/update_encr_initrd <<"EOF"
#!/bin/bash
version="$1"
bootopt=""
kf="/mnt/tmpboot/kernel${vn}.img"
mkdir -p /mnt/tmpboot
EOF
cat >> /usr/sbin/update_encr_initrd <<EOF
mount ${MMC_DEV}p1 /mnt/tmpboot
EOF
cat >> /usr/sbin/update_encr_initrd <<"EOF"
if [ -n "${version}" ]
then
echo "Kernel version ${version} passed in..."
fi
# Get the modifier from the running kernel version (e.g. "v7+" in "4.4.50-v7+")
kv=`uname -r`
vn=
echo "$kv" | grep '-' >/dev/null
if [ $? -eq 0 ]; then
mod=`echo "$kv" | cut -d '-' -f 2`
vn=$(echo "$mod" | sed 's/[^0-9]*//g')
fi
# If no version supplied, then figure it out on our own based on the currently
# running kernel
if [ -z "${version}" ]; then
# Derive the installed kernel's version number from the correct image.
# NOTE: since this script is meant to be run after an 'apt-get upgrade',
# this will not necessarily match 'uname -r'
echo -n "Getting most recently installed kernel version..."
sk=$(LC_ALL=C grep -a -b -o $'\x1f\x8b\x08\x00\x00\x00\x00\x00' ${kf} | cut -d ':' -f 1)
sk=`echo ${sk} | cut -d ' ' -f 1`
lv=`dd if=${kf} bs=1 skip=${sk} status=none | zcat -q | grep -a 'Linux version' | cut -d ' ' -f 3`
else
rmod=`echo "$version" | cut -d '-' -f 2`
if [ "${rmod}" != "${mod}" ]; then
echo "Aborting update-initramfs due to request mismatching running kernel..."
exit 0
fi
lv="${version}"
fi
if="initrd.img-${lv}"
# Bring the i2c drivers into initramfs
grep -q "^i2c-dev" /etc/initramfs-tools/modules || echo "i2c-dev" >> /etc/initramfs-tools/modules
grep -q "^i2c-bcm2835" /etc/initramfs-tools/modules || echo "i2c-bcm2835" >> /etc/initramfs-tools/modules
grep -q "^i2c-bcm2708" /etc/initramfs-tools/modules || echo "i2c-bcm2708" >> /etc/initramfs-tools/modules
echo -n "Updating initrd.img..."
rm /mnt/tmpboot/${if} 2>/dev/null
rm /mnt/tmpboot/initrd.img 2>/dev/null
update-initramfs -v -c -k ${lv} -b /mnt/tmpboot >/dev/null || exit
mv /mnt/tmpboot/${if} /mnt/tmpboot/initrd.img
echo "done."
EOF
chmod +x /usr/sbin/update_encr_initrd
# Replace existing kernel initramfs rebuild with our own
cat > /etc/kernel/postinst.d/initramfs-tools <<"EOF"
#!/bin/sh -e
version="$1"
bootopt=""
[ -x /usr/sbin/update_encr_initrd ] || exit 0
# passing the kernel version is required
if [ -z "${version}" ]; then
echo >&2 "W: initramfs-tools: ${DPKG_MAINTSCRIPT_PACKAGE:-kernel package} did not pass a version number"
exit 2
fi
# absolute file name of kernel image may be passed as a second argument;
# create the initrd in the same directory
if [ -n "$2" ]; then
bootdir=$(dirname "$2")
bootopt="-b ${bootdir}"
fi
# avoid running multiple times
if [ -n "$DEB_MAINT_PARAMS" ]; then
eval set -- "$DEB_MAINT_PARAMS"
if [ -z "$1" ] || [ "$1" != "configure" ]; then
exit 0
fi
fi
update_encr_initrd ${version}
EOF
chmod +x /etc/kernel/postinst.d/initramfs-tools
# Create the dm-crypt volume on external media
echo -n "Formatting crypto file system on ${EXT_DEV}..."
cat /run/key.bin | cryptsetup -q -v luksFormat ${EXT_RFS_PART} - >/dev/null
cat /run/key.bin | cryptsetup luksOpen ${EXT_RFS_PART} cryptrfs --key-file=- >/dev/null
echo "done."
echo -n "Creating ext4 partition on ${EXT_RFS_PART}..."
mkfs.ext4 -j /dev/mapper/cryptrfs -F >/dev/null || exit
echo "done."
echo "Copying files to crypto fs..."
crfsvol="/mnt/cryptrfs"
mkdir -p ${crfsvol}
mount /dev/mapper/cryptrfs ${crfsvol} >/dev/null || exit
rsync -axHAX --info=progress2 / ${crfsvol}
sync
echo "done."
# Remove the plaintext key now
rm /run/key.bin
mkdir -p /mnt/tmpboot
mount ${MMC_DEV}p1 /mnt/tmpboot
if [ ! -f /mnt/tmpboot/config.txt ]
then
# Mount the boot partition
mount ${MMC_DEV}p1 /mnt/tmpboot || exit
fi
# Change fstab to no longer use the unencrypted root volume
rfs=`grep -w "/" ${crfsvol}/etc/fstab | grep -v "^#" | awk '{print $1}'`; sed -i "s|$rfs|#$rfs|" ${crfsvol}/etc/fstab
grep -q "^/dev/mapper/cryptrfs" ${crfsvol}/etc/fstab || echo -e "\n# crypto root fs\n/dev/mapper/cryptrfs / ext4 defaults,noatime 0 1" >> ${crfsvol}/etc/fstab
# Make sure that boot uses initramfs
grep -q "^initramfs" /mnt/tmpboot/config.txt || echo "initramfs initrd.img followkernel" >> /mnt/tmpboot/config.txt
# Add crypto fs stuff to the kernel command line
# Get current file for kernel command line. Default to cmdline.txt
# if not present
cmdline_file=$(grep '^cmdline=' /mnt/tmpboot/config.txt | cut -d '=' -f 2)
cmdline_file=${cmdline_file:-cmdline.txt}
sed -i "s/root=[^ ]*//" /mnt/tmpboot/${cmdline_file}
sed -i "s/rootfstype=[^ ]*//" /mnt/tmpboot/${cmdline_file}
sed -i "s/ //" /mnt/tmpboot/${cmdline_file}
tr -d '\n' </mnt/tmpboot/${cmdline_file}> /tmp/${cmdline_file}
mv /tmp/${cmdline_file} /mnt/tmpboot/${cmdline_file}
echo " root=/dev/mapper/cryptrfs cryptdevice=${EXT_RFS_PART}:cryptrfs rng_core.default_quality=1000" >> /mnt/tmpboot/${cmdline_file}
# Add crypttab cfg
echo -e "cryptrfs\t${EXT_RFS_PART}\t/etc/cryptroot/key.bin\tluks,keyscript=/lib/cryptsetup/scripts/zk_get_key,tries=100,timeout=30s" > ${crfsvol}/etc/crypttab
# Bring the i2c drivers into initramfs
grep -q "^i2c-dev" ${crfsvol}/etc/initramfs-tools/modules || echo "i2c-dev" >> ${crfsvol}/etc/initramfs-tools/modules
grep -q "^i2c-bcm2835" ${crfsvol}/etc/initramfs-tools/modules || echo "i2c-bcm2835" >> ${crfsvol}/etc/initramfs-tools/modules
grep -q "^i2c-bcm2708" ${crfsvol}/etc/initramfs-tools/modules || echo "i2c-bcm2708" >> ${crfsvol}/etc/initramfs-tools/modules
grep -q "^lan78xx" ${crfsvol}/etc/initramfs-tools/modules || echo "lan78xx" >> ${crfsvol}/etc/initramfs-tools/modules
# chroot to future root fs
mount -t proc /proc ${crfsvol}/proc/
mount --rbind /sys ${crfsvol}/sys/
mount --rbind /dev ${crfsvol}/dev/
mount --rbind /run ${crfsvol}/run/
mkdir -p ${crfsvol}/mnt/tmpboot/
mount --bind /mnt/tmpboot ${crfsvol}/mnt/tmpboot/
cat << EOF | chroot ${crfsvol} /bin/bash
# Make the initramfs
echo -n "Building initramfs..."
rm /mnt/tmpboot/initrd.img-`uname -r` 2>/dev/null
update-initramfs -v -c -k `uname -r` -b /mnt/tmpboot/
EOF
mv /mnt/tmpboot/initrd.img-`uname -r` /mnt/tmpboot/initrd.img
echo "done."
# Reboot now
echo "Rebooting..."
sync
reboot
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment