简体   繁体   中英

creating a forward https proxy using http-node-proxy

I am trying to create a forward proxy capable of handling HTTPS websites as well. I am trying to observe and modify traffic for different sites. This is my code which works for http sites but not for https sites.

httpProxy.createServer(function(req, res, next) {
   //custom logic
   next();
}, function(req, res) {
   var proxy = new httpProxy.RoutingProxy();
   var buffer = httpProxy.buffer(req);
   var urlObj = url.parse(req.url);
   req.headers.host = urlObj.host;
   req.url = urlObj.path;
   console.log(urlObj.protocol);
  setTimeout(function() {
     proxy.proxyRequest(req, res, {
        host: urlObj.host,
        port: 80,
        buffer: buffer,
    }
   )}, 5000);

}).listen(9000, function() {
console.log("Waiting for requests...");
});

Thanks for your help guys!

There are https options which must be specified when handling the https traffic. Here is what I am doing in my proxy setup.

var fs = require('fs'),
    httpProxy = require('http-proxy');

var proxyTable = {};

proxyTable['domain.com'] = 'localhost:3001';
proxyTable['domain.com/path'] = 'localhost:3002';
proxyTable['sub.domain.com'] = 'localhost:3003';

var httpOptions = {
    router: proxyTable
};

var httpsOptions = {
    router: proxyTable,
    https: {
        passphrase: 'xxxxxxx',
        key: fs.readFileSync('/path/to/key'),
        ca: fs.readFileSync('/path/to/ca'),
        cert: fs.readFileSync('/path/to/crt')}
};

httpProxy.createServer(httpOptions).listen(80);
httpProxy.createServer(httpsOptions).listen(443);

The documentation for https is on their github page as well.

https://github.com/nodejitsu/node-http-proxy

If you're just doing a forward proxy there's a few things you'll have to take into account.

  • A regular request is NOT triggered on a proxy for a HTTPS request - instead you'll see a HTTP CONNECT.

Here's the sequence flow you'll need to handle.

  1. CONNECT event is sent from the browser to the proxy specified in the HTTPS section. You'll catch this here: http://nodejs.org/api/http.html#http_event_connect Note that this comes over the HTTP module, not the HTTPS connection.
  2. You create a new socket connection to the requested domain (or your mapped domain). [srvSocket]
  3. You'll respond back to the CONNECT socket with a 200
  4. You'll write the buffer you received with the CONNECT event to srvSocket, then pipe the two sockets together srvSocket.pipe(socket);
  5. socket.pipe(srvSocket);

Since you're trying to spoof the requested domain locally you'll need a few more things in place

  1. You'll need to generate a root CA.
  2. You will need to import this cert as a trusted authority to your OS
  3. You'll use this cert to create a new key/cert file for the domains you're trying to access
  4. Your mapped hosts will need to respond with the appropriate key/cert file generated in step 3 for EACH domain you are mapping.

https://github.com/substack/bouncy

var bouncy = require('bouncy');

var server = bouncy(function (req, res, bounce) {
    if (req.headers.host === 'beep.example.com') {
    bounce(8001);
    }
    else if (req.headers.host === 'boop.example.com') {
        bounce(8002);
    }
    else {
        res.statusCode = 404;
        res.end('no such host');
    }
});
server.listen(8000);

If you specify opts.key and opts.cert, the connection will be set to secure mode using tls. Do this if you want to make an https router.

We can have a middleware as below

request = require("request"); app.use(function (req, res, next) { request(' http://anotherurl.that.serves/the/request ').pipe(res); });

See example https://github.com/manuks/proxy

Basically, underneath the http-proxy npm is some networking libraries Node uses (specifically http://nodejs.org/api/https.html and TLS). Even though my Apache was able to connect me just fine on a self-signed certificate w/o the proxy by accessing it in my browser:

https://localhost:8002    

You need to establish a certificate authority to get past the "unable to verify leaf signature" error in Node (I used the SSLCACertificateFile option). Then, you'll get hit with "self_signed_cert_in_chain". This led to some Google results indicating npm abandoned self-signed certificates, but I'm pretty sure this does not regard Node.

What you end up with are some people indicating you use process.env.NODE_TLS_REJECT_UNAUTHORIZED or rejectUnauthorized within your https agent. If you dig through the http-proxy souce, you'll find it accepts an agent option. Use this:

/**
 * Module dependencies
 */

// basic includes
express     = require('express');
fs          = require('fs');
http        = require('http');
https       = require('https');
httpProxy   = require('http-proxy');
require('child_process').spawn(__dirname+'/../../../dependencies/apache/bin/httpd.exe',['-f',__dirname+'/../../../dependencies/apache/conf/httpd-payments.conf']); 

var app     = module.exports = express();
app.set('port', process.env.PORT || 8001); // we sometimes change the port

// creates an output object for this particular request
//app.use(express.cookieParser(''));
//app.use(express.bodyParser());
//app.use(express.methodOverride());

proxy   = httpProxy.createProxyServer();
proxy.on('error', function (err, req, res) {
    console.log(err);
    res.send(500,err);
    res.end();
});

app.all('*',function(req,res,next) {
    var options = {
      hostname: '127.0.0.1',
      port: 8002,
      rejectUnauthorized: false,
      key: fs.readFileSync(__dirname+"/../../../deployment/server.key.pem"),
      cert: fs.readFileSync(__dirname+"/../../../deployment/server.crt.pem")
    };
    agent = new https.Agent(options);
    try {
        proxy.web(req,res, {
            target: "https://localhost:8002",
            proxyTimeout: 30,
            agent: agent
        });
    } catch(e) {
        // 500 error
        res.send(500,e);
    }
})

/** 
 * Start Server
 */

var options = {
  key: fs.readFileSync(__dirname+"/../../../deployment/server.key.pem"),
  cert: fs.readFileSync(__dirname+"/../../../deployment/server.crt.pem")
};
server  = https.createServer(options,app).listen(app.get('port'), function () {
  console.log('Running payments server on port ' + app.get('port'));
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM