Apache reverse proxy for websockets - apache

I'm using Apache on my server to proxy traffic on port 80 and 443 out to separate VM's running different websites and services. I'm having trouble setting up a proxy for MeshCentral which requires websockets. I'm using Debian 10 with Apache 2.4.38.
I can load MeshCentral, but once I login it tries to use websockets and I get the following error;
Firefox can’t establish a connection to the server at wss://example.com/control.ashx?auth=Uu7PBFNsswzzWoQaVNPH2N3ZwkWbx7DSsljaaY8cxthO5fcPVSz#sqLbGzyOpvxTxvfmV7WgwLdRklqLNYC5KQTjrZPCYDcNDvJ0AY7V8DGdUk68jK3sPfnc$Sl7rvhaQwR1xBukiZ8=. meshcentral.js:27:21
I've added the wstunnel proxy
a2enmod proxy_wstunnel
And setup HTTP and HTTPS proxies which work fine
/etc/apache2/sites-enabled/000-default.conf
<VirtualHost *:80>
ServerName example.com
ProxyPreserveHost On
ProxyPass "/" "http://192.168.200.11/"
ProxyPassReverse "/" "http://example.com/"
</VirtualHost>
/etc/apache2/sites-enabled/000-default-le-ssl.conf
<IfModule mod_ssl.c>
<VirtualHost *:443>
ServerName example.com
RewriteEngine on
RewriteCond ${HTTP:UPGRADE} websocket [NC]
RewriteCond ${HTTP:CONNECTION} upgrade [NC]
RewriteRule /(.*) "wss://example.com/$1" [P]
ProxyPreserveHost On
ProxyPass "/" "https://192.168.200.11/"
ProxyPassReverse "/" "https://example.com/"
SSLProxyEngine On
Include /etc/letsencrypt/options-ssl-apache.conf
SSLCertificateFile /etc/letsencrypt/live/mydomain.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/mydomain.com/privkey.pem
</VirtualHost>
</IfModule>
I've restarted apache before I tried loading the page in firefox and also tried google-chrome, same error.

You can try with:
Ubuntu
a2enmod proxy
a2enmod proxy_http
a2enmod proxy_wstunnel
Centos
Open the module configuration file for proxies.
sudo vi /etc/httpd/conf.modules.d/00-proxy.conf
All modules related to proxying are listed in this configuration file. Verify that the following lines exist and are uncommented.
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule proxy_wstunnel modules/mod_proxy_wstunnel.so
If you made any changes to the file, save them now.
Restart Apache Web Server to apply your changes.
sudo systemctl restart httpd
Configuration:
<VirtualHost *:443>
ServerName ws.serverlab.ca
RewriteEngine on
RewriteCond ${HTTP:Upgrade} websocket [NC]
RewriteCond ${HTTP:Connection} upgrade [NC]
RewriteRule .* "wss:/localhost:3000/$1" [P,L]
<Proxy balancer://backend-cluster>
BalancerMember http://server01:3000
BalancerMember http://server02:3000
BalancerMember http://server03:3000
</Proxy>
ProxyPass / balancer://backend-cluster/
ProxyPassReverse / balancer://backend-cluster/
ProxyRequests off
</VirtualHost>
ServerName ws.serverlab.ca
The hostname of the virtual web host that will handle the WebSocket connections.
RewriteEngine on
Used to set the status of the RewriteEngine to either on or off. To support WebSockets it must be turned on.
RewriteCond ${HTTP:Upgrade} websocket [NC]
A condition that must be matched in order for a request to be processed by the RewriteRule.
RewriteCond ${HTTP:Connection} upgrade [NC]
To something
RewriteRule . “wss:/ws-backend%{REQUEST_URI}” [P]*
Rewrite all incoming requests to use the wss protocol, and replace the destination hostname to that of a backend service.
Documentation from: How to Reverse Proxy Websockets with Apache 2.4

Related

Wrong certificate being presented behind apache reverse proxy

I think I know the reason this is happening but I don't know how to fix it. I have a reverse proxy set up on one server and it's proxying from port 443 to my mattermost server (not on the same machine) on the default port of 8065.
I have SSL set up on the proxy with a let's encrypt certificate.
Port 8065 is opened on my firewall to the mattermost server but also on that same public IP I have port 80 and 443 opened to a completely different server.
What is happening is that when I hit the url for my mattermost server the certificate for that other server is being presented to my browser instead of the one that is configured on the proxy. I have no idea why this is happening. Here is my virtual host section:
<IfModule mod_ssl.c>
<VirtualHost *:443>
DocumentRoot "/var/www/html"
ServerName chat.example.com
<Directory "/var/www/html">
allow from all
Options None
Require all granted
</Directory>
ProxyPreserveHost On
RewriteEngine On
RewriteCond %{REQUEST_URI} /api/v[0-9]+/(users/)?websocket [NC]
RewriteCond %{HTTP:UPGRADE} ^WebSocket$ [NC]
RewriteCond %{HTTP:CONNECTION} \bUpgrade\b [NC]
RewriteRule .* ws://x.x.x.x:8065%{REQUEST_URI} [P,QSA,L]
ProxyPass / http://x.x.x.x:8065/ timeout=31536000
ProxyPassReverse / http://x.x.x.x:8065/
SSLCertificateFile /etc/letsencrypt/live/chat.example.com/cert.pem
SSLCertificateKeyFile /etc/letsencrypt/live/chat.example.com/privkey.pem
Include /etc/letsencrypt/options-ssl-apache.conf
SSLCertificateChainFile /etc/letsencrypt/live/chat.example.com/chain.pem
</VirtualHost>
</IfModule>
Let me know if more information is required.
Rich
I ended up doing this a different way - not sure why it wasn't working. I'm calling it worked around.

How to run socket.io on port 443 where apache is running?

I need to run socket.io on port 443 (where apache run https site with Let's Encrypt)
The idea is to use a apache proxy that will redirect the traffic to the socket.io port.
I found that solution:
<VirtualHost *:443>
ServerName mysite.com
ServerAlias www.mysite.com
SSLEngine on
SSLProxyEngine On
ProxyRequests Off
SSLCertificateFile /etc/apache2/ssl/mysite.com.crt
SSLCertificateKeyFile /etc/apache2/ssl/mysite.com.key
SSLCertificateChainFile /etc/apache2/ssl/ca.cer
DocumentRoot /var/www/errorPages
ErrorDocument 503 /503.html
ProxyPass /503.html !
ProxyPass / http://localhost:3999/
ProxyPassReverse / http://localhost:3999/
RewriteEngine on
RewriteCond %{HTTP:UPGRADE} ^WebSocket$ [NC]
RewriteCond %{HTTP:CONNECTION} ^Upgrade$ [NC]
RewriteRule .* ws://localhost:3999%{REQUEST_URI} [P]
</VirtualHost>
I run the socket.io on port 3999
HTTPS site works fine, howevever I got http 404 errors.
I guess problem is on rewriteCond.
websocket.js:112 WebSocket connection to
'wss://mysite.com/socket.io/?id=11518237&username=john failed: Error
during WebSocket handshake: Unexpected response code: 404
Try mod_proxy_wstunnel
It provides support for the tunnelling of web socket connections to a backend websockets server. The connection is automatically upgraded to a websocket connection
https://httpd.apache.org/docs/2.4/mod/mod_proxy_wstunnel.html
Use different IP addresses for the different uses. You have <VirtualHost *:443> which tries to use all IP addresses for the single virtual host. I think you want a <VirtualHost pub.lic.ip.addr:443> for Let's Encrypt and a <VirtualHost localhost:443> for the socket.io proxy.

MQTT over websocket over Apache SSL

I have an mqtt broker providing unencrypted websocket. I would like to proxy it through an Apache which should encrypt the websocket to the outside.
It is an Apache 2.4 on a Windows machine.
My config is:
<VirtualHost *:80>
ServerName test.someurl.com
RewriteEngine On
RewriteCond %{HTTP:Upgrade} =websocket [NC]
RewriteRule /(.*) ws://localhost:9876/$1 [P,L]
</VirtualHost>
<VirtualHost *:443>
ServerName test.someurl.com
SSLEngine on
SSLCertificateFile "C:/Program Files (x86)/Apache24/conf/ssl/some_certificate.crt"
SSLCertificateKeyFile "C:/Program Files (x86)/Apache24/conf/ssl/some_key.key"
RewriteEngine On
RewriteCond %{HTTP:Upgrade} =websocket [NC]
RewriteRule /(.*) ws://localhost:9876/$1 [P,L]
# Websocket proxy
# wss redirects to working ws protocol
# ProxyPass /wss ws://127.0.0.1:9876 retry=0 keepalive=On
# ProxyPassReverse /wss ws://127.0.0.1:9876 retry=0
</VirtualHost>
I am able to connect through ws / port 80. It works fine. However, I am not able to connect using the wss.
I tried both using a rewriting and also a proxy_pass directives. I tried 100 different solution. However, this one looked most promising as port 80 is working for ws but not for the encrypted part. Any idea? Or I am just blinded by the options O:)
This is an old question, but as I've just got this working:
I have Mosquitto listening on port 8000 (which is firewalled to block any connections other than from localhost)
listener 8000
socket_domain ipv4
allow_anonymous true
protocol websockets
Then setup apache as so:
<VirtualHost *:80>
ProxyPass /ws/ ws://localhost:8000/
ProxyPassReverse /ws/ ws://localhost:8000/
</VirtualHost>
<VirtualHost *:443>
SSLCertificateFile ...
SSLCertificateChainFile ...
SSLCertificateKeyFile ...
ProxyPass /ws/ ws://localhost:8000/
ProxyPassReverse /ws/ ws://localhost:8000/
</VirtualHost>
Finally, the web-application is set to connect like so:
mqtt.connect((window.location.protocol == "https:" ? "wss:" : "ws:") + "//example.org/ws/");
Note that the proxy protocol can be "ws" or "wss" - both seem to work interchangeably. This is the connection between apache and mosquitto, there's no need to encrypt (they're on the same host). The use of the "/ws/" suffix on the path means I can do without mod_rewrite, and simply use mod_proxy.
This approach is the only way I could require authentication when accessed over HTTPS (which is public) but not over HTTP (which is behind the firewall).

