egress (egress-control)
Purpose: default-deny egress for the namespace, with a squid forward-proxy enforcing an explicit FQDN allowlist. True hostname control on a CNI that can't do FQDN policy.
Deploys (manifests/components/egress/): NetworkPolicies + an egress-proxy (squid)
Deployment/Service/ConfigMap.
The model:
- default-deny-egress (all pods). CI pods (con-ci: "true") may reach only the proxy
(allow-ci-to-proxy, :3128) + DNS. The proxy may reach the public internet (:80/:443, not
private/cluster ranges). So a proxy misconfig can't open the network — the NetworkPolicy is the
L3/L4 backstop.
- squid allowlist (proxy-config.yaml, no wildcards): codeberg.org, apps.nextcloud.com,
api.anthropic.com, ghcr.io + its blob host pkg-containers.githubusercontent.com +
GitHub-releases hosts (Trivy DB), docker.io. semgrep.dev
intentionally absent (rules vendored). The con-ci-oci overlay has its own, narrower list —
since 2026-07-06 incl. pypi.org/files.pythonhosted.org (handbook docs pipeline), since
2026-07-07 also registry.npmjs.org (npm install inside kaniko image builds) and
index.docker.io (kaniko/go-containerregistry normalizes docker.io to index.docker.io,
not registry-1.docker.io like podman) + release-assets.githubusercontent.com (GitHub
release downloads 302 there now, not to objects.githubusercontent.com — npm postinstall
binaries like sharp/libvips), since 2026-07-13 also api.cloudflare.com (wrangler deploys
of docs sites to Cloudflare Workers from container jobs; the telemetry host
sparrow.cloudflare.com is deliberately absent — workflows set
WRANGLER_SEND_METRICS=false).
Calico gotchas (hard-won — keep):
- DNS is on port 8053, not 53: kube-dns Service maps 53→8053 and Calico evaluates egress
post-DNAT, so allow-dns must permit 8053 to the kube-dns pods. A port-53 rule silently
fails.
- ipBlock does not match in-cluster destinations on Calico; an empty namespaceSelector {}
matches nothing here. Use an explicit kubernetes.io/metadata.name: kube-system + k8s-app:
kube-dns selector.
Operate / gotchas:
- squid image docker.io/ubuntu/squid runs non-root (uid 13) + readonly rootfs; the ICMP pinger
FATAL log line is benign (CAP_NET_RAW dropped) — it still listens on 3128.
- Debug egress from the proxy pod: kubectl -n con-ci exec deploy/egress-proxy -- bash -c
'getent hosts codeberg.org; ...'. squid TCP_TUNNEL/200 = good; 503/HIER_NONE = upstream
unreachable; TCP_DENIED = allowlist.
Future: Calico supports native domain-based egress (GlobalNetworkSet) — could replace squid.