Apache, SSL, NodeJS, Express, Socket.io, Digital Ocean set up? - node.js

I tried setting up some mod_proxy methods (link below) but when active, it gives me a Service Unavailable message, (sorry, not a server/sysadmin guy)
We have a development server without any SSL and it works perfectly.
Our code so far (nodejs/server.js):
var app = require("express")();
var https = require("https");
var io = require("socket.io")(https);
var port = 3000;
var privateKey = fs.readFileSync('/etc/apache2/ssl-certificate/site.key', 'utf8'); // change with your ssl .key file
var certificate = fs.readFileSync('/etc/apache2/ssl-certificate/site.crt', 'utf8'); // change with your ssl .crt file
option = {
key: privateKey,
cert: certificate
}
io.on("connection", function (socket) {
socket.on("message", function (data) {
console.log("message Recieved: " + JSON.stringify(data));
io.emit("conversation:" + data.conversation_id, data);
});
socket.on("chat_attachment", function (data) {
console.log("message Recieved: " + JSON.stringify(data));
io.emit("conversation:" + data.conversation_id, data);
});
});
https.createServer(option, app).listen(port, '0.0.0.0', function () {
console.log("Listening on Port " + port);
});
Same code but without SSL config, works over our dev server with normal HTTP.
I tried to follow recommendations at:
Apache and NodeJS over SSL

Just to guide anyone experiencing this issue, we were able to resolve this issue following #paul's advice (see comments).
Basically we have set up a subdomain (backend.ourdomain.com) which resolves port 3000 and from there we can run nodejs just fine.

Related

I need to secure my node js website to HTTPS running on the default 80 port of HTTP

I am running my server on ionos hosting and executing nodejs on the default port of 80.
I don't know how to enable the HTTPS for it.
Following is my sample node js server creation code:
const Https = require('https');
const fs = require('fs');
const httpsServer = Https.createServer({
key: fs.readFileSync("private.key"),
cert: fs.readFileSync("Dev-2020-09-12-013930.cer")
}, app);
var io = require('socket.io').listen(Https);
global.SOCKET = io;
const ip = require('ip');
console.log('websocket server start.' + ' ipaddress = ' + ip.address() );
// const socket = io('http://localhost:5000');
httpsServer.listen(80, function () {
console.log('Server port: ' + port);
});
I have generated certificates and added them. On running the server it gives message of server started but does not load on browser.
Try adding these lines of code and see if you get "Hello" text in your browser.
https.get("/", (req, res) => {
res.send("Hello");
});
if that didn't work try doing it this way
httpsServer.get("/", (req, res) => {
res.send("Hello");
});
EDIT
Check out the official documentation https://nodejs.org/api/https.html

Communication between client and proxy keeps failing because of self-signed certificate

I have a proxy application written in Javascript/Node.js that uses the http-mitm-proxy library.
I also have an https server on a separate machine that will serve files (currently it just replies "hello world"). I used a self signed root CA and a self-signed certificate for this (details below).
On a third machine, I try to access the https file server via the proxy. I just use Firefox for this (I've configured the proxy settings with my proxy machine's IP).
What I'm trying to do is intercept the https traffic (i.e. the files) on the proxy and cache it (i.e. decrypt it, store it locally and then pass it on). This is basically a Man-In-The-Middle attack.
The problem is that the proxy and client keep rejecting the self-signed certificate during communication.
I generated the rootCA and self-signed certificate using the steps found here. I can provide the exact values/details, if necessary.
I imported the certificate in Firefox on my client machine and I use them in the proxy app code.
I also added the appropriate entries in the hosts file on both client and proxy machine.
Here is my code:
// HTTPS File Server:
const https = require('https');
const fs = require('fs');
const options = {
key: fs.readFileSync('myupdateproxy.com.key'),
cert: fs.readFileSync('myupdateproxy.com.crt')
};
https.createServer(options, function (req, res) {
res.writeHead(200);
res.end("hello world\n");
}).listen(8000);
// proxy server code:
'use strict';
var port = 8081;
var path = require('path');
var Proxy = require('http-mitm-proxy');
var proxy = Proxy();
const fs = require('fs');
var chunks1 = [];
var chunks2 = [];
proxy.onCertificateRequired = function(hostname, callback) {
return callback(null, {
keyFile: path.resolve('myupdateproxy.com.key'),
certFile: path.resolve('myupdateproxy.com.crt')
});
};
proxy.onError(function(ctx, err, errorKind) {
// ctx may be null
var url = (ctx && ctx.clientToProxyRequest) ? ctx.clientToProxyRequest.url : '';
console.error(errorKind + ' on ' + url + ':', err);
});
proxy.onRequest(function(ctx, callback) {
console.log('onRequest');
callback();
});
proxy.onRequestData(function(ctx, chunk, callback) {
console.log('onRequestData');
chunks1.push(chunk);
callback(null, chunk);
});
proxy.onRequestEnd(function(ctx, callback) {
if (ctx.clientToProxyRequest.socket.remoteAddress !== undefined &&
ctx.proxyToServerRequest.socket.remoteAddress !== undefined &&
(Buffer.concat(chunks1)).length > 0)
{
console.log('From: ' + ctx.clientToProxyRequest.socket.remoteAddress);
console.log('To: ' + ctx.proxyToServerRequest.socket.remoteAddress);
console.log('Size: ' + (Buffer.concat(chunks1)).length);
console.log('');
}
chunks1 = [];
callback();
});
proxy.onResponse(function(ctx, callback) {
callback(null);
});
proxy.onResponseData(function(ctx, chunk, callback) {
chunks2.push(chunk);
callback(null, chunk);
});
proxy.onResponseEnd(function(ctx, callback) {
var total_size=(Buffer.concat(chunks2)).length;
if (ctx.serverToProxyResponse.socket.remoteAddress !== undefined &&
ctx.proxyToClientResponse.socket.remoteAddress !== undefined &&
total_size > 0)
{
console.log('From: ' + ctx.serverToProxyResponse.socket.remoteAddress);
console.log('To: ' + ctx.proxyToClientResponse.socket.remoteAddress);
console.log('Size: ' + total_size);
console.log('');
}
console.log((Buffer.concat(chunks2)).toString());
chunks2 = [];
callback();
});
proxy.listen({ port: port, sslCaDir: "/home/user/mycerts/" });
console.log('listening on ' + port);
I keep getting this error in the proxy app:
onRequest
PROXY_TO_SERVER_REQUEST_ERROR on /: Error: self signed certificate
at TLSSocket.onConnectSecure (_tls_wrap.js:1473:34)
at TLSSocket.emit (events.js:311:20)
at TLSSocket._finishInit (_tls_wrap.js:916:8)
at TLSWrap.ssl.onhandshakedone (_tls_wrap.js:686:12) {
code: 'DEPTH_ZERO_SELF_SIGNED_CERT'
}
And this message on the client machine (in Firefox):
PROXY_TO_SERVER_REQUEST_ERROR: Error: self signed certificate
Does anyone know what I'm doing wrong and how I can make this work?
Thanks!
Try to make insecure TLS connection from the proxy to the app - add this snippet to your proxy code:
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
Or try to add your custom CA cert to your system (where proxy app is running) CA certs. Exact command depends on used OS.

