Hola Habr, mi nombre es Ilya, trabajo en el equipo de plataforma de Exness. Desarrollamos e implementamos los componentes de infraestructura central que utilizan nuestros equipos de desarrollo de productos.
En este artículo, me gustaría compartir mi experiencia en la implementación de tecnología SNI cifrada (ESNI) en la infraestructura de sitios web públicos.
El uso de esta tecnología aumentará el nivel de seguridad al trabajar con un sitio web público y cumplirá con los estándares de seguridad internos adoptados por la Compañía.
En primer lugar, me gustaría señalar que la tecnología no está estandarizada y aún está en borrador, pero CloudFlare y Mozilla ya la admiten (en
Un poco de teoría
ESNI es una extensión del protocolo TLS 1.3 que permite el cifrado SNI en el mensaje "Cliente Hola" del protocolo de enlace TLS. Así es como se ve Client Hello con soporte ESNI (en lugar del SNI habitual vemos ESNI):
Para utilizar ESNI, necesita tres componentes:
- DNS;
- Atención al cliente;
- Soporte del lado del servidor.
DNS
Debe agregar dos registros DNS: AY TXT (El registro TXT contiene la clave pública con la que el cliente puede cifrar SNI); consulte a continuación. Además, debe haber apoyo Departamento de Salud (DNS sobre HTTPS) porque los clientes disponibles (ver más abajo) no habilitan la compatibilidad con ESNI sin DoH. Esto es lógico, ya que ESNI implica cifrar el nombre del recurso al que accedemos, es decir, no tiene sentido acceder a DNS a través de UDP. Es más, el uso
Actualmente disponible
CloudFlare
А registro:
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 registro, la solicitud se genera según 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."
}
Entonces, desde una perspectiva de DNS, deberíamos usar DoH (preferiblemente con DNSSEC) y agregar dos entradas.
Atención al cliente
Si hablamos de navegadores, entonces en este momento
Por supuesto, se debe utilizar TLS 1.3 para admitir ESNI, ya que ESNI es una extensión de TLS 1.3.
Con el fin de probar el backend con soporte ESNI, implementamos el cliente en go, Pero más sobre eso más adelante.
Soporte del lado del servidor
Actualmente, ESNI no es compatible con servidores web como nginx/apache, etc., ya que funcionan con TLS a través de OpenSSL/BoringSSL, que no son compatibles oficialmente con ESNI.
Por lo tanto, decidimos crear nuestro propio componente de front-end (proxy inverso ESNI), que admitiría la terminación TLS 1.3 con ESNI y el tráfico proxy HTTP(S) hacia el flujo ascendente, que no admite ESNI. Esto permite utilizar la tecnología en una infraestructura ya existente, sin cambiar los componentes principales, es decir, utilizando servidores web actuales que no son compatibles con ESNI.
Para mayor claridad, aquí hay un diagrama:
Observo que el proxy fue diseñado con la capacidad de terminar una conexión TLS sin ESNI, para admitir clientes sin ESNI. Además, el protocolo de comunicación con upstream puede ser HTTP o HTTPS con una versión de TLS inferior a 1.3 (si upstream no es compatible con 1.3). Este esquema ofrece la máxima flexibilidad.
Implementación del soporte de ESNI en go tomamos prestado de
Para generar claves ESNI utilizamos
Probamos la compilación usando go 1.13 en Linux (Debian, Alpine) y MacOS.
Algunas palabras sobre las características operativas.
El proxy inverso ESNI proporciona métricas en formato Prometheus, como rps, latencia ascendente y códigos de respuesta, protocolos de enlace TLS fallidos/exitosos y duración del protocolo de enlace TLS. A primera vista, esto parecía suficiente para evaluar cómo maneja el tráfico el proxy.
También realizamos pruebas de carga antes de su uso. Resultados a continuación:
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
Llevamos a cabo pruebas de carga puramente cualitativas para comparar el esquema utilizando y sin proxy inverso ESNI. "Vertimos" tráfico localmente para eliminar la "interferencia" en los componentes intermedios.
Entonces, con el soporte de ESNI y el proxy ascendente con HTTP, obtuvimos alrededor de ~550 rps de una instancia, con el consumo promedio de CPU/RAM del proxy inverso de ESNI:
- 80% de uso de CPU (4 vCPU, hosts de 4 GB de RAM, Linux)
- 130 MB Memoria RSS
A modo de comparación, el RPS para el mismo nginx upstream sin terminación TLS (protocolo HTTP) es ~ 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 presencia de tiempos de espera indica que hay falta de recursos (utilizamos 4 vCPU, hosts de 4 GB de RAM, Linux) y, de hecho, el RPS potencial es mayor (obtuvimos cifras de hasta 2700 RPS en recursos más potentes).
En conclusión, observo que la tecnología ESNI parece bastante prometedora. Todavía quedan muchas preguntas abiertas, por ejemplo, las cuestiones de almacenar una clave ESNI pública en el DNS y la rotación de claves ESNI; estas cuestiones se están discutiendo activamente y la última versión del borrador de ESNI (en el momento de escribir este artículo) ya está disponible.
Fuente: habr.com