Skip to content

Commit c1278e9

Browse files
h4x3rotabclaude
andcommitted
tsm-shim: tighten README — lead with the point, drop the rest
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 059809a commit c1278e9

1 file changed

Lines changed: 24 additions & 62 deletions

File tree

tsm-shim/README.md

Lines changed: 24 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,18 @@
1-
# configfs-tsm shim (run unmodified TDX attestation binaries)
1+
# configfs-tsm shim
22

3-
Some programs request a TDX quote through the **standard Linux interfaces**
4-
`configfs-tsm` (`/sys/kernel/config/tsm/report/*`, with `inblob`/`outblob`) and a
5-
`/dev/tdx-guest` device — rather than through the dstack SDK / guest-agent socket.
6-
A stock dstack CVM doesn't expose those kernel interfaces to app containers, so
7-
such binaries fail out of the box.
3+
Some attestation binaries get their TDX quote through the kernel's `configfs-tsm`
4+
files (`/sys/kernel/config/tsm/report/*` — write `inblob`, read `outblob`) instead
5+
of the dstack SDK. dstack doesn't expose those files to containers, so they fail.
86

9-
This example ships a small **sidecar** that bridges the gap: it re-exposes the
10-
guest-agent's `GetQuote` RPC under the configfs-tsm file ABI. Your app writes
11-
`report_data` to `inblob` and reads the raw Intel DCAP TDX quote from `outblob`,
12-
exactly as against the kernel. No OS change, no FUSE, no privileged container; the
13-
quote is the genuine hardware quote and `report_data` is forwarded byte-for-byte.
7+
This sidecar bridges them: it serves `inblob`/`outblob` from a shared volume and
8+
forwards each request to the guest-agent's `GetQuote`. The quote is the real
9+
hardware quote (`report_data` passed through unchanged), so an unmodified binary
10+
works with only docker-compose changes — no OS change, no FUSE, no privileged
11+
container. CI publishes the image to `ghcr.io/dstack-tee/dstack-tsm-shim`.
1412

15-
The image is built and published to GHCR by
16-
[`build-tsm-shim.yml`](../.github/workflows/build-tsm-shim.yml):
17-
`ghcr.io/dstack-tee/dstack-tsm-shim`.
13+
## Use it
1814

19-
## Try it
20-
21-
```bash
22-
phala deploy -n tsm-shim-example -c docker-compose.yaml
23-
phala cvms logs <app_id> -c app
24-
```
25-
26-
Expected `app` log:
27-
28-
```
29-
quote length : 5010 bytes
30-
quote header : 0400 (a TDX v4 quote starts with 0400)
31-
report_data bound in quote: True
32-
PASS - unmodified configfs-tsm app got a real TDX quote via the shim
33-
```
34-
35-
## Adopt it in your app
36-
37-
Add the `tsm-shim` service and two volumes, then add the four lines marked `(+)`
38-
to your existing service:
15+
Add the sidecar, then point your app at it with the `(+)` lines:
3916

4017
```yaml
4118
services:
@@ -48,7 +25,6 @@ services:
4825

4926
my-app:
5027
image: your-app:latest
51-
# ... your existing config ...
5228
depends_on:
5329
tsm-shim:
5430
condition: service_healthy # (+) wait until the shim is ready
@@ -63,34 +39,20 @@ volumes:
6339
tsm-devstub: {}
6440
```
6541
66-
If your binary **hard-codes** `/sys/kernel/config/tsm/report`, mount the shared
67-
volume there instead of using `TSM_REPORT_PATH`:
68-
`- tsm-report:/sys/kernel/config/tsm/report`. For production, pin the image by
69-
digest (`ghcr.io/dstack-tee/dstack-tsm-shim@sha256:...`).
70-
71-
## How it works
42+
If your binary hard-codes `/sys/kernel/config/tsm/report`, mount the volume there
43+
instead of setting `TSM_REPORT_PATH`. For production, pin the image by digest.
7244

73-
`inblob`/`outblob` are **named pipes (FIFOs)** in a shared volume; a read of
74-
`outblob` blocks until the quote is ready (configfs-tsm's write-then-read
75-
contract). On an `inblob` write the shim `POST`s `/GetQuote` to
76-
`/var/run/dstack.sock` and writes the quote to `outblob`. The image reports
77-
healthy only once both FIFOs exist, so the app gates on `service_healthy`. An
78-
**empty `outblob` read means the quote failed** (the shim logs why).
45+
## Try the demo
7946

80-
`/dev/tdx-guest` can't be created in another container from a sidecar, so an
81-
empty volume is mounted there to satisfy the common "does the device exist?"
82-
check (dstack permits mounting under `/dev`).
47+
```bash
48+
phala deploy -n tsm-shim-example -c docker-compose.yaml
49+
phala cvms logs <app_id> -c app # expect PASS and a ~5 KB quote
50+
```
8351

84-
## Limitations
52+
## Good to know
8553

86-
- Covers the **configfs-tsm `inblob`/`outblob`** path (used by `go-configfs-tsm`,
87-
recent `libtdx-attest`, etc.).
88-
- Does **not** emulate the `/dev/tdx-guest` `TDX_CMD_GET_REPORT0` ioctl: it
89-
returns a raw, locally-MAC'd TDREPORT, which dstack doesn't expose and which
90-
can't be derived from a quote, so it isn't recoverable in userspace by any
91-
shim. Binaries that drive the device by ioctl are out of scope.
92-
- **Single in-flight requester** per shim — the shared `inblob`/`outblob` pair
93-
can't correlate concurrent callers (the kernel gives each opener its own
94-
`report/<entry>/`; this doesn't). Run one shim per app; the shim rejects an
95-
`inblob` write larger than 64 bytes (a sign of racing writers) rather than
96-
return an ambiguous quote.
54+
- Covers the configfs-tsm `inblob`/`outblob` path (go-configfs-tsm, recent
55+
libtdx-attest). It does **not** handle the `/dev/tdx-guest` ioctl, which needs a
56+
raw TDREPORT that dstack doesn't expose.
57+
- One request at a time, one shim per app — a shared `inblob`/`outblob` can't tell
58+
concurrent callers apart. An empty `outblob` read means the quote failed.

0 commit comments

Comments
 (0)