miércoles, 2 de noviembre de 2011

Servidor https con node.js

Montar un servidor https con node.js es casi trivial. Realizando dos sencillas tareas, lo tendremos funcionando en muy poco tiempo. La primera y más tediosa (por decir algo) es generar la clave y el certificado. La segunda tarea es crear y arrancar el servidor.

1. Generar el certificado
Esta tarea se hace en tres pasos:

1.a Primero generamos la clave privada:

openssl genrsa -des3 -out localhost.key 1024

Nos pedira una password para la clave que utilizaremos en los siguientes pasos:

Generating RSA private key, 1024 bit long modulus
.....................++++++
.......++++++
e is 65537 (0x10001)
Enter pass phrase for localhost.key:

La clave tiene el siguiente aspecto:

cat localhost.key 
 
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,38E8987484A8E569

WrRipQoSD+Tf8EmIiZHpd0ucL5gP5wFmxPAeUdIvEvBGCobPafPHCGi6u7B8yHtc
fykQK3SO+1BueMBgkha0GGLd8t+aENUtZK9rcrfUg2XUL0a7BvVZD2LU46zChtD7
YJS7UHyc53m5Lj4OX80Xq2wzL5OyHI064cCMwLthkj75onVevyVGniZm8xfV9Ez2
VpnVlu/djODNIoKfOGhhRejKi36sZNaPouwa9ubtaZyW9xzGrsH5M3ufMZpXe1Gf
W9rQ5x9oTyGZE7fbHj4UKRnXf/DpgY0rXcgs77P9J+kQ0oa5NsK/qxPSwveS1Utt
0SD1TTAvAPnb2CUd7NgVkYsfPpD/A/p1b5mL8nQrhO6BNP/ARKG5k7MfDEYmvbyN
OeVaaMxm2gy7tn0Z3XI8oWstnXGV4NfebSyDfsb0aCtaaK5DFRkWgt6Navc2d2bL
71lM2ipEmC2ZpXm5RsAl65Ghw2BW/XWTAwlUpP274c8adOzbSYeG4FwMHAr8tnK9
kYc6BKkGqqh6NDwlOfD2HUo2zy+41GmoNm4l9Onv914Gac2QRgsw/EmetwZxGFtZ
5cyN4YddVJq/L5CVIdLThVym63qgSoGZ4BLtxJAaW2dAq+F9Z31NfxOYOFbIjaK8
sXF5GJhPlUIY2N/1ofdH7XbilgSepD2Dvvld4nmanix19dGRW2cKGgq8Pojmq6lj
kDGPGVMksjfiSulZJsRgrQl+xHOvFUY74fihqzHWIya+6nqE9dN/QwEyffaHrHk3
AHHko5RAHcRjG93fpgCke5zwFPgz4torf+qDBj0/NVJBF951pW8euA==
-----END RSA PRIVATE KEY-----

1.b. Lo siguiente es generar el CSR o (Certificate Signing Request, petición de firma de certificado)

openssl req -new -key localhost.key -out localhost.csr

Rellenamos los datos que se nos van pidiendo:

Enter pass phrase for localhost.key:
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:ES
State or Province Name (full name) [Some-State]:Madrid
Locality Name (eg, city) []:Madrid
Organization Name (eg, company) [Internet Widgits Pty Ltd]:NewCo,s.l.
Organizational Unit Name (eg, section) []:Dpto de IT    
Common Name (eg, YOUR name) []:NewCo
Email Address []:localhost@localhost

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

El aspecto del csr es parecido:

