How to debug ECONNRESET with socket.io and express encountered when running a Nessus scan? - node.js

I'm encountering ECONNRESET errors that are crashing my node server when I run a Nessus Essentials basic network scan:
node:events:505
throw er; // Unhandled 'error' event
^
Error: read ECONNRESET
at TCP.onStreamRead (node:internal/stream_base_commons:217:20)
Emitted 'error' event on Socket instance at:
at emitErrorNT (node:internal/streams/destroy:157:8)
at emitErrorCloseNT (node:internal/streams/destroy:122:3)
at processTicksAndRejections (node:internal/process/task_queues:83:21) {
errno: -54,
code: 'ECONNRESET',
syscall: 'read'
}
I don't know exactly what Nessus is doing and I haven't managed to reproduce the error any other way, but I've identified two interesting facts:
The problem only occurs when running on Node versions < 16.16.0
It also goes away if I remove socket.io, or if I instantiate socket.io using a different port or HTTP server than my express server
For context, I've simplified my application down to a minimal project still exhibits the error:
import express from 'express';
import { Server } from 'socket.io';
import morgan from 'morgan';
import winston from 'winston';
const logger = winston.createLogger({
level: 'debug',
format: winston.format.combine(
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss:ms' }),
winston.format.splat(),
winston.format.prettyPrint(),
winston.format(info => {
info.level = `[${info.level.toUpperCase()}]`;
return info;
})(),
winston.format.printf(info => {
if (typeof info.message === 'object') {
info.message = JSON.stringify(info.message, null, 3);
}
return `${info.timestamp} ${info.level} ${info.message}`;
}),
),
transports: [new winston.transports.Console()]
});
const morganMiddleware = morgan(
':remote-addr :method :url :status :response-time ms',
{
stream: {
write: message => logger.http(message.trim()),
},
});
const app = express();
app.use(morganMiddleware);
const port = 3001;
const httpServer = app.listen(port, () => {
logger.info(
`Server is now listening on port ${port} ✓`,
);
new Server(httpServer);
});
export default app;
If I remove the new Server(httpServer); line, the app doesn't crash. So the error seems to be linked to socket.io and Express sharing a connection, and something going wrong during the vulnerability scan (something causing the socket to be dropped?), but I haven't managed to debug it further.
I have tried all sorts of ways of catching the error, but the only way that works is using process.on('uncaughtException'). That's not helpful however, because at that point, there is no safe way to recover from the error. The error bypasses all the error handling of both Express and socket.io.
I could of course "solve" the problem by upgrading to a more recent version of node, but I need to understand the problem in order to be sure that it's actually fixed and won't surface again in some other form. Also, I would like to be able to make my app more resilient by catching this sort of error if they were to occur in the future.
Or perhaps there's some way to separate socket.io from express that without using separate ports (which wouldn't be practical in my use-case). Can I use proxy websocket related requests through Express to socket.io without sharing an HTTP server?
Any suggestions, either to help understand/debug the problem, or to work around it, would be welcome.

After much investigation, I figured out what was going on.
The ECONNRESET error I was getting occurred when Socket.io received an HTTP2 upgrade request: upgrade events are intercepted by Socket.io so that websocket upgrades can be handled, but for HTTP2 upgrades, the socket was incorrectly being disconnected without any error handler to catch the error, which in turn led to the the application crashing.
I was able to fix the bug, which was released as engine.io 6.2.1.
The reason I was not seeing the issue with versions of Node older than 16.16.0 was actually unrelated: Nessus was, for some reason, sending malformed HTTP requests for the HTTP2 upgrade, using LF as line separators instead of CRLF. Starting with v16.16.0, Node now rejects such requests with a 400 Bad Request, which avoids the Socket.io issue.

Related

SignalR with React through NodeJS proxy

