Account Handling in Maintainer Scripts

This script is an attempt to build a list of things to do with Unix system accounts in Debian Maintainer scripts. This is not an official document, it might be flawed, and its contents are under discussion at this time.

Introduction

Packages can include files owned by any of the UIDs known by "base-passwd". Any other UIDs need to be dynamically allocated by the package, typically in its postinstall script. The created account might be used as the UID of a daemon process, or as the owner of files included in the package, or generated by the package. This is commonly done due to security considerations, since it should be avoided to have unrelated processes running under the same UID (allowing them to attack each other, for example using ptrace type attacks), and it is desireable to have tight control on file access rights.

See this discussion.

Account Naming

Debian Policy ยง9.2.1 states that the account name should start with an underscore, so that accounts created by packages are not colliding with locally created users.

Account creation

The adduser program does the right thing if called with the --system option. It is thus usually only necessary to call

adduser --system $USERNAME

in your postinst to create the account with logins disabled, a primary group of nogroup and a home directory under /home. If you want other options, add them as you want to.

It should normally not be necessary to cross-check with getent whether an account already exists since adduser --system generally does the right thing. If not, please report a bug against adduser to keep your maintainer scripts simple.

Account deletion

There are different opinions whether accounts should be deleted at all.

Reasons for not deleting accounts

It needs to be made sure uid/gid are not reused on the system, as there could be still log files or other stuff around and/or on the backup media. The easiest way to block uid/gid permanently is to leave the account in the password file.

Packages removing system users will screw people if by chance there is another package using the same user. This happened in libchipcard-tools (1 and 2). This sucks and will create some unhappy users during sargeR[01]->etch upgrade (a fix was in sarge R2 or something).

Wishlist bug 390457 asks deluser to get a new configuration option that makes deluser --system not actually delete but only lock the account. That way, packages could deluser or delgroup as they see fit, and the local admin could prevent accounts from being deleted anyway.

If the account was "useable", make sure to set shell to /bin/false, to disable the password etc. [FIXME: What else?] when the package is purged.

Another advantage of not deleting accounts is that you don't have to hassle how to delete them. :-)

Asking the user if he wants to delete stuff doesn't save lots of work compared to the administrator calling "deluser" himself.

Reasons for deleting accounts

A purged package should vanish from the system without a trace. Having a deinstallation routine that actually works and is complete greatly eases debugging, makes results reproducible and is an advantage we have over the mainstream operating systems.

Having a purged package disappear completely is also useful for automated tests (for example piuparts).

Permit both?

A possible solution for this dilemma is to make the behavior of packages during their removal configurable. The local system administrator could decide which parts of a package he would want to be removed automatically or not.

The natural way to have this implemented would be a central package (dpkg-custom for example) to configure purge behaviour for accounts, log files, DBs etc.

One way would be a shell sourcefile providing functions like del_user(), purge_logs(), purge_cache() etc; a different way would provide an interface to the local configuration allowing the maintainer scripts to act accordingly.

The biggest problem is still that one cannot rely on the central package still being available at purge time. So probably the only clean way to implement this would be within some Essential package.

Summary

Because of this reasoning, it is highly discussed whether a package should even try removing its account on uninstall or purge. Debian policy should be made clearer with regard to what to do with an account on package removal.

Technical Issues of account removal

Deleting an account is significantly harder than creating it because of the following reasons:

Account-owned property to consider

How to delete accounts

Since a package cannot rely on adduser being present at package purge time, it is probably adviseable to remove the account at package uninstall time if none of the config files, logs and other files that are only removed on purge belong to the package account. Otherwise, it is acceptable to mask the deluser call in the purge code with

  if [ -x "$(command -v deluser)" ]; then
     deluser --quiet --system $USERNAME > /dev/null || true
  else
     echo >&2 "not removing $USERNAME system account because deluser command was not found"
  fi

which will result in a warning, purge completing and leaving the account around if adduser was removed after the package was uninstalled and before it was purged.

If you want to delete a group as well, the code cited above can easily be adapted.

Another possibility is to fall back to userdel/groupdel if deluser is not found. But this is probably overkill as it moves more complexity into the maintainer scrips which is not desireable.

Credits

This Wiki page was created by Marc Haber. Loic Minier, Andreas Barth, Jonas Meurer and James van Zandt gave important input.