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 |
| 449 | assuming, enables lingering, and verifies rootless podman actually runs |
449 | assuming, enables lingering, and verifies rootless podman actually runs |
| 450 | as that user. It is idempotent. |
450 | as that user. It is idempotent. |
| 451 | |
451 | |
| |
452 | The drop-in sets =NoNewPrivileges=no=, without which rootless podman |
| |
453 | cannot call =newuidmap= and the runner refuses to start. That is a |
| |
454 | considered trade, explained in the file and in the Threat-Model; if you |
| |
455 | run with =-isolation none=, set it back to =yes=. |
| |
456 | |
| 452 | *Do not deploy an isolating runner to a host that has not been |
457 | *Do not deploy an isolating runner to a host that has not been |
| 453 | prepared.* The runner is specified to refuse to start without a working |
458 | prepared.* The runner is specified to refuse to start without a working |
| 454 | podman rather than fall back to running builds unsandboxed — a fallback |
459 | podman 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. |
| 145 | repository is trusted; there is no automatic fallback to it — a runner |
145 | repository is trusted; there is no automatic fallback to it — a runner |
| 146 | configured for podman that cannot find one refuses to start, because |
146 | configured for podman that cannot find one refuses to start, because |
| 147 | dropping isolation silently is worse than a stopped runner. The |
147 | dropping isolation silently is worse than a stopped runner. The |
| 148 | systemd drop-in still adds =NoNewPrivileges=, =ProtectSystem=full= and |
148 | systemd drop-in still adds =ProtectSystem=full= and the kernel and |
| 149 | the kernel and cgroup protections, and =-repos= still limits a runner |
149 | cgroup protections, and =-repos= still limits a runner to named |
| 150 | to named repositories. |
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. |
| 151 | |
157 | |
| 152 | Under =-isolation none=, anything a step can do as the runner's user a |
158 | Under =-isolation none=, anything a step can do as the runner's user a |
| 153 | pushed =ci.yml= can do. Under podman a step is confined to its |
159 | pushed =ci.yml= can do. Under podman a step is confined to its |
deploy/gitbay-runner.override.conf
+18 −1
| @@ -30,7 +30,24 @@ |
| 30 | Nice=10 |
30 | Nice=10 |
| 31 | CPUWeight=30 |
31 | CPUWeight=30 |
| 32 | IOWeight=30 |
32 | IOWeight=30 |
| 33 | NoNewPrivileges=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. |
| |
50 | NoNewPrivileges=no |
| 34 | ProtectSystem=full |
51 | ProtectSystem=full |
| 35 | ProtectKernelTunables=yes |
52 | ProtectKernelTunables=yes |
| 36 | ProtectControlGroups=yes |
53 | ProtectControlGroups=yes |