無法讓 websockets 通過 apache HTTPS 代理工作(302 錯誤)
我無法通過 HTTPS 使用 apache 代理在節點後端使 websockets 工作以連接到節點實例。如果不使用 (apache) http(s) 代理,Websocket 將正常工作。
我的設置:我有一個帶有多個虛擬主機的 apache 伺服器。我有一個用於 myserver.com 的 HTTPS 網頁以及通過代理在 api.myserver.com 子域中帶有 node/express/ws 的 HTTPS API,它將請求重定向到在埠 3333 上執行的 node.js 實例(PM2 上的多個實例) .
這是我的子域的 apache 虛擬主機:
<VirtualHost *:443> ServerName api.myserver.com ServerAdmin hello@myserver.com 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>
這適用於將連接重定向到 node express 後端。我已經安裝了 mod_proxy、mod_proxy_http 和 mod_proxy_wstunnel。
這是 node.js API 後端:首先,我初始化 express、會話等。
// 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
如果我在本地伺服器上直接對節點實例https://localhost:3333/進行測試,它工作得很好,並且 websockets 可以正常工作。
知道如何解決這個問題嗎?Apache 代理模組進行的 ws 重定向是否存在問題?
您已經在 Apache 上設置了 HTTPS,因此您已經獲得了從客戶端到物理伺服器的安全連接。您正在代理從 Apache 到本地主機上的 node.js 應用程序的連接,因此唯一可能的竊聽者已經在您的盒子上。您已經告訴您的應用信任代理
app.enable('trust proxy'); app.set("trust proxy", 1);
那麼為什麼還要為 node.js 配置 HTTPS?我們使用 apache/nginx/haproxy 對 Web 應用程序進行前端處理的主要原因之一是 TLS 解除安裝。
除了
X-Forwarded-For
使用遠端客戶端 IP 獲取標頭之外,啟用代理信任還可以X-Forwarded-Proto
讓您的 Express 應用程序知道連接是 http 還是 https。因此,Apache 和節點之間的 https 層除了為您的應用程序引入成本之外幾乎沒有做任何事情。我無法幫助您使用 Apache 代理 WebSocket。當它仍然是 httpd 的一系列更新檔時,我開始使用 Apache。我在 WebSockets 出現之前就退出了,我不能冒險從馬車上掉下來。太多痛苦的回憶。如果您好奇,以下是我使用 haproxy 將 WebSockets 傳遞給我的一個快速應用程序的方法:
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...