Skip to content

feat(kms)!: refuse to serve without a key handover path - #1564

Merged
kvinwang merged 3 commits into
feat/kms-epochs-combinedfrom
feat/kms-handover-config-check
Nov 11, 2026
Merged

kvinwang merged 3 commits into
feat/kms-epochs-combinedfrom
feat/kms-handover-config-check

Conversation

@kvinwang

@kvinwang kvinwang commented Nov 11, 2026 •

Copy link
Copy Markdown
Collaborator

With the public handover off by default (#1552), other KMS nodes can only onboard through the admin listener over TLS. A KMS that has neither leaves new nodes unable to join, and nobody notices until a node actually tries.

  • Startup check: at startup, before onboarding or bootstrap, a KMS needs either core.onboard.public_key_handover = true or the admin listener with [core.admin.tls] key and certs. If neither is configured, it refuses to start and the error names the missing setting and both ways to fix it. There is no opt-out, auth_api = "dev" included.
  • Default kms.toml: the admin listener and its TLS are now on, using the RPC key and certificate. The admin listener refuses to start without a credential, so a KMS on the default config needs a token.
  • core.admin.auth_token_hash: a KMS running in a CVM with the local key provider can't receive secrets, and its app compose is public. This setting takes the token's hex SHA-256, so the compose carries only the hash. The hash is public there, so the token must be high-entropy random (openssl rand -hex 32).
  • Configs updated:
    • The dstack-app composes and entrypoint use the ADMIN_TOKEN_HASH that the deploy scripts already compute. The scripts publish the admin listener when KMS_ADMIN_ADDR is set.
    • dstackup generates a KMS admin token and renders its hash into the config.
    • tools/dev-stack.sh generates a local token.
    • The full-stack e2e KMS config, which is shared with KMS 0.5.8, turns the public handover on instead.
  • Docs: KMS README, deployment.md, upgrading-to-0.6.md, both KMS tutorials, the rc runbook and CHANGELOG.

Tests:

  • dstack-kms 92 passed, with unit tests covering every branch of the check, the default config and auth_token_hash. dstack-cli-core 31 and dstackup 17 also passed.
  • Clippy and fmt are clean.
  • Every KMS config the repo generates was parsed and run through the new check and the admin authenticator: both composes, the entrypoint, dev-stack, the e2e config and the dstackup config. All of them pass.

Real binary runs (debug dstack-kms, dev auth, attest_rpc_cert = false, onboard enabled, fresh cert dir each time):

Config Result
admin off, no public handover (with and without auto_bootstrap_domain) exits with the handover error, no files written
default admin, no token (with and without auto-bootstrap) exits with the admin credential error, no files written
[core.admin.tls] key = "" exits naming [core.admin.tls] key
admin + TLS + auth_token_hash enters onboarding; with auto-bootstrap, bootstraps and starts serving
admin off, public_key_handover = true enters onboarding

On the serving node, admin over HTTPS with the plaintext token behind auth_token_hash returns 200; a wrong or missing token returns 401.

With the public handover off by default, other KMS nodes can only onboard
through the admin listener over TLS. A KMS without either would strand
them, which shows up only when a node needs to join. Before serving, the
KMS now requires core.onboard.public_key_handover = true, or the admin
listener enabled with [core.admin.tls] key and certs, and names the
missing setting otherwise. There is no opt-out, dev auth included. The
onboarding phase is not checked.

The built-in kms.toml now enables the admin listener with TLS on the RPC
key and certificate. The admin listener fails closed, so a KMS on the
default config needs an admin token to start.

A KMS deployed as a CVM with the local key provider has no secret channel
for its config: the app compose is measured and public. The new
core.admin.auth_token_hash takes the token's hex SHA-256, so the compose
carries only the hash:

- dstack-app composes and entrypoint use the ADMIN_TOKEN_HASH the deploy
  scripts already compute, and the scripts publish the admin listener when
  KMS_ADMIN_ADDR is set.
- dstackup generates a KMS admin token next to the VMM one and renders its
  hash.
- tools/dev-stack.sh generates a local token.
- The full-stack e2e KMS, shared with KMS 0.5.8, keeps the admin listener
  off and serves the public handover.

Signed-off-by: Kevin Wang <wy721@qq.com>
Checked only before serving, a misconfigured node completed onboarding
or bootstrap, getting or generating root keys, and only then failed.
Check at startup instead, in every mode. Also document that an
auth_token_hash, public in an app compose, needs a high-entropy random
token: a weak one can be brute-forced offline from its hash.

Signed-off-by: Kevin Wang <wy721@qq.com>
With the default config and no admin token, the handover check passed (admin
enabled, TLS paths set) but the missing credential surfaced only when the
admin listener was built, after onboarding or bootstrap. Build the admin
listener setup right after the handover check, so such a node also fails
before it gets or generates root keys.

Signed-off-by: Kevin Wang <wy721@qq.com>
@kvinwang
kvinwang merged commit 9b87d9f into feat/kms-epochs-combined Nov 11, 2026
5 checks passed
@kvinwang
kvinwang deleted the feat/kms-handover-config-check branch November 11, 2026 13:37
kvinwang added a commit that referenced this pull request Nov 11, 2026
Since #1564 a KMS refuses to start without a key handover path, and the
default config enables the admin listener with TLS at /etc/kms/certs.

- The isolated KMS fixtures serve admin TLS with their own RPC key and
  certificate, so their admin URLs are https.
- tc-kms-onboard-005 no longer adds admin TLS before disabling the public
  handover; the fixture already has it.
- tc-kms-startup-001 calls the target's admin listener over https.
- Drop the public_key_handover line this branch added to the full-stack
  KMS config; #1564 added the same key there.

Signed-off-by: Kevin Wang <wy721@qq.com>
kvinwang added a commit that referenced this pull request Nov 11, 2026
Since #1564 a KMS refuses to start without a key handover path, and the
default config enables the admin listener with TLS at /etc/kms/certs.

- The isolated KMS fixtures serve admin TLS with their own RPC key and
  certificate, so their admin URLs are https.
- tc-kms-onboard-005 no longer adds admin TLS before disabling the public
  handover; the fixture already has it.
- tc-kms-startup-001 calls the target's admin listener over https.
- Drop the public_key_handover line this branch added to the full-stack
  KMS config; #1564 added the same key there.

Signed-off-by: Kevin Wang <wy721@qq.com>
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.

1 participant