Credentialed Cloudflare-fronted upstreams have no working TLS mode: unconditional termination (#533) collides with Cloudflare rejecting the re-originated handshake
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 5/5
- Tempo stimato
- Più di una settimana
- Idoneità per principianti
- 25/100
- Tipo di issue
- Funzionalità
- Chiarezza
- Da chiarire
- Stato di attività
- Tranquilla
- Stack tecnologico
- rust
- Ambito
- backend-api-design, networking, security
Direzione di ricerca
Inizia determinando se l'ambito comprende il controllo delle credenziali fail-closed per tls: skip, la compatibilità con la TLS re-origination o entrambe; l'issue presenta alternative invece di un design definito. Riproduci il caso opencode.ai con le policy elencate, confronta le osservazioni OCSF e a livello di pacchetto e ispeziona i percorsi del client TLS e della risoluzione delle credenziali del proxy. Il lavoro è completo quando la policy scelta ha un comportamento definito e un risultato end-to-end verificato senza inviare placeholder irrisolti.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
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
- Lingua principale
- Rust
- Stelle
- 8.7k
- Fork
- 1.3k
- Merge medio
- 2g 7h
- PR unite (30g)
- 243
Guida per i contributori
Apri la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di NVIDIA/OpenShell
-
area:docs
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 88/100
-
state:triage-needed
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
-
area:cli state:validated
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
-
state:triage-needed
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 90/100
-
area:build spike state:review-ready state:stale
Difficoltà 2/5 Mezza giornata Idoneità per principianti 68/100
Tutte le issue di NVIDIA/OpenShell
Issue simili
-
risk:low runtime status:in-progress type:test
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 92/100
zeroclaw-labs/zeroclaw#11023 ·
-
good first issue refactor
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 84/100
EricSpencer00/Resilient#4835 · 1 commento ·
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 74/100
bisq-network/bisq-musig#204 ·
-
agent:ready documentation
Difficoltà 2/5 1-3 ore Idoneità per principianti 88/100
cesarferreira/stax#890 ·