NodeJS Express SSL not working with Bitnami EC2 Instance - node.js

I am running my Bitnami Amazon EC2 VPC App via NodeJS using Express (Apache is disabled) and am having trouble getting SSL to work. I have implemented SSL using this implementation: https://www.npmjs.org/package/ssl-root-cas. I purchased the SSL Certificate from Symantec. The app runs HTTP/S servers on ports 8080/8443 and I port forward each to 80/443. The app is run via nohup. I have enabled inbound/outbound https traffic in my security group on EC2.
Here is the sample implementation:
require("ssl-root-cas/latest")
.inject()
.addFile(__dirname + "/ssl/IntermediateCA.cer")
.addFile(__dirname + "/ssl/crossRootCA.cer");
var server = {
http: {
port: process.env.PORT || 8080
},
https: {
port: 8443,
options: {
key: fs.readFileSync("./ssl/server.key"),
cert: fs.readFileSync("./ssl/ssl_certificate.cer")
}
}
};
https.createServer(server.https.options, app).listen(server.https.port);
http.createServer(app).listen(server.http.port);
For whatever reason, SSL is still not working. Suggestions??

Related

Cloudflare 522 error - javascript client connecting to node server

I'm trying to connect to a nodejs https server from a apache web server hosted javascript client and I'm getting an error message : 522 - Failed to load response data: No data found for resource with given indentifier. The apache web server runs on the same domain/server as the node server and the servers are proxied by Cloudflare:
Client app: https://www.example.com (apache web server on port 443)
Node SERVER: https://www.example.com:2053
Both services run in the same server/machine. This is how I start nodejs server:
// Certificates are the same used by apache web server in Virtual Host
// and were got from Cloud Flare Panel > SSL/TLS > Origin Server
var options = {
key: fs.readFileSync('/etc/cloudflare/example.com.key'),
cert: fs.readFileSync('/etc/cloudflare/example.com.pem'),
};
var socket = require('socket.io');
var http = require('https');
// Port 2053 was listed as a https port supported by Cloud Flare in
// https://developers.cloudflare.com/fundamentals/get-started/reference/network-ports/
var argv = require('optimist')
.usage('Usage: --port [num]')
.default({port: 2053})
.argv;
var server = http.createServer(options, function(req, res) {
});
server.listen(argv.port);
var io = socket.listen(server);
This is how I connect to nodejs server from the javascript client:
let socket = io.connect("https://www.example.com:2053", {secure: true});
Any tips?
Edit 1
It works if I create the node server as http (instead of https).
I was able to connect to node server by doing the follow:
Set "key" and "cert" options when instancing https node server: these files can be generated in Cloud Flare Panel > Select your domain > SSL/TLS > Origin Server. There was no need for "ca", "requestCert" or "rejectUnauthorized" parameters.
Use one of the ports listed in https://developers.cloudflare.com/fundamentals/get-started/reference/network-ports/ in the node server. Cloud flare automatically redirect these ports to the same port in your origin server.
Allow inbound connections on the selected port (step 2) in your origin server firewall.
Set SSL setting to FULL in Cloud Flare Panel > Select your domain > SSL/TLS > Overview

How do you write an SSL redirect in NodeJS running on AWS EC2 without using port 80 (http) or port 43 (https)?

I have two node servers on a single host. One HTTP server with the responsibility of redirecting to HTTPS, and one HTTPS server responsible for serving my application:
const express = require('express');
const https = require('https');
const http = require('http')
const fs = require('fs');
const app = express();
const httpsOptions = {
key: fs.readFileSync('./local-ssl/key.pem'),
cert: fs.readFileSync('./local-ssl/cert.pem'),
passphrase: '*****'
}
//other stuff
http.createServer(function (req, res) {
res.writeHead(301, { "Location": "https://" + req.headers['host'] + req.url });
res.end();
}).listen(80);
https.createServer(httpsOptions, app).listen(443)
This works great locally.
The issue is, when I deploy this code and run these servers on AWS EC2 you cannot start servers on ports 80 and 443. I am trying to figure out how I can get around this issue. If I run them on different ports, the servers will not respond, and worse, redirect incorrectly.
Example:
If I serve HTTP on 8081 and HTTPS on 8443, when a redirect occurs, the code redirects to
https://my-fun-url.com:8081
which of course does not work because I am not responding to HTTPS on port 8081.
Now, I've explored the option of port forwarding, but how would this work? If I forward ports 80 and 443 to internal ports (let's say) 3000 and 4000 the same redirection problem will occur.
I have scoured the internet for so long and to me this is a simple requirement for any web-app. I would very much appreciate some detailed guidance on my strategy.
If you want to keep ports 8081 and 8443, then you simply replace 8081 with 8443 in the host header:
httpsHost = req.headers.host.replace('8081', '8443');
res.writeHead(301, {
"Location": "https://" + httpsHost + req.url
});
Now, I've explored the option of port forwarding, but how would this work? If I forward ports 80 and 443 to internal ports (let's say) 3000 and 4000 the same redirection problem will occur.
Not exactly. When someone navigates to http://my-fun-url.com (80) the request is forwarded to 3000. Your http server will respond with a redirect to https://my-fun-url.com (443) which will be forwarded to 4000, and the https server will take it from there.
The difference between the two methods is that with ports 80 and 443 being the default, they are implied and therefore can be left out from the host part of the URL. Which makes the redirect easier as there's no port in the host to replace in the first place, just the protocol part (HTTP/HTTPS).

node http proxy port number to accept SSL traffic

Following example is taken from the github page of node-http-proxy
HTTPS -> HTTP
//
// Create the HTTPS proxy server in front of a HTTP server
//
httpProxy.createServer({
target: {
host: 'localhost',
port: 9009
},
ssl: {
key: fs.readFileSync('valid-ssl-key.pem', 'utf8'),
cert: fs.readFileSync('valid-ssl-cert.pem', 'utf8')
}
}).listen(8009);
Question: Why is httpProxy not listening to port 443 for secure SSL traffic?
SSL has default port which is 443 but like with regular HTTP protocol which also has default 80 port it could be bind to a custom port and accessed by specifying a port in the url(https://localhost:8009). Based on this answer.

HTTPS on Nodejitsu using Express

Okay. I have an app on express which also uses Socket.io and works fine via HTTP. However, now I have to move to HTTPS. Nodejitsu provide a lot of documentation on this. They suggest to use node-http-proxy (https://github.com/nodejitsu/node-http-proxy). Fine!
From the code for HTTP:
var server = http.createServer(app) // app is an Express instance
server.listen(config.port,config.hostip) // config.port is 80 for localhost and 3000 for Nodejitsu, config.hostip is 127.0.0.1 for localhost and 0.0.0.0 for Nodejitsu
I got this:
var server = http.createServer(app)
var options = {
https: {
key: fs.readFileSync(__dirname+"/ssl/privatekey.pem", 'utf8'),
cert: fs.readFileSync(__dirname+"/ssl/certificate.pem", 'utf8')
}
}
httpProxy.createServer(config.port, config.hostip, options).listen(3001,config.hostip)
var proxy = new httpProxy.HttpProxy({
target: {
host: config.hostip,
port: config.port
}
})
https.createServer(options.https, function (req, res) {
proxy.proxyRequest(req, res)
}).listen(3002,config.hostip)
server.listen(config.port,config.hostip)
When I finally deploy (no errors during deployment), I visit the page and see 502 error Socket hang up. OK, I might doing something wrong, so I just copy and paste the example from https://github.com/nodejitsu/node-http-proxy "Proxying to HTTP from HTTPS" to check if it works. But it doesn't - 502 error.
It works fine on my localhost though. I have also tried to launch standalone HTTPS server without node-https-proxy, but no luck. Please help, I cannot solve this for weeks.
Found by myself. Nodejitsu offers SSL by default, just visit your site via HTTPS://. For custom domains to apply SSL certs you need to subscribe for Business Plan.

Installing SSL Certificate On Node Server

I created a self-signed certificate and installed it on apache as well as on node.js(port 3000). On localhost both https://localhost and https://localhost:3000 works well.
So, I bought GoDaddy Standard SSL certificate and installed it on the server(http://gatherify.com). Now https://gatherify.com works well, but ssl on node isn't working.
When I access https://gatherify.com:3000 i get "The connection was interrupted".
I executed curl:
root#host [~]# curl -v -s -k https://gatherify.com:3000
* About to connect() to gatherify.com port 3000 (#0)
* Trying 108.160.156.123... connected
* Connected to gatherify.com (108.160.156.123) port 3000 (#0)
* Initializing NSS with certpath: sql:/etc/pki/nssdb
* warning: ignoring value of ssl.verifyhost
* NSS error -5938
* Closing connection #0
* SSL connect error
Any suggestions to fix this?
UPDATE
*SERVER SIDE :*
var io = require('socket.io'),
connect = require('connect'),
fs = require('fs'),
var privateKey = fs.readFileSync('cert/server.key').toString();
var certificate = fs.readFileSync('cert/server.crt').toString();
var options = {
key: privateKey,
cert: certificate
};
var app = connect(options).use(connect.static('../htdocs/node/'));
app.listen(3000);
var server = io.listen(app);
server.sockets.on('connection', function(socket) {
console.log("Connected");
});
CLIENT SIDE:
<html> <head>
<script type = "text/javascript" src = "https://gatherify.com:3000/socket.io/socket.io.js"></script>
<script type = "text/javascript">
var socket = io.connect('https://gatherify.com:3000', {secure:true});
</script>
</head><body></body></html>
If you want to run a node.js app on port 3000 with (behind) HTTPS, then you need to set up a proxy service on port 443 to proxy HTTPS requests to port 3000.
You didn't mention what server you have running on port 443 right now (is it Apache?) but you might want to
move that service to a new port (e.g. 4000), then run a node http proxy on port 443 that handles HTTPS.
Then set up a subdomain for the node.js app that you have running on port 3000 (e.g. blah.gatherify.com).
Then, using node http proxy, you will proxy all requests that are made to "gatherify.com" to port 4000, and all requests that are made to "blah.gatherify.com" to port 3000.
When all is set up properly, users can visit "https://gatherify.com" or "https://blah.gatherify.com" (without using :port numbers) and it'll all be secured with SSL. ;)
Install certificates Client Side (in Node.js)
If you need a node.js client to be able to recognize your self-assigned or cheaply-bought SSL certificates you can use ssl-root-cas, which is available on npm.
'use strict';
var https = require('https')
, cas
;
// This will add the well-known CAs
// to `https.globalAgent.options.ca`
require('ssl-root-cas').inject();
cas = https.globalAgent.options.ca;
cas.push(fs.readFileSync(path.join(__dirname, 'ssl', '01-cheap-ssl-intermediary-a.pem')));
cas.push(fs.readFileSync(path.join(__dirname, 'ssl', '02-cheap-ssl-intermediary-b.pem')));
cas.push(fs.readFileSync(path.join(__dirname, 'ssl', '03-cheap-ssl-site.pem')));
This will make your certs available to the core https module as well as modules that depend on it such as request and socket.io-client without deleting the normal ssl certs (which is the default behavior for some odd reason).

Resources