How to get websocket over SSL in Express app? - node.js

After making my websocket work localy i deployed it and got security errors due to it not being over SSL. I am hosting a NodeJS Express app with Nginx and Certbot.
server.js
const express = require('express');
const app = express();
// unrelated imports
app.use(bodyParser.json());
app.use(cors());
app.use(express.static('public'));
app.use(bodyParser.json({ limit: '50mb' }))
require('./services/socket')(app)
// unrelated endpoints
app.listen(port);
socket.js
let WebSocket = require('ws');
module.exports = function(app) {
let ws_server = new WebSocket.Server();
ws_server.on('connection', (connection) => {
connection.on('message', (message) => {
ws_server.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN && client != connection) {
client.send(message);
}
});
});
});
}
Now the app is HTTPS but not the websocket, how can i get the socket to also use SSL/certs?

In such setup nginx needs to handle the SSL and be properly set up to work with web sockets. Here's an article from nginx blog NGINX as a WebSocket Proxy . The essential bits are:
location /wsapp/ {
proxy_pass http://wsbackend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
}
You might also want to set up timeouts for the connection.
And Express needs to trust the reverse proxy. Here's an article from them Express behind proxies
. If you are deploying in a secure environment you can just set it to app.set('trust proxy', ()=>true)
For reference here are the nginx timeouts I had to set up for my application:
proxy_connect_timeout 14d;
proxy_read_timeout 14d;
proxy_send_timeout 14d;

Related

CORS issues with Express, Socket.io and Nginx

I have adapted a tutorial to get a simple Socket.io chat going in Node. It works when hosted locally, but after pushing it to a test server I can't get the socket connection to be accepted. Seems to be a cross-origin related matter, though I'm slightly confused about how to route things in Nginx also. Following the advice in the related questions hasn't helped.
Client script:
var socket = io.connect('http://localhost/socket.io');
Index.js:
const express = require('express');
const app = express();
const path = require('path');
const httpServer = require('http').createServer(app);
const io = require('socket.io')(httpServer, {
cors:true,
origins:["*"],
// origins:["http://xxx.xxx.xxx.xxx:8080"],
// transports: ['websocket'],
});
const views_path = (__dirname + '/views');
app.set('views',views_path);
app.use(express.static(__dirname + '/public'));
app.get('/', function(req,res){
console.log('render request received');
res.render('startPage.ejs');
});
io.sockets.on('connection', socket => {
console.log('connection received.')
socket.on('username', function(username) {
socket.username = username;
io.emit('is_online', socket.username);
});
//...
});
httpServer.listen(8080);
nginx sites-available:
server {
server_name campfire;
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/campfire/html;
index index.html index.htm index.nginx-debian.html;
location ^~ /assets/ {
gzip_static on;
expires 12h;
add_header Cache-Control public;
}
location / {
proxy_set_header Host $host;
#proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://localhost:8080;
}
location /socket.io/ {
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'Upgrade';
proxy_http_version 1.1;
#proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_pass http://localhost:8080/socket.io/;
}
}
Any insights welcome!
Hope this will help with all the CORS error
Because it will handle it for you
const cors = require('cors');
app.use(cors());
Docs CORS
Making this change fixed the issue:
Client script: var socket = io.connect();
This way uses the default connection destination with socket.

How nginx proxy to express(node.js)?Why res.data is index.html?

