Skip to content

Commit 7304668

Browse files
committed
trusted-workload-launcher: entrypoint.sh default, dstack.sock mount,
reference-value comparison Aggregator runtime now goes through the dstack SDK for KMS keys and TDX quotes against /var/run/dstack.sock, so the launcher's docs/examples need to (a) match the entry-script name workloads adopt and (b) make the dstack agent socket visible inside the workload container. The verifier-side review also asked for an explicit reference-values vs attestation-measurements comparison. Changes: - bin/trusted-workload-launcher: rename default entry script to entrypoint.sh and accept a new optional ENTRYPOINT_SCRIPT key (relative path, validated against absolute paths and '..'). Default mode still runs `bash <entry>` with no exec-bit requirement. Updated usage/help text, top comments, parse_config comment, and the `mode:` / `entry:` log lines. - tests/run-tests.sh: fixture's c3 now adds sub/entrypoint.sh and a sub/alt-entry.sh; added entrypoint_script_override and entrypoint_script_escape_rejected; updated default-mode marker assertions. 21/21 PASS. - examples/web-app.conf: now describes entrypoint.sh as the convention and documents ENTRYPOINT_SCRIPT as an optional override. - README.md (example): same convention; intro now lists `REPO_SUBDIR` / `ENTRYPOINT_SCRIPT` as the two path-selection trust- bearing fields. Added a `Mounting the dstack socket` subsection and added `/var/run/dstack.sock:/var/run/dstack.sock` to every compose snippet (local dev, production option A, and a note for option B). Workload-specific endpoint envs (e.g. DSTACK_LLM_ROUTER_DSTACK_ENDPOINT) are explicitly directed to CHILD_ENV_FILE, not to launcher config. - VERIFY.md: added a `Reference values to compare` block under step 2 with the concrete extraction shell snippet for `mrtd`, `rtmr0..2`, `compose-hash`, `os-image-hash`, `app-id`, and the launcher image digest inside the attested compose. Updated the quick-path step 1 to state explicitly that this comparison is what makes the attestation useful. Mentions of `REPO_SUBDIR` now also list `ENTRYPOINT_SCRIPT`. - README.md (root): unchanged in this commit (still the one-liner from b0478b6). No Rust-specific logic added to the launcher; it remains a build-system- agnostic bash launcher. Removed nothing about ALLOW_INSECURE / DSTACK_LLM_ROUTER_DSTACK_QUOTER_URL because neither was ever referenced here (confirmed via grep).
1 parent 331c3d0 commit 7304668

5 files changed

Lines changed: 250 additions & 105 deletions

File tree

trusted-workload-launcher/README.md

Lines changed: 50 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@ auditable image digest and an attested config that names the workload
1313
commit. Whether the workload at that commit is itself trustworthy is up to
1414
the auditor.
1515

16-
By convention, **the workload repo provides its own bash entry point at the
17-
fixed path `tee-launch.sh`** (default mode). This keeps install/build/run
18-
logic inside the workload repo, where it is covered by source provenance of
19-
the pinned `COMMIT_SHA` and is **not** a trust-bearing field in the
20-
launcher config. A verifier therefore audits two things: the launcher
21-
image's identity, and the `REPO_URL` + `COMMIT_SHA` pair (plus
22-
`REPO_SUBDIR` when used, since it selects *which* `tee-launch.sh` runs)
23-
in the attested config.
16+
By convention, **the workload repo provides its own bash entry point at
17+
`entrypoint.sh`** (default mode). This keeps install/build/run logic inside
18+
the workload repo, where it is covered by source provenance of the pinned
19+
`COMMIT_SHA` and is **not** a trust-bearing field in the launcher config.
20+
A verifier therefore audits two things: the launcher image's identity, and
21+
the `REPO_URL` + `COMMIT_SHA` pair (plus `REPO_SUBDIR` and
22+
`ENTRYPOINT_SCRIPT` when used, since each selects *which* script runs) in
23+
the attested config.
2424

