Skip to content

Commit 2a16ce5

Browse files
committed
Merge remote-tracking branch 'origin/master' into e2e-host-kms-gateway-compose
# Conflicts: # dstack/local-key-provider/build/Dockerfile.key-provider
2 parents 4a5ae2d + 49723e4 commit 2a16ce5

210 files changed

Lines changed: 16998 additions & 8132 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/scripts/cargo-publish-idempotent.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ set -euo pipefail
1111

1212
crate=${1:?missing crate name}
1313

14-
if output=$(cargo publish --manifest-path dstack/Cargo.toml -p "$crate" 2>&1); then
14+
if output=$(cargo publish --manifest-path sdk/rust/Cargo.toml -p "$crate" 2>&1); then
1515
echo "$output"
1616
exit 0
1717
fi
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
# SPDX-FileCopyrightText: © 2026 Phala Network <dstack@phala.network>
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
name: Local Key Provider Release
6+
7+
on:
8+
workflow_dispatch:
9+
inputs:
10+
version:
11+
description: Release version without the local-key-provider-v prefix
12+
required: true
13+
type: string
14+
push:
15+
tags:
16+
- 'local-key-provider-v*'
17+
18+
permissions:
19+
attestations: write
20+
id-token: write
21+
contents: write
22+
packages: write
23+
24+
jobs:
25+
build-and-release:
26+
runs-on: ubuntu-latest
27+
steps:
28+
- name: Checkout repository
29+
uses: actions/checkout@v7
30+
31+
- name: Parse version from tag
32+
env:
33+
DISPATCH_VERSION: ${{ inputs.version }}
34+
run: |
35+
if [[ "$GITHUB_REF" == refs/tags/local-key-provider-v* ]]; then
36+
VERSION=${GITHUB_REF#refs/tags/local-key-provider-v}
37+
else
38+
VERSION=$DISPATCH_VERSION
39+
fi
40+
echo "VERSION=$VERSION" >> "$GITHUB_ENV"
41+
echo "Parsed version: $VERSION"
42+
43+
- name: Log in to Docker Hub
44+
uses: docker/login-action@v4
45+
with:
46+
username: ${{ vars.DOCKERHUB_USERNAME }}
47+
password: ${{ secrets.DOCKERHUB_TOKEN }}
48+
49+
- name: Set up Docker Buildx
50+
uses: docker/setup-buildx-action@v4
51+
52+
- name: Build and push Docker image
53+
id: build-and-push
54+
uses: docker/build-push-action@v7
55+
with:
56+
context: dstack
57+
file: dstack/local-key-provider/build/Dockerfile.key-provider
58+
push: true
59+
tags: ${{ vars.DOCKERHUB_ORG }}/local-key-provider:${{ env.VERSION }}
60+
platforms: linux/amd64
61+
provenance: false
62+
63+
- name: Generate artifact attestation
64+
uses: actions/attest-build-provenance@v4
65+
with:
66+
subject-name: "docker.io/${{ vars.DOCKERHUB_ORG }}/local-key-provider"
67+
subject-digest: ${{ steps.build-and-push.outputs.digest }}
68+
push-to-registry: true
69+
70+
- name: Export enclave measurements
71+
env:
72+
IMAGE_DIGEST: ${{ steps.build-and-push.outputs.digest }}
73+
IMAGE_NAME: ${{ vars.DOCKERHUB_ORG }}/local-key-provider
74+
run: |
75+
docker pull "${IMAGE_NAME}@${IMAGE_DIGEST}"
76+
docker run --rm \
77+
--entrypoint gramine-sgx-sigstruct-view \
78+
"${IMAGE_NAME}@${IMAGE_DIGEST}" \
79+
--output-format json \
80+
/dstack/local-key-provider/build/local-key-provider.sig \
81+
| tee local-key-provider.sig.json
82+
83+
MRENCLAVE=$(jq -er .mr_enclave local-key-provider.sig.json)
84+
MRSIGNER=$(jq -er .mr_signer local-key-provider.sig.json)
85+
echo "MRENCLAVE=$MRENCLAVE" >> "$GITHUB_ENV"
86+
echo "MRSIGNER=$MRSIGNER" >> "$GITHUB_ENV"
87+
88+
- name: GitHub Release
89+
uses: softprops/action-gh-release@v3
90+
with:
91+
name: "Local Key Provider Release v${{ env.VERSION }}"
92+
tag_name: local-key-provider-v${{ env.VERSION }}
93+
files: local-key-provider.sig.json
94+
body: |
95+
## Docker Image Information
96+
97+
**Image**: `docker.io/${{ vars.DOCKERHUB_ORG }}/local-key-provider:${{ env.VERSION }}`
98+
99+
**Digest (SHA256)**: `${{ steps.build-and-push.outputs.digest }}`
100+
101+
**MRENCLAVE**: `${{ env.MRENCLAVE }}`
102+
103+
**MRSIGNER**: `${{ env.MRSIGNER }}`
104+
105+
**Verification**: [Verify on Sigstore](https://search.sigstore.dev/?hash=${{ steps.build-and-push.outputs.digest }})
106+
107+
The enclave signing key is generated for each build. Pin the
108+
release's image digest and MRENCLAVE rather than expecting MRSIGNER
109+
to remain stable across releases.

.github/workflows/rust-sdk-release.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ jobs:
5353
pkg_version("sdk/rust/types/Cargo.toml")),
5454
("sdk/rust/Cargo.toml [package.version]",
5555
pkg_version("sdk/rust/Cargo.toml")),
56-
("dstack/Cargo.toml [workspace.dependencies.dstack-sdk-types.version]",
57-
ws_dep_version("dstack/Cargo.toml", "dstack-sdk-types")),
56+
("sdk/rust/Cargo.toml [workspace.dependencies.dstack-sdk-types.version]",
57+
ws_dep_version("sdk/rust/Cargo.toml", "dstack-sdk-types")),
5858
]
5959
6060
fail = False
@@ -64,7 +64,7 @@ jobs:
6464
print(f" {'OK ' if ok else 'BAD'} {label}: {got}")
6565
6666
if fail:
67-
print(f"\ntag is dstack-sdk-v{want}; bump all three to {want} before tagging.",
67+
print(f"\ntag is dstack-sdk-v{want}; bump SDK package versions to {want} before tagging.",
6868
file=sys.stderr)
6969
sys.exit(1)
7070
PY

.github/workflows/sdk.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@ jobs:
3434

3535
- name: Verify WASM compilation
3636
# Ensures SDK types can be used in smart contracts
37-
run: cargo check --manifest-path dstack/Cargo.toml --target=wasm32-unknown-unknown -p dstack-sdk-types
37+
run: cargo check --manifest-path sdk/rust/Cargo.toml --target=wasm32-unknown-unknown -p dstack-sdk-types
3838

3939
- name: Verify no_std compatibility
4040
run: |
41-
cargo test --manifest-path dstack/Cargo.toml -p dstack-sdk-types --test no_std_test --no-default-features
42-
cargo check --manifest-path dstack/Cargo.toml -p no_std_check --target thumbv6m-none-eabi
41+
cargo test --manifest-path sdk/rust/Cargo.toml -p dstack-sdk-types --test no_std_test --no-default-features
42+
cargo check --manifest-path sdk/rust/Cargo.toml -p no_std_check --target thumbv6m-none-eabi

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/dstack/target
2+
/dstack/local-key-provider/build/target
23
/certs
34
/build-config.sh
45
/build/*

CLAUDE.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -146,11 +146,12 @@ forge clean
146146

147147
### Logging and Error Messages
148148

149-
- **Never capitalize** the first letter of log messages and error messages
149+
- Start log messages and error messages with lowercase text
150+
- Preserve the conventional capitalization of identifiers and acronyms when they
151+
begin a message (for example, `RIM`, `OCSP`, or `HTTP`)
150152
- Example: `log::info!("starting server on port {}", port);`
151153
- Example: `anyhow::bail!("failed to connect to server");`
152-
153-
This rule is enforced in `.cursorrules`.
154+
- Example: `log::warn!("RIM upstream is unavailable");`
154155

155156
## Key Security Concepts
156157

CONTRIBUTING.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,47 @@ Use the narrowest owning directory. Fixture explanations and component
3232
READMEs should stay with their fixtures/components; general guides should be
3333
linked from the root README and live under `docs/`.
3434

35+
## Test a guest without TEE hardware
36+
37+
Development images include the extensible `dstack-tee-simulator`. Its default
38+
platform backend is TDX. On a VM launched with `no_tee`, that backend exposes
39+
the Linux interfaces used by the normal guest software:
40+
41+
- configfs-tsm quote generation under `/sys/kernel/config/tsm/report`;
42+
- RTMR extension files used by `tdx-attest`;
43+
- a CCEL boot-event fixture.
44+
45+
This keeps `dstack-prepare`, measurement, encrypted-storage setup, the guest
46+
agent, and attestation APIs on their production code paths. The generated quote
47+
has an intentionally invalid signature, so a production verifier or KMS must
48+
reject it.
49+
50+
The service uses the default TDX backend. For direct simulator development, the
51+
same selection can be made explicitly with `dstack-tee-simulator --platform
52+
tdx`. New TEE platforms are implemented as separate backends behind the shared
53+
simulator lifecycle.
54+
55+
Build or install a `dstack-dev-*` image, then deploy without KMS or gateway:
56+
57+
```bash
58+
dstack deploy ./docker-compose.yml \
59+
--name no-tee-dev \
60+
--image dstack-dev-VERSION \
61+
--no-kms \
62+
--no-tee
63+
```
64+
65+
To exercise persistent TPM-backed app keys as well, install `swtpm` on the VMM
66+
host and select `key_provider=tpm` in the VMM console (or pass `--key-provider
67+
tpm` in tooling that exposes the compose option). The VMM keeps the software
68+
TPM state in the VM work directory so that seal/unseal survives guest restarts.
69+
The software TPM is host-controlled and does not provide hardware isolation.
70+
71+
The simulator package is installed only in development images. This mode
72+
provides no hardware isolation and must never be used with production
73+
workloads or secrets. Real quote generation, hardware isolation, and KMS
74+
authorization still require TDX or another supported TEE.
75+
3576
## Commit Convention
3677

3778
This project uses [Conventional Commits](https://www.conventionalcommits.org/). Please format your commit messages as:

REUSE.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ SPDX-License-Identifier = "Apache-2.0"
5959
path = [
6060
"dstack/gateway/templates/wg.conf",
6161
"dstack/guest-agent/templates/metrics.tpl",
62-
"dstack/key-provider-build/sgx_default_qcnl.conf",
62+
"dstack/local-key-provider/build/sgx_default_qcnl.conf",
6363
]
6464
SPDX-FileCopyrightText = "Copyright (c) 2024-2025 The Project Contributors"
6565
SPDX-License-Identifier = "Apache-2.0"

docs/amd-sev-snp.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,15 @@ Verification is fail-closed and includes:
8282
5. the unified OS image identity, `sha256(sha256sum.txt)`, which must match
8383
`digest.txt` and the SNP measurement document.
8484

85+
For a GPU VM, the optional `MrConfigV3.gpu_policy_hash` field contains
86+
`SHA-256(JCS(requirements.gpu_policy))`. The signed SNP report binds the exact
87+
MrConfigV3 document through `HOST_DATA`, so a verifier can validate the report
88+
and document binding and then compare this field with the expected GPU policy
89+
digest. If the field is absent, this optional check is not asserted. This binds
90+
the GPU policy, but not the later `gpu-attestation` runtime event or the
91+
`GpuInfo` output: the current SEV-SNP path has no quote-bound runtime
92+
measurement register.
93+
8594
The verifier supports the AMD Milan, Genoa, and Turin KDS product families.
8695
Bergamo and Siena are handled through AMD's canonical Genoa KDS product path.
8796

docs/attestation-tdx.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,16 @@ The MR register values indicate the following:
2525
- RTMR0: OVMF records CVM's virtual hardware setup, including CPU count, memory size, and device configuration. While dstack uses fixed devices, CPU and memory specifications can vary. RTMR0 can be computed from these specifications.
2626
- RTMR1: OVMF records the Linux kernel measurement.
2727
- RTMR2: Linux kernel records kernel cmdline (including rootfs hash) and initrd measurements.
28-
- RTMR3: initrd records dstack App details, including compose hash, instance id, app id, and key provider.
28+
- RTMR3: initrd records dstack App details, including compose hash, GPU policy and attestation events, instance id, app id, and key provider.
2929

3030
MRTD, RTMR0, RTMR1, and RTMR2 can be pre-calculated from the built image (given CPU+RAM specifications). Compare these with the verified quote's MRs to confirm correct base image code execution.
3131

3232
RTMR3 differs as it contains runtime information like compose hash and instance id. Verify this by replaying the event log - if the calculated RTMR3 matches the quote's RTMR3, the event log information is valid. Then verify the compose hash, key provider, and other event log details match expectations.
3333

34+
For a GPU launch, `compose-hash` is followed by `gpu-policy-hash` and, after successful NVIDIA attestation and policy evaluation, `gpu-attestation`. The `gpu-policy-hash` payload is `SHA-256(JCS(requirements.gpu_policy))`, using `{}` when the field is omitted. The `gpu-attestation` payload is JSON containing the verified device count, CC/DevTools state, and `evidence_sha256`.
35+
36+
The guest-agent `GpuInfo` API returns the complete `nvattest` JSON captured during boot. It is not trustworthy by itself. After verifying the TDX quote and replaying the event log to RTMR3, hash the exact UTF-8 bytes of `GpuInfo.attestation` and require the result to equal the `gpu-attestation` event's `evidence_sha256`. See [GPU Security for AI Workloads](./security/security-model.md#gpu-security-for-ai-workloads) for the event schema, ordering, Rego example, and platform differences.
37+
3438
### 2.2. Determining expected MRs
3539
MRTD, RTMR0, RTMR1, and RTMR2 correspond to the image. dstack OS builds all related software from source.
3640
Build the exact image revision you intend to verify. See

0 commit comments

Comments
 (0)