RPM-SYSUSERS(7)

NAME

rpm-sysusers - RPM native allocation of users and groups through sysusers.d(5)

SYNOPSIS

  Declaration

%{_sysusersdir}/*.conf
%{add_sysuser [-b] ENTRY}

  File format

ENTRY
# COMMENT

  Entry format

u USERNAME UID GECOS HOMEDIR SHELL
u USERNAME UID:GID GECOS HOMEDIR SHELL
u USERNAME FILE GECOS HOMEDIR SHELL
g GROUPNAME GID
g GROUPNAME FILE
m USERNAME GROUPNAME

  Notes

•   Suffix u with an exclamation mark (!) to create a fully locked account (recommended)
•   Use a dash (-) as UID/GID for automatic number allocation (recommended)
•   FILE is an absolute path to a file whose UID/GID should be used

DESCRIPTION

RPM has native support for declarative user and group creation by integrating with the sysusers.d(5) format (introduced by systemd). Packagers only need to drop one or more files to /usr/lib/sysusers.d/ and RPM will handle the rest, such as dependency generation and the invocation of systemd-sysusers(8) during the build and installation, respectively.

Vendors on non-systemd platforms can enable the feature by shipping any missing tools and configuring RPM accordingly (see PORTABILITY for details).

Note that for certain types of systemd services (such as transient or socket activated), it may be possible to avoid package level user/group allocation altogether by using Dynamic Users (see DynamicUser= in systemd.exec(5) and the URL in SEE ALSO for details).

Added: 4.19.0

DEPENDENCIES

Packaged sysusers.d(5) files generate virtual provides for the users and/or groups they define. Similarly, any non-root ownership defined with %attr() or %defattr() in an rpm-spec(5) file generates virtual requires for the named users and/or groups. Both dependencies complement each other to ensure correct installation when a package relies on a user/group from another package.

Explicit group membership (the m sysusers.d directive) will also generate virtual requires for both the user and the group name in the originating package.

For more details on the mechanism itself, see rpm-dependency-generators(7).

LIMITATIONS

Currently, RPM only supports the following sysusers.d directives:
•   u and g (Added: 4.19.0)
•   m (Added: 4.20.0)
•   u! (Added: 4.20.1)

Other directives, if used, will be ignored. If such are needed, the package has to call systemd-sysusers(8) with the correct arguments manually.

PORTABILITY

RPM provides a number of macros to enable vendors and packagers to implement the declarative user and group integration on platforms that do not ship with the default systemd tooling. Note, however, that some of these tunables may need tweaking even on platforms with systemd.

  Vendors

%__systemd_sysusers PATH
    Path to the systemd-sysusers(8) executable or a compatible program.

    On systemd native platforms, set PATH to the systemd-sysusers(8) program, typically at /usr/bin/systemd-sysusers. Note that the program may also be shipped by a standalone package with no systemd dependencies (such as systemd-standalone-sysusers on Fedora Linux) which makes it useful in containers or similar environments.

    RPM ships with a replacement shell script that calls the standard UNIX tools such as useradd(8) or groupadd(8) for account creation. The script is installed at %{_rpmconfigdir}/sysusers.sh and can be used on platforms where the original program is not available.

    On platforms without the standard UNIX account tools, a custom script or program can be written, which must:
    •   Process sysusers.d(5) entries on standard input
    •   Create or modify users and groups accordingly
    •   Never delete any users or groups
    •   Handle the --root and --replace options like the original program
    •   Avoid changing the host if --root is used

%_sysusersdir PATH
    Path to the drop-in directory where packages are supposed to install the sysusers.d(5) files. Defaults to /usr/lib/sysusers.d.

  Packagers

%add_sysusers [-b] ENTRY
    Interpret ENTRY as if written in a sysusers.d(5) file and expand to a (bare) dependency string as described in Encoding. ENTRY denotes the individual fields passed as macro arguments.

    When used with -b, expand to a bare dependency string. This form can be used to implement rpm-dependency-generators(7) for sysusers.d(5) files. RPM ships with such a generator (sysusers.attr) installed by default.

    When used without -b, expand to a dependency string and prefix it with the appropriate dependency tag for use in rpm-spec(5) files. This form can be used in the (sub)package context of a spec file, to declare users and/or groups directly, without having to package sysusers.d(5) files. It is mainly intended for development purposes, and is discouraged otherwise since it conflicts with the original sysusers.d(5) scheme (see also TECHNICAL DETAILS).

COMPATIBILITY

  Vendors

By default, the generated virtual requires are hard dependencies. This can be problematic when upgrading an existing distribution to RPM 4.19, however, so it is possible to weaken these requires into recommends, by setting %_use_weak_usergroup_deps to 1 in the rpmbuild-config(5).

TECHNICAL DETAILS

  Encoding

RPM will generate any combination of the following virtual provides based on, and attached to, the packaged sysusers.d(5) file(s):

user(username) = base64
group(groupname) [= base64]
groupmember(username/groupname) = base64

base64 is a base64 encoded representation of the given sysusers.d entry, with the input \0-padded as needed, in order to avoid the base64 native = padding in the output, which is illegal in an EVR string.

For example, the following sysusers.d entry:

    u dnsmasq - "Dnsmasq DHCP and DNS server" /var/lib/dnsmasq

would emit the following provides attached to the originating file:

    user(dnsmasq) = dSBkbnNtYXNxIC0gIkRuc21hc3EgREhDUCBhbmQgRE5TIHNlcnZlciIgL3Zhci9saWIvZG5zbWFzcQAA
    group(dnsmasq)

As systemd-sysusers(8) implicitly creates a matching group for any created users, the group provides does not have an EVR here. Only explicitly created groups will have the encoded sysusers.d entry as an EVR.

Entries adding users to groups, such as:

    m klangd klong

will create a provides:

    groupmember(klangd/klong) = bSBrbGFuZ2Qga2xvbmcA

They will also create two requires (or recommends if %_use_weak_usergroup_deps is set to 1):

    group(klong)
    user(klangd)

to make sure the packages creating the user and group are installed first.

  Decoding

When RPM installs a package with virtual provides as described in ENCODING, it decodes their EVR strings back from base64 to the original sysusers.d entries, and groups them by their source file. Then, it runs the systemd-sysusers(8) program once for each source file, with --replace pointing to the path of the source file when installed, and passes the decoded entries pertaining to that file on standard input. The program then creates new, or modifies existing, users and groups on the system, according to those entries.

Since the above process happens before the payload is unpacked to disk, packages can ship files owned by their own users and groups.

Virtual provides generated with the %add_sysuser macro in rpm-spec(5) files are package-level, with no source files to group them by. Thus, RPM does not use --replace when calling systemd-sysusers(8) for such entries. That may cause unintended behavior since the program then gives the entries on standard input higher priority than any sysusers.d(5) overrides that the administrator may have configured on the system.

Note that the packaged sysusers.d(5) files, if any, are installed as normal, but do not participate in the user/group creation itself. They simply serve as static definition files for reconstructing /etc/passwd and /etc/group later, as per sysusers.d(5).

TROUBLESHOOTING

rpm -q {--provides|--requires} PACKAGE_NAME | grep '^\(user(\|group(\|groupmember(\)'
    Display the virtual user/group/groupmember provides/requires of PACKAGE_NAME.

rpm -q --queryformat '[%{SYSUSERS}\n]' PACKAGE_NAME
    Display the original sysusers.d entries decoded from the virtual provides of PACKAGE_NAME by using rpm-queryformat(7).

EXAMPLES

  Example 1. Packaging a sysusers.d(5) file

Consider a dnsmasq.spec file with the following contents (irrelevant parts ellipsized or omitted):

    %install
    mkdir -p ${RPM_BUILD_ROOT}/%{_var}/lib/dnsmasq
    mkdir -p ${RPM_BUILD_ROOT}/%{_sysusersdir}
    cat << EOF > ${RPM_BUILD_ROOT}/%{_sysusersdir}/dnsmasq.conf
    u dnsmasq - "Dnsmasq DHCP and DNS server" /var/lib/dnsmasq
    EOF
    [...]

    %files
    %dir %attr(0755,root,dnsmasq) %{_var}/lib/dnsmasq
    %{_sysusersdir}/dnsmasq.conf
    [...]

When the package is built, it will contain the following requires and provides:

    $ rpm -qp --requires /path/to/dnsmasq.rpm
    group(dnsmasq)
    [...]
    $ rpm -qp --provides /path/to/dnsmasq.rpm
    group(dnsmasq)
    user(dnsmasq) = dSBkbnNtYXNxIC0gIkRuc21hc3EgREhDUCBhbmQgRE5TIHNlcnZlciIgL3Zhci9saWIvZG5zbWFzcQAA
    [...]

Finally, when the package is installed, RPM will create the dnsmasq user and group automatically, and the /var/lib/dnsmasq directory installed by the package itself will be owned by that group as per the spec file:

    $ getent passwd dnsmasq
    dnsmasq:x:999:999:Dnsmasq DHCP and DNS server:/var/lib/dnsmasq:/usr/sbin/nologin
    $ getent group dnsmasq
    dnsmasq:x:999:
    $ stat -c '%G:%g' /var/lib/dnsmasq
    dnsmasq:999

The original sysusers.d(5) file will be installed as normal:

    $ rpm -ql dnsmasq | grep sysusers
    /usr/lib/sysusers.d/dnsmasq.conf
    $ cat /usr/lib/sysusers.d/dnsmasq.conf
    u dnsmasq - "Dnsmasq DHCP and DNS server" /var/lib/dnsmasq

  Example 2. Declaring a sysusers.d entry manually

Consider a dnsmasq.spec file with the following contents starting just below the preamble block (irrelevant parts ellipsized or omitted):

    %{add_sysuser u dnsmasq - "Dnsmasq DHCP and DNS server" /var/lib/dnsmasq}
    [...]

    %files
    %dir %attr(0755,root,dnsmasq) %{_var}/lib/dnsmasq
    [...]

Building and installing this package will have the same effect as described in Example 1. The only difference is that, in this example, no sysusers.d(5) file is packaged or installed.

SEE ALSO

rpm(8), rpmbuild-config(5), rpm-spec(5), rpm-scriptlets(7), rpm-dependency-generators(7), sysusers.d(5), systemd-sysusers(8), systemd.exec(5), useradd(8), groupadd(8)

https://0pointer.net/blog/dynamic-users-with-systemd.html