Туннелирование безопасных соединений websocket с apache

У меня есть работа Apache, доступная только через HTTPS. Я хочу обслуживать веб-порты из дополнительного серверного приложения, которое работает на одном компьютере, но поскольку клиенты не могут подключаться к другому порту, чем 443, к нашему серверу, эти подключения к веб-узлам должны быть проксированы через Apache.

Теперь я установил mod_proxy и настроил его следующим образом:

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

Однако это не работает. Я могу подключиться к https://server/ws в моем браузере сейчас, но apache, похоже, проглотит часть заголовков веб-узлов, так что реальные соединения с веб-сокетами не работают.

Как я могу выполнить туннелирование моих подключений через сервер Apache?

Ответы

Ответ 1

У меня это работает.

Сценарий

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

Браузер WebSocket через Apache httpd, обратный прокси к веб-приложению в Tomcat. Все SSL-обратные.

Здесь конфигурация для каждой части:

Клиент браузера

Обратите внимание на конечный "/" в URL: wss://host/app/ws/. Необходимо было подобрать правильную директиву wss ProxyPass (показанную далее в разделе конфигурации Apache) и предотвратить перенаправление 301 на https://host/app/ws. То есть он перенаправлялся с использованием схемы https, а не для схемы wss для 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

Я использую Apache httpd 2.4.9, который из коробки предоставляет mod_proxy_wstunnel. Однако предоставленный mod_proxy_wstunnel.so не поддерживает SSL при использовании схемы wss://. В результате он пытается подключиться к фоновому контенту (Tomcat) в обычном тексте, что не позволяет SSL-квитирование. Смотрите здесь здесь. Итак, вы должны исправить mod_proxy_wstunnel.c самостоятельно, выполнив предложенную коррекцию в отчете об ошибке. Это легкое 3-х строчное изменение.

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

Затем перестройте модуль и замените его в новом mod_proxy_wstunnel.so старым.

Построение Apache httpd

Здесь (2.4.9) команда, которую я использовал для сборки модулей, которые я хотел. Возможно, вам не нужны все.

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

Обратите внимание на самый последний переключатель: --enable-proxy_wstunnel=shared. Сначала я неправильно использовал --enable-proxy-wstunnel=shared, который, казалось, строился отлично, но в конечном счете не работал, если Я использовал полученный .so файл. Увидеть разницу? Вы хотите удостовериться, что используйте знак подчеркивания "proxy_wstunnel", а не тире.

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!
#<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/

Если вы не заметили мою заметку в приведенной выше конфигурации, здесь она снова: Уделите пристальное внимание косам "/" или их отсутствию!

Кроме того, если вы видите инструкции журнала отладки в своем журнале apache, в котором говорится, что соединение wss было завершено, тогда вы можете отключить mod_reqtimeout, как и я, поэтому убедитесь, что он не загружен:

#LoadModule reqtimeout_module modules/mod_reqtimeout.so

Tomcat

Предполагая, что ваш HTTP-коннектор настроен правильно, в tomcat не так много настроек. Хотя для помощи в отладке мне было полезно создать $CATALINA_HOME/bin/setenv.sh, который выглядит так:

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

Это позволило мне увидеть, работает ли mod_proxy_wstunnel.so, который я модифицировал, или нет для wss://. Когда он не работал, файл журнала catalina.out будет показывать:

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

Заключительные мысли

Хотя я использую Apache httpd 2.4.9, Я видел, где backports mod_proxy_wstunnel можно применить к версиям 2.2.x. Надеюсь, мои заметки выше могут быть применены к тем более старым версиям.

Ответ 2

Если вы не хотите, чтобы Apache завершал соединение SSL (и пересылал незашифрованный трафик WebSocket), но SSL завершался на конечном целевом сервере WebSocket и, которые хотят использовать WSS на WebSocket трафик, поступающий в Apache, то mod_proxy_connect может просто подключиться через необработанный трафик. Не уверен. Мне было бы интересно, если это сработает.

Если выше не выполняется, вот дополнительная информация:

В любом случае использование Apache будет сильно ограничивать масштабируемость в отношении количества одновременно обслуживаемых соединений WebSocket, поскольку каждое соединение WS будет потреблять 1 процесс/поток на Apache.