diff --git a/.env.example b/.env.example index b94479e..7ec20e7 100644 --- a/.env.example +++ b/.env.example @@ -8,7 +8,8 @@ MAPLE_PORT=8080 # Maple Backend Configuration # Production: https://enclave.trymaple.ai # Development: https://enclave.secretgpt.ai -# Local: http://localhost:3000 +# Local: use `just run-local`; plain HTTP mock attestation is compile-time +# gated and is never enabled in release, Docker, or embedded Maple builds. MAPLE_BACKEND_URL=https://enclave.trymaple.ai # Authentication diff --git a/CLAUDE.md b/CLAUDE.md index 4ea10ad..4d9acb3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -55,11 +55,40 @@ Maple Proxy is a lightweight OpenAI-compatible proxy server that forwards reques - Handles streaming responses for chat completions - Transforms responses to OpenAI format +### TEE attestation boundary + +TEE measurement authorization is owned by the OpenSecret SDK. Maple Proxy +should only call `perform_attestation_handshake`; do not add a proxy-specific +PCR allowlist or Sigstore/Rekor verifier. + +The intended SDK handshake authenticates the AWS Nitro attestation document, +compares the complete PCR0/PCR1/PCR2 tuple with a release snapshot embedded in +the SDK, and only then accepts the enclave public key and performs key exchange. +There is no runtime Sigstore/Rekor or release-metadata network lookup. At SDK +update time, the updater verifies the release manifest and Cosign bundle, +including the expected signing identity and Rekor evidence, before generating +the embedded snapshot. + +Mock attestation is available only through the explicitly named +`insecure-local-mock-attestation` Cargo feature. Keep it disabled in release, +Docker, and embedded Maple builds; only `just run-local` opts in. + +Rekor supplies tamper-evident transparency-log evidence for the signed release +statement used to generate that snapshot. It does not itself prove Nix +reproducibility, release freshness, rollback prevention, or revocation. + +This integration branch pins an exact reviewed SDK commit with default features +disabled. That staging commit deliberately contains an empty release snapshot, +so remote handshakes fail closed. Before release, replace the pin with a +snapshot-bearing reviewed commit or published crate; do not invent an +unpublished version or vendor the SDK here. + ### Request Flow 1. Client sends OpenAI-compatible request to proxy 2. Proxy extracts API key (from header or default config) -3. Creates OpenSecret client and performs TEE attestation +3. Creates an OpenSecret client and delegates TEE authentication, release + authorization, and key exchange to the SDK 4. Forwards request to Maple backend (enclave.trymaple.ai or configured URL) 5. Streams response back to client in OpenAI format diff --git a/Cargo.lock b/Cargo.lock index 4eddf78..97ff71c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1638,8 +1638,7 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "opensecret" version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7652ddb1661444ea0cbacacbc62c13f5073f1687a2e765e0195ebf29d3e394d" +source = "git+https://github.com/OpenSecretCloud/OpenSecret-SDK?rev=1bd7196043b26b3a2f8080534bd14031afcd5cc8#1bd7196043b26b3a2f8080534bd14031afcd5cc8" dependencies = [ "aes-gcm", "anyhow", diff --git a/Cargo.toml b/Cargo.toml index b282dcd..008d40a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,9 +27,15 @@ path = "src/lib.rs" name = "maple-proxy" path = "src/main.rs" +[features] +default = [] +# Explicit development-only opt-in. Release, Docker, and embedded Maple builds +# use default features and cannot accept mock attestation documents. +insecure-local-mock-attestation = ["opensecret/mock-attestation"] + [dependencies] # OpenSecret SDK -opensecret = "3.4.0" +opensecret = { git = "https://github.com/OpenSecretCloud/OpenSecret-SDK", rev = "1bd7196043b26b3a2f8080534bd14031afcd5cc8", default-features = false } # Web server axum = { version = "0.8.4", features = ["http2", "macros"] } diff --git a/README.md b/README.md index 3042a1c..c802a48 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,8 @@ Environment (TEE) processing. ## 🚀 Features - **OpenAI-Compatible Surface** - Models, chat completions, and embeddings endpoints -- **Secure TEE Processing** - All requests processed in secure enclaves +- **Attested TEE Transport** - The OpenSecret SDK establishes an attested, + encrypted channel before inference requests are forwarded - **Lossless Chat Parameters** - Provider-specific request fields pass through unchanged - **Streaming and Non-Streaming** - Supports both chat completion response modes - **Flexible Authentication** - Environment variables or per-request API keys @@ -44,7 +45,7 @@ Set environment variables or use command-line arguments: # Environment Variables export MAPLE_HOST=127.0.0.1 # Server host (default: 127.0.0.1) export MAPLE_PORT=8080 # Server port (default: 8080) -export MAPLE_BACKEND_URL=http://localhost:3000 # Maple backend URL (prod: https://enclave.trymaple.ai) +export MAPLE_BACKEND_URL=https://enclave.trymaple.ai # Maple backend URL export MAPLE_API_KEY=your-maple-api-key # Default API key (optional) export MAPLE_DEBUG=true # Enable debug logging export MAPLE_ENABLE_CORS=true # Enable CORS @@ -57,6 +58,10 @@ Or use CLI arguments: cargo run -- --host 0.0.0.0 --port 8080 --backend-url https://enclave.trymaple.ai ``` +For an unsigned local backend, use `just run-local`. That recipe alone enables +the explicitly named `insecure-local-mock-attestation` Cargo feature. Generic, +release, Docker, and embedded Maple builds leave the feature disabled. + ## 🛠️ Usage ### Using as a Binary @@ -453,9 +458,49 @@ cargo run ``` 1. **Client** makes standard OpenAI API calls to localhost -2. **Maple Proxy** handles authentication and TEE handshake -3. **Requests** are securely forwarded to Maple's TEE infrastructure -4. **Responses** are streamed back to the client in OpenAI format +2. **Maple Proxy** handles authentication and asks the OpenSecret SDK to + establish the TEE channel +3. **OpenSecret SDK** authenticates and authorizes the enclave before accepting + its key and completing key exchange +4. **Requests** are encrypted and forwarded to Maple's TEE infrastructure +5. **Responses** are streamed back to the client in OpenAI format + +### TEE release authorization + +The Sigstore/Rekor release-authorization work belongs in the OpenSecret SDK, +not in Maple Proxy. For each non-local backend, the SDK is expected to: + +1. verify the AWS Nitro attestation document, certificate chain, nonce, and + signature; +2. extract and validate the complete PCR0/PCR1/PCR2 measurement tuple; +3. compare that tuple with the release snapshot embedded in the SDK; and +4. accept the enclave public key and perform key exchange only after the tuple + is present in that snapshot. + +Maple Proxy continues to call `perform_attestation_handshake`; it neither +maintains a second PCR allowlist nor implements a separate Sigstore verifier. +Keeping this policy in the SDK gives every Rust SDK consumer the same +fail-closed authorization boundary before application data is sent. + +There is no Sigstore, Rekor, or other release-metadata network lookup during a +runtime handshake. At SDK update time, the release-snapshot updater verifies +the release manifest and Cosign bundle, including the expected signing identity +and Rekor evidence, before generating the embedded snapshot. Consumers then +review and pin the SDK release containing that generated snapshot. + +Sigstore makes a release statement and its signing identity tamper-evident in +an append-only transparency log. It does **not** prove that an artifact was +reproducibly built, and it does **not** make an old, previously authorized +release fresh. Reproducibility remains a separate Nix rebuild/compare property; +rollback prevention, revocation, or minimum-version policy must also be handled +separately. + +> **Integration status:** this branch pins the exact reviewed SDK integration +> commit with default features disabled. Its embedded release snapshot is +> intentionally empty, so remote handshakes fail closed. Update the pin to a +> reviewed snapshot-bearing commit or published crate after the first signed +> backend release; do not merge or publish this staging state as a working +> production proxy. ## 📝 License diff --git a/justfile b/justfile index 04251b8..b3e8888 100644 --- a/justfile +++ b/justfile @@ -61,7 +61,8 @@ run-with-backend url: # Run pointing to local backend run-local: - @just run-with-backend "http://localhost:3000" + @echo "🚧 Starting with development-only mock attestation enabled" + @bash -c 'set -a; source .env 2>/dev/null; set +a; MAPLE_BACKEND_URL=http://localhost:3000 cargo run --features insecure-local-mock-attestation' # Run pointing to production backend run-prod: