Туннелирование безопасных соединений через веб-сокеты с Apache

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

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

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

Это не работает однако. Теперь я могу подключиться к https://server/ws в моем браузере, но apache, похоже, поглощает часть заголовков websockets, так что реальные соединения websocket не работают.

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

3 ответа

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

сценарий

-------------       ----------------       ----------
| 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 для серверной части.

Тестовая страница
<!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

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

Кот

Предполагая, что ваш 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, я видел, где обратные порты mod_proxy_wstunnel могут применяться к версиям 2.2.x. Hopefully my notes above can be applied to those older versions.

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

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

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

Я пытаюсь установить этот https://github.com/kawasima/mod_proxy_websocket. Надеюсь, поможет.

Другие вопросы по тегам