Automatically shut down sensitive Qubes I always forget to shutdown when I close my laptop (password managers, sys-net, vault, sys-usb, etc). I like the idea of ensuring specific Qubes have been shutdown in case of theft or not knowing whether my laptop will resume in a friendly or hostile environment.
Configuration
Mostly takes place from dom0
Tag VMs to shutdown upon suspend with a tag of your choice qvm-tags <VM NAME> a shutdown-on-suspend
Edit the 52qubes-pause-vms script sudo vi /usr/lib64/pm-utils/sleep.d/52qubes-pause-vms
Add the following commands to the suspend section (above the existing qubesd-query command), matching the tag you set earlier
# Cycle through all Qubes with the shutdown-on-suspend tag
for QUBE in $(qvm-ls --tags shutdown-upon-suspend | awk 'NR>1 {print $1}'); do
# Trigger parallel shutdown commands in the background
qvm-shutdown --wait --force $QUBE &
# Close the for loop
done
# Wait until all shutdown commands complete
wait
Bonus
Install the qubes-app-shutdown-idle package inside of your TemplateVM or StandaloneVM and change the TIMEOUT_SECONDS value in /lib/python*/site-packages/qubesidle/idleness_monitor.py or /usr/lib/python3/dist-packages/qubesidle/idleness_monitor.py to something shorter (like 20 seconds) and check the Shutdown when idle for more than 15 minutes box in the Qube Manager Settings to have your Qubes automatically shut down once you close the app window.
Troubleshooting
Make sure the target Qubes are running then trigger a suspend by running systemctl suspend and note how long it takes for the computer to go to suspend. If it suspends quickly, there’s a chance it isn’t working.
Check logs via journalctl -b suspend
Feedback
Always open to it. This community has been so helpful to me–I wanted to share a guide that I hope may help someone else.
Seems like a recent Dom0 update might’ve removed the modifications I made to /usr/lib64/pm-utils/sleep.d/52qubes-pause-vms
I tried to append/prepend my script to /usr/lib/systemd/system/qubes-suspend.service’s ExecStart= line but couldn’t get it to trigger my script.
Ultimately, I moved my shutdown on sleep script to /usr/local/bin/ and inserted it where I had inserted my commands in /usr/lib64/pm-utils/sleep.d/52qubes-pause-vms. The main difference being that the next time this file gets overwritten, I won’t lose my entire script.
I started a little script to re-insert my script into /usr/lib64/pm-utils/sleep.d/52qubes-pause-vms, but I’m not sure the best way to hook it. Open to ideas.
Here’s the script I wrote to re-add my script:
### Variables
myscript="/usr/local/bin/<my script>"
target="/usr/lib64/pm-utils/sleep.d/52qubes-pause-vms"
### Check to see if the script is in place; add it if not, peace out if so
if ! grep -q "$myscript" "$target"; then
# If the search string is not found, insert the new string
sed -i "/hibernate)/a\ $myscript" $target
fi
I played around with /usr/lib/systemd/system/qubes-suspend.service’s ExecStart= line a little more and was able to insert my script in front of /usr/lib64/pm-utils/sleep.d/52qubes-pause-vms with just a space (Not sure where I got the idea to separate commands here with a semicolon when a space appears to work just fine). I commented out calling my script from /usr/lib64/pm-utils/sleep.d/52qubes-pause-vms and tested. Now, who knows whether a future update might break this too. I’d prefer not to have to resort to what I came up with above (Automatically shutdown specific Qubes on Suspend - #2 by SensibleBurrito).
Configuration (revised)
Tag VMs to shutdown upon suspend with a tag of your choice qvm-tags <VM NAME> a shutdown-on-suspend
Create an executable script to cycle through tagged VMs and shut them down
# Cycle through all Qubes with the shutdown-on-suspend tag
for QUBE in $(qvm-ls --raw-list --tags shutdown-upon-suspend); do
# Trigger parallel shutdown commands in the background
qvm-shutdown --wait --force $QUBE &
# Close the for loop
done
# Wait until all shutdown commands complete
wait
Edit the qubes-suspend service unit file sudo vi /usr/lib/systemd/system/qubes-suspend.service
Insert the path to your script between ExecStart= and the existing command ExecStart=/usr/local/bin/shutdown-on-suspend.sh /usr/lib64/pm-utils/sleep.d/52qubes-pause-vms suspend suspend
Update: Inserted barto’s improvement to the executable script.
Update 2: My system seems less stable after resuming. Qubes that were open seem to be non-responsive and I can’t seem to start my vpn qube. I read more into modifying the service unit file and saw that multiple commands might need to be separated by a semicolon on both sides, which I hadn’t tried before: ;
Nice work!
To make it faster, you can skip the | awk ... construct and run instead just qvm-ls --raw-list --tags shutdown-upon-suspend which returns directly the qube name without any header and additional clutter.
So:
# Cycle through all Qubes with the shutdown-on-suspend tag
for QUBE in $(qvm-ls --raw-list --tags shutdown-upon-suspend); do
# Trigger parallel shutdown commands in the background
qvm-shutdown --wait --force $QUBE &
# Close the for loop
done
# Wait until all shutdown commands complete
wait
Would you happen to know whether service unit files gets overwritten/modified during updates? That is, do you think the changes to /usr/lib/systemd/system/qubes-suspend.service will persist across updates?
So every time there is a change to this QubesOS-specific rpm, there exists a risk of the qubes-suspend.service file being clobbered. I say “risk” because it may happen that the system spares your changes and creates a “.rpmnew” file - which is not guaranteed.
@barto,
Thanks. That sudo rpm -qf command to surface which package installed a specific file is neat.
I’m not sure if its making a difference, but separating the ExecStart commands in /usr/lib/systemd/system/qubes-suspend.service with a semicolon with spaces on both sides ( ; ) seems to have helped stability.
I also came across the ExecStartPre=directive (ref. man systemd.service) which could be a potential alternative to daisy-chaining the commands in ExecStart.