dangerzone/docs/developer/updates.md
Alex Pyrgiotis 5b00f56a1f
doc: Add design doc for the update notifications
Add a design document for the update notifications mechanism, adapted
from the write-up in the original GitHub issue.

Refs #189
2024-06-12 13:22:45 +03:00

10 KiB

Update notifications

This design document explains how the notification mechanism for Dangerzone updates works, what are its benefits and limitations, and what other alternatives we have considered. It has been adapted by discussions on GitHub issue #189, and has been updated to reflect the current design.

A user-facing document on how update notifications work can be found in https://github.com/freedomofpress/dangerzone/wiki/Updates

Design overview

This feature introduces a hamburger icon that will be visible across almost all of the Dangerzone windows. This will be used to notify the users about updates.

First run

We detect it's the first time Dangerzone runs because the settings["updater_last_check"] is None.

Add the following keys in our settings.json file.

  • "updater_check": None: Whether to check for updates or not. None means that the user has not decided yet, and is the default.
  • "updater_last_check": None: The last time we checked for updates (in seconds from Unix epoch). None means that we haven't checked yet.
  • "updater_latest_version": "0.4.2": The latest version that the Dangerzone updater has detected. By default it's the current version.
  • "updater_latest_changelog": "": The latest changelog that the Dangerzone updater has detected. By default it's empty.
  • "updater_errors: 0: The number of update check errors that we have encountered in a row.

Note:

  • If on Linux, make "updater_check": False, since we normally have other update channels for these platforms.

Second run

We detect it's the second time Dangerzone runs because settings["updater_check"] is not None and settings["updater_last_check"] is None.

Before starting up the main window, show this window:

  • Title: Dangerzone Updater

  • Body:

    Do you want Dangerzone to automatically check for updates?

    If you accept, Dangerzone will check the latest releases page in github.com on startup. Otherwise it will make no network requests and won't inform you about new releases.

    If you prefer another way of getting notified about new releases, we suggest adding to your RSS reader our Mastodon feed. For more information about updates, check this webpage.

  • Buttons:

    • Check Automaticaly: Store settings["updater_check"] = True
    • Don't Check: Store settings["updater_check"] = False

Note:

  • Users will be able to change their choice from the hamburger menu, which will contain an entry called "Check for updates", that users can check and uncheck.

Subsequent runs

We perform the following only if settings["updater_check"] == True.

  1. Spawn a new thread so that we don't block the main window.

  2. Check if we have cached information about a release (version and changelog). If yes, return those immediately.

  3. Check if the last time we checked for new releases was less than 12 hours ago. In that case, skip this update check so that we don't leak telemetry stats to GitHub.

  4. Hit the GitHub releases API and get the latest release. Store the current time as the last check time, even if the call fails.

  5. Check if the latest release matches settings["updater_latest_version"]. If yes, return an empty update report.

  6. If a new update has been detected, return the version number and the changelog.

  7. Add a green bubble in the notification icon, and a menu entry called "New version available".

  8. Users who click on this entry will see a dialog with more info:

    • Title: "Dangerzone v0.5.0 has been released"

    • Body:

      A new Dangerzone version been released. Please visit our downloads page to install this update.

      (Show changelog rendered from Markdown in a collapsible text box)

    • Buttons:

      • OK: Return

Notes:

  • Any successful attempt to fetch info from GitHub will result in clearing the settings["updater_errors"] key.

Error handling

We trigger error handling when the updater thread encounters an error (either due to an HTTPS failure or a Python exception) and does not complete successfully.

  1. Bump the number of errors we've encountered in a row (settings["updater_errors"] += 1)
  2. Return an update report with the error we've encountered.
  3. Update the hamburger menu with a red notification bubble, and add a menu entry called "Update error".
  4. If a user clicks on this menu entry, show a dialog window:
    • Title: "Update check error"

    • Body:

      Something went wrong while checking for Dangerzone updates:

      You are strongly advised to visit our downloads page and check for new updates manually, or consult this page for common causes of errors . Alternatively, you can uncheck "Check for updates", if you are in an air-gapped environment and have another way of learning about updates.

      (Show the latest error message in a scrollable, copyable text box)

    • Buttons:

      • Close: Return

Key Benefits

  1. The above approach future-proofs Dangerzone against API changes or bugs in the update check process, by asking users to manually visit https://dangerzone.rocks.
  2. If we want to draw the attention of users to immediately install a release, we can do so in the release body, which we will show in a pop-up window.
  3. If we are aware of issues that prevent updates, we can add them in the wiki page that we show in the error popup. Wiki pages are not versioned, so we can add useful info even after a release.

Security Considerations

Because this approach does not download binaries / auto-updates, it does not add any more security issues than the existing, manual way of installing updates. These issues have to do with a compromised/malicous GitHub service, and are the following:

  1. GitHub pages can alter the contents of our main site (https://dangerzone.rocks)
  2. GitHub releases can serve an older, vulnerable version of Dangerzone, instead of a new update.
  3. GitHub releases can serve a malicious binary (requires a joint operation from a malicious CA as well, for extra legitimacy).
  4. GitHub releases can silently drop updates.
  5. GitHub releases can know which users download Dangerzone updates.
  6. Network attackers can know that a user has Dangerzone installed (because we ask the user to visit https://dangerzone.rocks)

A good update framework would probably defend against 1,2,3. This is not to say that our users are currently unprotected, since 1-4 can be detected by the general public and the developers (unless GitHub specifically targets an individual, but that's another story).

Usability Considerations

  1. We do not have an update story for users that only use the Dangerzone CLI. A good assumption is that they are on Linux, so they auto-update.

Alternatives

We researched a bit on this subject and found out that there are update frameworks that do this job for us. While working on this issue, we decided that integrating with one framework will certainly take a bit of work, especially given that we target both Windows and MacOS systems. In the meantime though, we didn't want to have releases out without including at least a notification channel, since staying behind on updates has a huge negative impact on the users' safety.

The update frameworks that we learned about are:

Sparkle Project

Sparkle project seems to be the de-facto update framework in MacOS. Integrators in practice need to care about two things: creating a proper Appcast.xml file on the server-side, and calling the Sparkle code from the client-side. These are covered in the project's documentation.

The client-side part is not very straight-forward, since Sparkle is written in Objective-C. Thankfully, there are others who have ventured into this before: https://fman.io/blog/codesigning-and-automatic-updates-for-pyqt-apps/

The server-side part is also not very straight-forward. For integrators that use GitHub releases (like us), this issue may be of help: https://github.com/sparkle-project/Sparkle/issues/648

The Windows platform is not covered by Sparkle itself, but there are other projects, such as WinSparkle, that follow a similar approach. I see that there's a Python library (pywinsparkle) for interacting with WinSparkle, so this may alleviate some pains.

Note that the Sparkle project is not a silver bullet. Development missteps can happen, and users can be left without updates. Here's an example issue that showcases this.

The Update Framework

The Update Framework is a graduated CNCF project hosted by Linux Foundation. It's based on the Thandy updater for Tor. It's not widely adopted, but some of its adopters are high-profile, and it has passed security audits.

It's more of a specification and less of a software project, although a well-maintained reference implementation in Python exists. Also, a Python project (tufup) that builds upon this implementation makes it even easier to generate the required keys and files.

Regardless of whether we use it, knowing about the threat vectors that it's protecting against is very important.

Other Projects