Improve reverse proxy, automatic handling of websockets

Many applications use websockets today (e.g. webtop5). The current reverse proxy module of Nethserver only adds ProxyPass and ProxyPassReverse, so Websockets are not passed correctly. The only solution is to not use the reverse proxy UI at all and create manual configurations in /etc/httpd/conf.d instead, which is however complicated and not shown in the UI.

Fortunately it is possible to handle websocktets automatically by using the rewrite enginge:

E.g. Reverse proxy xy.mydomain host to xy-backend.

<VirtualHost *:443>
    ServerName xy.mydomain.de

    RewriteEngine on
    RewriteCond %{HTTP:Upgrade} websocket [NC]
    RewriteCond %{HTTP:Connection} upgrade [NC]
    RewriteRule .* "wss://xy-backend%{REQUEST_URI}" [P]

    ProxyPass / https://xy/
    ProxyPassReverse / https://xy-backend/
</VirtualHost>

So nethserver should always the four lines with the rewrite condition to the reverse proxy configuration. Caveat: If destination is http, the protocol ws must be used, if destiation is https, use protocol wss.

4 Likes

I created a solution and a feature request. I hope someone can integrate it.

1 Like

I experienced this as well trying to use NethServer as a reverse proxy for web services like FreeNAS and Xen Orchestra. In the end switched to PfSense with haproxy/ACME auto-renew LE certs because of it.

@royceb With my modification it plug and play. Just create the 5 lines file as described in the github issue. I hope this will be integrated in the near future.

Do you know if your fix would also help with BOSH connections not working behind reverse proxy?

I agree that could be useful, but often the path of the websocket is different than the web application one.
This scenario makes harder to create a simple UI for the user. Explaining the difference between HTTP and websocket is even harder :frowning:

So, in the end, I prefer to not implement such thing.
@davidep @stephdl what do you think?

1 Like

There is nothing to explain to the user, because it works out of the box in all cases where a whole subdomain is proxied, because the path doesn’t matter and the websocket upgrade request is automatically detected.

It also works in in most other cases, even if there is no subdomain, beause allmost always the websockets paths are subpaths of the main application path.

So why to you prefer NOT implementing such a thing and have the users write apache conf-files by hand instead of using the Nethserver UI?

Also there is NO UI chance, so nothing gets more complicated. At most you COULD implemement a check box “detect any proxy websocktets in subpath”, but I have no idea, why someone should disable it. So my suggestion is: implement no UI change but only take my fix to correctly proxy the websockts out of the box.

We design the UI for the general way to administer your server, all corner cases or specific usages are done by custom templates.

That’s said, I needed recently to make a reverse proxy on a tcp linux socket and this case is not covered also by the UI :smiley:

Another link to this feature: https://www.serverlab.ca/tutorials/linux/web-servers-linux/how-to-reverse-proxy-websockets-with-apache-2-4/

I do not understand why you oppose a really simple and useful feature, which has no negative consequences. By the way: Your example would have been solved by my change also. No file editing, just create reverse proxy for ws.serverlab.ca => localhost:3000 in the Nethserver UI and problem is solved.

I can’t speak to any of the technical stuff hear but from a from a marketing point I offer NethServer with base Crostino subscription or higher to clients with idea everything can be managed via Cockpit. As the web socket fix here isn’t available as a standard option within web-admin interface I use another product that does. For my clients this makes documentation and consistent reproducible results. Just my thoughts.

1 Like

I studied a bit the proposed solution with this template custom /etc/e-smith/templates-custom/httpd/proxy-extra/20rewrite_websocket:

    RewriteEngine on
    RewriteCond %\{HTTP:Upgrade\} websocket [NC]
    RewriteCond %\{HTTP:Connection\} upgrade [NC]
    RewriteRule .* "wss://%\{SERVER_NAME\}%\{REQUEST_URI\}" [P]

After creating the fragment:

expand-template /etc/httpd/conf.d/virtualhosts.conf
systemctl reload httpd

The template doesn’t handle wss/ws so I forces everything to wss for simplicity.
I did a couple of tests: it works with mattermost but not with webtop.
So it’s not applicable to all scenarios.

It could be a valuable addition but it can’t be enabled on all installations without an explicit configuration by the user: I fear regressions.
It also need extra informations from the user to make it work on all scenario (eg. the path for the web socket).

In the end, I still think it’s quite an effort to add such implementation.
Again, what do you think @dev_team?

1 Like

My proposed solution handled http to ws and https to wss correctly, whereas your template does not. Why did you change it? You should keep my solution.

Please post the manual matterhost-reverse-proxy.conf. Maybe matterhost needs some very special rules, which are not handled from either the original reverse-proxy nor my change, but this would be no argument for not adopting my change, since my proposition just solves most typical websockets proxy pass with no negative effects.

If you have to do a manual configuration for matterhost before my change, you are not doing worse with my change, because the Nethserver reverse-proxy-settings are to weak in both cases. But all the configuration for reverse-proxies were my change solves the problem do not need a manual configuration any more.

My change definitly works with webtop, because I am using it and created the change extra for webtop. If it does not work with your server, you might have run into the same bug as me. See Reverse proxy subdomain to secondary application Nethserver

2 Likes

I think the point is, that you don’t need the information from the user, which path the websockets are working on. You only need this information, if you don’t have the mod_rewrite module active, which can detect the request for protocol switch by parsing the header and takes whatever path the browser supplies, so it should work always.

2 Likes

I just spent a few minute to experiment a bit with the current template without changing it too much.

Happy to hear that, so in this case, it could be a simple solution transparent to the user.
Sadly, I couldn’t get it working very quickly, so any help would be really appreciated.

Do you want to hack a bit with the template and try to create a working template-custom for all scenarios?
I will gladly try it on a test machine next days.

@stephdl feel free to jump on board, if you want :wink:

2 Likes

I think, the template is working correctly. I use it for several reverse proxy items. Please give more information about the exact configuration where it does not work at your place. If you have a working manual configuration please post this.

Please keep in mind, that the template only solves the websocket problem, not all other problems which also might arise.

1 Like

To avoid regression, I think we need a check box to enable/disable this new feature.

Thank on my todo

1 Like

just an update … I start to work on it

3 Likes
3 Likes

Your code does not correctly proxy http to ws and https to wss. Please have a look at my code which selects the correct protocol

Your code:

RewriteRule .* “wss://$ws/$1” [P,L]

My code:
RewriteRule .* ws{ ($OUT = $Target) =~ s|http|| }%{REQUEST_URI} [P]

1 Like

could you explain how you determine if you need to use the wss or the ws, you will use always ws whatever the Port 80 or 443 and whatever the http or https of the target with your template

:-?