-----BEGIN CERTIFICATE REQUEST-----
MIIB6DCCAVECAQAwgY0xCzAJBgNVBAYTAkVTMQ8wDQYDVQQIEwZNYWRyaWQxDzAN
BgNVBAcTBk1hZHJpZDANBDHAGA1UEChMKmV3Q28scy5sLjETNAJDA1UECxMKRHB0
byBkZSBJVDEOMAwGA1UEAxMFTmV3Q28xIjAgBgkqhkiG9w0BCQEWE2xvY2FsaG9z
dEBsb2NhbGhvc3QwgZ8wDQYJKoZIhvcNAQE9Jan(gY0AMIGJAoGBALeou1eIQ0x2
NOtVujG0L5Mulo7CZIBPWZvOSZgR46JTtLmH/NTHA9dPM/MO+vuxo5X0X551x/N3
+MDP+GO1JAYkN/mXvW4jRMzM0AEWdd1sODxxCQtizFwj0biTrQ1StceGRbdsW6+2
B4acAfQGvimAfotdSvoO6NianDh7XQGzAgMBAAGgGjAYBgkqhkiG9w0BCQcxCxMJ
Y2hhbGxlbmdlMA0GCSqGSIb3DQEBBQUAA4GBAID43dLdWhSl5ni+jjThllIdkAMB
FLZHityDjo6bRi17pNUWoCWjKPHHHAIDHNg6OvcBiwCk+lrj1uC3/X0R3m4oeKRR
J+CCkP6Bsv804mS6X1c34TDEzR/mulAgQMvAHd2isPyrRdla1QaVgipMyuKhDPyE
c6iNvcf8iJ/ASSBW
-----END CERTIFICATE REQUEST-----

1.c. Y por último generamos el certificado ssl.

openssl x509 -req -days 365 -in localhost.csr -signkey localhost.key -out localhost.crt

Nos pedira la password de la clave

Signature ok
subject=/C=ES/ST=Madrid/L=Madrid/O=NewCo,s.l./OU=Dpto de IT/CN=NewCo/emailAddress=localhost@localhost
Getting Private key
Enter pass phrase for localhost.key:

Y ya tenemos todo listo para arrancar. Lo comprobamos:

ls -l

-rw-r--r--  1 miusuario  staff  952  2 nov 21:40 localhost.crt
-rw-r--r--  1 miusuario  staff  737  2 nov 21:32 localhost.csr
-rw-r--r--  1 miusuario  staff  963  2 nov 21:23 localhost.key

2. Crear y arrancar el servidor

El código no puede ser más sencillo. Creamos el fichero https-server.js con el siguiente código:

var https = require('https');
var fs = require('fs');

var options = {
  key: fs.readFileSync('localhost.key'),
  cert: fs.readFileSync('localhost.crt')
};

https.createServer(options, function (req, res) {
  res.writeHead(200);
  res.end("¡Responidiendo por SSL!\n");
}).listen(8000);

Lo arrancamos con:

node.js https-server.js

Nos pedirá la password de la clave:

Enter PEM pass phrase:

Ahora solo hay que acceder a https://localhost:8000 para ver en acción al servidor.

7 comentarios:

  1. He estado leyendo con atención tus posts, y todavía no veo una aplicación práctica a esto del node.js. Si, es cierto, simplifica mucho muchísimo el código necesario para implementar un server... y otras cosas me imagino, pero ¿para que?

    ¿Que soluciones crees que puede aportar este framework?

    Un saludo.

    ResponderEliminar
  2. A mí lo que me atrae de la solución es, sobre todo, la posibilidad de ejecutar cosas de manera asíncrona. Me explico.

    Normalmente, en aplicaciones web, en el servidor no machacamos muchos números ni hacemos grandes simulaciones, y en cambio sí pedimos datos a bases de datos, consumimos servicios web, etc.

    Todo esto lo solemos hacer en un único thread que se puede pasar la mitad del tiempo esperando a otros sistemas para luego componer la respuesta. Todo este tiempo de espera son recursos perdidos, sobre todo porque tenemos un número limitado de threads.

    En node.js, solo tenemos un thread y cuando llegamos al punto en que toca esperar, registramos un handler de la función que hay que utilizar al final de la espera y los recursos se dedican a hacer otra cosa. Esto proporciona en algunos casos una escalabilidad que no tenemos en soluciones tradicionales.

    Por otra parte, al haber un único thread, resulta imposible tener problemas de concurrencia, que ya sabemos que cuando aparecen suele ser difíciles de depurar.

    Por último, cada vez mas nos vamos a mover hacia aplicaciones que utilicen websockets o cosas parecidas que implicarán que cada servidor mantendrá abiertas miles de conexiones simultáneas con los clientes web. Para este tipo de aplicaciones, la naturaleza de node.js encaja perfectamente.

    ResponderEliminar
  3. Ok, mas o menos tenia esa idea, que donde es beneficioso este framework es en comunicaciones al estilo Comet.

    ResponderEliminar
  4. Se me olvidaba decirte que, ahora con Tomcat7 y su nuevo conector AJP que soporta NIO, tienes non-blocking I/O en threads, resultando en la misma solución que node.js.

    ResponderEliminar
  5. Si, es una solución parecida. Por lo que he podido leer, usando NIO tal vez se consiguen soluciones con más capacidad sobre una sola máquina, pero al implementar las soluciones, me parece que resulta más natural en node.js. Va con su naturaleza y no figura como un añadido.

    ResponderEliminar
  6. Hola, se te ocurre como adaptar este ejemplo para configurar un server que valide mediante DNIe? Tan sencillo como declara como opción la variable 'ca' apuntando al certificado raíz emitido por la entidad certificadora en formato .cer o se ha de hacer algo mas.
    Muchas gracias

    ResponderEliminar
  7. ¿Has probado a utilizar alguna librería como https://github.com/tgies/client-certificate-auth?
    Lo cierto es que las veces que trabajado con DNIe, por distintos motivos ajenos a mi, me he tenido que apoyar en applets y el uso del DNIe ha sido bastante escaso. Espero que tengas mejor suerte que yo :)

    ResponderEliminar