Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 78 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -424,10 +424,87 @@ jobs:
echo "::warning::SBOM attestation for agent-act failed after 3 attempts (Rekor may be unavailable)"
exit 1

# Build build-tools sysroot image for ARC/DinD deployments
# Provides system-level build infrastructure (gcc, make, dev libraries) for agent containers
build-build-tools:
name: Build Build-Tools Image
runs-on: ubuntu-latest
needs: bump-version
outputs:
digest: ${{ steps.build_build_tools.outputs.digest }}
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4
with:
ref: ${{ needs.bump-version.outputs.version }}

- name: Log in to GitHub Container Registry
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3

- name: Set up QEMU
uses: docker/setup-qemu-action@a3c050c5b9001e95079e262a6db77c5e7c7c3467 # v3
with:
platforms: linux/arm64

- name: Install cosign
uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 # v3.5.0

- name: Build and push Build-Tools image
id: build_build_tools
uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5
with:
context: ./containers/build-tools
push: true
platforms: linux/amd64,linux/arm64
tags: |
ghcr.io/${{ github.repository }}/build-tools:${{ needs.bump-version.outputs.version_number }}
ghcr.io/${{ github.repository }}/build-tools:latest
no-cache: true

- name: Sign Build-Tools image with cosign
run: |
cosign sign --yes \
ghcr.io/${{ github.repository }}/build-tools@${{ steps.build_build_tools.outputs.digest }}

- name: Generate SBOM for Build-Tools image
uses: anchore/sbom-action@28d71544de8eaf1b958d335707167c5f783590ad # v0.22.2
with:
image: ghcr.io/${{ github.repository }}/build-tools@${{ steps.build_build_tools.outputs.digest }}
format: spdx-json
output-file: build-tools-sbom.spdx.json

- name: Attest SBOM for Build-Tools image
continue-on-error: true
run: |
for attempt in 1 2 3; do
echo "Attempt $attempt of 3..."
if cosign attest --yes \
--predicate build-tools-sbom.spdx.json \
--type spdxjson \
ghcr.io/${{ github.repository }}/build-tools@${{ steps.build_build_tools.outputs.digest }}; then
echo "SBOM attestation succeeded on attempt $attempt"
exit 0
fi
if [ "$attempt" -lt 3 ]; then
sleep_time=$((30 * attempt))
echo "Attempt $attempt failed, retrying in ${sleep_time}s..."
sleep "$sleep_time"
fi
done
echo "::warning::SBOM attestation for build-tools failed after 3 attempts (Rekor may be unavailable)"
exit 1

release:
name: Create Release
runs-on: ubuntu-latest
needs: [bump-version, build-squid, build-agent, build-api-proxy, build-cli-proxy, build-agent-act]
needs: [bump-version, build-squid, build-agent, build-api-proxy, build-cli-proxy, build-agent-act, build-build-tools]
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4
Expand Down
96 changes: 96 additions & 0 deletions containers/build-tools/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Build-tools sysroot image for ARC/DinD deployments.
#
# Provides system-level build infrastructure (compilers, linkers, dev libraries)
# that requires root to install. On ARC/DinD, the agent container cannot run as
# root, so these packages must be baked in at image build time.
#
# Language SDKs (Go, Node, Java, .NET, Rust, Python) are NOT included here —
# they are installed on-demand by setup-* actions into a shared tool cache volume.
#
# Usage in docker-compose (generated by AWF when runner.topology = arc-dind):
#
# sysroot-stage:
# image: ghcr.io/github/gh-aw-firewall/build-tools:<version>
# volumes:
# - sysroot:/sysroot
# entrypoint: ["/bin/sh", "-c"]
# command: ["cp -a /usr /lib /bin /sbin /etc /sysroot/ && [ -d /lib64 ] && cp -a /lib64 /sysroot/ || true"]
#
# agent:
# depends_on:
# sysroot-stage: { condition: service_completed_successfully }
# volumes:
# - sysroot:/host:ro

FROM ubuntu:22.04

LABEL org.opencontainers.image.source="https://github.com/github/gh-aw-firewall"
LABEL org.opencontainers.image.description="Build-tools sysroot for ARC/DinD agent containers"

