CI runner executes untrusted repository code as the instance admin #92

closed cmc opened this on 2026-09-03 05:12 UTC · bug ci priority security · milestone v1.9.0

Discussion

cmc 2026-09-03 05:12 UTC

Runner commands require an admin account (internal/control/build.go:56, requireRunner at :309). On gitbay.org the runner polls as cmc (admin runners --json) with no -repos scope, so it claims builds from every repository. Steps run through sh -c under the runner's own uid with os.Environ() inherited (cmd/gitbay-runner/main.go:259, :269), on the same host as gitbayd and the database. deploy/gitbay-runner.override.conf sets only Nice and IO weight. Registration is open.

Chain: sign up, push a repository with a .gitbay/ci.yml, read the runner's SSH key from inside a step, run admin user promote.

The Threat-Model wiki page does not mention the runner. The roadmap's "the forge never executes repository content itself" is true of gitbayd, not of the host.

Remedy, short term: a dedicated non-admin runner role that only runner next/log/done accept; the runner as its own unix user with ProtectHome=yes and a scrubbed environment; a -repos allowlist or an admin-set per-repo ci_enabled flag. Until then run the runner with -repos krz/gitbay and keep registration on invite. Medium term: steps in a container or user namespace, ideally on another host.

referenced in commit b15d84acd6 by cmc: runner: a key scope that reaches the runner protocol and nothing else

2026-09-03 15:59 UTC
cmc 2026-09-03 16:11 UTC

Rotated on bay1 2026-09-03. The runner authenticates as ci, a non-admin account whose only key has scope runner; the same key is no longer registered on cmc. admin runners shows ci polling and build 395 succeeded under the sandboxed unit. The account had one bootstrap full-scope key for the duration of the rotation, since removed.

Mechanism landed in !188. Left for later: env scrubbing in the runner and a per-repository CI opt-in; the runner is scoped with -repos krz/gitbay on the host instead. The Threat-Model wiki page still needs a section on the runner (#138).