compoundjs support both ssl and normal http - ssl

I needed to use SSL on my site in certain situations,
I have followed the example in the readme
require('compound').createServer({
key: fs.readFileSync('/tmp/tls.key').toString(),
cert: fs.readFileSync('/tmp/tls.cert').toString()
});
My server.js is
#!/usr/bin/env node
var fs = require('fs');
/**
* Server module exports method returning new instance of app.
*
* #param {Object} params - compound/express webserver initialization params.
* #returns CompoundJS powered express webserver
*/
var app = module.exports = function getServerInstance(params) {
params = params || {};
// specify current dir as default root of server
params.root = params.root || __dirname;
params["key"] = fs.readFileSync('/tmp/tls.key').toString();
params["cert"] = fs.readFileSync('/tmp/tls.cert').toString();
return require('compound').createServer(params);
};
if (!module.parent) {
var port = process.env.PORT || 3000;
var host = process.env.HOST || '0.0.0.0';
var server = app();
server.listen(port, host, function () {
console.log(
'Compound server listening on %s:%d within %s environment',
host, port, server.set('env')
);
});
}
Now the problem is I can't get http, only https.
If I just put my cert and key inside of the config folder and have
**var app = module.exports = function getServerInstance(params) {
params = params || {};
// specify current dir as default root of server
params.root = params.root || __dirname;
return require('compound').createServer(params);
};**
Then I get some error that
config/tls.cert:1
tion (exports, require, module, __filename, __dirname) { -----BEGIN CERTIFICAT
^^^^^^^^^^^
SyntaxError: Unexpected identifier
at Module._compile (module.js:439:25)
What is the quickest and easiest way to get compoundjs to support both https and http routes, do I need to have two servers (like you would often with straight express)?

YES - need to have two servers like you do with express. So
var app = module.exports = function getServerInstance(params) {
params = params || {};
// specify current dir as default root of server
params.root = params.root || __dirname;
return require('compound').createServer(params);
};
var port = process.env.PORT || 80;
var host = process.env.HOST || '0.0.0.0';
var port_https = 443;
var server = app();
server.listen(port, host, function () {
server.set("confvar", process.env.SERVER_NAME);
console.log(
'Compound server listening on %s:%d within %s environment',
host, port, server.set('env')
);
});
if(fs.existsSync(PATH TO KEY GOES HERE)){
var apphttps = module.exports = function getServerInstance(params) {
params = params || {};
// specify current dir as default root of server
params.root = params.root || __dirname;
params.key = fs.readFileSync(PATH TO KEY GOES HERE).toString();
params.cert = fs.readFileSync(PATH TO CERT GOES HERE).toString();
return require('compound').createServer(params);
};
var serverhttps = apphttps();
serverhttps.listen(port_https, host, function () {
serverhttps.set("confvar", process.env.SERVER_NAME);
console.log(
'Compound https server listening on %s:%d within %s environment',
host, port_https, serverhttps.set('env')
);
});
}

Related

Express JS Websocket Connection to wss failed without error but still able to communicate

I am using an Express JS to initiate websocket connection. Below is the following configuration that I used.
socket.js
const ip_address = `${process.env.MIX_HTTPS_APP_URL}`;
const socket_port = `${process.env.MIX_EXPRESS_PORT}`;
const URL = ip_address + ":" + socket_port;
export const socket = io(URL, { autoConnect: true });
socket.onAny((event, ...args) => {
// console.log(event, args);
});
index.js
const app = express();
// app settings
/** Create HTTP server. */
const privateKey = fs.readFileSync(env.privateKey, "utf8");
const certificate = fs.readFileSync(env.certificate, "utf8");
const options = {
key: privateKey,
cert: certificate,
};
const server = https.createServer(options, app).listen(port);
/** Create socket connection */
const socketio = new Server(server);
global.io = socketio.listen(server, {
cors: {
origin: env.url,
},
});
global.io.on("connection", WebSockets.connection);
/** Listen on provided port, on all network interfaces. */
/** Event listener for HTTP server "listening" event. */
server.on("listening", () => {
console.log(`Listening on port:: ${env.url}:${port}/`);
});
Though I can emit and listen to socket, I am still getting this error message. How should I solve this so that users wont see this error message when they view the console?
With reference to your example, the listening on port should be on http server, & socket.io should just attach to on new conn.
see doc https://socket.io/docs/v4/server-initialization/#with-an-https-server
Hope this helps.

Port error when deploying express server with OIDC to Azure App Service

I am attempting to deploy a server to an azure app service. The server code can be found below.
The error I am getting from the log stream is:
2020-11-18T23:36:06.088Z ERROR - Container [container name] didn't respond to HTTP pings on port: 8080, failing site start. See container logs for debugging.
I have PORT set to 8080 and I know that config is picking up as I can see "Server listening on port 8080" in the logs. I have tried changing WEBSITES_PORT to 80 and 8080 as I saw that other posts, but I think my issue is different.
This site was working prior to my adding auth with OIDC libraries.
The app works locally with the server code below.
const https = require('https')
const express = require('express')
const path = require('path')
const app = express()
const fs = require('fs')
const key = fs.readFileSync('./key.pem')
const cert = fs.readFileSync('./cert.pem')
require('dotenv').config()
app.use(express.json())
app.use(express.urlencoded({
extended: true
}))
app.use(express.static('express'))
var cors = require('cors')
const OktaJwtVerifier = require('#okta/jwt-verifier')
const session = require('express-session')
const {
ExpressOIDC
} = require('#okta/oidc-middleware')
var getUserInfo = require('./getUserInfo')
// session support is required to use ExpressOIDC
app.use(
session({
secret: 'this should be secure',
resave: true,
saveUninitialized: false,
cookie: {
httpOnly: false,
secure: true,
},
})
)
const oidc = new ExpressOIDC({
issuer: process.env.ISSUER || 'https://[custom auth server domain].gov/oauth2/default',
client_id: process.env.CLIENT_ID || 'xxxxxxxxxxxxxxxxx',
client_secret: process.env.CLIENT_SECRET || 'xxxxxxxxxxxxxxxxxx',
redirect_uri: process.env.REDIRECT_URI ||
'https://localhost:3000/authorization-code/callback',
appBaseUrl: process.env.APP_BASE_URL || 'https://localhost:3000',
scope: 'openid profile',
})
// ExpressOIDC attaches handlers for the /login and /authorization-code/callback routes
app.use(oidc.router)
app.use(cors())
app.options('*', cors())
app.get('/userinfo', (req, res) => {
let domain = 'dev'
if (req.isAuthenticated()) {
getUserInfo.userRequest(res, req.userContext, domain)
}
})
app.get('/authStatus', (req, res) => {
if (req.isAuthenticated()) {
res.send(req.userContext.userinfo)
}
})
app.post('/forces-logout', oidc.forceLogoutAndRevoke(), (req, res) => {
// Nothing here will execute, after the redirects the user will end up wherever the `routes.logoutCallback.path` specifies (default `/`)
})
var linkObj = {not relevant links used hrefs on html based on env}
// default URL for website
app.get('/', function(req, res) {
res.sendFile(path.join(__dirname + '/express/index.html'))
//__dirname : It will resolve to your project folder.
})
// FAQ Path
app.get('/help', function(req, res) {
res.sendFile(path.join(__dirname + '/express/help.html'))
//__dirname : It will resolve to your project folder.
})
app.get('/links', (req, res) => {
res.json(linkObj)
})
app.post('/forces-logout', oidc.forceLogoutAndRevoke(), (req, res) => {
// Nothing here will execute, after the redirects the user will end up wherever the `routes.logoutCallback.path` specifies (default `/`)
})
// default URL for website
app.get('*', function(req, res) {
res.sendFile(path.join(__dirname + '/express/index.html'))
//__dirname : It will resolve to your project folder.
})
const port = normalizePort(process.env.PORT || '3000')
if (process.env.PORT) {
const server = https.createServer(app)
server.listen(port)
} else {
const server = https.createServer({
key: key,
cert: cert
}, app)
server.listen(port)
}
console.debug('Server listening on port ' + port)
function normalizePort(val) {
var port = parseInt(val, 10)
if (isNaN(port)) {
// named pipe
return val
}
if (port >= 0) {
// port number
return port
}
return false
}
I believe it's this line that could be giving you issues:
const port = normalizePort(process.env.PORT || '3000')
I'd try changing it to:
const port = normalizePort(process.env.PORT || '8080')
You'll also need to change these lines to have your public URL, not localhost:
redirect_uri: process.env.REDIRECT_URI ||
'https://localhost:3000/authorization-code/callback',
appBaseUrl: process.env.APP_BASE_URL || 'https://localhost:3000',
After you change these, you'll need to update your app on Okta to your production redirect URI.

Koa HTTPS connection with koa-sslify module Without Reverse Proxy

I try to make HTTPS connection for Koa server with that module https://www.npmjs.com/package/koa-sslify but I get error "AssertionError: app.use() requires a generator function"
'use strict';
var app = require('application'),
enforceHttps = require('koa-sslify'),
config = require('config'),
fs = require('fs'),
path = require('path'),
routes = fs.readdirSync(path.join(__dirname, '../routing')).sort();
routes.forEach(function (route) {
require('../routing/' + route);
});
// Force HTTPS on all page
app.use(enforceHttps({
trustProtoHeader: true
}));
app.listen(config.server.port,config.server.host);
UPD:
I used NGINX instead, because probably it does that work better and use less resources
try in this way, but better use NGINX as a reverse proxy server
const Koa = require('koa');
const https = require('https');
const fs = require('fs');
const { default: enforceHttps } = require('koa-sslify');
const app = new Koa();
// Force HTTPS using default resolver
app.use(enforceHttps({
port: 443
}));
// index page
app.use(ctx => {
ctx.body = `hello world from ${ctx.request.url}`;
});
// SSL options
const options = {
key: fs.readFileSync('server.key'),
cert: fs.readFileSync('server.crt')
}
// start the server
https.createServer(options, app.callback()).listen(443);

Socket Programming res.sendFile is not a function

res.sendFile is not a function.
The following is my server js code
var app = require('express')();
var http = require('http').Server(app);
var fs = require('fs');
var io = require("socket.io")(http);
var users = {};
app.get('/', function(req, res) {
res.sendFile(__dirname + '/client.html');
});
app.listen(3000)
io.on("connection", function (client) {
client.on("join", function (name) {
users[client.id] = name;
client.emit("update", "Connected to Server");
socket.sockets.emit("update", name + " has entered the confession room");
socket.sockets.emit("update-users", users);
});
client.on("send", function (msg) {
socket.sockets.emit("chat", users[client.id], msg);
});
client.on("disconnect", function () {
socket.sockets.emit("update", users[client.id] + " has left the confession room");
delete users[client.id];
socket.sockets.emit("update-users", users);
});
});
Things I have tried:
- Checking the order of the parameter.
- Installing express module.
You have duplicate servers with these two lines of code and you're passing the one to socket.io that you never start:
var http = require('http').Server(app); // one on port 80
app.listen(3000); // one on port 3000
Remove the first one, then modify your code as shown below. And, make sure you're connecting on port 3000 where the Express server and socket.io severs are.
Or, if you want the server and socket.io to be on port 80, then change to:
const server = app.listen(80);
So, you should end up with this:
const app = require('express')();
const fs = require('fs');
const users = {};
app.get('/', function(req, res) {
res.sendFile(__dirname + '/client.html');
});
const server = app.listen(3000);
const io = require("socket.io")(server);
// various socket.io code here

node.js, socket.io with SSL

I'm trying to get socket.io running with my SSL certificate however, it will not connect.
I based my code off the chat example:
var https = require('https');
var fs = require('fs');
/**
* Bootstrap app.
*/
var sys = require('sys')
require.paths.unshift(__dirname + '/../../lib/');
/**
* Module dependencies.
*/
var express = require('express')
, stylus = require('stylus')
, nib = require('nib')
, sio = require('socket.io');
/**
* App.
*/
var privateKey = fs.readFileSync('../key').toString();
var certificate = fs.readFileSync('../crt').toString();
var ca = fs.readFileSync('../intermediate.crt').toString();
var app = express.createServer({key:privateKey,cert:certificate,ca:ca });
/**
* App configuration.
*/
...
/**
* App routes.
*/
app.get('/', function (req, res) {
res.render('index', { layout: false });
});
/**
* App listen.
*/
app.listen(443, function () {
var addr = app.address();
console.log(' app listening on http://' + addr.address + ':' + addr.port);
});
/**
* Socket.IO server (single process only)
*/
var io = sio.listen(app,{key:privateKey,cert:certificate,ca:ca});
...
If I remove the SSL code it runs fine, however with it I get a request to http://domain.example/socket.io/1/?t=1309967919512
Note it's not trying HTTPS, which causes it to fail.
I'm testing on chrome, since it is the target browser for this application.
I apologize if this is a simple question, I'm a node/socket.io newbie.
Use a secure URL for your initial connection, i.e. instead of "http://" use "https://". If the WebSocket transport is chosen, then Socket.IO should automatically use "wss://" (SSL) for the WebSocket connection too.
Update:
You can also try creating the connection using the 'secure' option:
var socket = io.connect('https://localhost', {secure: true});
The following is how I set up to set it up with express:
var app = require('express')();
var https = require('https');
var fs = require( 'fs' );
var io = require('socket.io')(server);
var options = {
key: fs.readFileSync('./test_key.key'),
cert: fs.readFileSync('./test_cert.crt'),
ca: fs.readFileSync('./test_ca.crt'),
requestCert: false,
rejectUnauthorized: false
}
var server = https.createServer(options, app);
server.listen(8080);
io.sockets.on('connection', function (socket) {
// code goes here...
});
app.get("/", function(request, response){
// code goes here...
})
Update : for those using lets encrypt use this
var server = https.createServer({
key: fs.readFileSync('privkey.pem'),
cert: fs.readFileSync('fullchain.pem')
}, app);
On the same note, if your server supports both http and https you can connect using:
var socket = io.connect('//localhost');
to auto detect the browser scheme and connect using http/https accordingly. when in https, the transport will be secured by default, as connecting using
var socket = io.connect('https://localhost');
will use secure web sockets - wss:// (the {secure: true} is redundant).
for more information on how to serve both http and https easily using the same node server check out this answer.
If your server certificated file is not trusted, (for example, you may generate the keystore by yourself with keytool command in java), you should add the extra option rejectUnauthorized
var socket = io.connect('https://localhost', {rejectUnauthorized: false});
Depending on your needs, you could allow both secure and unsecure connections and still only use one Socket.io instance.
You simply have to instanciate two servers, one for HTTP and one for HTTPS, then attach those servers to the Socket.io instance.
Server side :
// needed to read certificates from disk
const fs = require( "fs" );
// Servers with and without SSL
const http = require( "http" )
const https = require( "https" );
const httpPort = 3333;
const httpsPort = 3334;
const httpServer = http.createServer();
const httpsServer = https.createServer({
"key" : fs.readFileSync( "yourcert.key" ),
"cert": fs.readFileSync( "yourcert.crt" ),
"ca" : fs.readFileSync( "yourca.crt" )
});
httpServer.listen( httpPort, function() {
console.log( `Listening HTTP on ${httpPort}` );
});
httpsServer.listen( httpsPort, function() {
console.log( `Listening HTTPS on ${httpsPort}` );
});
// Socket.io
const ioServer = require( "socket.io" );
const io = new ioServer();
io.attach( httpServer );
io.attach( httpsServer );
io.on( "connection", function( socket ) {
console.log( "user connected" );
// ... your code
});
Client side :
var url = "//example.com:" + ( window.location.protocol == "https:" ? "3334" : "3333" );
var socket = io( url, {
// set to false only if you use self-signed certificate !
"rejectUnauthorized": true
});
socket.on( "connect", function( e ) {
console.log( "connect", e );
});
If your NodeJS server is different from your Web server, you will maybe need to set some CORS headers. So in the server side, replace:
const httpServer = http.createServer();
const httpsServer = https.createServer({
"key" : fs.readFileSync( "yourcert.key" ),
"cert": fs.readFileSync( "yourcert.crt" ),
"ca" : fs.readFileSync( "yourca.crt" )
});
With:
const CORS_fn = (req, res) => {
res.setHeader( "Access-Control-Allow-Origin" , "*" );
res.setHeader( "Access-Control-Allow-Credentials", "true" );
res.setHeader( "Access-Control-Allow-Methods" , "*" );
res.setHeader( "Access-Control-Allow-Headers" , "*" );
if ( req.method === "OPTIONS" ) {
res.writeHead(200);
res.end();
return;
}
};
const httpServer = http.createServer( CORS_fn );
const httpsServer = https.createServer({
"key" : fs.readFileSync( "yourcert.key" ),
"cert": fs.readFileSync( "yourcert.crt" ),
"ca" : fs.readFileSync( "yourca.crt" )
}, CORS_fn );
And of course add/remove headers and set the values of the headers according to your needs.
check this.configuration..
app = module.exports = express();
var httpsOptions = { key: fs.readFileSync('certificates/server.key'), cert: fs.readFileSync('certificates/final.crt') };
var secureServer = require('https').createServer(httpsOptions, app);
io = module.exports = require('socket.io').listen(secureServer,{pingTimeout: 7000, pingInterval: 10000});
io.set("transports", ["xhr-polling","websocket","polling", "htmlfile"]);
secureServer.listen(3000);
Server-side:
import http from 'http';
import https from 'https';
import SocketIO, { Socket } from 'socket.io';
import fs from 'fs';
import path from 'path';
import { logger } from '../../utils';
const port: number = 3001;
const server: https.Server = https.createServer(
{
cert: fs.readFileSync(path.resolve(__dirname, '../../../ssl/cert.pem')),
key: fs.readFileSync(path.resolve(__dirname, '../../../ssl/key.pem'))
},
(req: http.IncomingMessage, res: http.ServerResponse) => {
logger.info(`request.url: ${req.url}`);
let filePath = '.' + req.url;
if (filePath === './') {
filePath = path.resolve(__dirname, './index.html');
}
const extname = String(path.extname(filePath)).toLowerCase();
const mimeTypes = {
'.html': 'text/html',
'.js': 'text/javascript',
'.json': 'application/json'
};
const contentType = mimeTypes[extname] || 'application/octet-stream';
fs.readFile(filePath, (error: NodeJS.ErrnoException, content: Buffer) => {
if (error) {
res.writeHead(500);
return res.end(error.message);
}
res.writeHead(200, { 'Content-Type': contentType });
res.end(content, 'utf-8');
});
}
);
const io: SocketIO.Server = SocketIO(server);
io.on('connection', (socket: Socket) => {
socket.emit('news', { hello: 'world' });
socket.on('updateTemplate', data => {
logger.info(data);
socket.emit('updateTemplate', { random: data });
});
});
server.listen(port, () => {
logger.info(`Https server is listening on https://localhost:${port}`);
});
Client-side:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Websocket Secure Connection</title>
</head>
<body>
<div>
<button id='btn'>Send Message</button>
<ul id='messages'></ul>
</div>
<script src='../../../node_modules/socket.io-client/dist/socket.io.js'></script>
<script>
window.onload = function onload() {
const socket = io('https://localhost:3001');
socket.on('news', function (data) {
console.log(data);
});
socket.on('updateTemplate', function onUpdateTemplate(data) {
console.log(data)
createMessage(JSON.stringify(data));
});
const $btn = document.getElementById('btn');
const $messages = document.getElementById('messages');
function sendMessage() {
socket.emit('updateTemplate', Math.random());
}
function createMessage(msg) {
const $li = document.createElement('li');
$li.textContent = msg;
$messages.appendChild($li);
}
$btn.addEventListener('click', sendMessage);
}
</script>
</body>
</html>
For enterprise applications it should be noted that you should not be handling https in your code. It should be auto upgraded via IIS or nginx. The app shouldn't know about what protocols are used.
In case someone need a shorter form
var fs = require('fs');
var https = require('https');
var express = require('express');
var app = express();
var options = {
key: fs.readFileSync('/path-to/ssl.key'),
cert: fs.readFileSync('/path-to/ssl.cert')
};
var server = https.createServer(options, app);
var io = require('socket.io')(server);
This is my nginx config file and iosocket code. Server(express) is listening on port 9191. It works well:
nginx config file:
server {
listen 443 ssl;
server_name localhost;
root /usr/share/nginx/html/rdist;
location /user/ {
proxy_pass http://localhost:9191;
}
location /api/ {
proxy_pass http://localhost:9191;
}
location /auth/ {
proxy_pass http://localhost:9191;
}
location / {
index index.html index.htm;
if (!-e $request_filename){
rewrite ^(.*)$ /index.html break;
}
}
location /socket.io/ {
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_pass http://localhost:9191/socket.io/;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
ssl_certificate /etc/nginx/conf.d/sslcert/xxx.pem;
ssl_certificate_key /etc/nginx/conf.d/sslcert/xxx.key;
}
Server:
const server = require('http').Server(app)
const io = require('socket.io')(server)
io.on('connection', (socket) => {
handleUserConnect(socket)
socket.on("disconnect", () => {
handleUserDisConnect(socket)
});
})
server.listen(9191, function () {
console.log('Server listening on port 9191')
})
Client(react):
const socket = io.connect('', { secure: true, query: `userId=${this.props.user._id}` })
socket.on('notifications', data => {
console.log('Get messages from back end:', data)
this.props.mergeNotifications(data)
})
I needed to get this to work with Debian 10, ISPConfig 3 and Let's Encrypt. Took me a while to work out the specifics. Maybe this saves someone else some timeā€¦
Server-side:
const fs = require('fs');
const https = require('https');
const express = require('express');
const socketio = require('socket.io');
const app = express();
const https_options = {
key: fs.readFileSync('/var/www/clients/client1/web1/ssl/your-domain.com-le.key'),
cert: fs.readFileSync('/var/www/clients/client1/web1/ssl/your-domain.com-le.crt'),
ca: fs.readFileSync('/root/.acme.sh/your-domain.example/fullchain.cer'),
requestCert: false,
rejectUnauthorized: false
}
const server = https.createServer(https_options, app);
server.listen(3000, () => {
console.log('server started ok');
});
const io = socketio(server, {
cors: {
origin: "https://your-domain.example",
},
secure: true
});
io.on('connection', (sock) => {
console.log('someone connected');
}
Client-side:
const sock = io('https://your-domain.example:3000/');
sock.on('status', (text) => {
add_text($('#status'), text);
});
Server side:
var ssl_options = {
ca: [fs.readFileSync('../ssl/cert1.crt'), fs.readFileSync('../ssl/cert2.crt'), fs.readFileSync('../ssl/cert3.crt')],
key: fs.readFileSync('../ssl/xxx.key'),
cert: fs.readFileSync('../ssl/xxx.example.crt'),
};
var wssServer = https.createServer(ssl_options,app); // Express app
wssServer.listen(4433, '0.0.0.0');
global.io = require("socket.io")();
io.listen(wssServer);
io.on( "connection", function( socket ) {
console.log( "user connected" );
});
Client-side (no luck with the built-in WebSocket API):
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/3.1.3/socket.io.js"></script>
<script>
const socket = io("https://xxx.example:4433",{ transports: ['websocket', 'polling', 'flashsocket'] });
</script>