From 0dace185def2da85d6bc7a366c03bdc74bf22ecf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matheus=20Bregu=C3=AAz?= Date: Sun, 13 Sep 2026 15:10:19 -0300 Subject: [PATCH] ci: let bubblewrap create a user namespace on the runner The judge-image job failed its first run against a master that has the sandbox: 820 tests, 48 of them skipped with "bubblewrap cannot create a sandbox here (user namespaces unavailable?)", and --fail-on-skipped turned that into a failure. Which is the job working as intended -- that flag exists precisely so a sandbox that cannot start reads as a failure rather than as a green build over 48 quietly skipped tests. The cause is the host, not the container: Ubuntu 24.04 runners set kernel.apparmor_restrict_unprivileged_userns=1, and AppArmor is enforced on the host, so --privileged alone does not get bwrap a user namespace. The sysctl is relaxed before the run, and apparmor/seccomp confinement is dropped explicitly rather than relied on as a side effect of --privileged. Both sysctls are `|| true`: neither knob exists on every runner image, and their absence is not itself a problem. Whether the sandbox actually came up is decided by --fail-on-skipped, not by these lines succeeding. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01Pvf2R8xtgbWQm1wRvjBqQY --- .github/workflows/ci.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 30c9ee6c..37e00b33 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -141,6 +141,21 @@ jobs: # to is the targeted fix; running the container as the runner's uid # instead would stop matching how docker-compose.yml actually runs the # autojudge service, which is the thing this job exists to verify. + # Ubuntu 24.04 runners ship + # kernel.apparmor_restrict_unprivileged_userns=1, and that policy is + # enforced on the host, so it reaches processes inside the container + # too: bwrap cannot create a user namespace and every sandbox test + # skips itself. --fail-on-skipped is what turned that into a failure + # instead of a green build over 48 silently skipped tests. + # + # `|| true` because neither knob exists on every runner image, and + # their absence is not itself a problem -- whether the sandbox came up + # is decided by --fail-on-skipped, not by these lines succeeding. + - name: Allow unprivileged user namespaces for bubblewrap + run: | + sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 || true + sudo sysctl -w kernel.unprivileged_userns_clone=1 || true + - name: Let the judge user write to the mounted workspace run: | chmod -R a+rwX storage bootstrap/cache @@ -157,6 +172,8 @@ jobs: - name: Full suite inside the judge image run: | docker run --rm --privileged \ + --security-opt apparmor=unconfined \ + --security-opt seccomp=unconfined \ -v "$PWD":/var/www/html \ -w /var/www/html \ microhelium-judge:ci \