How to get SSL to play nice with non-ssl protocol data - node.js

Short background: If we go back in time to about 2006-ish: We (ie: my company) used a java client app embedded in the browser that connected via port 443 to a C program backend running on port 8068 on an in-house server. At the time when the java app was first developed, port 443 was the only port that we knew would not be blocked by our customers that used the software (ease of installation and possibly the customer in-house staff didn't have the power or knowledge to control their internal firewall).
Fast-forward to 2016, and I'm hired to help develop a NodeJS/Javascript version of that Java app. The Java app continues to be used during development of its replacement, but whoops - we learn that browsers will drop support for embedded Java in the near future. So we switch to Java Web Start, so that the customers can continue to download the app and it still connects to the in house server with it's port 443->8068 routing.
2017 rolls around and don't you know, we can't use the up-coming JS web-app with HTTPS/SSL and the Java app at the same time, 'cause they use the same port. "Ok let's use NGINX to solve the problem." But due to in house politics, customer needs, and a turn-over of web-developer staff, we never get around to truly making that work.
So here we are at 2020, ready to deploy the new web version of the client software, and the whole 443 mess rears it's ugly head again.
Essentially I am looking to allow (for the time being) the Java app to continue using 443, but now need to let the web app use HTTPS too. Back in 2017/2018 we Googled ways to let them cohabitate through NGINX, but we never really got them to work properly, or the examples and tutorials were incomplete or confusing. It seemed like we needed to either use streaming along the lines of https://www.nginx.com/blog/running-non-ssl-protocols-over-ssl-port-nginx-1-15-2/ , or look at the incoming HTTPS header and do an 'if (https) { route to nodeJS server } else { assume it must be the java app and route to port 8068 }' -sort of arrangement inside the NGINX config file.
Past Googled links appear to not exist anymore, so if anyone knows of an NGINX configuration that allows an HTTPS website to hand off to a non-SSL application that still needs to use 443, I would greatly appreciate it. And any docs and/or tutorials that point us in the right direction would be helpful too. Thanks in advance!

You can do this using ssl_preread option. Basically, this option will allow access to the variable $ssl_preread_protocol, that contains the protocol negotiated at SSL port. If no valid protocol was detected, the variable will be empty.
Using this parameters, you could use the follow configuration to your environment:
stream {
upstream java {
server __your_java_server_ip__:8068;
}
upstream nodejs {
server __your_node_js_server_ip__:443;
}
map $ssl_preread_protocol $upstream {
default java;
"TLSv1.2" nodejs;
}
server {
listen 443;
proxy_pass $upstream;
ssl_preread on;
}
}
In your case, this configuration will pass the connection directly to your nodejs and java backend servers, so, nodejs will need to negotiate the SSL. You can pass this work to NGiNX using another server context, like:
stream {
upstream java {
server __your_java_server_ip__:8068;
}
upstream nodejs {
server 127.0.0.1:444;
}
map $ssl_preread_protocol $upstream {
default java;
"TLSv1.2" nodejs;
}
server {
listen 443;
proxy_pass $upstream;
ssl_preread on;
}
}
http {
server {
listen 444 ssl;
__your_ssl_cert_configurations_here__
location / {
proxy_pass http://__your_nodejs_server_ip__:80;
}
}
}
You'll need NGiNX at least version 1.15.2 to this configuration to work, and compiled with ngx_stream_ssl_preread_module module (need to compile with --with-stream_ssl_preread_module configuration parameter, because this module is not built by default).
Source: https://www.nginx.com/blog/running-non-ssl-protocols-over-ssl-port-nginx-1-15-2/

Related

NGINX Reverse Proxy microservices built with cherrypy

I'm building a smart greenhouse for a university project and we must follow a microservices architecture and use cherrypy. My proposed solution is having different microservices handling the different telemetrics using nginx, docker and docker-compose.
I'm using nginx for reverse proxy my front-end and all the microservices, but how do I handle the uri? Can nginx handle URIs with <ids>?
Cherrypy doesn't seen to provide horizontal scalling, the RESTful-style dispatcher provided seems like just provides monolithic approach.
My current NGINX:
server {
listen 80;
location / {
proxy_pass http://web:80;
}
location /api/v1/moisture {
proxy_pass http://moisture:5001;
}
location /api/v1/light {
proxy_pass http://moisture:5001;
}
}
My API should look something like this /api/v1/greenhouse/<id>/moisture, were moisture can be every telemetric I can measure like humidity or light.
The objective was that nginx could send a request to /api/v1/greenhouse/<id>/moisture to the moisture service and a request to /api/v1/greenhouse/<id>/humidity the humidity service, since cherrypy does not provide a solution.

Can I run multiple loopback.io apps on same port?

Referring to the following question:
Running multiple Node (Express) apps on same port
Can I run multiple apps (backend, api rest) on same port, if I am using strongloop loopback to generates my Node app?
Generally what you will be doing is running multiple instances of your app on different ports and have some sort of load balancer in front switching among the instances and thus exposing it as one port.
Assuming you've started 3 instances on ports 3001, 3002, and 3003, you can do it in nginx like this:
http {
upstream myloopbackapp {
server localhost:3001;
server localhost:3002;
server localhost:3003;
}
server {
listen 80;
location / {
proxy_pass http://myloopbackapp;
}
}
}
Further reading: http://nginx.org/en/docs/http/load_balancing.html
There are equally easy ways to do this in Apache and IIS as well.

Websocket server running fine but cannot connect from client (what url should I use?)

OK this is very simple to anyone who's used websocket and nodejs.
I have created a websocket server named ws_server.js and put it in C:\Program Files (x86)\nodejs where I have installed the nodejs framework. I started the server and it is running and it says it's listening on port 8080. So far so good, I have the server running.
Now I simply want to connect to it from client code so that I can do all that lovely stuff about capturing events using event listeners etc. The problem is, embarassingly, I cannot figure out what URL to use to connect to my websocket server.
function init() {
testWebSocket();
}
function testWebSocket() {
websocket = new WebSocket("ws://localhost:8080/"); // WHAT URL SHOULD BE USED HERE?
websocket.onopen = function(evt) { alert("OPEN") };
websocket.onclose = function(evt) { alert("CLOSE") };
websocket.onmessage = function(evt) { alert("MESSAGE") };
websocket.onerror = function(evt) { alert("ERROR") };
}
function doSend(message) {
// this would be called by user pressing a button somewhere
websocket.send(message);
alert("SENT");
}
window.addEventListener("load", init, false);
When I use ws://localhost:8080 the only events that trigger are CLOSE and ERROR. I cannot get the client to connect. I must be missing something very simple. Do I need to set up my nodejs folder in IIS for example and then use that as the URL?
Just to reiterate, the websocket server is running fine, I just don't know what URL to use to connect to it from the client.
EDIT: The websocket server reports the following error.
Specified protocol was not requested by the client.
I think I have got it working by doing the following.
var websocket = new WebSocket("ws://localhost:8080/","echo-protocol");
The problem being that I needed to specify a protocol. At least now I get the onopen event. ...if nothing much else
I was seeing the same error, the entire web server goes down. Adding the protocol fixes it but leaves me wondering why it was implemented this way. I mean, one bad request should not bring down your server.
You definitely have to encase it a try/catch, but the example code provided here https://www.npmjs.com/package/websocket (2019-08-07) does not. This issue can be easily avoided.
I just wanted to share a crazy issue that I had. I was able to connect to a websocket of an old version of a 3rd party app in one computer, but not to a newer version of the app in another.
Moreever, even in new computer with the new version of the app, The app was able to connect to the websocket, but no matter what I did, when I tried to connect with my own code, I kept getting the error message that the websocket connection failed
Long story short, They changed an apache configuration that allowed connecting to the websocket via a proxy.
In the old version, apache config was:
ProxyPass /socket/ ws://localhost:33015/ retry=10
ProxyPass /socket ws://localhost:33015/ retry=10
In the new version, apache config was changed to:
ProxyPass /socket/ ws://localhost:33015/ retry=10
By bad luck, I was trying to connect to ws://localhost/socket and not to ws://localhost/socket/. As a result, proxy was not found, and connection returned an error.
Moral of the story: Make sure that you are trying to connect to a websocket url that exists.
For me, the solution was to change the URL from ws:// to wss://. This is because the server I was connecting to had updated its security, and now only accepted wss.

How to run multiple StrongLoop LoopBack apps on the same server?

I'm currently running two StrongLoop LoopBack apps (Nodejs apps) on a single server with different ports. Both apps were created using slc lb project and slc lb model from the command line.
Is it possible to run these apps on a single ports with different path and/or subdomain? If it is, how do I do that on a Linux machine?
Example:
http://api.server.com:3000/app1/ for first app.
http://api.server.com:3000/app2/ for second app.
thanks.
Since LoopBack applications are regular Express applications, you can mount them on a path of the master app.
var app1 = require('path/to/app1');
var app2 = require('path/to/app2');
var root = loopback(); // or express();
root.use('/app1', app1);
root.use('/app2', app2);
root.listen(3000);
The obvious drawback is high runtime coupling between app1 and app2 - whenever you are upgrading either of them, you have to restart the whole server (i.e. both of them). Also a fatal failure in one app brings down the whole server.
The solution presented by #fiskeben is more robust, since each app is isolated.
On the other hand, my solution is probably easier to manage (you have only one Node process instead of nginx + per-app Node processes) and also allows you to configure middleware shared by both apps.
var root = loopback();
root.use(express.logger());
// etc.
root.use('/app1', app1);
root.use('/app2', app2);
root.listen(3000);
You would need some sort of proxy in front of your server, for example nginx. nginx will listen to a port (say, 80) and redirect incoming requests to other servers on the machine based on some rules you define (hostname, path, headers, etc).
I'm no expert on nginx but I would configure it something like this:
server {
listen: 80;
server_name api.server.com;
location /app1 {
proxy_pass http://localhost:3000
}
location /app2 {
proxy_pass http://localhost:3001
}
}
nginx also supports passing query strings, paths and everything else, but I'll leave it up to you to put the pieces together :)
Look at the proxy server documentation for nginx.

Running peerjs-server on OpenShift returns 503 Service Unavailable

I have set up up a node.js 0.10 gear in OpenShift which I deployed a simple server which is based off peerjs-server. All I want this server to do is act as a signalling server to communicate the connection info between peers connected to my application and from then on they communicate peer-to-peer using WebRTC. Everything works when pointing to the demo "PeerJS Cloud" signalling server but when trying to use my own server set up I keep getting returned 503 status codes.
Here is the server creation code I use:
var host = process.env.OPENSHIFT_NODEJS_IP;
var port = process.env.OPENSHIFT_NODEJS_PORT || 8080;
var server = new PeerServer({ port: port, host: host});
NB: I have added host to peerjs-server so I can use OpenShift's IP, not sure if this was necessary but it wasn't working without this either.
The server peerjs-server uses is restify. Here is the server create and listen code:
this._app = restify.createServer(this._options.ssl);
/* A lot of set up code that I have not changed from peerjs-server */
this._app.listen(this._options.port, this._options.host);
Where this._options.port and this._options.host are the ones defined in the previous code segment and I am not using SSL so nothing is being passed in there.
When deploying this code to OpenShift I get no errors but when accessing the site on port 80 or 8000 (the open external ports) I get 503's. I also checked rhc tail and this is what I get:
Screenshot (Can't post images because I have no reputation..). Not sure exactly what that means if anything.
Any help is much appreciated, and if more info is needed I can add more, was not sure what was important information or not.
UPDATE: It's a scaled application using 1-3 small gears.
from https://github.com/peers/peerjs-server/blob/master/lib/server.js:
// Listen on user-specified port and IP address.
if (this._options.ip) {
this._app.listen(this._options.port, this._options.ip);
} else {
this._app.listen(this._options.port);
}
So, use 'ip' and not 'host'. Worked for me.

Resources