Git, CI/CD & GitOpsCIV · CI/CD Anti-PatternsRunnerHardening
Permanent privileged runners — long-lived self-hosted runners with standing credentials
What you'll learn
- Identify the three standing-privilege shapes that turn a self-hosted runner into a persistence host
- Explain why a runner that never restarts is a runner that never re-checks trust
- Distinguish ephemeral runners with OIDC federation from long-lived runners with stored credentials
Prerequisites
Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x
A permanent self-hosted runner is a host an attacker
inherits - always on, registered, holding a long-lived
cloud key, and answering every PR the forge sends. The
first untrusted PR that runs sudo has not broken out
of a sandbox; it has landed on a host whose privileges
are the runner’s.
Three shapes the violation takes
- Always-on VM. Linux host registered 24/7 with
sudo and a stale
~/.aws/credentials. - Shared Docker socket.
/var/run/docker.sockmounted into every job;docker run --privilegedescapes to the host as root. - Persistent cloud key.
AWS_ACCESS_KEY_IDexported from a startup script and reused across every job.
flowchart LR
A["Always-on runner VM"] --> D["Standing privilege"]
B["Mounted Docker socket"] --> D
C["Persistent cloud key"] --> D
D --> E["Compromised PR pivots to host"]
D --> F["Compromised PR pivots to cloud"]
D --> G["Credentials reused across jobs"]
D --> H["No job-to-job isolation"]
Why a runner that never restarts never re-checks trust
Ephemeral runners flip the model: the host boots with
no credentials; the job requests an OIDC token
(short-lived, scoped to repo and branch); the host
terminates; the token is revoked. A permanent runner
skips every step, and the ~/.aws/credentials from
the previous job stays on disk.
What ephemeral runners with OIDC look like
Ephemeral runners. Actions Runner Controller scales
a Kubernetes pod per job; --ephemeral runners
register, take one job, and unregister. OIDC
federation. The cloud never sees a long-lived key; it
sees a token issued by the forge, scoped to the
repository and branch, with a sub claim naming the
workflow. The trust policy in cloud IAM decides
whether the token may assume a role.
Production discipline
- Runners are ephemeral by default.
- Standing credentials are forbidden on runner
hosts.
~/.aws/credentialsis minted per job by OIDC. - The Docker socket is not mounted.
Cross-course references
- This course, Part XLI (RunnerSecurity) - runner threat model, Docker socket risk.
- This course, Part XLIII (OIDC) - OIDC federation.
- Linux for Production Sysadmins, Part XII (RepoSecurity) - package repository trust.
Quiz
Knowledge check · 4 questions
Q1. A self-hosted runner registered two years ago holds an AWS access key in ~/.aws/credentials. A fork PR exfiltrates it. What has the runner leaked?
Q2. Mounting the host Docker socket into a CI job container is not necessarily acceptable just because the job runs untrusted code in a sandbox.
Q3. Name the two controls that together remove the standing credential from a runner host.
Q4. Diagnose a permanent-runner compromise and recommend the architecture that prevents it.
Terraform pipeline runs on a runner registered eighteen months ago holding ~/.aws/credentials, mounting /var/run/docker.sock into every job, never rebooted.
Passing score: 75%. Answers are checked in this browser.