Skip to content

feat(proof-vm-agent): boot-check TLS SANs for the dedicated droplet, cert helper (Architecte follow-up 2/4) - #249

Closed
echobt wants to merge 1 commit into
cursor/proof-vm-crypto-provider-bootfrom
cursor/proof-vm-tls-san-dedicated-droplet
Closed

feat(proof-vm-agent): boot-check TLS SANs for the dedicated droplet, cert helper (Architecte follow-up 2/4)#249
echobt wants to merge 1 commit into
cursor/proof-vm-crypto-provider-bootfrom
cursor/proof-vm-tls-san-dedicated-droplet

Conversation

@echobt

@echobt echobt commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Summary

Architecte / Owner follow-up 2 of 4 (TLS SAN — hostname / VPC IP of the dedicated droplet). Stacked on #248 (same file, bins/proof-vm-orchestrator/src/main.rs); base retargets to main once #248 merges. Placement per the Owner lock: prod is a dedicated DO droplet g-8vcpu-32gb nyc1 on the VPC with nested /dev/kvm — not DO bare metal — never the CP droplet.

Problem. The CP's rustls client (proof-vm-fc) refuses a certificate without a SAN for the host in PROOF_VM_ORCHESTRATOR_URL. Staging minted a certificate for the docker gateway (172.18.0.1, what a colocated CP container reaches). That is worthless on the dedicated prod droplet, where the CP arrives on the VPC address / a hostname — and nothing in the repo generated certificates or checked them.

Agent (bins/proof-vm-orchestrator)

  • New --tls-sans / PROOF_VM_AGENT_TLS_SANS (comma-separated DNS names / IPs): every host the CP's URL may name for this agent. Default: the bind address when PROOF_VM_AGENT_BIND is a specific IP; required with a wildcard bind (0.0.0.0 / [::] say nothing about the host clients use — no default, no docker-gateway guess).
  • Boot check with the CP's own validator (webpki::EndEntityCert::verify_is_valid_for_subject_name, already in the graph via rustls): the leaf must be valid for every listed name. A miss exits 1: tls cert … has no SAN for 10.116.0.7 (it presents […]); the control plane's rustls client refuses it — regenerate with deploy/scripts/proof-vm-agent-tls.sh --san 10.116.0.7 (CN alone is never a name). Success logs listening (https); certificate covers every listed name with tls_sans.
  • Tests: names from the list / specific bind / wildcard refusal; rcgen certs — VPC IP + hostname accepted, docker-gateway-only and CN-only refused with the presented names and the helper command; the helper script's output is held to the same check and loaded as the listener would.

