Add Gateway API TCPRoute support for raw TCP forwarding - #130
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds Gateway API
TCPRoutesupport: forward a whole listener port to backends, plain TCP, no SNI or TLS. Closes theGateway API: TCPRouteboard item.Design
TCPRoute is the raw-TCP counterpart to the TLSRoute shipped in #126, with the name-routing removed. Where TLSRoute routes by the ClientHello SNI, TCPRoute routes nothing — one catch-all
Protocol::Tcpentrypoint per (route, port),sni: None, forwarding the whole port. It reuses the existing machinery directly: theParentReftrait, theTcpListenerPortsresolver,tls_entrypoint_defaults, and the same watcher / apply / delete / re-resolve shape.Same constraint as TLSRoute: the Gateway's
TCPlistener port must match aproxy.tcpentry inconfig.yaml— Sōzune binds TCP ports at startup, never from a Gateway alone. A route on an undeclared port is tracked but unserved, and goes live on the next reload once the listener is declared.Verified on a real cluster
Built into an image and run on kind end to end (not just unit-tested): the k8s suite (
tests/e2e/k8s/04-tcproute.sh) installs the TCPRoute CRD, stands up a socat echo backend, and asserts the whole port is forwarded (k8s-tcproute-okcomes back). 43/43 in the k8s suite, 126/126 on the Docker suite (no HTTPS regression), plus 7 new unit tests.A ReferenceGrant bug this surfaced
Codex review caught that
trusted_route_from_namespacesonly recognisedfrom.kind == "HTTPRoute", so aReferenceGrantnamingTCPRoute(orTLSRoute) was never recorded and cross-namespace backends for those kinds were always rejected. Fixed to accept all three route kinds — which also unblocks cross-namespace TLSRoute backends, a pre-existing defect.Known pre-existing issues (not in scope here)
Codex also flagged three defects that live in the shared HTTPRoute/TLSRoute attachment path, not in TCPRoute specifically, so they're left for a dedicated Gateway API hardening pass rather than widened into this PR:
parentRef.group: ""(explicit empty) is treated as the Gateway API group; per spec only an omitted group defaults.allowedRoutes(namespacefrom, kind restrictions) is not enforced —ListenerInfodoesn't retain the policy.SCOPE_SUBSCRIBERS/Notify: threenotify_one()calls can collapse to one permit if a watcher isn't parked, so a watcher may wait out its 2s tick instead of re-resolving immediately. (Bumped 2→3 here for correctness of the count, but the underlying broadcast guarantee is weak.)ReferenceGrant kind granularity is also documented as a known looseness: grants are keyed by
(from_ns, to_ns), not by route kind, so a grant for one kind admits another kind in the same namespace.