deploy: NoNewPrivileges=no so rootless podman can start !304
merged
merged by cmc on 2026-09-06 23:34 UTC
· krz/gitbay:runner-nonewprivs into main
3 files changed, +32 −4
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 |