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

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

Open
cgwalters opened this issue Mar 14, 2016 · 80 comments
Open
Labels
bug difficulty/hard hard complexity/difficutly issue kind/enhancement

Comments

@cgwalters
Copy link
Member

cgwalters commented Mar 14, 2016

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.

@copumpkin
Copy link
Contributor

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

@cgwalters cgwalters changed the title Determine support path for RPMs that install into /opt Determine support path for RPMs that install into /opt, /usr/local etc Apr 1, 2016
@copumpkin
Copy link
Contributor

@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?

@copumpkin
Copy link
Contributor

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

@cgwalters
Copy link
Member Author

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

@copumpkin
Copy link
Contributor

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

@cgwalters
Copy link
Member Author

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.

@copumpkin
Copy link
Contributor

@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?

@cgwalters
Copy link
Member Author

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

@copumpkin
Copy link
Contributor

Oh, excellent, thanks!

@copumpkin
Copy link
Contributor

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.

@copumpkin
Copy link
Contributor

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.

@cgwalters
Copy link
Member Author

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.

@copumpkin
Copy link
Contributor

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 😔

@copumpkin
Copy link
Contributor

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.

@cgwalters
Copy link
Member Author

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.

@copumpkin
Copy link
Contributor

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.

@tobiashochguertel
Copy link

tobiashochguertel commented Apr 24, 2016

@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?

@tobiashochguertel
Copy link

tobiashochguertel commented Apr 24, 2016

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

@tobiashochguertel
Copy link

tobiashochguertel commented Apr 25, 2016

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?

@tobiashochguertel
Copy link

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

@cgwalters
Copy link
Member Author

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).

@cgwalters
Copy link
Member Author

cgwalters added a commit to cgwalters/rpm-ostree that referenced this issue Feb 13, 2017
See coreos#233 - for RPMs which
place files in e.g. `/opt`, we have different behavior in the treecompose case
(silently drop it) versus package layering (does the wrong thing).

Since the unpacker right now is only used in the layering case, this just
ensures we'll get a consistent error there.
jlebon pushed a commit to alexlarsson/rpm-ostree that referenced this issue Mar 26, 2019
This adds support for layering rpms with files in /opt. The way we
do this is that when importing the rpms we rewrite any files in /opt
into /usr/lib/opt, and then we add back a symlink from the toplevels
of /opt into /usr/lib/opt via the per-package tmpfiles.d.

Also, in order for this to work with the %post script we bind /opt
to usr/lib/opt during the script execution.

This fixes coreos#233 at least for Google Chrome.
@steffansluis
Copy link

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.

@timcoote
Copy link

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.

@jlebon
Copy link
Member

jlebon commented Sep 30, 2020

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

@jlebon jlebon reopened this Sep 30, 2020
@cjao
Copy link

cjao commented Oct 3, 2020

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.

@eissturm
Copy link

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

@mariaWitch
Copy link

mariaWitch commented Sep 13, 2022

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

@ghost
Copy link

ghost commented Oct 12, 2022

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

@cgwalters
Copy link
Member Author

cgwalters commented Nov 2, 2022

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?)

@luisprgr
Copy link

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

@616b2f
Copy link

616b2f commented Apr 14, 2023

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug difficulty/hard hard complexity/difficutly issue kind/enhancement
Projects
None yet
Development

Successfully merging a pull request may close this issue.