Skip to content

feat: runner egress control and isolation #28

@ms280690

Description

@ms280690

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:

# Future syntax — 2026 roadmap
jobs:
  build:
    runs-on: ubuntu-latest
    egress:
      mode: monitor   # log all outbound traffic

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 feeds
  • renovatebot.com — Renovate update checks

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

jobs:
  build:
    runs-on: ubuntu-latest
    egress:
      mode: enforce
      allow:
        - ghcr.io
        - registry.npmjs.org
        - pypi.org

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  # v2
    with:
      egress-policy: audit          # start in audit mode
      # egress-policy: block        # switch to block after baseline captured
      allowed-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

Metadata

Metadata

Assignees

Labels

No fields configured for Feature.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions