Hej Habr, jeg hedder Ilya, og jeg arbejder i platformteamet hos Exness. Vi udvikler og implementerer grundlæggende infrastrukturkomponenter, der anvendes af vores produktudviklingsteams.
I denne artikel vil jeg gerne dele erfaringer med implementeringen af teknologien encrypted SNI (ESNI) i infrastrukturen for offentlige hjemmesider.

Brugen af denne teknologi vil øge sikkerhedsniveauet ved arbejde med offentlige hjemmesider og overholde de interne sikkerhedsstandarder vedtaget af virksomheden.
Først vil jeg fremhæve, at teknologien ikke er standardiseret og stadig er i udkast, men CloudFlare og Mozilla understøtter allerede den (i ). Dette motiverede os til et sådant eksperiment.
Lidt teori
ESNI er en udvidelse af TLS 1.3-protokollen, der gør det muligt at kryptere SNI i "Client Hello" meddelelsen fra TLS handshake. Her er, hvordan Client Hello ser ud med ESNI-understøttelse (i stedet for den sædvanlige SNI ser vi ESNI):

For at bruge ESNI er der tre komponenter, der er nødvendige:
- DNS;
- Klientunderstøttelse;
- Serverunderstøttelse.
DNS
Det er nødvendigt at tilføje to DNS-poster - A, og TXT (TXT-posten indeholder den offentlige nøgle, som klienten kan bruge til at kryptere SNI) - se nedenfor. Derudover bør der være understøttelse af DoH (DNS over HTTPS), da de tilgængelige klienter (se nedenfor) ikke aktiverer understøttelse af ESNI uden DoH. Dette giver mening, da ESNI indebærer kryptering af navnet på den ressource, vi anmoder om, hvilket betyder, at det er meningsløst at henvende sig til DNS via UDP. Endda brugen af giver beskyttelse mod "cache poisoning" angreb i dette scenarie.
I øjeblikket er der tilgængeligt , blandt dem:
CloudFlare (Check My Browser → Encrypted SNI → Learn More), hvilket viser, at deres servere allerede nu understøtter ESNI, derfor har vi for CloudFlare-servere i DNS mindst to poster - A og TXT. I eksemplet nedenfor anmoder vi om Google DNS (over HTTPS):
A post:
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 post, forespørgslen dannes efter skabelonen _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."
}Så fra et DNS-perspektiv skal vi bruge DoH (helst med DNSSEC) og tilføje to poster.
Klientens støtte
Når vi taler om browsere, er der i øjeblikket . Her er instrukserne til, hvordan man aktiverer ESNI og DoH i FireFox. Når browseren er konfigureret, skal vi se noget som dette:

til browserens kontrol.
Selvfølgelig skal TLS 1.3 bruges til at støtte ESNI, da ESNI er en udvidelse til TLS 1.3.
For backend-testning med ESNI-støtte har vi implementeret en klient i go, men det vender vi tilbage til senere.
Serverens støtte
I øjeblikket understøttes ESNI ikke af webservere som nginx/apache osv., da de arbejder med TLS via OpenSSL/BoringSSL, hvor ESNI ikke officielt understøttes.
Derfor har vi besluttet at skabe vores front-end komponent (ESNI reverse proxy), som understøtter TLS 1.3 terminering med ESNI og HTTP(S) trafik proxying til upstream, der ikke understøtter ESNI. Dette muliggør anvendelse af teknologien i en eksisterende infrastruktur uden at ændre de grundlæggende komponenter - det vil sige, at vi kan bruge nuværende webservere, der ikke understøtter ESNI.
For klarhedens skyld præsenterer vi en skitse:

Jeg vil bemærke, at proxyen blev designet med muligheden for at terminere TLS-forbindelsen uden ESNI for at støtte klienter uden ESNI. Derudover kan kommunikationsprotokollen med upstream være både HTTP og HTTPS med en TLS-version lavere end 1.3 (hvis upstream ikke understøtter 1.3). Denne skitse giver maksimal fleksibilitet.
Implementeringen af ESNI-støtte på go har vi lånt fra . Jeg skal straks bemærke, at selve implementeringen er ret ikke-triviel, da den involverer ændringer i standardbiblioteket crypto/tls og kræver derfor "patching" GOROOT før opbygning.
Til generering af ESNI-nøgler brugte vi (også et produkt fra CloudFlare). Disse nøgler bruges til kryptering/dekryptering af SNI.
Vi har testet opbygningen med go 1.13 på Linux (Debian, Alpine) og MacOS.
Et par ord om driftsmæssige særheder.
ESNI reverse proxy giver metrics i Prometheus-format, såsom rps, upstream latenstid & svarskoder, fejlede / lykkedes TLS håndtryk & varighed af TLS håndtryk. Ved første øjekast så det ud til at være tilstrækkeligt til at vurdere, hvordan proxyen håndterer trafikken.
Desuden udførte vi belastningstest inden brug. Resultaterne er nedenfor:
wrk -t50 -c1000 -d360s 'https://esni-rev-proxy.npw:443' --timeout 15s
Kører 6m test @ https://esni-rev-proxy.npw:443
50 tråde og 1000 forbindelser
Tråd Statistik Gennemsnit Stdev Max +/− Stdev
Latens 1.77s 1.21s 7.20s 65.43%
Req/Sek 13.78 8.84 140.00 83.70%
206357 anmodninger i 6.00m, 6.08GB læst
Anmodninger/s: 573.07
Overførsel/s: 17.28MB Vi gennemførte belastningstest til ren kvalitetsændring for at sammenligne skemaer med og uden brug af ESNI reverse proxy. Vi "fødte" trafik lokalt for at udelukke "forstyrrelser" i mellemliggende komponenter.
Så med ESNI støtte og proxiering til upstream med HTTP, opnåede vi omkring ~ 550 rps fra én instans, mens den gennemsnitlige CPU/RAM for ESNI reverse proxy var:
- 80% CPU-brug (4 vCPU, 4 GB RAM værter, Linux)
- 130 MB Mem RSS

Til sammenligning, RPS for den samme upstream nginx uden TLS terminering (HTTP-protokol) ~ 1100:
wrk -t50 -c1000 -d360s 'http://lb.npw:80' –-timeout 15s
Kører 6m test @ http://lb.npw:80
50 tråde og 1000 forbindelser
Tråd Statistik Gennemsnit Stdev Max +/− Stdev
Latens 1.11s 2.30s 15.00s 90.94%
Req/Sek 23.25 13.55 282.00 79.25%
393093 anmodninger i 6.00m, 11.35GB læst
Socket-fejl: connect 0, read 0, write 0, timeout 9555
Non-2xx eller 3xx svar: 8111
Anmodninger/s: 1091.62
Overførsel/s: 32.27MB Tilstedeværelsen af timeouts indikerer, at der er mangel på ressourcer (vi brugte 4 vCPU, 4 GB RAM værter, Linux), og den potentielle RPS er højere (vi fik tal op til 2700 RPS på mere kraftfulde ressourcer).
Afslutningsvis vil jeg bemærke, at teknologien ESNI ser ganske lovende ud. Der er stadig mange åbne spørgsmål, såsom opbevaring af den offentlige ESNI-nøgle i DNS og rotation af ESNI-nøgler – disse spørgsmål er aktivt diskuteret, og den seneste version af udkastet (på tidspunktet for skrivning) ESNI er allerede .
Kilde: habr.com
