coreos/rpm-ostree

Determine support path for RPMs that install into `/opt`, `/usr/local` etc

cgwalters opened this issue Β· 90 comments

RPMs like Puppet and Google Chrome go in /opt.

For the package layering case, in this PR we made it an obvious error.

The core problem here is: OSTree defines /opt (really /var/opt) as system administrator territory - it's never rolled forward/backwards etc. Content in there isn't protected by the ro bind mount covering /usr right now.

One approach would be to - for these RPMs, automatically rewrite the content into /usr. Chrome I don't believe actually stores any persistent state in /var, so simply rewriting /opt/google/chrome -> /usr/lib/opt/google/chrome with a compatibility symlink would likely work.

Puppet probably stores state in /var and hence would be harder.

I'd support the /usr/lib/opt approach!

@cgwalters any hints about what I can do in the meantime to get a sensible /opt to work? I was thinking of manually putting things into /usr/lib/opt during my install script, and then setting up a bind mount to /opt in my /etc/fstab, but I don't know if that'll be incompatible with the existing mount mechanism under OSTree. Is there a better way to weasel my way into the standard OSTree mount mechanism?

(the issue here is proprietary software with hardcoded assumptions about being in /opt, by the way; otherwise I'd just patch it)

We can change rpm-ostree to do this postprocessing automatically.

@cgwalters yeah, sorry, I realize that; was just asking about what to do between now and having native support for /opt in rpm-ostree

The only thing I can think of is to postprocess the RPM outside of rpm-ostree, basically RPMs are just cpio blobs with metadata and scripts. One could rpm2cpio foo.rpm to unpack it, then mv opt/foo usr/lib/foo, then turn it into a "source" tarball and make a binary-only RPM from that.

It might sound complex at first but it'd probably be a 4 line script + 15 line spec file.

@cgwalters but then I also need something to add an /opt bind mount back at runtime, right? The RPMs are still hardcoded to think they're in /opt. Would I have my RPM-wrangler add an entry to fstab as well?

Add a systemd tmpfiles.d file which creates the symlink /opt/foo -> /usr/lib/foo. This is pretty similar to what rpm-ostree automatically generates for directories in /var. See also https://github.com/projectatomic/rpm-ostree/blob/master/src/app/tmpfiles-ostree-integration.conf#L18

Oh, excellent, thanks!

Ah, it looks like https://github.com/projectatomic/rpm-ostree/blob/master/src/libpriv/rpmostree-postprocess.c#L102 is already putting a symlink for /opt -> /var/opt, so I think I need to use an L+ tmpfiles entry.

Turns out even with L+, systemd-tmpfiles-setup.service doesn't have the power to unlink /opt. Not does root. I'm confused what kind of thing /opt is to prevent me from changing it like this, given that it's on a RW filesystem and isn't a special mount point or anything weird.

I don't think you want to replace the full /opt symlink, just create a sub-symlink inside it, no? I.e your tmpfiles.d snippet should be

L /opt/appname - - - - ../usr/share/appname

or so.

In the current model, the /opt -> /var/opt symlink is part of the ostree commit (see the init_rootfs() function), it isn't created on boot. The reason you're getting EPERM is because of the immutable bit.

Ah yes, I just figured out the immutable bit on /. The reason I was trying to redirect all of /opt, rather than just subdirectories within it is that I have a few different packages that want to put things into /opt, and I was just going to have a general purpose "/opt fixer" in my postprocessing script rather than teaching my individual RPMs how to add to tmpfiles.

So my postprocess script currently just copies /opt into /usr/lib/opt and adds a tmpfiles.d entry that (tries to) symlink /opt to /usr/lib/opt.

I can do it for individual packages, but that starts putting a lot more burden on different subprojects, so I was hoping to avoid that πŸ˜”

Interesting effect of having /opt -> /var/opt and then using tmpfiles.d to populate /opt: I need to ensure that my tmpfiles.d conf file sorts lexicographically later than rpm-ostree-autovar.conf (or tmpfiles-ostree-integration.conf, which also defines the same entry) because otherwise tmpfiles.d tries to create my /opt/myapp while /opt is still a broken link, before /var/opt has been created.

Yeah, this would all be better if rpm-ostree handled it internally. I'll get around to this at some point if no one else does.

How would you envision the "ostree-native" version of this working? My concern with the obvious thing is that a lot of programs use it to store all their files, including ones they expect to mutate. Simply making a symlink from /opt/foo to /usr/lib/opt/foo will work until the program tries to mutate one of those, and then we'll run into other issues. What I'm doing today with my horrible hack is copying stuff from /usr/lib/opt into /opt, which ostree sets up to point to /var/opt so it's mutable anyway. This leads to some duplication and more runtime mutability than I'd like, but I don't have a cleaner solution in mind other than handling /opt entries on a case-by-case basis.

@cgwalters I would suggest an migration of /opt to /usr/lib/opt 233#issue-140670748. From treefile we can set remove-from-packages to remove files.

I'm not sure if the way to say we drop all files from /opt/ except for the following files and directories which are definied from treefile option include-from-opt (Array)*.The Entries from include-from-opt are moved to /usr/lib/opt. The previous behavior of rpm-ostree does not change (version compatibility).

*New treefile option introduction: include-from-opt (Array).

What do you think?

I hope this will help others to workaround until there is an solution, thanks to @cgwalters and @copumpkin

Rebuild puppet-agent rpm with new path

Regular Expression to change /opt/ -to-> /var/opt:

:%s/"\/opt/"\/var\/opt/g

Copy Conent from /opt/ to the new location: /var/opt/

cp -rfa /opt/puppetlabs /var/opt/puppetlabs
rpmrebuild -e puppet-agent

   opens vi/vim, run the regular expression... and write close the change ( !wq ).

ls -lha /root/rpmbuild/RPMS/x86_64/puppet-agent-1.4.1-1.el7.x86_64.rpm
yum install createrepo
createrepo --database /root/customrpms

File: sig-atomic-buildscripts/puppetlabs-pc1.repo

[puppetlabs-pc1]
name=Puppet Labs PC1 Repository el 7 - $basearch
#baseurl=http://yum.puppetlabs.com/el/7/PC1/$basearch
baseurl=http://192.168.99.102:6060
gpgcheck=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-puppetlabs-PC1

Start Custom Yum repository:

  • http-server: ( npm install -g http-server )
cd /root/customrpms/
http-server -p 6060

via rpm-ostree upgrade I've got the changes in ostree and /opt/puppetlabs -> /var/opt/puppetlabs e.g. I can run now puppet agent on centos-atomic host. The above documented solution is upgradeable. I have no blog or website where I can put this and link from here.

HTH
Tobias

by the way this does not work with rpm-ostree -> I get the following log error: https://nopaste.me/view/dd563d20

A test installation of my rpmrebuild rpm on my ostree-build system was just fine, files & binaries are located in /var/opt/puppetlabs/. With sudo rpm-ostree-toolbox treecompose -c ~/sig-atomic-buildscripts/config.ini --ostreerepo /srv/repo/ -p hochguertel.local and testing the custom ostree via virtualmachine shows me that there something went wrong. /var/opt/puppetlabs and directory structure and even some files are there - but also files are missing, binaries are missing.

Can someone help?

After some research I was able to understand that I have to modify my custom rpm so that is will be located under /usr/ instead /var/. /var stays empty in ostree. After changing the location to /usr/opt things went fine and I was able to get the complete content of the custom rpm on my custom ostree.

I changed the regular expression from above to: :%s/"\/opt/"\/var\/opt/g.

As next I have to add or change the tmpfiles.d file (system.d tmpfiles.d) of puppet-agent rpm (my custom rpm) before rpmrebuild it.

cat <<EOF > /usr/lib/tmpfiles.d/puppet-agent.conf
d /var/run/puppetlabs 0755 root root -
d /tmp/puppetlabs 0755 root root -
d /tmp/puppetlabs/puppet 755 root root -
d /tmp/puppetlabs/puppet/cache 755 root root -
L+ /usr/opt/puppetlabs/puppet/cache - - - - /tmp/puppetlabs/puppet/cache
L+ /opt/puppetlabs - - - - /usr/opt/puppetlabs
EOF

On my testing centos-atomic-host it is now possible to start the puppet-agent systemd service, the original location of /opt/puppetlabs is a symLink to /usr/opt/puppetlabs.

There are some addtional adjustments todo at the custom rpm - which are not yet done by me, but the informations here are already useful so I hope.

HTH
Tobias

Thanks for providing the workarounds. I would like to support this better, however, it would touch a lot of code that is in flux for other work (e.g. the just-landed package layering).

I briefly looked at the Google Chrome case. They do a fair bit in %post, which actually calls into at to work around the inability to import their GPG key at install time. The other fix for this is (for Fedora) to put it in /etc/pki/rpm-gpg where libdnf will auto-import it. (This whole story of 3rd party repos and how they get configured and GPG keys is a mess)

hit this today with the fwupd package (more specifically fwupdate-efi) :

[root@dhcp137-98 ~]# rpm-ostree install fwupd
...
error: Unpacking fwupdate-efi-8-2.fc26.x86_64: Unsupported path: /boot/efi; See https://github.com/projectatomic/rpm-ostree/issues/233

Let's move /boot to #853

another example of this is trying to package layer cuda from nvidia. The cuda-license package has files in /usr/local/:

Overlaying... error: Checking out cuda-license-9-0-9.0.176-1.x86_64: openat: No such file or directory

Why don't I see the Unsupported path: message here if I'm using rpm-ostree-client-2017.6-6.atomic.el7.x86_64 which should include that code with the helpful error message?

Why don't I see the Unsupported path: message

PR to resolve that here: https://github.com/projectatomic/rpm-ostree/pull/1090/files

Hit this today with the riot Copr:

https://copr.fedorainfracloud.org/coprs/taw/Riot/

$ sudo rpm-ostree install riot             
Checking out tree e824ec1... done                   
Enabled rpm-md repositories: updates fedora rpmfusion-nonfree rpmfusion-nonfree-updates rpmfusion-free rpmfusion-free-updates taw-Riot                                                                             
rpm-md repo 'updates' (cached); generated: 2017-12-03 17:30:49                                           

rpm-md repo 'fedora' (cached); generated: 2017-11-05 05:51:47                                            

rpm-md repo 'rpmfusion-nonfree' (cached); generated: 2017-11-13 19:15:57                                 

rpm-md repo 'rpmfusion-nonfree-updates' (cached); generated: 2017-11-27 16:42:42                         

rpm-md repo 'rpmfusion-free' (cached); generated: 2017-11-27 14:03:16                                    

rpm-md repo 'rpmfusion-free-updates' (cached); generated: 2017-11-27 16:33:16                            


Updating metadata for 'taw-Riot': [=========================] 100%
rpm-md repo 'taw-Riot'; generated: 2017-12-04 08:08:05                                                   


Importing metadata [======================] 100%
Resolving dependencies... done                      
Will download: 2 packages (60.3 MB)                 

  Downloading from taw-Riot: [======================] 100%

  Downloading from fedora: [======================] 100%
error: Unpacking riot-0.13.1-0.taw.fc27.x86_64: Unsupported path: /opt/riot; See https://github.com/projectatomic/rpm-ostree/issues/233

I'm using the flatpak for it from flathub myself.

I'm using the flatpak for it from flathub myself.

Of course! That wasn't immediately obvious in any of the docs I found. :(

Fixing (or working around) this problem sooner than later will be advantageous to us, especially if more people start using atomic workstation.

Yeah, it shouldn't be too hard now to go the translate path + symlink approach. Will try to take a look at this soon!

Yeah, it shouldn't be too hard now to go the translate path + symlink approach

But how will we deal with apps that actually have data in /opt? I'm not sure this should be on top of our list, the complexity seems really high versus the gain, since we want people to containerize most of these apps anyways right? (Puppet is perhaps an exception)

But how will we deal with apps that actually have data in /opt?

Yeah, those will just error out at runtime, I guess. Depending on how common it is in those RPMs to only have code in /opt but still keep data in /var, it might still be worth doing the easy thing, no? E.g. it seems like this should fix Chrome at least, right?

(But true, you'd want to use flatpak there -- although it doesn't seem like there's an official one out yet).

My feeling here is that there's two types of /opt things - those which put content into /usr (let's call this "mixed") and those that don't.

For Chrome, which is mixed, I'll reiterate that I think the most sustainable path is to get them to change their RPM upstream to move into e.g. /usr/lib/google/chrome or whatever. /opt isn't buying them much here as far as avoiding clashes.

For the "non-mixed" i.e. pure /opt RPMs...it would be a lot of work but we could probably make those "non-transactional". The problem is maintaining coherence for the rpmdb. Maybe what we could do is have /opt/rpm-ostree/rpmdb, and basically union that data with the final rpmdb in the tree whenever the host tree changes. And changes to opt-RPMs always behave traditionally, i.e. updates to them are live and not offline? Or perhaps for offline updates (i.e. the rpm-ostree default), we do updates to them at shutdown time (ugh)?

Hm, no that wouldn't quite work, since at the moment rollback in order to be ultra-reliable is a simple swap of bootloader entries. We'd have to change it to re-union the DB. Hmm. Probably the most reliable thing would be resynthesizing a unioned rpmdb at boot time. Or of course drive db unioning into librpm. Or try to detect non-coherence dynamically. And all of this would not play nicely with ostree admin unlock + rpm -Uvh some-pure-opt.rpm since the overlay wouldn't cover /opt, but would cover /usr where we store the db...

let's take a step back I definitely don't want complicated uniondb without it being necessary. Is it possible to allow someone to layer packages into /opt/ (using package layering), but if they do this they are giving up write access to /opt/. i.e. if you install this package via package layering you give up write access to /opt/ (y/n). It would also require that /opt/ be empty before the package layering operation.

If this is a possible solution, then what about the same thing for /usr/local?

we could even implement this as two operations:

rpm-ostree seal /opt/  --> requires $dir to be empty
rpm-ostree install rpm-with-opt-contents.rpm

If they change their mind later:

rpm-ostree uninstall rpm-with-opt-contents.rpm
rpm-ostree unseal /opt/

If we were going to go that way I think it'd be simpler to have the "seal" operation be:

rmdir /var/opt && ln -sfr /usr/opt /var/opt

or so. But the hard part is investigating whether this is actually compatible with the RPMs that install in /opt.

rmdir /var/opt && ln -sfr /usr/opt /var/opt

Sounds easy, would that satisfy the requirements? Is there a way that we could make this a 'generic solution' such that other top level directories could be included or excluded from the tree? If say another company had some top level directory they used that was specific to them /abccompany/ and they wanted to install rpm that owned files there, could we allow them to create this directory and include it in the tree with a 'seal' operation.

or so. But the hard part is investigating whether this is actually compatible with the RPMs that install in /opt.

Only one way to find out: rpm-ostree ex :)

