How to log SSL handshake in node's https module - node.js

I have a server written in node which implements a secure two-way SSL web-server:
var https = require('https');
var express = require('express');
var app = express();
var options {
key: ...,
cert: ...,
ca: ...,
requestCert: true,
rejectUnauthorized: true
};
https.createServer(options, app).listen(port, host);
But for some unknown reason, the client fails to connect. So it would be great if I could get any logs on why the connection has failed.
So far, all the logs I can get come from app which is an express object. But the problem is that when a connection is rejected due to a certificate issues, it does not reach express so I get no error logs. How can I get logs from https server?

I've run into this problem as well and while I couldn't come up with a solution that logs all the errors within the https module, I was able to get it to log debug information by using:
NODE_DEBUG='tls,https' node server.js
This isn't ideal as it doesn't give you the exact error (eg: Bad SSL Handshake) and the related traceback, it does give you information like TLS: onhandshakestart which lets you figure out if there was an error if you can't find a corresponding TLS: onhandshakeend in the logs.

There is an option in the config for createServer called enableTrace that causes Node to print a ton of details about the handshake:
var options {
key: ...,
cert: ...,
ca: ...,
requestCert: true,
rejectUnauthorized: true,
enableTrace: true // Set this :)
};
See the docs for more info.

Related

NodeJS http-proxy module returning SSL error when connecting to certain doamins

I am using the node js HTTP-Proxy module to proxy requests to domain. When I use the module to proxy requests as below I get an SSL error
httpProxy = require('http-proxy'),
fs = require('fs');
process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = 0;
httpProxy.createServer({
ssl: {
key: fs.readFileSync('key.pem', 'utf8'),
cert: fs.readFileSync('certificate.pem', 'utf8')
},
target: 'https://company.com',
secure:false
// Depends on your needs, could be false.
}).listen(443);
This error out
Error: write EPROTO 4548494784:error:14094438:SSL routines:ssl3_read_bytes:tlsv1 alert internal error:../deps/openssl/openssl/ssl/record/rec_layer_s3.c:1544:SSL alert number 80
However when I try to get to the same domain using Request module I get a proper response
app.get('/', (req, res) => request({
uri: 'https://company.com'
}).pipe(res))
This works fine.
This error is only noticed in SSL connection to some domains. I suspect its something in the domain. However I cant understand how Request module could work and the http-proxy module will fail for the same url considering they would be probably be using the same underlying foundation code. If someone can help throw light on where I am going wrong, it would be much appreciated.

SocketIO throws net::ERR_CERT_AUTHORITY_INVALID on self signed certificate

I am using socket io on client:
const socket = require('socket.io-client')('https://localhost:4200', {secure: true, rejectUnauthorized: false})
And on server:
let https = require('https')
let fs = require('fs')
let options = {
key: fs.readFileSync('cert/my.net.key'),
cert: fs.readFileSync('cert/my.net.cert'),
requestCert: false,
rejectUnauthorized: false,
};
const server = https.createServer(options, require('express')())
const io = require('socket.io')(server)
All services are started normally, but on client I am getting polling-xhr.js:263 GET https://localhost:4200/socket.io/?EIO=3&transport=polling&t=MPa6ZuL net::ERR_CERT_AUTHORITY_INVALID
Why? Whats is wrong?
Browsers don't like self-signed certificates for security reasons.
To get around this in your development environment, I see three options:
Use a certificate issued by a certification unit.
It could be something free, like https://letsencrypt.org/.
Create your server dynamically, based on the development environment, not to include certificates and work directly with HTTP and WS (and not HTTPS and WSS).
Change the configuration of your browser used in development so that it accepts self-signed certificates.
For Chrome, for example, just enable the Allow invalid certificates for resources loaded from localhost. (chrome://flags/#allow-insecure-localhost) setting.
But remember that you will not be able to use self-signed certificates in production environments.

How to establish a secure websocket connection to a node server using self-signed certs?

I've got an HTTPS webpage that won't let me connect to an insecure websocket, which I was using to communicate with a node sever, so I'm trying to migrate my node server to https. The client-side https page was given to me to integrate my previously insecure page with, so I don't know anything about the certs it's using, if that matters.
To connect to it, on the client side I was using connection = new Websocket('ws://node_server_address') which worked fine for insecure connections. Now that the page uses https, I'm just using connection = new Websocket('wss://node_server_address'), which I hope is all I need to change on the client side. However, with self signed certs on the node server, I get this error when I try to connect with Chrome:
Websocket connection to 'wss://address' failed: Error in connection establishment: net::ERR_CERT_AUTHORITY_INVALID
On Firefox all it tells me is that it can't connect to the server. I've seen plenty of examples where they don't specify a CA at all and they supposedly work fine, so I'm hoping I don't have to mess with that. However, assuming the only solution is to specify some valid CA, how would I generate that for a self-signed certificate?
Node server:
const https = require('https');
const WebSocketServer = require('websocket').server;
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
let serverOptions = {
key: fs.readFileSync("./self-signed.key"),
cert: fs.readFileSync("./self-signed.cert"),
requestCert: false,
rejectUnauthorized: false
}
const server = https.createServer(serverOptions, (req, res) => { res.end('') });
server.listen(port, function() { /* Logging */ }
const wsServer = new WebSocketServer({
httpServer:server
});

Self signed cert NodeJS rejectUnauthorized

I created a structure certificates to authenticate client-> server, allowing only certificates recognized by the CA using this step by step: https://jamielinux.com/docs/openssl-certificate-authority/create-the-root-pair.html
I checked the authority with openssl, and it returns to me OK the certificate server and client, with the same CA. But by setting the parameter rejectUnauthorized to true on the server, the client can not connect.
Is there any extra parameter should I set up to allow authentication by a certificate that I generated?
---- Edit
On the client side I get the following error: ""ECONNRESET" socket hang up"
I spent a long time digging into a similar issue, and I wrote up this to talk about how to dig into various OpenSSL issues with node.js: http://www.thedreaming.org/2016/09/27/nodejs-ssl/
The short answer, though, is if you need to pass the ca parameter when creating you client connection. If you have the self-signed certificate stored in cert.pem, then the client code looks something like:
var https = require('https');
var fs = require('fs');
var certificate = fs.readFileSync('cert.pem');
var options = {
host: serverHost,
port: 443,
path: '/',
ca: [certificate]
};
https.request(options, function(res) {
res.pipe(process.stdout);
}).end();

how to configure https in sails.js

I am trying to setup a local HTTPS server for testing in Sails.js? I am not able to find any pointer how to do that in sails.js? For express,
var express = require('express');
var https = require('https');
var http = require('http');
var fs = require('fs');
// This line is from the Node.js HTTPS documentation.
var options = {
key: fs.readFileSync('test/fixtures/keys/agent2-key.pem'),
cert: fs.readFileSync('test/fixtures/keys/agent2-cert.pem')
};
// Create a service (the app object is just a callback).
var app = express();
// Create an HTTP service.
http.createServer(app).listen(80);
// Create an HTTPS service identical to the HTTP service.
https.createServer(options, app).listen(443);
Any idea about sails.js?
For sails.js version 0.10, include this in config/locals.js (if locals.js does not exist, create it):
var fs = require('fs');
module.exports = {
ssl : {
key: fs.readFileSync('path-to-key.key'),
cert: fs.readFileSync('path-to-crt.crt')
}
};
Source: https://stackoverflow.com/a/28565113/2459071
If you're using the latest v0.9 (and maybe some versions of v0.8) take look inside of config/bootstrap.js. You should be able to access your express app via the sails.express context. From there I think you should be able to do with it what you want to...
Also someone in the #sailsjs irc channel said this worked for them
module.exports.bootstrap = function (cb) {
var fs = require('fs');
sails.config.express.serverOptions = {
key: fs.readFileSync('ssl/key.pem'),
cert: fs.readFileSync('ssl/cert.pem')
};
cb();
};
Maybe it's just me but I could get either of the above working for sails v0.9.7, but I did get it working by editing the config/local.js file like so;
var fs = require('fs');
module.exports = {
port: process.env.PORT || 1337,
environment: process.env.NODE_ENV || 'development',
express: { serverOptions : {
key: fs.readFileSync('ssl/key.pem'),
cert: fs.readFileSync('ssl/cert.pem')
}
}
};
Now I'm not saying this is the 'correct' way to do this, however it works for me!
Shameless self promotion
More about this on my blog!
End shameless self promotion :D
This contribution enhances the solution for to support native mobile applications and old browsers.
This solution worked really well for me when when just using a modern web browser to access my SSL site. However when I attempted to make requests using the AFNetworking library it did not recognise the SSL certificate. This was due to the iPhone application requiring the intermediate SSL certificates (sometimes called the ca bundle).
You can add the intermediate certificate in using the following code.
express: {
serverOptions : {
key: fs.readFileSync('ssl/key.pem'),
cert: fs.readFileSync('ssl/cert.pem'),
ca: fs.readFileSync('ssl/intermediate.pem')
}
}
When creating you intermediate certificate (which can normally be downloaded from your SSL certificate provider) it is important to get the order of certificates right.
This linux command really helped with debugging.
openssl s_client -connect yoursite.com:443 -showcerts
The above does not work for sails v0.9.3. I ended up with the following workaround. (require fs first of course)
express : {serverOptions : {
key: fs.readFileSync('ssl/server-key.pem'),
cert: fs.readFileSync('ssl/server-cert.pem'),
}}
I have also faced this kind of issues in my production sails app (v0.11.x and v0.12.x). Android release version apk is not able to connect to sails app and some old version browsers do not accept SSL certificate with web app.
I got some intermediate certificate error like below
The certificate is not trusted in all web browsers. You may need to install an Intermediate/chain certificate to link it to a trusted root certificate.
Finally, I found a solution
ssl: {
ca: require('fs').readFileSync('ssl/intermediate.crt', 'utf8').toString(),
key: require('fs').readFileSync('ssl/example_com.key', 'utf8').toString(),
cert: require('fs').readFileSync('ssl/main.crt', 'utf8').toString()
}
I hope this will help someone.

Resources