Игорь Надеин e3c677dda0
Some checks failed
Deploy production / deploy (push) Failing after 9m4s
Fix non-root runner SSH identity and verify image prerequisites
2026-09-10 17:06:11 +03:00
..
…

Lightweight shared Gitea runner

The existing global registration and deploy label are retained. The runner is not privileged, has no Docker socket, drops all capabilities, and now runs as UID 10001 with a read-only root filesystem and read-only registration mount. Job workspaces live in bounded tmpfs and do not persist across container restarts. Job capacity is one; timeout is 60 minutes. The deployment SSH credential remains repository-scoped and its CORP forced command accepts only the current master SHA.

The derived 0.2.13-hardened image adds a real runner passwd/group entry for UID/GID 10001. Setting a numeric Compose user without this entry breaks OpenSSH with No user exists for uid 10001. The installer builds from the already installed base image without network access, then verifies UID resolution and ssh -G in a read-only, network-disabled container before replacing the running service.

Install this directory's files on the Gitea server and run sudo python3 /path/to/install-runner-hardening.py only after pausing assignment of new jobs in Gitea and waiting for the current job to finish. Resume assignment after the installer confirms the runner is healthy. An idle ps snapshot alone is insufficient: the runner could receive a new job before container replacement. The installer does not pause scheduling automatically or verify that pause. The installer backs up configuration and restores it if the runner cannot rejoin.

Trust boundary: deploy:host executes all jobs as the same container user. It is not a security boundary between repository authors. Keep workflow-writing permissions restricted to trusted developers. Strict isolation requires separate ephemeral job environments; the shared host runner must not run untrusted forks. Container restrictions do not make it safe for mutually untrusted tenants.