Commit 888ccc5a authored by intrigeri's avatar intrigeri

Return to the initramfs (unpacked in /run/initramfs) on shutdown (refs:...

Return to the initramfs (unpacked in /run/initramfs) on shutdown (refs: #12428, #12354, Debian#778849).

… otherwise the aufs read-write (tmpfs) branch, among possibly other things,
can't be properly unmounted and its content remains in memory.

Notes:

 * We have to handle some unmounting ourselves in initramfs-pre-shutdown-hook:
   systemd-shutdown doesn't manage to unmount the aufs read-write
   branch (/oldroot/lib/live/mount/overlay) as it is needed by the
   aufs (/oldroot) filesystem, and reciprocally it cannot unmount /oldroot as it
   is kept busy by /oldroot/lib/live/mount/*. So we disentangle this mess
   ourselves. And we have to manually empty the aufs read-write (tmpfs) branch,
   otherwise for some reason its content remains in memory. This code will of
   course need to be adapted for overlayfs some day.

 * We lock /bin/kill in memory: apparently systemd-exit.service needs it.

 * We remount /run on shutdown *before* dropping caches, just in case dropping
   caches removes what we've locked into memory.

 * We unpack the initramfs to /run/initramfs at *boot* time: sadly, I was not
   able to have it unpacked reliably in udev-watchdog-wrapper when the boot
   medium is ejected, so we'll use a little bit more RAM (instead of locking the
   compressed initramfs into memory, we're storing the uncompressed one there)
   and probably slow down the boot a bit, in order to make emergency shutdown
   robust. Note, however, that we save some of the RAM used by the uncompressed
   initramfs by deleting the worst offenders (kernel modules).

 * For now the whole procedure is quite noisy on the screen: the pre-shutdown
   hook runs under "set -x", doesn't run "clear", and spits out lots of
   debugging information. The goal is to enable users to provide useful
   debugging data if they have problems with emergency shutdown. Once we have
   shipped this code in a few releases and trust it's robust enough, we can
   surely reconsider and polish the UX by making the output less noisy.

 * We use absolute paths in many places to avoid $PATH lookup which might
   fail if the root filesystem is not there anymore.
parent 690b968b
......@@ -8,9 +8,9 @@ set -e
systemctl enable memlockd.service
# Enable our own systemd unit files
systemctl enable initramfs-shutdown.service
systemctl enable onion-grater.service
systemctl enable tails-autotest-remote-shell.service
systemctl enable tails-reconfigure-memlockd.service
systemctl enable tails-set-wireless-devices-state.service
systemctl enable tails-shutdown-on-media-removal.service
systemctl enable tails-tor-has-bootstrapped.target
......
+/bin/clear
+/bin/cat
+/bin/echo
+/bin/grep
+/bin/kill
+/bin/loginctl
+/bin/ls
+/bin/mkdir
+/bin/mktemp
+/bin/mount
+/bin/mv
+/bin/plymouth
+/bin/rm
+/bin/sh
+/bin/sleep
+/bin/stty
+/bin/systemctl
/lib/systemd/system-shutdown/tails
+/lib/systemd/systemd-shutdown
+/usr/bin/eject
+/usr/bin/pkill
+/usr/local/sbin/udev-watchdog
#!/bin/sh
echo 3 > /proc/sys/vm/drop_caches
set -x
# This script is only run by the instance of systemd-shutdown that's
# run outside of the initramfs, and not by the other instance of
# systemd-shutdown that's run (as /shutdown) after returning to the
# initramfs during shutdown: in the initramfs, this script is
# overwritten with /usr/local/lib/initramfs-pre-shutdown-hook.
# Otherwise systemd-shutdown cannot execute /run/initramfs/shutdown
/bin/mount -o remount,exec /run
if [ -e /run/tails_shutdown_debugging ] ; then
/bin/plymouth quit
/bin/stty sane < /dev/console
echo "Going to sleep 10 minutes. Happy dumping!" > /dev/console
sleep 600
fi
# Debugging
/bin/ls -l /run/initramfs
echo 3 > /proc/sys/vm/drop_caches
[Unit]
Description=Reconfigure memlockd depending on running kernel
Description=Prepare /run/initramfs for shutdown
Documentation=https://tails.boum.org/contribute/design/memory_erasure/
Before=memlockd.service
ConditionPathExists=!/run/initramfs/bin/sh
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/tails-reconfigure-memlockd
RemainAfterExit=yes
CapabilityBoundingSet=
PrivateDevices=yes
PrivateNetwork=yes
PrivateTmp=yes
ProtectHome=yes
ProtectSystem=yes
Type=oneshot
ExecStart=/usr/local/lib/initramfs-restore
[Install]
WantedBy=multi-user.target
[Unit]
Description=Wipe memory on live media removal
Documentation=https://tails.boum.org/contribute/design/memory_erasure/
After=memlockd.service tails-reconfigure-memlockd.service
After=memlockd.service
[Service]
Type=simple
......
#!/bin/sh
# This script is installed by /usr/share/initramfs-tools/hooks/shutdown
# into the initramfs, as /lib/systemd/system-shutdown/tails. It's run by the
# copy of systemd-shutdown that runs (as /shutdown) from inside
# the unpacked initramfs, immediately before executing the requested action
# (halt/poweroff/reboot).
set -x
### Unmount relevant filesystems
# Debugging
mount
# Otherwise aufs pseudo-links can't be cleaned and we cannot drop caches.
# This may also help for tracking remaining mounts.
mount -o remount,rw /proc
# Otherwise we can't create new mountpoints in /mnt
mount -o remount,rw /
# Otherwise aufs pseudo-links can't be removed while unmounting /oldroot,
# and we can't clean up the content of /mnt/live/overlay.
mount -o remount,rw /oldroot/lib/live/mount/overlay
# Move /oldroot/* mountpoints out of the way
mkdir -p /mnt/live/overlay
mount --move \
/oldroot/lib/live/mount/overlay \
/mnt/live/overlay
mkdir -p /mnt/live/squashfs
mount --move \
/oldroot/lib/live/mount/rootfs/filesystem.squashfs \
/mnt/live/squashfs
mkdir -p /mnt/live/medium
mount --move \
/oldroot/lib/live/mount/medium \
/mnt/live/medium
# Finally, really unmount relevant filesystems
umount /oldroot
rm -rf /mnt/live/overlay/.w* /mnt/live/overlay/*
umount /mnt/live/overlay
# Debugging
mount
### Ensure any remaining disk cache is erased by Linux' memory poisoning
echo 3 > /proc/sys/vm/drop_caches
### Pause if the test suite wants us to
if [ -e /tails_shutdown_debugging ] ; then
echo "Going to sleep 2 minutes. Happy dumping!"
sleep 120
fi
#!/bin/sh
set -e
set -u
WORKDIR=$(/bin/mktemp -d)
/usr/bin/unmkinitramfs \
"$(/usr/local/bin/tails-get-bootinfo initrd)" \
"$WORKDIR"
# We should not need any kernel modules in there at shutdown time,
# and they take 66% of the entire uncompressed initramfs size, so
# let's save some RAM.
/bin/rm -rf "$WORKDIR"/main/lib/modules
/bin/mv "$WORKDIR"/main/* /run/initramfs/
/bin/rm -rf "$WORKDIR"
......@@ -51,6 +51,16 @@ do_stop() {
/usr/bin/eject -m "${BOOT_DEVICE}" || true
fi
# Kill everything run by amnesia or Debian-gdm, otherwise emergency
# shutdown fails for some reason. Incidentally, this also allows
# the test suite to look for a known message ("Happy dumping!")
# on the screen.
/bin/loginctl --signal=9 kill-user amnesia || true
/bin/systemctl stop gdm.service || true
/bin/systemctl --signal=9 kill gdm.service gdm.service || true
/bin/loginctl --signal=9 kill-user Debian-gdm || true
# Finally, return to the initramfs and poweroff the system
/bin/systemctl --force poweroff
}
......
#!/bin/sh
set -e
set -u
PATH="/usr/local/bin:${PATH}"
MEMLOCKD_CONF=/etc/memlockd.cfg
tails-get-bootinfo kernel >> "$MEMLOCKD_CONF"
tails-get-bootinfo initrd >> "$MEMLOCKD_CONF"
#!/bin/sh
set -e
PREREQ=""
prereqs () {
echo "${PREREQ}"
}
case "${1}" in
prereqs)
prereqs
exit 0
;;
esac
. /usr/share/initramfs-tools/hook-functions
# systemd-shutdown itself
mkdir -p $DESTDIR/lib/systemd
copy_exec /lib/systemd/systemd-shutdown /shutdown
# Our shutdown hook (run from inside the initramfs)
mkdir -p $DESTDIR/lib/systemd/system-shutdown
copy_file "regular file" \
/usr/local/lib/initramfs-pre-shutdown-hook \
/lib/systemd/system-shutdown/tails
# Ensure systemd detects when we're in the initramfs on shutdown
# (see the in_initrd function in the systemd source tree)
touch $DESTDIR/etc/initrd-release
exit 0
......@@ -66,12 +66,14 @@ Feature: System memory erasure on shutdown
# ensure the pattern is in memory due to tmpfs, not to disk cache
And I drop all kernel caches
Then patterns cover at least 128 MiB in the guest's memory
When I trigger shutdown and wait for the system to be almost halted
When I trigger shutdown
And I wait 20 seconds
Then I find very few patterns in the guest's memory
Scenario: Erasure of read and write disk caches of persistent data on shutdown
Given I have started Tails without network from a USB drive with a persistent partition enabled and logged in
And I prepare Tails for memory erasure tests
When I fill a 128 MiB file with a known pattern on the persistent filesystem
When I trigger shutdown and wait for the system to be almost halted
When I trigger shutdown
And I wait 20 seconds
Then I find very few patterns in the guest's memory
......@@ -61,7 +61,7 @@ Given /^I prepare Tails for memory erasure tests$/ do
step "I drop all kernel caches"
# Have our initramfs-pre-shutdown-hook sleep for a while
$vm.execute_successfully("touch /run/tails_shutdown_debugging")
$vm.execute_successfully("touch /run/initramfs/tails_shutdown_debugging")
# The (guest) kernel may freeze when approaching full memory without
# adjusting the OOM killer and memory overcommitment limitations.
......@@ -239,7 +239,6 @@ When(/^I drop all kernel caches$/) do
$vm.execute_successfully("echo 3 > /proc/sys/vm/drop_caches")
end
When(/^I trigger shutdown and wait for the system to be almost halted$/) do
When(/^I trigger shutdown$/) do
$vm.spawn("halt")
@screen.wait('ShutdownAlmostCompleted.png', 60)
end
......@@ -24,6 +24,26 @@ problems (e.g. [[!tails_ticket 12354]], [[!tails_ticket 11786]]).
So it was removed in Tails 3.0, and Tails now relies on the Linux
kernel's freed memory poisoning feature.
But memory poisoning only works when memory is actually freed,
and a regular shutdown would not free the memory used by the aufs
read-write branch. So we use the `systemd-shutdown` ability to return
to the initramfs, to ensure the root filesystem is unmounted.
The initramfs is unpacked in `/run/initramfs` at boot time:
* [[!tails_gitweb config/chroot_local-includes/lib/systemd/system/initramfs-shutdown.service]]
* [[!tails_gitweb config/chroot_local-includes/usr/local/lib/initramfs-restore]]
* [[!tails_gitweb config/chroot_local-includes/usr/local/lib/udev-watchdog-wrapper]]
… so that the copy of `systemd-shutdown` that runs in the real,
non-initramfs system can switch root into `/run/initramfs`, and run
another copy of `systemd-shutdown` that's
[[!tails_gitweb config/chroot_local-includes/usr/share/initramfs-tools/hooks/shutdown desc="included"]]
in the initramfs. That one will unmount all filesystems, run
[[!tails_gitweb config/chroot_local-includes/usr/local/lib/initramfs-pre-shutdown-hook desc="a custom hook"]]
that helps us automatically test this behavior, and finally perform
the requested poweroff/reboot action.
#### Triggers
Different kinds of events trigger the memory erasure process. All lead
......@@ -64,5 +84,3 @@ to memory erasure time.
- [[!tails_gitweb config/chroot_local-includes/etc/memlockd.cfg]]
- [[!tails_gitweb config/chroot_local-patches/keep_memlockd_on_shutdown.diff]]
- [[!tails_gitweb config/chroot_local-includes/lib/systemd/system/memlockd.service.d/oom.conf]]
- [[!tails_gitweb config/chroot_local-includes/lib/systemd/system/tails-reconfigure-memlockd.service]]
- [[!tails_gitweb config/chroot_local-includes/usr/local/sbin/tails-reconfigure-memlockd]]
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment