Skip to content

atelet: only ate-api-server may call AteomHerder - #2516

Merged
Taahir Ahmed (ahmedtd) merged 1 commit into
agent-substrate:mainfrom
destijl:atelet-herder-authz
Oct 19, 2026
Merged

Taahir Ahmed (ahmedtd) merged 1 commit into
agent-substrate:mainfrom
destijl:atelet-herder-authz

Conversation

@destijl

@destijl Greg Castle (destijl) commented Oct 12, 2026 •

Copy link
Copy Markdown
Collaborator

The AteomHerder listener required a client certificate chaining to the pod-identity CA, but any pod in the cluster can obtain one, and the caller's identity was never checked. Any pod reaching atelet could drive actor lifecycles (Run/Checkpoint/Restore/Terminate) on the node.

  • Authorize the verified client cert's SPIFFE ID against --herder-allowed-spiffe-id (default spiffe://cluster.local/ns/<atelet ns>/sa/ate-api-server) during the TLS handshake.
  • Install client verification on the per-handshake config returned by GetConfigForClient. crypto/tls replaces the whole config with that one, so the same-node check previously set on the outer config of the ateom-facing credential broker never ran; it now does.
  • Drop hostPort 8085 from the atelet DaemonSet: ate-api-server dials the atelet Pod IP, so the node-IP exposure only widened reachability.

Fixes #2073

Breaking change

Only ate-api-server can call AteomHerder now. Anything else that called atelet on :8085 (including via <node-ip>:8085) is rejected at the TLS handshake. Installs that rename the ate-api-server ServiceAccount or run it in another namespace must pass --herder-allowed-spiffe-id to atelet.

  • Tests pass
  • Appropriate changes to documentation are included in the PR (none needed)

Comment thread cmd/atelet/main.go
GetCertificate: serverCert,
ClientAuth: tls.RequireAndVerifyClientCert,
ClientCAs: clientCAs,
VerifyConnection: verifyClient,

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.

I think in the long run it's probably better to verify the certificate per-request --- atelet will probably eventually need a richer authorization policy than "only ate-api-server can call any RPC", and the error message for clients is much better if the cert is checked during request processing, rather than connection establishment.

This is fine for now, though.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Ack.

Comment thread cmd/atelet/main.go
return nil, fmt.Errorf("load CA bundle %s: %w", clientCAPath, err)
}
serverCert := credbundle.Loader(servingBundlePath)
return &tls.Config{

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.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

🤖 Thanks. Whichever lands second can carry the callback through: credbundle.ServerConfig would need a VerifyConnection field set on the config that GetConfigForClient returns. I'm happy to rebase onto #2237 if it lands first.

Comment thread cmd/atelet/main.go Outdated
tlsCfg, err := ateletServerTLSConfig(*grpcServerCredBundle, *clientCACerts)
// The flag defaults to installdefaults.APIServerServiceAccount
// ("ate-api-server"), so this only rejects an explicit empty override:
// path.Join would drop the empty segment, yielding an identity that parses

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.

I feel like we're heavy on the agent comments here. Can you slim them down?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

🤖 Trimmed the comments in main.go, atelet.yaml, and installdefaults.

Comment thread cmd/atelet/main.go Outdated

grpcServerCredBundle = pflag.String("grpc-server-cred-bundle", "/run/podidentity.podcert.ate.dev/credential-bundle.pem", "Credential bundle atelet presents as its gRPC serving certificate.")
clientCACerts = pflag.String("client-ca-certs", "/run/podidentity.podcert.ate.dev/trust-bundle.pem", "CA bundle used to verify gRPC client certificates.")
ateapiServiceAccount = pflag.String("ateapi-service-account", installdefaults.APIServerServiceAccount, "ServiceAccount ate-api-server runs as, in atelet's namespace. It is the service-account segment of the only SPIFFE ID allowed to call AteomHerder, so a deployment that prefixes resource names needs it set.")

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.

I feel like we should just take the whole SPIFFE ID that's allowed to call? That way ate-setup can handle all the stuff with figuring out namespaces and so on.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

🤖 Done. Replaced --ateapi-service-account with --herder-allowed-spiffe-id. It defaults to spiffe://cluster.local/ns//sa/ate-api-server, so ate-setup can pass the full identity.

@destijl

Copy link
Copy Markdown
Collaborator Author

FYI The test failure is due to a bad test that is hitting "example.com" #2513

@destijl

Copy link
Copy Markdown
Collaborator Author

Rebased to fix merge conflicts and pick up the CI testing fix. Should be OK to merge directly now.

Comment thread manifests/ate-install/atelet.yaml Outdated
- configMapRef:
name: ate-otel-config
ports:
# No hostPort: ate-api-server dials atelet's Pod IP.

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.

lets remove this line, feels redundant

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

removed

Comment on lines +113 to +115
func APIServerSPIFFEID(namespace string) string {
return SPIFFEID(namespace, APIServerServiceAccount)
}

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 a special helper for this, vs just use SPIFFEID(namespace, APIServerServiceAccount)?

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.

I see other function do the same... feels like a lot of redundant code.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I agree. I asked gemini exactly the same thing and it told me it was following the convention set by all the other functions, so I left it in 🤷 . I'll take it out.

Comment on lines +119 to +125
func TestAPIServerSPIFFEID(t *testing.T) {
// Matches the --herder-allowed-spiffe-id default atelet ships with.
const want = "spiffe://cluster.local/ns/ate-system/sa/ate-api-server"
if got := APIServerSPIFFEID(SystemNamespace); got != want {
t.Errorf("APIServerSPIFFEID(%q) = %q, want %q", SystemNamespace, got, want)
}
}

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.

similar comment to above, test and function feels very redundant.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Removed along with the helper.

@bowei Bowei Du (bowei) added the kind/bug Something isn't working / bugfixes label Oct 19, 2026
@destijl

Copy link
Copy Markdown
Collaborator Author

rebased and addressed comments

@ahmedtd
Taahir Ahmed (ahmedtd) added this pull request to the merge queue Oct 19, 2026
Merged via the queue into agent-substrate:main with commit 4945b64 Oct 19, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/node area/security Security related issue/pr kind/bug Something isn't working / bugfixes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Security] atelet AteomHerder gRPC listener on hostPort 8085 performs no caller identity authorization

5 participants