Skip to content

Support CONNECT in atenet router - #715

Open
Keith Mattix II (keithmattix) wants to merge 13 commits into
agent-substrate:mainfrom
keithmattix:ate-router-connect
Open

Support CONNECT in atenet router#715
Keith Mattix II (keithmattix) wants to merge 13 commits into
agent-substrate:mainfrom
keithmattix:ate-router-connect

Conversation

@keithmattix

@keithmattix Keith Mattix II (keithmattix) commented Aug 3, 2026

Copy link
Copy Markdown

Fixes #689, #265 and starts to address #484. Modifies the ate router to:

  1. Read :authority from extproc forwarded metadata instead of the requests authority (necessary for CONNECT since the inner http request may not have the proper host header).
  2. Always send to the port in the authority header (passed back to Envoy via metadata)
  3. Serve network extproc on a separate port (needed for TCP tunneled within CONNECT). Doesn't fully work though; see below.

#484 is currently blocked because Envoy's implementation of NetworkExternalProcessor doesn't support passing filter state to the extproc server. Dynamic metadata doesn't work because only Host (i.e. endpoint) and cluster metadata can propagate across internal listeners (a necessary implementation detail of doing CONNECT in Envoy). This PR still adds the xDS for it; the extproc calls just fail because there's no metadata.

Open questions:

  1. Do we need all combinations of [plaintext CONNECT, TLS CONNECT] x [http 1.1, h2]? I've implemented all 4 but wanted to confirm
  2. This PR still has extproc relying on :authority header to determine actor+atespace since potential changes to DNS (e.g. maybe we use headers instead) are under discussion. Need to resolve that at some point, but doesn't have to block this.
  3. We're assuming that non HTTP actors don't get suspended during the lifetime of a TCP connection; we only run Resume on the initial connection establishment. We could potentially do network ext authz instead (it actually on gets invoked once during conn establishment vs. we hit extproc on every client write), but wanted to discuss. I will say that I highly doubt that we'll need ANYTHING in the TCP body.
  4. Not sure if we've agreed if we want to do CONNECT (+ TLS?) to atunnel or not. This PR doesn't do that yet

Tested 6 scenarios locally:

  1. Plain HTTP ingress → hello from: 169.254.17.2 | preserved memory count: 14...
  2. Plain HTTPS ingress → hello from: 169.254.17.2 | preserved memory count: 15...
  3. CONNECT (plaintext) + HTTP/1.1 → port 9090 → hello from extra port 9090 on pod 169.254.17.2
  4. CONNECT (plaintext) + h2c → port 9090 → hello from extra port 9090 on pod 169.254.17.2
  5. CONNECT+TLS + HTTP/1.1 → port 9090 → hello from extra port 9090 on pod 169.254.17.2
  6. CONNECT+TLS + h2c → port 9090 → hello from extra port 9090 on pod 169.254.17.2

Once #652 merges, I can add the agentgateway implementation Added and tested agentgateway implementation as well

  • Tests pass
  • Appropriate changes to documentation are included in the PR

@keithmattix Keith Mattix II (keithmattix) changed the title Ate router connect Support CONNECT in atenet router Aug 3, 2026
@keithmattix
Keith Mattix II (keithmattix) marked this pull request as ready for review August 3, 2026 20:59
@maxsmythe Max Smythe (maxsmythe) added kind/feature An enhancement / feature request or implementation area/network labels Aug 4, 2026
@maxsmythe

Copy link
Copy Markdown
Collaborator

Bowei Du (@bowei) to route networking PR

@bowei

Copy link
Copy Markdown
Collaborator

Can you put the vendor change in its own commit (make it obvious)

Consolidates the vendor/, go.mod, and go.sum changes needed for
arbitrary-port CONNECT ingress support into a single commit.
…T terminate listener implementation

Signed-off-by: Keith Mattix II <keithmattix2@gmail.com>
Signed-off-by: Keith Mattix II <keithmattix2@gmail.com>
Signed-off-by: Keith Mattix II <keithmattix2@gmail.com>
Signed-off-by: Keith Mattix II <keithmattix2@gmail.com>
Signed-off-by: Keith Mattix II <keithmattix2@gmail.com>
Signed-off-by: Keith Mattix II <keithmattix2@gmail.com>
Signed-off-by: Keith Mattix II <keithmattix2@gmail.com>
Signed-off-by: Keith Mattix II <keithmattix2@gmail.com>
Agentgateway's ext_proc client reads only Value and ignores RawValue,
silently applying our RawValue-only mutations as empty-string headers.
This broke agentgateway's dynamic routing headers (x-ate-original-dst,
X-Ate-Original-Host, :authority, X-Ate-Target-Port) and any immediate
error response's content-type header. Newer Envoy versions drop Value
in favor of RawValue, so both must be set to work on either dataplane.
Signed-off-by: Keith Mattix II <keithmattix2@gmail.com>
@keithmattix

Copy link
Copy Markdown
Author

Bowei Du (@bowei) good call - done

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need that network ext_proc? Can we resolve instead on the CONNECT itself. connect_terminate listener is already an HCM with no ext_proc on it - we can add the http ExtProc there.

What are the concerns with that?

Comment on lines +140 to +147
// TODO(router): authority is always empty today (see SubstrateMetadataNamespace's
// doc comment) -- dynamic metadata doesn't survive the connect_terminate ->
// main_internal internal-listener hop, and NetworkExternalProcessor has no
// filter-state-reading mechanism (no request_attributes) to fall back on the
// way the HTTP leg's handleRequestHeaders does. Until Envoy adds one, every
// CONNECT scenario that reaches this raw TCP leg -- non-HTTP payloads, and any
// TLS-wrapped payload per buildMainInternalListener's transport-protocol
// match -- fails here.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure I understand that. Can you elaborate?

AFAIU the metadata fails to cross the hop likely because passthrough_metadata only forwards static Host/Cluster metadata and never carried the value in the first place. I dont think its a NetworkExternalProcessor gap. Unless I am missing something

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I call it a NetworkExternalProcessor gap because its HTTP equivalent has a way to pass filter state to the extproc proc server and we use that to get the original CONNECT authority over there. Because request level metadata isn't passed through the internal listener transport socket; we can't use metadata to propagate it; we have to use filter state. So the requirements for the Envoy team is to be either allow internal listeners to propagate request metadata OR to allow network extproc to pass filter state via connection attributes. The same limitation applies to network ext auth btw

@keithmattix

Copy link
Copy Markdown
Author

Lior Lieberman (@LiorLieberman) at connect_terminate, we don't know whether the CONNECT is tunneling HTTP or not and we want different behavior depending on the inner protocol. If it's HTTP, we want to hit the ext proc on every request so that we Resume. If it's just TCP, we only want to hit the extproc once (on connection establishment). But we don't know which one we want until we get to main_internal and sniff the protocol of the CONNECT tunnel's contents.

CI's hack/verify/gofmt.sh flagged this after the previous commit added
fields without re-running gofmt.
Signed-off-by: Keith Mattix II <keithmattix2@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/network kind/feature An enhancement / feature request or implementation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support multiple, arbitrary actor ports

4 participants