web-dev-qa-db-fra.com

Serveur proxy HTTPS dans node.js

Je développe une application de serveur proxy node.js Et je veux qu'elle prenne en charge les protocoles HTTP et HTTPS(SSL) (en tant que serveur).

J'utilise actuellement node-http-proxy comme ceci:

const httpProxy = require('http-proxy'),
      http = require('http');

var server = httpProxy.createServer(9000, 'localhost', function(req, res, proxy) {
    console.log(req.url);
    proxy.proxyRequest(req, res);
});

http.createServer(function(req, res) {
    res.end('hello!');
}).listen(9000);

server.listen(8000);

J'ai configuré mon navigateur pour utiliser HTTP proxy sur localhost:8000 Et cela fonctionne. Je veux également intercepter HTTPS requêtes (c'est-à-dire configurer mon navigateur pour utiliser également localhost:8000 Comme HTTPS proxy et intercepter les requêtes dans mon application). Pourriez-vous s'il vous plaît m'aider comment puis-je faire cela?

[~ # ~] ps [~ # ~] :

Si je m'abonne à upgrade événement de httpProxy objet serveur, je peux obtenir les demandes mais je ne sais pas comment transmettre la demande et envoyer la réponse au client:

server.on('upgrade', function(req, socket, head) {
    console.log(req.url);
    // I don't know how to forward the request and send the response to client
});

Toute aide serait appréciée.

31
user1051478

Les solutions existent à peine pour cela, et la documentation est au mieux pauvre pour prendre en charge les deux sur un serveur. L'astuce consiste à comprendre que les configurations de proxy client peuvent envoyer des requêtes https à un serveur proxy http. Cela est vrai pour Firefox si vous spécifiez un proxy HTTP, puis cochez "Identique pour tous les protocoles".

Vous pouvez gérer les connexions https envoyées à un serveur HTTP en écoutant l'événement "connect". Notez que vous n'aurez pas accès à l'objet de réponse sur l'événement connect, uniquement au socket et au bodyhead. Les données envoyées via ce socket resteront cryptées pour vous en tant que serveur proxy.

Dans cette solution, vous n'avez pas à créer vos propres certificats et vous n'aurez donc pas de conflits de certificats. Le trafic est simplement mandaté, pas intercepté et réécrit avec différents certificats.

//  Install npm dependencies first
//  npm init
//  npm install --save [email protected]
//  npm install --save [email protected]

var httpProxy = require("http-proxy");
var http = require("http");
var url = require("url");
var net = require('net');

var server = http.createServer(function (req, res) {
  var urlObj = url.parse(req.url);
  var target = urlObj.protocol + "//" + urlObj.Host;

  console.log("Proxy HTTP request for:", target);

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

  proxy.web(req, res, {target: target});
}).listen(8080);  //this is the port your clients will connect to

var regex_hostport = /^([^:]+)(:([0-9]+))?$/;

var getHostPortFromString = function (hostString, defaultPort) {
  var Host = hostString;
  var port = defaultPort;

  var result = regex_hostport.exec(hostString);
  if (result != null) {
    Host = result[1];
    if (result[2] != null) {
      port = result[3];
    }
  }

  return ( [Host, port] );
};

server.addListener('connect', function (req, socket, bodyhead) {
  var hostPort = getHostPortFromString(req.url, 443);
  var hostDomain = hostPort[0];
  var port = parseInt(hostPort[1]);
  console.log("Proxying HTTPS request for:", hostDomain, port);

  var proxySocket = new net.Socket();
  proxySocket.connect(port, hostDomain, function () {
      proxySocket.write(bodyhead);
      socket.write("HTTP/" + req.httpVersion + " 200 Connection established\r\n\r\n");
    }
  );

  proxySocket.on('data', function (chunk) {
    socket.write(chunk);
  });

  proxySocket.on('end', function () {
    socket.end();
  });

  proxySocket.on('error', function () {
    socket.write("HTTP/" + req.httpVersion + " 500 Connection error\r\n\r\n");
    socket.end();
  });

  socket.on('data', function (chunk) {
    proxySocket.write(chunk);
  });

  socket.on('end', function () {
    proxySocket.end();
  });

  socket.on('error', function () {
    proxySocket.end();
  });

});
31
y3sh

Voici ma solution sans dépendances (bibliothèques système NodeJS pures):

const http = require('http')
const port = process.env.PORT || 9191
const net = require('net')
const url = require('url')

const requestHandler = (req, res) => { // discard all request to proxy server except HTTP/1.1 CONNECT method
  res.writeHead(405, {'Content-Type': 'text/plain'})
  res.end('Method not allowed')
}

const server = http.createServer(requestHandler)

const listener = server.listen(port, (err) => {
  if (err) {
    return console.error(err)
  }
  const info = listener.address()
  console.log(`Server is listening on address ${info.address} port ${info.port}`)
})

server.on('connect', (req, clientSocket, head) => { // listen only for HTTP/1.1 CONNECT method
  console.log(clientSocket.remoteAddress, clientSocket.remotePort, req.method, req.url)
  if (!req.headers['proxy-authorization']) { // here you can add check for any username/password, I just check that this header must exist!
    clientSocket.write([
      'HTTP/1.1 407 Proxy Authentication Required',
      'Proxy-Authenticate: Basic realm="proxy"',
      'Proxy-Connection: close',
    ].join('\r\n'))
    clientSocket.end('\r\n\r\n')  // empty body
    return
  }
  const {port, hostname} = url.parse(`//${req.url}`, false, true) // extract destination Host and port from CONNECT request
  if (hostname && port) {
    const serverErrorHandler = (err) => {
      console.error(err.message)
      if (clientSocket) {
        clientSocket.end(`HTTP/1.1 500 ${err.message}\r\n`)
      }
    }
    const serverEndHandler = () => {
      if (clientSocket) {
        clientSocket.end(`HTTP/1.1 500 External Server End\r\n`)
      }
    }
    const serverSocket = net.connect(port, hostname) // connect to destination Host and port
    const clientErrorHandler = (err) => {
      console.error(err.message)
      if (serverSocket) {
        serverSocket.end()
      }
    }
    const clientEndHandler = () => {
      if (serverSocket) {
        serverSocket.end()
      }
    }
    clientSocket.on('error', clientErrorHandler)
    clientSocket.on('end', clientEndHandler)
    serverSocket.on('error', serverErrorHandler)
    serverSocket.on('end', serverEndHandler)
    serverSocket.on('connect', () => {
      clientSocket.write([
        'HTTP/1.1 200 Connection Established',
        'Proxy-agent: Node-VPN',
      ].join('\r\n'))
      clientSocket.write('\r\n\r\n') // empty body
      // "blindly" (for performance) pipe client socket and destination socket between each other
      serverSocket.pipe(clientSocket, {end: false})
      clientSocket.pipe(serverSocket, {end: false})
    })
  } else {
    clientSocket.end('HTTP/1.1 400 Bad Request\r\n')
    clientSocket.destroy()
  }
})

J'ai testé ce code avec les paramètres du proxy Firefox (il demande même un nom d'utilisateur et un mot de passe!). J'ai entré l'adresse IP de la machine où ce code est exécuté et le port 9191 comme vous pouvez le voir dans le code. J'ai également défini "Utiliser ce serveur proxy pour tous les protocoles". J'exécute ce code localement et sur VPS - dans les deux cas, ça marche!