2525
The launcher image is **generic**: its digest attests the launcher's
2626
implementation, not the workload. The workload identity comes from the
@@ -69,7 +69,7 @@ launcher config file ──► workload pin
6969
7070
──► workload running inside the TEE
7171
= workload repo at commit U,
72-
starting from its tee-launch.sh
72+
starting from its entrypoint.sh
7373
```
7474

7575
The published launcher image is a **generic** runner: the same image digest
@@ -115,7 +115,7 @@ trusted-workload-launcher <config-file>
115115
The launcher is a single bash script (`bin/trusted-workload-launcher`). It
116116
depends only on `bash`, `git`, and POSIX coreutils. It is **not** sourced
117117
and **does not source** the config. In default mode, the only bytes it
118-
executes are those of the workload repo's `tee-launch.sh` at the pinned
118+
executes are those of the workload repo's `entrypoint.sh` at the pinned
119119
`COMMIT_SHA`. In advanced mode (see below), it additionally executes the
120120
configured `INSTALL_CMD` / `RUN_CMD` via `bash -c`.
121121

@@ -139,36 +139,37 @@ no shell expansion in the parse step.
139139
| Key | Meaning |
140140
| --- | --- |
141141
| `REPO_SUBDIR` | Relative directory inside the repo to `cd` into before running the entry point or `RUN_CMD`. Must not be absolute and must not contain `..`. |
142-
| `CHILD_ENV_FILE` | Path to a separate env file. Each `KEY=VALUE` line is `export`ed into the environment seen by `tee-launch.sh` / `INSTALL_CMD` / `RUN_CMD`. The file is parsed line-by-line just like the main config (not sourced). |
143-
| `RUN_CMD` | **Advanced.** Shell command to exec instead of the default `tee-launch.sh`. Use only when the workload repo cannot host its own entry script. |
142+
| `ENTRYPOINT_SCRIPT` | Relative path (inside `REPO_SUBDIR` or repo root) to the bash entry script for default mode. Defaults to `entrypoint.sh`. Must not be absolute and must not contain `..`. Trust-bearing in default mode — like `REPO_SUBDIR`, it selects which script runs. |
143+
| `CHILD_ENV_FILE` | Path to a separate env file. Each `KEY=VALUE` line is `export`ed into the environment seen by `entrypoint.sh` / `INSTALL_CMD` / `RUN_CMD`. The file is parsed line-by-line just like the main config (not sourced). |
144+
| `RUN_CMD` | **Advanced.** Shell command to exec instead of the default `entrypoint.sh`. Use only when the workload repo cannot host its own entry script. |
144145
| `INSTALL_CMD` | **Advanced.** Shell command to run before `RUN_CMD`. Only valid alongside `RUN_CMD`. |
145146

146-
### Default mode: `tee-launch.sh` in the workload repo
147+
### Default mode: `entrypoint.sh` in the workload repo
147148

148149
Recommended for every workload you control. The workload repo provides a
149-
bash script at the fixed path `tee-launch.sh` (at the repo root, or at
150-
`REPO_SUBDIR/tee-launch.sh` if `REPO_SUBDIR` is set). The launcher runs it
151-
with `bash tee-launch.sh` after checkout — **no executable bit is
150+
bash script at the fixed path `entrypoint.sh` (at the repo root, or at
151+
`REPO_SUBDIR/entrypoint.sh` if `REPO_SUBDIR` is set). The launcher runs it
152+
with `bash entrypoint.sh` after checkout — **no executable bit is
152153
required**. All install/build/run logic lives in that script.
153154

154155
In this mode the trust-bearing config in the launcher's config file is
155156
`REPO_URL` + `COMMIT_SHA` (and `REPO_SUBDIR` if used, since it selects
156-
which `tee-launch.sh` runs). `WORK_DIR` is local plumbing — it names
157+
which `entrypoint.sh` runs). `WORK_DIR` is local plumbing — it names
157158
where on the in-TEE filesystem to keep the checkout — and is not
158159
trust-bearing. `CHILD_ENV_FILE` (and any env it provides) can change the
159160
script's runtime behavior but does not change the bytes that run; if the
160161
deployment uses it, audit it the same way you audit any other runtime
161162
configuration the deployment ships with.
162163

163-
Because `tee-launch.sh`'s bytes are pinned by `COMMIT_SHA` and stored in
164+
Because `entrypoint.sh`'s bytes are pinned by `COMMIT_SHA` and stored in
164165
the workload repo, they are covered by source provenance of the pinned
165166
commit. The verifier does not need to extract or audit any command string
166167
out of the launcher config.
167168

168169
### Advanced mode: explicit `RUN_CMD` / `INSTALL_CMD`
169170

170171
Use this when the workload repo cannot be modified to add a
171-
`tee-launch.sh` (e.g. you are pinning a third-party repo unchanged).
172+
`entrypoint.sh` (e.g. you are pinning a third-party repo unchanged).
172173
Setting `RUN_CMD` switches the launcher into advanced mode; if you need
173174
more than one command, set `INSTALL_CMD` to run before `RUN_CMD`. Each is
174175
a single-line shell string and the launcher does not implement multi-line
@@ -186,23 +187,23 @@ audited alongside `COMMIT_SHA`.
186187
* Will not: accept short SHAs. A truncated SHA could resolve ambiguously if
187188
the upstream history changes.
188189
* Will not: source the config or `eval` config values. In default mode the
189-
launcher executes `bash tee-launch.sh` from the pinned commit; in advanced
190+
launcher executes `bash entrypoint.sh` from the pinned commit; in advanced
190191
mode it executes `INSTALL_CMD` / `RUN_CMD` via `bash -c`. Nothing else
191192
from the config reaches a shell.
192193

193194
## Example
194195

195196
See [`examples/web-app.conf`](./examples/web-app.conf). Adapt `REPO_URL`,
196197
`COMMIT_SHA`, and (if you need it) `REPO_SUBDIR` for your workload, and
197-
make sure the workload repo has a `tee-launch.sh` at the pinned commit.
198+
make sure the workload repo has a `entrypoint.sh` at the pinned commit.
198199

199200
```sh
200201
./bin/trusted-workload-launcher ./examples/web-app.conf
201202
```
202203

203204
The launcher logs the resolved repo, commit, workdir, and selected mode at
204205
startup, then logs the verified `HEAD` after checkout, before handing
205-
control to `tee-launch.sh` (or `INSTALL_CMD` / `RUN_CMD` in advanced mode).
206+
control to `entrypoint.sh` (or `INSTALL_CMD` / `RUN_CMD` in advanced mode).
206207

207208
## Deploying with dstack
208209

@@ -211,11 +212,25 @@ so the dstack attestation binds to the exact launcher bytes you audited.
211212
How the config gets in front of the launcher depends on which binding from
212213
the trust model above you chose.
213214

215+
### Mounting the dstack socket
216+
217+
If the workload uses the dstack SDK (Rust, Python, etc.) to request KMS
218+
keys or TDX quotes — the recommended pattern for workloads that need
219+
in-TEE identity — the dstack agent's Unix socket must be visible inside
220+
the workload container. By default the SDK looks at `/var/run/dstack.sock`.
221+
Mount it in every compose snippet below; the snippets already include the
222+
mount.
223+
224+
If your workload talks to a non-default dstack endpoint, set
225+
`DSTACK_LLM_ROUTER_DSTACK_ENDPOINT` (or whatever endpoint variable your
226+
workload reads) via `CHILD_ENV_FILE` rather than baking it into the
227+
launcher config — it is runtime configuration, not a trust-bearing field.
228+
214229
### Local development (host bind-mount)
215230

216-
Convenient for iterating on the config. **Not for production**: the host can
217-
swap the mounted file at any time and nothing about that swap is reflected
218-
in the dstack attestation.
231+
Convenient for iterating on the config. **Not for production**: the host
232+
can swap the mounted file at any time and nothing about that swap is
233+
reflected in the dstack attestation.
219234

220235
```yaml
221236
services:
@@ -225,18 +240,19 @@ services:
225240
volumes:
226241
- ./web-app.conf:/etc/trusted-workload-launcher/config.conf:ro
227242
- workload-checkout:/var/lib/trusted-workload-launcher
243+
- /var/run/dstack.sock:/var/run/dstack.sock
228244
restart: unless-stopped
229245

