Zdravo Habr, moje ime je Ilya, radim u timu platforme u Exnessu. Razvijamo i implementiramo osnovne komponente infrastrukture koje koriste naši timovi za razvoj proizvoda.
U ovom članku želim podijeliti svoje iskustvo implementacije šifrirane SNI (ESNI) tehnologije u infrastrukturu javnih web stranica.

Upotreba ove tehnologije će povećati nivo sigurnosti pri radu sa javnom web-stranicom i biti usklađena sa internim sigurnosnim standardima koje je usvojila Kompanija.
Prije svega, želio bih istaći da tehnologija nije standardizirana i da je još uvijek u nacrtu, ali je CloudFlare i Mozilla već podržavaju (u ). To nas je motivisalo za ovakav eksperiment.
Malo teorije
ESNI je proširenje TLS 1.3 protokola koje omogućava SNI enkripciju u TLS rukovanju "Client Hello" poruci. Evo kako izgleda Client Hello sa ESNI podrškom (umjesto uobičajenog SNI vidimo ESNI):

Da biste koristili ESNI, potrebne su vam tri komponente:
- DNS;
- Podrška klijentima;
- Podrška na strani servera.
DNS
Morate dodati dva DNS zapisa – Ai TXT (TXT zapis sadrži javni ključ kojim klijent može šifrirati SNI) - vidi dolje. Osim toga, mora postojati podrška DoH (DNS preko HTTPS-a) jer dostupni klijenti (pogledajte dolje) ne omogućavaju ESNI podršku bez DoH-a. Ovo je logično, jer ESNI podrazumijeva šifriranje imena resursa kojem pristupamo, odnosno nema smisla pristupati DNS-u preko UDP-a. Štaviše, upotreba omogućava vam zaštitu od napada trovanja keš memorije u ovom scenariju.
Trenutno dostupno , među njima:
CloudFlare (Check My Browser → Encrypted SNI → Learn More) da njihovi serveri već podržavaju ESNI, odnosno da za CloudFlare servere u DNS-u imamo najmanje dva zapisa - A i TXT. U primjeru ispod postavljamo upit za Google DNS (preko HTTPS-a):
А unos:
curl 'https://dns.google.com/resolve?name=www.cloudflare.com&type=A'
-s -H 'accept: application/dns+json'
{
"Status": 0,
"TC": false,
"RD": true,
"RA": true,
"AD": true,
"CD": false,
"Question": [
{
"name": "www.cloudflare.com.",
"type": 1
}
],
"Answer": [
{
"name": "www.cloudflare.com.",
"type": 1,
"TTL": 257,
"data": "104.17.210.9"
},
{
"name": "www.cloudflare.com.",
"type": 1,
"TTL": 257,
"data": "104.17.209.9"
}
]
}
TXT zapis, zahtjev se generira prema šablonu _esni.FQDN:
curl 'https://dns.google.com/resolve?name=_esni.www.cloudflare.com&type=TXT'
-s -H 'accept: application/dns+json'
{
"Status": 0,
"TC": false,
"RD": true,
"RA": true,
"AD": true,
"CD": false,
"Question": [
{
"name": "_esni.www.cloudflare.com.",
"type": 16
}
],
"Answer": [
{
"name": "_esni.www.cloudflare.com.",
"type": 16,
"TTL": 1799,
"data": ""/wEUgUKlACQAHQAg9SiAYQ9aUseUZr47HYHvF5jkt3aZ5802eAMJPhRz1QgAAhMBAQQAAAAAXtUmAAAAAABe3Q8AAAA=""
}
],
"Comment": "Response from 2400:cb00:2049:1::a29f:209."
}
Dakle, iz perspektive DNS-a, trebali bismo koristiti DoH (po mogućnosti sa DNSSEC) i dodati dva unosa.
Korisnička podrška
Ako govorimo o pretraživačima, onda u ovom trenutku . Evo instrukcija kako da aktivirate ESNI i DoH podršku u FireFox-u. Nakon što je pretraživač konfiguriran, trebali bismo vidjeti nešto poput ovoga:

