Skip to content

On GCS, snapshot transfers run as a dedicated service account and their tokens no longer run short near the end of each hour - #3093

Merged
Benjamin Elder (BenTheElder) merged 4 commits into
agent-substrate:mainfrom
ILLUM1N0X:snapshot-transfer-account
Oct 22, 2026
Merged

Benjamin Elder (BenTheElder) merged 4 commits into
agent-substrate:mainfrom
ILLUM1N0X:snapshot-transfer-account

Conversation

@ILLUM1N0X

Copy link
Copy Markdown
Collaborator

Fixes #2722

The credential exchange downscoped ate-api-server's own token, so GCS audit logs named ate-api-server for every node's snapshot transfer, and the metadata server's cached token left downscoped tokens short for the last twenty minutes of each hour. The exchange now impersonates a dedicated Google service account, ate-snapshot-transfer by default, and fetches a new token once less than 40 minutes are left. setup-gcp creates the account and its grants, and ate-setup passes it to the exchange.

Breaking change

GKE installs must run go run ./tools/setup-gcp create iam and set SNAPSHOT_TRANSFER_GSA_NAME before deploying. ate-setup refuses to deploy ate-api-server on GKE without it. With Cloud SQL, also rerun create cloudsql, so its account may create the transfer account's tokens.

Tested on a GKE cluster: the full e2e suite passed on gVisor workers, a micro-VM actor suspended and resumed, and GCS audit logs named the transfer account for every node snapshot transfer.

AI use: AI-assisted, I reviewed and tested it.

  • Tests pass
  • Docs updated

@ILLUM1N0X ILLUM1N0X added the breaking-change PRs with breaking changes label Oct 21, 2026
…e account

The credential exchange downscoped ate-api-server's own token, so GCS audit
logs named ate-api-server for every node's snapshot transfer, and the GKE
metadata server's cached token left downscoped tokens short for the last
twenty minutes of each hour. The control sidecar now impersonates the
service account --transfer-service-account names through the IAM
Credentials API, and fetches a new token once less than 40 minutes are
left, so every downscoped token starts with at least that long. While a
fetch fails, it keeps using the last token until that expires.
ate-setup takes the account's name from SNAPSHOT_TRANSFER_GSA_NAME, builds
its email in gcp.projectID, writes it to the ate-api-server ConfigMap, and
the manifest passes it to the control sidecar's --transfer-service-account.
A GKE deploy of ate-api-server refuses to run without it. kind stores
snapshots in S3 and does not use it.
@ILLUM1N0X
ILLUM1N0X force-pushed the snapshot-transfer-account branch from 5158770 to ca9795d Compare October 21, 2026 22:48
create iam and bootstrap create ate-snapshot-transfer, let ate-api-server's
Workload Identity principal create tokens for it, and grant it object admin
and bucket viewer on the snapshot bucket. create cloudsql gives its own
account the same token creator grant, since ate-api-server's pod runs as
that account once Cloud SQL is on. enable and bootstrap turn on the IAM and
IAM Credentials APIs. teardown.sh deletes the account.
@ILLUM1N0X
ILLUM1N0X force-pushed the snapshot-transfer-account branch from ca9795d to 8b87d67 Compare October 21, 2026 23:06
Comment thread tools/setup-gcp/cmd/transferaccount.go Resolved
Comment thread cmd/ate-setup/internal/config/config.go Resolved
Comment thread cmd/ate-setup/internal/steps/deploy.go Resolved
setup-gcp retries the token creator grant, since a service account created
moments earlier can be rejected as missing. deploy ate-api-server checks for
the transfer account before it touches the cluster, and kind ignores
SNAPSHOT_TRANSFER_GSA_NAME from the environment. The exchange falls back to
the last source token only while it outlasts the STS call.
@BenTheElder
Benjamin Elder (BenTheElder) added this pull request to the merge queue Oct 21, 2026
Merged via the queue into agent-substrate:main with commit e7cfad2 Oct 22, 2026
15 checks passed
Steven Shriver (stshrive) added a commit to stshrive/substrate-gke that referenced this pull request Oct 26, 2026
…e/substrate

Pulls in the dedicated snapshot transfer service account (`ate-snapshot-transfer`) that ate-api-server's credential exchange impersonates from [Agent Substrate PR 3093](agent-substrate/substrate#3093).

From Substrate v0.6.0, ate-setup refuses to deploy ate-api-server on GKE
unless SNAPSHOT_TRANSFER_GSA_NAME names this account, so v0.6.0 cannot be
installed without it.

Differences from upstream:

- The default name is a constant in setup-gcp (DefaultTransferGSAName),
  since this module cannot import upstream's internal/installdefaults.
- The service account policy is read at version 3 and written back with
  its conditional bindings (ai-on-gke#55), and the IAM client uses the test fakes'
  client options. createCloudSQLGSA uses the shared
  addServiceAccountBinding.
- bucketGrant is keyed by member. atelet's snapshot bucket roles stay
  behind --atelet-snapshot-access. The bucket policy retry, which waits for
  a new service account to propagate, uses the version 3 handle.
- teardown.sh goes through gcloud_step and bucket_gone, so a missing
  account or binding counts as already deleted and other failures are
  reported. cleanup-gcp's text names the account.
- Installer: SNAPSHOT_TRANSFER_GSA_NAME=ate-snapshot-transfer is in the
  shared environment, so bootstrap creates the account under the name
  ate-setup is given. Trees before v0.6.0 ignore it. The bootstrap
  checklist and dry-run lines have the new step. The project step's
  permission sample adds iam.serviceAccounts.create. The upgrade
  hand-over exports the variable and runs `create iam` before the new
  release is deployed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Steven Shriver (stshrive) added a commit to stshrive/substrate-gke that referenced this pull request Oct 26, 2026
…e/substrate

Pulls in the dedicated snapshot transfer service account (`ate-snapshot-transfer`) that ate-api-server's credential exchange impersonates from [Agent Substrate PR 3093](agent-substrate/substrate#3093).

From Substrate v0.6.0, ate-setup refuses to deploy ate-api-server on GKE
unless SNAPSHOT_TRANSFER_GSA_NAME names this account, so v0.6.0 cannot be
installed without it.

Differences from upstream:

- The default name is a constant in setup-gcp (DefaultTransferGSAName),
  since this module cannot import upstream's internal/installdefaults.
- The service account policy is read at version 3 and written back with
  its conditional bindings (ai-on-gke#55), and the IAM client uses the test fakes'
  client options. createCloudSQLGSA uses the shared
  addServiceAccountBinding.
- bucketGrant is keyed by member. atelet's snapshot bucket roles stay
  behind --atelet-snapshot-access. The bucket policy retry, which waits for
  a new service account to propagate, uses the version 3 handle.
- teardown.sh goes through gcloud_step and bucket_gone, so a missing
  account or binding counts as already deleted and other failures are
  reported. cleanup-gcp's text names the account.
- Installer: SNAPSHOT_TRANSFER_GSA_NAME=ate-snapshot-transfer is in the
  shared environment, so bootstrap creates the account under the name
  ate-setup is given. Trees before v0.6.0 ignore it. The bootstrap
  checklist and dry-run lines have the new step. The project step's
  permission sample adds iam.serviceAccounts.create. The upgrade
  hand-over exports the variable and runs `create iam` before the new
  release is deployed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking-change PRs with breaking changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

objectstore-plugin: mint the source credential itself so downscoped tokens are never short

3 participants