egress: add egress gateway support - atenet-egress - #693
egress: add egress gateway support - atenet-egress#693Lior Lieberman (LiorLieberman) wants to merge 2 commits into
Conversation
The gateway terminates actor's CONNECT request. It requires downstream mTLS, so only a worker's atunnel can reach it. The gateway consists of an Envoy and an `atenet router --standalone` ext_proc sidecar.
demos/egress is a small Actor that fetches a URL it is given and echoes the upstream status and body back, which makes the egress path observable from outside the sandbox. hack/install-demo-egress.sh registers it as a --deploy-demo-egress fixture and hack/verify-egress-demo.sh drives it and checks the atenet-egress logs for the corresponding authorized CONNECT. TestActorEgress in the networking suite covers the same path automatically: it creates an Actor from the demo template, POSTs a fetch request through atenet-router, and asserts 200. The suite's actor helper is parameterised by template so the ingress test keeps using the counter fixture.
305b232 to
598a548
Compare
| # Envoy calls it over localhost to authenticate actor identity against the | ||
| # ate API on every CONNECT. This mirrors the ingress gateway topology | ||
| # (Envoy + ext_proc in one pod); a shared/standalone ext_proc is a future step. | ||
| - name: ext-proc |
There was a problem hiding this comment.
Let us make ext-proc a native sidecar (initContainers + restartPolicy: Always).
Regular containers get SIGTERM together, ext_proc has no drain and will exit first,
and with failure_mode_allow: false every CONNECT arriving during Envoy's drain
window fails closed with a 503.
| # Co-located ext_proc server (the atenet router, ext_proc-only). The egress | ||
| # Envoy calls it over localhost to authenticate actor identity against the | ||
| # ate API on every CONNECT. This mirrors the ingress gateway topology | ||
| # (Envoy + ext_proc in one pod); a shared/standalone ext_proc is a future step. |
There was a problem hiding this comment.
a shared/standalone ext_proc is a future step: can we first move the exe_proc logic into a separate binary?
There was a problem hiding this comment.
ext_proc is already separate
There was a problem hiding this comment.
I don't think so. See Line 268 in this file:
image: ko://github.com/agent-substrate/substrate/cmd/atenet
|
implementation itself looks ok, but would like to fix a few organization things:
probably something like: |
Bowei Du (bowei)
left a comment
There was a problem hiding this comment.
You need to scrub through all of the comments -- there seems to some internal notes to yourself and your friend Claude.
| localhostRegistryReplacement = pflag.String("localhost-registry-replacement", "", "The replacement registry endpoint for localhost and/or loopback IP addresses, useful for local development. for example kind-registry:5000") | ||
| imageCacheDir = pflag.String("image-cache-dir", ateompath.ImageCacheDir, "Directory for the node-local OCI image layer cache. Must be on the volume shared with the ateom pods (the cached layers are their overlay lowerdirs), and on a disk sized for both capacity and IOPS: unpack throughput is gated by the volume's IOPS.") | ||
|
|
||
| // SEE(lior): both sides kept — main added --log-level here while this branch |
| imageCacheDir = pflag.String("image-cache-dir", ateompath.ImageCacheDir, "Directory for the node-local OCI image layer cache. Must be on the volume shared with the ateom pods (the cached layers are their overlay lowerdirs), and on a disk sized for both capacity and IOPS: unpack throughput is gated by the volume's IOPS.") | ||
|
|
||
| // SEE(lior): both sides kept — main added --log-level here while this branch | ||
| // added --egress-gateway-address. Independent flags, no interaction. |
There was a problem hiding this comment.
probably want to scrub your comments
| // When set, actors whose Run/Restore request does not already carry an egress | ||
| // gateway address have this address injected, causing ateom to redirect actor | ||
| // TCP egress through atunnel to the egress gateway. Empty keeps egress off. | ||
| egressGatewayAddress = pflag.String("egress-gateway-address", "", "Address (host:port) of the egress gateway. When set, actor TCP egress is transparently tunneled through atunnel to this gateway. Empty disables egress.") |
There was a problem hiding this comment.
Your comment and the help text are different.
As I understand it -- this sets a default egress if not specified on the Actor resource explicitly?
Can you fix it to match up?
| case *extprocv3.ProcessingRequest_RequestHeaders: | ||
| start := time.Now() | ||
| hResponse, rqm, target, tmplNs, tmplName, resumeOutcome, err := s.handleRequestHeaders(stream.Context(), reqType.RequestHeaders) | ||
| // One ext_proc server handles both directions: actor egress |
There was a problem hiding this comment.
Let's factor this so you could run it in this way, but in a real deployment, I think you would keep them as separate deployments as they will likely have different scaling parameters.
| // filter chain that accepted the request. The egress Envoy asks for it via | ||
| // request_attributes on its ext_proc filter. | ||
| // | ||
| // SEE(lior): this was xds.listener_name, which reads more naturally but |
There was a problem hiding this comment.
you should scrub for these.
|
Needs rebase |
Added pluggable egress PEP support. (Feedback on atenet-egress name is welcomed - will open a separate PR to rename atenet-router to atenet-ingress )
#559 shipped the actor egress data path without any egress gateway. This adds an Envoy deployment that terminates actor CONNECTs. It requires downstream mTLS, so only a worker's atunnel can reach it.
The gateway consists of an Envoy and an
atenet router --standaloneext_proc sidecar.The same ext_proc binary now serves both directions. Direction is decided by
the accepting listener via the xds.listener_name CEL attribute, not by
anything in the request, so an ingress client cannot reach the egress handler
by crafting a CONNECT (and maybe thats not ideal? feedback is welcomed!).
handleEgressRequestHeaderschecks the method,validates the worker-asserted actor identity headers, authenticates them
against the control plane with GetActor, and rejects actors that are not
RUNNING or whose asserted version is stale.
With that change, atelet's Run/Restore requests grow two fields it now
populates on the way down to ateom:
egress_gateway_address, from the new cluster-wide
--egress-gateway-address flag, which the manifests point at
atenet-egress.ate-system.svc:443. This is the value that arms the
nftables REDIRECT; from here actor TCP egress leaves through the gateway
rather than the worker's masquerade.
actor_version, the Actor resource version ate-api observed when it
assigned the worker. atunnel asserts it to the gateway, and the gateway
fails closed when its own GetActor read is older, so a stale control-plane
view cannot authorize egress for an Actor whose assignment has since
moved. atelet rejects Run/Restore without it.