I'm trying to use SignalR on a React application through a NodeJS proxy using the NPM package 'http-proxy-middleware'. However, it appears that the connection is falling to connect. Below is the console log from the react application.
[2022-11-29T23:02:24.022Z] Information: Normalizing '/hubs/Chat' to 'https://localhost:3000/hubs/Chat'.
[2022-11-29T23:02:24.022Z] Debug: Starting connection with transfer format 'Text'.
[2022-11-29T23:02:24.023Z] Debug: Sending negotiation request: https://localhost:3000/hubs/Chat/negotiate?negotiateVersion=1.
[2022-11-29T23:02:24.642Z] Debug: Selecting transport 'WebSockets'.
[2022-11-29T23:02:24.643Z] Trace: (WebSockets transport) Connecting.
WebSocket connection to 'wss://localhost:3000/hubs/Chat?id=DykJoTMv9LiYrtjYHUCQVg' failed:
[2022-11-29T23:06:24.671Z] Information: (WebSockets transport) There was an error with the transport.
[2022-11-29T23:06:24.672Z] Error: Failed to start the transport 'WebSockets': Error: WebSocket failed to connect. The connection could not be found on the server, either the endpoint may not be a SignalR endpoint, the connection ID is not present on the server, or there is a proxy blocking WebSockets. If you have multiple servers check that sticky sessions are enabled.
[2022-11-29T23:06:24.672Z] Debug: Skipping transport 'ServerSentEvents' because it was disabled by the client.
[2022-11-29T23:06:24.672Z] Debug: Skipping transport 'LongPolling' because it was disabled by the client.
[2022-11-29T23:06:24.672Z] Error: Failed to start the connection: Error: Unable to connect to the server with any of the available transports. Error: WebSockets failed: Error: WebSocket failed to connect. The connection could not be found on the server, either the endpoint may not be a SignalR endpoint, the connection ID is not present on the server, or there is a proxy blocking WebSockets. If you have multiple servers check that sticky sessions are enabled. ServerSentEvents failed: Error: 'ServerSentEvents' is disabled by the client. LongPolling failed: Error: 'LongPolling' is disabled by the client.
Error establishing the connection: Error: Unable to connect to the server with any of the available transports. Error: WebSockets failed: Error: WebSocket failed to connect. The connection could not be found on the server, either the endpoint may not be a SignalR endpoint, the connection ID is not present on the server, or there is a proxy blocking WebSockets. If you have multiple servers check that sticky sessions are enabled. ServerSentEvents failed: Error: 'ServerSentEvents' is disabled by the client. LongPolling failed: Error: 'LongPolling' is disabled by the client.
at HttpConnection._createTransport (HttpConnection.ts:407:1)
at async HttpConnection._startInternal (HttpConnection.ts:283:1)
at async HttpConnection.start (HttpConnection.ts:137:1)
at async HubConnection._startInternal (HubConnection.ts:207:1)
at async HubConnection._startWithStateTransitions (HubConnection.ts:181:1)
If I connect directly to the .NET application, which is running the server-side part of SignalR, it works fine. So it's not a problem with that.
This is the code snippet from the React App which performs the connection.
this.hubConnection = new HubConnectionBuilder()
.withUrl("/hubs/chat",{
transport: HttpTransportType.WebSockets,
logger: LogLevel.Trace
})
.withAutomaticReconnect()
.build();
this.hubConnection.start().catch(error => console.log("Error establishing the connection: ", error));
The proxy is running in NodeJS through nodemon. Below is the code.
const PORT = 8000;
const express = require('express');
const https = require('https');
const fs = require('fs')
const {createProxyMiddleware} = require('http-proxy-middleware');
const app = express();
require('dotenv').config({ path: `.env.${process.env.NODE_ENV.trim()}` });
const httpsOptions = {
key: fs.readFileSync('./certs/cert.key'),
cert: fs.readFileSync('./certs/cert.pem')
}
app.use('/hubs/chat', createProxyMiddleware({
target: process.env.REACT_APP_APISERVICEURL,
changeOrigin: false ,
secure: false,
ws: true,
logger: console
}));
var secure = https.createServer(httpsOptions, app);
secure.listen(8000, () => console.log(`Server is running on port ${PORT}`));
The proxy does work for all other API calls to the .NET Application. The React application has the proxy attribute set in the package.json. The proxy then re-writes the URL and adds an API Token header to send to the API. Hence the use of http-proxy-middleware.
I have tried the below as well, but it still produces the same result.
const signlrRProxy = createProxyMiddleware({
target: process.env.REACT_APP_APISERVICEURL,
changeOrigin: false ,
secure: false,
ws: true,
logger: console
});
app.use('/hubs/chat', signlrRProxy);
var secure = https.createServer(httpsOptions, app);
secure.listen(8000, () => console.log(`Server is running on port ${PORT}`));
secure.on('upgrade', signlrRProxy.upgrade);
I am using the latest package versions to everything.

Error: No event 'socketConnect' in state 'SentPrelogin'

I am trying to connect my SQL Server database with node.js using knex but I am facing issue
Error: No event 'socketConnect' in state 'SentPrelogin'
at Connection.dispatchEvent (C:\Users\temp\Documents\PRactice\node_modules\tedious\lib\connection.js:1281:26)
at Connection.socketConnect (C:\Users\temp\Documents\PRactice\node_modules\tedious\lib\connection.js:1303:10)
at C:\Users\temp\Documents\PRactice\node_modules\tedious\lib\connection.js:1145:12
at Socket.onConnect (C:\Users\temp\Documents\PRactice\node_modules\tedious\lib\connector.js:106:7)
at Socket.emit (events.js:314:20)
at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1131:10)
Emitted 'error' event on Connection instance at:
at Connection.dispatchEvent (C:\Users\temp\Documents\PRactice\node_modules\tedious\lib\connection.js:1281:12)
at Connection.socketConnect (C:\Users\temp\Documents\PRactice\node_modules\tedious\lib\connection.js:1303:10)
[... lines matching original stack trace ...]
at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1131:10)
[nodemon] app crashed - waiting for file changes before starting...
My code is
var knex = require('knex')({
client: 'mssql',
version:"7_1",
connection: {
user: 'sa',
password: 'Admin#123',
server: 'localhost',
database: 'Demo'
}
});
knex.select("*").from("Country")
.then(function (depts){
depts.forEach((dept)=>{ //use of Arrow Function
console.log({...dept});
});
}).catch(function(err) {
// All the error can be checked in this piece of code
console.log(err);
}).finally(function() {
// To close the connection pool
knex.destroy();
});
You need to add a missing dependency:
npm install --save tedious
As of knex v0.95.0 you'll need to use the tedious library instead of mssql when connecting to an MSSQL database. According to the knex upgrade instructions:
MSSQL driver was completely reworked in order to address the multitude of connection pool, error handling and performance issues. Since the new implementation uses tedious library directly instead of mssql, please replace mssql with tedious in your dependencies if you are using a MSSQL database.
Installing the package above should resolve your issue. I also had to set the encrypt option to false when connecting to my local database to avoid this error:
ConnectionError: Failed to connect to localhost:1433 - self signed certificate

Database is not running in mongodb atlas due to unhandled promise exception

My code is running fine in localhost with no warnings but when I'm changing my connection string to the string provided by mongodb atlas, following errors are showing which you can see in the below pic and my browser keeps circulating plz guide me what I'm doing wrong
(node:13700) DeprecationWarning: 'open()' is deprecated in mongoose >=
4.11.0,
use 'openUri() instead, or set the 'useMongoClient' option if using 'connect()' or 'createConnection()'. See
http://mongoosejs.com/docs/4.x/docs/connections.html#use-mongo-client
(Use 'node --trace-deprecation... to show where the warning was
created)
Server started on port 3000
Error [MongoError]: failed to connect to server [undefined:27017] on first connect [Error: getaddrinfo ENOTFOUND undefined
at GetAddrInfoReqwrap.onlookup [as oncomplete] (dns.js:67:26) {}] name: 'MongoError'
at Pool. (C:\Users\Umer\Desktop\node\Alhamdulillah, complete webapp\All Validations Completed\node_modules\mongodb-core\lib\topologies\server.js: 336:35)
at Pool.emit (events.js:315 :20) at Connection.
(C:\Users\Umer\Desktop\node\Alhamdulillah, complet e webapp\A11
Validations Completed\node_modules\mongodb-core\lib\connection\poo1
Lis:280:12)
screenshot
According to the problem that you're facing, it is because you're coding the deprecated method of connecting to the MongoDB.
Your database.js file seems okay but I would suggest that you use dotenv and not a separate file for your secrets
But in your app.js file, kindly completely change the way you're connecting to MongoDB.
const config = require('./config/database')
const options = {useUnifiedTopology: true, useNewUrlParser: true}
mongoose.connect(config.mongoURI, options).then(
()=> console.log('connected to mongodb'),
(reason)=> console.error(`Error: ${reason}`)
)
// All of your other code ahead.
Happy Coding!