Apache SSL Configuration

We are using apache tomcat 7.0 in our application. We need configure SSL but i want install the httpd apache on top the tomcat which would do ssl encryption and decryption. so it is possible have such configuration?
Yes, you can use mod_proxy_http to proxy HTTPS requests back to HTTP request to the Apache Tomcat server.
So if you want all traffic to be served over HTTPS do the following:
Make sure tomcat is running on port 8080 (and not 80), install apache2 httpd and then:
a2enmod proxy
a2enmod proxy_http
a2enmod ssl
a2enmod rewrite
create a file called mysite.conf with the following VirtualHost directives:
<VirtualHost *:443>
ServerName www.yourdomainname.com
SSLEngine on
SSLCertificateFile "/path/to/www.yourdomainname.com.cert"
SSLCertificateKeyFile "/path/to/www.yourdomainname.com.key"
ProxyPreserveHost On
ProxyPassReverse / http://localhost:8080/
</VirtualHost>
<VirtualHost *:80>
ServerName www.yourdomainname.com
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=302]
</VirtualHost>
Finally
a2ensite mysite

Is it possible to use "%{HTTP_HOST}" in ProxyPass?

I'm trying to implement a transparent proxy using apache2 and mod_proxy that for now - doesn't do anything. just forwards the traffic to the correct "host".
I don't want it to be host-dependant - but dynamic so it'll work for all hosts.
I tried to do this:
RewriteEngine on
RewriteLogLevel 5
RewriteLog "/var/log/apache2/rewrite.log"
RewriteRule ^(.*)$ $1
ProxyPass / http://$1
I also tried several other approaches (none worked).
Is there any way I can access the "host" from the header and use it in the ProxyPass directive?
In nginx I would use $host, $remote_addr, etc.. any way to replace that on apache?
What I need is to be able to access %{HTTP_HOST}, %{REQUEST_URI} and %{SERVER_PORT} inside the ProxyPass command.
To use Apache ProxyPass directives with dynamic hostnames you will need to also use ModRewrite.
Objective
All requests to the virtualhost will ProxyPass and ProxyPassReverse (also known as an "Apache Gateway") to the %{HTTP_HOST}
The only reason this would make sense to do is if you have localhost entries on the apache server for specfic host names
Examples
Localhost File
10.0.0.2 foo.bar.com
10.0.0.3 bar.bar.com
How it works
The client makes a request to foo.bar.com (dnslookup is a public IP... YOUR APACHE SERVER)
Your apache server has a localhost entry of 10.0.0.2 for foo.bar.com (some other server on your network)
The request goes through ModRewrite and /path1 is appended, then handed off to ProxyPass and ProxyPassReverse
ProxyPass and ProxyPassReverse hand the call off to foo.bar.com at ip 10.0.0.2
Client requests foo.bar.com ---reverse proxies to----> foo.bar.com/path1 (on some OTHER internal server)
Apache Configuration
<VirtualHost *:443>
Servername *
# Must not contain /path1 in path (will add /path1)
RewriteEngine on
RewriteCond %{REQUEST_URI} !^/path1/.*
RewriteRule ^/(.*) https://%{HTTP_HOST}/path1$1 [NC,R=302,L]
# Must contain /path1 in path (will send request to the proxy)
RewriteEngine On
RewriteOptions Inherit
RewriteCond %{REQUEST_URI} ^/path1/.*
RewriteRule ^(.*)$ https://%{HTTP_HOST}$1 [NC,P]
SSLEngine on
SSLProxyEngine On
ProxyRequests Off
ProxyPass / https://$1/
ProxyPassReverse / https://$1/
ProxyPreserveHost On
###################
# SSL Constraints #
###################
SSLProtocol -ALL +SSLv3 +TLSv1
# Choose cipher suites
SSLHonorCipherOrder On
SSLCipherSuite ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:!LOW:!SSLv2:!EXPORT
# SameOrigin The page can only be displayed in a frame on the same origin as the page itself
Header set X-Frame-Options SAMEORIGIN
SSLCertificateFile /etc/apache2/example.crt
SSLCertificateKeyFile /etc/apache2/example.key
SSLCertificateChainFile /etc/apache2/gd_bundle.crt
SetOutputFilter INFLATE;proxy-html;DEFLATE
</VirtualHost>
Just answering my own question:
I was missing 2 things:
the configuration should be:
RewriteEngine On
RewriteRule ^(.*)$ http://%{HTTP_HOST}$1 [P]
Not to forget to enable inherit in the virtual directory:
RewriteEngine On
RewriteOptions Inherit
You should read this page if you haven't done it already :
https://httpd.apache.org/docs/2.2/mod/mod_proxy.html#forwardreverse
I think the ProxyRequests directive is what you are looking for.