Another example is nessus:

error: Unsupported path: /opt/nessus_agent;

reported by southsys in IRC

would be good to figure some fix for the existing chrome rpm, indeed

mklvr commented

SpiderOak ONE is also affected by this (one Project Atomic Workstation):
error: Unsupported path: /opt/SpiderOakONE; See https://github.com/projectatomic/rpm-ostree/issues/233

Random Idea:

Another possibility would be to have a readonly /usr/opt and a read/write /opt/. Files that were owned by rpms would get placed into both, but the rpm owned files in /opt/ maybe have the immutable bit set (chattr +i) and an ostree fsck would verify the files had not changed.

Another example is citrix receiver.
error: Unsupported path: /opt/Citrix

The Keybase.io Fedora client also attempts to install into /opt/keybase/Keybase and errors out.

yup, experiencing this problem with WPS office, Google chrome, and the Hangouts plugin on Fedora Atomic Workstation. Would manually extracting the RPM and copying the files to the directories (such as to /opt) work?

Any traction on this? If not, is there something I can help with?

Any updates to this?

znmeb commented

SpiderOak One (https://spideroak.com/one/) also installs to /opt. This is my go-to backup / device sync software.

add the bluejeans app to the /opt -out list :)

FWIW, if anyone is like "Just make Chrome work on Silverblue", you can do:

