Jak zlepšit výkon HTTPS serveru v Node.js

V dnešním příspěvku se ponoříme do protokolu TLS a ukážeme si, které možnosti nastavení HTTPS serveru v Node.js mají vliv na jeho výkonnost.

SSL, TLS, HTTPS

Na úvod začněme stručnou rekapitulací protokolů pro zabezpečení spojení mezi klientem a serverem.

  • SSL je zkratkou pro Secure Socket Layer. Protokol byl vytvořený v polovině devadesátých let společností Netscape, poměrně brzo byl nahrazený TLS.
  • TLS označuje Transport Security Layer. Jedná se o standard udržovaný organizací IETF, který odstraňuje nedostatky a bezpečnostní slabiny protokolu SSL.
  • Oba protokoly SSL a TLS fungují na úrovni TCP sockets, poskytují prostředky k převedení otevřeného toku (plaintext stream) na plně šifrovaný kanál.
  • HTTS, nebo-li HTTP Secure, je kombinací HTTP protokolu komunikujícím přes SSL nebo TLS kanál.

Znovupoužití TLS Session

Založení nového zabezpečeného kanálu vyžaduje dvě kolečka mezi klientem a serverem. Navíc klient i server musí vykonat výpočetně náročné kryptografické operace, které jsou potřebné pro vytvoření sdíleného tajného klíče, který se dál používá pro šifrování dat.

Znovupoužití session funguje na principu opětovného využití stejné konfigurace, která byla použita pro předchozí spojení. Ušetří nám jedno kolečko a umožní vynechat drahou přípravu kryptografických parametrů. Tento mechanizmus je jedním z nejdůležitějších způsobů jak zvýšit výkonnost SSL/TLS komunikace.

Poznámka: pozor na záměnu pojmu TLS session se session používanou ve webových aplikacích pro zachování stavu mezi dotazy. Jedná se o dva různé a nesouvisející koncepty.

Jsou dva způsoby jak znovu použít TLS session:

  • Identifikátor session (Session Identifier) je součástí SSL i TLS standardů a měl by být podporován všemi implementacemi. Od TLS serveru se vyžaduje, aby si udržoval seznam všech platných session, a pak na základě identifikátoru prezentovaném klientem byl schopen nastavit (obnovit) příslušné parametry spojení. Jak uvidíme později, tento požadavek znamená pro Node.js servery práci navíc.
  • Session lístek (Session Ticket) je rozšířením protokolu TLS, které umožňuje zakódovat všechny potřebné údaje do jednoho lístku (ticketu), čímž řeší problém sdílení platných session mezi servery v distribuovaném prostředí. Když se klient představí serveru se správným lístkem, server dokáže obnovit session aniž by se musel podívat do externí keše platných session. Toto rozšíření zatím není implementováno ve všech TLS klientech. Klienti podporující session tickets jsou prohlížeče Google Chrome a Mozilla Firefox, knihovny openssl and gnutls. Prohlížeče Internet Explorer a Safari tuto funkcionalitu zatím nepodporují.

Podrobnější informace lze nalézt ve výborném článku (v angličtině).

Konfigurace Node.js serveru pro znovupoužití session

Znovupoužití session přes session ticket funguje samo, implementace je z pohledu vašeho kódu úplně transparentní a při běhu v jednom procesu funguje bezvadně. Když dojde na nativní cluster, chyba #5871 ve verzi v0.10 znamená, že procesy v clusteru neakceptují lístky vydané sousedními procesy. Protože však rozdělení požadavků mezi procesy není ve verzi v0.10 rovnoměrné (většina spojení skončí u jednoho nebo dvou nejvytíženějších procesů), dopad tohoto problému by neměl být příliš velký. Chyba je opravena ve verzi v0.11.

Znovupoužití session přes session identifikátory vyžaduje, aby vaše aplikace implementovala úložiště platných session (session store) a obsloužila události newSession a resumeSession. Následující kód demonstruje možnou implementaci pro TLS server běžící v jednom procesu. Poznamenejme, že HTTPS server je potomkem TLS serveru, takže stejný mechanizmus lze použít pro nastavení HTTP serveru.

var tls = require('tls');

var tlsSessionStore = {};
// TODO - implement removal of expired sessions

var opts = { /* configure certificates, etc. */ };
var server = tls.createServer(opts, tlsConnectionHandler);

server.on('newSession', function(id, data) {
  tlsSessionStore[id] = data;
});
server.on('resumeSession', function(id, cb) {
  cb(null, tlsSessionStore[id] || null);
});

server.listen(4433);

Když provozujeme cluster z vícero procesů, potřebujeme sdílet úložiště session mezi všemi procesy. Nejjednodušší řešení je použít balíček strong-cluster-tls-store, který je postavený nad posíláním zpráv mezi hlavním procesem a pracovními procesy a nevyžaduje tak žádné externí služby typu Redis. Podívejme se, jak nakonfigurovat HTTPS v Express aplikaci bežící v clusteru.

var https = require('https');
var express = require('express');
var shareTlsSessions = require('strong-cluster-tls-store');

if (cluster.isMaster) {
  // Setup your master and fork workers.
} else {
  // Start the server and configure TLS sessions sharing

  var app = express();
  // configure the app

  var httpsOpts = { /* configure certificates, etc. */ }
  var server = https.createServer(httpsOpts, app);
  shareTlsSessions(server);

  server.listen(port);
  // etc.
}

Tož to bylo jednoduché, že?

Malé upozornění: současná stabilní verze Node (v0.10) obsahuje chybu, kdy server vyvolá událost resumeSession i v případě, kdy session bude obnovena ze session lístku (#5872). To znamená, že zavedení sdíleného úložiště nejspíš zpomalí proces obnovení session. V závislosti na tom, jak velká část vašich klientů podporuje a nepodporuje TLS session lístky, se může stát, že lepším řešením bude vůbec nepoužívat sdílené úložiště.

Doporučuji, abyste sledovali výkon vaší aplikace a zjistili sami, jestli použití sdíleného úložiště pro TLS sessions přinese zrychlení. Pokud hledáte šikovný monitorovací nástroj, můžete vyzkoušet StrongOps (donedávna NodeFly).

Ověření konfigurace

Když máme správně nakonfigurovanou aplikaci, pojďme ověřit, že server skutečně umožní znovupoužití té samé session. Použijeme k tomu nástroj z balíčku openssl, který je součástí Mac OS X a taky většiny Linuxových distribucí. Ukázky předpokládají, že TLS nebo HTTPS server běží a poslouchá na portu 3000.

Poznámka: verze openssl dodávána s Mac OS X je snad ještě z minulého století a nefunguje moc dobře s verzí používanou v Node. Před provedením následujícího testu by bylo vhodné povýšit na aktuální verzi, například nainstalováním balíčku z homebrew.

  1. Zkontrolujte verzi openssl, potřebujeme alespoň 1.0
    $ openssl version
    OpenSSL 1.0.1e 11 Feb 2013
    
  2. Navážeme několik spojení se znovupoužitím té samé TLS session. Test ukončíme klávesovou zkratkou Ctrl+C.
    $ openssl s_client  -reconnect -port 3000 2>&1 \
    | grep "^\(New\|Reused\)"
    New, TLSv1/SSLv3, Cipher is AES256-GCM-SHA384
    Reused, TLSv1/SSLv3, Cipher is AES256-GCM-SHA384
    Reused, TLSv1/SSLv3, Cipher is AES256-GCM-SHA384
    Reused, TLSv1/SSLv3, Cipher is AES256-GCM-SHA384
    Reused, TLSv1/SSLv3, Cipher is AES256-GCM-SHA384
    Reused, TLSv1/SSLv3, Cipher is AES256-GCM-SHA384
    ^C
    
  3. Zopakujeme ten samý test, ale zakážeme klientovi používat TLS session ticket.
    $ openssl s_client  -reconnect -port 3000 2>&1 -no_ticket \
    | grep "^\(New\|Reused\)"
    New, TLSv1/SSLv3, Cipher is AES256-GCM-SHA384
    Reused, TLSv1/SSLv3, Cipher is AES256-GCM-SHA384
    Reused, TLSv1/SSLv3, Cipher is AES256-GCM-SHA384
    Reused, TLSv1/SSLv3, Cipher is AES256-GCM-SHA384
    Reused, TLSv1/SSLv3, Cipher is AES256-GCM-SHA384
    Reused, TLSv1/SSLv3, Cipher is AES256-GCM-SHA384
    ^C
    

Z obou výstupů je vidět, že první spojení vytvořilo novou session a následujících pět spojení tuto session znovu použilo.

Závěrem

Tento článek je překladem anglického originálu, který vyšel na firemním blogu StrongLoop. Podívejte se taky na infografiku o stavu Node.js nebo video Node.js je tady!, ve kterém dva hlavní vývojáři Node.js vysvětlují naši misi zjednodušit vývoj mobilních aplikací pro Enterprise.

3 komentáře u “Jak zlepšit výkon HTTPS serveru v Node.js
  1. Poznámka: verze openssl dodávána s Mac OS X je snad ještě z minulého století a nefunguje moc dobře s verzí používanou v Node. Před provedením následujícího testu by bylo vhodné povýšit na aktuální verzi, například nainstalováním balíčku z homebrew.

    Apple označil openSSL za deprecated už v OS X 10.7 (červen 2011) nahrazuje ji “Common Crypto”, kterou samo vyvíjí, takže Node.js by mělo na OS X používat spíše tuto knihovnu. http://www.opensource.apple.com/source/CommonCrypto/CommonCrypto-60049/

  2. Youre so cool! I dont suppose Ive learn something like this before. So nice to find someone with some authentic ideas on this subject. realy thanks for starting this up. this website is one thing that is wanted on the internet, someone with somewhat originality. useful job for bringing something new to the internet! http://pcslotsonline.ru

Napsat komentář

Vaše emailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *

*

Mete pouvat Markdown: **Tun**, *kurzva*, `kd` atd.

Můžete používat následující HTML značky a atributy: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>