Summary
When an agentic workflow targets an ARC/DinD runner, gh-aw must:
- Emit
runner.topology: arc-dind in the AWF config so the firewall activates all split-filesystem handling automatically (sysroot image, tool cache redirection, network isolation, path-prefix probes).
- Ensure every step in the agent job runs rootless — no
sudo, no apt-get install, no privileged operations. On ARC, the runner container does not have root access, so anything requiring root must already be handled at image build time (by AWF's build-tools sysroot image).
Companion issue
What gh-aw needs to do
1. Detect ARC/DinD topology
When the workflow targets an ARC runner label (or the user explicitly opts in), gh-aw should know to emit the arc-dind topology config.
Detection options (not mutually exclusive):
- Explicit: user sets
runner.topology: arc-dind in their workflow config / .github/gh-aw.yml
- Inferred: gh-aw recognizes ARC-specific runner labels (e.g., labels containing
arc, dind, or custom org labels mapped to ARC runner scale sets)
- Environment probe: at runtime, detect
ACTIONS_RUNNER_CONTAINER_HOOKS or KUBERNETES_SERVICE_HOST env vars that indicate an ARC environment
2. Emit AWF config with topology selector
When ARC/DinD is detected, gh-aw emits a single key in the AWF config:
{
"runner": {
"topology": "arc-dind"
}
}
AWF resolves all internal details from this signal:
network.isolation → true (ARC k8s lacks NET_ADMIN)
container.sysrootImage → ghcr.io/github/gh-aw-firewall/build-tools:{version} (system base)
dind.preStageDirs → true
- Split-filesystem path-prefix probes activate automatically
- Tool cache validation (warn if
RUNNER_TOOL_CACHE is under /opt, which is invisible to the DinD daemon)
An explicit value in any downstream AWF key always overrides the topology default.
3. Ensure rootless agent job execution
The core constraint: No code in the workflow action can run as root on ARC. This means:
a) No sudo or privileged commands in generated steps
Audit all generated steps in the agent job to ensure none use:
sudo apt-get install ...
sudo ... anything
- Commands that implicitly require root (writing to
/usr, /etc, /opt)
b) Tool cache redirection
On ARC, the standard RUNNER_TOOL_CACHE=/opt/hostedtoolcache is useless — /opt on the runner is invisible to the DinD daemon's filesystem. gh-aw should emit an early step that redirects the tool cache to a shared volume:
- name: Redirect tool cache for ARC/DinD
if: runner.topology == 'arc-dind' # pseudocode for the detection mechanism
run: echo "RUNNER_TOOL_CACHE=/tmp/gh-aw/tool-cache" >> "$GITHUB_ENV"
This ensures that subsequent setup-* actions (setup-go, setup-node, setup-java, setup-dotnet) install to a path on the shared emptyDir volume between the runner and DinD containers.
c) setup-* actions still run on-demand
Language SDKs are NOT baked into images. They are installed on-demand by setup-* actions, which download pre-built tarballs and extract them — no root needed. The flow:
setup-go runs on the runner → extracts to /tmp/gh-aw/tool-cache/go/1.22.x/x64/
- DinD daemon can see
/tmp/gh-aw/tool-cache (shared emptyDir)
- AWF mounts it into the agent at
/host/tmp/gh-aw/tool-cache:ro
- Agent wrapper discovers SDK
bin/ dirs via find and adds them to PATH
d) Validate no root-requiring steps leak through
Add a lint or validation pass that flags any generated step using sudo or writing to root-owned paths when runner.topology: arc-dind is active. This prevents regressions as new workflow types are added.
Privilege hierarchy
| When |
Root? |
What happens |
Who owns it |
| Image build (release pipeline) |
✅ |
apt-get install gcc make libssl-dev ... |
AWF build-tools image |
| ARC pod startup (K8s) |
✅ |
DinD daemon starts, shared volumes created |
K8s / ARC operator |
| Workflow steps (runner container) |
❌ |
setup-* actions, checkout, config generation |
gh-aw generated workflow |
| Agent container (AWF) |
❌ |
Build, test, agent execution |
AWF runtime |
gh-aw's responsibility spans rows 3 and 4 — it generates the workflow steps and configures AWF. Neither layer has root.
End-to-end flow on ARC/DinD
┌─────────────────────────────────────────────────────────────────────┐
│ ARC Pod │
│ │
│ ┌──────────────────────┐ ┌──────────────────────────────────┐ │
│ │ Runner Container │ │ DinD Sidecar (dockerd) │ │
│ │ │ │ │ │
│ │ 1. gh-aw detects │ │ │ │
│ │ ARC/DinD topology │ │ │ │
│ │ │ │ │ │
│ │ 2. Redirect tool cache│ │ │ │
│ │ → /tmp/gh-aw/ │ │ │ │
│ │ tool-cache │ │ │ │
│ │ │ │ │ │
│ │ 3. setup-go, etc. │ │ │ │
│ │ install to shared │────▶│ (sees /tmp/gh-aw via emptyDir) │ │
│ │ tool cache │ │ │ │
│ │ │ │ 4. AWF starts compose stack: │ │
│ │ 4. AWF invoked with │────▶│ - sysroot-stage (cp -a) │ │
│ │ topology: arc-dind │ │ - squid proxy │ │
│ │ │ │ - agent container │ │
│ │ │ │ mounts: sysroot + cache │ │
│ │ │ │ │ │
│ │ 5. Agent runs build/ │◀───│ 5. Agent executes in chroot │ │
│ │ test, gets exit │ │ with gcc, go, node, etc. │ │
│ │ code │ │ │ │
│ └──────────────────────┘ └──────────────────────────────────┘ │
│ │
│ Shared emptyDir volumes: │
│ /home/runner/_work (workspace) │
│ /tmp/gh-aw (tool-cache, staging, config) │
│ Docker socket │
└─────────────────────────────────────────────────────────────────────┘
Acceptance criteria
Notes
- The
runner.topology key is the single stable contract between gh-aw and AWF for ARC deployments
- AWF handles all split-FS, sysroot, and network isolation details internally once it sees this key
- gh-aw should not need to know about sysroot images, path prefixes, or compose generation details
Summary
When an agentic workflow targets an ARC/DinD runner, gh-aw must:
runner.topology: arc-dindin the AWF config so the firewall activates all split-filesystem handling automatically (sysroot image, tool cache redirection, network isolation, path-prefix probes).sudo, noapt-get install, no privileged operations. On ARC, the runner container does not have root access, so anything requiring root must already be handled at image build time (by AWF'sbuild-toolssysroot image).Companion issue
build-toolssysroot image for build-test workflows gh-aw-firewall#5693 — AWF-side implementation: versionedbuild-toolssysroot image,runner.topologyconfig handling, compose generation changes./hoston split-filesystem runners.runner.topologyconfig selector design (subsumed into Fix missing log directory for copilot CLI execution #5693).What gh-aw needs to do
1. Detect ARC/DinD topology
When the workflow targets an ARC runner label (or the user explicitly opts in), gh-aw should know to emit the
arc-dindtopology config.Detection options (not mutually exclusive):
runner.topology: arc-dindin their workflow config /.github/gh-aw.ymlarc,dind, or custom org labels mapped to ARC runner scale sets)ACTIONS_RUNNER_CONTAINER_HOOKSorKUBERNETES_SERVICE_HOSTenv vars that indicate an ARC environment2. Emit AWF config with topology selector
When ARC/DinD is detected, gh-aw emits a single key in the AWF config:
{ "runner": { "topology": "arc-dind" } }AWF resolves all internal details from this signal:
network.isolation→true(ARC k8s lacks NET_ADMIN)container.sysrootImage→ghcr.io/github/gh-aw-firewall/build-tools:{version}(system base)dind.preStageDirs→trueRUNNER_TOOL_CACHEis under/opt, which is invisible to the DinD daemon)An explicit value in any downstream AWF key always overrides the topology default.
3. Ensure rootless agent job execution
The core constraint: No code in the workflow action can run as root on ARC. This means:
a) No
sudoor privileged commands in generated stepsAudit all generated steps in the agent job to ensure none use:
sudo apt-get install ...sudo ...anything/usr,/etc,/opt)b) Tool cache redirection
On ARC, the standard
RUNNER_TOOL_CACHE=/opt/hostedtoolcacheis useless —/opton the runner is invisible to the DinD daemon's filesystem. gh-aw should emit an early step that redirects the tool cache to a shared volume:This ensures that subsequent
setup-*actions (setup-go, setup-node, setup-java, setup-dotnet) install to a path on the shared emptyDir volume between the runner and DinD containers.c) setup-* actions still run on-demand
Language SDKs are NOT baked into images. They are installed on-demand by
setup-*actions, which download pre-built tarballs and extract them — no root needed. The flow:setup-goruns on the runner → extracts to/tmp/gh-aw/tool-cache/go/1.22.x/x64//tmp/gh-aw/tool-cache(shared emptyDir)/host/tmp/gh-aw/tool-cache:robin/dirs viafindand adds them toPATHd) Validate no root-requiring steps leak through
Add a lint or validation pass that flags any generated step using
sudoor writing to root-owned paths whenrunner.topology: arc-dindis active. This prevents regressions as new workflow types are added.Privilege hierarchy
apt-get install gcc make libssl-dev ...build-toolsimagesetup-*actions, checkout, config generationgh-aw's responsibility spans rows 3 and 4 — it generates the workflow steps and configures AWF. Neither layer has root.
End-to-end flow on ARC/DinD
Acceptance criteria
{ "runner": { "topology": "arc-dind" } }in the AWF config/tmp/gh-aw/tool-cacheearly in the workflow when on ARCsudoor requires rootsetup-*actions continue to work (install to redirected tool cache, no root needed)topology: arc-dindNotes
runner.topologykey is the single stable contract between gh-aw and AWF for ARC deployments