Credentialed Cloudflare-fronted upstreams have no working TLS mode: unconditional termination (#533) collides with Cloudflare rejecting the re-originated handshake
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 5/5
- Tiempo estimado
- Más de una semana
- Aptitud para principiantes
- 25/100
- Tipo de issue
- Nueva funcionalidad
- Claridad
- Necesita aclaración
- Estado de actividad
- Tranquilo
- Stack tecnológico
- rust
- Área
- backend-api-design, networking, security
Línea de trabajo
Empieza por resolver si el alcance incluye la comprobación de credenciales fail-closed para tls: skip, la compatibilidad con TLS re-origination o ambas; el issue presenta alternativas en lugar de un diseño establecido. Reproduce el caso de opencode.ai con las políticas indicadas, compara las observaciones de OCSF y a nivel de paquetes, e inspecciona las rutas del cliente TLS y de resolución de credenciales del proxy. Se considera terminado cuando la política elegida tiene un comportamiento definido y un resultado end-to-end verificado sin enviar placeholders sin resolver.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
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
- Lenguaje dominante
- Rust
- Estrellas
- 8.7k
- Forks
- 1.3k
- Merge medio
- 2 d 7 h
- PR fusionados (30 d)
- 243
Guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de NVIDIA/OpenShell
-
area:docs
Dificultad 1/5 Menos de una hora Aptitud para principiantes 88/100
-
state:triage-needed
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
-
area:cli state:validated
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
-
state:triage-needed
Dificultad 1/5 Menos de una hora Aptitud para principiantes 90/100
-
area:build spike state:review-ready state:stale
Dificultad 2/5 Medio día Aptitud para principiantes 68/100
Todos los issues de NVIDIA/OpenShell
Issues similares
-
risk:low runtime status:in-progress type:test
Dificultad 1/5 Menos de una hora Aptitud para principiantes 92/100
zeroclaw-labs/zeroclaw#11023 ·
-
good first issue refactor
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 86/100
kwakseongjae/auto-hwp#319 ·
-
area:cli bug filter-quality good first issue priority:medium
Dificultad 2/5 1-3 horas Aptitud para principiantes 84/100
-
Dificultad 1/5 Menos de una hora Aptitud para principiantes 72/100
bevyengine/bevy#25861 ·