Self-hosted runners run untrusted code by default
If your repo accepts PRs from anyone, those PRs can include workflow changes. Without isolation, a malicious PR can: read your filesystem, dump env vars, install a backdoor, exfiltrate secrets cached on disk. The default setup is dangerous.
The hardening checklist
- Don't expose to public forks. Repo Settings → Actions → Require approval for outside collaborators (or all PRs).
- Use ephemeral runners. One job per VM lifecycle. After the job, the VM is destroyed. Tools: actions-runner-controller (k8s), runs-on (AWS), philips-labs/terraform-aws-github-runner.
- Run in a container or VM. Don't run the runner agent on the host OS directly.
- Strict network egress. Allow only outbound to GitHub + your intended targets. Block default-allow.
- Separate trust tiers. Public-PR runners and trusted deploy-runners must not share a host.
- Secrets at registration time, not on disk. Don't bake secrets into the runner image. Use OIDC and per-job secret injection.
- Patch and rotate. Runner agents are auto-updated; OS patches are your responsibility.