Repository navigation
Add kubectl ate proxy actor command. - #3191
Benjamin Elder (BenTheElder) merged 2 commits into
Conversation
b71cab9 to
95e8b98
Compare
8f8975d to
928511f
Compare
| // IngressAllowedSPIFFEIDs are the SPIFFE IDs the ingress router lets | ||
| // reach actors over its TLS listeners (ATE_INGRESS_ALLOWED_SPIFFE_IDS). | ||
| // See RequireIngressAllowedSPIFFEIDs. | ||
| // Unset defaults to spiffe://cluster.local/ns/<namespace>/sa/atenet-client. |
There was a problem hiding this comment.
Personally, I feel that our demo setups should be adding this, rather than adding it to every install by default.
There was a problem hiding this comment.
Is this a value that the user could also want to be empty and patched later?
There was a problem hiding this comment.
I agree with taahir. Default should be empty. demo should patch it
There was a problem hiding this comment.
I changed it so that the demo instructions explicitly add the atenet-client. I still think this is not well thought-out and that it's not consistent with how the control plane bootstraps the ownership, but let's discuss that as a follow up.
928511f to
9ad76c1
Compare
f1f0cba to
18cf3c2
Compare
| func (c *Config) RequireIngressAllowedSPIFFEIDs() error { | ||
| if c.Router == RouterAgentgateway || len(c.IngressAllowedSPIFFEIDs) > 0 { | ||
| return nil |
There was a problem hiding this comment.
Taahir Ahmed (@ahmedtd) thinking about it more. I think we should allow setting this empty. it is a pretty common architecture that actors can pull for work vs get work. Resume is also called from different controllers and not necessarily through atenet
There was a problem hiding this comment.
Empty now is allowed and means no egress.
There was a problem hiding this comment.
Some high level notes:
- can we mint the proxy cert outside ate-system? whoever gets PCR create there can also mint atelet for any node and get actor certs
- orthogonal to this PR: This pr is relying on
the fact that non-Kubelet clients are allowed to create arbitary PodCertificateRequests, even if the pod or service account they reference does not exist(#2389). Bowei Du (@bowei) had an idea of two podcertcontrollers - a demo one that issues certs for made-up pods and a prod one that refuses them.
7dcc7d8 to
7b8bd99
Compare
Haven Xia (HavenXia)
left a comment
There was a problem hiding this comment.
Should the e2e router client use the new atenet-client identity instead of ate-e2e-router-client/router-client?
Lior Lieberman (LiorLieberman)
left a comment
There was a problem hiding this comment.
LGTM generally,
Since we now dont reject empty list for spiffee ids, - can you add a big note somewhere in the installation md files that "In order to allow ingress traffic to actors you should add the identities in this flag. If you have one frontend / gateway - it could be this identity, but if you have different callers to atenet-router - it should list them" (not this words verbatim but something similar)
in a follow up (really not now) we may want to look at whether we want to even deploy atenet-router if the list is empty.
Theoretically - we it does nothing so why deploying it BUT on the flip side, if/once we support better ingress auth we should not really require this "simple" flag and deprecate this authz mode.
| the client certificate for `spiffe://cluster.local/ns/ate-system/sa/atenet-client`, so the install must list that ID in | ||
| `--ingress-allowed-spiffe-ids`. |
There was a problem hiding this comment.
should we have a kubectl command to validate --ingress-allowed-spiffe-ids was set?
follow up please !! :) |
e37bcff to
7eb301f
Compare
atenet-router requires mTLS with an allowed SPIFFE client certificate, so plain kubectl port-forward can no longer reach actors from a workstation. - Add kubectl ate proxy actor <actor> [[local-port:]remote-port ...] (default 8000:80) to tunnel TCP connections to an actor over atenet-router's :8444 CONNECT listener. It presents a certificate for spiffe://cluster.local/ns/<namespace>/sa/atenet-client, which the install must list in ingressAllowedSPIFFEIDs. - Extract podidentity.RequestCertificate and ateclient.ConnectActorWithHeaders so e2e tests and ateclient share certificate minting and CONNECT tunneling. - Let ate-setup and atenet-router accept an empty ingressAllowedSPIFFEIDs. The Envoy router then serves no listener, and agentgateway denies every client. Document which identities to list. AI use: AI-assisted, I reviewed and tested it. Signed-off-by: Julian Gutierrez Oschmann <[email protected]>
- Replace kubectl port-forward svc/atenet-router 8000:80 with kubectl ate proxy actor across README.md, demo walkthroughs, and verification scripts. - Remove the unused jupyter-proxy NGINX deployment from the Jupyter demo. - Update demos/sandbox/client to dial ate-api-server via ateclient.NewClient. - Update demos/egress/test-egress.sh for the worker-for-actor SPIFFE URI. AI use: AI-assisted, I reviewed and tested it. Signed-off-by: Julian Gutierrez Oschmann <[email protected]>
5dd9c4e
7eb301f to
5dd9c4e
Compare
Benjamin Elder (BenTheElder)
left a comment
There was a problem hiding this comment.
I think these can all be small follow-ups.
| func (c *routerClientCerts) get(ctx context.Context) (*tls.Certificate, error) { | ||
| c.mu.Lock() | ||
| defer c.mu.Unlock() | ||
| now := time.Now() | ||
| if c.cert != nil && now.Before(c.refreshAt) { | ||
| return c.cert, nil | ||
| } | ||
| timeout := routerCertTimeout | ||
| if c.cert != nil && now.Before(c.cert.Leaf.NotAfter) { | ||
| timeout = routerCertRefreshTimeout | ||
| } | ||
| namespace := systemNamespace() | ||
| cert, err := podidentity.RequestCertificate(ctx, c.clientset, routerCertRequester, namespace, installdefaults.IngressClientServiceAccount, timeout) |
There was a problem hiding this comment.
c.mu is held across RequestCertificate, and this runs inside the TLS handshake. Once refreshAt passes, every new connection blocks for up to 15s behind the refresh even though the cached cert is still valid. After the cert expires with the signer unavailable, each queued handshake then makes its own 2 minute attempt in turn, since refreshAt isn't moved on that path.
Can we hand out the cached cert while one caller refreshes, or refresh in the background?
| // certificate and presents a podidentity certificate for the ingress client | ||
| // ServiceAccount, which the router allows by default. |
There was a problem hiding this comment.
stale, the allowlist defaults to empty now, so the router doesn't allow this client unless the install lists it
| // client-go tears down a whole port-forward when any one of its | ||
| // streams errors, so each tunnel gets its own. That also picks | ||
| // a ready router pod again after one goes away. | ||
| localPort, stop, err := portforward.ServicePortForward(ctx, d.config, d.clientset, systemNamespace(), installdefaults.RouterServiceName, routerConnectTLSPort) |
There was a problem hiding this comment.
non-blocking: every accepted connection does a Service GET and a Pod LIST (internal/portforward/portforward.go:66, :74) plus a port-forward upgrade before any bytes move, on a rest.Config left at client-go's default 5 QPS / burst 10. That's two apiserver round trips of latency per connection, and a browser opening a burst of connections (the Jupyter flow) gets throttled to ~2.5 new tunnels/s after the first few.
Could we resolve the router pod once and only re-resolve when a port-forward fails?
| ``` | ||
|
|
||
| ### 3. Port-Forward Services | ||
| ### 3. Proxy to the Actor |
There was a problem hiding this comment.
nit: demos/claude-code-multiplex/README.md:31 links #3-port-forward-services here for the api port-forward, that anchor and the api port-forward are both gone now
The client called ResumeActor before starting its REPL. A request through the router resumes a suspended actor, so the call is redundant. It was also the first thing to fail before agent-substrate#3191 gave the client credentials. The connection stays where it was: before the REPL, on the signal context. NewClient uses that context only while connecting, for the kube API calls, the token mint and the port-forward readiness wait; the tunnel and gRPC connection outlive it and close with the client. Signed-off-by: Ziy1-Tan <[email protected]>
The client called ResumeActor before starting its REPL. A request through the router resumes a suspended actor, so the call is redundant. It was also the first thing to fail before agent-substrate#3191 gave the client credentials. The connection stays where it was: before the REPL, on the signal context. NewClient uses that context only while connecting, for the kube API calls, the token mint and the port-forward readiness wait; the tunnel and gRPC connection outlive it and close with the client. Signed-off-by: Ziy1-Tan <[email protected]>
Allow a user to land traffic on an actor from a dev machine.
The
kubectl-ateCLI now authenticates toatenetby minting a certificate (viaPodCertificateRequest) forspiffe://cluster.local/ns/ate-system/sa/atenet-client.Ideally, atenet should support JWT based auth and there would be no need for a certificate minting. But that support is not in place yet.
The
kubectl ate proxy actorcommand also improves local dev ergonomics as it attachjes the actor header so clients don't need to be "substrate-aware". For example, a web browser can just point to the local proxy.Fixed #3194 and #3187