Node.js+express proxy ssl - node.js

I'm trying to write a reverse proxy in node.js using express, and it works fine for http requests. The problem is that when requesting https it never responds, and the browser states that the proxy refused to connect.
Here is the working code for http requests:
var app = express(),
http=require('http');
app.configure(function(){ /* express stuff to log and use routes and the like */ });
http.createServer(app).listen(8000, function(){
console.log("Express server listening on port " + 8000);
});
app.all('*', proxy);
var request=require('request');
var proxy=function(req,resp){
var data={
url:req.url,
headers: {
'Connection': 'keep-alive'
}
}
var proxy=request(req.url);
proxy.pipe(resp);
}
Now, as for SSL, i am currently trying with:
var https=require('https'),
fs=require('fs');
https.createServer({
key: fs.readFileSync(__dirname+'/ssl/server.key', 'utf8'),
cert: fs.readFileSync(__dirname+'/ssl/server.crt', 'utf8')
},app).listen(8001, function(){
console.log("Express server listening on port " + 8001);
});
The proxy can be used from anywhere requiring 50.56.195.215:8000 for HTTP and 50.56.195.215:8001 for SSL. It has no security whasoever, so don't log in to anything important =D
I'm using a self signed SSL Certificate, and i guess it's kind of silly of me to try to do such a thing, but i don't have any ideas left :P

My suggestion is use the great existing library node-http-proxy from Nodejitsu. If you want to write your own, at least study their source code academically.
Some notes on your approach above:
You aren't handling HTTP methods other than GET (POST, PUT, DELETE, etc). These exist. You must handle them if you want your proxy to actually work. Every time you call request(req.url), request is making a GET request by default.

For HTTPS, you need to be able to handle HTTP Connects and also impersonate the destination server. You will need to have a Certificate for this.
You can try using this.
https://github.com/noeltimothy/noelsproxy
Copy the directory "magical" that contains a certificate as well as a key and then use noelsproxy. Remember to add the ca.pem to your trusted root store on your system.
If you are using windows, do this:
certutil -addstore -enterprise -f \"Root\" ./magical/ca.pem
Let me know if you have any issues. I'm willing to fix them immediately.

Related

NodeJS Secure Websockets will not attach to https

I'm going insane trying to get a super basic wss:// functioning in NodeJS for the last 2 days. I've tried quite a few methods and libraries but I can't seem to get the websocket server attached to an https instance. I have no problem leveraging regular old http and attaching it to that instance. I don't get any errors in my debug console.
I've created both self-style type certs (Create Key + CA, create CSR,
sign it, use new server cert), and (Create Key + self-signed Cert,
use them).
I've tried disabling TLS verification via env var:
NODE_TLS_REJECT_UNAUTHORIZED="0"
I've tried both ws, and websocket libraries and many different combos
of basic ws creation vs server attaching methods.
I've built a VM of Ubuntu 21.04, installed dependencies and vscode
just to rule out my OS. Same issue here.
Tried using node versions 14 + 16.
:Package Deps:
"websocket": "^1.0.34",
"ws": "^8.0.0"
:server.js:
const fs = require('fs');
const WebSocket = require('ws');
//HTTPS
const https = require('https');
const server = new https.createServer({
key: fs.readFileSync('./config/certs/key.pem'),
cert: fs.readFileSync('./config/certs/cert.pem')
});
//HTTP
// const http = require('http');
// const server = new http.createServer();
const wss = new WebSocket.Server({server});
wss.on('connection', function connection(ws) {
ws.on('message', function incoming(message) {
console.log('received: %s', message);
ws.send('hello from server!, the time is: ' + timestamp());
});
});
//Helper function to create a timestamp
function timestamp() {
return (new Date)
.toISOString()
.replace(/z|t/gi, ' ')
.trim()
};
//Start the server
server.listen(3000);
I'm suspecting some underlying compatibility issues between node and dependencies or something...Any advice would be much appreciated. I'm not too familiar with debugging internal modules so if there are some command line switches I should add to node/nodemon please let me know. I have --inspect and --trace-warnings enabled at the very least.
I just figured it out and as usual it was something simple and overlooked. I've been using Firefox with the Weasel client add-on to test websockets. I had imported my self-signed cert along with the root CA cert I had created into Firefox. Even though it was imported, I still had to navigate to the HTTPS url and acknowledge the wonderful yellow border popup. As soon as I clicked on "Accept risk and continue" I tabbed over to Weasel and it established a connection to wss://localhost:3000 with no problems.
Even though the cert is whitelisted I still receive the warning page and have to acknowledge it. Next time I'll try a different client like one built in another language (Python, .NET...). Never would have thought it to be a browser issue but it makes sense with the way ssl/tls works.

Added SSL to client side server but still not performing handshake with backend server nodejs

I am trying to implement SSL on my nodejs project. Currently, my servers are split between a client side server running on localhost port 443 and a backend server running on localhost port 5000. I have already added a self-signed SSL certificate by openSSL to my client side server as shown below.
Now here's my issue. When I send a post request to login, from what I understand, a handshake is suppose to happen between the server and the client to make a secure connection. However, that's not the case. When I used Wireshark the intercept the packets, there is no handshake happening in the process.
I am currently not sure on how to proceed because I have limited knowledge on this kind of security topics. Do I need to sign a new key and cert and add it to my backend server? Or am I doing everything wrong? If so, can I get a source or guide on how to properly create one for a nodejs server?
you have many options here for securing your backend server :
first, you can use Nginx reverse proxy server and you can add ssl/tls logic to it. nginx will handle this stuff for you.
second, you can use [https][1] package directly and pass your SSL certificate and key to it :
const https = require('https');
const fs = require('fs');
const options = {
key: fs.readFileSync('test/fixtures/keys/agent2-key.pem'),
cert: fs.readFileSync('test/fixtures/keys/agent2-cert.pem')
};
https.createServer(options, (req, res) => {
res.writeHead(200);
res.end('hello world\n');
}).listen(8000);
remember that the domain name your are trying to access must be set in your host ip.
[1]: https://nodejs.org/api/https.html

