Hei Habr, jeg heter Ilya, og jeg jobber i plattformteamet hos Exness. Vi utvikler og implementerer grunnleggende infrastrukturkomponenter som produktutviklingsteamene våre bruker.
I denne artikkelen vil jeg dele mine erfaringer med implementering av kryptert SNI-teknologi (ESNI) i infrastrukturen til offentlige nettsteder.

Bruken av denne teknologien vil øke sikkerhetsnivået når man jobber med et offentlig nettsted og overholde de interne sikkerhetsstandardene som er vedtatt av selskapet.
Først og fremst vil jeg påpeke at teknologien ikke er standardisert og fortsatt er i utkastfasen, men CloudFlare og Mozilla støtter den allerede (i ). Dette var grunnen til at vi ble bedt om å gjennomføre et slikt eksperiment.
Litt teori
ESNI – er en utvidelse av TLS 1.3-protokollen som lar deg kryptere SNI i TLS-håndtrykkmeldingen «Client Hello». Slik ser en Client Hello med ESNI-støtte ut (i stedet for den vanlige SNI-en ser vi ESNI):

For å bruke ESNI kreves tre komponenter:
- DNS;
- Kundestøtte;
- Støtte på serversiden.
DNS
Du må legge til to DNS-oppføringer – AOg TXT (TXT-oppføringen inneholder den offentlige nøkkelen som klienten kan kryptere SNI med) – se nedenfor. I tillegg bør det være støtte for Doh (DNS over HTTPS), siden de tilgjengelige klientene (se nedenfor) ikke aktiverer ESNI-støtte uten DoH. Dette er logisk, siden ESNI innebærer kryptering av ressursnavnet vi har tilgang til, dvs. det gir ingen mening å få tilgang til DNS over UDP. Dessuten, bruk av lar deg beskytte mot cache-forgiftningsangrep i dette scenariet.
Tilgjengelig for øyeblikket , blant dem:
CloudFlare (Sjekk nettleseren min → Kryptert SNI → Lær mer), at serverne deres allerede støtter ESNI, det vil si at for CloudFlare-servere i DNS har vi minst to poster - A og TXT. I eksemplet nedenfor ber vi om Google DNS (over HTTPS):
А inngang:
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ørselen genereres i henhold til en mal _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, bør vi bruke DoH (helst med DNSSEC) og legge til to poster.
Kundestøtte
Hvis vi snakker om nettlesere, så for øyeblikket . Instruksjoner om hvordan du aktiverer ESNI- og DoH-støtte i FireFox er gitt. Etter at nettleseren er konfigurert, bør vi se noe slikt:

for å sjekke nettleseren.
Selvfølgelig må TLS 1.3 brukes for å støtte ESNI, siden ESNI er en utvidelse av TLS 1.3.
For å teste backend-systemet med ESNI-støtte, implementerte vi en klient på go, men mer om det senere.
Støtte på serversiden
For øyeblikket støttes ikke ESNI av webservere som nginx/apache osv., ettersom de fungerer med TLS via OpenSSL/BoringSSL, som ikke offisielt støtter ESNI.
Derfor bestemte vi oss for å lage vår egen front-end-komponent (ESNI reverse proxy), som ville støtte TLS 1.3-terminering med ESNI og proxying av HTTP(S)-trafikk til oppstrøms, som ikke støtter ESNI. Dette tillater bruk av teknologien i en allerede eksisterende infrastruktur, uten å endre hovedkomponentene – det vil si å bruke nåværende webservere som ikke støtter ESNI.
For klarhetens skyld, her er et diagram:

Jeg vil bemerke at proxyen ble designet med muligheten til å avslutte en TLS-tilkobling uten ESNI, for å støtte klienter uten ESNI. Protokollen for kommunikasjon med oppstrømsleverandøren kan også være enten HTTP eller HTTPS med en TLS-versjon under 1.3 (hvis oppstrømsleverandøren ikke støtter 1.3). Denne ordningen gir maksimal fleksibilitet.
Implementering av ESNI-støtte på go vi lånte fra Jeg vil umiddelbart bemerke at selve implementeringen er ganske enkel, siden den innebærer endringer i standardbiblioteket. krypto/tls og krever derfor "patching" GOROOT før montering.
For å generere ESNI-nøkler brukte vi (også en CloudFlare-kreasjon). Disse nøklene brukes til å kryptere/dekryptere SNI.
Vi testet bygget med go 1.13 på Linux (Debian, Alpine) og MacOS.
Noen ord om driftsfunksjoner
ESNI reverse proxy tilbyr målinger i Prometheus-format, som rps, oppstrøms latens og responskoder, mislykkede/vellykkede TLS-håndtrykk og TLS-håndtrykkvarighet. Ved første øyekast virket dette tilstrekkelig for å vurdere hvordan proxyen håndterer trafikk.
Vi utførte også belastningstesting før bruk. Resultatene er nedenfor:
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
Vi utførte rent kvalitativ belastningstesting for å sammenligne skjemaet med og uten ESNI reverse proxy. Vi "pumpet" trafikk lokalt for å eliminere "forstyrrelser" i mellomliggende komponenter.
Så, med ESNI-støtte og oppstrøms proxy med HTTP, fikk vi rundt ~550 rps fra én instans, med gjennomsnittlig CPU/RAM-forbruk for ESNI reverse proxy:
- 80 % CPU-bruk (4 vCPU, 4 GB RAM-verter, Linux)
- 130 MB minne-RSS

Til sammenligning er RPS for samme nginx oppstrøms uten TLS-terminering (HTTP-protokoll) ~ 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
Tilstedeværelsen av tidsavbrudd indikerer at det er mangel på ressurser (vi brukte 4 vCPU, 4 GB RAM-verter, Linux), og faktisk er den potensielle RPS høyere (vi mottok tall opptil 2700 RPS på kraftigere ressurser).
Avslutningsvis vil jeg bemerke at at ESNI-teknologien ser ganske lovende ut. Det er fortsatt mange åpne spørsmål, for eksempel spørsmålene om lagring av den offentlige ESNI-nøkkelen i DNS og rotasjon av ESNI-nøkler – disse problemstillingene diskuteres aktivt, og den nyeste versjonen av utkastet (i skrivende stund) av ESNI er allerede klar. .
Kilde: www.habr.com
