我无法通过HTTPS使用apache代理使节点后端上的websocket工作来连接到节点实例。 如果不使用(apache)http代理,Websockets可以正常工作。
我的设置:我有一个具有多个虚拟主机的Apache服务器。 我有一个HTTPS网页myserver.com和HTTPS API与api.myserver.com子域中的节点/ express / ws通过代理,将请求redirect到端口3333上运行的node.js实例(在PM2上的多个实例) 。
这是我的子域的apache虚拟主机:
<VirtualHost *:443> ServerName api.myserver.com ServerAdmin [email protected] DocumentRoot /var/www/html/myserver/api Options -Indexes SSLEngine on SSLProtocol all -SSLv2 SSLCipherSuite ALL:!ADH:!EXPORT:!SSLv2:RC4+RSA:+HIGH:+MEDIUM SSLCertificateFile /etc/apache2/certs/STAR_myserver_co.crt SSLCertificateKeyFile /etc/apache2/certs/myserver_private_key.pem SSLCertificateChainFile /etc/apache2/certs/STAR_myserver_co.ca-bundle SSLProxyEngine On ProxyPreserveHost On ProxyRequests Off # This is for websocket requests ProxyPass /wss/ wss://localhost:3333/ ProxyPassReverse /wss/ wss://localhost:3333/ # This is for normal requests ProxyPass / https://localhost:3333/ ProxyPassReverse / https://localhost:3333/ </VirtualHost>
这适用于将连接redirect到后端节点。 我已经安装了mod_proxy,mod_proxy_http和mod_proxy_wstunnel。
这是node.js API后端:首先,我初始化express,session等
// express, session and mongodb session storage var express = require('express') var session = require('express-session') var MongoStore = require('connect-mongo')(session) var app = express() // configure sessionStore and sessions, mongodb, etc... // Certificates and credentials for HTTPS server var fs = require('fs') var privateKey = fs.readFileSync(__dirname + '/certs/myserver_private_key.pem', 'utf8') var certificate = fs.readFileSync(__dirname + '/certs/myserver_cert.pem', 'utf8') var ca = fs.readFileSync(__dirname + '/certs/myserver_ca.pem', 'utf8') var credentials = {key: privateKey, cert: certificate, ca: ca} app.enable('trust proxy') app.set("trust proxy", 1)
然后,我使用与APACHE相同的证书安全地设置HTTPS服务器:
// Setup HTTPS server var https = require('https') var server = https.createServer(credentials, app) server.listen(appPort, 'localhost', function () { // Server up and running! var host = server.address().address var port = server.address().port console.log('myserver listening at https://%s:%s', host, port) })
最后,我设置了websocket连接:
// setup Websockets wss = new WebSocketServer({ server: server }) wss.on('connection', function connection(ws) { var cookies = cookie.parse(ws.upgradeReq.headers.cookie) var sid = cookieParser.signedCookie(cookies["connect.sid"], myserver_secret) // extract user credentials and data from cookie/sid, // get the session object sessionStore.get(sid, function (err, ss) { ... }) })
然后,我的客户端只是尝试连接到websockets安全(因为,作为一个HTTPS应用程序,我不能使用ws://不安全的websockets连接):
window.WebSocket = window.WebSocket || window.MozWebSocket webSocket = new WebSocket('wss://' + location.host + '/wss')
然后我总是得到相同的错误302:
[Error] WebSocket connection to 'wss://api.myserver.com/wss' failed: Unexpected response code: 302
如果我在本地服务器上直接testing节点实例https:// localhost:3333 /,它工作正常,并且websockets正常工作。
任何想法如何解决这个问题? Apache代理模块的wsredirect有问题吗?
您已经在Apache上设置了HTTPS,因此您可以获得从客户端到物理服务器的安全连接。 你代理从Apache到你本地主机上的node.js应用程序的连接,所以唯一可能的窃听者已经在你的盒子上。 你已经告诉你的应用程序信任代理与app.enable('trust proxy'); app.set("trust proxy", 1); app.enable('trust proxy'); app.set("trust proxy", 1);
那么为什么还要为node.jsconfigurationHTTPS呢? 我们用apache / nginx / haproxy前端我们的web应用程序的一个主要原因是TLS卸载。
除了使用远程客户端IP获取X-Forwarded-For标题外,启用代理信任还可以让您获得X-Forwarded-Proto以便Express应用程序知道连接是http还是https。 因此,除了为您的应用程序引入开销之外,Apache和节点之间的https层还很less。
我无法帮助您用Apache代理WebSockets。 当它还是一系列httpd补丁时,我开始使用Apache。 我在WebSockets回滚之前退出了,我不能冒险从车皮上掉下来。 太多痛苦的回忆。 如果你好奇的话,下面是我如何使用haproxy将WebSocket传递给我的一个快速应用程序:
mode http option forwardfor frontend http-in bind :::80 v4v6 bind :::443 v4v6 ssl crt /etc/ssl/private http-request set-header X-Forwarded-Proto https if { ssl_fc } http-request set-header X-Forwarded-Port %[dst_port] acl is_websocket hdr(Upgrade) -i WebSocket acl is_websocket hdr_beg(Host) -i ws redirect scheme https code 301 if !is_websocket !{ ssl_fc } use_backend websocket_server if is_websocket backend websocket_server timeout queue 5s timeout server 86400s timeout connect 86400s server node_app 127.0.0.1:8080 backend ...others...