Repository navigation
VM driver: trust a CA bundle for its image registry (for a local registry with a self-signed CA) #4681
Description
User Story
I use OpenShell's VM driver on a Mac to run local agents that must keep working when the internet is down. The gateway and the VM driver run as a dedicated, non-admin macOS service user under launchd, so a breakout lands outside my own account. The driver's bootstrap image comes from a registry on 127.0.0.1 with a self-signed CA, so no sandbox needs the internet to start. I need the driver to trust that one CA without changing what the rest of the Mac trusts.
Problem Statement
The VM driver verifies its image registry's TLS certificate only against the operating system's trust store. There is no driver setting for a registry CA, so a registry with a private or self-signed CA can be used only after that CA is trusted by the OS for the user the driver runs as.
Impact / Why This Matters
On macOS that is costly for a service user. Apple documents that per-user trust can be changed only in a GUI login session: "it is not possible to modify per-user trust settings when not running in a GUI environment (that is, when the user is not logged in via the login window)" (SecTrustSettingsSetTrustSettings, Special Considerations). Measured on macOS 27.0.1 with 0.1.5-dev.49+ga4c848adb:
- From another session,
security add-trusted-certfor the service user fails with "The authorization was denied since no user interaction was possible." It works only after someone logs in to the service user's desktop once and adds the trust there. - A native macOS role account (
sysadminctl -roleAccount: no password, no shell) can never log in and has no keychain, so it cannot trust the CA at all. - The only other option is admin-domain trust, which Apple describes as "Locally administered, system-wide": every account and daemon on the Mac then trusts a CA that exists for one process.
So today an offline install needs a manual desktop login as the service user (again whenever the registry certificate is renewed), or a Mac-wide trust change. Both are hard to automate and both widen trust beyond the driver.
Proposed Design
A driver setting naming a PEM bundle of extra roots, trusted only for the driver's registry connections, the way proxy_ca_bundle already names a CA bundle for the guest proxy:
[openshell.drivers.vm]
bootstrap_image = "127.0.0.1:18086/openshell/bootstrap@sha256:…"
registry_ca_bundle = "/path/to/registry-ca.pem" # extra roots for the image registry; the OS store still applies- Unset: today's behavior, unchanged.
- Set: the driver adds these roots to its registry client's verification, alongside the platform store. Nothing outside the driver process trusts them.
- An unreadable or invalid file fails driver startup with a clear error naming the setting, rather than falling back silently.
Acceptance Criteria
-
With
registry_ca_bundleset to a self-signed CA, the VM driver pulls the bootstrap image from a registry using a certificatethat CA signed, while the CA is **not** in any OS trust store (on macOS: absent from the user, admin and system domains). -
With the setting unset, that same pull fails certificate verification, as today.
-
Other TLS connections on the host are unaffected: the CA is trusted only by the driver's registry client.
-
A missing or malformed bundle fails driver startup with an error that names
registry_ca_bundle. -
The setting is documented with the other
[openshell.drivers.vm]options.
Alternatives Considered
- Per-user OS trust for the service user: works, but needs a desktop login as that user (Apple's documented rule above) and again at each certificate renewal; impossible for a role account.
- Admin-domain (system-wide) trust: no login, but every account and daemon then trusts the CA.
- Plain HTTP to the loopback registry: avoids TLS, but drops the certificate check the registry path has today.
- No registry at all for the bootstrap image (fix(vm): boot a digest-pinned image from the cache without a registry call #4582's cache path, or a local-file bootstrap as asked on VM driver authenticates with the registry on every boot, even for a cached digest-pinned image #4581): would remove the need entirely, and I'd still welcome it; this setting is the smaller step that helps any private registry, on Linux too.
- Patching the driver locally: means building and signing our own driver binary, which we want to avoid.
Agent Investigation
I investigated this with an AI coding agent, reading the source at a4c848adb. The driver builds its registry client in registry_client() (crates/openshell-driver-vm/src/driver.rs) with only a platform resolver, leaving every TLS field at its default. The oci-client it uses (0.16.1 in Cargo.lock) already has ClientConfig::extra_root_certificates ("extra root
certificate to trust. This can be used to connect to servers using self-signed certificates") and tls_certs_only, and its reqwest 0.13.2 passes extra roots to rustls_platform_verifier::Verifier::new_with_extra_roots, so the platform store keeps
working beside them. Reading only; nothing was built or run against a patched driver.
Checklist
- I've reviewed existing issues and the published docs
- This is a design proposal, not a "please build this" request
Activity