rpm-ostree install libXScrnSaver

(Then optionally rpm-ostree ex livefs, or reboot)

Then:

rpm2cpio /path/to/google-chrome.rpm | (cd / && cpio -div)

It will fail to write files to /usr which breaks desktop integration (like the app launcher), but you can run Chrome directly from the command line by executing /opt/google/chrome/google-chrome.

With this path you won't get updates though without redownloading the RPM.

This could be scripted, but any effort here is probably better put into flatpak'ing it.

FWIW, if anyone is like "Just make Chrome work on Silverblue", you can do

I've been telling people to try out rpm-ostree install chromium, but I'm sure in some cases they want chrome and not just chromium. chromium works pretty well for me so it depends on what you need it for.

FWIW, if anyone is like "Just make Chrome work on Silverblue", you can do

I've been telling people to try out rpm-ostree install chromium, but I'm sure in some cases they want chrome and not just chromium. chromium works pretty well for me so it depends on what you need it for.

Yeah, I am currently using Chromium, but I would really like regular Chrome to have the flash support and other features.

marking this as difficulty/hard. Please let me know if that needs to be adjusted.

jurf commented

Ah, ULD also requires /opt:

error: Importing uld: Unsupported path: /opt/samsung/scanner/share/oem.conf; See https://github.com/projectatomic/rpm-ostree/issues/233

