使用 Socket.IO 与 Apache、Node.js、SSL 和 CloudFlare (HTTP 500)
Using Socket.IO with Apache, Node.js, SSL and CloudFlare (HTTP 500)
我正在创建一个将连接到 Node.js Socket.IO WebSocket 的网站。此节点服务器在端口 4923 上 运行,Socket.IO 节点配置在端点 /socket
上。我不想通过 https://domain1.com:4932/socket
连接,而是从指向我的 Apache 服务器的 https://domain1.com/socket.io
连接。防火墙为了屏蔽https://domain1.com:4932/socket
.
而屏蔽了4932端口
Apache 服务器应代理从 https://domain1.com/socket.io
到 https://localhost:4932/socket
的所有连接,包括 SSL。 SSL 证书是 Let's Encrypt 的证书。
创建新连接时,Apache 和 Node.JS 服务器之间的 SSL 握手失败。
我的网站流量通过已激活完整通用 SSL 的 Cloudflare。
在apache虚拟主机中,我有如下配置:
<VirtualHost *:443>
...
SSLEngine On
SSLProxyEngine On
SSLCertificateFile /etc/letsencrypt/live/domain1.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/domain1.com/privkey.pem
Include /etc/letsencrypt/options-ssl-apache.conf
# Handle wss proxy connections
RewriteEngine On
RewriteCond %{REQUEST_URI} ^/socket.io [NC]
RewriteCond %{QUERY_STRING} transport=websocket [NC]
RewriteRule /(.*) ws://localhost:4923/ [P,L]
# Autogenerated ProxyPass
ProxyPass /socket.io http://localhost:4923/socket
ProxyPassReverse /socket.io http://localhost:4923/socket
...
</VirtualHost>
我的 node.js 服务器通过 HTTPS:
const port = 4923;
const fs = require('fs');
var options = {
key: fs.readFileSync('/etc/letsencrypt/live/domain1.com/privkey.pem'),
cert: fs.readFileSync('/etc/letsencrypt/live/domain1.com/fullchain.pem'),
};
var app = require('https').createServer(options);
var io = require('socket.io')(app, {
path: '/socket',
serveClient: false,
pingInterval: 10000,
pingTimeout: 5000,
cookie: false
});
app.listen(port);
io.httpServer.on('listening', function () {
console.log("Listening on", port);
});
io.on('connect', (socket) => {
console.log("Socket connected: " + socket.id);
socket.on('disconnect', () => {
console.log("Socket disconnected: " + socket.id);
});
socket.on('download-request', (id) => {
onDownloadRequest(socket, id);
});
});
function onDownloadRequest(socket, id) {
console.log("Request from " + socket.id + " and video id: " + id);
}
同时,在客户端,我在 window 加载时创建一个新的 WebSocket 连接,然后在单击按钮时发送消息。
...
const websocketOptions = {
server : {
protocol : 'https://',
host : 'domain1.com',
port : 443,
endpoint : '/socket.io'
}
};
function requestDownload() {
if (isAnySelected() || socket !== null) {
console.log(document.getElementsByClassName("download-input-box")[0].value);
socket.emit('download-request', {id: document.getElementsByClassName("download-input-box")[0].value});
}
}
function createConnection() {
console.log("Creating websocket connection!");
socket = io.connect(websocketOptions.server.protocol
+ websocketOptions.server.host
+ ':'
+ websocketOptions.server.port
+ websocketOptions.server.endpoint,
{transport: 'websocket'});
}
function onWindowLoad() {
createConnection();
}
window.onload = onWindowLoad;
这会导致响应代码 500 和 apache 错误日志中的错误消息:
AH00898: Error during SSL Handshake with remote server returned by /socket.io/, referer: https://domain1.com/
我做错了什么?感谢任何帮助。
编辑:
既然 SSL 握手问题已经解决,我又遇到了一个新问题。可以查看here.
您不能使用 SSL“localhost”,所以有三个选项:
使用一个可以请求 SSL 的域
将“localhost”用于 HTTP(不是 s)
将“localhost”与 SSL 结合使用,但在 Apache2 配置中禁用“SSLProxyCheckPeerName”。 (参见 this 参考)
SSLProxyCheckPeerName 关闭
我正在创建一个将连接到 Node.js Socket.IO WebSocket 的网站。此节点服务器在端口 4923 上 运行,Socket.IO 节点配置在端点 /socket
上。我不想通过 https://domain1.com:4932/socket
连接,而是从指向我的 Apache 服务器的 https://domain1.com/socket.io
连接。防火墙为了屏蔽https://domain1.com:4932/socket
.
Apache 服务器应代理从 https://domain1.com/socket.io
到 https://localhost:4932/socket
的所有连接,包括 SSL。 SSL 证书是 Let's Encrypt 的证书。
创建新连接时,Apache 和 Node.JS 服务器之间的 SSL 握手失败。
我的网站流量通过已激活完整通用 SSL 的 Cloudflare。
在apache虚拟主机中,我有如下配置:
<VirtualHost *:443>
...
SSLEngine On
SSLProxyEngine On
SSLCertificateFile /etc/letsencrypt/live/domain1.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/domain1.com/privkey.pem
Include /etc/letsencrypt/options-ssl-apache.conf
# Handle wss proxy connections
RewriteEngine On
RewriteCond %{REQUEST_URI} ^/socket.io [NC]
RewriteCond %{QUERY_STRING} transport=websocket [NC]
RewriteRule /(.*) ws://localhost:4923/ [P,L]
# Autogenerated ProxyPass
ProxyPass /socket.io http://localhost:4923/socket
ProxyPassReverse /socket.io http://localhost:4923/socket
...
</VirtualHost>
我的 node.js 服务器通过 HTTPS:
const port = 4923;
const fs = require('fs');
var options = {
key: fs.readFileSync('/etc/letsencrypt/live/domain1.com/privkey.pem'),
cert: fs.readFileSync('/etc/letsencrypt/live/domain1.com/fullchain.pem'),
};
var app = require('https').createServer(options);
var io = require('socket.io')(app, {
path: '/socket',
serveClient: false,
pingInterval: 10000,
pingTimeout: 5000,
cookie: false
});
app.listen(port);
io.httpServer.on('listening', function () {
console.log("Listening on", port);
});
io.on('connect', (socket) => {
console.log("Socket connected: " + socket.id);
socket.on('disconnect', () => {
console.log("Socket disconnected: " + socket.id);
});
socket.on('download-request', (id) => {
onDownloadRequest(socket, id);
});
});
function onDownloadRequest(socket, id) {
console.log("Request from " + socket.id + " and video id: " + id);
}
同时,在客户端,我在 window 加载时创建一个新的 WebSocket 连接,然后在单击按钮时发送消息。
...
const websocketOptions = {
server : {
protocol : 'https://',
host : 'domain1.com',
port : 443,
endpoint : '/socket.io'
}
};
function requestDownload() {
if (isAnySelected() || socket !== null) {
console.log(document.getElementsByClassName("download-input-box")[0].value);
socket.emit('download-request', {id: document.getElementsByClassName("download-input-box")[0].value});
}
}
function createConnection() {
console.log("Creating websocket connection!");
socket = io.connect(websocketOptions.server.protocol
+ websocketOptions.server.host
+ ':'
+ websocketOptions.server.port
+ websocketOptions.server.endpoint,
{transport: 'websocket'});
}
function onWindowLoad() {
createConnection();
}
window.onload = onWindowLoad;
这会导致响应代码 500 和 apache 错误日志中的错误消息:
AH00898: Error during SSL Handshake with remote server returned by /socket.io/, referer: https://domain1.com/
我做错了什么?感谢任何帮助。
编辑:
既然 SSL 握手问题已经解决,我又遇到了一个新问题。可以查看here.
您不能使用 SSL“localhost”,所以有三个选项:
使用一个可以请求 SSL 的域
将“localhost”用于 HTTP(不是 s)
将“localhost”与 SSL 结合使用,但在 Apache2 配置中禁用“SSLProxyCheckPeerName”。 (参见 this 参考)
SSLProxyCheckPeerName 关闭