Websockets, CentOS 6, and Apache 2.4
Problem: Apache 2.2 does not support websockets. This precludes using it as reverse proxy for NoVNC, and also for RShiny apps. This is a problem for us because we require access to Shiny apps over our standard Apache proxy. Read on for our solution!
Approach: Solve the problem using standard CentOS image with Apache 2.4 (which supports websockets). Prove we have a solution on a standalone VM, then we’ll migrate our existing Apache proxy.
Procedure:
- Create / basic configure the Guest VM within your virtualization environment (we use OpenStack). Launch a new instance based on vanilla CentOS VM instance, reboot at first login to force hostname to set completely, perform a
yum update
). - Setup
iptables
:iptables -I INPUT 2 -p tcp -m state --state NEW -m tcp \ -m multiport --dports 80,443 -m comment --comment "HTTP/HTTPS" -j ACCEPT
Be sure to add the HTTP/HTTPS ports for your VM Security Group if running virtually. Then reload the firewall:
service iptables reload
- Install Apache 2.4 for CentOS (no base support in CentOS
yum
repository):cd /etc/yum.repos.d wget http://repos.fedorapeople.org/repos/jkaluza/httpd24/epel-httpd24.repo
Install the software and verify the version (note the explicit package names and non-standard location for installed binary):
yum install httpd24.x86_64 /opt/rh/httpd24/root/usr/sbin/httpd -version
Set software to start automatically (note service name):
chkconfig httpd24-httpd on service httpd24-httpd start
- Setup SSL. We’ll install the module, then disable the default SSL Web site since we’ll use our own for our test. This directly emulates what we do in production.
First, install the module:yum install httpd24-mod_ssl.x86_64
Then comment out the default SSL Web site:
# /opt/rh/httpd24/root/etc/httpd/conf.d/ssl.conf [...comment out lines from...] <VirtualHost _default_:443> [...all the way to...] </VirtualHost>
- Setup system to emulate “standard” Apache file locations (remember that installing the special 2.4 Apache will not create these locations). This allows us to setup our configuration file exactly as we will use it in production:
mkdir -p /etc/httpd /var/log/httpd chmod 700 /var/log/httpd
- Create the configuration file:
cd /opt/rh/httpd24/root/etc/httpd/conf.d
Set the configuration file contents:
# /opt/rh/httpd24/root/etc/httpd/conf.d/wstunnel-test.conf LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so <VirtualHost *:80> LogLevel debug ServerName shiny-demo.hlsdev.com RewriteEngine On RewriteRule (.*) https://shiny-demo.hlsdev.com/%{REQUEST_URI} CustomLog /var/log/httpd/demo.log combined ErrorLog /var/log/httpd/demo_error.log </VirtualHost> <VirtualHost *:443> LogLevel debug ServerName shiny-demo.example.com SSLEngine ON SSLCertificateFile /etc/httpd/example.com.crt SSLCertificateKeyFile /etc/httpd/example.com.key ProxyRequests Off ProxyPreserveHost Off <Proxy *> Order allow,deny Allow from all </Proxy> # websockets? ProxyPassMatch ^/(.*/__sockjs__/[0-9]+/.*) ws://mybackend.example.local:3838/$1 ProxyPass / http://mybackend.example.local:3838/ max=1 disablereuse=on keepalive=on ProxyPassReverse / http://mybackend.example.local:3838/ CustomLog /var/log/httpd/demo.log combined ErrorLog /var/log/httpd/demo_error.log </VirtualHost>
In the above: the Websockets support is provided by two additional lines. The first line loads the Websockets Tunneling code (which is an add-on to standard ws-proxy module):
LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so
The second line performs the check for the “websocketized” application using the
ProxyPassMatch
command. The regular expression checks for__socksjs__
followed by an all-numeric identifier. If found, then the proxy will send the request to thews://
destination, which automagically invokes the ws_tunnel code. - Restart your web server:
service httpd24-httpd restart
- Test your websocketized application. Notice that we enabled
LogLevel debug
above so we can see the Websockets tunnel at work in the error log file:[Wed Dec 03 12:17:15.558370 2014] [proxy_wstunnel:debug] [pid 2042] mod_proxy_wstunnel.c(331): [client 172.20.128.2:62126] AH02451: serving URL ws://mybackend.example.local:3838/sample-apps/rmd/__sockjs__/510/w2kfd1y0/websocket
In the above snippet, the Websockets tunnel code kicked in, detected the sockets pattern we defined in the
ProxyPassMatch
and served the file to the Websockets backend server. Kewl!
That is all.
Hello Andrew Bruce,
Thank you very much for the post. Did you get a chance to try this solution in a mod_cluster clustered environment ?
Hi Sam – No, I have not run websockets in a clustered environment with Apache as reverse proxy. Let me know how it goes, thanks for your reply!