Raw TCP and UDP
For traffic that is not HTTP — Postgres, SSH, a binary protocol — the proxy gates the connection once and then splices bytes.
net { allow_tcp ["db.internal:5432", "ssh.internal:22"] allow_udp ["ntp.internal:123"] deny_tcp ["*:25"]}The port is mandatory
Section titled “The port is mandatory”allow_tcp ["db.internal"] # ← errorallow_tcp ["db.internal:*"] # every port, said explicitlyA missing port is never an implicit :*. Forgetting one cannot quietly grant
every port on a host.
What you give up
Section titled “What you give up”Compared to an allow_http block:
allow_http | allow_tcp | |
|---|---|---|
| Per-request rules | yes | no |
| Credential injection | yes | no |
| Learn-mode detail | method and path | destination only |
So prefer allow_http for anything HTTP — see allow_http or
allow_tcp? for the full
tradeoff. One destination cannot have both: naming it in each is a resolve-time
error, because the opaque grant would carry the same traffic uninspected and
the request rules would be decoration.
Platform differences
Section titled “Platform differences”Raw grants are where the two backends diverge most.
| macOS, capture | macOS, fallback | Linux | |
|---|---|---|---|
| TCP | transparent | SOCKS5 via ALL_PROXY | transparent |
| UDP | connected sockets, QUIC included | not available — denied | transparent where xt_TPROXY exists |
On Linux and under macOS transparent capture, any client is intercepted with
nothing to configure. The macOS fallback (no system extension installed) is
the constrained case — the client must speak SOCKS5, and allow_udp grants
nothing — and a couple of clients (libpq, psycopg, most MySQL/Redis drivers)
do not speak SOCKS5 at all. The macOS reference has
the detail, including which flows need an IP rule rather than a name. Every
failure fails closed: a flow wardcell cannot name or route loses egress rather
than bypassing the gate.
Denies
Section titled “Denies”deny_tcp and deny_udp block their own protocol. deny_hosts blocks every
protocol to a destination, which is usually what you want:
deny_hosts ["telemetry.example.com:*"] # http, tcp and udp