Getting ECONNREFUSED error in nodejs when logstash server disconnects

I am using node-bunyan and bunyan-logstash-tcp in my nodejs application to send the logs to logstash (1.4.2) and elasticsearch (1.4.2).
Whenever the logstash server disconnects or is not reachable, my nodejs application crashes giving the following error
ERROR
-------
events.js:72
throw er; // Unhandled 'error' event
^
Error: connect ECONNREFUSED
at errnoException (net.js:904:11)
at Object.afterConnect [as oncomplete] (net.js:895:19)
bunyan-logstash-tcp should actually handle this error.
Can anyone please help me to solve this nodejs crashing issue.
I was able to figure out the issue.
Error event needs to be handled while creating tcp bunyan stream
stream: bunyantcp.createStream({
host: '127.0.0.1',
port: 9998
}).on('error', console.log)
This is not mentioned in the bunyan-logstash-tcp documentation, but was there in an example code.
UPDATE: Sample configuration
this.log = bunyan.createLogger({
name: name,
streams: [
{
level: 'error',
type: 'raw',
serializers: bunyan.stdSerializers,
stream: bunyantcp.createStream({
application: process.title,
max_connect_retries: 10, // Don't give up on reconnecting
retry_interval: 1000 * 60 // Wait 1s between reconnect attempts
}).on('error', console.log)
}
],
level: 'debug'
});
For logstash you could probably switch to UDP from TCP.
There are few important advantages:
no connection issues (no reconnect problems, etc)
less performance overhead
less network overhead
Logentries' got a nice blogpost about it: https://blog.logentries.com/2014/07/tcp-or-udp-for-logging/
Take a look at some out-of-the box libs for logstash (elk stack) that support UDP: https://github.com/devmetrics/devmetrics-nodejs-core

Node.js crashing

My node.js app keeps crashing. It's a simple web service that gets data from a mysql database. I get about 20k to 30k queries a day. I'm not sure if it's crashing because I need to give it more resources or if I have a problem with my code. It's hosted in AppFog and here's the crashlog.
I'm not sure what the hashish package is but I tried installing it using 'npm install hashish' and it didn't fix the problem.
Any thoughts?
C:\Users\Tom\\nodejs>af crashlogs TomsApp
====> /logs/staging.log <====
# Logfile created on 2013-02-05 00:53:01 +0000 by logger.rb/25413
Installing dependencies. Node version 0.8.14
Installing mysql#mysql#0.9.6 from local path
Installing hashish#hashish#0.0.4 from registry
Package is not found in npm registry hashish#hashish#0.0.4
Failed getting the requested package: hashish#hashish#0.0.4
Installing require-all#require-all#0.0.5 from local path
====> /logs/stderr.log <====
events.js:71
throw arguments[1]; // Unhandled 'error' event
^
Error: Connection lost: The server closed the connection.
at Protocol.end (/mnt/var/vcap.local/dea/apps/ta-0-365d7313671d4e40105a4d158
f1247d5/app/node_modules/mysql/lib/protocol/Protocol.js:63:13)
at Socket.onend (stream.js:66:10)
at Socket.EventEmitter.emit (events.js:126:20)
at TCP.onread (net.js:417:51)
How are you getting your connection? Do you get it once, or get a new one for each request and then return it to the pool? The pool should be handling stale connections behind the scene.
var mysql = require('mysql');
var pool = mysql.createPool({
host : 'example.org',
user : 'bob',
password : 'secret'
});
// for each request where you need the database connection, wrap it with
pool.getConnection(function(err, connection) {
// do something with connection here
...
connection.end();
});
I would say mysql server's refusing the connection maybe you want to look into that
You probably have to handle the error event on the socket (http://nodejs.org/api/net.html#net_event_error_1) for all your connections.
That will keep it from crashing and you should then see the actual problem.

Resources