Skip to content

docs: add encrypted env vars technical spec#506

Open
Leechael wants to merge 1 commit intomasterfrom
docs/encrypted-env-spec
Open

docs: add encrypted env vars technical spec#506
Leechael wants to merge 1 commit intomasterfrom
docs/encrypted-env-spec

Conversation

@Leechael
Copy link
Collaborator

Summary

  • Adds docs/encrypted-env-spec.md documenting the X25519 + AES-256-GCM scheme used to encrypt/decrypt application environment variables
  • Covers encryption public key source (KMS derivation chain, RPC interface, signature verification)
  • Documents the .appkeys.json file format and hex-encoded field conventions
  • Provides cross-language implementation guides for Rust, Go, Python, and TypeScript (both encrypt and decrypt)
  • Includes end-to-end flow diagram from deploy-time encryption through TEE-side decryption

Test plan

  • Review doc accuracy against source code (dstack-util/src/crypto.rs, kms/src/main_service.rs, SDK implementations)
  • Verify code samples compile/run correctly

Describes the X25519 + AES-256-GCM scheme used to protect app
environment variables, including key derivation from KMS, the
.appkeys.json file format, and cross-language encrypt/decrypt
implementation guides (Rust, Go, Python, TypeScript).
Copy link
Collaborator

@kvinwang kvinwang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: Missing Security Caveat — Encrypted Env Lacks Origin Authentication

The spec accurately documents the encryption/decryption protocol, but as a technical specification it has an obligation to clearly state the security boundary of this scheme. Currently it only describes confidentiality, without warning that there is no origin authentication.

The Problem

The encryption public key is derived from app_id, which is a public value (SHA256(app-compose.json)[0..20]). Anyone who knows the app_id can:

  1. Call VMM.GetAppEnvEncryptPubKey({ app_id }) to get the public key
  2. Encrypt arbitrary env vars with it
  3. Submit the encrypted payload to the VMM

The CVM will happily decrypt and use these forged env vars. The ECIES scheme guarantees confidentiality (only the TEE can decrypt), but provides zero authenticity — the CVM cannot distinguish between env vars from the legitimate developer and env vars from a malicious host operator or other attacker.

What's Already Documented Elsewhere

This limitation is already acknowledged in:

  • docs/security/security-model.md §"Environment variables need application-layer authentication"
  • docs/security/security-best-practices.md §"Authenticated envs and user_config"

But a standalone spec document should not rely on readers having seen those pages.

Suggested Addition

Add a Security Considerations section (before or after "End-to-End Flow"):

## Security Considerations

### Encryption provides confidentiality, not origin authentication

The encrypted env scheme guarantees that only the target CVM (via its TEE-held private
key) can decrypt the environment variables. However, it does **not** authenticate who
encrypted them. Since the encryption public key is derivable from the public `app_id`,
**anyone** can encrypt and submit env vars to the CVM.

This means a malicious host operator (or any party with VMM access) could replace
the encrypted env payload with their own. The CVM will decrypt and use the replacement
without detecting the substitution.

### Developer responsibility: application-layer authentication

Developers **must** implement origin verification for encrypted env vars. Recommended
approaches:

1. **`APP_LAUNCH_TOKEN` pattern** (built-in support): Include a secret token as an
   encrypted env var. Its SHA256 hash is embedded in `app-compose.json`
   (`launch_token_hash` field), which is measured into the TDX attestation. A prelaunch
   script verifies the token hash before the app starts. The VMM UI auto-generates
   this prelaunch script — developers only need to add the `APP_LAUNCH_TOKEN` env var.
   See [security-best-practices.md](./security/security-best-practices.md#authenticated-envs-and-user_config).

2. **Custom signing**: Sign the env var payload with a key known only to the developer,
   and verify the signature inside the app at startup.

3. **Embedded secret**: Include a shared secret (known only to the developer and the
   app) in the encrypted env vars, and verify its presence at runtime.

For full guidance, see [security-best-practices.md](./security/security-best-practices.md)
and [security-model.md](./security/security-model.md#environment-variables-need-application-layer-authentication).

Other Observations

  1. Spec accuracy: The protocol details (X25519 + AES-256-GCM, no KDF, ciphertext format, key derivation chain) are consistent with the source code in dstack-util/src/crypto.rs and kms/src/main_service.rs. The protobuf definitions including signature_v1 also match.

  2. Minor: HKDF salt not mentioned: The key derivation uses HKDF-SHA256 with salt b"RATLS" internally. This is an implementation detail but could be noted for completeness.

  3. user_config parallel: The best practices doc also notes that user_config has the same authenticity problem. Consider mentioning this in the spec if user_config interacts with encrypted env in any way.

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