Skip to content

Per-actor, restore-time credential projection (SystemInfo credential data source) that never enters snapshots #2335

Description

@acherifi

Summary

Actor processes have no way to receive a Secret-backed value that is (a) delivered per actor at Run/Restore time and (b) never captured by the template's golden snapshot. Request: a SystemInfoVolumeSource data source that projects a credential (resolved through the existing ate-secret:// credential providers) into the actor's system-info volume, regenerated by atelet on every Run/Restore and excluded from every checkpoint.

This is one concrete instance of #1449 (golden snapshot state pollution, "currency broken" class) and builds on #802 (SystemInfo volumes), which already calls out the credential-leak risk.

Why the existing hooks are not enough (v0.4.0-alpha1 and main @ 296e329)

  • Env is template-only. Actor carries only actor_template, worker_selector and source_tag (ateapi.proto#L374); ResumeActorRequest only an ObjectRef (#L1582). The atelet RestoreRequest.spec is built from the ActorTemplate (workflow_resume.go#L632, env copied verbatim in workload_spec.go#L140).
  • Every template is golden-snapshotted, FULL scope, and every actor defaults to it. The template reconciler builds a golden actor for every template (template_reconciler.go#L172); golden commits are forced to FULL (workflow_suspend.go#L161); CreateActor without a source_tag freezes the golden tag (actor.go#L93). So any value placed in Container.env is in the golden actor's process memory, stored in object storage, and restored into every actor of the template. It cannot be rotated without a new template, and it is readable by anyone who can GetActorTemplate.
  • SystemInfo is the right lifecycle but has no secret source. SystemInfoVolumeSource is "regenerated by atelet on every Run/Restore" (ateapi.proto#L1306) but only offers actor_metadata and trust_bundle (#L1318).
  • Gateway header replacement covers HTTP bearer/API keys only. CredentialHeader (ateapi.proto#L630) solves keys sent as a header. It cannot serve values the process must read: AWS web-identity token files (AWS_WEB_IDENTITY_TOKEN_FILE for STS AssumeRoleWithWebIdentity, where the token is a form parameter), SigV4 signing keys, client certificates, git/ssh credentials, non-HTTP protocols.

Proposal

message SystemInfoDataSource {
  ActorMetadataDataSource actor_metadata = 1;
  TrustBundleDataSource   trust_bundle   = 2;
  // New: one file whose content is resolved at Run/Restore.
  CredentialDataSource    credential     = 3;
}

message CredentialDataSource {
  // ate-secret://<provider-class>/<provider-name>/<tail>, same providers and
  // atespace grants as CredentialHeader.credential_uri.
  string credential_uri = 1;
  // Alternatively: a Substrate-minted actor JWT (ActorJWTSource), refreshed on
  // Restore, which directly gives an IRSA-style web-identity token file.
  ActorJWTSource actor_jwt = 2;
  string path = 3;
}

Required semantics:

  1. Resolved by atelet (or ate-api on its behalf) on every Run and Restore, after the restore of guest memory, never at template creation.
  2. Never part of any checkpoint: excluded from FULL and DATA snapshot content, including golden snapshots. The golden actor itself should get an empty or absent file, so that nothing derived from it is frozen into the golden image (processes must read lazily, as for trust bundles).
  3. Rotation: a new value on the next Restore; optionally refreshed in place while running (like projected service-account tokens), with a documented maximum staleness.
  4. Same authorization model as CredentialHeader (namespace policy of the credential provider).

actor_jwt would unlock AWS/GCP/Azure workload-identity federation for actors without any static Secret: the actor's JWT is written to the file, the SDK exchanges it for cloud credentials, and the golden snapshot only ever contains the golden actor's (useless, short-lived, or absent) token.

Consumer

kagent (kagent-dev/kagent) compiles Agents into ActorTemplates. Its PR adding Harness env[].credentialRef has to restrict itself to gateway header injection for exactly the reasons above; this data source is what would let it support in-process credentials safely.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/nodearea/securitySecurity related issue/prkind/featureAn enhancement / feature request or implementation

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions