Skip to content

RFC: Implement framework for handling conffile prompts

I am looking for comments on a proposal to address conffile prompts. This could solve #1429 (closed), #1246 (closed), #1246 (closed), #1381 (closed).

Problem

Currently, when FreedomBox sets up an application, it modifies configuration of various packages to suit a typical user. When a configuration file is modified, the package can't be automatically upgraded without user intervention. The unattended-upgrades package that is used in FreedomBox will refuse to automatically upgrade the affected package.

Current Status

We already employ multiple approaches to address this issue.

Debconf

We try to use debconf to configure package instead of editing the configuration. This way, changes to the configuration are handled by package itself. Upgrades to the packages are not obstructed. Any complex changes such as changes in configuration keywords and changes in configure file patches etc. required to upgrade the package are automatically handled by the package.

However, not all packages implement debconf. Even when they implement debconf, it is usually for basic configuration required to get the package running for typical case. Advanced configuration may not be available via debconf.

Configuration Path

Next, we try to add/remove configuration files to a package such that the original configuration files brought in by the package are never touched by FreedomBox setup scripts. This ensures that during upgrades, the newly added files are not seen as modifications to the configuration by dpkg. Automatic package upgrades go unhindered.

This method has the disadvantage that when major structural changes happen to the way package does configuration, then package upgrade will succeed but wrong configuration will cause the service startup to fail, or in the worst case, lead to incorrect configuration with security implications.

This method works well for services like Apache and systemd which have very stable configuration options. When configuration options are changed, there is usually a deprecation path with backward compatibility.

Unhandled Areas

Unfortunately, there are many packages used by FreedomBox that can't make use of the above strategies. It is a very common occurrence for a package to provide neither of the above approaches. Lacking any alternative, such packages in FreedomBox need to upgraded manually by users. This is a contravention of our goals to make FreedomBox accessible to non-technical users.

Proposed Solution

We should try to have Debconf of a package handle our configuration changes. We should try to contribute patches to Debian packages to make this happen. When this approach does not work we should try to fallback to providing our configuration changes in a separate file. Here too, we can provide patches to upstream or Debian packaging to enable reading configuration from multiple locations and conf.d directories. When these approaches do not work (for example due to upgrade from an older version of the package that does not support these solutions), we should take the following approach:

  1. Get a trigger into FreedomBox Service when a package upgrade is available but cannot be upgraded due to configuration file prompts.
  2. FreedomBox service will then search for all the applications that are managing the package and will run a hook on them say upgrade_packages(). This hook is only called if application is in installed state, it has been fully updated to the latest version and if there are no other dpkg operations running. If the the hook reports failure, the information about needing package upgrades is stored and this is retried regularly (if the trigger repeats on a regular basis this can be skipped).
  3. In the hook upgrade_packages(), applications should read the current configuration, upgrade the package by force and re-apply the configuration.
    • Current configuration should include the fact whether an app is currently enabled, configuration set by the user in the interface, configuration set by user while interacting the app during its normal operation.
    • Configuration set by FreedomBox as defaults for the service may be ignored because setting up the new package will set this defaults again.
    • Ideally, configuration changed by user by editing configuration file manually should also be preserved. This may be done by storing backups of the modified files before forcing and upgrade, restoring them after the upgrade and then running a migration on them. However, this part may be ignored if the package configuration is unlikely to be modified by users manually.

Trigger when Package Needs Manual Upgrade

When package needs to be upgraded manually due to a configuration file prompt and unattended upgrades refuses to upgrade it, FreedomBox needs to be triggered. This can happen as follows:

  1. Unattended-upgrades package may already have an option to run an external command when this happens. If such a facility does not exist, we can submit a patch (this can be useful in more generic cases where administrators want to receive an email when a security upgrade is stuck due to configuration file prompts).
  2. If such a change is not accepted into unattended-upgrades, we may resort to watching the log file of unattended-upgrades using custom code or a utility that can watch log files and match regular expressions to trigger commands.
  3. We could consider implementing a hook to apt update that would trigger a FreedomBox action that inturn checks for all the upgrade-able packages and sees if any of their configuration has been modified. If so, the trigger is fired.
  4. We could run a cron job (or systemd timer) that would every day check for upgrade-able packages and if any of them have configuration files modified, the trigger is fired. This cron job could also be a long running thread within the FreedomBox service that wakes up once a day to do the checks and triggers.

Security for the Trigger

If a trigger originates from outside of FreedomBox Service, then the service needs to verify that a sufficiently privileged process has fired the trigger and not other users/daemons on the machine and also not from network. This framework is also useful to get many other kinds of triggers such as when Let's Encrypt certificates are renewed.

When FreedomBox daemon starts up, it shall write a nonce to a file that can only be read by privileged users in a transient file such /run/plinth/auth-nonce. The trigger receiving point is a unique regular HTTP URL which shall check for the correct nonce in the GET/POST parameters.

This approach also makes testing triggers easier since a simple HTTP request can simulate firing a trigger.

Force Upgrading a Package

When manual intervention is needed for package upgrade, FreedomBox service will read the configuration and upgrade the package by force. This can be done by:

  • If possible, pass a configuration option to the upgrade command to accept package maintainers version of the configuration file. We may even keep the modified, older configuration file if we are going to properly upgrade it.
  • Reset the configuration to original:
    • Always keep a backup of the original configuration file and restore it before package upgrade. This may not work for configuration files that have already been modified but will work for application that newly looking to change configuration files.
    • Carefully, undo the configuration changes to arrive at the original configuration file. This approach should be avoided as manual changes and unexpected changes by configuration tools means that original file won't be arrived at.
  • Alter the expected hash of the configuration file to match the hash of the current configuration file. Packages that implement Debconf do something similar after they change the configuration files.