Does anyone have a workaround for Keybase in the meantime?

Does anyone have a workaround for Keybase in the meantime?

I was able to run keybase from inside a container

cjao commented

Chrome also seems to work in a container (tested in Fedora Toolbox ), albeit with limited desktop integration.

cjao commented

The core problem here is: OSTree defines /opt (really /var/opt) as system administrator territory - it's never rolled forward/backwards etc.

Is this set in stone? What would happen if one treats /opt like /usr?

Does is issue relevant to my problem - after i'm build my new ostree with fedora 29 and install it so system i have old chrome browser. Package systems says tha i have google-chrome-stable-70.0.3538.102-1.x86_64 but about says: Version 63.0.3239.84

also rpmverify says that md5 of files does not match.

any chance to get this fixed?

I ran into this issue twice in the previous week. First a printer driver would not install and second Enpass 6 is now distributed as an rpm package (they used a custom installer before which worked without any problems), both issues were due not being able to write to /opt.

One can install chromium but chromecasting does not work (a feature I actually would like to use from time to time). Being able to use Google Chrome would solve this issue for me as well I guess.

@cgwalters @dustymabe any change to fix this really old issue? I need to use google chrome and printer drivers.

@vtolstov sorry - unfortunately we are busy with other commitments right now

@cgwalters @jlebon - out of the possible solutions to this problem what do you think is the most attractive?

  • seal opt (or other directories), allow package layering - conversation around #233 (comment)
  • have a readonly /usr/opt and a read/write /opt where copies of files in /usr/opt get shadowed and made immutable over in /opt - suggested in #233 (comment)

