feat(sandbox): TLS + service CA + mTLS agent enrollment
BN
2d ago+3734+3734+0123456789012345678901234567890123456789-623-623-012345678901234567890123456789
Stacked on #619. One auth model for every deployment.
- Always TLS.
sandbox serveand the engine's listener serve TLS only; the loopback/no-token exemption is gone. The in-processInProcesspath is unchanged. - Service CA. On first start the service creates
<state>/sandbox-ca/{ca.pem,ca.key}(0600) and issues its server certificate with SANs fromPRELOOP_SANDBOX_TLS_SANS(default hostname, localhost, 127.0.0.1, ::1);PRELOOP_SANDBOX_TLS_CERT/_KEYoverride it.GET /sandbox/v1/ca.pemis public for pin verification. - Callers keep one bearer token (tenancy deferred), now always required:
PRELOOP_SANDBOX_TOKEN, or a generated<state>/sandbox-client-token. The CLI finds the token (--token/env/file) and CA (--ca/PRELOOP_SANDBOX_CA/local) automatically. - Join tokens.
preloop sandbox token create [--ttl 1h]→plj1.<id>.<secret>.<ca_sha256>; only a hash is stored (join_tokens, SQLite + Postgres migrations); single use, expiring. - Enrollment.
preloop sandbox agent --join https://… --token plj1…checks the server CA against the token's pin, thenProviders.Enrollsigns a 24 h client certificate from the agent's CSR and binds it to the provider name. The identity lives in<home>/state/sandbox-identity/(0600) and is reused on later runs. - mTLS on Providers. Every Providers RPC except
Enroll(and the agent blob endpoints) requires an enrolled client certificate chaining to the service CA; identity is the leaf fingerprint.Registeronly succeeds for the certificate's own name.PRELOOP_SANDBOX_PROVIDER_TOKENis removed. - Renewal / revocation. Agents renew after 2/3 of the lifetime (
Providers.RenewCertificate, old fingerprint valid until expiry) and hot-swap their identity.preloop sandbox provider rm <name>revokes.
Tests (tests/mtls.rs + units): enrollment, single-use and expired tokens, wrong CA pin (both sides), name mismatch, no client cert, renewal, revocation, caller token required; conformance's remote-agent test now runs over TLS with enrollment.
Lockfile: first-party edges only (hostname, rand, rcgen, time on preloop-sandbox; all already locked).
Summary by cubic
Applies one credential model to every Sandbox Service deployment: TLS transport, a caller bearer token, and mTLS provider identities obtained through join-token enrollment. Removes the loopback/no-token exemption and the shared PRELOOP_SANDBOX_PROVIDER_TOKEN.
TLS and caller auth
sandbox serveand the engine listener serve TLS only; the in-processInProcesspath is unchanged.- The service creates a CA and server certificate under
<state>/sandbox-ca/; onlyca.keyis mode 0600,ca.pemis public, andPRELOOP_SANDBOX_TLS_CERT/_KEYand_SANSoverride the server cert. - Callers always need a bearer token (
PRELOOP_SANDBOX_TOKENor a generated<state>/sandbox-client-token); the CLI discovers the token and CA automatically.
Provider enrollment and mTLS
preloop sandbox token create [--ttl]mints single-use, expiringplj1.<id>.<secret>.<ca_sha256>tokens; the service stores only a hash.preloop sandbox agent --joinverifies the server CA against the token's pin, thenEnrollsigns a 24h client certificate, bound to the provider name and persisted under<home>/state/sandbox-identity/.- Every
ProvidersRPC exceptEnrollrequires an enrolled client certificate;Registeronly succeeds for the certificate's own name, so existing providers must be re-enrolled. - Agents renew at 2/3 of the lifetime and hot-swap identity;
preloop sandbox provider rm <name>revokes. - Certificates carry Subject and Authority Key Identifiers,
basicConstraints, and matching EKUs so OpenSSL 3 verifiers accept the chain; an agent without--joinreuses its stored service URL, and a re-keyed CA surfaces a re-enroll error with 30s backoff instead of a handshake retry loop. - Provider host operations (probes, boots, transfers, host-tool sweeps) run under shared deadline bounds, so a wedged smolvm child or host tool fails instead of hanging.
Summary by CodeRabbit
- New Features
- Sandbox services now use HTTPS, with configurable certificate authorities and secure provider enrollment using single-use join tokens.
- Providers authenticate with certificates that can be renewed; agents can reuse their saved identity when reconnecting.
- Added commands to create join tokens and list or remove providers.
- Improvements
- Rejected TLS handshakes and RPC registrations are logged. Authentication and permission failures trigger a longer retry delay, with guidance to re-enroll when needed.