2016-02-23 1 views
2

Je crée un simple proxy POP3 compatible STARTTLS dans Node.JS et je rencontre des difficultés.NodeJS STARTTLS Utiliser SNI

Le proxy sert de serveur frontal pour de nombreux serveurs principaux. Il doit donc charger ses certificats de manière dynamique, en fonction de la connexion du client. J'essaie d'utiliser le SNICallback, qui m'apporte le nom de serveur que le client utilise, mais je ne peux pas définir le bon certificat après cela, car j'ai besoin d'un certificat avant d'avoir cet appel, quand je crée le le contexte.

Le code est comme ci-dessous:

// Load libraries 
var net = require('net'); 
var tls = require('tls'); 
var fs = require('fs'); 

// Load certificates (created with openssl) 
var certs = []; 
for (var i = 1; i <= 8; i++) { 
    var hostName = 'localhost' + i; 
    certs[hostName] = { 
     key : fs.readFileSync('./private-key.pem'), 
     cert : fs.readFileSync('./public-cert' + i + '.pem'), 
    } 
} 

var server = net.createServer(function(socket) { 
    socket.write('+OK localhost POP3 Proxy Ready\r\n'); 

    socket.on('data', function(data) { 
     if (data == "STLS\r\n") { 
     socket.write("+OK begin TLS negotiation\r\n"); 
     upgradeSocket(socket); 
     } else if (data == "QUIT\r\n") { 
     socket.write("+OK Logging out.\r\n"); 
     socket.end(); 
     } else { 
     socket.write("-ERR unknown command.\r\n"); 
     } 
    }); 

}).listen(10110); 

et upgradeSocket() se présente comme suit:

function upgradeSocket(socket) { 
    // I need this 'details' or handshake will fail with a message: 
    // SSL routines:ssl3_get_client_hello:no shared cipher 
    var details = { 
     key : fs.readFileSync('./private-key.pem'), 
     cert : fs.readFileSync('./public-cert1.pem'), 
    } 

    var options = { 
     isServer : true, 
     server : server, 
     SNICallback : function(serverName) { 
     return tls.createSecureContext(certs[serverName]); 
     }, 
    } 

    sslcontext = tls.createSecureContext(details); 
    pair = tls.createSecurePair(sslcontext, true, false, false, options); 

    pair.encrypted.pipe(socket); 
    socket.pipe(pair.encrypted); 

    pair.fd = socket.fd; 
    pair.on("secure", function() { 
     console.log("TLS connection secured"); 
    }); 
} 

Il poignées de mains correctement, mais le certificat que j'utilise est celui statique dans 'détails', non celui que je reçois dans le SNICallback.

Pour le tester, je suis en cours d'exécution du serveur et en utilisant gnutls-cli comme client:

~$ gnutls-cli -V -s -p 10110 --crlf --insecure -d 5 localhost3 
STLS 
^D (Control+D) 

La commande ci-dessus est censé me obtenir le certificat « localhost3 », mais il devient le « localhost1 » parce que c'est défini dans 'details' var; Il y a juste trop d'exemples sur Internet avec HTTPS ou pour les clients TLS, ce qui est très différent de ce que j'ai ici, et même pour les serveurs, mais ils n'utilisent pas SNI. Toute aide serait appréciée.

Merci d'avance.

+0

Je viens de voir ce bug ouvert à Node.js github, je me demande si elle est, en fait, le problème: https://github.com/nodejs/node/issues/4878 –

Répondre

1

La réponse est assez simple en utilisant tls.TLSSocket, bien qu'il y ait un gotcha avec les écouteurs.

Vous devez supprimer tous les écouteurs de la net.Socket régulière que vous avez, instancier un nouveau tls.TLSSocket en utilisant votre net.Socket et remettre les écouteurs sur le tls.TLSSocket.

Pour y parvenir facilement, utilisez un emballage comme Haraka's tls_socket pluggableStream sur la net.Socket régulière et remplacer la « mise à niveau » fonction à quelque chose comme:

pluggableStream.prototype.upgrade = function(options) { 
    var self = this; 
    var socket = self; 
    var netSocket = self.targetsocket; 

    socket.clean(); 

    var secureContext = tls.createSecureContext(options) 
    var tlsSocket = new tls.TLSSocket(netSocket, { 
     // ... 
     secureContext : secureContext, 
     SNICallback : options.SNICallback 
     // ... 
    }); 

    self.attach(tlsSocket); 
} 

et vos options objet aurait le SNICallback défini comme:

var options { 
    // ... 
    SNICallback : function(serverName, callback){ 
     callback(null, tls.createSecureContext(getCertificateFor(serverName)); 
    // ... 
    } 
}