We need declarative system user and group handling. This should be done from dpkg itself, because eventually dpkg will also be aware of the files metadata which includes users/group information, and this would allow shipping packages using dynamic user/group, while generating them before unpack with no maintscript required.
Doing this via any other mechanism, requires either maintscripts (which is undesirable and goes against what is being tried to achieve here), or triggers (which might be too late in many situations, or require awaiting ones which are generally undesirable).
Analysis of pre-existing implementations
Using adduser is not satisfactory because:
- It's written in perl, which dpkg.deb does not use anymore.
- Several things are configurable which should not be, such as the valid name regexes (and that forces packages to use --force-badname in case the admin has configured something else).
- Uses the shadow package underneath, which is not portable to other systems dpkg runs on.
I've checked the systemd sysusers.conf stuff, and it also seems unsatisfactory, because it lacks things from the list below. Also being tied to what systemd might or might not agree with, does not seem wise. The systemd sysusers support has two parts:
- file format: which we could use as our transport, but that has its own implicit semantics, where if we diverge we might cause run-time issues,
- CLI: which while it has been reimplemented now by other parties, still ties us to any decision or limitation it might include.
It's probably better, if there's ever a need, to include a mapping tool that can convert at package build-time from systemd sysusers, or any other OS-specific declarative format, into the native dpkg format, where any semantic discrepancies can be adapted gracefully.
What we'd need from this new interface and declarative file would be:
- Hardcoded (at least per-vendor) to:
- Accept only "[_a-z][_a-z0-9-]*" names.
- Always create system accounts.
- Default to:
- Home set to something like /nonexistent (not /).
- User disabled.
- User w/o password.
- User shell set to /bin/false.
- GECOS set to empty.
- Create matching user and group.
- Lock the user on last ref.
- Make it possible to:
- Create users or groups.
- Rename users or groups.
- Lock and unlock users.
- Add users to a group.
- Update the user/group metadata.
- Refresh/invalidate data in external daemons (NIS, NSCD), ideally via hooks/plugins, to not hardcode stuff (like adduser does).
- Create the home directory?
- Specify modes for the created home directory?
- Not create a group matching a user.
- Use the same id for both uid and gid.
- Set non-default home.
- Set non-default shell.
- Set a GECOS.
- SE Linux user stuff perhaps?
- Let the admin, set the default home?
- Let the admin, change whether to remove instead of lock on last ref.
- Let the admin, change the range of allowed uid/gids.
This would be either implemented by a new dpkg command or internally, because in theory everything above would be expressible with the declarative file format, so there should be no need to call anything explicitly?
If this is implemented by a new command then the actions could perhaps be:
- --ref-user user
- --ref-group group
- --unref-user user
- --unref-group group
For sysadmins, shadow and or adduser would still be the interface to use, as those are really fine for those jobs.
For a first iteration I guess we could either use the shadow commands as backend or the glibc/musl FILE-stream interfaces. Ideally this should be implemented natively in dpkg, to make it "portable". But there's no standard API to handle the gshadow file, if it even exists.
- GNU/* and musl/* uses /etc/passwd, /etc/shadow, /etc/group, /etc/gshadow.
- BSDs uses /etc/passwd and /etc/master.passwd, /etc/group.
- FreeBSD pw(8), adduser(8), pwd_mkdb(8).
- DragonFlyBSD pw(8), adduser(8), pwd_mkdb(8).
- NetBSD user(8), useradd(8), usermod(8), userdel(8), group(8), groupadd(8), groupmod(8), groupdel(8), pwd_mkdb(8).
- OpenBSD adduser(8), rmuser(8), pwd_mkdb(8).
- AIX uses /etc/passwd and /etc/security/passwd, /etc/group, /etc/security/group.
- mkuser(8), rmuser(8), mkgroup(8), rmgroup(8), pwdadm(8), chuser(8), lsuser(8), chgroup(8), chgrpmem(8), lsgroup(8), useradd(8), userdel(8), usermod(8).
- Mac OS X uses the Directory Service, or legacy /etc/master.passwd.
- Legacy pwd_mkdb(8).
- New dscl(8), Directory Service command-line utility
In addition to the aforementioned non-standardized interfaces available, there are at least the following problems to keep in mind:
- At least on glibc, an additional complication is that there does not seem to be any interface to use the passwd/shadow APIs for a different root directory while preserving NSS support. So either the FILE-based stream functions can be used (with no NSS support), or chroot(2) needs to be used, which is problematic for dpkg's chrootless mode. Given that system users should in most if not all cases be present locally regardless of any NSS setup, it might be an option to create them via the FILE-based interfaces, and then once no chrootless mode is needed batch the creation via chroot() and the NSS-supporting interfaces.
- Given that we will not be supporting system users with passwords, that means we can completely ignore a huge potential for security concerns involved in either having to implement the crypto for the passwords encoding, or having to depend on an external library. This makes using libc APIs a worthwhile prospect.
- Given that we do not need to support privilege escalation, as we are supposed to be running as the system administrator (or as non-root, with no elevated privileges), we also remove a whole swath of security concerns.