CLOS-7025: reinstall the CageFS hooks on the first boot - #73
Open
prilr wants to merge 3 commits into
Open
Conversation
CageFS installs its hooks from its own %posttrans scriptlet in two steps: install-cagefs-plugin.py --install runs first and, on Plesk >= 10, deliberately strips the pam_lve.so and pam_sulve.so lines from /etc/pam.d/su, and the cagefsctl --hook-remove + --hook-install pair further down (CAG-377) is what puts the pam_lve.so line back. cagefsctl cannot run inside leapp's upgrade transaction: the system is not booted with systemd as PID 1 and the process cannot look itself up in /proc, so clcommon's logging setup raises psutil.NoSuchProcess and cagefsctl exits having done nothing - one converted box's leapp-upgrade.log records 171 cagefsctl invocations dying that way. Only the stripping half of the pair takes effect, so after a CloudLinux 8 to 9 elevation on Plesk /etc/pam.d/su has no pam_lve.so line, CageFS users silently stop entering the cage through 'su', and cldiag fails on "pam_lve configuration is not found in /etc/pam.d/su config file". util-linux, which owns the file, is not involved: it ships /etc/pam.d/su as %config(noreplace) and its el8 and el9 copies are byte-identical, so rpm leaves the file alone and does not even write an .rpmnew. The first boot is a fully booted system, where cagefsctl works, so the update_cagefs actor runs 'cagefsctl --hook-install' there - before the cage rebuild, so that the cages get the corrected /etc/pam.d files. It is idempotent, and its failure does not skip the rebuild. Verified on a CloudLinux 9 + Plesk VM: with the hooks intact a CageFS user is caged, running the real CageFS plugin strips the line and the user reaches the host binary instead, and 'cagefsctl --hook-install' cages them again. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Worked-On: cl-aiworkspaces
Review findings on the previous commit. 'cagefsctl --hook-install' cannot report failure through its exit code: cagefsctl calls HooksInstall() and then exits zero unconditionally, and configure_pam_lve() swallows IOError and OSError into a printed message. So an immutable or read-only /etc/pam.d/su left CageFS users uncaged while this actor logged "cagefs hooks were reinstalled successfully" - checked=True could not detect the one failure its error message was written for. Check the resulting configuration instead, parsing the same non-comment 'pam_lve.so' line cldiag looks for, and parse it here rather than importing clcommon so that a first-boot actor does not depend on the CloudLinux venv being healthy. A silent loss of confinement on an otherwise successful upgrade does not belong in a debug log nobody reads afterwards, so that outcome now also emits a report. The actor already declared produces = (Report,); this makes the declaration true. Second finding: leapp's run() raises OSError, not CalledProcessError, when the executable is missing or has wrong permissions, and the guard in process() is os.path.exists rather than os.access(X_OK). A cagefsctl that exists but is zero-byte or non-executable - the shape of leapp's own /sbin/new-kernel-pkg stub - therefore aborted the actor and cost the cage rebuild that follows. Both cagefsctl calls now go through one helper that catches either exception, so neither call can take the other's step with it. Tests: the two failure modes are pinned, along with a commented-out pam_lve line and an unreadable config, both of which must count as not configured. Before this change five of the nine tests fail - two on the OSError escaping, three on the silent failure going unreported. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Worked-On: cl-aiworkspaces
CageFS's %posttrans makes 25 cagefsctl calls; this actor restores two. --force-update sets force-update/update/build_jail_flag and checks the skeleton, so it rebuilds the skeleton and the jails and covers none of the others: --setup-cl-selector, --update-wrappers, --reconfigure-cagefs, --isolates-regenerate, --sync-proxy-commands, --create-virt-mp-all and the rest have no references in cagefsctl.py outside their own option handlers, so nothing in the update path reaches them. They are lost to the same crash, which on a Plesk or cPanel box with site isolation can leave PHP Selector broken inside the cages and the per-domain lsphp includes absent. Widening this actor is not the answer. The fix belongs in clcommon, where one guard restores all 25 at once, and the sequencing makes that sufficient on its own: the transaction upgrades the CloudLinux venv and cllib around step 1.5k of 7k while CageFS's %posttrans runs at 7077/7077, so the copy that crashes is the target system's, and a fixed cllib in the target repositories covers every subsequent upgrade - the source system's copy never runs. Replaying more of a vendor scriptlet from a first-boot actor would trade a measured narrow gap for unmeasured breadth: --remount-all on a live box, for one. Comment only, so the narrow scope reads as a decision rather than an oversight. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Worked-On: cl-aiworkspaces
prilr
force-pushed
the
CLOS-7025-reinstall-cagefs-hooks-on-first-boot
branch
from
September 8, 2026 02:39
a09b13a to
edb95a9
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
After a CloudLinux 8 to 9 elevation on Plesk,
/etc/pam.d/suhas nopam_lve.soline, CageFS users silently stop entering the cage throughsu, andcldiag --allfails on "pam_lve configuration is not found in /etc/pam.d/su config file".Why it happens
CageFS installs its hooks from its own
%posttransscriptlet in two steps:install-cagefs-plugin.py --installruns first and, on Plesk >= 10, deliberately strips thepam_lve.soandpam_sulve.solines from/etc/pam.d/suwithsed.cagefsctl --hook-remove+cagefsctl --hook-installfurther down (CAG-377) put thepam_lve.soline back.On a booted system both halves run and the file settles at
session required pam_lve.so 500 1.Inside leapp's upgrade transaction
cagefsctlcannot run at all: the system is not booted with systemd as PID 1 and the process cannot look itself up in/proc, so clcommon logging setup raisespsutil.NoSuchProcessand cagefsctl exits having done nothing. Only the stripping half takes effect.Plesk only: cPanel, DirectAdmin and nopanel keep the line, because the strip sits in the plugin's Plesk branch.
The change
The
update_cagefsFirstBoot actor now runscagefsctl --hook-installbeforecagefsctl --force-update, performing on the first boot the repair the transaction could not. Idempotent, a failure does not skip the cage rebuild, and both failure paths log the command to rerun.The actor's logic moves into
libraries/updatecagefs.pywith tests, following the repository's actor convention. Six unit tests cover ordering, the cagefsctl-absent guard, failure isolation and failure reporting; the ordering assertion was mutation-checked by swapping the two calls.