nodejs can't work with SSL

Im trying to run nodejs app to work with my php project. the problem is I think with SSL which is enabled in the server.
I have two files that I found in my root directory after SSL install: domain.com.csr and domain.com.key and I tried to combine them to connection while creating https server, but nothing worked for me.
so far I have this code:
var socket = require('socket.io');
var express = require('express');
var http = require('http');
var app = express();
var server = http.createServer(app);
var io = socket.listen(server);
app.get('/test', function(req, res) {
res.send('hello world');
console.log('visited test')
});
io.sockets.on('connection', function (client) {
console.log("New client !");
client.on('message', function (data) {
console.log('Message received ' + data.name + ":" + data.message);
io.sockets.emit('message', {name: data.name, message: data.message});
});
});
server.listen(8080, function () {
console.log('listen me on: 8080');
});
and it works well when I'm trying to visit http://ip:8080/test so it means that node server is working, but when I try to create socket connection on my view file var socket = io.connect('http://ip:8080'); it gives me error:
The page at 'https://www.domain.com/' was loaded over HTTPS, but requested an insecure XMLHttpRequest endpoint 'http://ip:8080/socket.io/?EIO=3&transport=polling&t=1446818946199-0'. This request has been blocked; the content must be served over HTTPS.
so the problem is clear enough, but how to deal with it?
also I have tried this connection:
var socket = io.connect('https://www.domain.com:8080');
but the result is 404 GET Error. How to deal with it?
Update
now the part of code I should use, but don't know how to get cert of existing SSL in the server.
var socket = require('socket.io');
var express = require('express');
var https = require('https');
var fs = require('fs');
var options = {
key: fs.readFileSync('path/to/key.pem'), // dont have
cert: fs.readFileSync('path/to/cert.cert') // dont have
};
var app = express();
var server = https.createServer(options, app);
var io = socket.listen(server);
app.get('/test', function(req, res) {
res.send('hello world');
console.log('visited test')
});
io.sockets.on('connection', function (client) {
console.log("New client !");
client.on('message', function (data) {
console.log('Message received ' + data.name + ":" + data.message);
io.sockets.emit('message', {name: data.name, message: data.message});
});
});
server.listen(443, function () {
console.log('listen me on: 443');
});
I think you need to contact your certificate authority (the organization that issued your first ssl certificate) and get a copy of the certificate (the path/to/key.pem and path/to/cert.cert) or find the existing keys somewhere on your existing server.
If you're running apache, your configuration file will have a section with values for the paths of the .cert and .pem files labeled SSLCertificateFile and SSLCertificateKeyFile, then just update the paths in your node app to point to them. You also have to make sure that your SSL certificate meets the requirements (for example, needs to be Multi-domain if your node app runs on a different domain, or a Wildcard SSL certificate to run your node app on a subdomain).
The domain.com.csr and domain.com.key files you found are the private key and certificate request used to generate your initial SSL certificate and aren't going to do anything to enable SSL on your node app.