Creating A HTTP proxy server with https support and use another proxy server to serve the response using nodejs

I need help creating a proxy server using node js to use with firefox.
the end goal is to create a proxy server that will tunnel the traffic through another proxy server (HTTP/SOCKS) and return the response back to firefox. like this
I wanna keep the original response received from the proxy server and also wanna support https websites as well.
Here is the code I came up with.
var http = require('http');
var request = require("request");
http.createServer(function(req, res){
const resu = request(req.url, {
// I wanna Fetch the proxy From database and use it here
proxy: "<Proxy URL>"
})
req.pipe(resu);
resu.pipe(res);
}).listen(8080);
But it has 2 problems.
It does not support https requests.
It also does not supports SOCKS 4/5 proxies.
EDIT: I tried to create a proxy server using this module. https://github.com/http-party/node-http-proxy
but the problem is we cannot specify any external proxy server to send connections through.
I have found a really super simple solution to the problem. We can just forward all packets as it is to the proxy server. and still can handle the server logic with ease.
var net = require('net');
const server = net.createServer()
server.on('connection', function(socket){
var laddr = socket.remoteAddress;
console.log(laddr)
var to = net.createConnection({
host: "<Proxy IP>",
port: <Proxy Port>
});
socket.pipe(to);
to.pipe(socket);
});
server.listen(3000, "0.0.0.0");
You have to use some middleware like http-proxy module.
Documentation here: https://www.npmjs.com/package/http-proxy
Install it using npm install node-http-proxy
This might help too: How to create a simple http proxy in node.js?

how do i correctly proxy https requests to another https server for cordova

I have a number of different servers running on my system, all of them running a secure connection on there own port, etc. 50001,50002,50003...
all of thees can be accessed directly from https://domain1.com:50001 ...
now, not only do I want to limit the number of ports, but also change the domain so etc.
https://domain1.com:50001 <- https://srv1.domain2.com:443
https://domain1.com:50002 <- https://srv2.domain2.com:443
https://domain1.com:50003 <- https://srv3.domain2.com:443
All of thees servers run separate nodejs instances.
Now I want to build a proxy than redirect this, and I have chosen nodejs since everything else we do is in nodejs.
what i have now:
var app = require('express')();
var options = {
key : fs.readFileSync(CONFIG.sslKey).toString(),
cert : fs.readFileSync(CONFIG.sslCertificate).toString(),
ca : fs.readFileSync(CONFIG.sslCA).toString()
};
var http = require('https').Server(options,app);
var httpProxy = require('http-proxy');
var proxy = httpProxy.createProxyServer({
ssl: {
key : fs.readFileSync(CONFIGsecure.sslKey).toString(),
cert : fs.readFileSync(CONFIGsecure.sslCertificate).toString(),
ca : fs.readFileSync(CONFIGsecure.sslCA).toString()
},
secure: true
});
var handleRequests = function(req, res){
proxyTo = "https://domain1.com:50001"; <= some logic chooses this based on req.headers.host
proxy.web(req, res, { target: proxyTo });
};
app.get('/*', handleRequests );
app.post('/*', handleRequests );
app.put('/*', handleRequests );
app.delete('/*', handleRequests );
http.listen(443, function(){});
okay so this actually works very well, everything is going where it should go in a browser, and in a cordova app using jquery ajax everything also works very well.
however if i use
FileTransfer().download(...)
I get error code 3 (connection error).
If I connect directly to https://domain1.com:50001 (direct) the app works, but if i connect to https://srv1.domain2.com:443 (the proxy) the app does not work.
All the certificates are valid, wildcard certificate on *.domain2.com and single certificate on domain1.com.
The end servers has domain1.com certificate installed and the proxy has *.domain2.com wildcard certificate installed.
Any idea on how to correctly setup a proxy server? The system is windows server 2012 R2 and I am open to use a real proxy if needed. However it would be nice with a solution as simple as possible.
I have tried example two form here:
http://blog.nodejitsu.com/http-proxy-intro/
however this is the same problem, and it is only GET requests.
I have also tried disabling https on the end server so thats it's only the proxy that is secure, however, same result...
Thanks...
Okay so i found the issue, for some reason req.headers.host string also contained the :port, and i was only switching on the address. now everything works perfekt.

HTTPS proxy with a self-signed certificate in Node.JS

I'm trying to create a HTTPS proxy server in Node.JS v0.10.24 using a self-signed certificate. Here's the code I'm using:
var https = require('https');
var server = https.createServer({
key: fs.readFileSync('key.pem'),
cert: fs.readFileSync('cert.pem')
});
server.on('request', function(req, res) {
res.end('hello');
});
server.listen(8080);
This server correctly boots up and is accessible via https://localhost:8080. However, when I set it as a HTTPS proxy (on Mac OS X), the server emits connection events but never emits either request or error, thus causing the connection to hang indefinitely and eventually time out.
I encountered the same issue on my Macbook. The issue appears to be that the proxy server option in OSX is using the HTTP CONNECT method to tunnel HTTPS requests.
In short, this means that you need make your server a http.Server instance and handle the connect event, which will involve forwarding TCP socket traffic.
I know this reply is a bit late, but I wrote my own HTTP/S proxy server that you can look at for reference: https://github.com/robu3/purokishi. The specific section covering the connect method is here.

Resources