From 1490d1e858064a3aa5a70f12df809c71a68d6b6b Mon Sep 17 00:00:00 2001 From: Gaspar Chilingarov Date: Sat, 17 Jun 2017 14:53:13 +0300 Subject: [PATCH] initial commit --- README.md | 83 ++++- .../hooks/prepare_auto_unlock_deps | 64 ++++ .../scripts/local-top/cryptroot-prepare | 338 ++++++++++++++++++ .../bin/autounlock_install_dependency.sh | 3 + usr/local/bin/autounlock_install_key.sh | 100 ++++++ usr/local/etc/auto_unlock.conf | 3 + 6 files changed, 589 insertions(+), 2 deletions(-) create mode 100755 etc/initramfs-tools/hooks/prepare_auto_unlock_deps create mode 100755 etc/initramfs-tools/scripts/local-top/cryptroot-prepare create mode 100755 usr/local/bin/autounlock_install_dependency.sh create mode 100755 usr/local/bin/autounlock_install_key.sh create mode 100644 usr/local/etc/auto_unlock.conf diff --git a/README.md b/README.md index ed30b8e..3a72c01 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,81 @@ -# ubuntu-luks-autounlock -Scripts to automatically unlock LUKS encrypted partitions based on machine environment +# Ubuntu LUKS auto unlock + +## Do not miss information +This is part of educational materials about Ubuntu administration from my site +[http://gasparchilingarov.com/](http://gasparchilingarov.com). + +Click link and subscibe to mailing list to start learning today. + +## Purpose + +This script is intended to help unlocking Ubuntu system encrypted disks +automatically when it is used in known environment (at home). In all other +environments it will still ask for passwords to unlock disks. + +This setup intends to protect system **only from accidental laptop theft**. If you are +potential hacking target - do not use it, your data may be at risk. + +Right now scripts take into account: + * MAC address of your Wifi network + * information from your external display + +If you use it without external monitor (it will pick up your build-in monitor +information) there is a risk someone can guess/scan your Wifi and find out +MAC address and be able to generate correct decryption key, so do not use it. + +## Compatibility + +Scripts are tested on Ubuntu 16.04 64-bit only. Use it on your own risk on other systems. + + +## Usage + +Copy files from repository to corresponding directories on your Ubuntu system. + +Run `/usr/local/bin/autounlock_install_dependency.sh` to install necessary +dependencies. + +Configure your Wifi interface (most probably "wlan0"), Wifi network name and +LUKS partition key slot number in `/usr/local/etc/auto_unlock.conf`. + +You can run `cryptsetup luksDump /dev/sdXXXX` to check which slots are free on +your encrypted partitions. LUKS partition can have up to 8 keys for +decyphering. Key slot `0` is used by default for your manually entered password +and cannot be used to auto-unlock. + +Run `/usr/local/bin/autounlock_install_key.sh` to add or update keys on all +LUKS partitions defined in `/etc/crypttab`. Follow script prompts to finish setup. + +## Add boot scripts + +After adding keys to partitions you need to add correspondig scripts to do auto +unlock into initramfs. + +You need to have scripts in corresponding directories under `/etc/initramfs-tools/`. + +Run `update-initramfs -k all -u` to update all kernel images. + + +## Try it out + +Reboot :) If everything went smoothly - your system will boot without asking passwords at all. + +Try disconnecting external monitor or turning off Wifi and rebooting again to +confirm that it asks for password to decode partitions. + + +## Removing extra keys + +If you want to remove auto-unlock keys use `cryptsetup luksKillSlot /dev/sdaXXXX KEYSLOT`. + +KEYSLOT should be same slot you used while setting up auto-unlock keys. Do not +delete occasionally other slots, as you may be locked out of your system. + +## Extra sources of information + +Adding extra information sources is pretty straightforward - just keep it in +sync between `etc/initramfs-tools/scripts/local-top/cryptroot-prepare:gather_key_information()` +and `usr/local/bin/autounlock_install_key.sh`. If you need extra +binaries/drivers in initramfs - add them into +`etc/initramfs-tools/hooks/prepare_auto_unlock_deps` script. + diff --git a/etc/initramfs-tools/hooks/prepare_auto_unlock_deps b/etc/initramfs-tools/hooks/prepare_auto_unlock_deps new file mode 100755 index 0000000..4802c7f --- /dev/null +++ b/etc/initramfs-tools/hooks/prepare_auto_unlock_deps @@ -0,0 +1,64 @@ +#!/bin/sh +PREREQ="" +prereqs() +{ + echo "$PREREQ" +} + +# These prerequisites are provided by the read-edid package. +COPY_EXEC_LIST="/usr/bin/get-edid /usr/bin/parse-edid" + +# These prerequisites are provided by the base system. +COPY_EXEC_LIST="$COPY_EXEC_LIST /sbin/iwlist" + +# Explicitly specify all kernel modules because automatic dependency resolution +# is unreliable on many systems. +MANUAL_ADD_MODULES_LIST="iwlwifi iwlmvm iwldvm mac80211 cfg80211" + +echo "Adding $MANUAL_ADD_MODULES_LIST for network detection" + +# Generic result code. +RC=0 + +case $1 in +prereqs) + prereqs + exit 0 + ;; +esac + +for ii in $COPY_EXEC_LIST +do + if [ ! -x "$ii" ] + then + echo "Error: $ii is not executable." + RC=2 + fi +done + +if [ "$RC" -ne 0 ] +then + exit "$RC" +fi + +. /usr/share/initramfs-tools/hook-functions + +for ii in $COPY_EXEC_LIST +do + copy_exec "$ii" +done + +for ii in $MANUAL_ADD_MODULES_LIST +do + manual_add_modules "$ii" +done + +mkdir -p ${DESTDIR}/lib/firmware +for ii in `find /lib/firmware/ -name 'iwlwifi-*'` +do + cp -p $ii ${DESTDIR}/lib/firmware/ +done + +mkdir -p ${DESTDIR}/etc +cp -p /usr/local/etc/auto_unlock.conf ${DESTDIR}/etc/auto_unlock.conf + diff --git a/etc/initramfs-tools/scripts/local-top/cryptroot-prepare b/etc/initramfs-tools/scripts/local-top/cryptroot-prepare new file mode 100755 index 0000000..e4372a7 --- /dev/null +++ b/etc/initramfs-tools/scripts/local-top/cryptroot-prepare @@ -0,0 +1,338 @@ +#!/bin/sh + +PREREQ="" + +# +# Standard initramfs preamble +# +prereqs() +{ + echo "$PREREQ" +} + +case $1 in +prereqs) + prereqs + exit 0 + ;; +esac + +# source for log_*_msg() functions, see LP: #272301 +. /scripts/functions + +# +# Helper functions +# +message() +{ + if [ -x /bin/plymouth ] && plymouth --ping; then + plymouth message --text="$@" + else + echo "$@" >&2 + fi + return 0 +} + +udev_settle() +{ + # Wait for udev to be ready, see https://launchpad.net/bugs/85640 + if command -v udevadm >/dev/null 2>&1; then + udevadm settle --timeout=30 + elif command -v udevsettle >/dev/null 2>&1; then + udevsettle --timeout=30 + fi + return 0 +} + +parse_options() +{ + local cryptopts + cryptopts="$1" + + if [ -z "$cryptopts" ]; then + return 1 + fi + + # Defaults + cryptcipher=aes-cbc-essiv:sha256 + cryptsize=256 + crypthash=ripemd160 + crypttarget=cryptroot + cryptsource="" + cryptheader="" + cryptlvm="" + cryptkeyscript="" + cryptkey="" # This is only used as an argument to an eventual keyscript + crypttries=3 + crypttcrypt="" + cryptrootdev="" + cryptdiscard="" + CRYPTTAB_OPTIONS="" + + local IFS=" ," + for x in $cryptopts; do + case $x in + hash=*) + crypthash=${x#hash=} + ;; + size=*) + cryptsize=${x#size=} + ;; + cipher=*) + cryptcipher=${x#cipher=} + ;; + target=*) + crypttarget=${x#target=} + export CRYPTTAB_NAME="$crypttarget" + ;; + source=*) + cryptsource=${x#source=} + if [ ${cryptsource#UUID=} != $cryptsource ]; then + cryptsource="/dev/disk/by-uuid/${cryptsource#UUID=}" + elif [ ${cryptsource#LABEL=} != $cryptsource ]; then + cryptsource="/dev/disk/by-label/${cryptsource#LABEL=}" + fi + export CRYPTTAB_SOURCE="$cryptsource" + ;; + header=*) + cryptheader=${x#header=} + if [ ! -e "$cryptheader" ] && [ -e "/conf/conf.d/cryptheader/$cryptheader" ]; then + cryptheader="/conf/conf.d/cryptheader/$cryptheader" + fi + export CRYPTTAB_HEADER="$cryptheader" + ;; + lvm=*) + cryptlvm=${x#lvm=} + ;; + keyscript=*) + cryptkeyscript=${x#keyscript=} + ;; + key=*) + if [ "${x#key=}" != "none" ]; then + cryptkey=${x#key=} + fi + export CRYPTTAB_KEY="$cryptkey" + ;; + tries=*) + crypttries="${x#tries=}" + case "$crypttries" in + *[![:digit:].]*) + crypttries=3 + ;; + esac + ;; + tcrypt) + crypttcrypt="yes" + ;; + rootdev) + cryptrootdev="yes" + ;; + discard) + cryptdiscard="yes" + ;; + esac + PARAM="${x%=*}" + if [ "$PARAM" = "$x" ]; then + VALUE="yes" + else + VALUE="${x#*=}" + fi + CRYPTTAB_OPTIONS="$CRYPTTAB_OPTIONS $PARAM" + eval export CRYPTTAB_OPTION_$PARAM="\"$VALUE\"" + done + export CRYPTTAB_OPTIONS + + if [ -z "$cryptsource" ]; then + message "cryptsetup: source parameter missing" + return 1 + fi + return 0 +} + +activate_vg() +{ + # Sanity checks + if [ ! -x /sbin/lvm ]; then + message "cryptsetup: lvm is not available" + return 1 + fi + + # Detect and activate available volume groups + /sbin/lvm vgscan + /sbin/lvm vgchange -a y --sysinit + return $? +} + +setup_mapping() +{ + local opts count cryptopen cryptremove NEWROOT + opts="$1" + + if [ -z "$opts" ]; then + return 0 + fi + + parse_options "$opts" || return 1 + + # The same target can be specified multiple times + # e.g. root and resume lvs-on-lvm-on-crypto + if [ -e "/dev/mapper/$crypttarget" ]; then + return 0 + fi + + modprobe -q dm_crypt + + # Make sure the cryptsource device is available + if [ ! -e $cryptsource ]; then + activate_vg + fi + + # If the encrypted source device hasn't shown up yet, give it a + # little while to deal with removable devices + + # the following lines below have been taken from + # /usr/share/initramfs-tools/scripts/local, as suggested per + # https://launchpad.net/bugs/164044 + if [ ! -e "$cryptsource" ]; then + log_begin_msg "Waiting for encrypted source device..." + + # Default delay is 180s + if [ -z "${ROOTDELAY}" ]; then + slumber=180 + else + slumber=${ROOTDELAY} + fi + + slumber=$(( ${slumber} * 10 )) + while [ ! -e "$cryptsource" ]; do + # retry for LVM devices every 10 seconds + if [ ${slumber} -eq $(( ${slumber}/100*100 )) ]; then + activate_vg + fi + + /bin/sleep 0.1 + slumber=$(( ${slumber} - 1 )) + [ ${slumber} -gt 0 ] || break + done + + if [ ${slumber} -gt 0 ]; then + log_end_msg 0 + else + log_end_msg 1 || true + fi + fi + udev_settle + + # We've given up, but we'll let the user fix matters if they can + if [ ! -e "${cryptsource}" ]; then + echo " ALERT! ${cryptsource} does not exist." + echo " Check cryptopts=source= bootarg: cat /proc/cmdline" + echo " or missing modules, devices: cat /proc/modules; ls /dev" + panic -r "Dropping to a shell. Will skip ${cryptsource} if you can't fix." + fi + + if [ ! -e "${cryptsource}" ]; then + return 1 + fi + + + # Prepare commands + cryptopen="/sbin/cryptsetup -T 1" + if [ "$cryptdiscard" = "yes" ]; then + cryptopen="$cryptopen --allow-discards" + fi + if [ -n "$cryptheader" ]; then + cryptopen="$cryptopen --header=$cryptheader" + fi + if /sbin/cryptsetup isLuks ${cryptheader:-$cryptsource} >/dev/null 2>&1; then + cryptopen="$cryptopen open --type luks $cryptsource $crypttarget --key-file=-" + elif [ "$crypttcrypt" = "yes" ]; then + cryptopen="$cryptopen open --type tcrypt $cryptsource $crypttarget" + else + cryptopen="$cryptopen -c $cryptcipher -s $cryptsize -h $crypthash open --type plain $cryptsource $crypttarget --key-file=-" + fi + cryptremove="/sbin/cryptsetup remove $crypttarget" + NEWROOT="/dev/mapper/$crypttarget" + + # Try to get a satisfactory password $crypttries times + count=0 + crypttries=1 + while [ $crypttries -le 0 ] || [ $count -lt $crypttries ]; do + export CRYPTTAB_TRIED="$count" + count=$(( $count + 1 )) + + cryptkeyscript="/bin/cat" + cryptkey="$LOCK_FILE" + + if [ ! -e "$NEWROOT" ]; then + if ! crypttarget="$crypttarget" cryptsource="$cryptsource" \ + $cryptkeyscript "$cryptkey" | $cryptopen; then + message "autounlock: cryptsetup failed, gathred bad key?" + break + fi + fi + + if [ ! -e "$NEWROOT" ]; then + message "cryptsetup: unknown error setting up device mapping" + return 1 + fi + + message "cryptsetup: $crypttarget set up successfully" + break + done + + if [ $crypttries -gt 0 ] && [ $count -gt $crypttries ]; then + message "cryptsetup: maximum number of tries exceeded for $crypttarget" + return 1 + fi + + udev_settle + return 0 +} + +# Look into phisical environment around and gather information +gather_key_information() +{ + if [ ! -f "/etc/auto_unlock.conf" ]; then + log_failure_msg "Auto-unlock does not have config in initramfs" + exit 0 + fi + + . /etc/auto_unlock.conf + udev_settle + + # gather local Wifi MAC + ifconfig $WIFI_INTERFACE up + WIFI_MAC=`iwlist scanning 2>/dev/null | egrep 'Address|ESSID' | grep -B1 'ESSID:"'"${WIFI_NETWORK}"'"' | awk -F'Address: ' '/Address/ {print $2}'` + + # gather external monitor EDID info + EDID_LINES=`get-edid 2>/dev/null | parse-edid | egrep 'Identifier|ModelName|VendorName|Manufactured week|DisplaySize' | LANG=C sort` + + echo -e "$WIFI_MAC\n$EDID_LINES" > $LOCK_FILE +} + +finish() +{ + rm -f $LOCK_FILE +} + +# +# Begin real processing +# + +# Acquire potential key from connected devices + +LOCK_FILE=/conf/autounlock.key +trap finish EXIT + +gather_key_information + +# Do we have any settings from the /conf/conf.d/cryptroot file? +if [ -r /conf/conf.d/cryptroot ]; then + while read mapping <&3; do + setup_mapping "$mapping" 3<&- + done 3< /conf/conf.d/cryptroot +fi + +exit 0 + diff --git a/usr/local/bin/autounlock_install_dependency.sh b/usr/local/bin/autounlock_install_dependency.sh new file mode 100755 index 0000000..12d6698 --- /dev/null +++ b/usr/local/bin/autounlock_install_dependency.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +apt install read-edid diff --git a/usr/local/bin/autounlock_install_key.sh b/usr/local/bin/autounlock_install_key.sh new file mode 100755 index 0000000..50bb464 --- /dev/null +++ b/usr/local/bin/autounlock_install_key.sh @@ -0,0 +1,100 @@ +#!/bin/bash + +mkdir -p /run/keytemp +LOCK_FILE="/run/keytemp/lock_key" + +function finish { + rm -f $LOCK_FILE +} +trap finish EXIT + + +echo "Generates key based on your Wifi MAC address and infromation from your external monitor" +echo "If you do not have external monitor attached, please turn it off in /usr/local/etc/auto_unlock.conf" +echo "It can decrease security of auto unlock because Wifi MAC is visible to everyone in proximity of your location." + +. /usr/local/etc/auto_unlock.conf + +if [ "$KEYSLOT" = "0" ]; then + echo "Cannot use key slot 0" + exit 10 +fi + +echo "Looking up AP MAC for Wifi network $WIFI_NETWORK" + +WIFI_MAC=`iwlist scanning 2>/dev/null | egrep 'Address|ESSID' | grep -B1 'ESSID:"'"${WIFI_NETWORK}"'"' | awk -F'Address: ' '/Address/ {print $2}'` + +echo "Found MAC == |$WIFI_MAC| " + +EDID_LINES=`get-edid 2>/dev/null | parse-edid | egrep 'Identifier|ModelName|VendorName|Manufactured week|DisplaySize' | LANG=C sort ` + +echo "Gathered display information" +echo -e "--------\n${EDID_LINES}\n-----------\n" + +echo "Please verify that Wifi MAC of Access Point and display information are valid" +echo "Especially verify that your external display is recognized, not build-in one" + +echo "Type in YES to set-up LUKS key based on information above" +read ANSWER + +if [ "$ANSWER" != "YES" ]; then + echo "Exiting without setup" + exit 5 +fi + +echo -e "$WIFI_MAC\n$EDID_LINES" > $LOCK_FILE +echo "Checksum of lockfile $(md5sum $LOCK_FILE)" +echo "Stored lock file in $LOCK_FILE, please delete it manually if you cancel script execution" + +echo "Parsing /etc/crypttab" + +for TEXT in $(awk '/^..*$/ {print $1 ":" $2}' /etc/crypttab); do + + NAME=`echo "$TEXT" | cut -d: -f1` + DEVICE=`echo "$TEXT" | cut -d: -f2` + + case $DEVICE in + /dev/*) ;; + UUID=*) UUID=${DEVICE##UUID=} + DEVICE=$(blkid -U "$UUID") + ;; + esac + + if [ -z "$DEVICE" ]; then + continue + fi + + echo "found partition $DEVICE for $NAME" + + if [ ! -b $DEVICE ]; then + echo "Cannot work on $DEVICE, not a block device" + continue + fi + + SLOTS=`cryptsetup luksDump $DEVICE | grep '^Key Slot'` + SLOT_USAGE=`echo "$SLOTS" | grep "Key Slot $KEYSLOT"` + + echo -e "slots usage for $DEVICE\n------\n${SLOTS}\n---------\n" + + SLOT_OK= + case "$SLOT_USAGE" in + *DISABLED) SLOT_OK=1 ;; + *ENABLED) + echo "Slot $KEYSLOT is used for device $NAME $DEVICE" + echo "Do you want to override it? Type YES to override" + read ANSWER + if [ "$ANSWER" != "YES" ]; then + echo "Not overriding ..." + continue + fi + cryptsetup luksKillSlot $DEVICE $KEYSLOT + SLOT_OK=1 + esac + + if [ "$SLOT_OK" = "1" ]; then + cryptsetup luksAddKey --key-slot $KEYSLOT $DEVICE $LOCK_FILE + fi + +done + + diff --git a/usr/local/etc/auto_unlock.conf b/usr/local/etc/auto_unlock.conf new file mode 100644 index 0000000..3841d80 --- /dev/null +++ b/usr/local/etc/auto_unlock.conf @@ -0,0 +1,3 @@ +WIFI_INTERFACE="wlan0" +WIFI_NETWORK="YOUR_NETWORK_NAME" +KEYSLOT=7