Olá Habr, meu nome é Ilya, trabalho na equipe de plataforma da Exness. Desenvolvemos e implementamos os principais componentes de infraestrutura usados por nossas equipes de desenvolvimento de produtos.
Neste artigo, gostaria de compartilhar minha experiência de implementação da tecnologia SNI criptografada (ESNI) na infraestrutura de sites públicos.
A utilização desta tecnologia aumentará o nível de segurança ao trabalhar com um site público e atenderá aos padrões internos de segurança adotados pela Empresa.
Em primeiro lugar, gostaria de salientar que a tecnologia não está padronizada e ainda está em fase de rascunho, mas CloudFlare e Mozilla já a suportam (em
Um pouco de teoria
ESNI é uma extensão do protocolo TLS 1.3 que permite a criptografia SNI na mensagem TLS handshake "Client Hello". Esta é a aparência do Client Hello com suporte ESNI (em vez do SNI normal, vemos ESNI):
Para usar ESNI, você precisa de três componentes:
- DNS;
- Suporte ao cliente;
- Suporte do lado do servidor.
DNS
Você precisa adicionar dois registros DNS – AE SMS (O registro TXT contém a chave pública com a qual o cliente pode criptografar o SNI) - veja abaixo. Além disso, deve haver apoio DoH (DNS sobre HTTPS) porque os clientes disponíveis (veja abaixo) não habilitam o suporte ESNI sem DoH. Isso é lógico, pois ESNI implica criptografia do nome do recurso que estamos acessando, ou seja, não faz sentido acessar DNS por UDP. Além disso, o uso
Disponível atualmente
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"
}
]
}
SMS registro, a solicitação é gerada de acordo com um modelo _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."
}
Portanto, do ponto de vista do DNS, devemos usar DoH (de preferência com DNSSEC) e adicionar duas entradas.
Suporte ao cliente
Se estamos falando de navegadores, então no momento
É claro que o TLS 1.3 deve ser usado para oferecer suporte ao ESNI, já que o ESNI é uma extensão do TLS 1.3.
Para testar o backend com suporte ESNI, implementamos o cliente em go, Mas mais sobre isso mais tarde.
Suporte do lado do servidor
Atualmente, ESNI não é suportado por servidores web como nginx/apache, etc., uma vez que funcionam com TLS via OpenSSL/BoringSSL, que não suportam oficialmente ESNI.
Portanto, decidimos criar nosso próprio componente front-end (proxy reverso ESNI), que suportaria terminação TLS 1.3 com ESNI e tráfego proxy HTTP(S) para o upstream, que não suporta ESNI. Isso permite que a tecnologia seja utilizada em uma infraestrutura já existente, sem alterar os componentes principais – ou seja, utilizando servidores web atuais que não suportam ESNI.
Para maior clareza, aqui está um diagrama:
Observo que o proxy foi projetado com a capacidade de encerrar uma conexão TLS sem ESNI, para oferecer suporte a clientes sem ESNI. Além disso, o protocolo de comunicação com o upstream pode ser HTTP ou HTTPS com uma versão TLS inferior a 1.3 (se o upstream não suportar 1.3). Este esquema oferece flexibilidade máxima.
Implementação do suporte ESNI em go nós pegamos emprestado de
Para gerar chaves ESNI usamos
Testamos a compilação usando go 1.13 no Linux (Debian, Alpine) e MacOS.
Algumas palavras sobre recursos operacionais
O proxy reverso ESNI fornece métricas no formato Prometheus, como rps, latência upstream e códigos de resposta, handshakes TLS com falha/bem-sucedidos e duração do handshake TLS. À primeira vista, isso parecia suficiente para avaliar como o proxy lida com o tráfego.
Também realizamos testes de carga antes do uso. Resultados abaixo:
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
Realizamos testes de carga puramente qualitativos para comparar o esquema usando e sem proxy reverso ESNI. “Despejamos” o tráfego localmente para eliminar “interferências” em componentes intermediários.
Portanto, com suporte ESNI e proxy para upstream de HTTP, recebemos cerca de aproximadamente 550 rps de uma instância, com o consumo médio de CPU/RAM do proxy reverso ESNI:
- 80% de uso de CPU (4 vCPU, hosts de 4 GB de RAM, Linux)
- 130 MB de memória RSS
Para efeito de comparação, o RPS para o mesmo upstream nginx sem terminação TLS (protocolo HTTP) é ~ 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
A presença de timeouts indica que há falta de recursos (usamos 4 vCPUs, hosts de 4 GB de RAM, Linux) e, de fato, o RPS potencial é maior (recebemos números de até 2700 RPS em recursos mais potentes).
Concluindo, observo que a tecnologia ESNI parece bastante promissora. Ainda há muitas questões em aberto, por exemplo, as questões de armazenamento da chave pública ESNI no DNS e rotação de chaves ESNI - essas questões estão sendo discutidas ativamente, e a versão mais recente do rascunho ESNI (no momento da redação) já está
Fonte: habr.com