[bug-bash] After a servicedns CA rotation, actor suspends crash on nodes whose atelet predates the new CA (objectstore-plugin loads exchange CA bundle once)
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Newbie friendliness
- 72/100
Research direction
Read internal/objectstoreplugin/exchangeclient/exchangeclient.go:89-92 and compare its CA loading with the credbundle.PoolLoader use in the sites fixed for #1931. Check whether the exchange client picks up trust bundle changes during later TLS handshakes, and inspect relevant tests or add a focused regression test. Done means suspending an actor succeeds after CA rotation without restarting atelet, with the failure explained by useful logs.
Written by the indexing model from the issue text.
Description
What happened?
When rotating podcertificate-controller-system/service-dns-ca-pool using the 3-step zero-downtime procedure documented in internal/localca/localca.go:50-59 (add new inactive CA -> wait for ClusterTrustBundle and projected pod volume propagation -> activate new CA -> ate-api-server receives a leaf signed by the new CA), suspending any actor that requires a fresh token exchange on a node whose atelet pod started before the new CA was added fails with Unavailable during external snapshot upload and crashes the actor (ACTOR_STATE_CRASHED), losing its unsnapshotted state.
- Restarting
ateleton one node (node-sznr) immediately restoressuspendfor actors placed on that node (config-ca4,config-ca5), while actors placed on the un-restarted node (node-02fq:config-ca2,config-ca3) continue to crash until that node'sateletis also restarted. - Zero log lines are emitted by
objectstore-pluginon the node or by the:8445exchange onate-api-serverexplaining the TLS verification failure.
Expected Behavior
Per internal/localca/localca.go:15-30, 50-59 and internal/credbundle/credbundle.go:56-65 ("a pool built at startup never sees a CA rotation"):
- Once the projected
/run/servicedns.podcert.ate.dev/trust-bundle.pemon each node contains both the old and new CA roots, activating the new CA for signing requires noateletrestarts. kubectl ate suspend actor <name> -a <atespace> --mode=syncsucceeds and transitions the actor toACTOR_STATE_SUSPENDED.
Steps to Reproduce
- Follow the 3-step rotation procedure in
internal/localca/localca.go:50-59onpodcertificate-controller-system/service-dns-ca-pool:- Step 1 (Add inactive CA 2): Add a second CA entry (
id: "2") topoolin Secretpodcertificate-controller-system/service-dns-ca-poolwhile keeping CA1asactiveForSigning. - Step 2 (Wait for trust propagation): Wait ~2 minutes until
ClusterTrustBundleservicedns.podcert.ate.dev:identity:primary-bundle(~64s) and projected/run/servicedns.podcert.ate.dev/trust-bundle.pemfiles in runningateletpods (~128s) contain both CA 1 and CA 2 roots. - Step 3 (Activate CA 2 & issue new serving leaf): Set
activeForSigningto"2"inservice-dns-ca-pool, and either wait forate-api-server'sservicednscert to renew or run:kubectl -n ate-system rollout restart deploy/ate-api-server kubectl -n ate-system rollout status deploy/ate-api-server
- Step 1 (Add inactive CA 2): Add a second CA entry (
- Create a fresh actor, wake it via the router, and run a synchronous suspend:
kubectl ate create actor config-ca1 -a ate-demo-counter --template counter ate_curl config-ca1 kubectl ate suspend actor config-ca1 -a ate-demo-counter --mode=sync kubectl ate get actor config-ca1 -a ate-demo-counter - A/B node control: Restart the
ateletpod on one of the two nodes (node-sznr) while leavingateletuntouched on the other node (node-02fq), then create, wake, and suspend fresh actors on each node (config-ca2/config-ca3onnode-02fq,config-ca4/config-ca5onnode-sznr).
Sandbox Runtime
gVisor (runsc)
Agent Substrate Version / Commit SHA
ffed8551
Kubernetes Version & Environment
1.37.1-gke.2097000
Host OS & Architecture
Linux x86_64 (COS, running on GKE)
Relevant Logs and Diagnostic Output
$ kubectl ate suspend actor config-ca1 -a ate-demo-counter --mode=sync
Error: failed to suspend actor: rpc error: code = Internal desc = internal server error: workflow failed at step CallAteletSuspend:
actor ate-demo-counter/config-ca1 crashed: rpc error: code = Internal desc = while uploading external snapshot: while uploading
snapshot files: rpc error: code = Unavailable desc = object-store plugin upload of gs://ate-snapshots-maxthomp-bugbash-cfg/atespaces/
ate-demo-counter/actors/8d0111f8-.../snapshots/939b4a6d-... failed (Unavailable)
== A/B node control: restart only atelet-fh8tj (node sznr); leave atelet-9gx67 (node 02fq) running
pod "atelet-fh8tj" deleted from ate-system namespace
NAME READY STATUS RESTARTS AGE IP NODE
atelet-5wn6v 2/2 Running 0 12s 10.96.1.28 gke-bugbash-maxthomp-substrate-node-p-2ec97ad4-sznr
atelet-9gx67 2/2 Running 0 47m 10.96.0.21 gke-bugbash-maxthomp-substrate-node-p-2ec97ad4-02fq
21:22:58 config-ca2 node=gke-bugbash-maxthomp-substrate-node-p-2ec97ad4-02fq suspend: Error: failed to suspend actor: rpc error: code = Internal desc = internal server error: workflow failed at step CallAteletSuspend: actor ate-demo-counter/config-ca2 crashed: rpc error: code = Internal ...
21:23:00 config-ca3 node=gke-bugbash-maxthomp-substrate-node-p-2ec97ad4-02fq suspend: Error: failed to suspend actor: rpc error: code = Internal desc = internal server error: workflow failed at step CallAteletSuspend: actor ate-demo-counter/config-ca3 crashed: rpc error: code = Internal ...
21:23:02 config-ca4 node=gke-bugbash-maxthomp-substrate-node-p-2ec97ad4-sznr suspend: ate-demo-counter config-ca4 ate-demo-counter/counter ACTOR_STATE_SUSPENDED <none> 5 1s
21:23:04 config-ca5 node=gke-bugbash-maxthomp-substrate-node-p-2ec97ad4-sznr suspend: ate-demo-counter config-ca5 ate-demo-counter/counter ACTOR_STATE_SUSPENDED <none> 5 1s
Additional Context
Root Cause:
internal/objectstoreplugin/exchangeclient/exchangeclient.go:89-92 loads the exchange trust bundle once at process startup via credbundle.ParsePool(cfg.CAFile) and freezes that x509.CertPool into a static tls.Config on a long-lived grpc.ClientConn, instead of reloading per handshake via credbundle.PoolLoader:
roots, err := credbundle.ParsePool(cfg.CAFile)
if err != nil {
return nil, fmt.Errorf("load exchange CA bundle: %w", err)
}
Relationship to #1931:
Open issue #1931 tracks the same bug class for cmd/atelet/main.go, internal/ateapiauth/client.go, and internal/ateletdial/ateletdial.go. At ffed8551, all 3 of those sites listed in #1931 have already been fixed to use credbundle.PoolLoader, whereas the following 3 sites in main are not listed in #1931 and still load the CA bundle once:
internal/objectstoreplugin/exchangeclient/exchangeclient.go:89-92(added in #2762)internal/atunnel/egress/client.go:115-121(os.ReadFile(cfg.TrustBundlePath)+AppendCertsFromPEMonce perNewClienton actor activation)cmd/ateapi/internal/controlapi/dialer.go:200-205(x509bundle.Loadonce per cachedateletconnection innewVerifyConnection)
Confirmation
- I have searched existing issues and verified that this is not a duplicate.
- I have verified that this issue occurs on the latest commit on
main.
- Dominant language
- Go
- Stars
- 5.9k
- Forks
- 637
- Avg merge
- 2d 1h
- Merged PRs (30d)
- 523
Getting set up
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from agent-substrate/substrate
-
Clean up the ko retry testsPossibly taken A pull request linked to this issue is open or already merged. Openarea/dev-infra area/reliability area/tests kind/cleanup
Difficulty 2/5 1-3 hours Newbie friendliness 66/100
agent-substrate/substrate#3461 ·
Maintainers usually reply within 1 day
-
egress: drop the empty-provider check in providerCredentialPossibly taken @cloudonly claimed this 3 days ago. Openarea/network kind/cleanup
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
agent-substrate/substrate#3428 ·
Maintainers usually reply within 1 day
-
kubectl-ate create actor-template -f <file> fails with useless errorPossibly taken @cloudonly claimed this 5 days ago. Openarea/cli kind/bug
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
agent-substrate/substrate#3406 ·
Maintainers usually reply within 1 day
-
k8s-credential-provider manifest is missing ate-otel-config envFrom, failing OTLP metric exports to [::1]:4317 / 127.0.0.1:4317 every 60sPossibly taken @ericcurtin claimed this 5 days ago. Openarea/dev-infra area/observability area/security kind/bug
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
agent-substrate/substrate#3282 ·
Maintainers usually reply within 1 day
-
filecachegc.runPass suppresses eviction logs when FreedBytes < target and consecutiveShortfalls > 3Possibly taken @ericcurtin claimed this 4 days ago. Openarea/node area/observability area/storage kind/bug
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
agent-substrate/substrate#3281 ·
Maintainers usually reply within 1 day
All issues in agent-substrate/substrate
Similar issues
-
[BUG] serviceenvupdater filter does not update KUBERNETES_SERVICE_HOST/PORT in init containersPossibly taken @RaphaelFakhri claimed this today. Open
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
openyurtio/openyurt#2845 ·
Maintainers usually reply within 7 days
-
enhancement good first issue help wanted
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
-
fix(scan): validate and normalize output format in validate-contractPossibly taken @WKr1shna claimed this today. Open
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
Maintainers usually reply within 1 day
-
bug good first issue help wanted
Difficulty 2/5 1-3 hours Newbie friendliness 70/100