Skip to content

[feature-request] Expose VM SSH host public key(s) in the customer API after provisioning #154

Description

@akovalenko

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.:

    "host_ssh_keys": [
      {
        "type": "ssh-ed25519",
        "public_key": "AAAAC3NzaC1lZDI1NTE5AAAA...",
        "fingerprint_sha256": "SHA256:..."
      }
    ]

    (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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions