apache - 使用 apache 隧道安全 websocket 连接

标签 apache ssl websocket mod-proxy

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

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

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

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

我怎样才能通过 Apache 服务器完成我的 websocket 连接隧道?

最佳答案

我已经开始工作了。

场景

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

通过 Apache httpd 的浏览器 WebSocket,反向代理到 Tomcat 中的 Web 应用程序。所有从前到后的 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 在使用 wss://方案时不支持 SSL。它最终尝试以明文方式连接到后端 (Tomcat),这导致 SSL 握手失败。查看错误 here .因此,您必须按照错误报告中的建议更正自己修补 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配置文件
...
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,I've seen where backports of mod_proxy_wstunnel can be applied to versions 2.2.x .希望我上面的笔记可以应用于那些旧版本。

关于apache - 使用 apache 隧道安全 websocket 连接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11468154/

相关文章:

c++ - Apache 模块命令解析器原型(prototype)

ssl - 管理 Multi-Tenancy 网站的 SSL 证书

ios - 从 OSX 复制 p12 证书,应用程序在调用 Web API 时将使用该证书

tomcat - JBoss 支持 Websockets 吗?

python - 使用 Apache 代理对 Tornado 的请求 - 502 错误

php - 如何关闭 CodeIgniter 应用程序?

django - 在 Ubuntu 11 上的 Apache 2 上使用 virtualenv 的多个 Django 应用程序

javascript window.location 删除 SSL 协议(protocol)

android - 在 Heroku 上使用 node.js 的 Pusher/PubNub 替代方案

php - 如何在共享主机上部署 laravel websockets?