- Rust 93.1%
- Shell 3.6%
- Dockerfile 1.2%
- Makefile 1.2%
- HCL 0.9%
Broker pins B's binary by hash, gates A's access by token hash, and runs the binary in a jail (namespaces, chroot, nobody, seccomp socket allowlist), re-pushable after it exits. Includes rocket-sim, the demo CLI (plaintext/STEVE), an escape probe, QEMU jail tests, README. |
||
|---|---|---|
| broker | ||
| cli | ||
| examples | ||
| rocket-sim | ||
| scripts/qemu-jail-test | ||
| .dockerignore | ||
| .gitignore | ||
| Cargo.lock | ||
| Cargo.toml | ||
| caution.hcl | ||
| Containerfile | ||
| Makefile | ||
| README.md | ||
| rust-toolchain.toml | ||
Two-party confidential processing on Caution
Company B sells access to rocket-sim, a flight simulator it shows no one, running on its
own Caution cloud. Company A, B's customer, has confidential rocket designs and wants
simulated launches without B ever seeing the designs or the results.
The pitch: open the jail, not the code. A never sees B's binary. It reviews a small open
jail, proves with caution verify that the jail is what runs, and only A's token gets in.
How it works
- Two binaries. The Caution app is
broker(open, built by theContainerfile, measured). B's simulatorrocket-simis never in the image: B uploads it after boot, encrypted to the verified enclave. - Two pins.
caution.hclpins the SHA-256 of B's binary and the SHA-256 of A's token as broker args, so both are measured. The broker runs only the pinned binary, and relays only requests carrying A's token (inside STEVE). B itself gets401. - The jail. The simulator runs with an empty network, an empty chroot, as
nobody, under a seccomp filter (no vsock to the host). Any failed step and nothing runs. - The probe. The broker tries eleven ways out of the same jail and reports each one, so A can watch it hold.
sequenceDiagram
participant A as Company A (customer)
participant E as Enclave on B's Caution cloud
participant B as Company B (provider)
A->>B: 1. token hash (A keeps the token)
B->>E: 2. deploy the broker with both hashes pinned
Note over E: hash check, token check and jail are up. No simulator yet.
B-->>A: 3. repo, deploy commit, app URL
A->>E: 4. review the jail, caution verify
E-->>A: PCRs match the commit A read
B->>E: 5. verify, upload the secret simulator (encrypted)
Note over E: hash matches, simulator starts in the jail
A->>E: 6. run the probe
E-->>A: 11 of 11 escapes blocked
A->>E: 7. design and launches, with A's token (encrypted)
E-->>A: results, encrypted to A
B->>E: 8. try with its own token
E-->>B: 401
B->>E: plain HTTP to the host
E-->>B: 403 e2e_required
A->>E: 9. wipe its data
B->>E: tear the enclave down
What A reviews: broker/src/jail.rs (seven numbered steps, ~190 lines),
broker/src/seccomp.rs (~110 lines), and caution.hcl: A's own token hash, B's binary hash,
no --dev, no egress, only port 8080.
What's in the repo
Each part plays a role in the story: the open app A reviews, B's secret program, and the tools both companies use.
broker/ the open Caution app, the only thing Caution builds and measures
src/jail.rs the jail around B's program (A reviews)
src/seccomp.rs the system calls the jail refuses (A reviews)
src/payload.rs checks B's upload against the pinned hash
src/auth.rs checks A's token on every request
src/relay.rs passes A's requests to B's program
src/launch.rs starts B's program inside the jail
src/probe.rs the built-in escape probe
src/server.rs the endpoints: upload, relay, probe, health
rocket-sim/ B's secret program, a stand-in (a real B keeps this private)
cli/ demo, the one CLI both companies use
examples/ A's confidential design (falcon-hobby.json)
caution.hcl the enclave config: both hash pins, one port, no egress (A reviews)
Containerfile reproducible build of the broker, and nothing else
Makefile make sim, make test, make test-jail
scripts/qemu-jail-test/ runs the jail tests on the pinned enclave kernel under QEMU
Quickstart: the demo on Nitro
Tested end to end, both sides, on a Caution staging environment on AWS Nitro. make and demo
need x86_64 Linux (on macOS, an OrbStack ubuntu-amd64 VM). B needs a Caution account; both
need docker for caution verify.
0 · Both: get the CLI.
git clone https://git.caution.co/public/demo-conf-processing.git && cd demo-conf-processing
cargo build --release -p cli && export PATH=$PWD/target/release:$PATH
1 · A: make a token and send B only the printed hash.
demo token # writes a.token, prints its SHA-256
2 · B: pin both hashes and deploy from a deploy branch.
make sim # builds rocket-sim, prints its SHA-256
git switch -c deploy # put both hashes in caution.hcl (the REPLACE_WITH_... args)
caution login --username <you> && caution init
git add caution.hcl .caution/deployment.json && git commit -m "Pin rocket-sim and A's token"
git push caution deploy:main # ~7 min; ends with "Application: http://<app-ip>"
3 · B: publish the deploy commit to a repo A can read, then send A the repo and app URL.
git push <b-repo> deploy
4 · A: review and verify. A clones B's repo inside its own folder, so the token is at
../a.token.
git clone -b deploy <b-repo> company-b-app && cd company-b-app
less broker/src/jail.rs broker/src/seccomp.rs caution.hcl
sha256sum ../a.token # must equal --client-token-sha256 in caution.hcl
APP=http://<app-ip>
caution verify --attestation-url "$APP/attestation" # PASSED
d() { demo "$@" --enclave "$APP" --pcrs .caution/trusted_hashes.json --token-file ../a.token; }
5 · B: load the secret simulator.
caution verify && APP=http://<app-ip>
d() { demo "$@" --enclave "$APP" --pcrs .caution/trusted_hashes.json; }
d push target/x86_64-unknown-linux-musl/release/rocket-sim # loaded
6 · A: run the probe.
d probe # 11 lines, all BLOCKED
7 · A: run the simulation.
d design examples/falcon-hobby.json # d1
d launch d1 --wind 5 --out out/calm # Nominal: apogee 1662 m
d launch d1 --wind 15 --angle 80 --out out/gusty # Nominal: apogee 934 m
8 · B: try to look.
demo token --out b.token && d design examples/falcon-hobby.json --token-file b.token # 401
curl -s "$APP/app/designs" # 403 e2e_required
9 · Clean up.
d wipe # A: the simulator forgets every design and launch
caution apps destroy # B: stops the enclave
One program at a time: a push while it runs gets 409. If it exits or crashes, B pushes the
same binary again (no redeploy) and A re-sends its designs. A new binary means a new hash and a
redeploy. Never add --dev, an egress block, or an ingress rule for port 9000 to
caution.hcl.
Tests
make test # unit and integration tests
make test-jail # the jail on the pinned Nitro kernel under QEMU (needs qemu and docker)
Limits
- The simulator controls the size and timing of its responses, which the host can see: a slow covert channel. Padding would close most of it.
- The jail still trusts the enclave kernel. For production, run the simulator under gVisor and switch the seccomp denylist to an allowlist.
- One customer per enclave, and the simulator must accept a handed-in socket (
--listen-fd).