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.
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.