zoraxy icon indicating copy to clipboard operation
zoraxy copied to clipboard

[HELP] CleanSlate deployment errors with ZORAXY and websocket connections

Open Mega61 opened this issue 11 months ago • 1 comments

What happened? I am triying to proxy a new service that has a websocket. It is called cleanslate. Ive managed to get it working but the websocket connection, which should be handled by ZORAXY does not work. I get this error

[websocket] [system:error] Couldn't dial to remote backend url ws://graphql-server:8080/graphql: websocket: bad handshake

Describe what have you tried Ive configured my ZORAXY like this

Image

ive used a virtual directory to pass the AUTH, which works fine, and the graphql server for the websocket.

The error I am seeing is ZORAXY passing the connection to the graphql container. If I delete that rule from the virtual directory it tries connecting to the client container and gives the same error

[websocket] [system:error] Couldn't dial to remote backend url ws://client:3000/v1/graphql: websocket: bad handshake

Ive tried adding to the websocket rule the /v1 and /v1/graphql terminations, but I get the same error.

From the graphql logs we can see this

ERR detail={"connection_info":{"msg":null,"token_expiry":null,"websocket_id":"52e5bac0-9330-4b1a-a020-6663ad760fcf"},"event":{"detail":{"code":"not-found","error":"only '/v1/graphql', '/v1alpha1/graphql' and '/v1beta1/relay' are supported on websockets","path":"$"},"type":"rejected"},"user_vars":null} timestamp=2025-04-04T05:21:03.666+0000 type=websocket-log

Describe the networking setup you are using I have Zoraxy deployed with DOCKER. I have a PROXIED docker network where I put every service I need to proxy, in this case the 3 containers I mentioned are within that network.

Additional context Ive deployed this new application via docker, it consist of 3 containers.

  1. The client
  2. The graphql-server
  3. Authentication-server

The nginx config that the creator recommends is this

http {
  server {
      listen 443 http2 ssl;
      listen [::]:443 http2 ssl;
      server_name XXX;

      ssl_certificate XXX
      ssl_certificate_key XXX;

      # HTTP Security Headers
      add_header Referrer-Policy "strict-origin";
      add_header Strict-Transport-Security "max-age=31536000; includeSubDomains;";
      add_header X-Content-Type-Options "nosniff";
      add_header X-Frame-Options "DENY";
      add_header X-XSS-Protection "0";
      # You can remove the Google, Firebase, and Sentry policies if you are not using them
      add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'wasm-unsafe-eval' https://apis.google.com https://www.google.com https://www.gstatic.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; connect-src 'self' https://*.ingest.sentry.io https://identitytoolkit.googleapis.com https://securetoken.googleapis.com https://apis.google.com https://world.openfoodfacts.org; frame-src 'self' https://*.firebaseapp.com https://www.google.com; img-src 'self' https://www.gstatic.com data:; font-src 'self' https://fonts.gstatic.com https://fonts.googleapis.com; worker-src 'self'; object-src 'none';"
      add_header Permissions-Policy "accelerometer=(self), autoplay=(self), camera=(self), cross-origin-isolated=(self), display-capture=(self), encrypted-media=(self), fullscreen=(self), geolocation=(self), gyroscope=(self), keyboard-map=(self), magnetometer=(self), microphone=(self), midi=(self), payment=(self), picture-in-picture=(self), publickey-credentials-get=(self), screen-wake-lock=(self), sync-xhr=(self), usb=(self), xr-spatial-tracking=(self)"

      location /v1 {
        # API (Hasura)
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'Upgrade';
        proxy_set_header Host $host;
        proxy_pass http://localhost:8080;
      }

      location /v2 {
          # API (Hasura)
          proxy_pass http://localhost:8080;
      }

      location /console {
          # Admin panel (Hasura)
          proxy_pass http://localhost:8080;
          add_header Content-Security-Policy "";
      }

      location /auth {
          # Authentication server (Express.js)
          proxy_pass http://localhost:3001;
      }

      location /healthz {
          # Health check (Hasura)
          proxy_pass http://localhost:8080;
      }

      location / {
          # Static files (Clean Slate)
          proxy_pass http://localhost:3000;
      }
  }
}

As it can be seen in there, there is a websocket, and a auth proxy_pass for the auth server

I really do not know what is happening here, but I am sure I am doing something wrong on ZORAXY.

Any help would be amazing!

Mega61 avatar Apr 04 '25 05:04 Mega61

Hi @Mega61 ,

I assume you are referring to this project.

The short answer is I have no idea.

The long answer is that they are not a self-contained system. They are using Caddy or Nginx as a web server middleware that bind services together (as you can see from their exceptionally long reverse proxy config). It is a common way to bind services together as a web development beginner and it is a quick way to get a prototype done, but the down side is making the project dependent on an external service, like nginx / Caddy in this case.

Zoraxy is a reverse proxy and it is not a fully featured web server like nginx. Sometime it might be compatible with systems like this but this really down to the implementation of the upstream system. Assuming your hostname are correct for upstreams (graphql-server:8080 makes no sense to me, but I will assume you replace this with some dummy text to prevent showing real public ip on the internet), your config should works.

For your information, Websocket works in both host name based routing and virtual directory routing, and your graphQL side actually see something connecting in, so Zoraxy did proxied the request to the correct location, just the payload that it proxied is not what the graphQL server wanted and the graphQL side close the connection (and hence the bad handshake see in Zoraxy log).

I would recommend you to ask the developer of the cleanslate project to add support for Zoraxy by improving their system design to depend less on external reverse proxy for maintaining their API structure. Alternatively, you can use an nginx instance as middleware for such system to workaround the problem.

tobychui avatar Apr 04 '25 06:04 tobychui