Engineering Note

Zero inbound: a firewall that blocks the internet

Our production firewall has no rule for HTTP or HTTPS. There is no load balancer with a public listener and no ingress controller on an open port. Customer storefronts are still served all day.

Engineering note · 30 October 2026

The usual shape, and ours

Most production environments start from "the internet can reach us on 443" and then add layers in front: a WAF, a load balancer, rate limits. Those are sensible, but the starting point is still a host somewhere that accepts connections from anyone. Every packet a stranger sends is parsed by your network stack before any of those layers get a say.

We started from the other end. The cloud firewall in front of every node is default-deny inbound, applied at the provider's network edge, so unwanted packets are dropped before they reach a server at all. No web ports, no API ports, no database ports. The only exception is a single administrative path, locked to an allowlisted address.

How traffic gets in when nothing is listening

The trick is to reverse the direction of the connection. A small connector, Cloudflare Tunnel (cloudflared), runs inside the cluster and dials out to Cloudflare's edge. Visitors connect to Cloudflare, which terminates their TCP and TLS sessions and passes the HTTP requests back down the tunnel that our side opened.

Everything else pulls

Tunnels solve customer traffic. The harder part of getting to zero inbound is making sure nothing operational needs a way in either. A lot of platforms quietly keep a port open for deploys, CI or webhooks. Ours are all pull-based:

It goes one step further on the worker nodes, which run the actual workloads: they are IPv6-only and have no public IPv4 address at all. Their outbound IPv4 goes through a NAT gateway, and their inbound is covered by the same default-deny firewall.

The firewall itself is code. It lives in OpenTofu alongside the servers it protects, so a change to it is a reviewed diff with a plan, not a click in a console that nobody remembers making.

Why it matters on CVE day

A large class of serious vulnerabilities shares one precondition: an attacker has to get packets to the vulnerable code. Bugs in the kernel's TCP/IP stack. Pre-authentication flaws in an SSH daemon or VPN appliance. A database, cache or dashboard that ships with a listening port and a weak default. Each year brings a handful of these with critical scores, and each one starts the same scramble: are we exposed, and how fast can we patch?

With zero inbound, the first question usually answers itself. The packets that would trigger the bug are dropped at the provider edge, so they never reach a node. The fix still goes in, but on the normal schedule: OS updates are applied automatically and nodes reboot one at a time in an overnight window. Nobody needs to patch at 2am.

It also fails closed. If a new workload opens a debug port, a service is misconfigured as a NodePort, or a package starts a daemon nobody asked for, it isn't exposed to the internet by accident. Being reachable is something you have to add on purpose, and that is a change someone reviews.

What it doesn't do

Zero inbound removes a whole category of risk, not all of it. It's worth being plain about the limits:

Getting there

You don't have to rebuild from scratch to get most of the benefit:

The last step is the satisfying one. Once nothing depends on inbound connections, closing them all is a one-line change.

Next: how we ship without downtime →