Summary
After a VM is provisioned, expose its SSH host public key(s) through the customer API, so a client can verify the host key out-of-band on the first SSH connect instead of falling back to blind trust-on-first-use (StrictHostKeyChecking=accept-new).
Motivation
On the very first SSH connection to a freshly provisioned VM, the host key is unknown, so automation has to accept whatever key is presented (accept-new) or disable checking entirely (=no). There is currently no channel in the customer API to learn the host key ahead of — or independently from — that first connection:
GET /api/v1/vm/{id} (ApiVmStatus) returns id / created / expires / mac_address / image / template / ssh_key / ip_assignments / status / auto_renewal_enabled — no host key. (ssh_key here is the customer's authorized key, not the VM's host key.)
- The only out-of-band channel today is the serial-console WebSocket (
GET /api/v1/vm/{id}/console): cloud-init prints the host-key fingerprint banner there at first boot. But it's a live stream with no scrollback, so it only helps if a client is attached at exactly the right moment during boot, and it yields free-text fingerprints rather than structured keys.
Net effect: secure, automated first-connect verification is impractical.
Proposed solution
Add the VM's SSH host public key(s) to the customer API, populated once the VM reaches running:
-
A read-only field on ApiVmStatus, e.g.:
(or a dedicated GET /api/v1/vm/{id}/host-keys endpoint).
-
null / empty until first boot completes; populated after capture.
-
Re-captured after re-install (which regenerates the host keys). Normal reconfig / IP changes do not change them — the shared vendor snippet already sets ssh_deletekeys: false — so the value is otherwise stable for the life of the VM.
Capture (host-side, no guest credentials required)
The API already SSHes to the Proxmox node and runs a post-provision worker (state creating → running). Once the VM is running, the node can obtain the guest's host key without logging into the guest, e.g.:
ssh-keyscan <vm-ip> from the Proxmox node — simple, image-agnostic; or
- qemu-guest-agent
guest-exec reading /etc/ssh/ssh_host_*_key.pub, if the guest agent is present — authoritative, since it reads the on-disk key rather than a presented one.
Only public keys are read or stored.
Why this shape
An alternative would be letting the client submit its own host key at order time (cloud-init ssh_keys: injection), which would give the fingerprint a-priori. Exposing the generated key is a better fit for this API:
- Public-key only — no need to accept or store customer private keys (secret at rest / extra attack surface).
- Additive, read-only — no changes to the order request structs, and no new per-VM user-data snippet mechanism (today there is only a single shared vendor snippet).
- Fits the existing post-boot worker and the
re-install lifecycle naturally.
- Matches how other providers surface host keys (Hetzner Cloud, DigitalOcean, EC2 all expose them post-boot; none accept a client-supplied host private key).
Alternative considered
Accept a client-supplied host key at order time and inject it via cloud-init. Strictly stronger trust (the fingerprint is known before the VM even boots, requiring zero trust in the boot), but it means ingesting and storing private-key material and adding a per-VM user-data path that doesn't exist today. Noted for completeness; the read-only expose above is preferred.
Summary
After a VM is provisioned, expose its SSH host public key(s) through the customer API, so a client can verify the host key out-of-band on the first SSH connect instead of falling back to blind trust-on-first-use (
StrictHostKeyChecking=accept-new).Motivation
On the very first SSH connection to a freshly provisioned VM, the host key is unknown, so automation has to accept whatever key is presented (
accept-new) or disable checking entirely (=no). There is currently no channel in the customer API to learn the host key ahead of — or independently from — that first connection:GET /api/v1/vm/{id}(ApiVmStatus) returnsid / created / expires / mac_address / image / template / ssh_key / ip_assignments / status / auto_renewal_enabled— no host key. (ssh_keyhere is the customer's authorized key, not the VM's host key.)GET /api/v1/vm/{id}/console): cloud-init prints the host-key fingerprint banner there at first boot. But it's a live stream with no scrollback, so it only helps if a client is attached at exactly the right moment during boot, and it yields free-text fingerprints rather than structured keys.Net effect: secure, automated first-connect verification is impractical.
Proposed solution
Add the VM's SSH host public key(s) to the customer API, populated once the VM reaches
running:A read-only field on
ApiVmStatus, e.g.:(or a dedicated
GET /api/v1/vm/{id}/host-keysendpoint).null/ empty until first boot completes; populated after capture.Re-captured after
re-install(which regenerates the host keys). Normal reconfig / IP changes do not change them — the shared vendor snippet already setsssh_deletekeys: false— so the value is otherwise stable for the life of the VM.Capture (host-side, no guest credentials required)
The API already SSHes to the Proxmox node and runs a post-provision worker (state
creating → running). Once the VM isrunning, the node can obtain the guest's host key without logging into the guest, e.g.:ssh-keyscan <vm-ip>from the Proxmox node — simple, image-agnostic; orguest-execreading/etc/ssh/ssh_host_*_key.pub, if the guest agent is present — authoritative, since it reads the on-disk key rather than a presented one.Only public keys are read or stored.
Why this shape
An alternative would be letting the client submit its own host key at order time (cloud-init
ssh_keys:injection), which would give the fingerprint a-priori. Exposing the generated key is a better fit for this API:re-installlifecycle naturally.Alternative considered
Accept a client-supplied host key at order time and inject it via cloud-init. Strictly stronger trust (the fingerprint is known before the VM even boots, requiring zero trust in the boot), but it means ingesting and storing private-key material and adding a per-VM user-data path that doesn't exist today. Noted for completeness; the read-only expose above is preferred.