Skip to content

Commit 4f4e657

Browse files
tee: enforce loopback-or-bearer worker API access
1 parent 6375b5b commit 4f4e657

8 files changed

Lines changed: 279 additions & 13 deletions

File tree

app/tee_client.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"encoding/json"
1212
"fmt"
1313
"net/http"
14+
"os"
1415
"strings"
1516
"sync"
1617
"time"
@@ -146,6 +147,7 @@ type RemoteTEEClient struct {
146147
endpoint string
147148
client *http.Client
148149
breaker *circuitbreaker.Breaker
150+
apiToken string
149151
}
150152

151153
// NewRemoteTEEClient creates a new HTTP-based TEE client.
@@ -160,6 +162,7 @@ func NewRemoteTEEClient(logger log.Logger, endpoint string) (*RemoteTEEClient, e
160162
logger: logger,
161163
endpoint: endpoint,
162164
breaker: circuitbreaker.NewDefault("tee_remote_execute"),
165+
apiToken: strings.TrimSpace(os.Getenv("AETHELRED_TEE_API_TOKEN")),
163166
client: httpclient.NewPooledClient(httpclient.PoolConfig{
164167
Timeout: 60 * time.Second,
165168
MaxIdleConns: 100,
@@ -200,6 +203,7 @@ func (c *RemoteTEEClient) Execute(ctx context.Context, request *TEEExecutionRequ
200203
return nil, fmt.Errorf("failed to create TEE request: %w", err)
201204
}
202205
httpReq.Header.Set("Content-Type", "application/json")
206+
c.applyAuth(httpReq)
203207

204208
resp, err := c.client.Do(httpReq)
205209
if err != nil {
@@ -257,6 +261,7 @@ func (c *RemoteTEEClient) GetCapabilities() *TEECapabilities {
257261
c.logger.Warn("Failed to create capabilities request", "error", err)
258262
return &TEECapabilities{Platform: "remote"}
259263
}
264+
c.applyAuth(httpReq)
260265

261266
resp, err := c.client.Do(httpReq)
262267
if err != nil {
@@ -314,6 +319,7 @@ func (c *RemoteTEEClient) IsHealthy(ctx context.Context) bool {
314319
}
315320
return false
316321
}
322+
c.applyAuth(httpReq)
317323
resp, err := c.client.Do(httpReq)
318324
if err != nil {
319325
if c.breaker != nil {
@@ -341,6 +347,13 @@ func (c *RemoteTEEClient) Close() error {
341347
return nil
342348
}
343349

350+
func (c *RemoteTEEClient) applyAuth(req *http.Request) {
351+
if c == nil || req == nil || c.apiToken == "" {
352+
return
353+
}
354+
req.Header.Set("Authorization", "Bearer "+c.apiToken)
355+
}
356+
344357
// Breaker exposes the circuit breaker for metrics.
345358
func (c *RemoteTEEClient) Breaker() *circuitbreaker.Breaker {
346359
return c.breaker

app/tee_client_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package app
22

33
import (
44
"context"
5+
"net/http"
6+
"net/http/httptest"
57
"strings"
68
"testing"
79
"time"
@@ -109,6 +111,32 @@ func TestRemoteTEEClient_IsHealthyRejectsBlockedEndpoint(t *testing.T) {
109111
}
110112
}
111113

114+
func TestRemoteTEEClient_IsHealthyUsesBearerTokenWhenConfigured(t *testing.T) {
115+
t.Setenv("AETHELRED_TEE_API_TOKEN", "worker-secret")
116+
117+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
118+
if r.URL.Path != "/health" {
119+
http.NotFound(w, r)
120+
return
121+
}
122+
if got := r.Header.Get("Authorization"); got != "Bearer worker-secret" {
123+
http.Error(w, "missing auth", http.StatusUnauthorized)
124+
return
125+
}
126+
w.WriteHeader(http.StatusOK)
127+
}))
128+
defer server.Close()
129+
130+
client, err := NewRemoteTEEClient(log.NewNopLogger(), server.URL)
131+
if err != nil {
132+
t.Fatalf("expected local test server endpoint to be accepted, got %v", err)
133+
}
134+
135+
if !client.IsHealthy(context.Background()) {
136+
t.Fatal("expected health probe to succeed with configured bearer token")
137+
}
138+
}
139+
112140
func bytes32(fill byte) []byte {
113141
out := make([]byte, 32)
114142
for i := range out {

docs/audits/EVIDENCE_INDEX.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,8 @@ The current evidence branch is a pre-audit hardening candidate on top of
391391
| Nitro parser fail-closed regression | `cargo test --manifest-path services/tee-worker/nitro-sdk/Cargo.toml --features attestation-evidence nitro` | `services/tee-worker/nitro-sdk/src/attestation/aws_nitro.rs` |
392392
| ARM attestation fail-closed regression | `cargo test --manifest-path services/tee-worker/nitro-sdk/Cargo.toml --features attestation-evidence arm` | `services/tee-worker/nitro-sdk/src/attestation/arm_trustzone.rs` |
393393
| Shared endpoint literal-IP regression | `go test ./x/verify/httputil` | `x/verify/httputil/httputil_test.go` |
394+
| Worker API auth boundary regression | `go test ./services/tee-worker/executor` | `services/tee-worker/executor/main_test.go` |
395+
| Remote TEE client auth regression | `go test ./app` | `app/tee_client_test.go` |
394396
| Seal verifier regression | `go test ./x/seal/keeper/...` | `x/seal/keeper/` |
395397
| Vault relay governance regression | `go test ./x/vault/keeper` | `x/vault/keeper/keeper_test.go` |
396398
| Sovereign access-control regression | `cargo test --manifest-path services/tee-worker/nitro-sdk/Cargo.toml --features full-sdk lib_full::sovereign::` | `services/tee-worker/nitro-sdk/src/sovereign/` |

docs/audits/REMEDIATION_REGISTER.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ part of the `main`-branch CLOSED statistics below.
219219
| HS-2026-04-16-52 | Medium | Worker / Nitro Parser Truthfulness | Fail closed in the Nitro attestation wrapper when the COSE/CBOR parsing backend is unavailable, instead of constructing a placeholder attestation document after only validating the outer COSE shell | `ramesh/protocol-hardening-sweep-20260416` / PR `#141` | `cargo test --manifest-path services/tee-worker/nitro-sdk/Cargo.toml --features attestation-evidence nitro` and `cargo check --manifest-path services/tee-worker/nitro-sdk/Cargo.toml --features attestation-evidence` |
220220
| HS-2026-04-16-53 | Medium | Worker / ARM Attestation Truthfulness | Fail closed in the ARM TrustZone / CCA wrapper when parser or signature-verification backends are unavailable, instead of fabricating placeholder token state or returning success from non-cryptographic signature paths | `ramesh/protocol-hardening-sweep-20260416` / PR `#141` | `cargo test --manifest-path services/tee-worker/nitro-sdk/Cargo.toml --features attestation-evidence arm` and `cargo check --manifest-path services/tee-worker/nitro-sdk/Cargo.toml --features attestation-evidence` |
221221
| HS-2026-04-16-54 | Medium | Verify / Shared Endpoint Literal IP Boundary | Tighten the shared endpoint validator so literal loopback, private, unspecified, and IPv6 non-routable addresses fail closed centrally instead of bypassing DNS-based SSRF checks when supplied directly as endpoint hosts | `ramesh/protocol-hardening-sweep-20260416` / PR `#141` | `go test ./x/verify/httputil`, `go test ./x/verify/...`, and `go test ./app ./x/pouw/keeper ./x/verify/... ./x/seal/keeper ./x/validator/keeper ./x/vault/keeper` |
222+
| HS-2026-04-16-55 | Medium | Worker / Execution API Boundary | Enforce loopback-or-bearer access on the TEE worker control plane, require an explicit API token for non-loopback exposure, and teach the remote Go TEE client to present that bearer token when configured instead of relying on network placement alone to protect `/health`, `/capabilities`, `/execute`, and `/verify` | `ramesh/protocol-hardening-sweep-20260416` / PR `#141` | `go test ./services/tee-worker/executor`, `go test ./app`, and `go test ./app ./x/pouw/keeper ./x/verify/... ./x/seal/keeper ./x/validator/keeper ./x/vault/keeper` |
222223

223224
See `docs/audits/protocol-hardening-sweep-2026-04-16.md` for the detailed
224225
scope and verification record.

docs/audits/STATUS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ March 30 verified baseline.
101101
| Branch / PR | Scope | Current Head | Status | Evidence |
102102
|---|---|---|---|---|
103103
| `ramesh/broad-review-cleanup-20260416` / `#139` | Repo review-surface cleanup for TypeScript SDK and VSCode tooling | Branch head in PR | In Review | PR checks + local `npm run typecheck`, `npm run compile`, `npm run lint`, `npm test` |
104-
| `ramesh/protocol-hardening-sweep-20260416` / `#141` | Bridge relayer persistence and authority, burn nonce extraction, fail-closed zk proof and TEE/VM verification, authenticated simulated keeper attestations, deterministic simulated EZKL verification, deterministic simulated keeper zk proof binding, secure-by-default TEE precompile registry wiring, seal verifier fail-closed defaults, stateful seal revocation approval/execution, removal of the ordinary privileged seal-revocation bypass, narrowed raw keeper revoke entrypoints, quorumed emergency seal revocation, explicit authority enforcement on governance-only vault keeper methods, authority-gated non-overwritable relay liveness challenges with audit logging, loopback-by-default admin consensus-audit endpoint enforcement with explicit bearer-token authorization for remote exposure, truthful simulated/degraded health reporting with `503` reserved for genuinely unhealthy runtime posture, redacted public health diagnostics with detailed output gated to loopback or explicit token authorization, loopback-by-default metrics endpoint enforcement with explicit bearer-token authorization for remote scraping, forwarded proxy traffic no longer inheriting unauthenticated loopback trust on admin, metrics, or detailed health routes, fail-closed remote TEE endpoint validation across startup and health probing, fail-closed readiness endpoint probing for configured verifier targets, fail-closed EZKL remote prover/verifier endpoint validation, fail-closed DCAP collateral and CRL endpoint validation, fail-closed mirrored worker Nitro remote endpoint validation, fail-closed drand relay endpoint validation with bounded local fallback decoding, fail-closed TEE worker backend proxy endpoint validation, fail-closed lightweight attestation collateral backends, fail-closed Nitro parser placeholder path, fail-closed ARM parser and signature placeholder paths, fail-closed shared endpoint literal-IP bypasses, fail-closed TEE startup gating for real remote verifier modes, explicit simulated Nitro client identity with schema-consistent quote/proof artifacts, fail-closed Nitro payload confidentiality handling, fail-closed seal signature verification semantics, fail-closed seal export provenance semantics, honest PQC backend availability gating, fail-closed enhanced seal signature semantics, fail-closed seal import provenance semantics, aligned simulated TEE platform taxonomy across app and PoUW validation, ABCI vote-extension request binding for validator identity and height, runtime enforcement of locked PoUW governance parameters, governance compatibility and compliance alignment with runtime lock policy, auditable trusted-measurement registry mutations with legacy Nitro index reconciliation, bonded-quorum trusted-measurement emergency revocation, aligned security audit and threat-model narratives with the hardened production governance posture, cryptographic mempool signature enforcement, fail-closed VM job-registry proof verification, owner-bound sovereign payload encryption and fail-closed sovereign access control, validator slashing economic-penalty enforcement, timelock-safe automation keeper ownership, fail-closed non-local deployment authority resolution, real hybrid secp256k1 + Dilithium signer/verification in the worker runtime, governance bootstrap, Cruzible deployability | Latest branch head in PR | In Review | `docs/audits/protocol-hardening-sweep-2026-04-16.md` |
104+
| `ramesh/protocol-hardening-sweep-20260416` / `#141` | Bridge relayer persistence and authority, burn nonce extraction, fail-closed zk proof and TEE/VM verification, authenticated simulated keeper attestations, deterministic simulated EZKL verification, deterministic simulated keeper zk proof binding, secure-by-default TEE precompile registry wiring, seal verifier fail-closed defaults, stateful seal revocation approval/execution, removal of the ordinary privileged seal-revocation bypass, narrowed raw keeper revoke entrypoints, quorumed emergency seal revocation, explicit authority enforcement on governance-only vault keeper methods, authority-gated non-overwritable relay liveness challenges with audit logging, loopback-by-default admin consensus-audit endpoint enforcement with explicit bearer-token authorization for remote exposure, truthful simulated/degraded health reporting with `503` reserved for genuinely unhealthy runtime posture, redacted public health diagnostics with detailed output gated to loopback or explicit token authorization, loopback-by-default metrics endpoint enforcement with explicit bearer-token authorization for remote scraping, forwarded proxy traffic no longer inheriting unauthenticated loopback trust on admin, metrics, or detailed health routes, fail-closed remote TEE endpoint validation across startup and health probing, fail-closed readiness endpoint probing for configured verifier targets, fail-closed EZKL remote prover/verifier endpoint validation, fail-closed DCAP collateral and CRL endpoint validation, fail-closed mirrored worker Nitro remote endpoint validation, fail-closed drand relay endpoint validation with bounded local fallback decoding, fail-closed TEE worker backend proxy endpoint validation, fail-closed lightweight attestation collateral backends, fail-closed Nitro parser placeholder path, fail-closed ARM parser and signature placeholder paths, fail-closed shared endpoint literal-IP bypasses, loopback-or-bearer TEE worker API enforcement with token-aware remote client requests, fail-closed TEE startup gating for real remote verifier modes, explicit simulated Nitro client identity with schema-consistent quote/proof artifacts, fail-closed Nitro payload confidentiality handling, fail-closed seal signature verification semantics, fail-closed seal export provenance semantics, honest PQC backend availability gating, fail-closed enhanced seal signature semantics, fail-closed seal import provenance semantics, aligned simulated TEE platform taxonomy across app and PoUW validation, ABCI vote-extension request binding for validator identity and height, runtime enforcement of locked PoUW governance parameters, governance compatibility and compliance alignment with runtime lock policy, auditable trusted-measurement registry mutations with legacy Nitro index reconciliation, bonded-quorum trusted-measurement emergency revocation, aligned security audit and threat-model narratives with the hardened production governance posture, cryptographic mempool signature enforcement, fail-closed VM job-registry proof verification, owner-bound sovereign payload encryption and fail-closed sovereign access control, validator slashing economic-penalty enforcement, timelock-safe automation keeper ownership, fail-closed non-local deployment authority resolution, real hybrid secp256k1 + Dilithium signer/verification in the worker runtime, governance bootstrap, Cruzible deployability | Latest branch head in PR | In Review | `docs/audits/protocol-hardening-sweep-2026-04-16.md` |
105105

106106
These branches are additive hardening tranches and do not reopen any finding
107107
that was already marked CLOSED on the March 30 baseline. They exist to reduce

docs/audits/protocol-hardening-sweep-2026-04-16.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -803,6 +803,26 @@ already merged.
803803
explicit localhost allowances plus blocked literal IPv4/IPv6 bypass cases,
804804
including non-canonical loopback and private IPv6 forms.
805805

806+
### 3zr. Worker API loopback-or-bearer hardening
807+
808+
- Tightened `services/tee-worker/executor/main.go`, where the TEE worker
809+
control plane still relied on ambient network placement instead of
810+
code-enforced caller authentication for `/health`, `/capabilities`,
811+
`/execute`, and `/verify`.
812+
- The worker now defaults to explicit loopback binding
813+
(`127.0.0.1:8545`), requires `AETHELRED_TEE_API_TOKEN` whenever it is bound
814+
beyond loopback, and enforces loopback-or-bearer authorization on all HTTP
815+
routes while refusing forwarded-header loopback trust.
816+
- Tightened `app/tee_client.go` so `RemoteTEEClient` automatically presents the
817+
configured `AETHELRED_TEE_API_TOKEN` as a bearer token on health,
818+
capabilities, and execution requests when remote exposure is intentionally
819+
enabled.
820+
- Added focused regressions in
821+
`services/tee-worker/executor/main_test.go` and `app/tee_client_test.go`
822+
covering remote request rejection without a bearer token, remote acceptance
823+
with the configured bearer token, and token-aware remote client health
824+
probing.
825+
806826
### 4. Cruzible deployability and reviewability
807827

808828
- Reduced `Cruzible.sol` deployed bytecode under the EIP-170 limit without
@@ -1034,6 +1054,10 @@ already merged.
10341054
IP forms centrally instead of relying on DNS resolution to catch those
10351055
inputs after the fact, which closes an SSRF-style bypass class across the
10361056
app, verify, and worker startup paths that reuse that helper.
1057+
- The TEE worker control plane no longer relies only on topology for safety.
1058+
Non-loopback exposure now requires an explicit API token, the worker enforces
1059+
loopback-or-bearer access on its HTTP routes, and the remote Go client
1060+
participates in that contract by attaching the configured bearer token.
10371061
- The built-in security audit and threat model now describe the same hardened
10381062
governance posture the runtime enforces, which removes an internal
10391063
claim-vs-control mismatch around consensus threshold policy, one-way

0 commit comments

Comments
 (0)