How to make 2 nodejs servers connect with SSL websocket? - node.js

I am trying to make 2 servers communicate via socket.io library and SSL.
This used to work until an upgrade of socket.io package (can't tell you which).
I have managed to fix secure connection with a browser. I have also made it work between unsecure (http) servers. But the secure (https) servers refuse to connect between themselves. You may argue that socket.io is not made for server to server communications, but it would save me lots of work to fix it.
I am now running:
node: 7.5.0
express: 4.16.2
socket.io (and socket.io-client): 2.0.3
I cannot even make simple examples below work (removing all my middleware).
node server
// Use SSL certificate
const cert_path = "..";
const fs = require('fs');
const https_options = {
key: fs.readFileSync(cert_path+'/privkey.pem'),
cert: fs.readFileSync(cert_path+'/cert.pem')
};
const app = require('express')();
const https = require('https');
const server = https.createServer(https_options, app);
const io = require('socket.io')(server);
server.listen(8000);
io.on('connection', function (socket) {
console.log("connected");
});
node client
const io = require('socket.io-client');
const socket = io.connect(
'https://localhost:8000',
{secure: true}
);
socket.on("connect", function () {
console.log("connected");
});
Nothing happens, none of them connect. Any idea why?
EDIT: I'm getting both connect_error and reconnect_error that pop every 5s on client side:
{ Error: xhr poll error
at XHR.Transport.onError (../node_modules/engine.io-client/lib/transport.js:64:13)
at Request.<anonymous> (../node_modules/engine.io-client/lib/transports/polling-xhr.js:128:10)
at Request.Emitter.emit (../node_modules/component-emitter/index.js:133:20)
at Request.onError (../node_modules/engine.io-client/lib/transports/polling-xhr.js:310:8)
at Timeout._onTimeout (../node_modules/engine.io-client/lib/transports/polling-xhr.js:257:18)
at ontimeout (timers.js:365:14)
at tryOnTimeout (timers.js:237:5)
at Timer.listOnTimeout (timers.js:207:5) type: 'TransportError', description: 503 }
Digging further in the errors, I see it may come from the certificate. But while I apply several workarounds of SO, I'm getting consecutively ECONNREFUSED, UNABLE_TO_VERIFY_LEAF_SIGNATURE, and finally DEPTH_ZERO_SELF_SIGNED_CERT...

After trying hard:
re-generate my Let's Encrypt certificate
re-generate my self-signed certificates (openssl) and use them by server+client
tinker with socket.io connect options (secure, rejectUnauthorized, ..)
tinker with nodejs global setup even (process.env['NODE_TLS_REJECT_UNAUTHORIZED'])
I finally stumbled on this page of github. It solved my issue and it's worth sharing it.
node client
const https = require('https');
https.globalAgent.options.rejectUnauthorized = false;
const io = require('socket.io-client');
const sockets = io.connect('https://localhost:8001', {agent: https.globalAgent});
Even if I would have preferred getting my connection authorized in the first place, this will work for me.

Related

Permessage-Deflate Extension Breaks Socket.io WebSocket Communication (400 Bad Request)

I need to enable compression for websocket communication (using socketio). I did this by setting the perMessageDeflate to true which worked fine in my development environment and on a staging server (verified with Wireshark). However, on our production server the websocket connection fails and socketio (v4.4.1) falls back to polling. Both use nginx as a reverse proxy with the same configuration. Chromium console shows
WebSocket connection to
'wss://***/ws-test/socket.io/?EIO=4&transport=websocket&sid=9P4EelJhF0CcxvwNAAAE'
failed: Error during WebSocket handshake: Unexpected response code:
400
I created a minimal sample that shows the same behaviour. Nodejs app on the server:
const express = require('express');
const app = express();
const http = require('http');
const server = http.createServer(app);
const { Server } = require("socket.io");
const io = new Server(server, {
perMessageDeflate: true
});
app.get('/', (req, res) => {
res.sendFile(__dirname + '/index.html');
});
server.listen(3001, () => {
console.log('listening on *:3001');
});
Client:
...
<script src="socket.io/socket.io.js"></script>
<script>
var socketio_path = window.location.pathname + 'socket.io';
var socket = io( { path: socketio_path } );
</script>
</body>
The error 400 seems to come from the nodejs server, not from nginx. I enabled all kinds of logging but couldn't find any related messages. Software versions are also reasonably close and up-to-date: nginx 1.18.0 on both (staging and production), nodejs 14.19.0/14.18.1 (staging/prod). Do you have any ideas that could help making this work on the production server?
It turned out, the issue is the Azure Application Gateway used on the production server. When it's bypassed, the websocket connection (with perMessageDeflate enabled) works fine.
Switching to a newer version of the Azure Application Gateway (v2 instead of v1) solved the issue in the end.

Prevent external users to connect websocket (wss)

I'm building a react-nodejs website at https://example.com and it utilizes websocket (using socket.io library), but right now people can just npm install socket.io-client and connect to my websocket using this:
const {io} = require("socket.io-client");
const socket = io("wss://example.com");
Now they can emit/listen to my backend. At the moment, I use ReCAPTCHA to prevent bots so you need to pass a token to connect to the socket.io server, I was wondering whether there is a better solution than this.
You are correct, you can't prevent people from trying to connect to your socket.io server manually with a custom script using socket.io-client. Normally that's a reason for you to design the server in a way, that connected client sockets can't do any harm. Client sockets are usually on the receiving side.
But of course there is a best-practice on how to authenticate the connecting clients, and, if the authentication is failing, to prevent the connection. Checkout out the socket.io Documentation about client options. There is an auth field available :
Client
import { io } from "socket.io-client";
const socket = io({
auth: {
token: "abcd"
}
});
Server
io.on("connection", (socket) => {
console.log(socket.handshake.auth); // prints { token: "abcd" }
});
So once the user is logged in, use the JWT (Token) (assuming you having a JWT Authentication in place) and pass it during establishing the connection. Then validate the JWT on the server side.
client-certificate authentication
There is also a more advanced option for client authentication which uses certificates:
Client
const fs = require("fs");
const socket = require("socket.io-client")("https://example.com", {
ca: fs.readFileSync("./server-cert.pem"),
cert: fs.readFileSync("./client-cert.pem"),
key: fs.readFileSync("./client-key.pem"),
});
Server
const fs = require("fs");
const server = require("https").createServer({
cert: fs.readFileSync("./server-cert.pem"),
key: fs.readFileSync("./server-key.pem"),
requestCert: true,
ca: [
fs.readFileSync('client-cert.pem')
]
});
const io = require("socket.io")(server);

WebSocket is closed before the connection is established. Socket.io and React

I want to make an Chat application with Socket.io and I've followed this tutorial: https://youtu.be/ZwFA3YMfkoc. I am using React and Node.js
Everything works fine while using it locally and even on different devices on my Network. However if I am hosting my Backend on Heroku it doesn't work.
The error Message is:
WebSocket connection to 'URL' failed: WebSocket is closed before the connection is established.
(URL is the URL of my Backend with the Port). I am using SSL.
I've already tried to enable session affinity but it already was enabled.
My backend code is: (atleast the code that I think is important to the Problem)
const app = express();
const server = http.createServer(app);
const io = socketio(server);
app.use(cors());
server.listen(PORT, () => console.log("Server started on " + PORT));
My frontend code is written in React and is:
var connectionOptions = {
"force new connection": true,
reconnectionAttempts: "Infinity",
timeout: 10000,
transports: ["websocket"],
};
const ENDPOINT = "URL";
socket = io(ENDPOINT, connectionOptions);
So I've fixed my Problem.
The URL on the client side had to be without the Port.
So for example:
const ENDPOINT = "https://web.site.com";
and not:
const ENDPOINT = "https://web.site.com:1337";
Call socket.connect() or just add autoconnect: true in options you are providing.

Tchat with NodeJS on port 1337 doesnt work (server on Debian)

Hello and sorry for my bad english, but I have a problem with my tchat created with NodeJS. My server.js running on port 1337 in my VPS, but with port isn't secure and i don't know how secure him.
All my website is secure with a Let's Encrypt certificate, but not the 1337 port...
I tried to secure https://www.temtem-france.com:1337/ with Let's encrypt but it doesn't work... :/
I have this on the beggining of my server.js :
var https = require('https'),
fs = require('fs'),
mysql = require('mysql');
var options = {
key: fs.readFileSync('/etc/letsencrypt/archive/temtem-france.com/privkey.pem'),
cert: fs.readFileSync('/etc/letsencrypt/archive/temtem-france.com/cert.pem'),
ca: fs.readFileSync('/etc/letsencrypt/archive/temtem-france.com/chain.pem'),
requestCert: false, rejectUnauthorized: false
};
var httpsServer = https.createServer(options, function (req, res) {
res.writeHead(200);
console.log('Affiché');
res.end("Hello World\n");
}).listen(1337);
With inspector on my client.php code, i have this :
socket.io.js:1 Failed to load resource: net::ERR_CERT_DATE_INVALID
client.js:31 Uncaught ReferenceError: io is not defined
at client.js:31
at HTMLDocument.<anonymous> (client.js:117)
at i (jquery-min.js:2)
at Object.fireWith [as resolveWith] (jquery-min.js:2)
at Function.ready (jquery-min.js:2)
at HTMLDocument.K (jquery-min.js:2)
Unchecked runtime.lastError: The message port closed before a response was received.
I already managed to run my codes, but it was several months ago and since I am not very comfortable with server management, I forgot how I had succeeded.
Thanks a lot if you can help me !

Socket io + Node Error: getaddrinfo EADDRINFO after a few hours with < 100 connections

I have a node + socket server running to simply emit to the clients. The client is on an https domain name. After a couple or few hours my socket server starts to log { [Error: getaddrinfo EADDRINFO] code: 'EADDRINFO', errno: 'EADDRINFO', syscall: 'getaddrinfo', fatal: true } killing all of the websocket connections and not being corrected until I restart the script that is running the server. After I restard the server script everything is fine for 2-3 hours.
The load is low, only me opening 10 - 30 tabs in my web browser.
I have searched all over including these and other questions 25684451,12565209, and 29536649.
As I mentioned my domain is https://redacted.com and as a result need, I believe, the socket server needs to be https as well, which is how I built it.
Is it hardware? Ubuntu's open file limit? https issue? dns routing issue? socket connection limit? How do I even test?
Ubuntu 14.04, nodeJS v0.10.25, Socket.io 1.3.6. at AWS t2.micro for testing.
Server:
var https = require('https'),
fs = require('fs'),
mysql = require('mysql');
var options = {
key: fs.readFileSync('/etc/ssl/certs/key2/redacted.key'),
cert: fs.readFileSync('/etc/ssl/certs/key2/STAR_redacted_com.crt'),
ca: fs.readFileSync('/etc/ssl/certs/key2/redacted.ca-bundle')
};
var app = https.createServer(options);
var client = require('socket.io').listen(app); //socket.io server listens to https connections
app.listen(8080);
var connected = 1;
client.on('connection', function(socket){
function sendStatus(s){
socket.emit('status', s);
}
sendStatus('Connected');
console.log(connected);
connected++;
});
Client:
try{
var socket = new io.connect('ws.redacted.com:8080');
//var socket = io.connect('ws.redacted.com:8080');
console.log(socket);
}catch(e){
//set status to warn user
console.log('3');
console.log(e);
}

Resources