Helper (deploy/scripts/proof-vm-agent-tls.sh, openssl only, runs on the KVM host) — mints ca.pem/ca.key (once; reused after, --force re-mints) and tls.crt/tls.key under /etc/proof-vm with subjectAltName = union of --san …, PROOF_VM_AGENT_TLS_SANS and a specific PROOF_VM_AGENT_BIND host from the env file, hostname -f (--no-hostname / --hostname NAME), and the first IPv4 on --vpc-iface (default eth1, DO's VPC NIC; --no-vpc). --write-env records exactly the minted names as PROOF_VM_AGENT_TLS_SANS; --dry-run lists. Refuses an empty list; warns when every SAN is a docker bridge address (172.17–31.x — colocated staging only). Keys 0400, certs 0644, openssl verify before install.

Wire checkproof-vm-wire-check.sh agent translates curl's SAN / chain refusals into the exact fix (proof-vm-agent-tls.sh --san <url host>, or copy ca.pem to the CP).

Docs / env — runbook § TLS (knob, boot check, helper, staging colo vs dedicated droplet), § Install / § 1 / § sign-off updated; env examples (PROOF_VM_AGENT_TLS_SANS), systemd unit comment; placement wording aligned to the Owner lock in AGENTS.md item 7, deploy/AGENTS.md, docs/PROOF.md, docs/ARCHITECTURE.md, docs/COMPLETENESS.md, module docs ("dedicated DO metal preferred" → "dedicated g-8vcpu-32gb droplet, nested KVM, never the CP").

Fail-closed, minimal wire impact: no protocol change, no CP change; a host whose certificate does not cover its listed names stops at boot instead of serving a certificate the CP refuses.

How to verify

cargo test -p proof-vm-orchestrator-bin            # SAN rules + rcgen certs + helper round-trip

# helper + boot check, no KVM needed
deploy/scripts/proof-vm-agent-tls.sh --out-dir /tmp/pv --no-hostname --no-vpc --san 10.116.0.7 --san kvm-1.internal
echo t > /tmp/pv/token
target/debug/proof-vm-orchestrator --bind 0.0.0.0:18200 --token-file /tmp/pv/token \
  --tls-cert /tmp/pv/tls.crt --tls-key /tmp/pv/tls.key \
  --kernel-digest sha256:$(printf 'a%.0s' {1..64}) --sister-image-digest sha256:$(printf 'b%.0s' {1..64})
#   → exit 1: wildcard bind needs PROOF_VM_AGENT_TLS_SANS
PROOF_VM_AGENT_TLS_SANS=172.18.0.1 target/debug/proof-vm-orchestrator --bind 0.0.0.0:18200 …   # exit 1: no SAN for 172.18.0.1 (it presents […])
PROOF_VM_AGENT_TLS_SANS=10.116.0.7,kvm-1.internal target/debug/proof-vm-orchestrator --bind 0.0.0.0:18200 …
#   → "listening (https); certificate covers every listed name" tls_sans=["10.116.0.7","kvm-1.internal"]

On the dedicated droplet: deploy/scripts/proof-vm-agent-tls.sh --write-env (VPC IP autodetected on eth1 + hostname), restart the agent, copy ca.pem to the CP, proof-vm-wire-check.sh agent && … cp.

Greptile

  • Greptile has reviewed this PR; findings are fixed or answered
  • If the bot was silent, I commented @greptileai review

Test plan

  • cargo test --workspace
  • cargo fmt --all -- --check
  • cargo clippy --workspace --all-targets -- -D warnings, cargo deny check, xtask loc-cap / consensus-lint / spec-check / design-check / external-docs-check (compose-matrix assertion needs docker; not touched)
  • Manual: helper minted CA + leaf (openssl verify OK), boot check refused wildcard-without-list / missing SAN / CN-only and accepted the listed names; proof-vm-wire-check.sh test suite green with the new agent messages

Risk

Deploy only (host agent + operator script + docs). Operator action on upgrade: a wildcard PROOF_VM_AGENT_BIND now needs PROOF_VM_AGENT_TLS_SANS, and the certificate must cover those names — the agent exits 1 with the exact fix otherwise (that is the intended fail-closed behaviour; staging colo keeps working by listing the gateway address it already serves). No miner CVM measurement, signature domain, or emission impact.

Naming

I did not rename BASE_* environment variables, deployed host paths
(/opt/base, /run/base, …), GHCR baseintelligence/base package names, or
base-*-v1 cryptographic domain tags, unless this PR’s purpose is a coordinated
cutover documented in docs/NAMING.md.

Open in Web Open in Cursor 

The agent certificate must carry a SAN for every host the control plane's
PROOF_VM_ORCHESTRATOR_URL may name (the dedicated droplet's VPC IP, its
hostname) - staging shipped a certificate for the docker gateway only and
the CP's rustls client refused everything else. New PROOF_VM_AGENT_TLS_SANS
(default: a specific bind address; required with a wildcard bind) is checked
against the certificate at boot with webpki (the CP's own validator); a
missing SAN exits 1 naming the host, what the cert presents, and the fix.
deploy/scripts/proof-vm-agent-tls.sh mints CA + leaf from the same names
(--san, env, bind host, hostname -f, VPC IPv4 on eth1) and can write the env
line; the wire check translates curl SAN / chain refusals into that command.
Placement wording everywhere: production is a dedicated g-8vcpu-32gb droplet
on the VPC (nested KVM, no DO bare metal), never the CP droplet.
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.

2 participants