Split from #115, whose remedy was "-jobs N in the runner; an image: per job once isolation exists". The concurrency half is done; this is the isolation half, which the wording already treats as gated on something that does not exist yet.
A build runs whatever a repository's .gitbay/ci.yml says, with sh -c, as the runner process's own user (cmd/gitbay-runner/main.go). deploy/gitbay-runner.override.conf constrains the service — NoNewPrivileges, ProtectSystem=full, RestrictSUIDSGID, CPU and IO weight — and the key is scoped runner, so a build cannot administer the instance or write outside its workspace through systemd's protections. What it can still do is read anything that user can read, reach the network, and see the other concurrent builds' workspaces under the shared -workdir.
That is acceptable while every repository on the instance is the operator's. It stops being acceptable the moment a fork's CI runs, which registration = "open" makes reachable.
Remedy needs a decision before code: which isolation mechanism. Options are a container runtime (Docker or podman, a new external dependency on the runner host and a deployment change for bay1), bwrap (lighter, Linux-only, already common on CI hosts), or a per-build unprivileged user plus a private mount namespace. image: per job in ci.yml only means something once one is chosen.
Until then: do not enable CI for repositories you do not trust, and note that -jobs N puts N untrusted builds side by side under one workdir.
referenced in commit 3b4f05eb52 by cmc: runner: -jobs N runs that many builds at once
2026-09-04 17:13 UTC