Skip to content

CONNECT tunnel with access: full + tls: skip still causes 405 from upstream (WhatsApp/Baileys) #760

@novarii

Description

@novarii

Summary

WebSocket connections through the sandbox proxy fail with HTTP 405 from WhatsApp's Noise protocol handshake, even with access: full and tls: skip configured. The same connection succeeds when made directly from the VPS host (outside the sandbox).

Environment

  • Gateway: ghcr.io/nvidia/openshell/gateway:0.0.21
  • Sandbox binary: built Apr 2, 2026 (post WS-relay fix c6f3087)
  • VPS: Hetzner CPX32, Ubuntu 24.04, x86_64
  • Client: Baileys 7.0.0-rc.9 (WhatsApp Web client library)
  • Runtime: Node.js 22 inside sandbox, also tested with Bun

Policy (relevant section)

whatsapp:
  name: whatsapp
  endpoints:
    - host: "*.whatsapp.net"
      port: 443
      access: full
      tls: skip
    - host: "*.whatsapp.com"
      port: 443
      access: full
      tls: skip
  binaries:
    - path: /usr/local/bin/bun
    - path: /usr/local/bin/node

What works

Test Location Result
fetch('https://web.whatsapp.com/') via bun Inside sandbox 200 OK
Raw CONNECT to web.whatsapp.com:443 Inside sandbox 200 Connection Established
TLS cert check after CONNECT Inside sandbox Real DigiCert cert (not MITM'd)
Baileys full connection (QR generated) VPS host directly Works
WebSocket open via ws library Inside sandbox Opens successfully

What fails

Test Location Result
Baileys connection via proxy Inside sandbox (Node) WS opens → Noise handshake → 405
Baileys connection via proxy Inside sandbox (Bun) WS opens → Noise handshake → 405

Detailed failure sequence

[WhatsApp] connection.update connection=connecting
[WhatsApp] WS open                              # <-- WebSocket establishes
{"msg":"connected to WA"}                        # <-- Noise hello sent
{"msg":"not logged in, attempting registration..."}
# Then noise-handler.js:141 throws:
Connection Failure, data: { reason: "405", location: "atn" }

Analysis

  • The CONNECT tunnel returns 200 and the TLS certificate is WhatsApp's real DigiCert cert (not the proxy's), so TLS termination appears to be correctly skipped.
  • The WebSocket opens successfully through the tunnel.
  • The failure happens at the application protocol level (WhatsApp's Noise protocol handshake), after the WebSocket is established.
  • The exact same Baileys version, Node version, and WhatsApp account work when connecting directly from the VPS host without the proxy.
  • This suggests the proxy is modifying something subtle about the TCP stream or WebSocket frames that WhatsApp's server-side validation detects and rejects.

Reproduction

  1. Create a sandbox with the policy above
  2. Inside the sandbox, install baileys and attempt a connection through the proxy agent
  3. Observe that the WebSocket opens but the Noise handshake fails with 405
  4. Run the same code on the host directly — it succeeds and generates a QR code

Expected behavior

With access: full + tls: skip, the proxy should act as a raw TCP relay after CONNECT, passing bytes unmodified between the client and upstream. The Baileys Noise handshake should succeed identically to a direct connection.

Workaround needed

Is there a policy configuration that produces a completely transparent TCP tunnel with zero modification of the byte stream? Or is there a known interaction between the proxy's frame handling and binary WebSocket protocols?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions