Sveiki, Habr, mano vardas Ilja, aš dirbu „Exness“ platformos komandoje. Kuriame ir įdiegiame pagrindinius infrastruktūros komponentus, kuriuos naudoja mūsų produktų kūrimo komandos.
Šiame straipsnyje norėčiau pasidalinti savo patirtimi diegiant šifruotą SNI (ESNI) technologiją viešųjų svetainių infrastruktūroje.

Šios technologijos naudojimas padidins saugumo lygį dirbant su vieša svetaine ir atitiks Bendrovės priimtus vidinius saugumo standartus.
Visų pirma norėčiau atkreipti dėmesį į tai, kad technologija nėra standartizuota ir vis dar yra juodraštyje, tačiau „CloudFlare“ ir „Mozilla“ ją jau palaiko (į ). Tai paskatino mus tokiam eksperimentui.
Teorijos tiek
ESNI yra TLS 1.3 protokolo plėtinys, leidžiantis SNI šifravimą TLS rankos paspaudimo pranešime „Klientas sveikas“. Štai kaip atrodo „Client Hello“ su ESNI palaikymu (vietoj įprasto SNI matome ESNI):

Norėdami naudoti ESNI, jums reikia trijų komponentų:
- DNS;
- Klientų palaikymas;
- Serverio pusės palaikymas.
DNS
Turite pridėti du DNS įrašus - AIr TXT (TXT įraše yra viešasis raktas, kuriuo klientas gali užšifruoti SNI) – žr. toliau. Be to, turi būti parama DoH (DNS per HTTPS), nes galimi klientai (žr. toliau) neįgalina ESNI palaikymo be DoH. Tai logiška, nes ESNI reiškia šaltinio, prie kurio mes pasiekiame, pavadinimo šifravimą, tai yra, nėra prasmės pasiekti DNS per UDP. Be to, naudojimas leidžia apsisaugoti nuo apsinuodijimo talpykla atakų pagal šį scenarijų.
Šiuo metu galimas , tarp jų:
CloudFlare (Patikrinkite Mano naršyklę → Šifruotas SNI → Sužinokite daugiau), kad jų serveriai jau palaiko ESNI, tai yra, CloudFlare serveriams DNS turime bent du įrašus - A ir TXT. Toliau pateiktame pavyzdyje pateikiame užklausą dėl Google DNS (per HTTPS):
А įrašas:
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 įrašas, užklausa sugeneruojama pagal šabloną _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."
}
Taigi, iš DNS perspektyvos, turėtume naudoti DoH (geriausia su DNSSEC) ir pridėti du įrašus.
Pagalba klientams
Jei mes kalbame apie naršykles, tai šiuo metu . Čia pateikiamos instrukcijos, kaip suaktyvinti ESNI ir DoH palaikymą „FireFox“. Kai naršyklė bus sukonfigūruota, turėtume pamatyti kažką panašaus:

norėdami patikrinti naršyklę.
Žinoma, ESNI palaikymui turi būti naudojamas TLS 1.3, nes ESNI yra TLS 1.3 plėtinys.
Siekdami išbandyti užpakalinę programą su ESNI palaikymu, įdiegėme klientą go, Bet apie tai vėliau.
Serverio pusės palaikymas
Šiuo metu ESNI nepalaiko žiniatinklio serveriai, tokie kaip nginx/apache ir kt., nes jie dirba su TLS per OpenSSL/BoringSSL, kurie oficialiai nepalaiko ESNI.
Todėl nusprendėme sukurti savo priekinį komponentą (ESNI atvirkštinį tarpinį serverį), kuris palaikytų TLS 1.3 nutraukimą su ESNI ir tarpinio serverio HTTP(S) srautą į aukštesnįjį srautą, kuris nepalaiko ESNI. Tai leidžia technologiją naudoti jau esamoje infrastruktūroje, nekeičiant pagrindinių komponentų – tai yra naudojant esamus žiniatinklio serverius, nepalaikančius ESNI.
Aiškumo dėlei čia yra diagrama:

Atkreipiu dėmesį, kad tarpinis serveris buvo sukurtas taip, kad būtų galima nutraukti TLS ryšį be ESNI, palaikyti klientus be ESNI. Be to, ryšio protokolas prieš srovę gali būti HTTP arba HTTPS su žemesne nei 1.3 TLS versija (jei prieš srovę nepalaiko 1.3). Ši schema suteikia maksimalų lankstumą.
ESNI paramos įgyvendinimas ant go mes skolinomės iš . Iš karto norėčiau pastebėti, kad pats diegimas yra gana nereikšmingas, nes jis apima standartinės bibliotekos pakeitimus crypto/tls todėl reikia „pataisyti“ GOROOT prieš surinkimą.
ESNI raktams generuoti naudojome (taip pat „CloudFlare“ idėja). Šie raktai naudojami SNI šifravimui / iššifravimui.
Мы протестировали сборку с использованием go 1.13 на Linux (Debian, Alpine) и MacOS.
Keletas žodžių apie veikimo ypatybes
ESNI atvirkštinis tarpinis serveris teikia metriką Prometheus formatu, pvz., RPS, priešpriešinio delsos ir atsako kodus, nesėkmingus / sėkmingus TLS rankų paspaudimus ir TLS rankos paspaudimo trukmę. Iš pirmo žvilgsnio to pakanka norint įvertinti, kaip tarpinis serveris tvarko srautą.
Taip pat prieš naudojimą atlikome apkrovos testavimą. Rezultatai žemiau:
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
Mes atlikome grynai kokybinius apkrovos testus, kad palygintume schemą naudodami ESNI atvirkštinį tarpinį serverį ir be jo. Mes „išliejome“ srautą vietoje, kad pašalintume „trukdžius“ tarpiniuose komponentuose.
Taigi, turėdami ESNI palaikymą ir tarpinį serverį prieš srovę nuo HTTP, iš vieno egzemplioriaus gavome apie 550 aps./s, o ESNI atvirkštinio tarpinio serverio vidutinis CPU/RAM suvartojimas:
- 80% CPU Usage (4 vCPU, 4 GB RAM хосты, Linux)
- 130 MB atminties RSS

Palyginimui, RPS tam pačiam nginx prieš srovę be TLS (HTTP protokolo) nutraukimo yra ~ 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
Наличие таймаутов говорит о том, что есть нехватка ресурсов (мы использовали 4 vCPU, 4 GB RAM хосты, Linux), и по факту потенциальный RPS выше (мы получали цифры до 2700 RPS на более мощных ресурсах).
Baigdamas pažymiu kad ESNI technologija atrodo gana daug žadanti. Vis dar yra daug neišspręstų klausimų, pavyzdžiui, viešo ESNI rakto saugojimo DNS ir ESNI raktų kaitaliojimo klausimai - šie klausimai yra aktyviai diskutuojami, o naujausia ESNI projekto versija (rašymo metu) jau yra .
Šaltinis: www.habr.com