da proverite pretraživač.
Naravno, TLS 1.3 se mora koristiti za podršku ESNI, pošto je ESNI proširenje za TLS 1.3.
Za potrebe testiranja pozadine sa ESNI podrškom, implementirali smo klijenta na go, Ali više o tome kasnije.
Podrška na strani servera
Trenutno, ESNI nije podržan od strane web servera kao što su nginx/apache, itd., jer oni rade sa TLS-om preko OpenSSL/BoringSSL-a, koji zvanično ne podržavaju ESNI.
Stoga smo odlučili kreirati vlastitu front-end komponentu (ESNI reverse proxy), koja bi podržala TLS 1.3 terminaciju sa ESNI i proxy HTTP(S) prometom prema uzvodnom toku, koji ne podržava ESNI. Ovo omogućava da se tehnologija koristi u već postojećoj infrastrukturi, bez mijenjanja glavnih komponenti – odnosno korištenje trenutnih web servera koji ne podržavaju ESNI.
Radi jasnoće, evo dijagrama:

Napominjem da je proxy dizajniran sa mogućnošću da prekine TLS vezu bez ESNI, da podrži klijente bez ESNI. Također, komunikacijski protokol sa uzvodnim putem može biti ili HTTP ili HTTPS sa TLS verzijom nižom od 1.3 (ako upstream ne podržava 1.3). Ova shema daje maksimalnu fleksibilnost.
Implementacija ESNI podrške na go pozajmili smo od . Želio bih odmah napomenuti da je sama implementacija prilično netrivijalna, jer uključuje promjene u standardnoj biblioteci crypto/tls i stoga zahtijeva "krpanje" GOROOT prije montaže.
Za generiranje ESNI ključeva koristili smo se (takođe zamisao CloudFlare-a). Ovi ključevi se koriste za SNI enkripciju/dešifriranje.
Мы протестировали сборку с использованием go 1.13 на Linux (Debian, Alpine) и MacOS.
Nekoliko riječi o operativnim karakteristikama
ESNI obrnuti proxy pruža metriku u Prometheus formatu, kao što su rps, uzvodno kašnjenje i kodovi odgovora, neuspješna/uspješna TLS rukovanja i trajanje TLS rukovanja. Na prvi pogled, ovo je izgledalo dovoljno da se proceni kako proxy upravlja saobraćajem.
Također smo izvršili testiranje opterećenja prije upotrebe. Rezultati ispod:
wrk -t50 -c1000 -d360s 'https://esni-rev-proxy.npw:443' --timeout 15s
Running 6m test @ https://esni-rev-proxy.npw:443
50 threads and 1000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 1.77s 1.21s 7.20s 65.43%
Req/Sec 13.78 8.84 140.00 83.70%
206357 requests in 6.00m, 6.08GB read
Requests/sec: 573.07
Transfer/sec: 17.28MB
Proveli smo čisto kvalitativno testiranje opterećenja kako bismo uporedili shemu koristeći ESNI reverse proxy i bez njega. Lokalno smo „sipali“ saobraćaj kako bismo eliminisali „smetnje“ u međukomponentama.
Dakle, sa podrškom za ESNI i proxyvanjem na uzvodno od HTTP-a, dobili smo oko ~550 okretaja u sekundi iz jedne instance, uz prosječnu potrošnju CPU/RAM-a ESNI obrnutog proxyja:
- 80% CPU Usage (4 vCPU, 4 GB RAM хосты, Linux)
- 130 MB Mem RSS

Za poređenje, RPS za isti nginx uzvodno bez TLS (HTTP protokol) terminacije je ~ 1100:
wrk -t50 -c1000 -d360s 'http://lb.npw:80' –-timeout 15s
Running 6m test @ http://lb.npw:80
50 threads and 1000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 1.11s 2.30s 15.00s 90.94%
Req/Sec 23.25 13.55 282.00 79.25%
393093 requests in 6.00m, 11.35GB read
Socket errors: connect 0, read 0, write 0, timeout 9555
Non-2xx or 3xx responses: 8111
Requests/sec: 1091.62
Transfer/sec: 32.27MB
Наличие таймаутов говорит о том, что есть нехватка ресурсов (мы использовали 4 vCPU, 4 GB RAM хосты, Linux), и по факту потенциальный RPS выше (мы получали цифры до 2700 RPS на более мощных ресурсах).
U zaključku, napominjem da ESNI tehnologija izgleda prilično obećavajuće. Još uvijek ima mnogo otvorenih pitanja, na primjer, pitanja pohranjivanja javnog ESNI ključa u DNS i rotiranja ESNI ključeva - o tim se pitanjima aktivno raspravlja, a najnovija verzija ESNI nacrta (u vrijeme pisanja) je već objavljena. .
izvor: www.habr.com
