Limits

What Egress CI covers in a CI job and what it does not: root, steps before the action, Docker, HTTPS content, IPv6 and runner types.

On this page

Egress CI makes a job's outbound traffic visible and limits it to a list. It raises the cost of exfiltration by a compromised dependency, build script or action, and shows the attempt in the summary. It is not a sandbox: read what it does not cover before relying on it.

Coverage

TrafficCoveredHow
Tools that honour HTTPS_PROXY/HTTP_PROXYYesThe proxy: name and SNI checked
Tools that ignore the proxy variables, the runner itselfWith the lockThe transparent proxy: named by SNI or Host, name checked against the address
DNSWith the lockThe forwarder: logged, NXDOMAIN for names outside the policy in enforce
UDP, ICMP, IPv6With the lockOnly to addresses of allowed names (IPv4); the rest refused in enforce
The job's containers and docker build stepsWith Docker trackingSame as the job, per container
Image pulls through the job's socketWith Docker trackingRegistry checked
Steps before the actionNo

Known limits

  • Root can undo it. The job has the root the action uses: on GitHub-hosted runners every job has sudo, and on self-hosted runners the docker group is equivalent to root. A step that sets out to remove the lock can.
  • Only the steps after the action. Put it right after actions/checkout.
  • HTTPS content is not seen unless the host is under inspect, and inspection needs the tool to trust the run's CA. A secret sent over HTTPS to an allowed host is not detected.
  • Allowed hosts can carry data. A package registry or an object store you allow can receive an upload. Allow exact names, and keep write credentials out of jobs that do not need them.
  • Docker without DOCKER_HOST. A tool that opens /var/run/docker.sock itself is not attributed; with lock-all its containers' traffic outside the policy is still refused. Containers with --network host are outside the lock unless they run as the job's user.
  • The Docker daemon's own connections (pushes, layer downloads of a pull once the registry is allowed) are not proxied.
  • Docker's address pool (172.16.0.0/12) is left to the runner's own rules, and is in NO_PROXY.
  • IPv4 only for naming TCP connections; the job's IPv6 is refused in enforce.
  • Without root (a self-hosted runner with neither passwordless sudo nor Docker), only HTTPS_PROXY is covered. lock: "on" makes that an error.
  • Linux only, x64 and arm64. No macOS or Windows runners.
  • One job per runner user at a time on a host.

What it costs

The action starts the sidecar before the job's own steps and stops it in the post step; the log line OpenWAF Egress CI: proxy …, started in … ms and the summary give the times of each run. Connections through the proxies add one local hop. With Docker tracking, docker build runs in the job builder instead of the daemon's builder; on runners that start clean for each job its cache starts empty.

Security model

  • The sidecar runs as root and the job's steps as the runner user; the lock matches that user's sockets and the job's Docker networks only.
  • The binaries are in the action's dist/, built reproducibly from its source (CI checks it); their SHA-256 are in dist/SHA256SUMS and checked before every run.
  • Tokens are never put on a command line or in a log: the GitHub token reaches curl on standard input, the OIDC tokens stay in the post step's memory.
  • The run's CA key never leaves the sidecar's memory.
  • The summary and report.json hold names, ports, counts and detection ids, never bodies or the secrets found.

Report vulnerabilities to security@openwaf.dev, as described in the repository's SECURITY.md (external link).

Last updated:

↑↓ to navigate↵ to openesc to close