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.
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.
Naming of system accounts is a highly discussed matter. Accounts created by packages should be unlikely to collide with locally created users. One of the most popular methods to ensure this is starting the account name with an underscore or a Capital. Some packages have decided to make a collision extra unlikely, choosing an account name like Debian-packagename. This is, however, highly discussed.
A collision free way to name system accounts should really be mentioned in Debian policy to stop this uncontrolled growth of different methods.
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.
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->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).
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.
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:
- at package purge time, a package can no longer rely on its dependencies being installed
- the local admin might have decided to chown unrelated files to the package account
- if we remove the account on purge, it is possible that the UID will be re-used at some later time
Account-owned property to consider
- home directory
- log files
- score files (for games)
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.
This Wiki page was created by Marc Haber. Loic Minier, Andreas Barth, Jonas Meurer and James van Zandt gave important input.
Please get in touch with Marc Haber before making significant changes here.