I wrote a Node.js server project by Express framework.It's all right at localhost.But it's have some problem when I build at remote server.(centos 6.5; ngnix 1.11.6; express 4.14.1; node 6.9.5)
It's nginx.conf.
listen 80;
server_name www.cheeseyu.cn cheeseyu.cn;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
proxy_pass http://127.0.0.1:3009;
proxy_redirect off;
}
location ~ .*.(gif|jpg|jpeg|png|bmp|swf|js|css|woff|ttf|TTF|svg)$ {
root /home/www/blog;
if (-f $request_filename) {
expires 100d;
break;
}
}
error_page 405 =200 #405;
location #405 {
proxy_method GET;
proxy_pass http://static_resource;
}
#error_page 404 /404.html;
There are info of xhr.
enter image description here
enter image description here
It's node.js.
var express = require('express');
var path = require('path');
var app = express();
var bodyParser = require('body-parser');
var routes = require('./routes');
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.all('', function(req, res, next) {
res.header("Access-Control-Allow-Origin", "");
res.header("Access-Control-Allow-Headers", "X-Requested-With,Content-Type");
res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");
res.header("Content-Type", "application/json;charset=utf-8");
next();
});
routes(app);
app.set('port', process.env.PORT || 3009);
app.listen(app.get('port'), function() {
console.log('Express server listening on port ' + app.get('port'));
});
So my question are :
a. Why status is 200,even I stoped the back-server?
b. why response data is html(content is index.html)?
c. why status still is 405,when I use the post?I have tried any
methods that I finded.
d. Why status is 200,but after request don't use '.then',but use
'.catch'?
e. How nginx proxy to express(node.js)?(I think above all problem is
nginx didn't proxy request to node server.)
If you want to know details about response,you can visit cheeseyu.cn
Thank you help :)
I can show you what i use in nginx conf
location / {
proxy_pass http://your-domain.com:3009;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
this and a process manager is what i needed to get my first node app running on a server.
As process manager i used stableloop.
Important: U also have to check on witch port your node process is running and fit ur ports to that.
Hope that helps you bit.
(and you have to $~ service nginx reload after all changes)
This setting don't have any problem.Just because I reopen nginx,but it no use.You should stop nginx and open nginx

Nginx setup for a node server using angular cli

I am having issues with Nginx, Node Server and Express. I have deleted the default server block under /etc/nginx/sites-available and have added my own let's call it mikes-domain.com. I have copied the contents of the dist folder to /var/www/mikes-domain.com/html.
server {
listen 80 default_server;
root /var/www/mikes-domain.com/html;
index index.html index.htm;
server_name: mikes-domain.com;
location /admin {
proxy_pass http://localhost:8082;
prody_http_version: 1.1;
proxy_set_header Upgrade $http_upgrade;
prody_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
Node and Express Server File
const express = require('express');
const path = require('path');
const http = require('http');
const bodyParser = require ('body-parser');
const api = require('./server/routes/api');
const app = express;
app.use(cors());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));
app.use(express.static(path.join(__dirname, 'node_modules')));
app.use(*/api', api);
app.set('trust proxy', true);
app.set('trust proxy', 'loopback');
const port = process.env.PORT || '8082';
http.createServer(app).listen(port);
console.log('Api listening on port ' + port);
When I visit mikes-domain.com/admin/ I am receiving this Cannot GET /admin/ but I am getting api works! which is what express should be displaying when I visit mikes-domain.com/admin/api/
In nginx config you pass all requests from mikes-domain.com/admin to localhost:8082
In Node Server: app.use('*/api', api)
You do not config for default route in Node ( localhost:8082 ) , so in this case Node will show nothing with (mikes-domain.com/admin)
You can config your router to handle request to /admin/api/ with template below, unless Node will show nothing.
./server/routes/api.js
var router = express.Router()
// define the home page route
router.get('/', function (req, res) {
res.send('Testing API')
})

nodejs & express https server with NGINX

I'm trying to setup a nodejs server with https to make REST calls to.
Connecting directly to ip + port through http works fine. When using https with ip and port my android app complains that "the Hostname xx.xx.xx.xx not verified". So I figured I should setup a domain for it to make my certificate match.
With NGINX I made the following:
server {
listen 80;
server_name rest-ssl.mydomain.com;
location / {
proxy_pass https://xx.xx.xx.xx:4443;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffering off;
}
}
server {
listen 80;
server_name rest-normal.mydomain.com;
location / {
proxy_pass http://xx.xx.xx.xx:4080;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffering off;
}
}
And restarted my nginx service. In this config file I have two other server configs listening on port 80 that works fine.
My nodejs app looks like the following:
var express = require('express'),
http = require('http'),
https = require('https'),
fs = require('fs'),
Security = require('./security.js'),
json = require('express-json'),
stylus = require('stylus'),
nib = require('nib'),
path = require('path'),
bodyParser = require('body-parser');
var app = express();
app.use(bodyParser.urlencoded({extended:true}));
app.use(bodyParser.json());
app.use(json());
app.use(authChecker);
app.use(stylus.middleware({
src: __dirname + '/public',
compile: compile,
keepExtensions: true,
uploadDir: __dirname + '/public/images'}));
app.use(express.static(path.join(__dirname, 'public')));
require('./routes-v1.js')(app);
require('./routes-v2.js')(app);
var hskey = fs.readFileSync('certificates/key.pem');
var hscert = fs.readFileSync('certificates/key-cert.pem');
var options = {
key: hskey,
cert: hscert
};
http.createServer(app).listen(4080);
console.log('Listening on port 4080...');
https.createServer(options, app).listen(4443);
console.log('Listening on port 4443...');
function authChecker(req, res, next) {
console.log("authChecker");
res.setHeader('Content-Type', 'application/json');
if (!Security.checkHMAC(req)) {
res.json({unauthorized:true});
} else {
next();
}
}
function compile(str, path) {
return stylus(str).set('filename', path).use(nib());
}
My problem is that the newly defined urls in NGINX doesn't work. The IP's work fine. What am I missing here? Is it something in NGINX or in node? My other subdomains through NGINX works flawlessly.

Express 4, NodeJS, AngularJS routing

I am using Express 4 to host my AngularJS app on my backend, with Nginx as my frontend server. However html5 mode does not seem to work, as I will get a Cannot /GET error when I try to enter the page link (e.g. http://localhost/login) via the browser. Is there any routing configuration I need to do for my Express/Nginx? Here's my config code:
Express 4:
var express = require('express'),
app = express(),
server = require('http').Server(app),
bodyParser = require('body-parser'),
db = require('./db'),
io = require('./sockets').listen(server),
apiRoutes = require('./routes/api'),
webRoutes = require('./routes/web');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: true
}));
app.use('/api', apiRoutes);
app.use(express.static(__dirname + '/public'));
server.listen(3000, function() {
console.log('Listening on port %d', server.address().port);
});
AngularJS:
'use strict';
var nodeApp = angular.module('nodeApp',['ngRoute']);
nodeApp.config(function($routeProvider, $locationProvider, $controllerProvider) {
$routeProvider.when('/', {
templateUrl: 'partials/home.html'
}).when('/login', {
templateUrl: 'partials/login.html'
});
$locationProvider.html5Mode(true);
nodeApp.controllerProvider = $controllerProvider;
});
Nginx:
# the IP(s) on which your server is running
upstream test-app {
server 127.0.0.1:3000;
}
# the nginx server instance
server {
listen 0.0.0.0:80;
server_name test-app.cloudapp.net;
access_log /var/log/nginx/test-app.log;
# pass the request to the nodejs server with correct headers
location / {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Nginx-Proxy true;
proxy_pass http://test-app/;
proxy_redirect off;
}
}
I'm assuming you are using a "single page" angular app, so one html page that uses ng-view to load all the other partials.
In this case you need to do something like this:
Express 4:
var express = require('express'),
app = express(),
server = require('http').Server(app),
bodyParser = require('body-parser'),
db = require('./db'),
io = require('./sockets').listen(server),
apiRoutes = require('./routes/api'),
webRoutes = require('./routes/web');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: true
}));
app.use('/api', apiRoutes);
app.use(express.static(__dirname + '/public'));
// Here's the new code:
app.use('/*', function(req, res){
res.sendfile(__dirname + '/public/index.html');
});
server.listen(3000, function() {
console.log('Listening on port %d', server.address().port);
});
The problem you're facing is that even though you have routes setup for '/login' before the routes are fired they need to be loaded. So the server tries to find a match for the route '/login' which it can't returning the 404. In the case of single page angular apps all the routes you use in routing must be caught by a route, app.get('/*', ... in this case, and then return the main angular.js html page. Note that this is the last call so it will be evaluated last, if you put it first it will prevent all the subsequent rules from running as express just runs the handler for the first rule it encounters.

Resources