ERR_SSL_UNEXPECTED_MESSAGE error in Node.js - node.js

I`m experiencing a challenge coding a TLS function in node.js and can`t find a root cause.
This is the scenario:
Client sends a https CONNECT request to the app
The APP is working as a http/https proxy
The APP replies the client back with a 200OK
Next request from client is TLS client hello sent through the tunnel
established by the CONNECT request
The APP crashes instead of replying TLS handshake with a server
hello
Here is the error I`m getting:
Emitted 'error' event on TLSSocket instance at:
at emitErrorNT (node:internal/streams/destroy:188:8)
at emitErrorCloseNT (node:internal/streams/destroy:153:3)
at processTicksAndRejections (node:internal/process/task_queues:80:21) {
library: 'SSL routines',
function: 'ossl_statem_client_read_transition',
reason: 'unexpected message',
code: 'ERR_SSL_UNEXPECTED_MESSAGE'
}
And here is the code:
proxyServerHttp.on('connect', (request, socket, head) => {
handleHttpConnectMethod(request, socket, head);
})
function handleHttpConnectMethod(request, socket, head) {
socket.write('HTTP/' + request.httpVersion + ' 200 OK\r\n\r\n', 'UTF-8');
connectTlsSocket(socket);
}
function connectTlsSocket(socket) {
let options = {
key: fs.readFileSync('key.pem'),
cert: fs.readFileSync('cert.pem'),
ca: [fs.readFileSync('cert.pem')],
socket: socket
}
tls.connect(options) //error is thrown when this line is executed
}
Any help is super welcome

after debugging and looking at node src code, I found the issue.
function connectTlsSocket(socket)
return a tls.socket object in client mode (default), so the socket was working in client mode and receiving 'Hello Client' from the source.
I had to create a socket obj in server mode in order to fix the issue.
function connectTlsSocket(socket) {
//let tlsSocketParameters = getCertAndKey();
//tlsSocketParameters.socket = socket;
let options = {
key: fs.readFileSync('key.pem'),
cert: fs.readFileSync('cert.pem'),
ca: [fs.readFileSync('cert.pem')],
isServer: true,
requestCert: false
}
let tlsSocket = new tls.TLSSocket(socket, options);
tlsSocket.on('error', (error) => {
console.log(error);
});
}

Related

Unable to verify the first certificate - Nodejs TLS

I'm using the node-module basic-ftp to try to establish a secure connection via TLS/ SSL.
The server uses a wildcard CA-signed certificate as it's hostname. I can't seem to find an answer for the followig error code.
Connected to 1.1.1.1:21
< 220 Welcome to ftp.foo.com
> AUTH TLS
< 234 Using authentication type TLS
{ Error: unable to verify the first certificate
at TLSSocket.onConnectSecure (_tls_wrap.js:1051:34)
at TLSSocket.emit (events.js:189:13)
at TLSSocket._finishInit (_tls_wrap.js:633:8) code: 'UNABLE_TO_VERIFY_LEAF_SIGNATURE'
}
Below you find the sample code:
const ftp = require("basic-ftp");
async establishFtpsConnection() {
const client = new ftp.Client();
client.ftp.verbose = true;
try {
await client.access({
host: "ftp.foo.com",
user: "bar",
password: "1234",
port: 21,
secure: true
});
const list = await client.list();
console.log(list);
} catch (err) {
console.log(err);
}
client.close();
}
NOTE: I'm trying to get it to work for my production environment. So disabling or rejecting unauthorization is NO option for me.
process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0';
OR
rejectUnauthorized: false
Try this :
const ftp = require("basic-ftp");
async establishFtpsConnection() {
const client = new ftp.Client();
client.ftp.verbose = true;
const tlsOptions = {
cert: fs.readFileSync('fullchain.pem', 'ascii')
// a PEM containing the SERVER and ALL INTERMEDIATES
}
try {
await client.access({
host: "ftp.foo.com",
user: "bar",
password: "1234",
port: 21,
secure: true,
secureOptions:tlsOptions
});
const list = await client.list();
console.log(list);
} catch (err) {
console.log(err);
}
client.close();
}
If you are still getting an error then try to inject root SSL certificates
var sslRootCAs = require('ssl-root-cas/latest')
sslRootCAs.inject()

Different behaviour between new tls.TLSSocket() followed by connect(..) versus tls.connect(..)

I'm attempting to use the following code to create a TLS Socket in nodejs
const socket = new net.Socket();
socket.setEncoding('utf8');
let tlsSocket = new tls.TLSSocket()
socket.on('connect', (connectionData) => {
// Do extra work with socket
tlsSocket.connect({
host: targetHost,
port: targetPort,
socket: socket,
})
}
and this is causing an ECONNREFUSED error. The events I see in this flow on tlsSocket are
lookup
error ECONNREFUSED
_tlsError
close
But if I change the code to
const socket = new net.Socket();
socket.setEncoding('utf8');
socket.on('connect', (connectionData) => {
// Do extra work with socket
let tlsSocket = tls.connect({
host: targetHost,
port: targetPort,
socket: socket,
})
}
It works with events on the tlsSocket
resume
data
readable
end
prefinish
finish
close
Why does this fail if I create the constructor version of the TLSSocket and connect it later. Rather than when I use tls.connect(..) which passes.
I need a reference to the tls socket to be returned before the socket connect event. Hence this is blocking me.

Errors with publishing messages to Amazon Queue using available node js libraries

I am trying to publish messages to Amazon MQ from Node JS and none of the libraries I have tried so far seem to be working.
Library 1:
stomp-client
Code:
var Stomp = require('stomp-client');
var destination = '/topic/{new_topic}';
var client = new Stomp('{prefix}.amazonaws.com',
61614,
'{user}',
'{password}');
client.connect(function(sessionId) {
client.publish(destination, 'Oh herrow');
});
Error with first library:
Emitted 'error' event at:
at StompFrameEmitter.<anonymous> (project_path\node_modules\stomp-client\lib\client.js:236:10)
at StompFrameEmitter.emit (events.js:182:13)
[... lines matching original stack trace ...]
at Socket.Readable.push (_stream_readable.js:219:10)
Library 2:
stompit
Code:
const stompit = require('stompit');
var connectOptions = {
'host': '{prefix}.amazonaws.com',
'port': 61614,
'connectHeaders':{
'host': '/',
'login': '{user}',
'passcode': '{password}',
'heart-beat': '5000,5000'
}
};
stompit.connect(connectOptions, function(error, client) {
if (error) {
console.log('connect error ' + error.message);
return;
}
var sendHeaders = {
'destination': '/topic/{new_topic}',
'content-type': 'text/plain'
};
var frame = client.send(sendHeaders);
frame.write('hello');
frame.end();
});
Error with second library: connect error unexpected end of stream
I am not sure what else I can try but I seem to be stuck here as the error messages are not even verbose plus there isnt a lot of information on this issue online.
Only relevant article I found has no answer on Amazon forum:
https://forums.aws.amazon.com/thread.jspa?messageID=831730&tstart=0
What worked for me to solve this issue was to set ssl connection to true as follows:
const server_options = {
host,
port,
ssl: true,
connectHeaders: {
host: '/',
'accept-version': '1.1',
'heart-beat': '0,0', // no heart beat
login: user,
passcode: pass,
},
};
Port must be set to 61614 for stomp+ssl connection.

Getting "bad username or password" error while attempting to connect to broker

I have followed the tutorial presented here, but I couldn't connect a client to the server. I always get the following error message (full stacktrace):
Error: Connection refused: Bad username or password
at MqttClient._handleConnack (${project_dir}/node_modules/mqtt/lib/client.js:760:24)
at MqttClient._handlePacket (${project_dir}/node_modules/mqtt/lib/client.js:300:12)
at process (${project_dir}/node_modules/mqtt/lib/client.js:242:12)
at Writable.writable._write (${project_dir}/node_modules/mqtt/lib/client.js:252:5)
at doWrite (${project_dir}/node_modules/mqtt/node_modules/readable-stream/lib/_stream_writable.js:345:64)
at writeOrBuffer (${project_dir}/node_modules/mqtt/node_modules/readable-stream/lib/_stream_writable.js:334:5)
at Writable.write (${project_dir}/node_modules/mqtt/node_modules/readable-stream/lib/_stream_writable.js:271:11)
at Socket.ondata (_stream_readable.js:528:20)
at emitOne (events.js:77:13)
at Socket.emit (events.js:169:7)
I have double-checked my environment variables, whose values came from my Auth0 account, (particularly CLIENT_ID, DOMAIN, CLIENT_SECRET and CONNECTION) but they seem fine.
I've changed the client's code a bit to match the current version of MQTT.js. Here is the code:
const mqtt = require('mqtt');
const settings = {
port: 1883,
keepalive: 1000,
protocolId: 'MQIsdp', // already tried 'MQTT' with protocol version 4
protocolVersion: 3,
clientId: 'a random id',
username: 'an account at Auth0',
password: 'the password of the account'
}
var client = mqtt.connect('mqtt://localhost', settings);
client.on("connect", function () {
client.subscribe("topic");
console.log("connected");
});
client.on("message", function (topic, message) {
// message is Buffer
console.log(message.toString());
client.end();
});
The broker code is very similar to the presented in the tutorial. I want to solve this error before changing it.
const mosca = require('mosca')
const Auth0Mosca = require('auth0mosca');
require('dotenv').config();
const settings = {
port: 1883
};
if (!process.env.AUTH0_DOMAIN || !process.env.AUTH0_CLIENT_ID ||
!process.env.AUTH0_CLIENT_SECRET || !process.env.AUTH0_CONNECTION) {
throw 'Make sure you have AUTH0_DOMAIN, AUTH0_CLIENT_ID, AUTH0_CLIENT_SECRET and AUTH0_CONNECTION in your .env file';
}
const auth0 = new Auth0Mosca('https://' + process.env.AUTH0_DOMAIN, process.env.AUTH0_CLIENT_ID, process.env.AUTH0_CLIENT_SECRET, process.env.AUTH0_CONNECTION);
// mosca server
const server = new mosca.Server(settings);
server.authenticate = auth0.authenticateWithCredentials();
server.authorizePublish = auth0.authorizePublish();
server.authorizeSubscribe = auth0.authorizeSubscribe();
server.on('ready', setup);
// MQTT server is ready
function setup() {
console.log('Mosca server is up and running');
}
server.on('clientConnected', function(client) {
console.log('New connection: ', client.id);
});
I know that it is probably a stupid mistake or a library update that is causing this. For the latter case, here are the versions:
"auth0mosca": "^0.1.0",
"mosca": "^2.3.0",
"mqtt": "^2.5.0"
Finally, I have checked that the request reaches the broker.
I have forked the repository with the code of the tutorial and updated its dependencies to the latest versions. Also, added the possibility to verify JWTs signed with, for example, RS256 algorithm.
This solved my problem and I hope it helps everyone facing the same issue.

Node 4.1.0 TLSSocket issues

Strange behavior in Node with TLSSocket and tls.connect.
var port = 7000;
var host = '94.125.182.252'; //freenode
var tls = require('tls');
var net = require('net');
var socket = new net.Socket();
var secure;
secure = new tls.TLSSocket( socket, {
isServer: false,
rejectUnauthorized: false
});
// edit (left out of original post, but present in my test code, whoops)
secure.connect( {
port: port,
host: host
});
secure.setEncoding( 'utf8' );
secure.on( 'connect' , function() {
console.log( 'connected' );
})
.on( 'secureConnect', function() {
console.log( 'secure connect' );
})
.on( 'error', function( e ) {
console.log( 'error', e );
})
.on( 'data', function( data ) {
console.log( data );
});
if ( secure.isPaused() ) {
console.log( 'socket was paused' );
secure.resume();
}
This doesn't even attempt to connect and no error messages are produced. I have wireshark monitoring and there is no activity captured.
A different approach:
secure = tls.connect( {
rejectUnauthorized: false,
host: host,
port: port,
socket: socket
});
Same story, nothing captured, no errors. If I remove the socket: socket aspect above it will connect. This makes some sense as the docs state that if the socket option is specified it will ignore port and host. The above works on my previous Node version( 0.12.7).
If I want to use the existing socket I have to tell it to connect before calling tls.connect.
socket.connect( {
port: port,
host: host
});
secure = tls.connect( {
rejectUnauthorized: false,
socket: socket
});
This doesn't seem proper.
Passing a connecting socket to tls.TLSSocket( socket, ...) seems to have no effect.
The 'connect' event is fired but I imagine that is not related to TLSSocket.
I could not get tls.TLSSocket(...) to work on previous Node iterations.
Stepping through with node debug did not expose any obvious problems.
The options for net.Socket([options]) don't seem to accept a port or host for configuring until you try to connect, and trying to connect before passing to tls.connect seems counter intuitive. It would suggest that is not the intended usage.
So my questions would be:
What am I doing wrong with tls.TLSSocket() or perhaps is it a bug?
Am I correct to assume that passing an existing socket into tls.connect() is for already established connections switching protocol? If not, whats the proper way to assign a port and host?
Edit:
As per suggestion:
secure = tls.connect( {
rejectUnauthorized: false,
socket: socket
});
socket.connect( {
port: port,
host: host
});
This works.
secure = new tls.TLSSocket( socket , {
isServer: false,
rejectUnauthorized: false
});
socket.connect( {
port: port,
host: host
});
Unfortunately this does not work. A 'connect' event is emitted, never a 'secureConnect' and never any other events or data.
In your first two (non-working) examples, you only created a socket and never started actually connected it. Add a socket.connect(); at the end of your original code and it should work fine.
tls.connect() when passed a plain socket, does not actually call socket.connect(); internally, it merely sets up to start listening for data on the socket so it can decrypt incoming data properly.

Resources