← Workflows

阅读中文版本

Self-Hosted Runner Hardening Workflow

Isolate runners per trust level, run jobs in containers or VMs, restrict network and filesystem access, and treat runners as untrusted infrastructure.

advanced~150 mincisecurityinfrastructure

Objective

Harden self-hosted CI runners so a malicious job cannot compromise the host or reach production.

Environment

  • Self-hosted runner infrastructure
  • Container or VM isolation for jobs

Steps

1. Separate runners by trust level: public,

Separate runners by trust level: public, internal, and production.

Expected state: Untrusted jobs cannot reach trusted networks.

2. Run jobs inside a fresh container

Run jobs inside a fresh container or VM that is destroyed after.

Expected state: Jobs do not persist state on the host.

3. Restrict filesystem and network access

Restrict filesystem and network access to what the build needs.

Expected state: Lateral movement is limited.

4. Treat runner hosts as untrusted

Treat runner hosts as untrusted and avoid long-lived credentials on them.

Expected state: A host compromise is contained.

Decision points

Persistent or ephemeral runners?

  • Ephemeral
  • Persistent with cleanup

Ephemeral for untrusted jobs; persistent only for controlled internal jobs with strict cleanup.

Allow production access from any runner?

  • No, separate production runners
  • Yes with guards

No; production access only from dedicated, tightly controlled runners.

Expected outcome

Runners isolated by trust level, jobs ephemeral, network and filesystem restricted, and no long-lived secrets on hosts.

Verification

Confirm a job cannot read the host filesystem beyond its workspace or reach production networks, and that runner hosts rotate regularly.

Cleanup

Record the runner inventory and trust classifications.

Overview

Harden self-hosted CI runners so a malicious job cannot compromise the host or reach production. Confirm a job cannot read the host filesystem beyond its workspace or reach production networks, and that runner hosts rotate regularly.

Steps

  1. Separate runners by trust level: public, internal, and production. Expected state: Untrusted jobs cannot reach trusted networks.
  2. Run jobs inside a fresh container or VM that is destroyed after. Expected state: Jobs do not persist state on the host.
  3. Restrict filesystem and network access to what the build needs. Expected state: Lateral movement is limited.
  4. Treat runner hosts as untrusted and avoid long-lived credentials on them. Expected state: A host compromise is contained.
# run a job in an ephemeral container with limited mounts
docker run --rm --network none -v "$PWD:/work" -w /work ubuntu:24.04 ./build.sh

Verification

  • Runners isolated by trust level, jobs ephemeral, network and filesystem restricted, and no long-lived secrets on hosts.

Decision points

  • Persistent or ephemeral runners? Options: Ephemeral; Persistent with cleanup. Recommended: Ephemeral for untrusted jobs; persistent only for controlled internal jobs with strict cleanup.
  • Allow production access from any runner? Options: No, separate production runners; Yes with guards. Recommended: No; production access only from dedicated, tightly controlled runners.