Skip to content

guest-agent: add the versioned v1 API - #1114

Closed
kvinwang wants to merge 7 commits into
feat/legacy-api-freezefrom
feat/guest-api-v1
Closed

guest-agent: add the versioned v1 API#1114
kvinwang wants to merge 7 commits into
feat/legacy-api-freezefrom
feat/guest-api-v1

Conversation

@kvinwang

Copy link
Copy Markdown
Collaborator

Stacked on #1113. Implements the versioned guest-agent API for 0.6.0.

Problem

The unversioned guest-agent API accreted design debt it cannot shed without breaking 0.5.x clients: GetTlsKey names a key what is really certificate issuance; GetKey's purpose and algorithm are accepted but do not domain-separate the derived key (the same 32 bytes serve both curves); Sign/Verify are pure computation that grant no capability GetKey does not already grant; Info returns a triple-nested JSON blob duplicating half its own top-level fields; and the signature-chain claim keccak256("{purpose}:{hex(pubkey)}") lets a caller-chosen purpose produce nearly arbitrary signed strings.

Fix

A versioned API selected by URL path only. Every surface is a frozen v0 fossil plus a v1:

Listener v0 (frozen = v0.5.11) v1
dstack.sock / (alias), /v0 /v1: IssueCert, GetKey, Attest, AttestGpu, Info, Version
external /prpc (alias), /prpc/v0 (Info, Version, GetAttestationForAppKey) /prpc/v1: Info, Version, AttestAppKey, Health
tappd.sock Tappd unversioned (predates v0)

v1 keeps only what needs the TEE: derivation, attestation, and cert issuance relayed to KMS SignCert.

  • IssueCert — honest name for what GetTlsKey did: issue a certificate, generating a fresh key as the integrated one-step mode.
  • GetKey(domain, algorithm) — new KDF with real domain separation: HKDF-SHA256(salt="dstack-guest-v1", IKM=app_root_k256, info=LP("dstack-guest-v1-key")||LP(algorithm)||LP(domain)), length-prefixed encoding, per-algorithm keys, v1-specific salt so no v0 input can reproduce a v1 key. Chain claim binds (algorithm, domain, raw pubkey) under a length-prefixed tag — structurally unforgeable from the v0 claim format (a v0 preimage's tail is lowercase hex; the v1 length prefix contains 0x00).
  • Attest (sole CVM attestation entry; GetQuote stays v0-only), AttestGpu, and a flat Info (identity + configuration; MRTD/RTMRs/event log deliberately excluded — they are attestation data and belong to Attest, quote-backed).
  • No Sign/Verify/EmitEvent/GpuInfo in v1. Never-released post-0.5.11 additions are removed from the unversioned surfaces, which now diff clean against v0.5.11.
  • Gateway health polling moves to /prpc/v1/Health.

Normative spec: docs/guest-api-v1.md — byte-level KDF and claim encodings, verification steps, migration contract (SDKs will ship ClientV0/ClientV1; v1 keys deliberately differ from v0 keys for the same input).

Verification

  • Acceptance diff vs v0.5.11 (comments stripped): services DstackGuest, Worker, Tappd identical; 16/19 messages byte-identical, 3 differ by reserved only.
  • Committed KDF/claim test vectors; a_v0_path_cannot_reproduce_a_v1_key feeds the v1 info byte string to the legacy KDF as a path and asserts divergence; forgery test asserts the structural hex/0x00 property, not just inequality.
  • Route tests: unversioned and /v0 return byte-identical bodies on both listeners; each mount serves only its own surface.
  • 118 guest-agent + 18 ra-tls + 289 gateway tests pass; fmt/clippy/workspace check clean.

/// legacy `info` is the caller's `path` verbatim -- so a caller that passed the
/// v1 `info` byte string as a v0 path would reproduce a v1 key. Different salts
/// close that by construction, whatever either side puts in `info`.
pub(crate) const KDF_SALT: &[u8] = b"dstack-guest-v1";
Comment thread dstack/ra-tls/src/kdf.rs Dismissed
Comment thread dstack/ra-tls/src/kdf.rs Dismissed
Comment thread dstack/ra-tls/src/kdf.rs Dismissed
Comment thread dstack/ra-tls/src/kdf.rs Dismissed
@kvinwang

Copy link
Copy Markdown
Collaborator Author

Reopening against next as a single combined PR (freeze + v1 + fixes + SDK).

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