131% more reported CVEs per day in 2026 than last year.
← Back to Glossary

Egress Policy

An egress policy defines which outbound network connections a system may make, by destination, protocol and port, and what happens to everything else.

Last updated: November 11, 2026

An egress policy defines which outbound network connections a system may make, by destination (a domain or an IP range), protocol and port, and what happens to everything else. Egress filtering is the enforcement of such a policy by a firewall, a proxy or a network policy. An egress firewall is the device or feature that does the enforcing.

Default allow and default deny

A default-allow policy blocks a list of known bad destinations and lets everything else through. A default-deny policy allows a list of known good destinations and blocks the rest. Kubernetes works the same way: a pod can reach anything until a network policy selects it for egress, and from then on only the connections that policy allows (Kubernetes network policies).

Only default deny stops traffic to a destination nobody anticipated, and an attacker's server is exactly that. The cost is operational: an allowlist has to be built and kept current.

A default-deny egress policy on Kubernetes

The Kubernetes documentation gives this policy, which selects every pod in a namespace and allows no egress (Kubernetes network policies):

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-egress
spec:
  podSelector: {}
  policyTypes:
    - Egress

It also blocks DNS, so workloads that resolve names need a second policy that allows egress to the cluster's DNS service. Each allowed destination is then added as another policy, by pod, namespace or IP range (ipBlock).

FQDN-based policies

A standard NetworkPolicy matches pods, namespaces and IP ranges, not domain names. Package registries sit behind content delivery networks whose addresses change, so allowlists for them are written by name, as FQDN-based policies that some network plugins add. Cilium's toFQDNs rules match an exact name with matchName or a wildcard with matchPattern, and learn the addresses from the DNS answers its DNS proxy sees (Cilium DNS-based policies). Calico Cloud and Calico Enterprise list domains in egress allow rules (Calico DNS policy).

Why CI needs one

A build job holds deploy keys, cloud credentials and tokens, and runs install scripts from the packages it downloads. A compromised dependency can send those secrets anywhere the CI runner can reach, which is how token exfiltration happens in CI. Most runners allow all outbound traffic by default.

Take an npm postinstall script that posts the job's environment variables to an unknown host. With a default-deny policy that allows only the npm registry and your artifact store, the request fails and the secrets stay in the job.

What a build sees when the policy blocks it

The connection fails, and the build reports an ordinary network error. Depending on where the policy is enforced, that is a refused or reset connection, a timeout, a failed DNS lookup or a TLS handshake error, often after the package manager has retried. To the developer it looks like a flaky registry, which is why a good egress policy records every blocked connection with its destination and the reason it was blocked.

What to look for

  • Typed destinations. Package registries named as sources, not only IP addresses, because registries sit behind content delivery networks whose addresses change.
  • Validated before the job starts, so a broken policy fails visibly instead of half applying.
  • Fails closed, so if enforcement is unavailable the job does not run unprotected.

For rollout in GitHub Actions, see GitHub Actions egress policy.

CRACI

CRACI enforces an egress policy at the runner, default deny or allow, with 24 built-in software-source presets and typed custom sources for npm, PyPI, apt, apk, the Go proxy, Nix, Cargo, OCI registries and Git over HTTPS and SSH. The policy is validated before the job starts and fails closed, and violations raise alerts. The same job records which packages came through the allowed connections. See runner security compared.

When CRACI's policy blocks a connection, the job sees an ordinary failure such as a 403 Forbidden, a connection reset, an EOF or a TLS handshake failure, and the job's network trace lists the connection under proxy violations with a reason, such as no_match when no preset, source or rule matched the destination. The trace streams in the job view while the job runs and is also available through the API (CRACI docs: debug network problems).

For egress control on Kubernetes workloads, servers or GitHub-hosted runners you keep, use a network policy, a cloud firewall or a runner agent: CRACI enforces its policy on its own runners only, and an audit mode before blocking is on its roadmap.