0

I may be going about this all wrong.

Goal: Recieve an https connection over apache, and do some validation before routing it to a websocket server. Return a 403 status code if invalid, and proceed with websocket connection if valid.

I decided to try involving a node proxy for this, based on http-proxy-middleware. In apache, I have a virtual host entry that includes:

  <Location /testws>
    ProxyPass        ws://127.0.0.1:6006/testws
    ProxyPassReverse        ws://127.0.0.1:6006/testws
  </Location>

The node proxy I have running on port 6006 is:

const express = require('express');
const { createProxyMiddleware, responseInterceptor } = require('http-proxy-middleware');

const app = express();

// proxy middleware options
const wsProxyOptions = {
    target: 'http://example.org', // target host

    changeOrigin: true, // needed for virtual hosted sites
    ws: true, // proxy websockets
    router: {
      // when request.headers.host == 'dev.localhost:3000',
      // override target 'http://www.example.org' to 'http://localhost:8000'
      // 'dev.localhost:3000': 'http://localhost:8000',
      '/testws': 'ws://127.0.0.1:6082',  //my websocket server is running on port 6082
    },
    selfHandleResponse: true,
    onProxyRes: responseInterceptor(async (responseBuffer, proxyRes, req, res) => {
        res.statusCode = 418; // set different response status code
        const response = responseBuffer.toString('utf8');
        return response.replace('Example ', 'NewText');
    }),
    logLevel: 'debug'
};

// The setup below is recommended for external websocket upgrades
// https://github.com/chimurai/http-proxy-middleware#external-websocket-upgrade
const wsProxy = createProxyMiddleware(wsProxyOptions);
app.use(wsProxy);

const server = app.listen(6006);
server.on('upgrade', wsProxy.upgrade); // optional: upgrade externally

When I use that setup, I go to https://server.ip/testws from my browser. I am routed to my websocket server as expected. However, onProxyRes(responseInterceptor) never gets accessed. I've also tried other event options such as onProxyReq, and onProxyReqWs. They don't get hit either.

Next, I tried changing the ProxyPass and ProxyPassReverse settings in my apache settings to go to http://127.0.0.1:6006/testws instead of ws://127.0.0.1:6006/testws. Now, onProxyReq does get accessed, but I get a response from my websocket server saying "Upgrade Required", which probably makes sense because the upgrade hasn't happened yet. Would upgrading the connection here be possible? If so, how?

Another thing I've been trying is involving an asynchronous function for the router option. That works for me to determine what the new target should be, but it didn't seem like there's a way to involve status codes from there, since it just wants a target value returned.

The reason I thought to try responseInterceptor is because I'd like to do some validation before making a connection to my websocket server, and return a status code 403 if the validation fails, instead of continuing with the websocket connection. Any thoughts on this? Or, is this the wrong approach for what I'm trying to do?

Update: I guess it makes sense that onProxyRes wouldn't be what I need for this, since the upgrade could be happening before that. I'm still not sure why the onProxyReq option wouldn't work. Either way, I started looking at:

server.on('upgrade', wsProxy.upgrade);

Would I somehow be able to respond with a status code if validation fails? For example:

server.on('upgrade', (req, socket, head) => {
    if (valid) {
        wsProxy.upgrade(req, socket, head);
    } else {
      //somehow return status 403
    }
});
user3345
  • 1
  • 1

1 Answers1

0

It looks like this post asked about the same thing, but much more clearly:

Intercept (and potentially deny) web socket upgrade request

I'm not sure if there's a more proper solution, but the one mentioned there seems to be working for me so far. It's basically showing how to respond with an http status code on the upgrade:

server.on('upgrade', (req, socket, head) => {
    if (validationPasses) {
        wsProxy.upgrade(req, socket, head);
    } else {
        socket.write('HTTP/1.1 403 Access Denied\r\n' +
            'Upgrade: WebSocket\r\n' +
            'Connection: Upgrade\r\n' +
            '\r\n');

        socket.destroy();
        return;
    }
});
user3345
  • 1
  • 1