OpenWAF Egress CI is a GitHub Action, openwaf-dev/egress-action (external link), that puts the outbound traffic of a CI job behind an allowlist. Every connection and DNS query of the job and of its containers is named and logged. Anything outside .github/egress.yml is reported (audit) or refused (enforce), what leaves is checked for secrets with the same detectors as the OpenWAF Egress gateway, and the job summary lists every destination the job talked to.
It is the CI counterpart of the gateway: a compromised dependency, build script or action that tries to send your secrets or your code somewhere else finds the way closed, and you see the attempt in the summary.
The action runs as a sidecar inside the job, on the runner itself. It needs no OpenWAF account and sends nothing anywhere unless you turn on sending events to OpenWAF. It is an early-access beta under the Apache License 2.0, listed on the GitHub Marketplace (external link).
Before you start
- A Linux runner, x64 or arm64. GitHub-hosted Ubuntu runners work as they are. On a self-hosted runner the job's user needs passwordless
sudoor access to the Docker daemon, and the host needsnft(nftables) andip(iproute2). - Write access to the workflow and to
.github/in the repository.
1. Add the action
Add it as the first step after actions/checkout. The steps before it are not covered.
name: test
on: [push, pull_request]
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v5
- uses: openwaf-dev/egress-action@v0.3.2
- run: make testIn production, pin the release's full commit SHA instead of the tag, with the version as a comment so Dependabot can update it: uses: openwaf-dev/egress-action@5a956e8bdeff9c34c13f2277f9096fddc6e6e51a # v0.3.2.
2. Write the policy
version: 1
mode: audit # report only, refuse nothing
presets: [github-actions] # the names GitHub publishes for Actions runners
allow:
- proxy.golang.org # ports 80 and 443
- "*.npmjs.org" # any name below npmjs.orggithub-actions covers what any job needs: the runner's own service, checkout, the API, the cache, artifacts and logs storage, and raw.githubusercontent.com. Every key is described in the egress.yml reference.
3. Read the job summary
Push and open the run. The job summary starts with OpenWAF Egress CI: audit and a table of destinations:
12 destinations, 2 outside the policy (enforce would block them), 0 detections.
| Destination | Ports | Via | Conn. | Up | Down | Policy | Detections |
| proxy.golang.org | 443 | proxy | 3 | 4.1 KiB | 2.3 MiB | allowed | |
| sum.golang.org | 443 | proxy | 1 | 812 B | 6.2 KiB | not allowed | |Via says how the connection left: proxy through HTTPS_PROXY, direct caught by the lock (a tool that ignores the proxy variables), container from one of the job's containers, udp for non-TCP flows. Under Names outside the policy the summary gives a ready-made allow block with every name that enforce would refuse.
4. Switch to enforce
Copy those names into allow, run again until the summary shows nothing outside the policy, and set mode: enforce. From then on a name outside the policy gets 403 from the proxy, NXDOMAIN from DNS, or a reset, and the summary says which and why. From audit to enforce walks through it.
Next steps
docker build and image pulls under the same policy.Inspection and DLPwhich requests are scanned for secrets, and the TLS trade-off.Limitswhat the action covers and what it does not.Written from github.com/openwaf-dev/egress-action@v0.3.2 (external link).