Credentialed Cloudflare-fronted upstreams have no working TLS mode: unconditional termination (#533) collides with Cloudflare rejecting the re-originated handshake
Dieses Issue hat noch niemand übernommen.
- Vorherrschende Sprache
- Rust
- Sterne
- 8.7k
- Forks
- 1.3k
- Ø Merge
- 2 T. 11 Std.
- Gemergte PRs (30 T.)
- 253
Beschreibung
Problem Statement
Hi,
Since #533 made TLS termination unconditional (auto-detect + terminate, with tls: skip as the explicit opt-out for raw tunneling), there is no configuration that lets the sandbox proxy reach an upstream that is both (a) fronted by Cloudflare, which rejects the proxy's re-originated TLS handshake, and (b) requires a credential that OpenShell resolves and injects at the egress boundary (an openshell:resolve:env:* placeholder).
Concretely, reaching opencode.ai (OpenCode Zen, a Cloudflare-fronted OpenAI-compatible inference API that requires a bearer token) from inside a NemoClaw/Hermes sandbox fails in every available policy configuration:
| Egress policy | Result |
|---|---|
Default / protocol: rest (auto-detect → terminate, per #533) |
Proxy terminates and re-originates TLS. Cloudflare resets the re-originated handshake. NET:FAIL, connection never completes. |
access: full + tls: skip |
Raw L4 passthrough succeeds — no reset, origin reachable. But because the proxy never sees plaintext, it cannot rewrite the openshell:resolve:env:OPENCODE_ZEN_API_KEY placeholder. The literal, unresolved placeholder string is sent as the bearer token. Upstream returns 401 Invalid API key. |
So today, for any endpoint matching this combination (Cloudflare or similarly TLS-fingerprint-sensitive front + boundary-resolved credential), there is no configuration that both reaches the origin and delivers a working credential. This was root-caused and reproduced against a live upstream in NVIDIA/NemoClaw#7323 (network-layer capture showing the reset under termination, and the raw placeholder under tls: skip) and NVIDIA/NemoClaw#7361 (a related but distinct client-side credential bug). Filing here at Dongni-Yang's request from that thread, since neither NemoClaw issue can be resolved without an OpenShell-side change.
Note this is a different symptom from #760: that report was about post-CONNECT stream/frame tampering under tls: skip with a verified-genuine origin certificate (WebSocket/Noise protocol breakage). Here, under tls: skip, the TCP/TLS layer works fine end-to-end — the problem is purely that credential injection requires plaintext visibility, which tls: skip by definition doesn't have.
Proposed Design
Two designs would address this; (b) is the one that fully closes the gap, (a) is a narrower, more mechanical addition:
(a) Explicit "CONNECT-passthrough with no credential resolution" mode. A policy value (e.g. tls: skip today, or a new explicit third state) that is documented as not attempting placeholder resolution, paired with a corresponding fail-closed check: if a policy in this mode targets a rule/binary that has a resolvable credential placeholder configured, refuse to apply the policy (or warn loudly) rather than silently sending the raw placeholder as if it were the credential. This wouldn't unblock credentialed Cloudflare-fronted endpoints, but it would turn today's silent 401 (leaking an internal placeholder string to the upstream) into a clear configuration-time error, which is a meaningful safety improvement on its own regardless of whether (b) ships.
(b) Make the proxy's re-originated TLS acceptable to Cloudflare (or similar fronting providers) while preserving termination for credential injection. The reset appears to be a TLS fingerprinting rejection (JA3/JA4-style, ALPN/ClientHello shape) rather than a protocol violation — a direct curl from the same host to the same origin at the same moment succeeds, so the origin and network path are otherwise healthy; only the proxy's re-issued handshake is rejected. This suggests the fix is about matching the re-originated ClientHello/ALPN/cipher-suite profile to what a normal Rustls/OpenSSL/BoringSSL client would present (or giving operators a way to select a specific TLS client fingerprint profile for re-origination), rather than a deeper architectural change. This is the only shape of fix that lets a Cloudflare-fronted endpoint receive both TLS connectivity and a rewritten credential simultaneously.
I don't have visibility into the proxy's TLS client implementation (OpenShell is a closed-source binary from this environment's vantage point, so I can't point to the exact crate/config responsible for the re-origination handshake's fingerprint) — this is offered as an external, black-box-observed root cause, not a code-level diagnosis. Happy to provide additional network captures (OCSF logs, packet-level detail if useful) against the live opencode.ai repro if that helps narrow down the TLS client behavior.
Alternatives Considered
- Rely on
tls: skipalone (status quo, documented in NemoClaw#7352): Works for credential-less upstreams (its original WhatsApp/Baileys use case) but is a dead end for anything requiring boundary-resolved credentials — it isn't an alternative to a real fix, just a workaround for a different problem shape. - Move credential injection to a layer other than the egress proxy (e.g., resolve the placeholder client-side inside the sandbox before the request leaves the process): rejected on the NemoClaw side as a deliberate security trade-off — the whole point of boundary-resolved credentials is that the sandboxed agent never sees the raw secret in-process. Reintroducing that would undo the security property #533 and the placeholder mechanism were built to provide.
- Have the client present its own genuine TLS handshake and inject the credential via a non-TLS mechanism (e.g., a side-channel header added at the HTTP layer only when a real passthrough is detected): more invasive, likely requires protocol-specific handling per upstream, and probably harder to build than fixing the fingerprint mismatch in (b).
Agent Investigation
Not applicable in the way the template envisions — I don't have access to the OpenShell source tree from the sandbox/host environment this was observed in (confirmed via find / -iname '*openshell*' -type d, which turns up only config/state directories, no source). Everything in this report is derived from external, black-box observation: OCSF egress logs, direct host-side curl comparisons against the same origin at the same time, and the policy-application behavior described in NemoClaw#7323/#7352/#7361. If someone with access to the proxy's TLS client code can confirm the specific ClientHello/ALPN mismatch, that would convert this from a black-box hypothesis into a concrete fix target.
Checklist
- I've reviewed existing issues and the architecture docs
- This is a design proposal, not a "please build this" request
Beitragsleitfaden
Erste Schritte
- Lies das ganze Issue und danach den Beitragsleitfaden des Projekts.
- Schreib ins Issue, dass du es übernimmst — das erspart doppelte Arbeit.
- Forke das Repository und arbeite in einem Branch.
- Öffne einen Pull Request, der die Issue-Nummer nennt.
Rechercherichtung
Beginne damit zu klären, ob der Umfang die Fail-Closed-Credential-Prüfung für tls: skip, die TLS-Re-Origination-Kompatibilität oder beides umfasst; das Issue stellt Alternativen statt eines festgelegten Designs dar. Reproduziere den Fall opencode.ai mit den aufgeführten Policies, vergleiche OCSF- und Beobachtungen auf Paketebene und untersuche die TLS-Client- und Credential-Resolution-Pfade des Proxys. Als abgeschlossen gilt, dass die gewählte Policy ein definiertes Verhalten und ein verifiziertes End-to-End-Ergebnis ohne das Senden ungelöster Platzhalter hat.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Bewertung
- Tech-Stack
- rust
- Bereich
- backend-api-design, networking, security
- Issue-Typ
- Feature
- Schwierigkeit
- 5/5
- Geschätzter Aufwand
- Über eine Woche
- Aktivitätsstatus
- Ruhig
- Klarheit
- Muss geklärt werden
- Anfängerfreundlichkeit
- 25/100