230246
volumes:
231247
workload-checkout:
232248
```
233249
234-
### Production option A: attest the config via dstack compose
250+
### Production option A (recommended): attest the config via dstack compose
235251
236-
Inline the config inside the compose file (or reference a sibling file that
237-
participates in the compose hash). dstack measures the compose into the
238-
attested app config, so a verifier can compare the deployed compose against
239-
the one they audited:
252+
Inline the config inside the compose file (or reference a sibling file
253+
that participates in the compose hash). dstack measures the compose into
254+
the attested app config, so a verifier can compare the deployed compose
255+
against the one they audited:
240256
241257
```yaml
242258
services:
@@ -248,6 +264,7 @@ services:
248264
target: /etc/trusted-workload-launcher/config.conf
249265
volumes:
250266
- workload-checkout:/var/lib/trusted-workload-launcher
267+
- /var/run/dstack.sock:/var/run/dstack.sock
251268
restart: unless-stopped
252269

253270
configs:
@@ -277,8 +294,9 @@ CMD ["/etc/trusted-workload-launcher/config.conf"]
277294
```
278295

279296
Deploy that derived image (pinned by its own `@sha256:…`). The derived
280-
image digest now implies both the launcher and the workload pin, and the
281-
dstack attestation over the image digest is sufficient.
297+
image digest now implies both the launcher and the workload pin. Still
298+
mount `/var/run/dstack.sock` from the host into the workload container in
299+
the deploying compose if the workload uses the dstack SDK.
282300

283301
## Tests
284302

0 commit comments

Comments
 (0)