Hallo Habr, mijn naam is Ilya en ik werk in het platformteam van Exness. Wij ontwikkelen en implementeren de belangrijkste infrastructuurcomponenten die onze productontwikkelingsteams gebruiken.
In dit artikel wil ik mijn ervaringen delen met de implementatie van gecodeerde SNI (ESNI)-technologie in de infrastructuur van openbare websites.

Door gebruik te maken van deze technologie wordt het beveiligingsniveau bij het werken met een openbare website verhoogd en wordt voldaan aan de interne beveiligingsnormen die het Bedrijf hanteert.
Allereerst wil ik erop wijzen dat de technologie niet gestandaardiseerd is en nog in de ontwerpfase zit, maar CloudFlare en Mozilla ondersteunen deze al (in ). Dit was voor ons de aanleiding om een ββdergelijk experiment uit te voeren.
Een beetje theorie
ESNI β is een uitbreiding van het TLS 1.3-protocol waarmee SNI kan worden gecodeerd in het TLS-handshakebericht "Client Hello". Dit is hoe Client Hello eruit ziet met ESNI-ondersteuning (in plaats van de gebruikelijke SNI zien we ESNI):

Om ESNI te gebruiken, zijn drie componenten nodig:
- DNS;
- Klantenservice;
- Ondersteuning aan serverzijde.
DNS
U moet twee DNS-records toevoegen: AEn TXT (TXT-record bevat de openbare sleutel waarmee de client SNI kan versleutelen) β zie hieronder. Daarnaast moet er draagvlak zijn DoH (DNS over HTTPS), aangezien de beschikbare clients (zie hieronder) geen ESNI-ondersteuning inschakelen zonder DoH. Dit is logisch, omdat ESNI encryptie van de resourcenaam die we benaderen vereist. Het heeft dus geen zin om DNS via UDP te benaderen. Bovendien is het gebruik Hiermee kunt u zich in dit scenario beschermen tegen cache-vergiftigingsaanvallen.
Momenteel beschikbaar , waaronder:
CloudFlare (Controleer mijn browser β Gecodeerde SNI β Meer informatie), dat hun servers ESNI al ondersteunen, dat wil zeggen dat we voor CloudFlare-servers in DNS minimaal twee records hebben - A en TXT. In het onderstaande voorbeeld raadplegen we Google DNS (via HTTPS):
Π vermelding:
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 record, verzoek wordt gegenereerd volgens een sjabloon _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."
}
Vanuit DNS-perspectief moeten we dus DoH gebruiken (bij voorkeur met DNSSEC) en twee records toevoegen.
Klantenservice
Als we het over browsers hebben, dan is het op dit moment . Hier vindt u instructies voor het activeren van ESNI- en DoH-ondersteuning in Firefox. Zodra de browser is geconfigureerd, zouden we zoiets moeten zien:

om de browser te controleren.
Uiteraard moet TLS 1.3 gebruikt worden ter ondersteuning van ESNI, aangezien ESNI een uitbreiding is op TLS 1.3.
Om de backend met ESNI-ondersteuning te testen, hebben we een client geΓ―mplementeerd op go, Maar daarover later meer.
Server-side ondersteuning
Momenteel wordt ESNI niet ondersteund door webservers als nginx/apache etc., omdat deze werken met TLS via OpenSSL/BoringSSL. Deze servers ondersteunen ESNI officieel niet.
Daarom hebben we besloten om een ββeigen front-endcomponent (ESNI reverse proxy) te creΓ«ren die TLS 1.3-terminatie met ESNI ondersteunt en HTTP(S)-verkeer proxyt naar een upstream die ESNI niet ondersteunt. Hierdoor kan de technologie worden gebruikt in een bestaande infrastructuur zonder dat de hoofdcomponenten worden gewijzigd. Dat wil zeggen, met gebruikmaking van bestaande webservers die ESNI niet ondersteunen.
Voor de duidelijkheid, hier is een diagram:

Ik wil er graag op wijzen dat de proxy is ontworpen met de mogelijkheid om een ββTLS-verbinding zonder ESNI te beΓ«indigen, ter ondersteuning van clients zonder ESNI. Het protocol voor communicatie met de upstream kan HTTP of HTTPS zijn, met een TLS-versie lager dan 1.3 (als de upstream 1.3 niet ondersteunt). Dit ontwerp biedt maximale flexibiliteit.
Implementatie van ESNI-ondersteuning op go we hebben geleend van . Ik wil er meteen op wijzen dat de implementatie zelf niet zo eenvoudig is, omdat er wijzigingen in de standaardbibliotheek nodig zijn. crypto/tls en vereist daarom "patching" GOROOT voor montage.
Om ESNI-sleutels te genereren, gebruikten we (ook een geesteskind van CloudFlare). Deze sleutels worden gebruikt om SNI te versleutelen/ontsleutelen.
We hebben de build getest met Go 1.13 op Linux (Debian, Alpine) en MacOS.
Een paar woorden over operationele kenmerken
ESNI reverse proxy biedt statistieken in Prometheus-formaat, zoals rps, upstream-latentie en responscodes, mislukte/geslaagde TLS-handshakes en TLS-handshakeduur. Op het eerste gezicht leek dit voldoende om te beoordelen hoe de proxy het verkeer verwerkt.
We hebben ook een belastingstest uitgevoerd vΓ³Γ³r gebruik. De uitslagen vindt u hieronder:
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
We hebben puur kwalitatieve belastingstesten uitgevoerd om het schema met en zonder gebruik van ESNI reverse proxy te vergelijken. We hebben het verkeer lokaal 'gegoten' om 'interferentie' in tussenliggende componenten te elimineren.
Met ESNI-ondersteuning en upstream-proxying met HTTP haalden we dus ongeveer ~550 rps per instantie, met het gemiddelde CPU/RAM-verbruik van een ESNI reverse proxy:
- 80% CPU-gebruik (4 vCPU, 4 GB RAM-hosts, Linux)
- 130 MB geheugen RSS

Ter vergelijking: de RPS voor dezelfde nginx upstream zonder TLS-beΓ«indiging (HTTP-protocol) is ~ 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
Het optreden van time-outs wijst op een gebrek aan resources (we gebruikten hosts met 4 vCPU's en 4 GB RAM). Linux), en in feite is het potentiΓ«le aantal RPS hoger (we ontvingen cijfers tot 2700 RPS op krachtigere bronnen).
Tot slot wil ik opmerken dat dat de ESNI-technologie er veelbelovend uitziet. Er zijn nog veel open vragen, zoals de opslag van de openbare ESNI-sleutel in DNS en de rotatie van ESNI-sleutels. Deze vragen worden actief besproken en de laatste conceptversie (op het moment van schrijven) van ESNI is al beschikbaar. .
Bron: www.habr.com
