Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rpmostree-core: Reload user/groups after each %post script #5334

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

travier
Copy link
Member

@travier travier commented Mar 12, 2025

As part of the migration to direct sysusers support in RPMs, packages are no longer creating their users/groups in %pre scripts as they rely on RPM to do the work.

In rpm-ostree unified core composes, the users/groups are not created this way as we don't use/call out to those new RPM scriptlets. (is that correct? to be confirmed).

Thus the "new" users (i.e. those not part of the default passwd & group files set in the manifests) are created when the systemd %post scriptlet runs the sysusers command.

As a workaround, we now reload the list of users/groups after each %post to make sure it is up-to-date.

See: https://fedoraproject.org/wiki/Changes/RPMSuportForSystemdSysusers
See: rpm-software-management/rpm@009d139

See: fedora-silverblue/issue-tracker#636
See: https://gitlab.com/fedora/ostree/sig/-/issues/70
See: #5333

@keszybz
Copy link
Contributor

keszybz commented Mar 12, 2025

Doesn't look too terrible ;)

@travier
Copy link
Member Author

travier commented Mar 12, 2025

PR description is my understanding, may not be fully accurate, suggestions welcomed (and then I'll update the commit message).

@AdamWill
Copy link

The code is written as it is specifically in order not to do this sort of thing. There's a long comment to that effect:

      /* We're technically deviating from RPM here by running all the %pre's
       * beforehand, rather than each package's %pre & %post in order. Though I
       * highly doubt this should cause any issues. The advantage of doing it
       * this way is that we only need to read the passwd/group files once
       * before applying the overrides, rather than after each %pre.
       */

so it seems like we might want to try and honor that goal somehow. Maybe special-case systemd and do something like this?

  1. pre loop
  2. read passwd
  3. systemd post
  4. read passwd again
  5. post loop

if we don't want to do that, we should probably at least update the comment...

@AdamWill
Copy link

also, doing it this way doesn't feel 100% safe, because it seems like it would rely on systemd coming very early in the %post loop - because it must be done before any other package containing a file owned by a user or group that would be created by systemd's %post - but we don't appear to do anything to guarantee that that's the case?

@travier
Copy link
Member Author

travier commented Mar 12, 2025

That should work but would make this very specific to the systemd package name. I agree that this is a bit of a brute force logic but maybe this is safer at the cost of re-reading the files a bit often?

@AdamWill
Copy link

well, then, how about:

  1. pre loop
  2. directly cause a run of systemd-sysusers in whatever context it needs to happen
  3. read passwd
  4. post loop

that seems most similar to what rpm is now doing, i guess?

@travier
Copy link
Member Author

travier commented Mar 12, 2025

also, doing it this way doesn't feel 100% safe, because it seems like it would rely on systemd coming very early in the %post loop - because it must be done before any other package containing a file owned by a user or group that would be created by systemd's %post - but we don't appear to do anything to guarantee that that's the case?

Agree that this is not a "fix", but more of a workaround. Ideally, we would add the RPM sysusers scriptlet support to rpm-ostree as a specific phase here before running the %post.

@travier
Copy link
Member Author

travier commented Mar 12, 2025

For this to be "correct" (i.e. matching how RPM does it) we would have to re-implement rpm-software-management/rpm@009d139 as far as I understand.

@travier
Copy link
Member Author

travier commented Mar 12, 2025

well, then, how about:

  1. pre loop
  2. directly cause a run of systemd-sysusers in whatever context it needs to happen
  3. read passwd
  4. post loop

that seems most similar to what rpm is now doing, i guess?

That could be another option.

@AdamWill
Copy link

i'm trying to figure out how to subvert the 'run a package script' bits to just run a script we make up on the fly...

@travier
Copy link
Member Author

travier commented Mar 12, 2025

rpm-ostree runs post-process scripts via https://github.com/coreos/rpm-ostree/blob/main/rust/src/composepost.rs#L515, so we should be able to directly add a call to sysusers using a similar logic.

@AdamWill
Copy link

AdamWill commented Mar 12, 2025

well, Ii'm trying to hook into the various levels of script running function in src/libpriv/rpmostree-scripts.cxx , which does more or less the same. But it's kinda tedious to figure out if there's a neat way to slip in, it's all tied into RPM business logic. but I don't want to repeat all the bwrap stuff...

for now I guess I'll just test this PR as-is, if it at least fixes ostree builds for now that's better than what we have, we can slap it in as a downstream patch till Colin or someone can decide what the best long-term fix is...

@AdamWill
Copy link

it does look like this works around the issue for now, so I'll go ahead and backport it downstream officially. I'll try and figure out if there's a significant impact on run time, too, once the test completes.

As part of the migration to direct sysusers support in RPMs, packages
are no longer creating their users/groups in %pre scripts as they rely
on RPM to do the work.

In rpm-ostree unified core composes, the users/groups are not created
this way as we don't use/call out to those new RPM scriptlets.

Thus the "new" users (i.e. those not part of the default `passwd` &
`group` files set in the manifests) are created when the systemd %post
scriptlet runs the `sysusers` command.

As a workaround, we now reload the list of users/groups after each %post
to make sure it is up-to-date.

See: https://fedoraproject.org/wiki/Changes/RPMSuportForSystemdSysusers
See: rpm-software-management/rpm@009d139

See: fedora-silverblue/issue-tracker#636
See: https://gitlab.com/fedora/ostree/sig/-/issues/70
See: coreos#5333
@travier travier force-pushed the fix-user-creation-in-post branch from b3217c7 to ae2c0d2 Compare March 13, 2025 08:40
@travier
Copy link
Member Author

travier commented Mar 13, 2025

Rebased and updated the commit description

@travier
Copy link
Member Author

travier commented Mar 13, 2025

From a discussion with Jonathan, this will not work for package layering as the systemd %post won't be called there, so this is not a complete workaround.

@jlebon
Copy link
Member

jlebon commented Mar 13, 2025

Whipped this up while chatting with @travier:

diff --git a/src/libpriv/rpmostree-core.cxx b/src/libpriv/rpmostree-core.cxx
index 70905996..208696fe 100644
--- a/src/libpriv/rpmostree-core.cxx
+++ b/src/libpriv/rpmostree-core.cxx
@@ -4422,6 +4422,26 @@ rpmostree_context_assemble (RpmOstreeContext *self, GCancellable *cancellable, G
 
   progress->end ("");
 
+  /* if the %__systemd_sysusers macro is defined, that _very likely_
+   * means that we're using an rpm with built-in sysusers support:
+   * https://fedoraproject.org/wiki/Changes/RPMSuportForSystemdSysusers Ideally,
+   * librpm would expose that part as a public API but it's currently just
+   * done during the transaction. So here we just do it ourselves (though a bit
+   * differently; librpm checks for the user/group provides before proceeding to call
+   * sysusers _just_ for that package. Here we do it for all of them... See also:
+   * https://github.com/coreos/rpm-ostree/issues/5333 */
+  g_autofree char *sysusers_cmd = rpmExpand ("%__systemd_sysusers", NULL);
+  if (sysusers_cmd && *sysusers_cmd)
+    {
+      CXX_TRY_VAR (bwrap,
+                   rpmostreecxx::bubblewrap_new_with_mutability (
+                       tmprootfs_dfd, rpmostreecxx::BubblewrapMutability::MutateFreely),
+                   error);
+      bwrap->append_child_arg (sysusers_cmd);
+      if (!CXX (bwrap->run (*cancellable), error))
+        return glnx_prefix_error (error, "Running systemd-sysusers (%s)", sysusers_cmd);
+    }
+
   /* Some packages expect to be able to make temporary files here
    * for obvious reasons, but we otherwise make `/var` read-only.
    */

Not tested...

@cgwalters
Copy link
Member

The approach of running systemd-sysusers "wholesale" does seem to match with the current design.

rpmostreecxx::BubblewrapMutability::Immutable

Hmm don't we need mutability? Though I forget if that means just /usr is immutable.

@jlebon
Copy link
Member

jlebon commented Mar 13, 2025

The approach of running systemd-sysusers "wholesale" does seem to match with the current design.

rpmostreecxx::BubblewrapMutability::Immutable

Hmm don't we need mutability? Though I forget if that means just /usr is immutable.

Ahh I think you're right. I thought the reverse and that it only implied /usr. There isn't a mode currently for "/usr only", but meh.. fine with MutateFreely. Updated patch! I think @travier is going to take it for a spin and update this PR if it works.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants