You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Track: B — Org/infrastructure governance Pillar: 5 — Network Egress & Infrastructure Isolation Parent: (see GitHub Actions platform security parent)
Why egress control matters
A compromised runner — via a malicious action, a supply chain attack, or an injected shell command — has network access from the runner's perspective. Without egress controls, it can exfiltrate secrets to an attacker-controlled endpoint, download additional malware, or call internal APIs on the same network. Egress control is the last line of defence when all other controls fail.
GitHub-hosted runners — native egress firewall
GitHub's 2026 Security Roadmap introduces a native Layer 7 egress firewall for GitHub-hosted runners. Two phases:
Phase 1 — Monitor mode (adopt immediately when available)
Run the egress firewall in audit mode: all outbound traffic is logged but not blocked. Use this to build the baseline allowlist of domains your pipelines legitimately need:
Near-term alternative — StepSecurity harden-runner
While the native GitHub egress firewall is in progress, harden-runner provides the same monitor → enforce pattern today on GitHub-hosted and self-hosted runners:
steps:
- uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2with:
egress-policy: audit # start in audit mode# egress-policy: block # switch to block after baseline capturedallowed-endpoints: > ghcr.io:443 registry.npmjs.org:443 pypi.org:443 api.github.com:443
harden-runner also detects and blocks:
Processes spawned outside the workflow steps
DNS exfiltration attempts
Cryptomining (anomalous CPU + outbound patterns)
Self-hosted runner policy
Self-hosted runners carry additional risk: they are persistent, may have local credentials, and run on your own infrastructure. Policy requirements:
Control
Requirement
Ephemeral runners
Runners must be ephemeral — spun up per job, destroyed after. No persistent runner that accumulates state across jobs.
Network isolation
Runner VPC/subnet must not have routes to corporate intranets, databases, or internal APIs. Internet egress only, through the allowlist.
No fork PRs
Self-hosted runners must never execute workflows triggered by external fork PRs. Configure at repo level: Settings → Actions → Fork pull request workflows → require approval for all outside collaborators.
Minimal IAM
Runner IAM role/instance profile scoped to the minimum required — no AdministratorAccess, no cross-account trust beyond what OIDC (#27) explicitly grants.
Acceptance criteria
harden-runner added to all workflows in this repo in audit mode — baseline traffic captured
Allowlist documented in docs/egress-allowlist.md
harden-runner switched to block mode after baseline confirmed
Self-hosted runner policy documented and applied to any Sparkgeo self-hosted runners
Native GitHub egress firewall adoption tracked — issue updated when feature reaches GA (2026 roadmap)
No self-hosted runners processing external fork PRs
Context
Track: B — Org/infrastructure governance
Pillar: 5 — Network Egress & Infrastructure Isolation
Parent: (see GitHub Actions platform security parent)
Why egress control matters
A compromised runner — via a malicious action, a supply chain attack, or an injected shell command — has network access from the runner's perspective. Without egress controls, it can exfiltrate secrets to an attacker-controlled endpoint, download additional malware, or call internal APIs on the same network. Egress control is the last line of defence when all other controls fail.
GitHub-hosted runners — native egress firewall
GitHub's 2026 Security Roadmap introduces a native Layer 7 egress firewall for GitHub-hosted runners. Two phases:
Phase 1 — Monitor mode (adopt immediately when available)
Run the egress firewall in audit mode: all outbound traffic is logged but not blocked. Use this to build the baseline allowlist of domains your pipelines legitimately need:
Typical allowlist entries for our stack:
ghcr.io,registry.npmjs.org,pypi.org,pkg.go.dev— package registries*.amazonaws.com— AWS APIs (scoped further once OIDC is in place, feat: OIDC federation and secret management #27)semgrep.dev,codeql.github.com— scanner update feedsrenovatebot.com— Renovate update checksPhase 2 — Enforce mode
After 2–4 weeks of monitoring to confirm the allowlist is complete, switch to enforce mode. Any connection not on the allowlist is blocked and logged.
Near-term alternative — StepSecurity
harden-runnerWhile the native GitHub egress firewall is in progress,
harden-runnerprovides the same monitor → enforce pattern today on GitHub-hosted and self-hosted runners:harden-runneralso detects and blocks:Self-hosted runner policy
Self-hosted runners carry additional risk: they are persistent, may have local credentials, and run on your own infrastructure. Policy requirements:
AdministratorAccess, no cross-account trust beyond what OIDC (#27) explicitly grants.Acceptance criteria
harden-runneradded to all workflows in this repo inauditmode — baseline traffic captureddocs/egress-allowlist.mdharden-runnerswitched toblockmode after baseline confirmed