Vous pouvez tester votre proxy NodeJS avec curl:

curl -x http://username:[email protected]:9191 https://www.google.com/
10
Alexey Volodko

J'ai créé un proxy http/https à l'aide du module http-proxy : https://Gist.github.com/ncthis/6863947

Code dès maintenant:

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

var isHttps = true; // do you want a https proxy?

var options = {
  https: {
    key: fs.readFileSync('key.pem'),
    cert: fs.readFileSync('key-cert.pem')
  }
};

// this is the target server
var proxy = new httpProxy.HttpProxy({
  target: {
    Host: '127.0.0.1',
    port: 8080
  }
});

if (isHttps)
  https.createServer(options.https, function(req, res) {
    console.log('Proxying https request at %s', new Date());
    proxy.proxyRequest(req, res);
  }).listen(443, function(err) {
    if (err)
      console.log('Error serving https proxy request: %s', req);

    console.log('Created https proxy. Forwarding requests from %s to %s:%s', '443', proxy.target.Host, proxy.target.port);
  });
else
  http.createServer(options.https, function(req, res) {
    console.log('Proxying http request at %s', new Date());
    console.log(req);
    proxy.proxyRequest(req, res);
  }).listen(80, function(err) {
    if (err)
      console.log('Error serving http proxy request: %s', req);

    console.log('Created http proxy. Forwarding requests from %s to %s:%s', '80', proxy.target.Host, proxy.target.port);
  });
8
ncabral

Les documents node-http-proxy contiennent des exemples de cela. Recherchez "Proxying to HTTPS from HTTPS" sur https://github.com/nodejitsu/node-http-proxy Le processus de configuration est légèrement différent dans chaque navigateur. Certains ont la possibilité d'utiliser vos paramètres de proxy pour tous les protocoles; certains dont vous avez besoin pour configurer le proxy SSL séparément.

2
Michael Brundage