others? If we can clearly identify which way we want to go then we can maybe get a volunteer to work on it.

Yes, please identify the preferred approach, then I'll look for somebody to dispatch here

i'm happy with any workaround that can be applied in tree compose before deployed to end-user hardware.

add the bluejeans app to the /opt -out list :)

FWIW, there's a BlueJeans flatpak available now: https://flathub.org/apps/details/com.bluejeans.BlueJeans

does anybody can provide workaround? slack already says that my chrome is too old (72 in rpm and 63 on my system, that can't upgrade it).

Ran into this today, with Chrome not being able to install plugins (presumably) because of read-only /opt. Specifically, I installed google-chrome-stable from the Google Chrome repositories after enabling them on both SilverBlue and regular Fedora 30, using rpm-ostree install google-chrome-stable and sudo dnf install google-chrome-stable). In SilverBlue, opening a flash application fails with download failed, and in regular Fedora everything works after restarting Chrome because the flash plugin is installed automatically succesfully. Chrome is installed into /opt, and based on what I've read the plugin is downloaded there, so it breaking with download failed (because of the read-only fs) makes sense.

Should this issue have been closed? Although installation into /opt works, installation into /usr/local - also mentioned in teh title - does not (afaict). Let alone "etc", which presumably means many more locations. Personally, I came across the issue when shiny-server started using /srv as the root for applications.

