Commit d6632e706a

d6632e706aa99f09cb384905f2d9bfb45d16f185

parent: 760bc11ce7

Verified · cmc ci/build: failure ci/test: failure

cmc <hello@cleberg.net> · 2026-09-06 23:26 UTC

deploy, wiki: NoNewPrivileges=no so rootless podman can start

Rootless podman sets up its namespace with the setuid newuidmap, which
NoNewPrivileges blocks, so the runner refuses to start. The trade is one
hardening layer on the runner process against running builds in
containers at all; the container is the stronger boundary.

Ref #144

Layout: unified · split

.gitbay/wiki/Admin.org +5
@@ -449,6 +449,11 @@ The script installs podman, delegates a subuid/subgid range to
449449assuming, enables lingering, and verifies rootless podman actually runs
450450as that user. It is idempotent.
451451
452The drop-in sets =NoNewPrivileges=no=, without which rootless podman
453cannot call =newuidmap= and the runner refuses to start. That is a
454considered trade, explained in the file and in the Threat-Model; if you
455run with =-isolation none=, set it back to =yes=.
456
452457*Do not deploy an isolating runner to a host that has not been
453458prepared.* The runner is specified to refuse to start without a working
454459podman rather than fall back to running builds unsandboxed — a fallback
.gitbay/wiki/Threat-Model.org +9 −3
@@ -145,9 +145,15 @@ runner, polling over SSH, clones the commit and runs its steps.
145145 repository is trusted; there is no automatic fallback to it — a runner
146146 configured for podman that cannot find one refuses to start, because
147147 dropping isolation silently is worse than a stopped runner. The
148 systemd drop-in still adds =NoNewPrivileges=, =ProtectSystem=full= and
149 the kernel and cgroup protections, and =-repos= still limits a runner
150 to named repositories.
148 systemd drop-in still adds =ProtectSystem=full= and the kernel and
149 cgroup protections, and =-repos= still limits a runner to named
150 repositories. =NoNewPrivileges= is *off*: rootless podman sets up its
151 namespace with the setuid =newuidmap=, which that flag blocks, so the
152 choice is between it and containers at all. Containers are the stronger
153 boundary — the flag constrained a process that was already running
154 arbitrary repository code, and under podman that code no longer runs in
155 the runner's process context. Under =-isolation none= there is no
156 container and the flag should be on.
151157
152158Under =-isolation none=, anything a step can do as the runner's user a
153159pushed =ci.yml= can do. Under podman a step is confined to its
deploy/gitbay-runner.override.conf +18 −1
@@ -30,7 +30,24 @@
3030Nice=10
3131CPUWeight=30
3232IOWeight=30
33NoNewPrivileges=yes
33# NoNewPrivileges is off, and that is a deliberate trade (#144).
34#
35# Rootless podman sets up its user namespace with newuidmap, a setuid
36# helper; NoNewPrivileges=yes blocks it and podman fails with
37# "newuidmap: write to uid_map failed: Operation not permitted", so the
38# runner refuses to start. The choice is between this flag and running
39# builds in containers at all.
40#
41# Containers are the stronger boundary by a wide margin. NoNewPrivileges
42# constrained a process that was already executing arbitrary repository
43# code as this user; a container confines that code to an image and a
44# bind-mounted workspace. What is lost is one hardening layer on the
45# runner process itself, which is ours rather than a build's — a build no
46# longer runs in this process's context at all.
47#
48# Under -isolation none there is no container, and this flag should be
49# yes. Set it back if you run that way.
50NoNewPrivileges=no
3451ProtectSystem=full
3552ProtectKernelTunables=yes
3653ProtectControlGroups=yes