Skip to content

Add actorIdentityToken as a SystemInfo volume data source - #1114

Draft
Max Thompson (thompsonmax) wants to merge 3 commits into
agent-substrate:mainfrom
thompsonmax:ait-source
Draft

Add actorIdentityToken as a SystemInfo volume data source#1114
Max Thompson (thompsonmax) wants to merge 3 commits into
agent-substrate:mainfrom
thompsonmax:ait-source

Conversation

@thompsonmax

Copy link
Copy Markdown
Collaborator

Part 2 of #802 (stacked on #941 — shows a combined diff until that merges. The new work is the single commit "Add actorIdentityToken as a SystemInfo volume data source").

Projects a signed JWT attesting the actor's identity, analogous to the serviceAccountToken projected volume source. ateapi mints on the resume path so the token is inherently activation-bound; claims mirror actoridentity.MintJWT so verifiers need one code path; the wire carries only minted bytes and atelet just writes them (find-paths-safe rename discipline). Re-minted on every Run/Restore — the e2e asserts a resume produces a fresh token — and snapshot-captured tokens expire quickly and can't be renewed elsewhere, per #802's leakage model. Fails closed without a signing pool. Live re-minting for long-running actors is deferred to the shared live-refresh mechanism (#932 PR 2).

A trustBundle data source projects the trust anchors of a named trust
bundle to a PEM file in the volume — inspired by the Kubernetes
clusterTrustBundle projected volume source, but source-neutral: the
template names a bundle, and where it is fetched from is a deployment
concern behind ateapi, not part of the API (agent-substrate#932).

Supported names are allowlisted in ateapi rather than the CRD schema,
so the eventual configurable backend registry widens them without an
API change. Initially the only supported bundle is egress-mitm.ate.dev
(the egress gateway CA bundle, agent-substrate#823), resolved from the
ClusterTrustBundle that atecontroller's EgressMITMTrustReconciler
(agent-substrate#946) derives from the egress-mitm-ca-pool Secret — the signer-linked
object name (egress-mitm.ate.dev:mitm:primary-bundle) is a backend
detail the allowlist mapping keeps out of the template API. Resolution
goes through an informer-backed lister and sanitizes the way kubelet
sanitizes projections: CERTIFICATE blocks only, deduplicated, headers
stripped (internal/pemutil).

The wire spec carries only {path, pem_bundle} resolved bytes; atelet
writes them at a stable path with the per-file temp+rename discipline
(find-paths safe) and never talks to any bundle backend. Actor start
fails closed, naming the bundle, when the name is not allowlisted, the
backend is unavailable in this deployment (certificates.k8s.io/v1beta1
is feature-gated; ateapi probes at startup and degrades instead of
hanging), or the bundle is missing, empty, or free of certificates.

The identity e2e drives the real chain end to end: it provisions the
egress-mitm-ca-pool Secret, waits for the reconciler to publish the
derived bundle, asserts the projected file in both CI lanes, then
rotates the pool and asserts a resumed actor observes the new contents
at the same path — writing the bundle directly is not an option, since
the reconciler reverts or deletes hand-written contents. Bundle
contents refresh on every Run/Restore; live refresh for running actors
is agent-substrate#932 PR 2. ateapi gains RBAC to read
clustertrustbundles.certificates.k8s.io.
…ed bundle

The identity suite verifies trust-anchor DELIVERY (pool -> reconciler ->
bundle -> resolution -> projection); this adds the CONSUMPTION half for
agent-substrate#871: an actor completes a TLS handshake with the sdsmint egress
gateway's per-SNI minted leaf using ONLY the anchors projected through
its trustBundle SystemInfo volume — on both sandbox classes, since
delivery differs per class (gVisor RO bind vs the micro-VM unified
virtio-fs share).

The probe gains /fetch?url=&roots=bundle|system, which GETs over the
actor's normal egress path with TLS roots from the projected bundle or
the image's system roots. The new egressmitm suite asserts the pair
that makes the result unambiguous: roots=bundle succeeds (and would
fail under a passthrough gateway, whose relayed public certificates the
bundle cannot validate — so a pass also certifies interception is on),
while roots=system fails certificate verification (the minted leaf
chains to no public CA; under passthrough it would succeed).

The sdsmint gateway variant replaces the passthrough gateway
cluster-wide, so CI deploys it as a separate step after both standard
lanes and runs only this suite against it — once per sandbox class,
gated by E2E_EGRESS_MITM. The suite ensures (never replaces) the CA
pool: sdsmintd signs with the pool mounted into the gateway pod, and
replacing it would race kubelet's Secret propagation into that mount.
Part 2 of agent-substrate#802: a data source that projects a signed JWT attesting the
actor's identity to a file — analogous to the Kubernetes
serviceAccountToken projected volume source:

  - actorIdentityToken:
      audience: some-verifier.example.com
      expirationSeconds: 3600   # default 3600, min 600, max 86400
      path: token

ateapi mints the token on the resume path, immediately before the spec
is sent to atelet, so the mint is inherently bound to the activation:
ateapi is placing this actor at this moment, no separate authorization
exchange needed. Claims mirror actoridentity.MintJWT (same issuer,
subject shape, and ate.dev claim object) so verifiers need one code
path; audience and TTL come from the template. The wire spec carries
only {path, token} minted bytes; atelet writes them at a stable path
with the per-file temp+rename discipline and never mints.

Tokens are re-minted on every Run/Restore: a resumed actor always
carries a token for its own, current activation (fresh iat/jti — the
e2e asserts a resume changes the token), and a token captured into a
snapshot expires quickly and cannot be renewed from elsewhere, per the
SystemInfo leakage model in agent-substrate#802. Live re-minting for actors that
outlive expirationSeconds is deliberately deferred to the shared
live-refresh mechanism (agent-substrate#932 PR 2 tracks the transport). Fails closed,
naming the flag, when the deployment runs ateapi without an actor JWT
signing pool.

The identity e2e projects a token in both CI lanes and asserts the
claims bind to the right actor and audience, the token is unexpired,
and a suspend/resume re-mints it while identity stays fixed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant