Config Self-Signed Cert to work with node,js - node.js

So I am currently working on a project that is looking to add SSL security to an web-app running on node.js that is being contained inside a Ubuntu core snap.
I am not a programmer so I am struggling to find a way to have the the app use the cert, let alone find a way to include it in the snap so that it will be part of the snap install process.
I understand that this might have to be separate processes but not having adequate footing to start from is making that hard to see.
Any direction in this would be helpful.

I think, Snaps are containerised software packages that are simple to create and install, which are similar to apt, yum, dnf, pkg. I'm not sure how it is linked to running Node.js server on SSL. TO run node server on SSL, you need to create or have SSL certificates. You can do something like this:
var https = require('https');
var fs = require('fs');
var https_options = {
key: fs.readFileSync("/path/to/private.key"),
cert: fs.readFileSync("/path/to/your_domain_name.crt"),
ca: [
fs.readFileSync('path/to/CA_root.crt'),
fs.readFileSync('path/to/ca_bundle_certificate.crt')
]
};
https.createServer(https_options, function (req, res) {
res.writeHead(200);
res.end("Welcome to Node.js HTTPS Servern");
}).listen(80)

Related

How to automatically reload updated SSL certificates in Node.js Application

I created nodejs application and I'm using Lets Encrypt SSL certificates. Following is my Code
var express = require(‘express’);
var https = require(‘https’);
var fs = require(‘fs’);
var option = {
key: fs.readFileSync(‘/etc/letsencrypt/live/$DOMAIN/privkey.pem’),
cert: fs.readFileSync(‘/etc/letsencrypt/live/$DOMAIN/fullchain.pem’)
};
const app = express();
app.use((req, res) =>
{
res.end(‘Hello World’);
});
https.createServer(option, app).listen(8000);
I have used pm2 to start this application using following command
sudo pm2 start app.js --watch
I am updating SSL certificates by using following cronjob
0 8 * * * sudo certbot renew
I want to reload SSL certificates automatically whenever certbot renews SSL certificates. How can I achieve this?
You can use the flag --post-hook to restart your application after every renewal.
certbot renew --post-hook "pm2 restart app_name"
Update #1
Please note that the command we are running is in crontab and any global program has to be referenced with the full path. You can use the which command to find the executable file path for the command.
For those of us who can't afford to or would rather not restart our servers to reload certs and that aren't comfortable with Dylan Landry's SNI-based approach, there has been a purpose-built way of doing this built into node for a while now, via server.setSecureContext (where server is a standard node https server instance). See snippet below:
const app = express();
function readCertsSync() {
return {
key: fs.readFileSync(sslKeyPath),
cert: fs.readFileSync(sslCertPath) + fs.readFileSync(sslFullChainPath)
}
}
let httpd = https.createServer(readCertsSync(), app).listen(port, onReady);
// Refresh httpd's certs when certs change on disk. The timeout stuff
// "ensures" that all 3 relevant files are updated, and accounts for
// sometimes trigger-happy fs.watch.
let waitForCertAndFullChainToGetUpdatedTooTimeout;
fs.watch(sslKeyPath, () => {
clearTimeout(waitForCertAndFullChainToGetUpdatedTooTimeout);
waitForCertAndFullChainToGetUpdatedTooTimeout = setTimeout(() => {
httpd.setSecureContext(readCertsSync());
}, 1000);
});
The fs.watch and timeout code is admittedly a bit clunky and can be improved using something like chokidar and some more expansive logic to monitor the state of all 3 relevant files. I chose to keep things simple to focus on the interesting bit: setSecureContext.
For reference, see https://nodejs.org/api/tls.html#tls_server_setsecurecontext_options
Also, credit goes to nolimitdev who came up with most of this before setSecureContext was even a thing.
You can reload the new certs without restarting your server.
According to the issue Reload certificate files of https.createServer() without restarting node server #15115 , specifically this comment from mscdex:
FWIW you can already do this with SNICallback():
const https = require('https');
const tls = require('tls');
const fs = require('fs');
var ctx = tls.createSecureContext({
key: fs.readFileSync(config.sslKeyPath),
cert: fs.readFileSync(config.sslCrtPath)
});
https.createServer({
SNICallback: (servername, cb) => {
// here you can even change up the `SecureContext`
// based on `servername` if you want
cb(null, ctx);
}
});
With that, all you have to do is re-assign ctx and then it will get used for any future requests.
Using the example above, you just need to do fs.readFileSync again on the cert path from within the SNICallback and attach them to the ctx object. But, you only want to do this when you know they've just changed. You can watch the files from javascript for changes. You can use fs.watch() for that or something from npm.

Socket.IO with Express will not connect using https (Apache2 Ubuntu16.04)

I am not very experienced with SSL certs and Im hopping someone can help me find out what I am doing wrong.
I am trying to host a NodeJS application with it's own port (*:1729) with Apache2 which has SSL enabled on port 443 (from which it servers a client application and not the NodeJS/Express/Socket.io application in question). When I set up Express with http it works fine, my client application communicates without error to the NodeJS application, however when I use https with express such as this:
this.express = require('express');
this.app = this.express();
var https = require('https');
var fs = require('fs');
var sslPath = '/etc/letsencrypt/live/yourdomain.example.com/';
var options = {
key: fs.readFileSync(sslPath + 'privkey.pem'),
cert: fs.readFileSync(sslPath + 'fullchain.pem')
};
this.server = https.createServer(options, this.app);
this.io = require('socket.io').listen(this.server);
this.server.listen(1729);
When ever my client app tries to connect I get this error:
node: ../src/util-inl.h:196: TypeName* node::Unwrap(v8::Local<v8::Object>) [with TypeName = node::TLSWrap]: Assertion ``(object->InternalFieldCount()) > (0)' failed.
Aborted (core dumped)
Any advice or corrections that could point me in the right direction would be much appreciated, thanks!
Thank you of the help! :) Updating my NodeJS to the latest version made the error disappear and the application is communicating with the client with no error's as well.
node: Github issue # 3682
I just followed these steps.
AskUbuntu: How can I update my nodeJS to the latest version?

How to enable SSL connection a NodeJS (Express) based Server

Following is the script I found on NodeJS Official Website:
// curl -k https://localhost:8000/
const https = require('https');
const fs = require('fs');
const options = {
key: fs.readFileSync('test/fixtures/keys/agent2-key.pem'),
cert: fs.readFileSync('test/fixtures/keys/agent2-cert.pem')
};
https.createServer(options, (req, res) => {
res.writeHead(200);
res.end('hello world\n');
}).listen(8000);
I'm completely new to SSL thing. I read some tutorials on how to enable SSL on Nodejs but still quite not confident with the process.
I have a domain name (Godaddy), SSL (Namecheap) and Cloud Server (Digital Ocean with an application deployed on HTTP prefix).
Whenever I open my Login page of my website, Google Chrome mark it as "Not secure" so I wanted to add SSL to the website.
What things I need to do on the NodeJS server (Express) and what things I need to do on Namecheap? What will be the sequence of doing that? What are cmd lines to generate .pem .csr files?
I'm didn't found and comprehensive guide laying down all the steps in a concise manner so just laid down the steps concisely (if possible) with the links to the resources for further digging.
And also, how can I use express framework to create https server in liue of above script?
That script is correct for setting the certs for your https. If your site is public, as it seems, then you'll want to buy certs from your ssl service, Namecheap in your example. Then you would move them to your host and reference them in the options object in your example. However, you can generate your own ssl certs and that will work as well. Though, any users will be warned that they're not trusted since you self signed/created them. I suggest going with the Namecheap option.
Note: You only have an https server in your example and anyone attempting to access your site via http will receive a blank page. You'll need to also create an http server, via the following:
var http = require('http');
http.createServer(...);
I would suggest having the http server simply redirect to the https url.
My code and error is here:
no such directory found error:
key: fs.readFileSync('../private.key'),
cert: fs.readFileSync('../public.cert')
error, no such directory found
key: fs.readFileSync('./private.key'),
cert: fs.readFileSync('./public.cert')
Working code should be
key: fs.readFileSync(__dirname + '/private.key', 'utf8'),
cert: fs.readFileSync(__dirname + '/public.cert', 'utf8')
Complete https code is:
const https = require('https');
const fs = require('fs');
// readFileSync function must use __dirname get current directory
// require use ./ refer to current directory.
const options = {
key: fs.readFileSync(__dirname + '/private.key', 'utf8'),
cert: fs.readFileSync(__dirname + '/public.cert', 'utf8')
};
// Create HTTPs server.
var server = https.createServer(options, app);
This is my working code for express 4.0.
express 4.0 is very different from 3.0 and others.
4.0 you have /bin/www file, which you are going to add https here.
"npm start" is standard way you start express 4.0 server.
readFileSync() function should use __dirname get current directory
while require() use ./ refer to current directory.
First you put private.key and public.cert file under /bin folder,
It is same folder as WWW file.

Install root certificate to Node.js (HTTPS Client)

I would like to connect to an internal corporate server with Node.js as a client via HTTPS.
How do I install my internal root certificate to Node.js, so the certificate validation is successful?
I don't want to disable certificate validation.
I solved the problem.
Use this, it works as charm: https://github.com/coolaj86/node-ssl-root-cas
Create a certificates folder
Place your root certificate there
Load the certificate with the module metnioned above
If you use Sails.js, put the code to config/bootstrap.js
Add an environment variable:
NODE_EXTRA_CA_CERTS=file
When set, the well known "root" CAs (like VeriSign) will be extended with the extra certificates in file. The file should consist of one or more trusted certificates in PEM format. A message will be emitted (once) with process.emitWarning() if the file is missing or malformed, but any errors are otherwise ignored.
Note that neither the well known nor extra certificates are used when the ca options property is explicitly specified for a TLS or HTTPS client or server.
This environment variable is ignored when node runs as setuid root or has Linux file capabilities set.
Hope this help someone:
var https = require('https');
var fs = require('fs');
var https_options = {
key: fs.readFileSync("/path/to/private.key"),
cert: fs.readFileSync("/path/to/your_domain_name.crt"),
ca: [
fs.readFileSync('path/to/CA_root.crt'),
fs.readFileSync('path/to/ca_bundle_certificate.crt')
]
};
Ref: https://cheapsslsecurity.com/blog/how-to-install-ssl-certificate-on-node-js/
And you can use this link to check your cert:
https://www.digicert.com/help/

basic node website returns "Cannot GET /" when hosted with forever.js

I'm trying to get my first production node website up (just a basic hello world on my production web server).
The below is what I'm using (basic http proxy to pass off apache websites to port :9000 and node websites to port :8000). I know this part works because apache vhosts are forwarded as I expect. What does not work however is the node part -instead I get the error below
"Cannot GET /"
This is running node 0.8.1 on Ubuntu 12.04
I'm hosting this with forever.js (forever start foo.js). when I echo the NODE_ENV -it shows "production"
It might also be noted that I don't have node_modules on the path (as you will see in my require statements) **not sure if this has anything to do with my issue
var httpProxy = require('/usr/local/lib/node_modules/http-proxy/lib/node-http-proxy');
var express = require('/usr/local/lib/node_modules/express/lib/express');
httpProxy.createServer(function (req, res, proxy) {
var nodeVhosts = ['www.mysite.com'];
var host = req.headers['host'];
var port = nodeVhosts.indexOf(host) > -1
? 8000
: 9000;
proxy.proxyRequest(req, res, {host: 'localhost', port: port});
}).listen(80);
var one = express.createServer();
one.get('/', function(req, res){
res.send('Hello from app one!')
});
var app = express.createServer();
app.use(express.vhost('localhost', one));
app.listen(8000);
Since you are running Ubuntu, you might take a look at upstart. In case you don't know, upstart replaces the old-school-unix init-scripts approach to starting and stopping services. (Those were dark, scary days!) If you want your app to start automatically when your box boots/reboots and restart automatically after it (your app) crashes, then you want upstart. Learning the basics of upstart is easy, and then you have a tool that you can use over and over again, whether it's node, apache, nginx, postfix, mongodb, mysql, whatever!
I mean no disrespect to the good folks who work on the forever module. Arguably, it does have a solid use case, but too often it is used to imperfectly duplicate the bedrock which is already on your system -- upstart. Also, you might Google for some of the comments made by experienced users and some node.js committers about the forkability of node.js and the pitfalls, which are very relevant to forever.
I'd like to post links, but I don't have enough rep yet. Hopefully what I wrote is enough to google on.
Good luck!
http-proxy module doesn't change the host header of the request, and that's what connect/express vhost uses to distinguish virtualhosts.
In this line:
proxy.proxyRequest(req, res, {host: 'localhost', port: port});
you tell the proxy server to proxy the unchanged request to localhost:port.
So what you need to do is to change:
var app = express.createServer();
app.use(express.vhost('localhost', one));
app.listen(8000);
to:
var app = express.createServer();
app.use(express.vhost('www.mysite.com', one));
app.listen(8000);
and it should work.
Alternatively you can simply set the req.headers.host to localhost before proxying.

Resources