Yeah agreed, as mentioned in #1795, it was only a partial fix which addresses the Chrome case.

cjao commented

Instead of attempting to shoehorn incompatible RPMs into the OSTree model, what if one were to install them using regular dnf in an "app container" such as a system-wide version of toolbox? ChromeOS takes this approach with its Crostini subsystem -- users install standard deb packages in an LXD system container, and there is a middle layer connecting the container with the host system, for instance, by automatically exporting launchers and icons.

How do I get rpm-ostree install to put my .rpm in /var/opt/ rather than /usr/lib/opt? This application needs to be able to write to its own install directory and this behavior from rpm-ostree is breaking things

Requesting that this get addressed, Doppler-cli is also not compatible due to it dropping files into the /usr/local directory.

This issue also breaks the https://www.unifiedremote.com/download/other#linux install on Silverblue

OK so, nowadays with the whole container-native change, one can do completely arbitrary stuff in a container build (e.g. Dockerfile) and perform any requisite custom transformations there.

Here's a demo of installing the HP scripting tools:

FROM quay.io/fedora/fedora-coreos:testing-devel
RUN mkdir /var/opt && \
    rpm -Uvh https://downloads.linux.hpe.com/repo/stk/rhel/7/x86_64/current/hp-scripting-tools-11.60-20.rhel7.x86_64.rpm && \
    mv /var/opt/hp/ /usr/lib/hp && \
    echo 'L /opt/hp - - - - ../../usr/lib/hp' > /usr/lib/tmpfiles.d/hp.conf && \
    ostree container commit

Is it beautiful? No. But there's going to be a lot of special casing here, particularly for applications which mix mutable/writable state like log files alongside binaries. In this hp-scripting-tools case, it looks to me like it's mostly binaries, so we can just move them underneath /usr and leave a symlink generated via systemd-tmpfiles for CLI compatibility.

