web-dev-qa-db-fra.com

tunneling des connexions Websocket sécurisées avec Apache

J'ai un Apache en cours d'exécution qui est uniquement accessible via HTTPS. Je souhaite servir des websockets à partir d'une application serveur supplémentaire s'exécutant sur le même ordinateur, mais comme il n'est pas possible pour les clients de se connecter sur un autre port que le port 443, nos connexions websocket doivent être mandatées via Apache.

Maintenant, j'ai installé mod_proxy et l'ai configuré comme suit:

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

Cela ne fonctionne pas cependant. Je peux me connecter à https: // serveur/ws dans mon navigateur, mais Apache semble avaler une partie des en-têtes de websockets, de sorte que les vraies connexions websocket ne fonctionnent pas.

Comment puis-je effectuer la tunnellisation de mes connexions websocket via le serveur Apache?

20
mstud

Je le fais travailler.

Scénario

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

Navigateur WebSocket via Apache httpd, procuration inversée à l'application Web dans Tomcat. Tous les SSL avant-arrière.

Voici la configuration pour chaque pièce:

Navigateur Client

Notez le "/" suivant dans l'url: wss://Host/app/ws/. Il était nécessaire de faire correspondre la directive wss ProxyPass correcte (indiquée plus bas dans la section relative à la configuration d'Apache) et d'empêcher une redirection 301 vers https://Host/app/ws. C'est-à-dire qu'il redirigeait à l'aide du schéma https et non du schéma wss pour le back-end.

<!doctype html>
<body>

<script type="text/javascript">
    var connection = new WebSocket("wss://Host/app/ws/");

    connection.onopen = function () {
        console.log("connected");
    };

    connection.onclose = function () {
        console.log("onclose");
    };

    connection.onerror = function (error) {
        console.log(error);
    };
</script>

</body>
</html>

Apache httpd

J'utilise Apache httpd 2.4.9, qui fournit immédiatement mod_proxy_wstunnel. Cependant, le mod_proxy_wstunnel.so fourni ne prend pas en charge SSL lors de l'utilisation du schéma wss: //. Il finit par essayer de se connecter au back-end (Tomcat) en texte clair, ce qui échoue lors de l'établissement de la liaison SSL. Voir bug ici . Donc, vous devez vous corriger mod_proxy_wstunnel.c en suivant la correction suggérée dans le rapport de bogue. C'est un changement facile en 3 lignes. 

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

Reconstruisez ensuite le module et remplacez dans votre nouveau mod_proxy_wstunnel.so par l'ancien.

Construire Apache httpd

Voici la commande (2.4.9) que j'ai utilisée pour intégrer les modules que je voulais. Vous pourriez ne pas avoir besoin de tous.

./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

Notez le dernier commutateur: --enable-proxy_wstunnel=shared Au début, j’utilisais incorrectement --enable-proxy-wstunnel=shared, qui semblait bien se construire, mais ne fonctionnait pas lorsque j’utilisais le fichier .so résultant. Regarde la différence? Vous voulez vous assurer d'utiliser un trait de soulignement dans "proxy_wstunnel" plutôt qu'un tiret.

Apache httpd config

...
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!
#<Location /app/ws/>
#        ProxyPass wss://localhost:8443/app/ws
#        ProxyPassReverse wss://localhost:8443/app/ws
#</Location>
#<Location /app/>
#        ProxyPass https://localhost:8443/app/
#        ProxyPassReverse https://localhost:8443/app/
#</Location>

# 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 vous n'avez pas vu ma note dans la configuration ci-dessus, la voici à nouveau: Faites très attention aux barres obliques "/" ou à leur absence!

De plus, si vous voyez des instructions de journal de débogage dans votre journal Apache indiquant qu'une connexion wss a été établie puis fermée, il est possible que mod_reqtimeout soit activé comme je l'ai fait, assurez-vous donc qu'elle ne soit pas chargée:

#LoadModule reqtimeout_module modules/mod_reqtimeout.so

Matou

En supposant que votre connecteur HTTP soit correctement configuré, il n’ya pas grand chose à configurer dans Tomcat. Bien que pour faciliter le débogage, j'ai trouvé utile de créer un $CATALINA_HOME/bin/setenv.sh qui ressemblerait à ceci:

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

Cela m'a permis de voir si le mod_proxy_wstunnel.so que j'ai modifié fonctionnait ou non pour wss: //. Lorsque cela ne fonctionnait pas, mon fichier journal catalina.out indiquait:

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()

Dernières pensées

Bien que j'utilise Apache httpd 2.4.9, J'ai déjà vu où les backports de mod_proxy_wstunnel peuvent être appliqués aux versions 2.2.x . J'espère que mes notes ci-dessus pourront être appliquées à ces anciennes versions. 

27
Tom Cawley

Si vous ne voulez pas qu'Apache mette fin à la connexion SSL (et transfère le trafic WebSocket non chiffré), mais que le SSL soit terminé sur le serveur WebSocket cible final et que souhaite uniquement utiliser WSS sur le trafic WebSocket entrant dans Apache, mod_proxy_connect peut simplement se connecter via le trafic brut. Pas certain. Je serais également intéressé si cela fonctionne.

Si ci-dessus ne tient pas, voici plus d'informations:

Dans tous les cas, l'utilisation d'Apache limitera sévèrement l'évolutivité en ce qui concerne le nombre de connexions WebSocket desservies simultanément, car chaque connexion WS consomme un processus/thread sur Apache.

1
oberstet

J'essaye d'installer ceci https://github.com/kawasima/mod_proxy_websocket . J'espère que ça aide.

0
dlopezgonzalez