Implement Milestone2 Networking (Ingress and Egress)#460
Implement Milestone2 Networking (Ingress and Egress)#460John Howard (howardjohn) wants to merge 6 commits into
Conversation
Co-authored-by: Jet Chiang <pokyuen.jetchiang-ext@solo.io> Co-authored-by: npolshakova <nina.polshakova@solo.io>
a1492ed to
0049002
Compare
There was a problem hiding this comment.
Did these files get moved? Where did this all go?
There was a problem hiding this comment.
This was the old router logic for serving XDS/ext proc which is no longer there.
Looking at atstore specifically its used in two places:
- For some reason we call it and ignore the result in the router controller
- statusz page on the router.
I can add fetching the tempaltes and exposing them in atenet but it seems weird for atenet to expose these in its statusz given it doesn't care about them. I also think we can remove atenet (folding it into the controller) now that it does so little anyways
|
Probably want to split this up into:
in the real PRs |
|
Taking a quick scan through the changes, I do think we have to discuss what it means for us to delete functionality out of the Substrate project and move it entirely into the agent-gateway repo, which is an independently evolving repo. This is not necessarily a good precedent to set for this project. Specifically, I see that in order for this change to work, a changeset here (agentgateway/agentgateway#2586) also needs to go into Agent Gateway. I would much prefer keeping as much of the core logic associated with Substrate including the networking as part of this project in a self-contained way. At the very least, one requirement in my mind is that code and implementation should be hosted here. It can be vendored outside to create plugable implementations, but at this point, we do need to keep things more contained than have a deep dependency on outside... |
There was a problem hiding this comment.
Thanks John Howard (@howardjohn), half way through it, still havent reviewed atunnel itself. Meanwhile, some nit comments in-line with a few questions here;
-
“Atunnel will listen on port 443.. and forward to the actor’s port 80”” – does it mean we only support 443 for ingress? can an actor receive on 443? (in-app tls)?
-
“A redirect rule conditionally captures all traffic leaving the actor” – what is the condition? is it only when a policy exist? I guess for the context of this PR is it when the req contains egressGateway address?
-
Can connect + mTLS be pluggable/configurable? If yes, how?
-
for “configured egress proxy” why not per atespace? I can see its on the actor now, how does this gets configured? And this means we cant change it while the actor is running, correct? I dont think we should be worried about supporting it now but would be better to document it if my assumption is correct.
-
"Egress proxying is configured only if egress policies are set for the actor”
I know API is TBD still but what would be the component that watches is and activates the egress proxy - the controller? -
"The Egress component is expected to accept CONNECT requests over mTLS" – I think there were prior discussions to make that pluggable as well - how can we support different tunnels that are not only CONNECT?
| if req.GetActorVersion() < 1 { | ||
| errs = append(errs, field.Invalid(field.NewPath("actor_version"), req.GetActorVersion(), "must be positive")) | ||
| } |
There was a problem hiding this comment.
nit: question: whats the reason for that? why would it be less than 1?
| atunnelCredentialBundle = pflag.String("atunnel-credential-bundle", "/run/podidentity.podcert.ate.dev/credential-bundle.pem", "PEM credential bundle for actor ingress HTTPS") | ||
| atunnelTrustBundle = pflag.String("atunnel-trust-bundle", "/run/podidentity.podcert.ate.dev/trust-bundle.pem", "PEM trust bundle for actor ingress clients") | ||
| atunnelClientIdentity = pflag.String("atunnel-client-identity", "spiffe://cluster.local/ns/ate-system/sa/ateway-ingress", "SPIFFE identity allowed to call actor ingress HTTPS") | ||
| atunnelEgressListenAddress = pflag.String("atunnel-egress-listen-address", "0.0.0.0:15001", "Address for transparently intercepted actor egress TCP") |
There was a problem hiding this comment.
nit: should 15001 be configured as a shared const somewhere to ensure consistency with workload_apply.go?
|
|
||
| interiorNetNS netns.NsHandle | ||
| actorLogger *actorlog.ActorLogger | ||
| atunnel *atunnel.Server |
There was a problem hiding this comment.
nit: atunnelIngress ?
| if egressClient != nil { | ||
| if err := s.atunnelEgress.Activate(egressClient, atespace, actorName, actorVersion, ""); err != nil { | ||
| if s.atunnel != nil { | ||
| _ = s.atunnel.Deactivate(context.Background()) |
There was a problem hiding this comment.
why deactivating ingress if egress had a problem?
| // should replace the broad masquerade path with transparent TCP capture and | ||
| // default-deny rules. | ||
| // TODO: Restrict the compatibility masquerade to DNS traffic sent to the | ||
| // configured cluster resolver and drop all other non-tunneled actor egress. |
There was a problem hiding this comment.
whats cluster resolver? you mean dns resolver? So that only DNS traffic can flow without a tunnel?
| } | ||
|
|
||
| func tcpDestinationPortEqual(port uint16) []expr.Any { | ||
| func tcpProtocol() []expr.Any { |
There was a problem hiding this comment.
function name sounds weird. maybe tcpProtocolMatcher()?
| // logWriter with the runtime logger so the two streams to os.Stdout are | ||
| // serialized through one SyncedWriter and never interleave-corrupt lines. | ||
| actorLogger := actorlog.NewActorLogger(logWriter, metadata.OnGCE()) | ||
| upstream, err := url.Parse("http://169.254.17.2:80") |
There was a problem hiding this comment.
nit: you defined actorHTTPUpstream somewhere, would be better to use that constant
There was a problem hiding this comment.
majority of changes and content of this file is the same as ateom-gvisor, why not using shared funcs? is it by purpose?
This implements most of the approved M2 networking milestone scope.
Changes
A new
atunnelcomponent is added withinateom. This is embedded in the same binary so theatunnelname is mostly internal.atunnelhas 2 responsibilities: terminating incoming traffic from the ingress, and capturing and forwarding egress traffic.atunnellistens) or can leave that to users.atunnelwill listener on port 443, terminate TLS, and forward to the actor's port 80. Additional ports are a TODO pending an API for this.atunneladditionally listens on port 15001 and accepts egress traffic from the application. AREDIRECTrule conditionally captures all traffic leaving the actor and forwards to theatunnel. Theatunnelwill establish a CONNECT+mTLS request to the configured egress proxy.The Ext proc code is removed from
atenet. This leaves only the DNS stuff inatenet; depending on the outcome of Consider removing custom DNS for actors #445 we can removeatenetentirely (if not, we should probably rename it or just merge it intoate-controllerprobably).atenet-routeris renamed toateway-ingressand has a new implementation.standalonemode is supported now. This matches the existing default. This means the ingress is its own standalone deployment.ate.*attributes to logs/traces/etc as defined in feat(otel): set ate.* actor telemetry identity on ateapi spans #412ateway-egressis added. This terminates the CONNECT, and can enforce policies before dynamically forwarding the request.[Revert egress policy](https://github.com/agent-substrate/substrate/commit/21f854ef6a1d6d9cdc8cae9f506125c4432f9199)removing it. In parallel I will likely open a PR adding the API, but it doesn't need to block thisGetActorcall is made to load the actor policies. This is cached.atunnelincludes the actor id, atespace, and actor version. This ensures that if an actor policy is updated, we do not incorrectly apply stale policies from the cache.A new
egressdemo is added to showcase a worker that sends outbound requests.Blocking merge: this PR currently uses an unofficial image for the ateways. I will swap it once we get high level buy in for this (just to avoid needing to do a bunch of releases if we need to iterate on this).
Testing
Some basic testing using the counter demo shows substantially reduced tail latencies on the ingress flow:
Example showing end to end flow with tracing:

Aside from unit testing this PR also adds some basic end to end tests of the ingress and egress flow
Extensibility
The M2 design is to be batteries included but swappable. Both ingress and egress can be swapped by replacing the ateway-ingress/ateway-egress deployments with their own. These components are identified by their hostname/service by the relevant components (atenet dns, ateom, etc) and can be overriden (or simply re-using the same name).
The ingress component is expected to identify an actor (through implementation-specific means), and forward to the appropriate worker on port 443 with mTLS. While the ingress could support custom hostnames on the incoming request flow it must send them in the standard
my-counter.demo.actors.resources.substrate.ate.devstyle.The egress component is expected to accept CONNECT requests over mTLS and server the appropriate configuration for the actor identified by the X-Ate-Atespace, X-Ate-Actor, and X-Ate-Actor-Version headers. The
:authorityheader contains the original destination of the traffic.Note: I do not care if we keep the 'cute' names of
atewayandatunnel. I am also fine to keeprouterthoughingressaligns withegresswhich is why I shifted it.For #430
For #126
For #326
Replaces #338
Replaces #393
Related to #340