Hola Habr, em dic Ilya, treballo a l'equip de plataforma d'Exness. Desenvolupem i implementem els components bàsics de la infraestructura que utilitzen els nostres equips de desenvolupament de productes.
En aquest article, m'agradaria compartir la meva experiència en la implementació de la tecnologia SNI xifrada (ESNI) a la infraestructura de llocs web públics.

L'ús d'aquesta tecnologia augmentarà el nivell de seguretat quan es treballa amb un lloc web públic i complirà amb els estàndards de seguretat interna adoptats per l'Empresa.
En primer lloc, m'agradaria assenyalar que la tecnologia no està estandarditzada i encara està a l'esborrany, però CloudFlare i Mozilla ja la donen suport (en ). Això ens va motivar per a aquest experiment.
Una mica de teoria
ESNI és una extensió del protocol TLS 1.3 que permet l'encriptació SNI al missatge "Client Hello" de TLS. A continuació es mostra l'aspecte de Client Hello amb el suport d'ESNI (en lloc de l'SNI habitual, veiem ESNI):

Per utilitzar ESNI, necessiteu tres components:
- DNS;
- Suport al client;
- Suport del costat del servidor.
DNS
Heu d'afegir dos registres DNS: AI TXT (El registre TXT conté la clau pública amb la qual el client pot xifrar SNI) - vegeu a continuació. A més, hi ha d'haver suport Doh (DNS sobre HTTPS) perquè els clients disponibles (vegeu més avall) no habiliten el suport d'ESNI sense DoH. Això és lògic, ja que ESNI implica xifratge del nom del recurs al qual estem accedint, és a dir, no té sentit accedir a DNS per UDP. A més, l'ús us permet protegir-vos dels atacs d'enverinament de la memòria cau en aquest escenari.
Disponible actualment , entre ells:
CloudFlare (Consulta el meu navegador → SNI xifrat → Més informació) que els seus servidors ja admeten ESNI, és a dir, per als servidors CloudFlare del DNS tenim almenys dos registres: A i TXT. A l'exemple següent consultem el DNS de Google (a través d'HTTPS):
А entrada:
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 registre, la sol·licitud es genera segons una plantilla _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."
}
Per tant, des d'una perspectiva de DNS, hauríem d'utilitzar DoH (preferiblement amb DNSSEC) i afegir dues entrades.
Atenció al client
Si estem parlant de navegadors, de moment . Aquí teniu instruccions sobre com activar el suport ESNI i DoH a FireFox. Un cop configurat el navegador, hauríem de veure alguna cosa com això:

per comprovar el navegador.
Per descomptat, s'ha d'utilitzar TLS 1.3 per donar suport a ESNI, ja que ESNI és una extensió de TLS 1.3.
Amb el propòsit de provar el backend amb suport d'ESNI, hem implementat el client go, Però sobre això més endavant.
Suport del costat del servidor
Actualment, ESNI no és compatible amb servidors web com nginx/apache, etc., ja que funcionen amb TLS mitjançant OpenSSL/BoringSSL, que oficialment no admeten ESNI.
Per tant, vam decidir crear el nostre propi component de front-end (proxy invers ESNI), que admetria la terminació de TLS 1.3 amb ESNI i el trànsit HTTP(S) del proxy cap a l'aigües amunt, que no és compatible amb ESNI. Això permet utilitzar la tecnologia en una infraestructura ja existent, sense canviar els components principals, és a dir, utilitzant servidors web actuals que no admeten ESNI.
Per a més claredat, aquí teniu un diagrama:

Tinc en compte que el proxy es va dissenyar amb la possibilitat de finalitzar una connexió TLS sense ESNI, per donar suport als clients sense ESNI. A més, el protocol de comunicació amb upstream pot ser HTTP o HTTPS amb una versió TLS inferior a 1.3 (si upstream no admet 1.3). Aquest esquema ofereix la màxima flexibilitat.
Implementació del suport ESNI a go vam agafar en préstec . M'agradaria assenyalar de seguida que la implementació en si no és gens trivial, ja que implica canvis a la biblioteca estàndard. cripto/tls i per tant requereix un "pegat" GOROOT abans del muntatge.
Per generar les claus ESNI hem utilitzat (també la creació de CloudFlare). Aquestes claus s'utilitzen per al xifratge/desxifrat SNI.
Vam provar la compilació utilitzant go 1.13 a Linux (Debian, Alpine) i MacOS.
Unes paraules sobre les característiques operatives
El servidor intermediari invers ESNI proporciona mètriques en format Prometheus, com ara rps, latència amunt i codis de resposta, encaixades de mans TLS fallides/encertades i durada de l'enllaç TLS. A primera vista, això semblava suficient per avaluar com el servidor intermediari gestiona el trànsit.
També hem realitzat proves de càrrega abans del seu ús. Resultats a continuació:
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
Hem realitzat proves de càrrega purament qualitatives per comparar l'esquema utilitzant el proxy invers ESNI i sense. Hem "abocat" el trànsit localment per tal d'eliminar les "interferències" en components intermedis.
Per tant, amb el suport d'ESNI i el proxy a aigües amunt des d'HTTP, vam obtenir al voltant de 550 rps d'una instància, amb el consum mitjà de CPU/RAM del servidor intermediari invers ESNI:
- 80% d'ús de la CPU (amfitrions de 4 vCPU, 4 GB de RAM, Linux)
- 130 MB Mem RSS

Per comparar, RPS per al mateix nginx aigües amunt sense terminació TLS (protocol HTTP) és ~ 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
La presència de temps d'espera indica que hi ha una manca de recursos (hem utilitzat 4 vCPU, hosts de 4 GB de RAM, Linux), i de fet l'RPS potencial és més alt (hem rebut xifres de fins a 2700 RPS en recursos més potents).
En conclusió, anoto que la tecnologia ESNI sembla força prometedora. Encara hi ha moltes preguntes obertes, per exemple, els problemes d'emmagatzemar la clau ESNI pública al DNS i la rotació de les claus ESNI; aquests problemes s'estan discutint activament i la darrera versió de l'esborrany d'ESNI (en el moment d'escriure aquest escrit) ja està .
Font: www.habr.com