(I verified that at least the binaries run, though I don't have a HP box handy to test this stuff)

Also of note...instead of doing export LD_LIBRARY_PATH=$HP_SCRIPTING_TOOLS_DIR/lib:$LD_LIBRARY_PATH in e.g. /usr/bin/conrep it'd be better to compile the binary injecting a DT_RUNPATH, then there's no need for a wrapper script at all really, could just have ln -s /opt/hp/hp-scripting-tools/bin/conrep /usr/bin/conrep. (Although of course, the next question is - why have the binaries in /opt at all?)

this issue also breaks https://github.com/mozilla/sops installation

This issue breaks also these:

Microsoft Defender Endpoint (how to install
https://learn.microsoft.com/en-us/microsoft-365/security/defender-endpoint/linux-install-manually?view=o365-worldwide#rhel-and-variants-centos-fedora-oracle-linux-and-amazon-linux-2)

PaloAlto GlobalProtect VPN Client

Stuff like this is deal breaker for usage of rpm-ostree based Distributions like Silverblue in an Enterprise environment.

error: importing RPMs: Importing package 'cuda-cudart-12-2': Analyzing /usr/local/cuda-12.2: Unsupported path; see https://github.com/projectatomic/rpm-ostree/issues/233

Can't install cuda

In general there are really two things to do here:

  • Fix or adjust the upstream package to use /usr, /var and /etc
  • Move the content into a container (podman/flatpak style) and cleanly decouple from the host. A sub-variant of this is to install the software in a toolbox container. But note that the same problem applies here; when you update a container image by default unless you've attached persistent volumes all state is lost. That's the reason rpm-ostree has the rules around /opt

More than a few years later, some progress finally: ostreedev/ostree#3113

Just had a similar issue when trying to layer nix-multi-user RPM on Fedora Kinoite

error: Importing package 'nix-multi-user': Analyzing /nix/var/nix/daemon-socket: Unsupported path

imbev commented

error: Importing package 'docker-desktop': Analyzing /usr/local/bin/docker: Unsupported path; see #233

jlebon commented

More movement on this in ostreedev/ostree#3120 and #4728.

With those PRs, one can enable the feature locally by setting an environment variable:

root@cosa-devsh:~# mkdir -p /etc/systemd/system/rpm-ostreed.service.d/
root@cosa-devsh:~# cat > /etc/systemd/system/rpm-ostreed.service.d/state-overlay.conf
[Service]
Environment=RPMOSTREE_EXPERIMENTAL_FORCE_OPT_USRLOCAL_OVERLAY=1
root@cosa-devsh:~# systemctl daemon-reload
root@cosa-devsh:~# systemctl restart rpm-ostreed
root@cosa-devsh:~# systemctl status rpm-ostreed | grep overlay.conf
             └─state-overlay.conf

At least Puppet seems to work well (though I had to setenforce 0; need to dig into that, not sure if it's just because of f36 vs f39):

root@cosa-devsh:~# rpm-ostree install https://yum.puppetlabs.com/puppet/fedora/36/x86_64/puppet-agent-7.27.0-1.fc36.x86_64.rpm
Downloading https://yum.puppetlabs.com/puppet/fedora/36/x86_64/puppet-agent-7.27.0-1.fc36.x86_64.rpm...done
...
Added:
  puppet-agent-7.27.0-1.fc36.x86_64
Changes queued for next boot. Run "systemctl reboot" to start a reboot
root@cosa-devsh:~# reboot
...
root@cosa-devsh:~# setenforce 0
root@cosa-devsh:~# systemctl start puppet
root@cosa-devsh:~# systemctl status puppet
● puppet.service - Puppet agent
     Loaded: loaded (/usr/lib/systemd/system/puppet.service; disabled; preset: disabled)
    Drop-In: /usr/lib/systemd/system/service.d
             └─10-timeout-abort.conf
     Active: active (running) since Thu 2023-12-14 21:41:47 UTC; 1s ago
       Docs: man:puppet-agent(8)
   Main PID: 1430 (puppet)
      Tasks: 2 (limit: 2243)
     Memory: 64.5M
        CPU: 613ms
     CGroup: /system.slice/puppet.service
             └─1430 /opt/puppetlabs/puppet/bin/ruby /opt/puppetlabs/puppet/bin/puppet agent --no-daemonize

Dec 14 21:41:47 cosa-devsh systemd[1]: Started puppet.service - Puppet agent.
Dec 14 21:41:47 cosa-devsh (puppet)[1430]: puppet.service: Referenced but unset environment variable evaluates to an empty string: PUPPET_EXTRA_OPTS
Dec 14 21:41:47 cosa-devsh puppet-agent[1430]: Starting Puppet client version 7.27.0
jcpunk commented

I'm a bit curious as with puppet it puts some files under /opt/puppetlabs/facter/cache/cached_facts which are created by the embedded facter application depending on the config.

jlebon commented

I'm a bit curious as with puppet it puts some files under /opt/puppetlabs/facter/cache/cached_facts which are created by the embedded facter application depending on the config.

Yes, that should work. /opt is writable in this model but packaged content is still updated as expected during host updates.

Definitely interested in getting this into the hands of people so that real uses of /opt and /usr/local can be tested.