Node.JS https server for Webhook gets handshake error

The following NodeJS just handles a webhook coming from another source. I tested it out, it works on http port 80 but the source requires https.
When I opened that port and ran this script on 443, testing it with curl gets the following error. Yet I don't think it should require a certificate should it? How would I even solve this?
curl: (35) SSL peer handshake failed, the server most likely requires a client certificate to connect
Here is the script:
var http = require('https')
var server = http.createServer(function(req, res) {
if (req.method == "POST") {
var str = ''
req.on('data', function(data) {
str += data
})
req.on('end', function() {
var json = JSON.parse(str)
res.end(json.meta.status)
})
}
})
console.log("HTTPS server listening on port 443...")
server.listen(443)
UPDATE:
Here's the latest code. I created a self-signed cert without a passphrase. I get further but I still get an error using curl with the -k option added. I get a certificate verification error without the -k.
Cannot POST /
var https = require('https')
var fs = require('fs')
var express = require('express');
var options = {
key: fs.readFileSync('./server.key'),
cert: fs.readFileSync('./server.cert')
}
var app = express()
var server = https.createServer(options, app, function(req, res) {
if (req.method == "POST") {
var str = ''
req.on('data', function(data) {
str += data
})
req.on('end', function() {
var json = JSON.parse(str)
res.end(json.meta.status)
})
}
})
console.log("HTTPS server listening on port 443...")
server.listen(443)
HTTPS server config always requires SSL certificate. You can generate it using openssl here is in more details.
Then for node server use crypto,fs modules. Detailed config is here.

The nodejs net.createServer function results in a blank server.address value in Bluemix

The net module has a createServer function that allows you to create a network wrapper. This works fine on a local runtime of Nodejs, but when running in Bluemix it is unable to determine the host address. The server seems to get created, but upon further inspection I find the server.address to be blank.
var tls = require('tls');
var fs = require('fs');
var net = require('net');
var tunnelHost = (process.env.VCAP_APP_HOST || 'localhost');
var tunnelPort = 8888;
var server;
var gatewayOptions = {
host: 'http://cap-sg-prd-5.integration.ibmcloud.com/',
port: '15133',
key: fs.readFileSync('key.pem'),
cert: fs.readFileSync('cert.pem'),
ca: fs.readFileSync('ca.pem')
};
console.log("starting createSecureTunnel");
//create a server end point to use as a network wrapper for the secure gateway
server = net.createServer(function (connListener){
console.log('net server created');
connListener.on('end', function() {
console.log('client disconnected');
});
connListener.on('uncaughtException', function(err){
console.log('exception caught: ' + JSON.stringify(err));
});
//connect to farside, local/private server
connectFarside(connListener, function(err, remoteSocket){
if (err){
console.log(err);
}
console.log('connection made');
remoteSocket.pipe(connListener);
console.log('remote socket connecte to local connListener');
connListener.pipe(remoteSocket);
console.log('local connListener connected to remote socket');
});
});
//setup listener for network wrapper
server.listen(tunnelPort, tunnelHost, function(){
console.log('tunnel created at: ' + tunnelHost +":"+ tunnelPort); //.address +":"+ server.address().port);
});
//createa a TLS connection to the secure gateway
function connectFarside(conn, callback) {
console.log("starting connectFarside");
try {
console.log("initiating farside connection");
var socket = tls.connect(gatewayOptions, function(){
console.log("tunnel connected to " + gatewayOptions.host +":"+ gatewayOptions.port);
callback(null, socket);
});
socket.on("error", function(err){
console.log("Socket error: " + JSON.stringify(err));
});
} catch(err) {
console.log(err);
callback(err);
}
}
Bluemix gives your app a port to run on, this is the reason it is not working in Bluemix. You are starting to start your app on port 8888 with the following line of code.
var tunnelPort = 8888;
It should be changed to
var tunnelPort = process.env.VCAP_APP_PORT || 8888;
The above line will read an environment variable called VCAP_PORT where Bluemix assigns a port to your app, if it is not running Bluemix it will run on port 8888.
Your app will be accessible over the web on port 80 and 443. Bluemix will load balance to your app for you.
You can specify the server address when listening to the server
var net = require('net')
var server = net.createServer(handler)
server.listen(port, address)
Try with address = '0.0.0.0' and see if it works
Partially solved by using the cf-autoconfig module. It helps to reconfigure modules for use on Cloud Foundry platforms. By including this as the first line in my app, it mostly works. It doesn't use the port number. But at least I can access the wrapper.
So I added this as the first line
require("cf-autoconfig");
Then I changed the server.listen to this
//setup listener for network wrapper
server.listen(tunnelPort, function(){
console.log('tunnel created at: ' + tunnelHost +":"+ tunnelPort); //.address +":"+ server.address().port);
});
Now if I use my app name, I can connect to the server created by net.createServer().
I would still like to know how to get the port to work, so this can be used inside of a web application to provide the tunneling.

Resources