How the Agent Workflow Firewall (AWF) Integration Provides Egress Control in GitHub Actions
The Agent Workflow Firewall (AWF) integration provides egress control by wrapping AI engine commands in a sandboxed container that enforces network restrictions through command-line arguments specifying allowed and blocked domains.
The github/gh-aw repository implements the Agent Workflow Firewall (AWF) to isolate AI-driven workflows inside GitHub Actions. By centralizing egress policy generation in pkg/workflow/awf_helpers.go, the integration transforms declarative network permissions into concrete firewall rules that restrict outbound traffic at runtime.
Architectural Overview of AWF Egress Control
The AWF operates as a sandbox wrapper around every AI engine command executed by gh-aw. When a workflow lacks explicit sandbox configuration, the system automatically applies defaults that designate the AWF as the isolation mechanism.
Sandbox Defaults and Configuration
In pkg/workflow/sandbox.go, the applySandboxDefaults function ensures that workflows without custom sandbox settings receive an agent: awf configuration. This default assignment guarantees that every engine execution receives network isolation unless explicitly overridden.
The default AWF command itself is defined in pkg/constants/constants.go as sudo -E awf, though operators may override this via the AgentSandboxConfig.Command field.
Command Resolution and Prefix Generation
The GetAWFCommandPrefix function in pkg/workflow/awf_helpers.go resolves the final command prefix. It checks for custom sandbox commands; if none exist, it falls back to the constant AWFDefaultCommand. This prefix forms the base of the eventual firewall invocation.
Building the AWF Command for Network Restrictions
Egress control materializes through the BuildAWFArgs function, which translates workflow-level network permissions into AWF-specific command-line flags.
Domain Allowlisting and Blocklisting
The function constructs three critical argument categories:
--allow-domains: Populated from the workflow front-matternetwork.allowedfield. This comma-separated list explicitly permits outbound connections to specified hosts. An empty list results in zero egress, effectively creating an air-gapped environment.--block-domains: Derived fromNetworkPermissions.Blocked. These domains are explicitly denied regardless of other permissions. The validation layer rejects wildcard entries in strict mode to prevent overly broad blocking.--mount: Custom mount points from the sandbox configuration that expose necessary filesystem resources without compromising network isolation.
Additional Security Flags
Beyond domain controls, BuildAWFArgs appends several security-hardening parameters:
--enable-host-access: Conditionally added when MCP (Model Context Protocol) gateway containers are present, allowing the firewall to reach host services required for MCP functionality.--image-tag: Pins the AWF container image to a specific version defined inpkg/constants/constants.go, ensuring reproducible rule enforcement.--enable-api-proxy: Activates when API proxying is required for specific engine interactions.- SSL/TLS options: Additional flags handle certificate bumping and TLS inspection when enterprise TLS interception is configured.
Code Example: Generating an AWF-Wrapped Engine Command
The following Go code demonstrates how gh-aw assembles the final AWF command using the helper functions in pkg/workflow/awf_helpers.go:
import (
"github.com/github/gh-aw/pkg/workflow"
"github.com/github/gh-aw/pkg/constants"
)
func example() string {
// Assume we already have a parsed WorkflowData (populated by the compiler)
wfData := &workflow.WorkflowData{ /* …filled by gh‑aw… */ }
cfg := workflow.AWFCommandConfig{
EngineName: "copilot",
EngineCommand: "copilot chat --model gpt-4o-mini",
LogFile: "/tmp/gh-aw/engine.log",
WorkflowData: wfData,
UsesTTY: false,
UsesAPIProxy: true,
AllowedDomains: "api.github.com,registry.ghcr.io",
PathSetup: "export PATH=$HOME/.local/bin:$PATH",
}
// Produces the full script that will be placed in a GitHub‑Actions step
return workflow.BuildAWFCommand(cfg)
}
This invocation produces a shell script similar to:
set -o pipefail
sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" \
--allow-domains api.github.com,registry.ghcr.io \
--log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs \
--enable-host-access --image-tag v0.18.0 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'copilot chat --model gpt-4o-mini' 2>&1 | tee -a /tmp/gh-aw/engine.log
Only the domains explicitly listed in --allow-domains are reachable from within the engine container.
Execution Flow and Runtime Enforcement
When GitHub Actions executes the generated step, the AWF container initializes with a restricted network namespace. The enforcement mechanism operates as follows:
- Default Deny: Without explicit
--allow-domains, the firewall implements a default-deny policy, preventing all outbound connections. - Selective Permits: Domains listed in
--allow-domainsare resolved and added to the container's egress whitelist. - Explicit Blocks: Entries in
--block-domainsoverride any other permissions, ensuring specific hosts remain unreachable. - Host Access Gateway: When
--enable-host-accessis present, the firewall creates a controlled bridge to host-local MCP services without exposing the broader network.
This architecture ensures that AI engine workflows operate with the principle of least privilege, contacting only the external resources explicitly authorized by the workflow definition.
Summary
- The Agent Workflow Firewall (AWF) integration in
github/gh-awprovides deterministic egress control by wrapping AI engine commands in a sandboxed container. - Network policies are defined in workflow front-matter and compiled into AWF command-line arguments by functions in
pkg/workflow/awf_helpers.go. - Domain allowlisting (
--allow-domains) and blocklisting (--block-domains) provide granular control, with an empty allowlist resulting in complete network isolation. - Additional flags like
--enable-host-accessand--image-tagharden the execution environment while maintaining necessary functionality for MCP gateways and version consistency.
Frequently Asked Questions
What happens if no allowed domains are specified in the AWF configuration?
If the workflow front-matter does not specify any domains in network.allowed, the BuildAWFArgs function in pkg/workflow/awf_helpers.go passes an empty string to the --allow-domains flag. AWF interprets this as a default-deny policy, blocking all outbound network connections and creating an air-gapped environment for the AI engine.
How does AWF handle wildcard domains in blocklists?
The AWF integration explicitly forbids wildcard entries in the blocklist when strict-mode validation is enabled. The validation logic in the workflow compiler rejects patterns that could match overly broad domain sets, ensuring that --block-domains contains only specific, explicit hostnames that the AWF container will definitively reject.
Can I use a custom AWF binary instead of the default sudo -E awf?
Yes. While pkg/constants/constants.go defines AWFDefaultCommand as sudo -E awf, you can override this by specifying a custom command in the AgentSandboxConfig.Command field. The GetAWFCommandPrefix function in pkg/workflow/awf_helpers.go checks for this override and uses the custom path when present, falling back to the default only when no alternative is specified.
What is the purpose of the --enable-host-access flag in AWF?
The --enable-host-access flag is conditionally added by BuildAWFArgs when the workflow configuration includes MCP (Model Context Protocol) gateway containers. This flag creates a controlled network bridge that allows the AWF container to reach host-local services required for MCP functionality without exposing the broader external network, maintaining the principle of least privilege while enabling necessary integrations.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →