Туннелирование безопасных соединений через веб-сокеты с 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
это выглядело так:
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 может просто подключиться через сырой трафик. Точно сказать не могу. Мне также было бы интересно, если это работает.
Если выше не имеет места, вот больше информации:
- https://serverfault.com/questions/290121/configuring-apache2-to-proxy-websocket
- https://issues.apache.org/bugzilla/show_bug.cgi?id=47485
- http://blog.alex.org.uk/2012/02/16/using-apache-websocket-to-proxy-tcp-connection/
В любом случае использование Apache серьезно ограничит масштабируемость в отношении количества одновременно обслуживаемых подключений WebSocket, поскольку каждое подключение WS будет использовать 1 процесс / поток в Apache.
Я пытаюсь установить этот https://github.com/kawasima/mod_proxy_websocket. Надеюсь, поможет.