Skip to content

Instantly share code, notes, and snippets.

@toolness
Last active January 23, 2022 12:39
Show Gist options
  • Save toolness/10485006 to your computer and use it in GitHub Desktop.
Save toolness/10485006 to your computer and use it in GitHub Desktop.
HTTPS proxy server with SNI
var fs = require('fs');
var crypto = require('crypto');
var http = require('http');
var https = require('https');
var async = require('async');
var httpProxy = require('http-proxy');
var _ = require('underscore');
var UID = 1000;
var PASSPHRASE = process.env.PASSPHRASE || null;
var PROXIES = require('./proxies.json');
var DEFAULT_HOSTNAME = Object.keys(PROXIES)[0];
var credentials = {};
function securityOptions(hostname) {
var basePath = __dirname + '/certs/' + hostname;
var options = {
key: fs.readFileSync(basePath + '/key.pem'),
cert: fs.readFileSync(basePath + '/cert.pem'),
passphrase: PASSPHRASE
};
var caPath = basePath + '/ca.pem';
if (fs.existsSync(caPath))
options.ca = [fs.readFileSync(caPath)];
return options;
}
Object.keys(PROXIES).forEach(function(hostname) {
console.log('loading credentials for ' + hostname);
credentials[hostname] = crypto.createCredentials(securityOptions(hostname));
});
var proxy = httpProxy.createProxy();
console.log('loading credentials for default hostname ' + DEFAULT_HOSTNAME);
var server = https.createServer(_.extend(securityOptions(DEFAULT_HOSTNAME), {
SNICallback: function(servername) {
if (!(servername in credentials))
servername = DEFAULT_HOSTNAME;
return credentials[servername].context;
}
}), function(req, res) {
var host = req.headers['host'];
if (!(host in PROXIES))
host = DEFAULT_HOSTNAME;
return proxy.web(req, res, {target: PROXIES[host]}, function(e) {
try {
res.writeHead(502, {'Content-Type': 'text/plain'});
res.end('Error proxying request.');
} catch (e) {
res.end();
}
});
});
var redirectServer = http.createServer(function(req, res) {
res.writeHead(301, {
'Location': 'https://' + req.headers['host'] + req.url
});
res.end();
});
async.parallel([
server.listen.bind(server, 443),
redirectServer.listen.bind(redirectServer, 80)
], function(err) {
if (err) throw err;
process.setuid(UID);
console.log('listening on ports 80 and 443 as uid ' + UID);
});
@toolness
Copy link
Author

Note that this should be used with at least node v0.10.21, or else the proxy may close connections before sending all data.

@giftofhealth
Copy link

(node:20745) [DEP0010] DeprecationWarning: crypto.createCredentials is deprecated. Use tls.createSecureContext instead.
events.js:183
throw er; // Unhandled 'error' event
^

Error: listen EACCES 0.0.0.0:443
at Object._errnoException (util.js:1022:11)
at _exceptionWithHostPort (util.js:1044:20)
at Server.setupListenHandle [as _listen2] (net.js:1350:19)
at listenInCluster (net.js:1408:12)
at Server.listen (net.js:1492:7)
at /usr/lib/nodejs/async.js:570:21
at /usr/lib/nodejs/async.js:249:17
at /usr/lib/nodejs/async.js:125:13
at Array.forEach ()
at _each (/usr/lib/nodejs/async.js:46:24)

~/proxy $ node -v
v8.11.1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment