使用apache隧道安全websocket连接

mst*_*tud 20 apache ssl mod-proxy websocket

我有一个只能通过HTTPS访问的Apache运行.我想从另一个在同一台机器上运行的服务器应用程序提供websockets,但由于客户端无法连接到443以外的另一个端口到我们的服务器,因此需要通过Apache代理这些websocket连接.

现在,我已经安装了mod_proxy并按如下方式配置它:

SSLProxyEngine on
ProxyPass /ws https://127.0.0.1:9001
Run Code Online (Sandbox Code Playgroud)

但这不起作用.我现在可以在我的浏览器中连接到https:// server/ws,但是apache似乎吞下了部分websockets标头,因此真正的websocket连接不起作用.

如何通过Apache服务器完成对websocket连接的隧道连接?

Tom*_*ley 27

我有它的工作.

脚本

-------------       ----------------       ----------
| Browser   |<----->| Apache httpd |<----->| Tomcat |
|           |  SSL  |    2.4.9     |  SSL  | 7.0.52 |
-------------       ----------------       ----------
Run Code Online (Sandbox Code Playgroud)

浏览器WebSocket通过Apache httpd,反向代理Tomcat中的Web应用程序.所有SSL从前到后.

这是每件作品的配置:

Browser Client

请注意,在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>
Run Code Online (Sandbox Code Playgroud)

Apache httpd

我正在使用Apache httpd 2.4.9,开箱即用提供了mod_proxy_wstunnel.但是,在使用wss:// scheme时,提供的mod_proxy_wstunnel.so不支持SSL.它最终尝试以明文方式连接到后端(Tomcat),这使SSL握手失败.看这里的 bug .因此,您必须通过遵循错误报告中的建议更正来自行修补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;
Run Code Online (Sandbox Code Playgroud)

然后重建模块并用新的模块替换新的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
Run Code Online (Sandbox Code Playgroud)

注意最后一个开关:--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/
Run Code Online (Sandbox Code Playgroud)

如果你没有在上面的配置中看到我的注释,这里又是:严格注意斜杠"/"或缺少!

另外,如果您在apache日志中看到调试日志语句表示已关闭wss连接,则可能会像我一样启用mod_reqtimeout,因此请确保未加载:

#LoadModule reqtimeout_module modules/mod_reqtimeout.so
Run Code Online (Sandbox Code Playgroud)

Tomcat的

假设您的HTTP连接器设置正确,那么在tomcat中配置的并不多.虽然为了帮助调试,我发现创建一个$CATALINA_HOME/bin/setenv.sh看起来像这样的东西很有用:

setenv.sh
CATALINA_OPTS=$CATALINA_OPTS" -Djavax.net.debug=all -Djavax.net.debug=ssl:handshake:verbose"
Run Code Online (Sandbox Code Playgroud)

这让我可以看到我修改的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()
Run Code Online (Sandbox Code Playgroud)

最后的想法

虽然我使用Apache httpd 2.4.9,但我已经看到mod_proxy_wstunnel的backport可以应用于2.2.x版本.希望我上面的笔记可以应用于那些旧版本.

  • 值得注意的是,mod_proxy_wstunnel还可以在SSL代理后面包装非SSL websockets. (3认同)

obe*_*tet 1

如果您不希望 Apache 终止 SSL 连接(并转发未加密的 WebSocket 流量),但在最终目标 WebSocket 服务器上终止 SSL ,并且只想在进入 Apache 的 WebSocket 流量上使用 WSS,则mod_proxy_connect可能能够仅通过原始流量进行连接。没有把握。如果有效的话我也会感兴趣。

如果上述不成立,这里有更多信息:

无论如何,使用 Apache 将严重限制并发服务的 WebSocket 连接数量的可扩展性,因为每个 WS 连接将消耗 Apache 上的 1 个进程/线程。