Repository navigation
Conversation
GitHub-hosted runners restrict the unprivileged user namespaces bubblewrap needs, so `lake-check` cannot run there until something grants them. Making that change means changing the runner, so `lean-action` does not do it uninvited: `lake-check-sandbox` takes "none" (the default), "apparmor", "sysctl" or "setuid", and the chosen setup is applied only when the sandbox is probed and found not to work. "apparmor" is the narrowest and the documented choice. Ubuntu's restriction denies unprivileged user namespaces to programs whose AppArmor profile does not grant `userns`, so a profile covering bubblewrap leaves the restriction in force for everything else, where "sysctl" relaxes it for every program on the runner and "setuid" leaves a setuid binary behind. The privileged operations act only on a root-owned distribution bubblewrap at /usr/bin/bwrap or /bin/bwrap, never on a symlink and never on one named by COMPARATOR_BWRAP: otherwise PATH or that variable would decide which file gets made setuid root or gains a policy. `chmod u+s` on a file owned by the job user grants that user's privileges rather than root's, so refusing a non-root owner also avoids a change that would look like it had worked. None of these are restored afterwards, which the warnings and the documentation say plainly, because on a persistent self-hosted runner they outlive the job. The probe now exercises the namespace and mount operations Lake itself uses rather than a bare bind, since a runner can permit `--ro-bind` and still refuse the sandbox Lake actually builds.
austinletson
added this pull request to stack #200
October 12, 2026 17:35
Collaborator
|
I'd propose leaving the sandbox setup to users rather than doing it in the action. As far as I know, it's unusual for an action to relax a runner's security restrictions, and doing so here means lean-action takes on ~90 lines of privileged code that has to keep up with runner images, when one line in the user's workflow does the same thing transparently. Since What do you think? |
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.
This PR adds a
lake-check-sandboxinput choosing how to makelake check's bubblewrap sandbox able to start on a runner that will not otherwise permit it. It stacks on #190 — feat: addlake-checkinput for sandboxed independent kernel checks, and should be reviewed after it.GitHub-hosted runners restrict the unprivileged user namespaces bubblewrap needs, so
lake-checkfails there until something grants them. Granting them means changing the runner, whichlean-actionwill not do uninvited, so the input takes"none"(the default, change nothing),"apparmor","sysctl"or"setuid". Whichever is chosen is applied only when the sandbox has been probed and found not to work, so a runner that already permits user namespaces is left untouched."apparmor"is the narrowest and the documented choice. Ubuntu's restriction denies unprivileged user namespaces to programs whose AppArmor profile does not grantuserns, so a profile covering bubblewrap leaves the restriction in force for every other program, where"sysctl"relaxes it runner-wide and"setuid"leaves a setuid binary behind. Measured onubuntu-latest: all three make the sandbox work, and under"apparmor"and"setuid"the sysctl stays at1.Because these operations are privileged, they act only on a root-owned distribution bubblewrap at
/usr/bin/bwrapor/bin/bwrap, never on a symlink, and never on an executable named byCOMPARATOR_BWRAP. Without those guards,PATHor that variable would decide which file gets made setuid root or gains an AppArmor policy. Refusing a non-root owner also matters in its own right:chmod u+son a file owned by the job user grants that user's privileges rather than root's, so it would leave the sandbox broken while appearing to have worked.None of these changes is undone afterwards. The warnings and the documentation say so, because on a persistent self-hosted runner they outlive the job, and such runners are better configured once by their administrator with this left at
"none".The sandbox probe now exercises the namespace and mount operations Lake itself uses rather than a bare bind, since a runner can permit
--ro-bindand still refuse the sandbox Lake actually builds.A functional test runs
paranoidonubuntu-latestunder"apparmor", so the option the documentation recommends is the one CI exercises.🤖 Prepared with Claude+Codex