A GitHub Action for hardening CI/CD pipelines with bounded egress filtering and runner lockdown.
Add Fence as the first step in a supported GitHub-hosted Linux job:
- uses: openai/fence@<commit-sha> # pin@vX.Y.ZThis starts Fence in block mode with an empty user allowlist on a GitHub-hosted x64 job using ubuntu-24.04 or ubuntu-latest. Replace <commit-sha> with the full action_commit value and vX.Y.Z with the tag from the same release; release notes provide the ready-to-copy line with a Dependabot-friendly # pin@vX.Y.Z comment. main is source-only and does not contain a runnable production bundle. Put Fence before checkout and any other steps you want it to constrain.
Read more: Getting started with Fence
Start with a complete job that activates Fence before checkout:
jobs:
test:
runs-on: ubuntu-24.04
steps:
- uses: openai/fence@<fence-commit-sha>
- uses: actions/checkout@<checkout-commit-sha>
- run: script/testRun in audit mode first to see what would need review before enabling blocking:
- uses: openai/fence@<commit-sha>
with:
mode: auditThe audit summary suggests allowlist entries for observed hostnames and direct IPv4 or IPv6 destinations.
Allow GitHub artifact uploads, including GitHub Pages deployments and GitHub Actions caches, while keeping other network restrictions in place:
- uses: openai/fence@<commit-sha>
with:
allow_github_artifacts: trueThis setting is off by default. It permits up to four exact GitHub-shaped results-storage accounts on HTTPS and makes artifact storage available to the job. Enable it only when the workflow needs artifacts: later steps may also use the permitted accounts to upload data. Fence does not allow all Azure Blob Storage.
Allow one or more HTTPS hostnames:
- uses: openai/fence@<commit-sha>
with:
allowlist: |
api.example.com
artifacts.example.comAllow custom TCP, UDP, or CIDR destinations:
- uses: openai/fence@<commit-sha>
with:
allowlist: |
registry.example.com:8443
udp://dns.example.com:53
cidr 192.0.2.0/24 udp 123
cidr 2001:db8::/64 tcp 443Keep Docker/container access available while still applying network restrictions and disabling passwordless sudo:
- uses: openai/fence@<commit-sha>
with:
container_policy: unsafe_preserve
allowlist: |
*.docker.ioThe wildcard can authorize exact-depth names such as auth.docker.io, but image pulls may require additional registry, layer, CDN, or storage destinations. Normal Docker container startup and cleanup remain supported in this degraded mode without weakening checks for genuinely reachable root-control changes.
Remove the broad GitHub web, API, release-asset, and watchdog destinations and new platform-origin *.githubapp.com authorizations while keeping the core Actions reporting path:
- uses: openai/fence@<commit-sha>
with:
disable_broad_github_domains: trueUse raw JSON only when you need exact agent-schema control:
- uses: openai/fence@<commit-sha>
with:
config: >-
{"schema_version":1,"mode":"block","invocation_id":"my-job-1","allowlist":[]}Read more: Fence configuration examples
The native allowlist input accepts one destination per line:
example.com
example.com:8443
tcp://example.com:443
udp://dns.example.com:53
hostname example.com tcp 443
*.example.com
*.*.example.com
ip 192.0.2.10 tcp 443
ip 2001:db8::10 udp 53
cidr 192.0.2.0/24 udp 123
cidr 2001:db8::/64 tcp 443
Hostname shortcuts use TCP port 443; use the explicit ip or cidr form for address ranges and IPv6. CIDR entries must identify a network address without host bits. Fence accepts up to 64 unique, normalized allowlist entries; duplicate entries, blank lines, and comments beginning with # do not count more than once. Wildcards match exactly one or two leading labels and share a bounded lifetime authorization budget.
Read more: Allowlist syntax and DNS behavior
Fence validates that it is running through the supported Action path, turns your inputs into a local policy, and applies the controls for the selected mode. Local UDP and TCP DNS requests share the same tightly scoped root-only UDP resolver path. A resident agent keeps checking those controls throughout the job, and the protected post-job hook reports what happened and fails the job if it finds critical drift.
flowchart LR
start["Fence runs first"] --> verify["Validate runner and trusted bundle"]
verify --> policy["Build built-in platform and user policy"]
policy --> controls["Apply mode-specific controls"]
controls --> monitor["Monitor controls during the job"]
monitor --> report["Report evidence and critical drift"]
Read more: Fence architecture and lifecycle
At the end of each job, Fence adds a readable network activity table to the GitHub job summary and the Fence post-job log. The log also includes one bounded, machine-readable FENCE_REPORT_JSON= record containing sanitized network decisions, control results, warnings, and suggested audit-mode allowlist entries.
Blocked requests in block mode are normal: Fence records them without marking a healthy job as a warning. Warnings highlight failed caller attribution, exhausted account limits, weakened controls, or missing evidence. In audit mode, Fence reports what block mode would deny; it does not block the request.
Find the job and fetch its report through the GitHub API:
gh api repos/OWNER/REPO/actions/runs/RUN_ID/jobs \
--jq '.jobs[] | {id, name}'
gh api repos/OWNER/REPO/actions/jobs/JOB_ID/logs \
| sed -n 's/^.*FENCE_REPORT_JSON=//p' \
| jq .Job logs can be read by anyone with access to the workflow run. Reports include only bounded destination and approved actor information; they never include credentials, environment variables, request payloads, or full process paths.
Read more: Network reports and lifecycle
| Mode | What It Does | When To Use It |
|---|---|---|
block |
Blocks network traffic unless it matches the bounded built-in GitHub Actions and hosted-runner platform policy or your allowlist; turns off passwordless sudo and Docker. |
Default for locking down a job. |
block with allow_github_artifacts: true |
Keeps network, sudo, and Docker restrictions while allowing a small, bounded GitHub artifact-storage channel. | When a job needs GitHub artifacts, Pages, or caches and you accept that artifact uploads can send data out of the runner. |
block with container_policy: unsafe_preserve |
Blocks network traffic and turns off passwordless sudo, but leaves Docker/container access available. | When a workflow needs Docker and you accept the weaker security claim. |
audit |
Does not block traffic. Records what would need review before moving to block. |
When tuning a workflow. |
Read more: Fence assurance modes
Fence adds a layer of protection to GitHub Actions jobs by limiting where later steps can send network traffic. Its default block mode also turns off passwordless sudo and Docker to make those protections harder to undo. Fence is not a complete sandbox, and allowed destinations remain reachable.
- Supported runners: GitHub-hosted x64 jobs using
ubuntu-24.04orubuntu-latest. Useubuntu-24.04for the most predictable runner image;ubuntu-latestis also regularly tested but can change over time. Fence refuses to start if the runner does not pass its security checks. - Built-in GitHub connections: Fence keeps a limited set of GitHub service and reporting connections open. Later workflow steps can still reach those destinations and anything in your
allowlist. - GitHub results storage: Fence trusts five exact HTTPS destinations:
productionresultssa19.blob.core.windows.net,productionresultssa13.blob.core.windows.net,productionresultssa9.blob.core.windows.net,productionresultssa15.blob.core.windows.net, andproductionresultssa17.blob.core.windows.net. The GitHub runner may separately authorize up to four additional exact accounts. General Azure Blob Storage is not allowed. - Azure platform connections: Root-owned host processes can reach Azure WireServer (
168.63.129.16, TCP ports80and32526). Azure IMDS (169.254.169.254, TCP port80) remains reachable from host and forwarded traffic, including later workflow steps. These platform exceptions are separate from yourallowlist. - Tighter GitHub access: Set
disable_broad_github_domains: truewhen your workflow does not need optional GitHub destinations. - GitHub artifacts: Set
allow_github_artifacts: trueonly when the job needs GitHub artifact uploads, GitHub Pages, or caches. The option is off by default, shares the same four-account limit, and permits an artifact-storage egress channel that later workflow steps can also use. - Audit mode: Reports network activity without blocking it.
- Docker access:
container_policy: unsafe_preservekeeps Docker available but provides weaker protection. - Release pinning: Use the full, immutable
action_commitSHA from a published release.
Read more: Security boundaries and operational guidance
- Getting started
- Configuration examples
- Allowlist syntax
- Architecture and lifecycle
- Security boundaries
- Release provenance
- Troubleshooting
- Local development
- CLI reference
- Fence v0 security contract
- Threat model
- Security policy
- Security review
- Implementation history
- Repository settings
- Hermetic Builds
Fence is released under the MIT License.