# Reuse Azure mirror optimization from the agent Dockerfile
RUN if getent hosts azure.archive.ubuntu.com >/dev/null 2>&1; then \
echo "Using Azure apt mirror (DNS resolved successfully)"; \
if [ -f /etc/apt/sources.list ]; then \
sed -i 's|http://archive.ubuntu.com|http://azure.archive.ubuntu.com|g' /etc/apt/sources.list; \
sed -i 's|http://security.ubuntu.com|http://azure.archive.ubuntu.com|g' /etc/apt/sources.list; \
fi; \
else \
echo "Azure apt mirror not reachable, using default archive.ubuntu.com"; \
fi

# Install build essentials and system libraries.
# This mirrors the packages available on GitHub-hosted runner VMs
# (see actions/runner-images toolset-2204.json) minus language runtimes.
RUN set -eux; \
force_archive_mirror() { \
echo "Falling back to archive.ubuntu.com mirror..." >&2; \
if [ -f /etc/apt/sources.list ]; then \
sed -i 's|http://azure.archive.ubuntu.com|http://archive.ubuntu.com|g' /etc/apt/sources.list; \
sed -i 's|http://security.ubuntu.com|http://archive.ubuntu.com|g' /etc/apt/sources.list 2>/dev/null || true; \
fi; \
rm -rf /var/lib/apt/lists/* && apt-get update; \
}; \
apt_update_retry() { \
local i; for i in 1 2 3; do \
rm -rf /var/lib/apt/lists/*; \
if apt-get update > /tmp/apt-update.log 2>&1; then \
cat /tmp/apt-update.log; \
if ! grep -q "Failed to fetch" /tmp/apt-update.log; then return 0; fi; \
else \
cat /tmp/apt-update.log; \
fi; \
echo "apt-get update attempt $i/3 failed, retrying in $((i*10))s..." >&2; sleep $((i*10)); \
done; \
echo "All apt-get update retries failed, falling back to archive.ubuntu.com..." >&2; \
force_archive_mirror; \
}; \
apt_install_retry() { \
apt-get install -y --no-install-recommends "$@" && return 0; \
echo "apt-get install failed, forcing archive.ubuntu.com and retrying..." >&2; \
force_archive_mirror; \
apt-get install -y --no-install-recommends "$@"; \
}; \
apt_update_retry && \
apt_install_retry \
# Build toolchain
build-essential gcc g++ make cmake autoconf automake libtool \
pkg-config binutils bison flex m4 \
# Development libraries
libssl-dev libc6-dev libicu-dev libsqlite3-dev zlib1g-dev \
libcurl4-openssl-dev libffi-dev libreadline-dev libyaml-dev \
libxml2-dev libxslt1-dev libbz2-dev liblzma-dev \
libncurses5-dev libgdbm-dev libgdbm-compat-dev \
# Runtime libraries commonly needed by builds
libgdiplus libev-dev \
# System utilities
bash coreutils findutils grep sed gawk \
tar gzip bzip2 xz-utils unzip zip \
# Networking and certs
ca-certificates curl wget git jq \
dnsutils net-tools netcat-openbsd \
# Agent container dependencies (must match containers/agent/Dockerfile)
libcap2-bin gosu gnupg gh \
&& \
# Upgrade all packages for security patches
apt-get upgrade -y && \
rm -rf /var/lib/apt/lists/* /tmp/*
87 changes: 85 additions & 2 deletions docs/arc-dind.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,88 @@

AWF supports ARC runners where the runner filesystem and Docker daemon filesystem are split (DinD sidecar patterns).

## What AWF now handles automatically
## Runner topology selector

The simplest way to configure AWF for ARC/DinD is through the `runner.topology` config key:

```json
{
"runner": {
"topology": "arc-dind"
}
}
```

When `runner.topology` is set to `"arc-dind"`, AWF enables ARC/DinD-specific sysroot staging behavior:

| Behavior | Default | Override |
|----------|---------|----------|
| Sysroot image for `/host` base | `build-tools:<tag>` | `runner.sysrootImage` |
| Tool cache warning if under `/opt` | Emitted | Set `RUNNER_TOOL_CACHE` to shared path |

Other ARC/DinD settings (for example `network.isolation` and `dind.preStageDirs`) are configured explicitly through their own fields.

## Build-tools sysroot image

On ARC/DinD, the standard system mounts (`/usr:/host/usr:ro`, etc.) resolve to the runner container's filesystem, which is invisible to the Docker daemon's split filesystem. The `build-tools` sysroot image solves this by providing a pre-built Ubuntu 22.04 image containing system-level build infrastructure:

- **Compilers & linkers**: gcc, g++, make, cmake, autoconf, binutils
- **Dev libraries**: libssl-dev, libc6-dev, libicu-dev, zlib1g-dev
- **System utilities**: bash, coreutils, git, curl, wget, jq
- **Agent dependencies**: libcap2-bin (capsh), gosu, gnupg, gh

### How it works

1. AWF emits a `sysroot-stage` init service in the compose file
2. The init container copies the build-tools image FS into a named `sysroot` volume
3. The agent mounts the `sysroot` volume read-only at `/host`
4. `entrypoint.sh` finds `/host/bin/sh` and `capsh`, chroots successfully

```yaml
# Generated docker-compose.yml (simplified)
services:
sysroot-stage:
image: ghcr.io/github/gh-aw-firewall/build-tools:0.28.0
volumes: ["sysroot:/sysroot"]
entrypoint: ["/bin/sh", "-c"]
command: ["cp -a /usr /lib /bin /sbin /etc /sysroot/ ..."]

agent:
depends_on:
sysroot-stage: { condition: service_completed_successfully }
volumes:
- sysroot:/host:ro
- /tmp/gh-aw/tool-cache:/host/tmp/gh-aw/tool-cache:ro

volumes:
sysroot: {}
```

### Custom sysroot image

Override the default build-tools image:

```json
{
"runner": {
"topology": "arc-dind",
"sysrootImage": "ghcr.io/my-org/custom-sysroot:latest"
}
}
```

## Tool cache for language SDKs

Language SDKs (Go, Node, Java, .NET) are NOT baked into the sysroot image. They are installed on-demand by `setup-*` actions into a shared tool cache volume.

**Important**: On ARC, `RUNNER_TOOL_CACHE` must point to a shared path visible to both the runner container and the DinD daemon (e.g., `/tmp/gh-aw/tool-cache`). The default `/opt/hostedtoolcache` is invisible to the DinD daemon.

```yaml
# Early in workflow, before setup-* actions:
- run: echo "RUNNER_TOOL_CACHE=/tmp/gh-aw/tool-cache" >> "$GITHUB_ENV"
```

## What AWF handles automatically

- Split-filesystem probing for `--docker-host-path-prefix`
- Chroot staging for:
Expand All @@ -12,7 +93,9 @@ AWF supports ARC runners where the runner filesystem and Docker daemon filesyste
- generated chroot `/etc/hosts`
- DinD `DOCKER_HOST` propagation into agent/MCP environments when DinD is detected

## ARC/DinD stdin config surface
## Explicit ARC/DinD config surface

For fine-grained control (or when not using `runner.topology`):

```json
{
Expand Down
19 changes: 19 additions & 0 deletions docs/awf-config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -777,6 +777,25 @@
"description": "The GitHub deployment type. 'github.com' = GitHub.com (default), 'ghes' = GitHub Enterprise Server (on-premises), 'ghec' = GitHub Enterprise Cloud (*.ghe.com tenants), 'ghec-self-hosted' = GHEC with self-hosted runners."
}
}
},
"runner": {
"type": "object",
"description": "Runner topology configuration. Declares the runner deployment model so AWF can activate the correct split-filesystem, sysroot, and network isolation behavior.",
"additionalProperties": false,
"properties": {
"topology": {
"type": "string",
"enum": [
"standard",
"arc-dind"
],
"description": "Runner deployment topology. 'standard' (default) = GitHub-hosted VM or self-hosted runner with local Docker. 'arc-dind' = ARC (Actions Runner Controller) with Docker-in-Docker sidecar, where the runner and Docker daemon have separate filesystems. When set to 'arc-dind', AWF applies overridable defaults: network.isolation=true, dind.preStageDirs=true, sysroot image activation, and tool cache validation."
},
"sysrootImage": {
"type": "string",
"description": "Container image providing system-level build tools (gcc, make, libraries) for the agent's chroot base. Used as an init container that copies its filesystem into a named volume mounted at /host. Only used when runner.topology is 'arc-dind'. Defaults to 'ghcr.io/github/gh-aw-firewall/build-tools:<imageTag>'."
}
}
}
},
"$defs": {
Expand Down
Loading
Loading