Last active
February 26, 2020 05:25
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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