Skip to content

Mirror systemInfo volumes into the ActorTemplate substrate resource - #1134

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

Mirror systemInfo volumes into the ActorTemplate substrate resource#1134
Max Thompson (thompsonmax) wants to merge 3 commits into
agent-substrate:mainfrom
thompsonmax:si-substrate-resource

Conversation

@thompsonmax

Copy link
Copy Markdown
Collaborator

Part of #1131. Stacked on #941: the branch is based on its tip, so the diff includes #941's commits until it merges. The new work is the single "Mirror systemInfo volumes into the ActorTemplate substrate resource" commit; this rebases to a one-commit diff once #941 lands.

The substrate ActorTemplate's Volume union (#824, #1026) predates the systemInfo volume source: it only had durableDir and externalVolumeTemplate, so a template created through the substrate API could not carry SystemInfo projections. This PR adds:

  • SystemInfoVolumeSource with the actorMetadata and trustBundle data sources, mirrored from the CRD types in the plain-field union shape (Remove oneofs from ate-apiserver API #962) that Volume already uses ("SystemInfo" joins the type discriminator).
  • Store contract round-trip coverage: the shared fixture now carries a systemInfo volume through both the redis and postgres backends.

Not in this PR (tracked in #1131): validation and defaulting parity with the CRD's CEL rules, which belongs in the controlapi create/update handlers when ActorTemplate serving lands (#477), and conversion of the substrate resource in the actor-start path.

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 substrate
decision, not part of the API (agent-substrate#932).

Resolution happens on the node, per review: the wire spec carries
{name, path}, and atelet resolves the name against its supported-bundle
allowlist and reads the backing ClusterTrustBundle through an informer
at write time — the same watch dynamic refresh will hang off. Supported
names are allowlisted in code 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), backed by the ClusterTrustBundle
that atecontroller's EgressMITMTrustReconciler (agent-substrate#946) derives from the
egress-mitm-ca-pool Secret; the signer-linked object name is a backend
detail the allowlist mapping keeps out of the template API.

Sanitization matches kubelet's for projections — CERTIFICATE blocks
only, deduplicated, headers stripped, and the anchors deliberately
shuffled (kubelet-style) so consumers cannot grow a dependence on
order (internal/pemutil). Files land at stable paths with the per-file
temp+rename discipline from agent-substrate#803 (find-paths safe) and refresh on
every Run/Restore.

Fail-closed, naming the bundle: unsupported names, missing or unusable
bundles, and an unavailable backend all fail actor start. atelet's
startup probe is a one-shot authorized List (certificates.k8s.io/
v1beta1 is feature-gated): a genuinely unserved API degrades with a
warning, stale RBAC fails startup naming the missing rule rather than
hanging cache sync, and transient apiserver errors retry briefly then
fail startup. The atelet ClusterRole gains clustertrustbundles read
access.

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. e2e.DeployProbe ensures the bundle exists for
whatever suite deploys the shared probe fixture.
…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.
The substrate ActorTemplate (agent-substrate#824, aligned with the CRD in agent-substrate#1026) predates
the systemInfo volume source (agent-substrate#803, trustBundle in agent-substrate#941): its Volume union
had only durableDir and externalVolumeTemplate. Add systemInfo with the
actorMetadata and trustBundle data sources, following the plain-field union
shape (agent-substrate#962), and extend the store contract fixture so round-trips cover it.

The resource is schema+storage only today (no controlapi handlers), so the
CRD's CEL validation and defaulting move to the create/update path when
serving lands.
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