Introduction
unattended-upgrades (referenced as uu later) is really a neat tool to keep
you Debian and derivatives up to date with security.
You can get notified of upgrades by mail, which is great if your server has mail capabilities, or find the whole mail ecosystem actually a good thing (I don't).
I've a webhook that is feeding a database and output the latest events as RSS. Instead of having of reading some local mail, I'm better off being notified through RSS on many possible devices.
uu proposes a Unattended-Upgrade::Post-Invoke-Success configuration option.
After many headaches, I never managed to make it run, but the README.md
mentionned a plugin system in python. We're going to use that.
Creating an outdated package
At first we need to be able to trigger an upgrade at will. The idea here is to
create an outdated package with no other dependencies. On my servers, it
appears that unzip is that, I just need a fake, inferior, version of it.
Installing equivs
sudo apt install equivs
Creating a fake package
Fill /tmp/fake_unzip with this:
Priority: optional
Standards-Version: 4.5.1
Package: unzip
Version: 0.0.1
Maintainer: Mister fake
Architecture: amd64
Description: fake unzip package
This is a fake package. Upgrade me!
Let's build our deb package:
equivs-build /tmp/fake_unzip
Now you got a fake package at /tmp/unzip_0.0.1_amd64.deb. You can copy it in your server (we'll use /tmp again there)
Creating a plugin
Back to your server.
uu's use python for its plugin system. You'll need to install python3-requests to call the webhook URL.
You then need to create the plugin directory for uu:
sudo mkdir -p /etc/unattended-upgrades/plugins/
cd /etc/unattended-upgrades/plugins
The plugin
You can create /etc/unattended-upgrades/plugins/report.py, with the following
code. Indeed you'll needed adjustments. Read carefully the comments, more stuff
is available through the plugin, notably hostname (I don't need that):
#!/usr/bin/env python3
import sys
import requests
class UnattendedUpgradesPluginWebhook:
"""Call a webhook on unattended upgrade. Must start with
UnattendedUpgradesPlugin*"""
def post_webhook(self, title, description):
try:
r = requests.post("https://your_server/your_webhook",
data={"title": title, "description": description},
timeout=30)
r.raise_for_status()
except Exception as e:
print(e, file=sys.stderr)
sys.exit(1)
def postrun(self, result):
# The data in result is a python class called PluginDataPostrun.
# It can be viewed via "pydoc3 /usr/bin/unattended-upgrades"
# and then searching for PluginDataPostrun.
if len(result.packages_upgraded) == 0 and result.success:
print("No packages were upgraded, don't send a notification")
sys.exit()
if not result.success:
# Warning emoji
title = "⚠️ Unattended-upgrades results: FAILURE"
description = "<pre><code>" + result.log_dpkg + "</code></pre>"
self.post_webhook(title, description)
sys.exit(1)
reboot = "[Reboot required] " if result.reboot_required else ""
title = reboot + "Unattended-upgrades results: Success"
description = f"""
<p><b>Upgraded packages:</b>
{", ".join(result.packages_upgraded)}
</p>
<pre><code>{result.log_dpkg}
</code></pre>
"""
self.post_webhook(title, description)
If you really need credentials in the code, don't forget to put proper permissions and ownership
Testing
You can now test by cycling deinstalls/unattended-upgrades, like this:
sudo apt -y remove unzip; sudo apt install /tmp/unzip_0.0.1_amd64.deb; sudo unattended-upgrade --debug -vvv
Have fun!