Я не могу заставить веб-сокеты работать на серверной части узла, используя прокси-сервер apache через HTTPS для подключения к экземпляру узла. Веб-узлы работают правильно, если не используется прокси (apache) http (s).
Моя установка: у меня есть сервер apache с несколькими виртуальными хостами. У меня есть веб-страница HTTPS для myserver.com и HTTPS API с node / express / ws в поддомене api.myserver.com через прокси, который перенаправляет запросы к экземпляру node.js (несколько экземпляров на PM2), работающему на порту 3333 .
Это мой виртуальный хост 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>
Это нормально работает для перенаправления соединений на серверную часть Express. Я установил mod_proxy, mod_proxy_http и mod_proxy_wstunnel.
Это серверная часть API node.js: сначала я инициализирую экспресс, сеансы и т. Д.
// 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)
Затем я безопасно настраиваю сервер HTTPS, используя те же сертификаты, что и в APACHE:
// 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)
})
Наконец, я настраиваю соединения через веб-сокеты:
// 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) {
...
})
})
Затем мои клиенты просто пытаются безопасно подключиться к веб-узлам (потому что, будучи приложением HTTPS, я не могу использовать незащищенное соединение ws: // веб-узлы):
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 / он работает отлично, и веб-сокеты работают так, как должны.
Есть идеи, как это решить? Есть ли проблема с перенаправлением ws прокси-модулями Apache?
Вы уже настроили HTTPS на Apache, поэтому у вас есть безопасное соединение от клиента к вашему физическому серверу. Вы проксируете соединение Apache с вашим приложением node.js на localhost, поэтому единственные возможные перехватчики физически уже находятся на вашем компьютере. Вы сообщили своему приложению доверять прокси с участием app.enable('trust proxy'); app.set("trust proxy", 1);
Так зачем вам также настраивать HTTPS для node.js? Одна из основных причин, по которой мы используем apache / nginx / haproxy для внешнего интерфейса наших веб-приложений, - это разгрузка TLS.
Помимо получения X-Forwarded-For
заголовок с IP-адресом удаленных клиентов, включение доверия прокси также дает вам X-Forwarded-Proto
чтобы ваше приложение Express знало, было ли соединение http или https. Таким образом, уровень https между Apache и node мало что делает, кроме накладных расходов для вашего приложения.
Я не могу помочь вам с проксированием WebSockets с помощью Apache. Я начал использовать Apache еще тогда, когда это была еще серия патчей для httpd. Я ушел до того, как появились 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...