├── Makefile ├── README.md ├── arch-luks-suspend ├── initcpio-hook ├── initramfs-suspend └── systemd-suspend.service /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all install 2 | 3 | INSTALL_DIR := /usr/lib/arch-luks-suspend 4 | 5 | all: 6 | 7 | install: 8 | install -Dm755 arch-luks-suspend "$(DESTDIR)$(INSTALL_DIR)/arch-luks-suspend" 9 | install -Dm755 initramfs-suspend "$(DESTDIR)$(INSTALL_DIR)/initramfs-suspend" 10 | install -Dm644 initcpio-hook "$(DESTDIR)/usr/lib/initcpio/install/suspend" 11 | install -Dm644 systemd-suspend.service "$(DESTDIR)/etc/systemd/system/systemd-suspend.service" 12 | 13 | # vim:set sw=4 ts=4 noet: 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | arch-luks-suspend 2 | ================== 3 | 4 | A script for [Arch Linux][] to lock the encrypted root volume on suspend. 5 | 6 | When using [dm-crypt with LUKS][] to set up full system encryption, the 7 | encryption key is kept in memory when suspending the system. This drawback 8 | defeats the purpose of encryption if you carry around your suspended laptop 9 | a lot. One can use the `cryptsetup luksSuspend` command to freeze all I/O and 10 | flush the key from memory, but special care must be taken when applying it to 11 | the root device. 12 | 13 | The `arch-linux-suspend` script replaces the default suspend mechanism of 14 | systemd. It changes root to initramfs in order to perform the `luksSuspend`, 15 | actual suspend, and `luksResume` operations. It relies on the `shutdown` 16 | initcpio hook to provide access to the initramfs. 17 | 18 | [Arch Linux]: https://www.archlinux.org/ 19 | [dm-crypt with LUKS]: https://wiki.archlinux.org/index.php/Dm-crypt_with_LUKS 20 | 21 | 22 | Installation 23 | ------------- 24 | 25 | 1. Install this AUR package: https://aur.archlinux.org/packages/arch-luks-suspend-git/ 26 | Alternatively, run `make install` as root. 27 | 2. Edit `/etc/mkinitcpio.conf` and make sure the following hooks are enabled: 28 | `udev`, `encrypt`, `shutdown`, `suspend`. 29 | 3. Rebuild the initramfs: `mkinitcpio -p linux`. 30 | 4. Reboot. 31 | 32 | 33 | Author and license 34 | ------------------- 35 | 36 | Copyright 2013 Vianney le Clément de Saint-Marcq 37 | 38 | This program is free software: you can redistribute it and/or modify 39 | it under the terms of the GNU General Public License as published by 40 | the Free Software Foundation; version 3 of the License. 41 | 42 | This program is distributed in the hope that it will be useful, 43 | but WITHOUT ANY WARRANTY; without even the implied warranty of 44 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 45 | GNU General Public License for more details. 46 | 47 | You should have received a copy of the GNU General Public License 48 | along with This program. If not, see . 49 | -------------------------------------------------------------------------------- /arch-luks-suspend: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e -u 4 | trap 'echo "Press ENTER to continue."; read dummy' ERR 5 | 6 | ################################################################################ 7 | ## Parameters and helper functions 8 | 9 | INITRAMFS_DIR=/run/initramfs 10 | SYSTEM_SLEEP_PATH=/usr/lib/systemd/system-sleep 11 | BIND_PATHS="/sys /proc /dev /run" 12 | REMOUNT=0 13 | # Retrieve cryptdevice name from boot cmdline 14 | CRYPTNAME="$(sed -n 's/.*cryptdevice=[^: ]*:\([^: ]*\).*$/\1/p' /proc/cmdline)" 15 | 16 | # run_dir DIR ARGS... 17 | # Run all executable scripts in directory DIR with arguments ARGS 18 | run_dir() { 19 | local dir=$1 20 | shift 21 | find "${dir}" -type f -executable -exec "{}" "$@" ";" 22 | } 23 | 24 | # Restore chroot 25 | umount_initramfs() { 26 | local p 27 | for p in ${BIND_PATHS}; do 28 | ! mountpoint -q "${INITRAMFS_DIR}${p}" || umount "${INITRAMFS_DIR}${p}" 29 | done 30 | } 31 | 32 | ext4_cryptdevice_mount_options() { 33 | local mt="$(grep "^/dev/mapper/${1} " /proc/mounts | cut -d ' ' -f 3,4)" 34 | if [[ "${mt:0:5}" == "ext4 " ]]; then 35 | echo "${mt:5}" 36 | fi 37 | } 38 | 39 | ################################################################################ 40 | ## Main script 41 | 42 | [ -e "${INITRAMFS_DIR}/suspend" ] || exec /usr/lib/systemd/systemd-sleep suspend 43 | 44 | # Prepare chroot 45 | trap umount_initramfs EXIT 46 | for p in ${BIND_PATHS}; do 47 | mount -o bind ${p} "${INITRAMFS_DIR}${p}" 48 | done 49 | 50 | # Run pre-suspend scripts 51 | run_dir "${SYSTEM_SLEEP_PATH}" pre suspend 52 | 53 | # Stop udev service and prevent it to be autostarted. 54 | # Otherwise, luksResume will hang waiting for udev, which is itself waiting 55 | # for I/O on the root device. 56 | systemctl stop systemd-udevd-control.socket 57 | systemctl stop systemd-udevd-kernel.socket 58 | systemctl stop systemd-udevd.service 59 | 60 | # Journalled ext4 filesystems in kernel versions 3.11+ will block suspend 61 | # if mounted with `barrier=1`, which is the default. Temporarily remount with 62 | # `barrier=0` if this is true of the crypt fs. 63 | MOUNT_OPTS="$(ext4_cryptdevice_mount_options "$CRYPTNAME")" 64 | if [[ "$MOUNT_OPTS" ]] && ! [[ "$MOUNT_OPTS" == *nobarrier* || "$MOUNT_OPTS" == *barrier=0* ]]; then 65 | REMOUNT=1 66 | mount -o remount,"$MOUNT_OPTS",barrier=0 / 67 | fi 68 | 69 | # Synchronize filesystems before luksSuspend 70 | sync 71 | 72 | # Hand over execution to script inside initramfs 73 | cd "${INITRAMFS_DIR}" 74 | chroot . /suspend "$CRYPTNAME" 75 | 76 | # Restore original mount options if necessary 77 | if ((REMOUNT)); then 78 | mount -o remount,"$MOUNT_OPTS",barrier=1 / 79 | fi 80 | 81 | # Restart udev 82 | systemctl start systemd-udevd-control.socket 83 | systemctl start systemd-udevd-kernel.socket 84 | systemctl start systemd-udevd.service 85 | 86 | # Run post-suspend scripts 87 | run_dir "${SYSTEM_SLEEP_PATH}" post suspend 88 | 89 | # Unlock user sessions 90 | loginctl unlock-sessions 91 | -------------------------------------------------------------------------------- /initcpio-hook: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | build() { 4 | add_file "/usr/lib/arch-luks-suspend/initramfs-suspend" "/suspend" 755 5 | } 6 | 7 | help() { 8 | cat < /sys/power/state 13 | 14 | # Resume root device 15 | [ -z "${cryptname}" ] || 16 | while ! cryptsetup luksResume "${cryptname}"; do sleep 2; done 17 | 18 | # Stop udev from initramfs, as the real daemon from rootfs will be restarted 19 | udevadm control --exit 20 | -------------------------------------------------------------------------------- /systemd-suspend.service: -------------------------------------------------------------------------------- 1 | # This file has been adapted from systemd. 2 | # 3 | # systemd is free software; you can redistribute it and/or modify it 4 | # under the terms of the GNU Lesser General Public License as published by 5 | # the Free Software Foundation; either version 2.1 of the License, or 6 | # (at your option) any later version. 7 | 8 | [Unit] 9 | Description=Suspend 10 | Documentation=man:systemd-suspend.service(8) 11 | DefaultDependencies=no 12 | Requires=sleep.target 13 | After=sleep.target 14 | 15 | [Service] 16 | Type=oneshot 17 | ExecStart=/usr/bin/openvt -ws /usr/lib/arch-luks-suspend/arch-luks-suspend 18 | --------------------------------------------------------------------------------