tunelización de conexiones seguras de websocket con apache

Tengo un Apache ejecutándose al que solo se puede acceder a través de HTTPS. Quiero servir websockets desde una aplicación de servidor adicional que se ejecuta en la misma máquina, pero como no es posible que los clientes se conecten a otro puerto que no sea 443 a nuestro servidor, esas conexiones de websocket deben ser conectadas a través del Apache.

Ahora, instalé mod_proxy y lo configuré de la siguiente manera:

SSLProxyEngine on ProxyPass /ws https://127.0.0.1:9001 

Esto no funciona sin embargo. Me puedo conectar a https: // server / ws en mi navegador ahora, pero el apache parece tragarse parte de los encabezados websockets, por lo que las conexiones de websocket reales no funcionan.

¿Cómo puedo realizar túneles en las conexiones de mi websocket a través del servidor Apache?

Lo tengo funcionando.

Guión

 ------------- ---------------- ---------- | Browser |<----->| Apache httpd |<----->| Tomcat | | | SSL | 2.4.9 | SSL | 7.0.52 | ------------- ---------------- ---------- 

Navegador WebSocket a través de Apache httpd, proxy inverso a la aplicación web en Tomcat. Todo SSL de adelante hacia atrás.

Aquí está la configuración para cada pieza:

Cliente del navegador

Tenga en cuenta el “/” final en la url: wss://host/app/ws/ . Era necesario hacer coincidir la directiva wss ProxyPass correcta (que se muestra más abajo en la sección de configuración de Apache) y evitar una redirección 301 a https://host/app/ws . Es decir, estaba redirigiendo usando el esquema https y no el esquema wss para el back-end.

Página de prueba

      

Apache httpd

Estoy usando Apache httpd 2.4.9, que viene provisto de mod_proxy_wstunnel . Sin embargo, el mod_proxy_wstunnel.so provisto no admite SSL cuando se usa el esquema wss: //. Termina tratando de conectarse al back-end (Tomcat) en texto plano, lo que falla el protocolo de enlace SSL. Ver error aquí . Por lo tanto, debe parchear mod_proxy_wstunnel.c usted mismo siguiendo la corrección sugerida en el informe de errores. Es un cambio fácil de 3 líneas.

 Suggested correction, 314a315 > int is_ssl = 0; 320a322 > is_ssl = 1; 344c346 < backend->is_ssl = 0; --- > backend->is_ssl = is_ssl; 

A continuación, reconstruya el módulo y reemplace en su nuevo mod_proxy_wstunnel.so con el anterior.

Construyendo Apache httpd

Aquí está el comando (2.4.9) que solía construir en los módulos que quería. Puede que no los necesites a todos.

 ./configure --prefix=/usr/local/apache --with-included-apr --enable-alias=shared --enable-authz_host=shared --enable-authz_user=shared --enable-deflate=shared --enable-negotiation=shared --enable-proxy=shared --enable-ssl=shared --enable-reqtimeout=shared --enable-status=shared --enable-auth_basic=shared --enable-dir=shared --enable-authn_file=shared --enable-autoindex=shared --enable-env=shared --enable-php5=shared --enable-authz_default=shared --enable-cgi=shared --enable-setenvif=shared --enable-authz_groupfile=shared --enable-mime=shared --enable-proxy_http=shared --enable-proxy_wstunnel=shared 

Tenga en cuenta el último cambio: --enable-proxy_wstunnel=shared Al principio, estaba usando incorrectamente --enable-proxy-wstunnel=shared , que parecía estar bien, pero en última instancia no funcionaría cuando usé el archivo .so resultante. . ¿Ver la diferencia? Desea asegurarse de utilizar un guion bajo en "proxy_wstunnel" lugar de un guion.

Apache httpd config

httpd.conf

 ... LoadModule proxy_module modules/mod_proxy.so ... LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so ... LoadModule ssl_module modules/mod_ssl.so ... Include conf/extra/httpd-ssl.conf ... LogLevel debug ProxyRequests off # Note, this is the preferred ProxyPass configuration, and *should* be equivalent # to the same inline version below, but it does NOT WORK! # # ProxyPass wss://localhost:8443/app/ws # ProxyPassReverse wss://localhost:8443/app/ws # # # ProxyPass https://localhost:8443/app/ # ProxyPassReverse https://localhost:8443/app/ # # NOTE: Pay strict attention to the slashes "/" or lack thereof! # WebSocket url endpoint ProxyPass /app/ws/ wss://localhost:8443/app/ws ProxyPassReverse /app/ws/ wss://localhost:8443/app/ws # Everything else ProxyPass /app/ https://localhost:8443/app/ ProxyPassReverse /app/ https://localhost:8443/app/ 

Si no vio mi nota en la configuración anterior, aquí está de nuevo: preste atención estricta a las barras “/” o la falta de ella!

Además, si está viendo instrucciones de registro de depuración en su registro de apache que dice que se realizó una conexión de wss y luego se cerró, es posible que tenga mod_reqtimeout habilitado como lo hice, así que asegúrese de que no esté cargado:

 #LoadModule reqtimeout_module modules/mod_reqtimeout.so 

Gato

Suponiendo que su conector HTTP está configurado correctamente, no hay mucho que configurar en Tomcat. Aunque para ayudar en la depuración, me pareció útil crear un $CATALINA_HOME/bin/setenv.sh que se veía así:

setenv.sh

 CATALINA_OPTS=$CATALINA_OPTS" -Djavax.net.debug=all -Djavax.net.debug=ssl:handshake:verbose" 

Esto me permitió ver si el mod_proxy_wstunnel.so que modifiqué estaba funcionando o no para wss: //. Cuando no funcionaba, mi archivo de registro de catalina.out mostraba:

 javax.net.ssl.SSLException: Unrecognized SSL message, plaintext connection? http-nio-8443-exec-1, SEND TLSv1 ALERT: fatal, description = internal_error http-nio-8443-exec-1, WRITE: TLSv1 Alert, length = 2 http-nio-8443-exec-1, called closeOutbound() http-nio-8443-exec-1, closeOutboundInternal() 

Pensamientos finales

Aunque estoy usando Apache httpd 2.4.9, he visto que los puertos de soporte de mod_proxy_wstunnel se pueden aplicar a las versiones 2.2.x. Espero que mis notas anteriores se puedan aplicar a esas versiones anteriores.

Si no desea que Apache finalice la conexión SSL (y reenvíe el tráfico WebSocket sin encriptar), sino que finalice SSL en el servidor WebSocket de destino final y desee utilizar WSS de forma exclusiva en el tráfico WebSocket que llega a Apache, entonces mod_proxy_connect podrá hacerlo simplemente conectarse a través del tráfico sin procesar. No es seguro. También me interesaría si eso funciona.

Si lo anterior no se cumple, aquí hay más información:

En cualquier caso, usar Apache limitará severamente la escalabilidad con respecto al número de conexiones WebSocket servidas concurrentemente, ya que cada conexión WS consumirá 1 proceso / subproceso en Apache.

Estoy intentando instalar este https://github.com/kawasima/mod_proxy_websocket . Espero eso ayude.